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