public double ExecAst(IAstNode root) { switch (root) { case AstNumberNode numberNode: return(numberNode.Number); case AstVariableNode varNode: if (Constants.TryGetValue(varNode.Id, out var val)) { return((double)val); } else if (Variables.TryGetValue(varNode.Id, out val)) { return((double)val); } else { //Sync.Tools.IO.CurrentIO.WriteColor($"[RTPP:Expression]No Variable found (return zero). Variable name: { varNode.Id }", ConsoleColor.Yellow); return(0); } case AstOpNode opNode: switch (opNode.Op) { case "+": return(ExecAst(opNode.LNode) + ExecAst(opNode.RNode)); case "-": return(ExecAst(opNode.LNode) - ExecAst(opNode.RNode)); case "*": return(ExecAst(opNode.LNode) * ExecAst(opNode.RNode)); case "/": return(ExecAst(opNode.LNode) / ExecAst(opNode.RNode)); case "%": return(ExecAst(opNode.LNode) % ExecAst(opNode.RNode)); case "^": return(Math.Pow(ExecAst(opNode.LNode), ExecAst(opNode.RNode))); case ">": return((ExecAst(opNode.LNode) > ExecAst(opNode.RNode)) ? 1 : 0); case "<": return((ExecAst(opNode.LNode) < ExecAst(opNode.RNode)) ? 1 : 0); case ">=": return((ExecAst(opNode.LNode) >= ExecAst(opNode.RNode)) ? 1 : 0); case "<=": return((ExecAst(opNode.LNode) <= ExecAst(opNode.RNode)) ? 1 : 0); case "==": return(IsNotZero(ExecAst(opNode.LNode) - ExecAst(opNode.RNode)) ? 0 : 1); case "!=": return(IsNotZero(ExecAst(opNode.LNode) - ExecAst(opNode.RNode)) ? 1 : 0); case "!": return(IsNotZero(ExecAst(opNode.LNode)) ? 0 : 1); case "&&": return((IsNotZero(ExecAst(opNode.LNode)) && IsNotZero(ExecAst(opNode.RNode))) ? 1 : 0); case "||": return((IsNotZero(ExecAst(opNode.LNode)) || IsNotZero(ExecAst(opNode.RNode))) ? 1 : 0); } break; case AstFunctionNode funcNode: try { if (funcNode.Id == "set") { AstVariableNode varNode = funcNode.Args[0] as AstVariableNode; string varName = varNode?.Id ?? throw new ExpressionException($"The \"{funcNode.Id}()\" first parameter is the variable name."); double varVal = ExecAst(funcNode.Args[1]); Variables[varName] = ValueBase.Create(varVal); return(0); } else if (funcNode.Id == "if") { IAstNode condNode = funcNode.Args[0]; double condNodeResult = ExecAst(condNode); if (Math.Abs(condNodeResult) <= 1e-5) { return(ExecAst(funcNode.Args[2])); } else { return(ExecAst(funcNode.Args[1])); } } else if (funcNode.Id == "smooth") { /* Todo? * AstVariableNode varNode = funcNode.Args[0] as AstVariableNode; * string varName = varNode?.Id ?? throw new ExpressionException($"The \"{funcNode.Id}()\" first parameter is the variable name."); * double varVal = ExecAst(funcNode.Args[0]); * * return SmoothMath.SmoothVariable(varName, varVal); */ throw new ExpressionException($"not support function smooth() yet"); } else { if (Functions.TryGetValue(funcNode.Id, out var func)) { return(func(funcNode.Args.Select(x => ValueBase.Create(ExecAst(x))).OfType <ValueBase>().ToList())); } else { throw new ExpressionException($"No function found. Fucntion: {funcNode.Id}"); } } } catch (ArgumentOutOfRangeException) { throw new ExpressionException($"The function is missing a parameter. Fucntion: {funcNode.Id}"); } } return(double.NaN); }
public Func <ValueBase> ConvertAstToComplexLambda(IAstNode root, Func <string, ValueBase> variableRequireFunc, Func <string, List <ValueBase>, ValueBase> funcRequireFunc) { switch (root) { case AstStringNode stringNode: return(() => ValueBase.Create(stringNode.String)); case AstNumberNode numberNode: return(() => ValueBase.Create(numberNode.Number)); case AstVariableNode varNode: if (Constants.TryGetValue(varNode.Id, out var val)) { return(() => variableRequireFunc(varNode.Id)); } else if (Variables.TryGetValue(varNode.Id, out val)) { return(() => variableRequireFunc(varNode.Id)); } else { return(() => NumberValue.Zero); } case AstOpNode opNode: var leftValue = ConvertAstToComplexLambda(opNode.LNode, variableRequireFunc, funcRequireFunc); var rightValue = ConvertAstToComplexLambda(opNode.RNode, variableRequireFunc, funcRequireFunc); ValueBase NumberOpCheck(Func <double, double, double> func) { var a = leftValue(); if (a.ValueType != ValueBase.Type.Number) { throw new ExpressionException("Invaild Number Operations."); } var b = rightValue(); if (b.ValueType != ValueBase.Type.Number) { throw new ExpressionException("Invaild Number Operations."); } return(ValueBase.Create(func(a, b))); } ValueBase BoolOpCheck(Func <double, double, bool> func) { var a = leftValue(); if (a.ValueType != ValueBase.Type.Number) { throw new ExpressionException("Invaild Number Operations."); } var b = rightValue(); if (b.ValueType != ValueBase.Type.Number) { throw new ExpressionException("Invaild Number Operations."); } return(func(a, b) ? NumberValue.One : NumberValue.Zero); } ValueBase NumberOpCheck2(Func <double, double> func) { var a = leftValue(); if (a.ValueType != ValueBase.Type.Number) { throw new ExpressionException("Invaild Number Operations."); } return(ValueBase.Create(func(a))); } switch (opNode.Op) { case "+": return(() => { var a = leftValue(); var b = rightValue(); if (a.ValueType == ValueBase.Type.String || b.ValueType == ValueBase.Type.String) { return ValueBase.Create(a.ValueToString() + b.ValueToString()); } return ValueBase.Create(((NumberValue)a).Value + ((NumberValue)b).Value); }); case "-": return(() => NumberOpCheck((a, b) => a - b)); case "*": return(() => NumberOpCheck((a, b) => a * b)); case "/": return(() => NumberOpCheck((a, b) => a / b)); case "%": return(() => NumberOpCheck((a, b) => a % b)); case "^": return(() => NumberOpCheck((a, b) => Math.Pow(a, b))); case ">": return(() => BoolOpCheck((a, b) => a > b)); case "<": return(() => BoolOpCheck((a, b) => a < b)); case ">=": return(() => BoolOpCheck((a, b) => a >= b)); case "<=": return(() => BoolOpCheck((a, b) => a <= b)); case "==": return(() => BoolOpCheck((a, b) => a == b)); case "!=": return(() => BoolOpCheck((a, b) => a != b)); case "!": return(() => NumberOpCheck2(a => IsNotZero(a) ? NumberValue.One : NumberValue.Zero)); case "&&": return(() => BoolOpCheck((a, b) => IsNotZero(a))); case "||": return(() => BoolOpCheck((a, b) => IsNotZero(a))); } break; case AstFunctionNode funcNode: try { if (funcNode.Id == "set") { AstVariableNode varNode = funcNode.Args[0] as AstVariableNode; string varName = varNode?.Id ?? throw new ExpressionException($"The \"{funcNode.Id}()\" first parameter is the variable name."); var varVal = ConvertAstToComplexLambda(funcNode.Args[1], variableRequireFunc, funcRequireFunc)(); Variables[varName] = varVal; return(() => NumberValue.Zero); } else if (funcNode.Id == "if") { var condExpr = ConvertAstToComplexLambda(funcNode.Args[0], variableRequireFunc, funcRequireFunc); var falseResult = ConvertAstToComplexLambda(funcNode.Args[2], variableRequireFunc, funcRequireFunc); var trueResult = ConvertAstToComplexLambda(funcNode.Args[1], variableRequireFunc, funcRequireFunc); return(() => condExpr() <= 1e-5 ? falseResult() : trueResult()); } else if (funcNode.Id == "smooth") { /* Todo? * AstVariableNode varNode = funcNode.Args[0] as AstVariableNode; * string varName = varNode?.Id ?? throw new ExpressionException($"The \"{funcNode.Id}()\" first parameter is the variable name."); * double varVal = ExecAst(funcNode.Args[0]); * * return SmoothMath.SmoothVariable(varName, varVal); */ throw new ExpressionException($"not support function smooth() yet"); } else { return(() => funcRequireFunc(funcNode.Id, funcNode.Args.Select(arg => ConvertAstToComplexLambda(arg, variableRequireFunc, funcRequireFunc)()).ToList())); } } catch (ArgumentOutOfRangeException) { throw new ExpressionException($"The function is missing some parameters. Fucntion: {funcNode.Id}"); } } return(() => NumberValue.Nan); }