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