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 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 { public int Name { get; private set; } public bool Forced { get; private set; } public PushVariable(int name, bool forced = false) { Name = name; Forced = forced; } public PushVariable(string name, bool forced = false) : this(IdentifierManager.SharedInstance.Request(name)) { Forced = forced; } internal override void Execute(PdtEvaluatorBase etor) { etor.PushVariable(Name, Forced); } public override string ToString() { return string.Format(Forced ? "pushv ?{0}" : "pushv {0}", IdentifierManager.SharedInstance.Retrieve(Name)); } } public class Operate : PdtInstruction { public PdtOperatorSignature Signature { get; private set; } public Operate(int name, int paramCount) { Signature = new PdtOperatorSignature(name, paramCount); } public Operate(string name, int paramCount) { Signature = new PdtOperatorSignature(name, paramCount); } internal override void Execute(PdtEvaluatorBase etor) { etor.Operate(Signature); } public override string ToString() { return string.Format("op {0}", Signature); } } public class Collapse : PdtInstruction { public int Name { get; private set; } public LinkedListNode Target { get; internal set; } public Collapse(string name, LinkedListNode target) { Name = IdentifierManager.SharedInstance.Request(name); Target = target; } internal override void Execute(PdtEvaluatorBase etor) { etor.Collapse(Name, Target); } public override string ToString() { return string.Format("col {0}{{{1}}}", IdentifierManager.SharedInstance.Retrieve(Name), Target.Value); } } } public partial class PdtInterpreter { static readonly Dictionary OP_PRIORITY = new Dictionary { { '@', 7 }, { '*', 6 }, { '/', 6 }, { '%', 6 }, { '+', 5 }, { '-', 5 }, { '=', 4 }, { '<', 4 }, { '>', 4 }, { '!', 3 }, { '&', 2 }, { '|', 1 }, { ',', 0 }, { '$', -1 }, }; static readonly Dictionary OP_TYPE = new Dictionary { { '@', 0 }, { '*', 0 }, { '/', 0 }, { '%', 0 }, { '+', 0 }, { '-', 0 }, { '=', 0 }, { '<', 0 }, { '>', 0 }, { '!', 0 }, { '&', 1 }, { '|', 1 }, { ',', -2 }, { '$', -1 }, }; static readonly 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 static readonly 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 or equal to 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; Dictionary, string> colp = null; pc = 1; if (token == null) t1 = PdtExpToken.EmptyOperator; else { t1 = token.Value; if (OP_TYPE[t1.Value[0]] == 1) colp = new Dictionary, string> { { ins.Last, token.Value.Value } }; } while (true) { t2 = InterpretExpBlock(ins); panic: switch (t2.Type) { case 0x0080: if (OP_TYPE[t1.Value[0]] != -1) { int p1 = OP_PRIORITY[t1.Value[0]]; int p2 = OP_PRIORITY[t2.Value[0]]; if (p2 <= enc) goto exit; if (p2 > p1) { int _; t2 = InterpretExp(ins, p1, out _, t2); goto panic; } switch (OP_TYPE[t1.Value[0]]) { case 0: ins.AddLast(new PdtInstruction.Operate(t1.Value, 2)); break; case 1: colp.Add(ins.Last, t2.Value); break; default: pc++; break; } } if (OP_TYPE[t2.Value[0]] == 1) { colp = new Dictionary, string> { { ins.Last, t2.Value } }; } 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: switch (OP_TYPE[t1.Value[0]]) { case 0: ins.AddLast(new PdtInstruction.Operate(t1.Value, 2)); break; case 1: foreach (var p in colp) ins.AddAfter(p.Key, new PdtInstruction.Collapse(p.Value, ins.Last)); break; case -2: pc++; break; } 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 { var name = buf.Value.Value; if (name[0] == '?') ins.AddLast(new PdtInstruction.PushVariable(name.Substring(1), true)); else ins.AddLast(new PdtInstruction.PushVariable(name)); } 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; } } }