Пример #1
0
        private Expression TranslateUnaryExpression(XpressCompilationContext ctx, ParseTreeNode prefixExpressionNode)
        {
            //  unaryExpression -> unaryOperator  term
            //                   | primaryExpression

            var nodes = prefixExpressionNode.ChildNodes;

            if (nodes.Count == 2) // unaryOperator primaryExpression
            {
                var rightExpr = TranslatePrimaryExpression(ctx, nodes[1]);
                if (!IsRuntimeContextGetExpression(rightExpr) && rightExpr.Type != typeof(bool))
                {
                    throw new Exception($"Operator '{XpressConstants.Tokens.Terminals.UnaryNegationOperator}' can only be applied to 'boolean' operands. Error at '{nodes[1].FindTokenAndGetText()}' on position {nodes[1].Span.Location.Position}");
                }

                var op = nodes[0].FindTokenAndGetText().ToLowerInvariant();

                if (op == XpressConstants.Tokens.Terminals.UnaryNegationOperator)
                {
                    return(Expression.Not(ConvertToBoolean(rightExpr)));
                }
            }
            else if (nodes.Count == 1) // term
            {
                return(TranslatePrimaryExpression(ctx, nodes[0]));
            }

            return(null);
        }
Пример #2
0
        private Expression TranslateBinaryAdditiveExpression(XpressCompilationContext ctx, ParseTreeNode addExpressionNode)
        {
            //   binaryAdditiveExpression -> binaryAdditiveExpression  binaryAdditiveOperator  binaryMultiplicityExpression
            //                             | binaryMultiplicityExpression

            var nodes = addExpressionNode.ChildNodes;

            if (nodes.Count == 3) // binaryAdditiveExpression  binaryAdditiveOperator  binaryMultiplicityExpression
            {
                var op = nodes[1].FindTokenAndGetText().ToLowerInvariant();

                if (op == XpressConstants.Tokens.Terminals.BinaryAdditionOperator)
                {
                    return(TranslateAdditionExpression(ctx, nodes));
                }
                else if (op == XpressConstants.Tokens.Terminals.BinarySubtrationOperator)
                {
                    return(TranslateSubtractionExpression(ctx, nodes));
                }
            }
            else if (nodes.Count == 1) // binaryMultiplicityExpression
            {
                return(TranslateBinaryMultiplicityExpression(ctx, nodes[0]));
            }

            return(null);
        }
Пример #3
0
        internal Func <XpressRuntimeContext, bool> Translate(IXpressParseTree parseTree, ILogWriter log)
        {
            try
            {
                var ironyParseTree = (ParseTree)parseTree.Root;
                var parseNodes     = ironyParseTree.Root.ChildNodes;

                var requestInfoParam = Expression.Parameter(typeof(XpressRuntimeContext), "context");
                var ctx = new XpressCompilationContext {
                    RuntimeContextParameter = requestInfoParam
                };

                Expression condition = null;

                foreach (var parseNode in parseNodes)
                {
                    if (parseNode.Term.Name == XpressConstants.Tokens.NonTerminals.Expression)
                    {
                        condition = TranslateExpression(ctx, parseNode);

                        if (!IsRuntimeContextGetExpression(condition) && condition.Type != typeof(bool))
                        {
                            throw new Exception($"Expression must evluate to a 'boolean' value. Error at '{parseNode.FindTokenAndGetText()}' on position {parseNode.Span.Location.Position}");
                        }
                        condition = ConvertToBoolean(condition);

                        if (condition != null)
                        {
                            log.Debug($"{condition.ToString()}");
                        }
                    }
                }


                // Define a return statement label and expression
                var returnTarget     = Expression.Label(typeof(bool));
                var returnExpression = Expression.Return(returnTarget, condition);

                // Define func body statements
                List <Expression> bodyStatements = new List <Expression>();
                bodyStatements.Add(returnExpression);
                bodyStatements.Add(Expression.Label(returnTarget, condition));

                var bodyExpression   = Expression.Block(bodyStatements);
                var lambdaExpression = Expression.Lambda <Func <XpressRuntimeContext, bool> >(bodyExpression, requestInfoParam);

                return(lambdaExpression.Compile());
            }
            catch (Exception ex)
            {
                log.Error($"Error interpreting parse tree: {ex.Message}");
            }

            return(null);
        }
Пример #4
0
        private Expression TranslatePrimaryExpression(XpressCompilationContext ctx, ParseTreeNode termExpressionNode)
        {
            //  primaryExpression -> Identifier | NumberLiteral | StringLiteral | booleanLiteral | nullLiteral | parenExpression

            // nullLiteral -> ToTerm("null")
            // booleanLiteral -> ToTerm("true") | "false"

            var nodes = termExpressionNode.ChildNodes;

            if (nodes[0].Term.Name == XpressConstants.Tokens.NonTerminals.Identifier)
            {
                var identifierKey           = nodes[0].Token.ValueString;
                var identifierKeyExpression = Expression.Constant(identifierKey, typeof(string));

                var requestInfoGetMethodInfo = XpressCompilationContext.RuntimeContextGet;

                var requestInfoGetIdentifier = Expression.Call(ctx.RuntimeContextParameter, requestInfoGetMethodInfo, identifierKeyExpression);

                return(requestInfoGetIdentifier);
            }
            else if (nodes[0].Term.Name == XpressConstants.Tokens.NonTerminals.NumberLiteral)
            {
                var number = (int)nodes[0].Token.Value;
                return(Expression.Constant(number, typeof(int)));
            }

            else if (nodes[0].Term.Name == XpressConstants.Tokens.NonTerminals.StringLiteral)
            {
                var str = (string)nodes[0].Token.Value;
                return(Expression.Constant(str, typeof(string)));
            }
            else if (nodes[0].Term.Name == XpressConstants.Tokens.NonTerminals.BooleanLiteral)
            {
                var op = nodes[0].FindTokenAndGetText().ToLowerInvariant();

                if (op == XpressConstants.Tokens.Terminals.BooleanTrueValue)
                {
                    return(Expression.Constant(true, typeof(bool)));
                }

                if (op == XpressConstants.Tokens.Terminals.BooleanFalseValue)
                {
                    return(Expression.Constant(false, typeof(bool)));
                }
            }
            else if (nodes[0].Term.Name == XpressConstants.Tokens.NonTerminals.NullLiteral)
            {
                return(Expression.Constant(null));
            }
            else if (nodes[0].Term.Name == XpressConstants.Tokens.NonTerminals.ParenExpression)
            {
                return(TranslateExpression(ctx, nodes[0].ChildNodes[0]));
            }
            return(null);
        }
Пример #5
0
        private Expression TranslateAdditionExpression(XpressCompilationContext ctx, ParseTreeNodeList nodes)
        {
            var leftExpr  = TranslateBinaryAdditiveExpression(ctx, nodes[0]);
            var rightExpr = TranslateBinaryMultiplicityExpression(ctx, nodes[2]);

            var supporttedTypes = new[] { typeof(int), typeof(string) };

            // Handle 1+null , 1+ true , true +true, null+null, true+null, true +'a', null+'a'
            if (leftExpr.NodeType == ExpressionType.Constant && !supporttedTypes.Contains(leftExpr.Type))
            {
                throw new Exception($"Operator '{XpressConstants.Tokens.Terminals.BinaryAdditionOperator}' cannot be applied to operands of type {leftExpr.Type.Name}. Error at '{nodes[0].FindTokenAndGetText()}' on position {nodes[0].Span.Location.Position}");
            }

            if (rightExpr.NodeType == ExpressionType.Constant && !supporttedTypes.Contains(rightExpr.Type))
            {
                throw new Exception($"Operator '{XpressConstants.Tokens.Terminals.BinaryAdditionOperator}' cannot be applied to operands of type {leftExpr.Type.Name} and {rightExpr.Type.Name}. Error at '{nodes[2].FindTokenAndGetText()}' on position {nodes[2].Span.Location.Position}");
            }

            // Handle 1 + '2' or '1' + 2 cases
            if (leftExpr.Type == typeof(int) && rightExpr.Type == typeof(string) &&
                rightExpr.NodeType == ExpressionType.Constant)
            {
                throw new Exception($"Operator '{XpressConstants.Tokens.Terminals.BinaryAdditionOperator}' cannot be applied to operands of type {leftExpr.Type.Name} and {rightExpr.Type.Name}. Error at '{nodes[0].FindTokenAndGetText()}' on position {nodes[0].Span.Location.Position}");
            }

            if (rightExpr.Type == typeof(int) && leftExpr.Type == typeof(string) && leftExpr.NodeType == ExpressionType.Constant)
            {
                throw new Exception($"Operator '{XpressConstants.Tokens.Terminals.BinaryAdditionOperator}' cannot be applied to operands of type {leftExpr.Type.Name}. Error at '{nodes[0].FindTokenAndGetText()}' on position {nodes[0].Span.Location.Position}");
            }



            if (leftExpr.Type == typeof(int) || rightExpr.Type == typeof(int))
            {
                // add integers
                leftExpr  = ConvertToInteger(leftExpr);
                rightExpr = ConvertToInteger(rightExpr);

                return(Expression.Add(leftExpr, rightExpr));
            }
            else
            {
                // string.concat()
                leftExpr  = ConvertToString(leftExpr);
                rightExpr = ConvertToString(rightExpr);

                var stringConcatMethod = typeof(string).GetMethod("Concat",
                                                                  (BindingFlags.Public | BindingFlags.Static), null, CallingConventions.Any,
                                                                  new Type[] { typeof(string), typeof(string) }, null);
                return(Expression.Call(stringConcatMethod, leftExpr, rightExpr));
            }
        }
Пример #6
0
        private Expression TranslateBinaryMultiplicityExpression(XpressCompilationContext ctx, ParseTreeNode multiplyExpressionNode)
        {
            //   binaryMultiplicityExpression -> binaryMultiplicityExpression  binaryMultiplicityOperator  unaryExpression
            //                                 | unaryExpression

            var nodes = multiplyExpressionNode.ChildNodes;

            if (nodes.Count == 3) // binaryMultiplicityExpression  binaryMultiplicityOperator  unaryExpression
            {
                var op        = nodes[1].FindTokenAndGetText().ToLowerInvariant();
                var leftExpr  = TranslateBinaryMultiplicityExpression(ctx, nodes[0]);
                var rightExpr = TranslateUnaryExpression(ctx, nodes[2]);

                if (leftExpr.NodeType == ExpressionType.Constant && leftExpr.Type != typeof(int))
                {
                    throw new Exception($"Operator '{op}' can only be applied to 'Int32' constants. Error at '{nodes[0].FindTokenAndGetText()}' on position {nodes[0].Span.Location.Position}");
                }

                if (rightExpr.NodeType == ExpressionType.Constant && rightExpr.Type != typeof(int))
                {
                    throw new Exception($"Operator '{op}' can only be applied to 'Int32' constants. Error at '{nodes[2].FindTokenAndGetText()}' on position {nodes[2].Span.Location.Position}");
                }

                leftExpr  = ConvertToInteger(leftExpr);
                rightExpr = ConvertToInteger(rightExpr);

                if (op == XpressConstants.Tokens.Terminals.BinaryMultiplicationOperator)
                {
                    return(Expression.Multiply(leftExpr, rightExpr));
                }
                else if (op == XpressConstants.Tokens.Terminals.BinaryDivisionOperator)
                {
                    return(Expression.Divide(leftExpr, rightExpr));
                }
                else if (op == XpressConstants.Tokens.Terminals.BinaryModuloOperator)
                {
                    return(Expression.Modulo(leftExpr, rightExpr));
                }
            }
            else if (nodes.Count == 1) // prefixExpression
            {
                return(TranslateUnaryExpression(ctx, nodes[0]));
            }

            return(null);
        }
Пример #7
0
        private Expression TranslateExpression(XpressCompilationContext ctx, ParseTreeNode expressoinNode)
        {
            //  expression -> expression  booleanOperator  relationalExpression
            //              | relationalExpression

            var nodes = expressoinNode.ChildNodes;

            if (nodes.Count == 3) // expression  booleanOperator  relationalExpression
            {
                var conditionalOp = nodes[1].FindTokenAndGetText().ToLowerInvariant();
                var leftExpr      = TranslateExpression(ctx, nodes[0]);
                var rightExpr     = TranslateRelationalExpression(ctx, nodes[2]);

                if (!IsRuntimeContextGetExpression(leftExpr) && leftExpr.Type != typeof(bool))
                {
                    throw new Exception($"Operator '{conditionalOp}' can only be applied to 'boolean' operands. Error at '{nodes[0].FindTokenAndGetText()}' on position {nodes[0].Span.Location.Position}");
                }

                if (!IsRuntimeContextGetExpression(rightExpr) && rightExpr.Type != typeof(bool))
                {
                    throw new Exception($"Operator '{conditionalOp}' can only be applied to 'boolean' operands. Error at '{nodes[2].FindTokenAndGetText()}' on position {nodes[2].Span.Location.Position}");
                }


                leftExpr  = ConvertToBoolean(leftExpr);
                rightExpr = ConvertToBoolean(rightExpr);

                if (conditionalOp == XpressConstants.Tokens.Terminals.ConditionalAndOperator)
                {
                    return(Expression.AndAlso(leftExpr, rightExpr));
                }
                else
                {
                    return(Expression.OrElse(leftExpr, rightExpr));
                }
            }
            else if (nodes.Count == 1) // relationalExpression
            {
                return(TranslateRelationalExpression(ctx, nodes[0]));
            }

            return(null);
        }
Пример #8
0
        private Expression TranslateSubtractionExpression(XpressCompilationContext ctx, ParseTreeNodeList nodes)
        {
            var leftExpr  = TranslateBinaryAdditiveExpression(ctx, nodes[0]);
            var rightExpr = TranslateBinaryMultiplicityExpression(ctx, nodes[2]);

            if (leftExpr.NodeType == ExpressionType.Constant && leftExpr.Type != typeof(int))
            {
                throw new Exception($"Operator '{XpressConstants.Tokens.Terminals.BinarySubtrationOperator}' cannot be applied to operands of type {leftExpr.Type.Name}. Error at '{nodes[0].FindTokenAndGetText()}' on position {nodes[0].Span.Location.Position}");
            }

            if (rightExpr.NodeType == ExpressionType.Constant && rightExpr.Type != typeof(int))
            {
                throw new Exception($"Operator '{XpressConstants.Tokens.Terminals.BinarySubtrationOperator}' cannot be applied to operands of type {rightExpr.Type.Name}. Error at '{nodes[2].FindTokenAndGetText()}' on position {nodes[2].Span.Location.Position}");
            }


            // add integers
            leftExpr  = ConvertToInteger(leftExpr);
            rightExpr = ConvertToInteger(rightExpr);

            return(Expression.Subtract(leftExpr, rightExpr));
        }
Пример #9
0
        private Expression TranslateEqualityExpression(XpressCompilationContext ctx, string op, Expression leftExpr, Expression rightExpr, ParseTreeNodeList nodes)
        {
            // implicit casting for variables to match the compared operand type
            if (IsRuntimeContextGetExpression(leftExpr))
            {
                if (rightExpr.Type == typeof(bool))
                {
                    leftExpr = ConvertToBoolean(leftExpr);
                }
                else if (rightExpr.Type == typeof(int))
                {
                    leftExpr = ConvertToInteger(leftExpr);
                }
            }
            else if (IsRuntimeContextGetExpression(rightExpr))
            {
                if (leftExpr.Type == typeof(bool))
                {
                    rightExpr = ConvertToBoolean(rightExpr);
                }
                else if (leftExpr.Type == typeof(int))
                {
                    rightExpr = ConvertToInteger(rightExpr);
                }
            }


            if (leftExpr.Type != rightExpr.Type)
            {
                if ((leftExpr.Type == typeof(string) && IsNullExpression(rightExpr)) ||
                    (rightExpr.Type == typeof(string) && IsNullExpression(leftExpr)))
                {
                    // string.IsNullOrEmpty()
                    Expression operand = leftExpr;
                    // handle null comparison
                    if (IsNullExpression(leftExpr))
                    {
                        operand = rightExpr;
                    }
                    else if (IsNullExpression(rightExpr))
                    {
                        operand = leftExpr;
                    }

                    Expression nullCheckExpr = null;
                    if (IsRuntimeContextGetExpression(operand))
                    {
                        var keyExpr = ((MethodCallExpression)operand).Arguments[0];

                        if (op == XpressConstants.Tokens.Terminals.RelationalEqualityOperator) // ! requestInfo.Exists(key) || IsNotNullorEmpty(requestInfo.Get(key))
                        {
                            nullCheckExpr = Expression.OrElse(
                                Expression.Not(Expression.Call(ctx.RuntimeContextParameter, XpressCompilationContext.RuntimeContextExists, keyExpr)),
                                Expression.Call(XpressCompilationContext.IsNullOrEmptyMethod, operand));
                        }
                        else if (op == XpressConstants.Tokens.Terminals.RelationalNonEqualityOperator) // requestInfo.Exists(key) && ! IsNotNullorEmpty(requestInfo.Get(key))
                        {
                            nullCheckExpr = Expression.AndAlso(
                                Expression.Call(ctx.RuntimeContextParameter, XpressCompilationContext.RuntimeContextExists, keyExpr),
                                Expression.Not(Expression.Call(XpressCompilationContext.IsNullOrEmptyMethod, operand)));
                        }
                    }
                    else
                    {
                        nullCheckExpr = Expression.Call(XpressCompilationContext.IsNullOrEmptyMethod, operand);
                        if (op == XpressConstants.Tokens.Terminals.RelationalNonEqualityOperator)
                        {
                            nullCheckExpr = Expression.Not(nullCheckExpr);
                        }
                    }

                    return(nullCheckExpr);
                }
                else // any other type missmatch .. error out .. we can't implicitly cast
                {
                    throw new Exception($"Operator '{op}' cannot be applied to operands of type '{leftExpr.Type.Name}' and '{rightExpr.Type.Name}'. Error at '{nodes[0].FindTokenAndGetText()}' on position {nodes[0].Span.Location.Position}");
                }
            }

            // string comparison
            if (leftExpr.Type == typeof(string) || rightExpr.Type == typeof(string))
            {
                //string.Equals(a, b, StringComparison.InvariantCultureIgnoreCase)
                Expression equalityExpr = Expression.Call(XpressCompilationContext.StringEquals, leftExpr, rightExpr, Expression.Constant(StringComparison.InvariantCultureIgnoreCase));
                if (op == XpressConstants.Tokens.Terminals.RelationalNonEqualityOperator)
                {
                    equalityExpr = Expression.Not(equalityExpr);
                }

                return(equalityExpr);
            }
            else
            {
                if (op == XpressConstants.Tokens.Terminals.RelationalEqualityOperator)
                {
                    return(Expression.Equal(leftExpr, rightExpr));
                }
                else if (op == XpressConstants.Tokens.Terminals.RelationalNonEqualityOperator)
                {
                    return(Expression.NotEqual(leftExpr, rightExpr));
                }
            }

            return(null);
        }
Пример #10
0
        private Expression TranslateRelationalExpression(XpressCompilationContext ctx, ParseTreeNode relationalNode)
        {
            // relationalExpression -> relationalExpression  relationalOperator  binaryAdditiveExpression
            //                       | binaryAdditiveExpression

            var nodes = relationalNode.ChildNodes;

            // validation
            if (nodes.Count == 3) // relationalExpression  relationalOperator  binaryAdditiveExpression
            {
                var relationalOp = nodes[1].FindTokenAndGetText().ToLowerInvariant();
                var leftExpr     = TranslateRelationalExpression(ctx, nodes[0]);
                var rightExpr    = TranslateBinaryAdditiveExpression(ctx, nodes[2]);

                var relationalOps = new[] { XpressConstants.Tokens.Terminals.RelationalGreatThanOperator
                                            , XpressConstants.Tokens.Terminals.RelationalLessThanOperator
                                            , XpressConstants.Tokens.Terminals.RelationalGreatThanOrEqualOperator
                                            , XpressConstants.Tokens.Terminals.RelationalLessThanOrEqualOperator };

                if (relationalOps.Contains(relationalOp)) // comparison ops
                {
                    if (leftExpr.NodeType == ExpressionType.Constant && leftExpr.Type != typeof(int))
                    {
                        throw new Exception($"Operator '{relationalOp}' can only be applied to 'Int32' operands. Error at '{nodes[0].FindTokenAndGetText()}' on position {nodes[0].Span.Location.Position}");
                    }

                    if (rightExpr.NodeType == ExpressionType.Constant && rightExpr.Type != typeof(int))
                    {
                        throw new Exception($"Operator '{relationalOp}' can only be applied to 'Int32' operands. Error at '{nodes[2].FindTokenAndGetText()}' on position {nodes[2].Span.Location.Position}");
                    }

                    leftExpr  = ConvertToInteger(leftExpr);
                    rightExpr = ConvertToInteger(rightExpr);

                    if (relationalOp == XpressConstants.Tokens.Terminals.RelationalGreatThanOperator)
                    {
                        return(Expression.GreaterThan(leftExpr, rightExpr));
                    }
                    else if (relationalOp == XpressConstants.Tokens.Terminals.RelationalLessThanOperator)
                    {
                        return(Expression.LessThan(leftExpr, rightExpr));
                    }
                    else if (relationalOp == XpressConstants.Tokens.Terminals.RelationalGreatThanOrEqualOperator)
                    {
                        return(Expression.GreaterThanOrEqual(leftExpr, rightExpr));
                    }
                    else if (relationalOp == XpressConstants.Tokens.Terminals.RelationalLessThanOrEqualOperator)
                    {
                        return(Expression.LessThanOrEqual(leftExpr, rightExpr));
                    }
                }
                else // equality ops
                {
                    return(TranslateEqualityExpression(ctx, relationalOp, leftExpr, rightExpr, nodes));
                }
            }
            else if (nodes.Count == 1) // binaryAdditiveExpression
            {
                return(TranslateBinaryAdditiveExpression(ctx, nodes[0]));
            }

            return(null);
        }