using Cryville.Common.Pdt; using System; using System.Collections.Generic; using UnityEngine; namespace Cryville.Crtr { public class PdtEvaluator : PdtEvaluatorBase { static readonly Dictionary _shortops = new Dictionary(); static readonly Dictionary _longops = new Dictionary(); readonly Dictionary _ctxops = new Dictionary(); struct OperatorSignature : IEquatable { public string Name { get; private set; } public int ParamCount { get; private set; } readonly int _hash; public OperatorSignature(string name, int paramCount) { Name = name; ParamCount = paramCount; _hash = name.GetHashCode() ^ paramCount; } public override bool Equals(object obj) { if (!(obj is OperatorSignature)) return false; return Equals((OperatorSignature)obj); } public bool Equals(OperatorSignature other) { return Name == other.Name && ParamCount == other.ParamCount; } public override int GetHashCode() { return _hash; } } readonly byte[] _numbuf = new byte[4]; protected override void GetVariable(string name, out int type, out byte[] value) { switch (name) { case "w": LoadNum(ChartPlayer.hitRect.width); type = PdtInternalType.Number; value = _numbuf; return; case "h": LoadNum(ChartPlayer.hitRect.height); type = PdtInternalType.Number; value = _numbuf; return; case "true": LoadNum(1); type = PdtInternalType.Number; value = _numbuf; return; case "false": LoadNum(0); type = PdtInternalType.Number; value = _numbuf; return; default: PropSrc prop; string str; if (ContextEvent != null && ContextEvent.PropSrcs.TryGetValue(name, out prop)) { prop.Get(out type, out value); } else if (ContextJudge != null && ContextJudge.GetFormattedScoreStrings().TryGetValue(name, out str)) { type = PdtInternalType.String; value = GetBytes(str); RevokePotentialConstant(); } else { PropSrc.Arbitrary result; foreach (var cas in ContextCascade) { if (cas.TryGetValue(name, out result)) { result.Get(out type, out value); return; } } type = PdtInternalType.Undefined; value = GetBytes(name); } return; } } unsafe void LoadNum(float value) { fixed (byte* ptr = _numbuf) *(float*)ptr = value; } unsafe byte[] GetBytes(string value) { int strlen = value.Length; byte[] result = new byte[strlen * sizeof(char) + sizeof(int)]; fixed (byte* _ptr = result) { char* ptr = (char*)(_ptr + sizeof(int)); *(int*)_ptr = strlen; int i = 0; foreach (var c in value) ptr[i++] = c; } return result; } protected override PdtOperator GetOperator(string name, int pc) { PdtOperator result; if (name.Length == 1 && _shortops.TryGetValue(new OperatorSignature(name, pc), out result)) { return result; } else if (name == ",") { result = new op_arr(pc); _shortops.Add(new OperatorSignature(",", pc), result); return result; } else if (_longops.TryGetValue(name, out result)) { return result; } else if (_ctxops.TryGetValue(name, out result)) { return result; } else throw new KeyNotFoundException(string.Format("Undefined operator {0}({1})", name, pc)); } protected override bool Collapse(string name, PdtVariableMemory param) { switch (name) { case "&": return param.AsNumber() == 0; case "|": return param.AsNumber() != 0; default: throw new KeyNotFoundException(string.Format("Undefined collapse operator {0}", name)); } } public ChartEvent ContextEvent { private get; set; } public Transform ContextTransform { private get; set; } public Judge ContextJudge { private get; set; } public PropSrc ContextSelfValue { private get; set; } readonly List> ContextCascade = new List>(); public void ContextCascadeInsert() { ContextCascade.Add(new Dictionary()); } public void ContextCascadeUpdate(string key, PropSrc.Arbitrary value) { ContextCascade[ContextCascade.Count - 1][key] = value; } public void ContextCascadeDiscard() { ContextCascade.RemoveAt(ContextCascade.Count - 1); } public PdtEvaluator() { _ctxops.Add("screen_edge", new func_screen_edge(() => ContextTransform)); _ctxops.Add("int", new func_int(() => ContextSelfValue)); _ctxops.Add("clamp", new func_clamp(() => ContextSelfValue)); _ctxops.Add("min", new func_min(() => ContextSelfValue)); _ctxops.Add("max", new func_max(() => ContextSelfValue)); } static PdtEvaluator() { _shortops.Add(new OperatorSignature("*", 2), new op_mul_2()); _shortops.Add(new OperatorSignature("/", 2), new op_div_2()); _shortops.Add(new OperatorSignature("%", 2), new op_mod_2()); _shortops.Add(new OperatorSignature("+", 1), new op_add_1()); _shortops.Add(new OperatorSignature("+", 2), new op_add_2()); _shortops.Add(new OperatorSignature("-", 1), new op_sub_1()); _shortops.Add(new OperatorSignature("-", 2), new op_sub_2()); _shortops.Add(new OperatorSignature("=", 2), new op_eq_2()); _shortops.Add(new OperatorSignature("<", 2), new op_lt_2()); _shortops.Add(new OperatorSignature(">", 2), new op_gt_2()); _shortops.Add(new OperatorSignature("!", 1), new op_not_1()); _longops.Add("frame_seq", new func_frame_seq()); } #region Operators #pragma warning disable IDE1006 class op_add_1 : PdtOperator { public op_add_1() : base(1) { } protected override void Execute() { float result = GetOperand(0).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_add_2 : PdtOperator { public op_add_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() + GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_sub_1 : PdtOperator { public op_sub_1() : base(1) { } protected override void Execute() { float result = -GetOperand(0).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_sub_2 : PdtOperator { public op_sub_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() - GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_mul_2 : PdtOperator { public op_mul_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() * GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_div_2 : PdtOperator { public op_div_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() / GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_mod_2 : PdtOperator { public op_mod_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() % GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_eq_2 : PdtOperator { public op_eq_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() == GetOperand(1).AsNumber() ? 1 : 0; GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_lt_2 : PdtOperator { public op_lt_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() < GetOperand(1).AsNumber() ? 1 : 0; GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_gt_2 : PdtOperator { public op_gt_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() > GetOperand(1).AsNumber() ? 1 : 0; GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_not_1 : PdtOperator { public op_not_1() : base(1) { } protected override void Execute() { float result = GetOperand(0).AsNumber() == 0 ? 1 : 0; GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_arr : PdtOperator { public op_arr(int pc) : base(pc) { } protected override void Execute() { var o0 = GetOperand(0); int type = o0.Type; int len = o0.Length; bool blit = !IsBlittable(type); for (var i = 1; i < ParamCount; i++) { var o = GetOperand(i); if (o.Type != type) throw new InvalidOperationException("Cannot create variant type array"); else if (!IsBlittable(o.Type)) blit = true; len += o.Length; } if (blit) GetReturnFrame(PdtInternalType.Array, len + 2 * sizeof(int)).SetArraySuffix(type, ParamCount); else GetReturnFrame(PdtInternalType.Vector, len + sizeof(int)).SetArraySuffix(type); } bool IsBlittable(int type) { return type == PdtInternalType.Number; } } class func_frame_seq : PdtOperator { public func_frame_seq() : base(3) { } protected override unsafe void Execute() { string pf = GetOperand(0).AsString(); int f = (int)GetOperand(1).AsNumber(); int t = (int)GetOperand(2).AsNumber(); int pfl = pf.Length; int fc = t - f + 1; if (fc <= 0) throw new ArgumentException("Start index is greater than end index"); int l = (f == 0 ? 1 : 0) + pfl * fc; for (int p = 1, i = 1; p < 4; p++, i *= 10) { int j = i * 10 - 1; if (f <= j && t >= i) { int m = Math.Max(f, i); int n = Math.Min(t, j); l += (n - m + 1) * i; } } l *= sizeof(char); l += sizeof(int) * (fc + 2); var ret = GetReturnFrame(PdtInternalType.Array, l); ret.SetArraySuffix(PdtInternalType.String, fc); int o = 0; for (int i = f; i <= t; i++) { var s = pf + i.ToString(); ret.SetString(s, o); o += sizeof(int) + s.Length * sizeof(char); } } } class func_screen_edge : PdtOperator { readonly Func _ctxcb; public func_screen_edge(Func ctxcb) : base(1) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ctx = _ctxcb(); float dist; var ray = new Ray(ctx.position, ctx.rotation * Vector3.forward); ChartPlayer.frustumPlanes[(int)GetOperand(0).AsNumber()].Raycast(ray, out dist); var ret = GetReturnFrame(PdtInternalType.Vector, sizeof(Vector3) + sizeof(int)); var ptr = (Vector3*)ret.TrustedAsOfLength(sizeof(Vector3) + sizeof(int)); *ptr++ = ray.GetPoint(dist); *(int*)ptr = PdtInternalType.Number; } } class func_int : PdtOperator { readonly Func _ctxcb; public func_int(Func ctxcb) : base(1) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); float v; switch (LoadedOperandCount) { case 0: v = oputil.AsNumber(_ctxcb()); break; case 1: v = GetOperand(0).AsNumber(); break; default: throw new ArgumentException("Argument count not 0 or 1"); } ret.SetNumber(Mathf.Floor(v)); } } class func_clamp : PdtOperator { readonly Func _ctxcb; public func_clamp(Func ctxcb) : base(3) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); float min = GetOperand(0).AsNumber(); float value, max; switch (LoadedOperandCount) { case 2: value = oputil.AsNumber(_ctxcb()); max = GetOperand(1).AsNumber(); break; case 3: value = GetOperand(1).AsNumber(); max = GetOperand(2).AsNumber(); break; default: throw new ArgumentException("Argument count not 2 or 3"); } ret.SetNumber(Mathf.Clamp(value, min, max)); } } class func_min : PdtOperator { readonly Func _ctxcb; public func_min(Func ctxcb) : base(2) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); float a = GetOperand(0).AsNumber(); float b; switch (LoadedOperandCount) { case 1: b = oputil.AsNumber(_ctxcb()); break; case 2: b = GetOperand(1).AsNumber(); break; default: throw new ArgumentException("Argument count not 2 or 3"); } ret.SetNumber(Mathf.Min(a, b)); } } class func_max : PdtOperator { readonly Func _ctxcb; public func_max(Func ctxcb) : base(2) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); var a = GetOperand(0).AsNumber(); float b; switch (LoadedOperandCount) { case 1: b = oputil.AsNumber(_ctxcb()); break; case 2: b = GetOperand(1).AsNumber(); break; default: throw new ArgumentException("Argument count not 2 or 3"); } ret.SetNumber(Mathf.Max(a, b)); } } unsafe static class oputil { public static float AsNumber(PropSrc src) { int type; byte[] value; src.Get(out type, out value); if (type != PdtInternalType.Number) throw new ArgumentException("Not a number"); fixed (byte* ptr = value) { return *(float*)ptr; } } } #pragma warning restore IDE1006 #endregion } }