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));
        }
        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);
        }