using System; using System.Collections.Generic; using System.Text; namespace Cryville.Common.Pdt { /// /// PDT expression. /// public class PdtExpression { internal LinkedList Instructions; /// /// Whether the value of this expression is constant. /// /// The value of this property is false until it is optimized. public bool IsConstant { get; internal set; } internal bool IsPotentialConstant; internal PdtExpression(LinkedList ins) { Instructions = ins; } public override string ToString() { string r = ""; bool flag = false; foreach (PdtInstruction ins in Instructions) { if (flag) r += "; "; r += ins.ToString(); flag = true; } return r; } } internal abstract class PdtInstruction { internal abstract void Execute(PdtEvaluatorBase etor); public class PushConstant : PdtInstruction { public int Type { get; private set; } public byte[] Value { get; private set; } public PushConstant(int type, byte[] value) { Type = type; Value = value; } public PushConstant(int type, byte[] buffer, int offset, int len) { Type = type; Value = new byte[len]; Array.Copy(buffer, offset, Value, 0, len); } internal override void Execute(PdtEvaluatorBase etor) { etor.PushConstant(Type, Value); } public override string ToString() { return string.Format("pushc ({0:x8}){1}", Type, BitConverter.ToString(Value)); } } public class PushVariable : PdtInstruction { private string m_name; public string Name { get { return m_name; } } public PushVariable(string name) { m_name = name; } internal override void Execute(PdtEvaluatorBase etor) { etor.PushVariable(ref m_name); } public override string ToString() { return string.Format("pushv {0}", Name); } } public class Operate : PdtInstruction { private string m_name; public string Name { get { return m_name; } } public int ParamCount { get; private set; } public Operate(string name, int paramCount) { m_name = name; ParamCount = paramCount; } internal override void Execute(PdtEvaluatorBase etor) { etor.Operate(ref m_name, ParamCount); } public override string ToString() { return string.Format("op {0}({1})", Name, ParamCount); } } } public partial class PdtInterpreter { readonly static Dictionary pri = new Dictionary { { '*', 5 }, { '/', 5 }, { '%', 5 }, { '+', 4 }, { '-', 4 }, { '=', 3 }, { '<', 3 }, { '>', 3 }, { '&', 2 }, { '|', 1 }, { '!', 1 }, { ',', 0 }, }; readonly static PdtExpression _emptyexp; static PdtInterpreter() { var ins = new LinkedList(); ins.AddLast(new PdtInstruction.PushConstant( PdtInternalType.Number, BitConverter.GetBytes(1f) )); _emptyexp = new PdtExpression(ins); } PdtExpToken GetToken() { ws(); var result = new PdtExpToken { Type = ct & 0x0fe0 }; switch (result.Type) { case 0x0020: result.Value = GetIdentifier(); break; case 0x0040: result.Value = GetNumber(); break; case 0x0100: result.Value = GetString(); break; default: result.Value = cc.ToString(); Position++; break; } return result; } private struct PdtExpToken { public int Type { get; set; } public string Value { get; set; } public override string ToString() { return string.Format("0x{0:x4}: {1}", Type, Value); } public readonly static PdtExpToken EmptyOperator = new PdtExpToken { Type = 0x0080, Value = "", }; } /// /// Interprets an expression from the current position and loads it onto an instruction set. /// /// The instruction set. /// /// The enclosing scope type of the (sub)expression. May be one of the following: /// /// -2The expression is in a root scope which ends at a semicolon. /// -1The expression is in a bracketed scope which ends at a closing parenthesis. /// Any non-negative valuesThe expression is in an auxiliary scope which ends before a closing parenthesis or an operator whose priority is lower (less) than the value of this parameter. /// /// /// The parameter count in this (sub)expression. /// The token already parsed but required for the interpretation of the subexpression. /// The expression token following this (sub)expression. PdtExpToken InterpretExp(LinkedList ins, int enc, out int pc, PdtExpToken? token = null) { PdtExpToken t1, t2; int insc0 = ins.Count; pc = 1; if (token == null) t1 = PdtExpToken.EmptyOperator; else t1 = token.Value; while (true) { t2 = InterpretExpBlock(ins); panic: switch (t2.Type) { case 0x0080: if (t1.Value != "") { int p1 = pri[t1.Value[0]]; int p2 = pri[t2.Value[0]]; if (p2 <= enc) goto exit; if (p2 > p1) { int _; t2 = InterpretExp(ins, p1, out _, t2); goto panic; } if (t1.Value != ",") ins.AddLast(new PdtInstruction.Operate(t1.Value, 2)); else pc++; } t1 = t2; break; case 0x0400: if (enc == -2) throw new FormatException("Expression not enclosed correctly: Too many closing brackets"); if (ins.Count == insc0) pc = 0; goto exit; case 0x0800: goto exit; } } exit: if (t1.Value != "," && t1.Value != "") ins.AddLast(new PdtInstruction.Operate(t1.Value, 2)); else if (t1.Value == ",") pc++; return t2; } /// /// Interprets an expression block which consists of several adjacent constants and subexpressions, optionally with a unary operator. /// /// The instruction set. /// The expression token following this expression block. PdtExpToken InterpretExpBlock(LinkedList ins) { var t = GetToken(); if (t.Type == 0x0080) { var r = InterpretExpBlock(ins); ins.AddLast(new PdtInstruction.Operate(t.Value, 1)); return r; } bool flag = false; PdtExpToken? buf = null; while (true) { if (buf != null && t.Type != 0x0200) { PdtExpression def; if (defs.TryGetValue(buf.Value.Value, out def)) { foreach (var i in def.Instructions) ins.AddLast(i); } else ins.AddLast(new PdtInstruction.PushVariable(buf.Value.Value)); buf = null; TryPushAdjMul(ins, ref flag); } switch (t.Type) { case 0x0020: buf = t; break; case 0x0040: float num = float.Parse(t.Value); ins.AddLast(new PdtInstruction.PushConstant(PdtInternalType.Number, BitConverter.GetBytes(num))); break; case 0x0100: int strlen = t.Value.Length; unsafe { var strbuf = new byte[strlen * sizeof(char) + sizeof(int)]; fixed (byte* psuffix = strbuf) { *(int*)psuffix = strlen; } Encoding.Unicode.GetBytes(t.Value, 0, strlen, strbuf, sizeof(int)); ins.AddLast(new PdtInstruction.PushConstant(PdtInternalType.String, strbuf)); } break; case 0x0200: int pc; InterpretExp(ins, -1, out pc); if (buf != null) { ins.AddLast(new PdtInstruction.Operate(buf.Value.Value, pc)); buf = null; } else if (pc > 1) { ins.AddLast(new PdtInstruction.Operate(",", pc)); } else if (pc == 0) throw new FormatException("Empty subexpression"); break; default: return t; } if (buf == null) TryPushAdjMul(ins, ref flag); t = GetToken(); } } void TryPushAdjMul(LinkedList ins, ref bool flag) { if (flag) ins.AddLast(new PdtInstruction.Operate("*", 2)); else flag = true; } } }