using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace Cryville.Common.Pdt {
///
/// Base of evaluator for PDT expressions.
///
public abstract class PdtEvaluatorBase {
private struct StackFrame {
public int Offset;
public int Length;
public int Type;
}
int _framecount = 0;
int _goffset = 0;
readonly StackFrame[] _stack = new StackFrame[256];
readonly byte[] _mem = new byte[0x100000];
bool _revokepttconst;
LinkedListNode _rip;
///
/// Evaluates an expression and passes the result to a target operator.
///
/// The target operator.
/// The expression to evaluate.
public void Evaluate(PdtOperator target, PdtExpression exp) {
_framecount = 0;
_goffset = 0;
_revokepttconst = false;
for (_rip = exp.Instructions.First; _rip != null; _rip = _rip.Next)
_rip.Value.Execute(this);
Operate(target, _framecount, true);
if (exp.IsPotentialConstant) {
exp.IsConstant = exp.IsPotentialConstant = !_revokepttconst;
}
}
///
/// Patches an expression with a lefthand variable and a compound operator.
///
/// The name of the lefthand variable.
/// The name of the compound operator.
/// The expression.
public void PatchCompound(int target, int op, PdtExpression exp) {
exp.Instructions.AddFirst(new PdtInstruction.PushVariable(target));
exp.Instructions.AddLast(new PdtInstruction.Operate(op, 2));
}
///
/// Optimizes an expression by merging its instructions.
///
/// The expression to optimize.
public void Optimize(PdtExpression exp) {
_framecount = 0;
_goffset = 0;
List ct;
var cols = new Dictionary, List>();
var il = exp.Instructions;
for (_rip = il.First; _rip != null; _rip = _rip == null ? il.First : _rip.Next) {
var i = _rip.Value;
if (i is PdtInstruction.Operate) {
int fc0 = _framecount;
int fc1 = ((PdtInstruction.Operate)i).Signature.ParamCount;
try { i.Execute(this); } catch (Exception) { }
if (fc0 - _framecount == fc1) {
unsafe {
fixed (StackFrame* frame = &_stack[_framecount++]) {
frame->Type = PdtInternalType.Error;
frame->Offset = -1;
frame->Length = 0;
}
}
}
else {
var frame = _stack[_framecount - 1];
_rip = il.AddAfter(_rip, new PdtInstruction.PushConstant(frame.Type, _mem, frame.Offset, frame.Length));
for (var j = 0; j <= fc1; j++) il.Remove(_rip.Previous);
}
}
else if (i is PdtInstruction.Collapse) {
var t = (PdtInstruction.Collapse)i;
try {
var pins = _rip;
i.Execute(this);
if (_rip == pins) {
_rip = _rip.Next;
il.Remove(_rip.Previous);
il.Remove(_rip.Previous);
_rip = _rip.Previous;
}
else {
_rip = pins.Previous;
while (_rip.Next != t.Target) il.Remove(_rip.Next);
il.Remove(_rip.Next);
if (cols.TryGetValue(t.Target, out ct)) {
foreach (var u in ct) u.Target = _rip;
cols.Remove(t.Target);
cols.Add(_rip, ct);
}
}
}
catch (Exception) {
if (cols.TryGetValue(t.Target, out ct)) ct.Add(t);
else cols.Add(t.Target, new List { t });
}
}
else if (i is PdtInstruction.PushVariable) {
i.Execute(this);
var frame = _stack[_framecount - 1];
if (frame.Type != PdtInternalType.Undefined && frame.Type != PdtInternalType.Error) {
_rip = il.AddAfter(_rip, new PdtInstruction.PushConstant(frame.Type, _mem, frame.Offset, frame.Length));
il.Remove(_rip.Previous);
}
}
else i.Execute(this);
if (_rip != null && cols.TryGetValue(_rip, out ct)) {
unsafe {
fixed (StackFrame* frame = &_stack[_framecount - 1]) {
frame->Type = PdtInternalType.Error;
frame->Offset = -1;
frame->Length = 0;
}
}
}
}
exp.IsConstant = true;
exp.IsPotentialConstant = true;
for (var ins = il.First; ins != null; ins = ins.Next) {
if (!(ins.Value is PdtInstruction.PushConstant)) {
exp.IsConstant = false;
}
else if (!(ins.Value is PdtInstruction.PushVariable)) {
exp.IsPotentialConstant = false;
return;
}
}
}
///
/// Revokes the potential constant mark of the current expression.
///
protected void RevokePotentialConstant() {
_revokepttconst = true;
}
internal unsafe void PushConstant(int type, byte[] value) {
fixed (StackFrame* frame = &_stack[_framecount++]) {
frame->Type = type;
frame->Offset = _goffset;
frame->Length = value.Length;
Array.Copy(value, 0, _mem, _goffset, value.Length);
_goffset += value.Length;
}
}
internal unsafe void PushVariable(int name, bool forced) {
fixed (StackFrame* frame = &_stack[_framecount++]) {
byte[] value;
GetVariable(name, forced, out frame->Type, out value);
frame->Offset = _goffset;
frame->Length = value.Length;
Array.Copy(value, 0, _mem, _goffset, value.Length);
_goffset += value.Length;
}
}
///
/// Gets a variable of the specified name.
///
/// The name of the variable.
/// Whether to produce an error stack instead of an identifier stack if the variable is not found.
/// The type of the variable.
/// The value of the variable.
protected abstract void GetVariable(int name, bool forced, out int type, out byte[] value);
internal void Operate(PdtOperatorSignature sig) {
PdtOperator op;
try { op = GetOperator(sig); }
catch (Exception ex) {
for (int i = 0; i < sig.ParamCount; i++)
DiscardStack();
throw new EvaluationFailureException(string.Format("Failed to get operator {0}", sig), ex);
}
Operate(op, sig.ParamCount);
}
///
/// Gets an operator of the specified name and the suggested parameter count.
///
/// The name of the operator.
/// Suggested parameter count.
/// An operator of the specific name.
/// The parameter count of the returned operator does not necessarily equal to .
protected abstract PdtOperator GetOperator(PdtOperatorSignature sig);
unsafe void Operate(PdtOperator op, int pc, bool noset = false) {
fixed (byte* pmem = _mem) {
op.Begin(this, pc);
for (int i = 0; i < pc; i++) {
var frame = _stack[--_framecount];
op.LoadOperand(new PdtVariableMemory(frame.Type, pmem + frame.Offset, frame.Length));
_goffset -= frame.Length;
}
op.Call(pmem + _goffset, noset);
}
}
internal unsafe void Collapse(int name, LinkedListNode target) {
fixed (byte* pmem = _mem) {
var frame = _stack[--_framecount];
if (Collapse(name, new PdtVariableMemory(frame.Type, pmem + frame.Offset, frame.Length))) {
_framecount++;
_rip = target;
}
}
}
///
/// Gets whether to jump to the target of a collapse instruction.
///
/// The name of the collapse operator.
/// The top frame in the stack as the parameter.
/// Whether to jump to the target of the collapse instruction.
protected abstract bool Collapse(int name, PdtVariableMemory param);
internal unsafe PdtVariableMemory StackAlloc(int type, byte* ptr, int len) {
fixed (StackFrame* frame = &_stack[_framecount++]) {
frame->Type = type;
frame->Offset = _goffset;
frame->Length = len;
_goffset += len;
return new PdtVariableMemory(type, ptr, len);
}
}
internal void DiscardStack() {
_goffset -= _stack[--_framecount].Length;
}
}
///
/// The exception that is thrown when the evalution of a fails.
///
public class EvaluationFailureException : Exception {
///
public EvaluationFailureException() : base("Evaluation failed") { }
///
public EvaluationFailureException(string message) : base(message) { }
///
public EvaluationFailureException(string message, Exception innerException) : base(message, innerException) { }
///
protected EvaluationFailureException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
}