From 20fdfdd313ca949ac708fcf27a137f0f4972d034 Mon Sep 17 00:00:00 2001 From: D'athemon Aidan Date: Sat, 10 Oct 2020 14:53:30 +0300 Subject: [PATCH] AOT safe --- .gitignore | 1 + .../ExpressionLexer.cs | 46 ++++++- .../ExpressionParser.cs | 124 ++++++++++++------ .../NumericArithmeticExpression.cs | 44 +++++-- src/Dahomey.ExpressionEvaluator/Operator.cs | 32 ++++- 5 files changed, 183 insertions(+), 64 deletions(-) diff --git a/.gitignore b/.gitignore index e63cc93..f80bb31 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ _ReSharper*/ src/**/obj/ src/**/bin/ project.lock.json +*.meta diff --git a/src/Dahomey.ExpressionEvaluator/ExpressionLexer.cs b/src/Dahomey.ExpressionEvaluator/ExpressionLexer.cs index 20dbdc9..d70ef24 100644 --- a/src/Dahomey.ExpressionEvaluator/ExpressionLexer.cs +++ b/src/Dahomey.ExpressionEvaluator/ExpressionLexer.cs @@ -32,6 +32,12 @@ internal enum TokenType Mult, Div, Mod, + Pow, + Cos, + Sin, + Tan, + Abs, + Sqrt, BitwiseAnd, BitwiseOr, BitwiseXor, @@ -63,7 +69,7 @@ internal class ExpressionLexer public ExpressionLexer(string expression) { - this.expression = expression; + this.expression = expression.Replace("Pi", $"{Math.PI}").Replace("Pow","").Replace(',','^'); Advance(); NextToken(); } @@ -242,7 +248,31 @@ private bool TryScanName() case "false": currentTokenType = TokenType.False; break; - + + case "Sin": + currentTokenType = TokenType.Sin; + break; + + case "Cos": + currentTokenType = TokenType.Cos; + break; + + case "Tan": + currentTokenType = TokenType.Tan; + break; + + case "Abs": + currentTokenType = TokenType.Abs; + break; + + case "Sqrt": + currentTokenType = TokenType.Sqrt; + break; + + case "Pow": + currentTokenType = TokenType.Pow; + break; + default: currentTokenType = TokenType.Identifier; break; @@ -322,7 +352,15 @@ private bool TryScanOperatorOrPunctuation() case '^': Advance(); - currentTokenType = TokenType.BitwiseXor; + if (currentChar == '^') + { + Advance(); + currentTokenType = TokenType.BitwiseXor; + } + else + { + currentTokenType = TokenType.Pow; + } break; case '~': @@ -470,4 +508,4 @@ public Exception BuildException(Exception innerException, string message, params return BuildException(innerException, string.Format(message, args)); } } -} +} \ No newline at end of file diff --git a/src/Dahomey.ExpressionEvaluator/ExpressionParser.cs b/src/Dahomey.ExpressionEvaluator/ExpressionParser.cs index 9cdc78f..a2717a7 100644 --- a/src/Dahomey.ExpressionEvaluator/ExpressionParser.cs +++ b/src/Dahomey.ExpressionEvaluator/ExpressionParser.cs @@ -303,6 +303,10 @@ private IExpression MultiplicativeExpression() else if (lexer.Accept(TokenType.Mod)) { op = Operator.Mod; + } + else if(lexer.Accept(TokenType.Pow)) + { + op = Operator.Pow; } else { @@ -330,6 +334,46 @@ private IExpression UnaryExpression() LeftExpr = (INumericExpression)PrimaryExpression() }; } + else if (lexer.Accept(TokenType.Cos)) + { + return new NumericArithmeticExpression + { + Operator = Operator.Cos, + LeftExpr = (INumericExpression)PrimaryExpression() + }; + } + else if (lexer.Accept(TokenType.Sin)) + { + return new NumericArithmeticExpression + { + Operator = Operator.Sin, + LeftExpr = (INumericExpression)PrimaryExpression() + }; + } + else if (lexer.Accept(TokenType.Tan)) + { + return new NumericArithmeticExpression + { + Operator = Operator.Tan, + LeftExpr = (INumericExpression)PrimaryExpression() + }; + } + else if (lexer.Accept(TokenType.Abs)) + { + return new NumericArithmeticExpression + { + Operator = Operator.Abs, + LeftExpr = (INumericExpression)PrimaryExpression() + }; + } + else if (lexer.Accept(TokenType.Sqrt)) + { + return new NumericArithmeticExpression + { + Operator = Operator.Sin, + LeftExpr = (INumericExpression)PrimaryExpression() + }; + } else if (lexer.Accept(TokenType.BitwiseComplement)) { return new NumericArithmeticExpression @@ -362,7 +406,6 @@ private IExpression PrimaryExpression() { return VariableExpression(); } - return Literal(); } @@ -413,63 +456,58 @@ private IExpression ElementExpression(IExpression expr) private IExpression VariableOrFunctionExpression() { string identifier = lexer.Identifier(); - - // function - if (lexer.Peek(TokenType.OpenParenthesis)) + Type identifierType; + // variable + if (variableTypes.TryGetValue(identifier, out identifierType)) { - ListExpression argsExpr = (ListExpression)InvocationExpression(); - - Delegate function; - - if (!functions.TryGetValue(identifier, out function)) - { - throw BuildException("Unknown function '{0}()'", identifier); - } - - MethodInfo methodInfo = function.Method; - - if (ReflectionHelper.IsNumber(methodInfo.ReturnType)) + if (ReflectionHelper.IsNumber(identifierType)) { - return new NumericFuncExpression(identifier, function, argsExpr); + return new NumericVariableExpression(identifier, identifierType); } else { - return new ObjectFuncExpression(identifier, function, argsExpr); + return new ObjectVariableExpression(identifier, identifierType); } } - // variable or enum else { - Type identifierType; - // variable - if (variableTypes.TryGetValue(identifier, out identifierType)) + identifierType = ReflectionHelper.GetType(assemblies, identifier); + // enum + + if (identifier != null && identifierType.IsEnum) { - if (ReflectionHelper.IsNumber(identifierType)) - { - return new NumericVariableExpression(identifier, identifierType); - } - else - { - return new ObjectVariableExpression(identifier, identifierType); - } + lexer.Expect(TokenType.Dot); + string enumValue = lexer.Identifier(); + + Enum value = (Enum) Enum.Parse(identifierType, enumValue); + return new EnumLiteralExpression(value); } else - { - identifierType = ReflectionHelper.GetType(assemblies, identifier); - // enum - - if (identifier != null && identifierType.IsEnum) + { + // function + if (lexer.Peek(TokenType.OpenParenthesis)) { - lexer.Expect(TokenType.Dot); - string enumValue = lexer.Identifier(); + ListExpression argsExpr = (ListExpression) InvocationExpression(); - Enum value = (Enum)Enum.Parse(identifierType, enumValue); - return new EnumLiteralExpression(value); - } - else - { - throw BuildException(string.Format("Unknown variable '{0}'", identifier)); + Delegate function; + + if (!functions.TryGetValue(identifier, out function)) + { + throw BuildException("Unknown function '{0}()'", identifier); + } + + MethodInfo methodInfo = function.Method; + + if (ReflectionHelper.IsNumber(methodInfo.ReturnType)) + { + return new NumericFuncExpression(identifier, function, argsExpr); + } + else + { + return new ObjectFuncExpression(identifier, function, argsExpr); + } } + throw BuildException(string.Format("Unknown variable '{0}'", identifier)); } } } diff --git a/src/Dahomey.ExpressionEvaluator/Expressions/NumericArithmeticExpression.cs b/src/Dahomey.ExpressionEvaluator/Expressions/NumericArithmeticExpression.cs index 4f00641..6901c6c 100644 --- a/src/Dahomey.ExpressionEvaluator/Expressions/NumericArithmeticExpression.cs +++ b/src/Dahomey.ExpressionEvaluator/Expressions/NumericArithmeticExpression.cs @@ -23,14 +23,14 @@ public double Evaluate(Dictionary variables) switch (Operator) { case Operator.Plus: - return RightExpr == null ? - LeftExpr.Evaluate(variables) : - LeftExpr.Evaluate(variables) + RightExpr.Evaluate(variables); + return RightExpr == null + ? LeftExpr.Evaluate(variables) + : LeftExpr.Evaluate(variables) + RightExpr.Evaluate(variables); case Operator.Minus: - return RightExpr == null ? - -LeftExpr.Evaluate(variables) : - LeftExpr.Evaluate(variables) - RightExpr.Evaluate(variables); + return RightExpr == null + ? -LeftExpr.Evaluate(variables) + : LeftExpr.Evaluate(variables) - RightExpr.Evaluate(variables); case Operator.Mult: return LeftExpr.Evaluate(variables) * RightExpr.Evaluate(variables); @@ -41,23 +41,41 @@ public double Evaluate(Dictionary variables) case Operator.Mod: return LeftExpr.Evaluate(variables) % RightExpr.Evaluate(variables); + case Operator.Pow: + return Math.Pow(LeftExpr.Evaluate(variables), RightExpr.Evaluate(variables)); + + case Operator.Sin: + return Math.Sin(LeftExpr.Evaluate(variables)); + + case Operator.Cos: + return Math.Cos(LeftExpr.Evaluate(variables)); + + case Operator.Tan: + return Math.Tan(LeftExpr.Evaluate(variables)); + + case Operator.Abs: + return Math.Abs(LeftExpr.Evaluate(variables)); + + case Operator.Sqrt: + return Math.Sqrt(LeftExpr.Evaluate(variables)); + case Operator.BitwiseAnd: - return (int)LeftExpr.Evaluate(variables) & (int)RightExpr.Evaluate(variables); + return (int) LeftExpr.Evaluate(variables) & (int) RightExpr.Evaluate(variables); case Operator.BitwiseOr: - return (int)LeftExpr.Evaluate(variables) | (int)RightExpr.Evaluate(variables); + return (int) LeftExpr.Evaluate(variables) | (int) RightExpr.Evaluate(variables); case Operator.BitwiseXor: - return (int)LeftExpr.Evaluate(variables) ^ (int)RightExpr.Evaluate(variables); + return (int) LeftExpr.Evaluate(variables) ^ (int) RightExpr.Evaluate(variables); case Operator.BitwiseComplement: - return ~(int)LeftExpr.Evaluate(variables); + return ~(int) LeftExpr.Evaluate(variables); case Operator.LeftShift: - return (int)LeftExpr.Evaluate(variables) << (int)RightExpr.Evaluate(variables); + return (int) LeftExpr.Evaluate(variables) << (int) RightExpr.Evaluate(variables); case Operator.RightShift: - return (int)LeftExpr.Evaluate(variables) >> (int)RightExpr.Evaluate(variables); + return (int) LeftExpr.Evaluate(variables) >> (int) RightExpr.Evaluate(variables); default: throw new NotSupportedException(string.Format("operator {0} not support", Operator)); @@ -83,4 +101,4 @@ public override string ToString() return sb.ToString(); } } -} +} \ No newline at end of file diff --git a/src/Dahomey.ExpressionEvaluator/Operator.cs b/src/Dahomey.ExpressionEvaluator/Operator.cs index f9db7aa..d0c63da 100644 --- a/src/Dahomey.ExpressionEvaluator/Operator.cs +++ b/src/Dahomey.ExpressionEvaluator/Operator.cs @@ -15,8 +15,14 @@ public enum Operator Mult, // * Div, // / Mod, // % + Pow, // ^ Minus, // - Plus, // + + Cos, // Cos + Sin, // Sin + Tan, // Tan + Abs, // Abs + Sqrt, // Sqrt LeftShift, // << RightShift, // >> LessThan, // < @@ -26,7 +32,7 @@ public enum Operator Equal, // == NotEqual, // != BitwiseAnd, // & - BitwiseXor, // ^ + BitwiseXor, // ^^ BitwiseOr, // | BitwiseComplement, // ~ Not, // ! @@ -34,7 +40,7 @@ public enum Operator Or, // || } - internal static class OperatorExtension + public static class OperatorExtension { public static string PrettyPrint(this Operator op) { @@ -51,10 +57,28 @@ public static string PrettyPrint(this Operator op) case Operator.Minus: return "-"; + + case Operator.Pow: + return "^"; case Operator.Plus: return "+"; - + + case Operator.Sin: + return "Sin"; + + case Operator.Cos: + return "Cos"; + + case Operator.Abs: + return "Abs"; + + case Operator.Tan: + return "Tan"; + + case Operator.Sqrt: + return "Sqrt"; + case Operator.LeftShift: return "<<"; @@ -86,7 +110,7 @@ public static string PrettyPrint(this Operator op) return "|"; case Operator.BitwiseXor: - return "^"; + return "^^"; case Operator.BitwiseComplement: return "~";