Files
crtr/Assets/Cryville/Common/Pdt/PdtExpression.cs
2022-09-30 17:32:21 +08:00

248 lines
7.9 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 <c>false</c> 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 {
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<T> {
readonly static Dictionary<char, int> pri = new Dictionary<char, int> {
{ '*', 5 }, { '/', 5 }, { '%', 5 },
{ '+', 4 }, { '-', 4 },
{ '=', 3 }, { '<', 3 }, { '>', 3 },
{ '&', 2 },
{ '|', 1 }, { '!', 1 },
{ ',', 0 },
};
readonly static 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 readonly static 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 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;
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;
}
/// <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 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<PdtInstruction> ins, ref bool flag) {
if (flag) ins.AddLast(new PdtInstruction.Operate("*", 2));
else flag = true;
}
}
}