296 lines
9.8 KiB
C#
296 lines
9.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
namespace Cryville.Common.Pdt {
|
|
/// <summary>
|
|
/// PDT expression.
|
|
/// </summary>
|
|
public class PdtExpression {
|
|
internal LinkedList<PdtInstruction> Instructions;
|
|
/// <summary>
|
|
/// Whether the value of this expression is constant.
|
|
/// </summary>
|
|
/// <remarks>The value of this property is <see langword="false" /> until it is optimized.</remarks>
|
|
public bool IsConstant { get; internal set; }
|
|
internal bool IsPotentialConstant;
|
|
internal PdtExpression(LinkedList<PdtInstruction> 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<PdtInstruction> Target { get; internal set; }
|
|
public Collapse(string name, LinkedListNode<PdtInstruction> 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<char, int> OP_PRIORITY = new Dictionary<char, int> {
|
|
{ '@', 7 },
|
|
{ '*', 6 }, { '/', 6 }, { '%', 6 },
|
|
{ '+', 5 }, { '-', 5 },
|
|
{ '=', 4 }, { '<', 4 }, { '>', 4 },
|
|
{ '!', 3 },
|
|
{ '&', 2 },
|
|
{ '|', 1 },
|
|
{ ',', 0 },
|
|
{ '$', -1 },
|
|
};
|
|
static readonly Dictionary<char, int> OP_TYPE = new Dictionary<char, int> {
|
|
{ '@', 0 },
|
|
{ '*', 0 }, { '/', 0 }, { '%', 0 },
|
|
{ '+', 0 }, { '-', 0 },
|
|
{ '=', 0 }, { '<', 0 }, { '>', 0 },
|
|
{ '!', 0 },
|
|
{ '&', 1 },
|
|
{ '|', 1 },
|
|
{ ',', -2 },
|
|
{ '$', -1 },
|
|
};
|
|
|
|
static readonly PdtExpression _emptyexp;
|
|
static PdtInterpreter() {
|
|
var ins = new LinkedList<PdtInstruction>();
|
|
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 = "$",
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Interprets an expression from the current position and loads it onto an instruction set.
|
|
/// </summary>
|
|
/// <param name="ins">The instruction set.</param>
|
|
/// <param name="enc">
|
|
/// <para>The enclosing scope type of the (sub)expression. May be one of the following:</para>
|
|
/// <list type="bullet">
|
|
/// <item><term><c>-2</c></term><description>The expression is in a root scope which ends at a semicolon.</description></item>
|
|
/// <item><term><c>-1</c></term><description>The expression is in a bracketed scope which ends at a closing parenthesis.</description></item>
|
|
/// <item><term>Any non-negative values</term><description>The 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.</description></item>
|
|
/// </list>
|
|
/// </param>
|
|
/// <param name="pc">The parameter count in this (sub)expression.</param>
|
|
/// <param name="token">The token already parsed but required for the interpretation of the subexpression.</param>
|
|
/// <returns>The expression token following this (sub)expression.</returns>
|
|
PdtExpToken InterpretExp(LinkedList<PdtInstruction> ins, int enc, out int pc, PdtExpToken? token = null) {
|
|
PdtExpToken t1, t2;
|
|
int insc0 = ins.Count;
|
|
Dictionary<LinkedListNode<PdtInstruction>, 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<LinkedListNode<PdtInstruction>, 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<LinkedListNode<PdtInstruction>, 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Interprets an expression block which consists of several adjacent constants and subexpressions, optionally with a unary operator.
|
|
/// </summary>
|
|
/// <param name="ins">The instruction set.</param>
|
|
/// <returns>The expression token following this expression block.</returns>
|
|
PdtExpToken InterpretExpBlock(LinkedList<PdtInstruction> 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<PdtInstruction> ins, ref bool flag) {
|
|
if (flag) ins.AddLast(new PdtInstruction.Operate("*", 2));
|
|
else flag = true;
|
|
}
|
|
}
|
|
}
|