From e273ced6ae02e6887052fe7224239b94e1e49412 Mon Sep 17 00:00:00 2001 From: rameel Date: Fri, 21 Feb 2025 12:49:13 +0500 Subject: [PATCH 1/6] Simplify handlers --- .../ExpressionParser.Parser.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Ramstack.ExpressionParser/ExpressionParser.Parser.cs b/src/Ramstack.ExpressionParser/ExpressionParser.Parser.cs index d4325ff..8656c7d 100644 --- a/src/Ramstack.ExpressionParser/ExpressionParser.Parser.cs +++ b/src/Ramstack.ExpressionParser/ExpressionParser.Parser.cs @@ -187,57 +187,57 @@ static Expr HijackMemberExpression(Expr result, Expr value) var mul_expression = prefix_expression.Fold( - OneOf("*/%").ThenIgnore(S).Map(CreateIdentifier), + OneOf("*/%").ThenIgnore(S).Do(CreateIdentifier), (lhs, rhs, op) => new Expr.Binary(op, lhs, rhs)); var add_expression = mul_expression.Fold( - OneOf("+-").ThenIgnore(S).Map(CreateIdentifier), + OneOf("+-").ThenIgnore(S).Do(CreateIdentifier), (lhs, rhs, op) => new Expr.Binary(op, lhs, rhs)); var shift_expression = add_expression.Fold( - OneOf(["<<", ">>", ">>>"]).ThenIgnore(S).Map(CreateIdentifier), + OneOf(["<<", ">>", ">>>"]).ThenIgnore(S).Do(CreateIdentifier), (lhs, rhs, op) => new Expr.Binary(op, lhs, rhs)); var relational_expression = shift_expression.Fold( - OneOf(["<", ">", "<=", ">="]).ThenIgnore(S).Map(CreateIdentifier), + OneOf(["<", ">", "<=", ">="]).ThenIgnore(S).Do(CreateIdentifier), (lhs, rhs, op) => new Expr.Binary(op, lhs, rhs)); var equality_expression = relational_expression.Fold( - OneOf("==", "!=").ThenIgnore(S).Map(CreateIdentifier), + OneOf("==", "!=").ThenIgnore(S).Do(CreateIdentifier), (lhs, rhs, op) => new Expr.Binary(op, lhs, rhs)); var bitwise_and_expression = equality_expression.Fold( - L('&').ThenIgnore(S).Map(CreateIdentifier), + L('&').ThenIgnore(S).Do(CreateIdentifier), (lhs, rhs, op) => new Expr.Binary(op, lhs, rhs)); var bitwise_xor_expression = bitwise_and_expression.Fold( - L('^').ThenIgnore(S).Map(CreateIdentifier), + L('^').ThenIgnore(S).Do(CreateIdentifier), (lhs, rhs, op) => new Expr.Binary(op, lhs, rhs)); var bitwise_or_expression = bitwise_xor_expression.Fold( - L('|').ThenIgnore(S).Map(CreateIdentifier), + L('|').ThenIgnore(S).Do(CreateIdentifier), (lhs, rhs, op) => new Expr.Binary(op, lhs, rhs)); var logical_and_expression = bitwise_or_expression.Fold( - L("&&").ThenIgnore(S).Map(CreateIdentifier), + L("&&").ThenIgnore(S).Do(CreateIdentifier), (lhs, rhs, op) => new Expr.Binary(op, lhs, rhs)); var logical_or_expression = logical_and_expression.Fold( - L("||").ThenIgnore(S).Map(CreateIdentifier), + L("||").ThenIgnore(S).Do(CreateIdentifier), (lhs, rhs, op) => new Expr.Binary(op, lhs, rhs)); var null_coalesce_expression = logical_or_expression.FoldR( - L("??").ThenIgnore(S).Map(CreateIdentifier), + L("??").ThenIgnore(S).Do(CreateIdentifier), (lhs, rhs, op) => new Expr.Binary(op, lhs, rhs)); var conditional_expression = Deferred(); @@ -261,9 +261,9 @@ static Expr HijackMemberExpression(Expr result, Expr value) return S.Then(expression); } - private static Identifier CreateIdentifier(Match m, string v) => + private static Identifier CreateIdentifier(string v) => new(v); - private static Identifier CreateIdentifier(Match m, char v) => + private static Identifier CreateIdentifier(char v) => new(new string(v, 1)); } From b03b6276cc83f9d46731b07b12abeffdd6a6fd72 Mon Sep 17 00:00:00 2001 From: rameel Date: Fri, 21 Feb 2025 16:43:35 +0500 Subject: [PATCH 2/6] Remove trailing whitespace and normalize line endings --- src/Ramstack.ExpressionParser/Binder.cs | 2 +- .../ExpressionBuilder.Helpers.cs | 21 ++- .../Expressions/Expr.Unary.cs | 2 +- .../Expressions/Expr.cs | 64 +++---- .../Expressions/ExprKind.cs | 158 +++++++++--------- .../Expressions/ExprVisitor.cs | 15 +- .../Expressions/UnaryType.cs | 94 +++++------ .../ParseErrorException.cs | 4 +- .../Parsers/ConstantNumberParser.cs | 8 +- 9 files changed, 181 insertions(+), 187 deletions(-) diff --git a/src/Ramstack.ExpressionParser/Binder.cs b/src/Ramstack.ExpressionParser/Binder.cs index 5614594..21fc014 100644 --- a/src/Ramstack.ExpressionParser/Binder.cs +++ b/src/Ramstack.ExpressionParser/Binder.cs @@ -18,7 +18,7 @@ public abstract class Binder /// /// The representing the name of the requested type. /// - /// The instance corresponding to the specified + /// The instance corresponding to the specified /// if resolved successfully; otherwise, . /// public abstract Type? BindToType(Identifier name); diff --git a/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs b/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs index 6f46b63..6183e93 100644 --- a/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs +++ b/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs @@ -71,9 +71,14 @@ private static List CreateInjectingArguments(MethodInfo method, IRea var paramArrayType = parameter.ParameterType.GetElementType()!; for (; i < args.Count; i++) - paramArray.Add(Convert(args[i], paramArrayType)); + paramArray.Add( + Convert(args[i], paramArrayType)); + + list.Add( + Expression.NewArrayInit( + paramArrayType, + paramArray)); - list.Add(Expression.NewArrayInit(paramArrayType, paramArray)); break; } @@ -128,35 +133,35 @@ private static Expression ApplyBinaryExpression(Identifier op, Func>>") { if (lhs.Type == typeof(int)) return Expression.Convert( apply(Expression.Convert(lhs, typeof(uint)), rhs), typeof(int)); - + if (lhs.Type == typeof(long)) return Expression.Convert( apply(Expression.Convert(lhs, typeof(ulong)), rhs), typeof(long)); } - + return apply(lhs, rhs); } diff --git a/src/Ramstack.ExpressionParser/Expressions/Expr.Unary.cs b/src/Ramstack.ExpressionParser/Expressions/Expr.Unary.cs index 04b8fc7..cfc3060 100644 --- a/src/Ramstack.ExpressionParser/Expressions/Expr.Unary.cs +++ b/src/Ramstack.ExpressionParser/Expressions/Expr.Unary.cs @@ -3,7 +3,7 @@ namespace Ramstack.Parsing.Expressions; partial class Expr { /// - /// Represents an unary operation expression in an expression tree, consisting of an operator and a single operand. + /// Represents a unary operation expression in an expression tree, consisting of an operator and a single operand. /// /// The representing the unary operator, e.g., "-" or "int". /// The specifying the kind of unary operation (e.g., arithmetic or conversion). diff --git a/src/Ramstack.ExpressionParser/Expressions/Expr.cs b/src/Ramstack.ExpressionParser/Expressions/Expr.cs index 59ad12a..26b189b 100644 --- a/src/Ramstack.ExpressionParser/Expressions/Expr.cs +++ b/src/Ramstack.ExpressionParser/Expressions/Expr.cs @@ -1,32 +1,32 @@ -namespace Ramstack.Parsing.Expressions; - -/// -/// Represents a node in an expression tree, serving as the base class for all expression types. -/// -public abstract partial class Expr(ExprKind kind) -{ - /// - /// Gets the kind of this expression node. - /// - /// - /// An value indicating the type of expression, such as a literal, binary operation, or reference. - /// - public ExprKind Kind { get; init; } = kind; - - /// - /// Deconstructs the expression into its kind. - /// - /// The of this expression node, output as an enumeration value. - public void Deconstruct(out ExprKind kind) => - kind = Kind; - - /// - /// Accepts a visitor to process this expression node and return a result of type . - /// - /// The type of the result produced by the visitor. - /// The instance that processes this expression. - /// - /// A result of type as determined by the visitor's implementation. - /// - public abstract T Accept(ExprVisitor visitor); -} +namespace Ramstack.Parsing.Expressions; + +/// +/// Represents a node in an expression tree, serving as the base class for all expression types. +/// +public abstract partial class Expr(ExprKind kind) +{ + /// + /// Gets the kind of this expression node. + /// + /// + /// An value indicating the type of expression, such as a literal, binary operation, or reference. + /// + public ExprKind Kind { get; init; } = kind; + + /// + /// Deconstructs the expression into its kind. + /// + /// The of this expression node, output as an enumeration value. + public void Deconstruct(out ExprKind kind) => + kind = Kind; + + /// + /// Accepts a visitor to process this expression node and return a result of type . + /// + /// The type of the result produced by the visitor. + /// The instance that processes this expression. + /// + /// A result of type as determined by the visitor's implementation. + /// + public abstract T Accept(ExprVisitor visitor); +} diff --git a/src/Ramstack.ExpressionParser/Expressions/ExprKind.cs b/src/Ramstack.ExpressionParser/Expressions/ExprKind.cs index 04791ca..1ec4735 100644 --- a/src/Ramstack.ExpressionParser/Expressions/ExprKind.cs +++ b/src/Ramstack.ExpressionParser/Expressions/ExprKind.cs @@ -1,79 +1,79 @@ -namespace Ramstack.Parsing.Expressions; - -/// -/// Defines the possible types of nodes in an expression tree used by the expression parser. -/// -public enum ExprKind -{ - /// - /// Represents a reference to a variable, parameter, or member in the expression. - /// - /// - /// In the expression x, this would indicate a reference to the variable "x". - /// - Reference, - - /// - /// Represents a constant value, such as a number or string literal. - /// - /// - /// In the expression 42 or "hello", this denotes a literal value. - /// - Literal, - - /// - /// Represents a binary operation, such as addition or subtraction, involving two operands. - /// - /// - /// In the expression a + b, this indicates the addition operation. - /// - Binary, - - /// - /// Represents a method invocation within the expression. - /// - /// - /// In the expression Math.Abs(-5), this denotes the method call to "Abs". - /// - Call, - - /// - /// Represents an indexing operation or access to a parameterized property. - /// - /// - /// In the expression array[0], this indicates an index operation. - /// - Index, - - /// - /// Represents a read operation on a field or property of an object. - /// - /// - /// In the expression obj.Property, this denotes accessing the "Property" member. - /// - MemberAccess, - - /// - /// Represents a unary operation, such as negation or logical NOT, involving a single operand. - /// - /// - /// In the expression -x, this indicates the negation operation. - /// - Unary, - - /// - /// Represents a conditional (ternary) operation with a condition, true branch, and false branch. - /// - /// - /// In the expression a > b ? a : b, this denotes a conditional operation. - /// - Conditional, - - /// - /// Represents an expression enclosed in parentheses to enforce precedence or grouping. - /// - /// - /// In the expression (a + b), this indicates a parenthesized sub-expression. - /// - Parenthesized -} +namespace Ramstack.Parsing.Expressions; + +/// +/// Defines the possible types of nodes in an expression tree used by the expression parser. +/// +public enum ExprKind +{ + /// + /// Represents a reference to a variable, parameter, or member in the expression. + /// + /// + /// In the expression x, this would indicate a reference to the variable "x". + /// + Reference, + + /// + /// Represents a constant value, such as a number or string literal. + /// + /// + /// In the expression 42 or "hello", this denotes a literal value. + /// + Literal, + + /// + /// Represents a binary operation, such as addition or subtraction, involving two operands. + /// + /// + /// In the expression a + b, this indicates the addition operation. + /// + Binary, + + /// + /// Represents a method invocation within the expression. + /// + /// + /// In the expression Math.Abs(-5), this denotes the method call to "Abs". + /// + Call, + + /// + /// Represents an indexing operation or access to a parameterized property. + /// + /// + /// In the expression array[0], this indicates an index operation. + /// + Index, + + /// + /// Represents a read operation on a field or property of an object. + /// + /// + /// In the expression obj.Property, this denotes accessing the "Property" member. + /// + MemberAccess, + + /// + /// Represents a unary operation, such as negation or logical NOT, involving a single operand. + /// + /// + /// In the expression -x, this indicates the negation operation. + /// + Unary, + + /// + /// Represents a conditional (ternary) operation with a condition, true branch, and false branch. + /// + /// + /// In the expression a > b ? a : b, this denotes a conditional operation. + /// + Conditional, + + /// + /// Represents an expression enclosed in parentheses to enforce precedence or grouping. + /// + /// + /// In the expression (a + b), this indicates a parenthesized sub-expression. + /// + Parenthesized +} diff --git a/src/Ramstack.ExpressionParser/Expressions/ExprVisitor.cs b/src/Ramstack.ExpressionParser/Expressions/ExprVisitor.cs index 25762a9..9e1c315 100644 --- a/src/Ramstack.ExpressionParser/Expressions/ExprVisitor.cs +++ b/src/Ramstack.ExpressionParser/Expressions/ExprVisitor.cs @@ -14,19 +14,8 @@ public abstract class ExprVisitor /// /// A result of type produced by the corresponding visit method. /// - public virtual T Visit(Expr expr) => expr.Kind switch - { - ExprKind.Reference => VisitReference((Expr.Reference)expr), - ExprKind.Literal => VisitLiteral((Expr.Literal)expr), - ExprKind.Binary => VisitBinary((Expr.Binary)expr), - ExprKind.Call => VisitCall((Expr.Call)expr), - ExprKind.Index => VisitIndex((Expr.Indexer)expr), - ExprKind.MemberAccess => VisitMemberAccess((Expr.MemberAccess)expr), - ExprKind.Unary => VisitUnary((Expr.Unary)expr), - ExprKind.Conditional => VisitConditional((Expr.Conditional)expr), - ExprKind.Parenthesized => VisitParenthesized((Expr.Parenthesized)expr), - _ => throw new ArgumentOutOfRangeException(nameof(expr)) - }; + public virtual T Visit(Expr expr) => + expr.Accept(this); /// /// Visits a list of expressions and returns a collection of results. diff --git a/src/Ramstack.ExpressionParser/Expressions/UnaryType.cs b/src/Ramstack.ExpressionParser/Expressions/UnaryType.cs index 4da0230..ef344e8 100644 --- a/src/Ramstack.ExpressionParser/Expressions/UnaryType.cs +++ b/src/Ramstack.ExpressionParser/Expressions/UnaryType.cs @@ -1,47 +1,47 @@ -namespace Ramstack.Parsing.Expressions; - -/// -/// Defines the types of unary operations supported in an expression tree. -/// -public enum UnaryType -{ - /// - /// Represents a type cast or conversion operation, such as converting a value to a specific type. - /// - /// - /// In the expression x:int, this indicates a conversion of x to an integer. - /// - Convert, - - /// - /// Represents an arithmetic negation operation. - /// - /// - /// In the expression -x, this indicates the arithmetic negation of x. - /// - Negate, - - /// - /// Represents a logical negation operation. - /// - /// - /// In the expression !x, this indicates a logical NOT operation on x. - /// - Not, - - /// - /// Represents a bitwise ones complement operation, flipping all bits of a value. - /// - /// - /// In the expression ~x, this indicates a ones complement operation on x. - /// - OnesComplement, - - /// - /// Represents a unary plus operation. - /// - /// - /// In the expression +x, this indicates a unary plus operation on x. - /// - UnaryPlus -} +namespace Ramstack.Parsing.Expressions; + +/// +/// Defines the types of unary operations supported in an expression tree. +/// +public enum UnaryType +{ + /// + /// Represents a type cast or conversion operation, such as converting a value to a specific type. + /// + /// + /// In the expression x:int, this indicates a conversion of x to an integer. + /// + Convert, + + /// + /// Represents an arithmetic negation operation. + /// + /// + /// In the expression -x, this indicates the arithmetic negation of x. + /// + Negate, + + /// + /// Represents a logical negation operation. + /// + /// + /// In the expression !x, this indicates a logical NOT operation on x. + /// + Not, + + /// + /// Represents a bitwise ones complement operation, flipping all bits of a value. + /// + /// + /// In the expression ~x, this indicates a ones complement operation on x. + /// + OnesComplement, + + /// + /// Represents a unary plus operation. + /// + /// + /// In the expression +x, this indicates a unary plus operation on x. + /// + UnaryPlus +} diff --git a/src/Ramstack.ExpressionParser/ParseErrorException.cs b/src/Ramstack.ExpressionParser/ParseErrorException.cs index 56e173d..04cd7e8 100644 --- a/src/Ramstack.ExpressionParser/ParseErrorException.cs +++ b/src/Ramstack.ExpressionParser/ParseErrorException.cs @@ -15,11 +15,11 @@ public ParseErrorException(string? message) : base(message) } /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// with a specified error message and a reference to the inner exception that caused this exception. /// /// The message that describes the parsing error. - /// The exception that caused the current exception, + /// The exception that caused the current exception, /// or if no inner exception is specified. public ParseErrorException(string? message, Exception? innerException) : base(message, innerException) { diff --git a/src/Ramstack.ExpressionParser/Parsers/ConstantNumberParser.cs b/src/Ramstack.ExpressionParser/Parsers/ConstantNumberParser.cs index 44b3508..222bff9 100644 --- a/src/Ramstack.ExpressionParser/Parsers/ConstantNumberParser.cs +++ b/src/Ramstack.ExpressionParser/Parsers/ConstantNumberParser.cs @@ -102,7 +102,7 @@ private static int TryParseInteger(ReadOnlySpan s, out ulong value) // // Check for overflow - // + // if (r > ulong.MaxValue / 10 || (r == ulong.MaxValue / 10 && d > ulong.MaxValue % 10)) goto FAIL; @@ -143,7 +143,7 @@ private static int TryParseHexInteger(ReadOnlySpan s, out ulong value) // // Check for overflow - // + // if (r > ulong.MaxValue / 16) goto FAIL; @@ -181,7 +181,7 @@ private static int TryParseBinInteger(ReadOnlySpan s, out ulong value) // // Check for overflow - // + // if (r > ulong.MaxValue >> 1) goto FAIL; @@ -344,7 +344,7 @@ private static int AdjustNumericType(ReadOnlySpan s, ulong number, out obj // 6.4.5.3 Integer literals // // The type of an integer literal is determined as follows: - // + // // * If the literal has no suffix, it has the first of these types in which its value can be represented: int, uint, long, ulong. // * If the literal is suffixed by U or u, it has the first of these types in which its value can be represented: uint, ulong. // * If the literal is suffixed by L or l, it has the first of these types in which its value can be represented: long, ulong. From a8bc68d437d7c0d6d2b1e2afa8642aec2fef6c63 Mon Sep 17 00:00:00 2001 From: rameel Date: Fri, 21 Feb 2025 21:37:02 +0500 Subject: [PATCH 3/6] Formatting for improved readability --- src/Ramstack.ExpressionParser/DefaultBinder.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Ramstack.ExpressionParser/DefaultBinder.cs b/src/Ramstack.ExpressionParser/DefaultBinder.cs index df6ec27..0374e59 100644 --- a/src/Ramstack.ExpressionParser/DefaultBinder.cs +++ b/src/Ramstack.ExpressionParser/DefaultBinder.cs @@ -128,7 +128,9 @@ public void RegisterType(Type type, bool importAsStatic = false) /// public override MemberInfo? BindToMember(Type? type, Identifier memberName, bool isStatic) { - var bindingFlags = BindingFlags.Public | (isStatic ? BindingFlags.Static : BindingFlags.Instance); + var bindingFlags = isStatic + ? BindingFlags.Public | BindingFlags.Static + : BindingFlags.Public | BindingFlags.Instance; var q = type?.GetMembers(bindingFlags) ?? PredefinedType @@ -158,8 +160,9 @@ private static IEnumerable GetMethods(Type type, string methodName, ? BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Static : BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance; - return type.GetMethods(bindingFlags) - .Where(m => StringComparer.OrdinalIgnoreCase.Equals(m.Name, methodName)) + return type + .GetMethods(bindingFlags) + .Where(m => string.Equals(m.Name, methodName, StringComparison.OrdinalIgnoreCase)) .Distinct(); } } From 5e03e717c31a91351d270efc76d953c50f4be9d4 Mon Sep 17 00:00:00 2001 From: rameel Date: Fri, 21 Feb 2025 21:58:55 +0500 Subject: [PATCH 4/6] Add tests --- src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs | 2 -- tests/Rmastack.ExpressionParser.Tests/Data/Errors.txt | 3 +++ tests/Rmastack.ExpressionParser.Tests/Data/Expressions.txt | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs b/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs index 6183e93..b318b69 100644 --- a/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs +++ b/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs @@ -117,10 +117,8 @@ private static Expression ApplyBinaryExpression(Identifier op, Func 0), 10, 20), IIF((0 < 1), 30, 40)) StringSplitOptions.None & ~StringSplitOptions.RemoveEmptyEntries Convert((Convert(StringSplitOptions.None, Int32) & Convert(Convert(~(Convert(StringSplitOptions.RemoveEmptyEntries, Int32)), StringSplitOptions), Int32)), StringSplitOptions) +StringSplitOptions.None | StringSplitOptions.RemoveEmptyEntries +Convert((Convert(StringSplitOptions.None, Int32) | Convert(StringSplitOptions.RemoveEmptyEntries, Int32)), StringSplitOptions) + ~StringSplitOptions.RemoveEmptyEntries Convert(~(Convert(StringSplitOptions.RemoveEmptyEntries, Int32)), StringSplitOptions) From 3e464f8524472f3b42183518aac9df955931a3b1 Mon Sep 17 00:00:00 2001 From: rameel Date: Fri, 21 Feb 2025 23:25:42 +0500 Subject: [PATCH 5/6] Add tests for numeric promotions --- .../ExpressionBuilder.Helpers.cs | 34 ++- .../ExpressionParser.Parser.cs | 7 +- .../Data/Errors.txt | 43 +++ .../Data/Expressions.txt | 267 ++++++++++++++++++ .../ExpressionParserTest.cs | 4 +- 5 files changed, 344 insertions(+), 11 deletions(-) diff --git a/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs b/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs index b318b69..9162216 100644 --- a/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs +++ b/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs @@ -186,12 +186,28 @@ private static Expression ApplyBinaryExpression(Identifier op, Func CreateParser() ).Void(); var number_literal = - ConstantNumberParser.NumericLiteral + ConstantNumberParser + .NumericLiteral .Do(Expr (v) => new Expr.Literal(v)); var string_literal = @@ -262,8 +263,8 @@ static Expr HijackMemberExpression(Expr result, Expr value) } private static Identifier CreateIdentifier(string v) => - new(v); + new Identifier(v); private static Identifier CreateIdentifier(char v) => - new(new string(v, 1)); + new Identifier(v.ToString()); } diff --git a/tests/Rmastack.ExpressionParser.Tests/Data/Errors.txt b/tests/Rmastack.ExpressionParser.Tests/Data/Errors.txt index d152444..f6a5764 100644 --- a/tests/Rmastack.ExpressionParser.Tests/Data/Errors.txt +++ b/tests/Rmastack.ExpressionParser.Tests/Data/Errors.txt @@ -87,3 +87,46 @@ Ambiguous match found:\n Char Chars [Int32] (in System.String) StringSplitOptions.None | StringComparison.Ordinal Operator '|' cannot be applied to operands of type 'System.StringSplitOptions' and 'System.StringComparison'. + +####################################### +###### Binary Numeric Promotions ###### +####################################### + +####################################### +# If either operand is of type decimal, a binding-time error occurs +# if the other operand is of type float or double. + +1.0 + 2.0m +Operator '+' cannot be applied to operands of type 'System.Double' and 'System.Decimal'. + +1.0m + 2.0d +Operator '+' cannot be applied to operands of type 'System.Decimal' and 'System.Double'. + +1.0f + 2.0m +Operator '+' cannot be applied to operands of type 'System.Single' and 'System.Decimal'. + +1.0m + 2.0f +Operator '+' cannot be applied to operands of type 'System.Decimal' and 'System.Single'. + +####################################### +# If either operand is of type ulong, a binding-time error occurs +# if the other operand is of type sbyte, short, int, or long. + +1ul + 2:sbyte +Operator '+' cannot be applied to operands of type 'System.UInt64' and 'System.SByte'. + +1ul + 2:short +Operator '+' cannot be applied to operands of type 'System.UInt64' and 'System.Int16'. + +1ul + 2:int +Operator '+' cannot be applied to operands of type 'System.UInt64' and 'System.Int32'. + +1ul + 2:long +Operator '+' cannot be applied to operands of type 'System.UInt64' and 'System.Int64'. + +####################################### +###### Unary Numeric Promotions ###### +####################################### + +-1ul +Unary operator '-' cannot be applied to operand of type 'System.UInt64'. diff --git a/tests/Rmastack.ExpressionParser.Tests/Data/Expressions.txt b/tests/Rmastack.ExpressionParser.Tests/Data/Expressions.txt index 571f3b7..3dd97f7 100644 --- a/tests/Rmastack.ExpressionParser.Tests/Data/Expressions.txt +++ b/tests/Rmastack.ExpressionParser.Tests/Data/Expressions.txt @@ -198,3 +198,270 @@ Convert(~(Convert(StringSplitOptions.RemoveEmptyEntries, Int32)), StringSplitOpt true ^ false (True ^ False) + +true | false +(True Or False) + +true & false +(True And False) + +####################################### +# Precedence test +1 + 2 - 4 * 5 / 4 << 1 & -3 ^ 17 | ~5 >> 2 >>> 1 % 5 +((((((1 + 2) - ((4 * 5) / 4)) << 1) & -3) ^ 17) | Convert((Convert((~(5) >> 2), UInt32) >> (1 % 5)), Int32)) + +####################################### +###### Binary Numeric Promotions ###### +####################################### + +####################################### +# If either operand is of type decimal, the other operand is converted to type decimal +1m + 2 +(1 + Convert(2, Decimal)) + +1 + 2m +(Convert(1, Decimal) + 2) + +1m + 2:byte +(1 + Convert(Convert(2, Byte), Decimal)) + +1m + 2:sbyte +(1 + Convert(Convert(2, SByte), Decimal)) + +1m + 2:short +(1 + Convert(Convert(2, Int16), Decimal)) + +1m + 2:ushort +(1 + Convert(Convert(2, UInt16), Decimal)) + +1m + 2:int +(1 + Convert(2, Decimal)) + +1m + 2:uint +(1 + Convert(Convert(2, UInt32), Decimal)) + +1m + 2:long +(1 + Convert(Convert(2, Int64), Decimal)) + +1m + 2:ulong +(1 + Convert(Convert(2, UInt64), Decimal)) + + +####################################### +# If either operand is of type double, the other operand is converted to type double. + +1d + 2 +(1 + Convert(2, Double)) + +1 + 2d +(Convert(1, Double) + 2) + +1.0 + 2:byte +(1 + Convert(Convert(2, Byte), Double)) + +1.0 + 2:sbyte +(1 + Convert(Convert(2, SByte), Double)) + +1.0 + 2:short +(1 + Convert(Convert(2, Int16), Double)) + +1.0 + 2:ushort +(1 + Convert(Convert(2, UInt16), Double)) + +1.0 + 2:int +(1 + Convert(2, Double)) + +1.0 + 2:uint +(1 + Convert(Convert(2, UInt32), Double)) + +1.0 + 2:long +(1 + Convert(Convert(2, Int64), Double)) + +1.0 + 2:ulong +(1 + Convert(Convert(2, UInt64), Double)) + +####################################### +# If either operand is of type float, the other operand is converted to type float. + +1f + 2 +(1 + Convert(2, Single)) + +1 + 2f +(Convert(1, Single) + 2) + +1.0f + 2:byte +(1 + Convert(Convert(2, Byte), Single)) + +1.0f + 2:sbyte +(1 + Convert(Convert(2, SByte), Single)) + +1.0f + 2:short +(1 + Convert(Convert(2, Int16), Single)) + +1.0f + 2:ushort +(1 + Convert(Convert(2, UInt16), Single)) + +1.0f + 2:int +(1 + Convert(2, Single)) + +1.0f + 2:uint +(1 + Convert(Convert(2, UInt32), Single)) + +1.0f + 2:long +(1 + Convert(Convert(2, Int64), Single)) + +1.0f + 2:ulong +(1 + Convert(Convert(2, UInt64), Single)) + +####################################### +# If either operand is of type ulong, the other operand is converted to type ulong + +1ul + 2u +(1 + Convert(2, UInt64)) + +1u + 2ul +(Convert(1, UInt64) + 2) + +1ul + 2:byte +(1 + Convert(Convert(2, Byte), UInt64)) + +1ul + 2:ushort +(1 + Convert(Convert(2, UInt16), UInt64)) + +1ul + 2:uint +(1 + Convert(Convert(2, UInt32), UInt64)) + +1ul + 2:ulong +(1 + Convert(2, UInt64)) + +####################################### +# If either operand is of type long, the other operand is converted to type long. + +1L + 2 +(1 + Convert(2, Int64)) + +1 + 2L +(Convert(1, Int64) + 2) + +1L + 2:byte +(1 + Convert(Convert(2, Byte), Int64)) + +1L + 2:sbyte +(1 + Convert(Convert(2, SByte), Int64)) + +1L + 2:short +(1 + Convert(Convert(2, Int16), Int64)) + +1L + 2:ushort +(1 + Convert(Convert(2, UInt16), Int64)) + +1L + 2:int +(1 + Convert(2, Int64)) + +1L + 2:uint +(1 + Convert(Convert(2, UInt32), Int64)) + +1L + 2:long +(1 + Convert(2, Int64)) + +####################################### +# If either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long. +# If either operand is of type uint, the other operand is converted to type uint. + +1u + 2 +(Convert(1, Int64) + Convert(2, Int64)) + +1 + 2u +(Convert(1, Int64) + Convert(2, Int64)) + +1u + 2:byte +(1 + Convert(Convert(2, Byte), UInt32)) + +1u + 2:sbyte +(Convert(1, Int64) + Convert(Convert(2, SByte), Int64)) + +1u + 2:short +(Convert(1, Int64) + Convert(Convert(2, Int16), Int64)) + +1u + 2:ushort +(1 + Convert(Convert(2, UInt16), UInt32)) + +1u + 2:int +(Convert(1, Int64) + Convert(2, Int64)) + +1u + 2:uint +(1 + Convert(2, UInt32)) + +####################################### +# Otherwise, both operands are converted to type int. + +1 + 2:byte +(1 + Convert(Convert(2, Byte), Int32)) + +1 + 2:sbyte +(1 + Convert(Convert(2, SByte), Int32)) + +1 + 2:short +(1 + Convert(Convert(2, Int16), Int32)) + +1 + 2:ushort +(1 + Convert(Convert(2, UInt16), Int32)) + +1 + 2:int +(1 + 2) + + +####################################### +###### Unary Numeric Promotions ###### +####################################### + +####################################### +# + operator + ++1:byte ++Convert(Convert(1, Byte), Int32) + ++1:sbyte ++Convert(Convert(1, SByte), Int32) + ++1:short ++Convert(Convert(1, Int16), Int32) + ++1:ushort ++Convert(Convert(1, UInt16), Int32) + ++1:int ++1 + ++1u ++1 + ++1L ++1 + ++1ul ++1 + +####################################### +# - operator + +-1:byte +-Convert(Convert(1, Byte), Int32) + +-1:sbyte +-Convert(Convert(1, SByte), Int32) + +-1:short +-Convert(Convert(1, Int16), Int32) + +-1:ushort +-Convert(Convert(1, UInt16), Int32) + +-1:int +-1 + +-1u +Convert(1, Int64) + +-1L +-1 diff --git a/tests/Rmastack.ExpressionParser.Tests/ExpressionParserTest.cs b/tests/Rmastack.ExpressionParser.Tests/ExpressionParserTest.cs index 10984d0..eb6b9fb 100644 --- a/tests/Rmastack.ExpressionParser.Tests/ExpressionParserTest.cs +++ b/tests/Rmastack.ExpressionParser.Tests/ExpressionParserTest.cs @@ -93,11 +93,11 @@ private static List GetResource(string resourceName) string? e; while ((s = reader.ReadLine()) is not null) - if (!string.IsNullOrWhiteSpace(s)) + if (!string.IsNullOrWhiteSpace(s) && !s.StartsWith('#')) break; while ((e = reader.ReadLine()) is not null) - if (!string.IsNullOrWhiteSpace(e)) + if (!string.IsNullOrWhiteSpace(e) && !e.StartsWith('#')) break; if (s is null || e is null) From e0628eb2b02bc2db72b4902db795d110970c0f13 Mon Sep 17 00:00:00 2001 From: rameel Date: Fri, 21 Feb 2025 23:36:57 +0500 Subject: [PATCH 6/6] Formatting --- .../ExpressionBuilder.Helpers.cs | 16 ++++----- .../ExpressionBuilder.cs | 34 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs b/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs index 9162216..aee65b2 100644 --- a/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs +++ b/src/Ramstack.ExpressionParser/ExpressionBuilder.Helpers.cs @@ -288,25 +288,25 @@ private static void ApplyBinaryNumericPromotions(Identifier op, ref Expression l } } - private static Func CreateOperatorFactory(string @operator) + private static Func ResolveBinaryOperatorFactory(string @operator) { return @operator switch { "??" => Expression.Coalesce, - "||" => Expression.OrElse, "&&" => Expression.AndAlso, - "|" => Expression.Or, - "^" => Expression.ExclusiveOr, - "&" => Expression.And, + "||" => Expression.OrElse, "==" => Expression.Equal, "!=" => Expression.NotEqual, "<=" => Expression.LessThanOrEqual, - "<" => Expression.LessThan, ">=" => Expression.GreaterThanOrEqual, - ">" => Expression.GreaterThan, ">>" => Expression.RightShift, - ">>>" => Expression.RightShift, "<<" => Expression.LeftShift, + ">>>" => Expression.RightShift, + "<" => Expression.LessThan, + ">" => Expression.GreaterThan, + "&" => Expression.And, + "|" => Expression.Or, + "^" => Expression.ExclusiveOr, "+" => Expression.Add, "-" => Expression.Subtract, "*" => Expression.Multiply, diff --git a/src/Ramstack.ExpressionParser/ExpressionBuilder.cs b/src/Ramstack.ExpressionParser/ExpressionBuilder.cs index dc8a05c..013853b 100644 --- a/src/Ramstack.ExpressionParser/ExpressionBuilder.cs +++ b/src/Ramstack.ExpressionParser/ExpressionBuilder.cs @@ -37,38 +37,38 @@ protected internal override Expression VisitBinary(Expr.Binary expr) var lhs = Visit(expr.Left); var rhs = Visit(expr.Right); - var factory = CreateOperatorFactory(expr.Operator.Name); + var lhsType = lhs.Type; + var rhsType = rhs.Type; - var l = lhs; - var r = rhs; + var factory = ResolveBinaryOperatorFactory(expr.Operator.Name); switch (expr.Operator.Name) { case "&&": case "||": - l = ApplyImplicitConversion(lhs, typeof(bool)); - r = ApplyImplicitConversion(rhs, typeof(bool)); + lhs = ApplyImplicitConversion(lhs, typeof(bool)); + rhs = ApplyImplicitConversion(rhs, typeof(bool)); - if (l is null) - Error.MissingImplicitConversion(lhs.Type, typeof(bool)); + if (lhs is null) + Error.MissingImplicitConversion(lhsType, typeof(bool)); - if (r is null) - Error.MissingImplicitConversion(rhs.Type, typeof(bool)); + if (rhs is null) + Error.MissingImplicitConversion(rhsType, typeof(bool)); break; case "??": - if (lhs.Type.IsValueType && !lhs.Type.IsNullable()) - Error.NonApplicableBinaryOperator(expr.Operator, lhs.Type, rhs.Type); + if (lhsType.IsValueType && !lhsType.IsNullable()) + Error.NonApplicableBinaryOperator(expr.Operator, lhsType, rhsType); - r = ApplyImplicitConversion(rhs, lhs.Type); - if (r is null) - Error.NonApplicableBinaryOperator(expr.Operator, lhs.Type, rhs.Type); + rhs = ApplyImplicitConversion(rhs, lhsType); + if (rhs is null) + Error.NonApplicableBinaryOperator(expr.Operator, lhsType, rhsType); break; case "+": - if (lhs.Type != typeof(string) && rhs.Type != typeof(string)) + if (lhsType != typeof(string) && rhsType != typeof(string)) break; var arguments = new List(); @@ -137,11 +137,11 @@ void Flatten(Expression e) try { - return ApplyBinaryExpression(expr.Operator, factory, l, r); + return ApplyBinaryExpression(expr.Operator, factory, lhs, rhs); } catch (Exception e) when (e is not ParseErrorException) { - Error.NonApplicableBinaryOperator(expr.Operator, lhs.Type, rhs.Type); + Error.NonApplicableBinaryOperator(expr.Operator, lhsType, rhsType); } return null!;