/// <summary> /// Gets the next binary precedence. /// </summary> /// <param name="literal">The literal.</param> /// <returns>System.Int32.</returns> static int GetNextBinaryPrecedence(this ReadOnlySpan <char> literal) { if (!literal.IsNullOrEmpty() && !literal[0].IsExpressionTerminatorChar()) { literal.ParseJsBinaryOperator(out var binaryOp); if (binaryOp != null) { return(JsTokenUtils.GetBinaryPrecedence(binaryOp.Token)); } } return(0); }
/// <summary> /// Parses the binary expression. /// </summary> /// <param name="literal">The literal.</param> /// <param name="expr">The expr.</param> /// <param name="filterExpression">if set to <c>true</c> [filter expression].</param> /// <returns>ReadOnlySpan<System.Char>.</returns> /// <exception cref="ServiceStack.Script.SyntaxErrorException">Expected Expression but was {lhs.DebugToken()}</exception> /// <exception cref="ServiceStack.Script.SyntaxErrorException">Expected binary operator near: {literal.DebugLiteral()}</exception> /// <exception cref="ServiceStack.Script.SyntaxErrorException">Expected expression near: '{literal.DebugLiteral()}'</exception> /// <exception cref="ServiceStack.Script.SyntaxErrorException">Expected Expression but was {lhs.DebugToken()}</exception> public static ReadOnlySpan <char> ParseBinaryExpression(this ReadOnlySpan <char> literal, out JsExpression expr, bool filterExpression) { literal = literal.AdvancePastWhitespace(); literal = literal.ParseJsToken(out var lhs, filterExpression: filterExpression); if (literal.IsNullOrEmpty()) { expr = lhs as JsExpression ?? throw new SyntaxErrorException($"Expected Expression but was {lhs.DebugToken()}"); } else { literal = literal.ParseJsBinaryOperator(out var op); if (op == null) { throw new SyntaxErrorException($"Expected binary operator near: {literal.DebugLiteral()}"); } var prec = JsTokenUtils.GetBinaryPrecedence(op.Token); if (prec > 0 || op == JsAssignment.Operator) { literal = literal.ParseJsToken(out JsToken rhs, filterExpression: filterExpression); var stack = new Stack <JsToken>(); stack.Push(lhs); stack.Push(op); stack.Push(rhs); var precedences = new List <int> { prec }; while (true) { literal = literal.AdvancePastWhitespace(); if (filterExpression && literal.Length > 2 && literal[0] == '|' && literal[1] != '|') { break; } prec = literal.GetNextBinaryPrecedence(); if (prec == 0) { break; } while (stack.Count > 2 && prec <= precedences[precedences.Count - 1]) { rhs = stack.Pop(); var operand = (JsBinaryOperator)stack.Pop(); precedences.RemoveAt(precedences.Count - 1); lhs = stack.Pop(); stack.Push(CreateJsExpression(lhs, operand, rhs)); } literal = literal.ParseJsBinaryOperator(out op); if (literal.IsNullOrEmpty()) { throw new SyntaxErrorException($"Expected expression near: '{literal.DebugLiteral()}'"); } literal = literal.ParseJsToken(out var token, filterExpression: filterExpression); stack.Push(op); stack.Push(token); precedences.Add(prec); } var i = stack.Count - 1; var ret = stack.Pop(); while (stack.Count > 0) { op = (JsBinaryOperator)stack.Pop(); lhs = stack.Pop(); ret = CreateJsExpression(lhs, op, ret); } expr = (JsExpression)ret; } else { expr = lhs as JsExpression ?? throw new SyntaxErrorException($"Expected Expression but was {lhs.DebugToken()}"); } } return(literal); }