public static ScriptExpression Rewrite(TemplateContext context, ScriptBinaryExpression binaryExpression)
        {
            Debug.Assert(ImplicitFunctionCallPrecedence < Parser.PrecedenceOfMultiply);

            if (!HasImplicitBinaryExpression(binaryExpression))
            {
                return(binaryExpression);
            }

            // TODO: use a TLS cache
            var iterator = new BinaryExpressionIterator();

            // a b c / d + e
            // stack will contain:
            // [0] a
            // [1] implicit *
            // [2] b
            // [3] implicit *
            // [4] c
            // [5] /
            // [6] d
            // [7] +
            // [8] e
            FlattenBinaryExpressions(context, binaryExpression, iterator);

            return(ParseBinaryExpressionTree(iterator, 0, false));
        }
Example #2
0
        public override ScriptNode Visit(ScriptBinaryExpression node)
        {
            if (_isScientific)
            {
                var newNode = ScientificFunctionCallRewriter.Rewrite(_context, node);
                if (newNode != node)
                {
                    return(Visit((ScriptNode)newNode));
                }
            }

            var binaryExpression = (ScriptBinaryExpression)base.Visit((ScriptBinaryExpression)node);

            // We don't surround range with spaces
            if (_flags.HasFlags(ScriptFormatterFlags.AddSpaceBetweenOperators))
            {
                if (binaryExpression.Operator < ScriptBinaryOperator.RangeInclude)
                {
                    binaryExpression.OperatorToken.AddLeadingSpace();
                    binaryExpression.OperatorToken.AddSpaceAfter();
                }
            }

            if (_flags.HasFlags(ScriptFormatterFlags.ExplicitParenthesis))
            {
                if (binaryExpression.Operator == ScriptBinaryOperator.Divide || binaryExpression.Operator == ScriptBinaryOperator.DivideRound || (_isScientific && binaryExpression.Operator == ScriptBinaryOperator.Power))
                {
                    if (binaryExpression.Left is ScriptBinaryExpression leftBin && HasSimilarPrecedenceThanMultiply(leftBin.Operator))
                    {
                        binaryExpression.Left = null;
                        var nested = new ScriptNestedExpression(leftBin);
                        binaryExpression.Left = nested;
                        leftBin.MoveTrailingTriviasTo(nested.CloseParen, true);
                    }

                    if (binaryExpression.Right is ScriptBinaryExpression rightBin && HasSimilarPrecedenceThanMultiply(rightBin.Operator))
                    {
                        binaryExpression.Right = null;
                        var nested = new ScriptNestedExpression(rightBin);
                        binaryExpression.Right = nested;
                        rightBin.MoveTrailingTriviasTo(nested.CloseParen, true);
                    }
                }

                if (binaryExpression.Operator == ScriptBinaryOperator.Divide || binaryExpression.Operator == ScriptBinaryOperator.DivideRound || (_isScientific && binaryExpression.Operator == ScriptBinaryOperator.Power))
                {
                    var nested = new ScriptNestedExpression()
                    {
                        Expression = binaryExpression
                    };
                    binaryExpression.MoveTrailingTriviasTo(nested.CloseParen, true);
                    return(nested);
                }
            }

            return(binaryExpression);
        }
Example #3
0
        public override object Evaluate(TemplateContext context)
        {
            var caseValue = context.PeekCase();

            foreach (var value in Values)
            {
                var whenValue = context.Evaluate(value);
                var result    = ScriptBinaryExpression.Evaluate(context, Span, ScriptBinaryOperator.CompareEqual, caseValue, whenValue);
                if (result is bool && (bool)result)
                {
                    return(context.Evaluate(Body));
                }
            }
            return(context.Evaluate(Next));
        }
        private static ScriptExpression ParseBinaryExpressionTree(BinaryExpressionIterator it, int precedence, bool isExpectingExpression)
        {
            ScriptExpression leftOperand = null;

            while (it.HasCurrent)
            {
                var op          = it.Current;
                var nextOperand = op.Expression;

                if (nextOperand == null)
                {
                    var newPrecedence = Parser.GetDefaultBinaryOperatorPrecedence(op.Operator);

                    // Probe if the next argument is a function call
                    if (!isExpectingExpression && it.HasNext && it.PeekNext().CallKind != FunctionCallKind.None)
                    {
                        // If it is a function call, use its precedence
                        newPrecedence = newPrecedence < ImplicitFunctionCallPrecedence ? newPrecedence : ImplicitFunctionCallPrecedence;
                    }

                    if (newPrecedence <= precedence)
                    {
                        break;
                    }

                    it.MoveNext(); // Skip operator

                    var binary = new ScriptBinaryExpression
                    {
                        Left          = leftOperand,
                        Operator      = op.Operator,
                        OperatorToken = op.OperatorToken ?? ScriptToken.Star(), // Force an explicit token so that we don't enter an infinite loop when formatting an expression
                        Span          =
                        {
                            Start = leftOperand.Span.Start
                        },
                    };

                    binary.Right    = ParseBinaryExpressionTree(it, newPrecedence, isExpectingExpression);
                    binary.Span.End = binary.Right.Span.End;
                    leftOperand     = binary;
                }
                else
                {
                    if (!isExpectingExpression && op.CallKind != FunctionCallKind.None)
                    {
                        var functionCall = new ScriptFunctionCall
                        {
                            Target       = nextOperand,
                            ExplicitCall = true,
                            Span         =
                            {
                                Start = nextOperand.Span.Start
                            }
                        };

                        // Skip the function and move to the operator
                        if (!it.MoveNext())
                        {
                            throw new ScriptRuntimeException(nextOperand.Span, $"The function is expecting at least one argument");
                        }

                        // We are expecting only an implicit multiply. Anything else is invalid.
                        if (it.Current.Expression == null && (it.Current.Operator != ScriptBinaryOperator.Multiply || it.Current.OperatorToken != null))
                        {
                            throw new ScriptRuntimeException(nextOperand.Span, $"The function expecting one argument cannot be followed by the operator {it.Current.OperatorToken?.ToString() ?? it.Current.Operator.ToText()}");
                        }

                        // Skip the operator
                        if (!it.MoveNext())
                        {
                            throw new ScriptRuntimeException(nextOperand.Span, $"The function is expecting at least one argument");
                        }

                        var argExpression = ParseBinaryExpressionTree(it, ImplicitFunctionCallPrecedence, op.CallKind == FunctionCallKind.Expression);
                        functionCall.Arguments.Add(argExpression);
                        functionCall.Span.End = argExpression.Span.End;

                        leftOperand = functionCall;
                        continue;
                    }

                    leftOperand = nextOperand;
                    it.MoveNext();
                }
            }

            return(leftOperand);
        }
        private ScriptExpression Rewrite(TemplateContext context, int precedence, bool expectingExpression = false)
        {
            ScriptExpression leftValue = null;

            while (_index < Count)
            {
                ScriptExpression nextExpression = this[_index];

                // leftValue (*/^%)
                if (nextExpression is ScriptArgumentBinary bin)
                {
                    if (leftValue == null)
                    {
                        throw new ScriptRuntimeException(nextExpression.Span, precedence == 0 ? "This operator cannot be after a function call." : "This operator cannot be applied here.");
                    }

                    // Power has higher precedence than */%
                    var newPrecedence = Parser.GetDefaultBinaryOperatorPrecedence(bin.Operator);

                    if (newPrecedence <= precedence) // if new operator has lower precedence than previous, exit
                    {
                        break;
                    }

                    var rightArgExpr = this[_index + 1];
                    if (rightArgExpr is IScriptVariablePath)
                    {
                        var rightArg = context.Evaluate(rightArgExpr, true);
                        if (rightArg is IScriptCustomFunction function)
                        {
                            var maxArg = function.RequiredParameterCount;
                            if (maxArg >= 1 && PrecedenceTopLevel != precedence)
                            {
                                break;
                            }
                        }
                    }

                    // Skip the binary argument
                    _index++;
                    var rightValue = Rewrite(context, newPrecedence);

                    leftValue = new ScriptBinaryExpression()
                    {
                        Left          = leftValue,
                        Operator      = bin.Operator,
                        OperatorToken = bin.Operator.ToToken(),
                        Right         = rightValue,
                    };
                    continue;
                }

                object result = null;
                if (!expectingExpression && nextExpression is IScriptVariablePath)
                {
                    var restoreStrictVariables = context.StrictVariables;

                    // Don't fail on trying to lookup for a variable
                    context.StrictVariables = false;
                    try
                    {
                        result = context.Evaluate(nextExpression, true);
                    }
                    catch (ScriptRuntimeException) when(context.IgnoreExceptionsWhileRewritingScientific)
                    {
                        // ignore any exceptions during trial evaluating as we could try to evaluate
                        // variable that aren't setup
                    }
                    finally
                    {
                        context.StrictVariables = restoreStrictVariables;
                    }

                    // If one argument is a function, the remaining arguments
                    if (result is IScriptCustomFunction function)
                    {
                        var maxArg = function.RequiredParameterCount != 0 ? function.RequiredParameterCount : function.ParameterCount > 0 ? 1 : 0;

                        if (maxArg > 0)
                        {
                            if (PrecedenceTopLevel == precedence || leftValue == null)
                            {
                                var functionCall = new ScriptFunctionCall {
                                    Target = (ScriptExpression)nextExpression.Clone(), ExplicitCall = true
                                };
                                _index++;

                                var isExpectingExpression = function.IsParameterType <ScriptExpression>(0);

                                if (_index == Count)
                                {
                                    throw new ScriptRuntimeException(nextExpression.Span, "The function is expecting a parameter");
                                }

                                var arg = Rewrite(context, 0, isExpectingExpression);

                                functionCall.Arguments.Add(arg);

                                if (leftValue == null)
                                {
                                    leftValue = functionCall;
                                }
                                else
                                {
                                    leftValue = new ScriptBinaryExpression()
                                    {
                                        Left          = leftValue,
                                        Operator      = ScriptBinaryOperator.Multiply,
                                        OperatorToken = ScriptToken.Star(),
                                        Right         = functionCall,
                                    };
                                }
                                continue;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                }

                {
                    if (leftValue == null)
                    {
                        leftValue = (ScriptExpression)nextExpression.Clone();
                        _index++;
                    }
                    else
                    {
                        int precedenceOfImplicitMultiply = result is IScriptCustomImplicitMultiplyPrecedence ? PrecedenceOfMultiply : PrecedenceOfMultiply + 1;

                        if (precedenceOfImplicitMultiply <= precedence)
                        {
                            break;
                        }
                        // Implicit  the binary argument
                        var rightValue = Rewrite(context, precedenceOfImplicitMultiply);

                        if (rightValue is ScriptLiteral)
                        {
                            throw new ScriptRuntimeException(rightValue.Span, "Cannot use a literal on the right side of an implicit multiplication.");
                        }

                        leftValue = new ScriptBinaryExpression()
                        {
                            Left          = leftValue,
                            Operator      = ScriptBinaryOperator.Multiply,
                            OperatorToken = ScriptToken.Star(),
                            Right         = rightValue,
                        };
                    }
                }
            }

            return(leftValue);
        }