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; /// /// Evaluates an expression and passes the result to a target operator. /// /// The target operator. /// The expression to evaluate. /// Whether the evaluaton succeeded. public bool Evaluate(PdtOperator target, PdtExpression exp) { var prevFrameCount = _framecount; try { _revokepttconst = false; for (var ip = exp.Instructions.First; ip != null; ip = ip.Next) ip.Value.Execute(this, ref ip); if (exp.IsPotentialConstant) { exp.IsConstant = exp.IsPotentialConstant = !_revokepttconst; } var ret = Operate(target, _framecount - prevFrameCount, true); return ret; } catch (Exception ex) { throw new EvaluationFailureException(exp, ex); } finally { for (var i = prevFrameCount; i < _framecount; i++) DiscardStack(); } } /// /// 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; var ip = il.First; while (ip != null) { bool nextFlag = false; var i = ip.Value; if (i is PdtInstruction.Operate iop) { int fc0 = _framecount; int fc1 = iop.Signature.ParamCount; try { i.Execute(this, ref ip); } 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]; if (frame.Type != PdtInternalType.Error) { ReplaceIP(il, ref ip, new PdtInstruction.PushConstant(frame.Type, _mem, frame.Offset, frame.Length), cols); for (var j = 0; j < fc1; j++) il.Remove(ip.Previous); } } } else if (i is PdtInstruction.Collapse t) { try { var pins = ip; i.Execute(this, ref ip); if (_stack[_framecount - 1].Type == PdtInternalType.Error) { throw new EvaluationFailureException(); } if (ip == pins) { ip = ip.Next; il.Remove(ip.Previous); il.Remove(ip.Previous); ip = ip.Previous; if (ip == null) { ip = il.First; nextFlag = true; } } else { ip = pins.Previous; while (ip.Next != t.Target) il.Remove(ip.Next); il.Remove(ip.Next); if (cols.TryGetValue(t.Target, out ct)) { foreach (var u in ct) u.Target = ip; cols.Remove(t.Target); cols.Add(ip, 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, ref ip); var frame = _stack[_framecount - 1]; if (frame.Type != PdtInternalType.Undefined && frame.Type != PdtInternalType.Error) { ReplaceIP(il, ref ip, new PdtInstruction.PushConstant(frame.Type, _mem, frame.Offset, frame.Length), cols); } } else i.Execute(this, ref ip); if (ip != null && cols.TryGetValue(ip, out ct)) { unsafe { fixed (StackFrame* frame = &_stack[_framecount - 1]) { frame->Type = PdtInternalType.Error; frame->Offset = -1; frame->Length = 0; } } } if (!nextFlag) ip = ip.Next; } exp.IsConstant = true; exp.IsPotentialConstant = true; for (var ins = il.First; ins != null; ins = ins.Next) { if (ins.Value is not PdtInstruction.PushConstant) { exp.IsConstant = false; break; } } } void ReplaceIP(LinkedList il, ref LinkedListNode ip, PdtInstruction ins, Dictionary, List> cols) { if (cols.TryGetValue(ip, out List cins)) cols.Remove(ip); ip = il.AddAfter(ip, ins); il.Remove(ip.Previous); if (cins != null) cols.Add(ip, cins); } /// /// 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++]) { GetVariable(name, forced, out frame->Type, out byte[] 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 bool 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]; if (frame.Type == PdtInternalType.Error) { _framecount -= pc - i - 1; _stack[_framecount++] = new StackFrame { Type = PdtInternalType.Error, Offset = -1, Length = 0 }; return false; } op.LoadOperand(new PdtVariableMemory(frame.Type, pmem + frame.Offset, frame.Length)); _goffset -= frame.Length; } op.Call(pmem + _goffset, noset); return true; } } internal unsafe void Collapse(int name, ref LinkedListNode self, LinkedListNode target) { fixed (byte* pmem = _mem) { var frame = _stack[--_framecount]; _goffset -= frame.Length; if (frame.Type == PdtInternalType.Error) { _stack[_framecount++] = new StackFrame { Type = PdtInternalType.Error, Offset = -1, Length = 0 }; self = target; return; } if (Collapse(name, new PdtVariableMemory(frame.Type, pmem + frame.Offset, frame.Length))) { _framecount++; _goffset += frame.Length; self = 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 evaluation 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) { } /// /// Creates an instance of the class with the failing expression. /// /// The failing expression. public EvaluationFailureException(PdtExpression exp) : base("Evaluation failed for the expression: " + exp.ToString()) { } /// /// Creates an instance of the class with the failing expression and the inner exception. /// /// The failing expression. /// The inner exception. public EvaluationFailureException(PdtExpression exp, Exception innerException) : base("Evaluation failed for the expression: " + exp.ToString(), innerException) { } } }