private static ReadOnlySpan <char> ParseJsConditionalExpression(this ReadOnlySpan <char> literal, JsToken test, out JsConditionalExpression expression) { literal = literal.Advance(1); literal = literal.ParseJsExpression(out var consequent); literal = literal.AdvancePastWhitespace(); if (!literal.FirstCharEquals(':')) { throw new SyntaxErrorException($"Expected Conditional ':' but was {literal.DebugFirstChar()}"); } literal = literal.Advance(1); literal = literal.ParseJsExpression(out var alternate); expression = new JsConditionalExpression(test, consequent, alternate); return(literal); }
public static ReadOnlySpan <char> ParseJsCallExpression(this ReadOnlySpan <char> literal, out JsCallExpression expression, bool filterExpression = false) { literal = literal.ParseIdentifier(out var token); if (!(token is JsIdentifier identifier)) { throw new SyntaxErrorException($"Expected identifier but instead found {token.DebugToken()}"); } literal = literal.AdvancePastWhitespace(); if (literal.FirstCharEquals(WhitespaceArgument)) { literal = literal.Advance(1); literal = literal.ParseWhitespaceArgument(out var argument); expression = new JsCallExpression(identifier, argument); return(literal); } if (literal.StartsWith("=>")) { literal = literal.ParseArrowExpressionBody(new[] { new JsIdentifier("it") }, out var arrowExpr); expression = new JsCallExpression(identifier, arrowExpr); return(literal); } if (!literal.FirstCharEquals('(')) { expression = new JsCallExpression(identifier); return(literal); } literal = literal.Advance(1); literal = literal.ParseArguments(out var args, termination: ')'); expression = new JsCallExpression(identifier, args.ToArray()); return(literal); }
internal static ReadOnlySpan <char> ParseArguments(this ReadOnlySpan <char> literal, out List <JsToken> arguments, char termination) { arguments = new List <JsToken>(); while (!literal.IsNullOrEmpty()) { JsToken listValue; literal = literal.AdvancePastWhitespace(); if (literal[0] == termination) { literal = literal.Advance(1); break; } if (literal.StartsWith("...")) { literal = literal.Advance(3); literal = literal.ParseJsExpression(out listValue); if (!(listValue is JsIdentifier) && !(listValue is JsArrayExpression)) { throw new SyntaxErrorException($"Spread operator expected array but instead found {listValue.DebugToken()}"); } listValue = new JsSpreadElement(listValue); } else { literal = literal.ParseJsExpression(out listValue); } arguments.Add(listValue); literal = literal.AdvancePastWhitespace(); if (literal.IsNullOrEmpty()) { break; } if (literal[0] == termination) { literal = literal.Advance(1); break; } literal = literal.AdvancePastWhitespace(); var c = literal.SafeGetChar(0); if (c.IsEnd() || c == termination) { literal = literal.Advance(1); break; } if (c != ',') { throw new SyntaxErrorException($"Unterminated arguments expression near: {literal.DebugLiteral()}"); } literal = literal.Advance(1); literal = literal.AdvancePastWhitespace(); } literal = literal.AdvancePastWhitespace(); return(literal); }
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 is JsExpression jsExpr ? jsExpr : 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 is JsExpression jsExpr ? jsExpr : throw new SyntaxErrorException($"Expected Expression but was {lhs.DebugToken()}"); } } return(literal); }
public static ReadOnlySpan <char> ParseJsExpression(this ReadOnlySpan <char> literal, out JsToken token, bool filterExpression) { var peekLiteral = literal.ParseJsToken(out var node, filterExpression: filterExpression); peekLiteral = peekLiteral.AdvancePastWhitespace(); var peekChar = peekLiteral.SafeGetChar(0); if (literal.IsNullOrEmpty() || peekChar.IsExpressionTerminatorChar()) { token = node; return(peekLiteral); } if (peekChar == ConditionalExpressionTestChar && peekLiteral.SafeGetChar(1) != ConditionalExpressionTestChar) // not ?? { literal = peekLiteral.ParseJsConditionalExpression(node, out var expression); token = expression; return(literal); } if (node is JsIdentifier identifier) { if (peekLiteral.StartsWith("=>")) { literal = peekLiteral.ParseArrowExpressionBody(new[] { identifier }, out var arrowExpr); token = arrowExpr; return(literal); } var kind = identifier.Name == "var" ? JsVariableDeclarationKind.Var : identifier.Name == "let" ? JsVariableDeclarationKind.Let : identifier.Name == "const" ? JsVariableDeclarationKind.Const : (JsVariableDeclarationKind?)null; if (kind != null) { literal = peekLiteral.ParseVariableDeclaration(kind.Value, out var varDec); token = varDec; return(literal); } if (peekChar == '=' && peekLiteral.SafeGetChar(1) != '=') // not ==, === { literal = peekLiteral.ParseAssignmentExpression(identifier, out var assignExpr); token = assignExpr; return(literal); } } peekLiteral = peekLiteral.AdvancePastWhitespace(); if (!peekLiteral.IsNullOrEmpty()) { if (filterExpression && peekLiteral.Length > 2) { var char1 = peekLiteral[0]; var char2 = peekLiteral[1]; if ((char1 == '|' && char2 != '|') || (char1 == '}' && char2 == '}')) { token = node; return(peekLiteral); } } } peekLiteral = peekLiteral.ParseJsBinaryOperator(out var op); if (op != null) { literal = literal.ParseBinaryExpression(out var expr, filterExpression); token = expr; literal = literal.AdvancePastWhitespace(); if (literal.FirstCharEquals(ConditionalExpressionTestChar)) { literal = literal.ParseJsConditionalExpression(expr, out var conditionalExpr); token = conditionalExpr; return(literal); } return(literal); } literal = peekLiteral.ParseJsMemberExpression(ref node, filterExpression); token = node; return(literal); }
public static ReadOnlySpan <char> ParseJsExpression(this ReadOnlySpan <char> literal, out JsToken token, bool filterExpression) { var peekLiteral = literal.ParseJsToken(out var node, filterExpression: filterExpression); peekLiteral = peekLiteral.AdvancePastWhitespace(); var peekChar = peekLiteral.SafeGetChar(0); if (literal.IsNullOrEmpty() || peekChar.IsExpressionTerminatorChar()) { token = node; return(peekLiteral); } if (peekChar == ConditionalExpressionTestChar && peekLiteral.SafeGetChar(1) != ConditionalExpressionTestChar) // not ?? { literal = peekLiteral.ParseJsConditionalExpression(node, out var expression); token = expression; return(literal); } if (node is JsIdentifier identifier && peekLiteral.StartsWith("=>")) { literal = peekLiteral.ParseArrowExpressionBody(new[] { identifier }, out var arrowExpr); token = arrowExpr; return(literal); } peekLiteral = peekLiteral.AdvancePastWhitespace(); if (!peekLiteral.IsNullOrEmpty()) { if (filterExpression && peekLiteral.Length > 2) { var char1 = peekLiteral[0]; var char2 = peekLiteral[1]; if ((char1 == '|' && char2 != '|') || (char1 == '}' && char2 == '}')) { token = node; return(peekLiteral); } } } peekLiteral = peekLiteral.ParseJsBinaryOperator(out var op); if (op != null) { literal = literal.ParseBinaryExpression(out var expr, filterExpression); token = expr; literal = literal.AdvancePastWhitespace(); if (literal.FirstCharEquals(ConditionalExpressionTestChar)) { literal = literal.ParseJsConditionalExpression(expr, out var conditionalExpr); token = conditionalExpr; return(literal); } return(literal); } literal = peekLiteral.ParseJsMemberExpression(ref node, filterExpression); token = node; return(literal); }