private ITexlSource ParseTrivia(TokenCursor cursor = null) { cursor = cursor ?? _curs; var sources = new List <ITexlSource>(); if (_extraTrivia != null) { sources.Add(_extraTrivia); _extraTrivia = null; } bool triviaFound; do { triviaFound = false; var tokens = cursor.SkipWhitespace(); if (tokens.Any()) { sources.Add(new WhitespaceSource(tokens)); triviaFound = true; } if (cursor.TidCur == TokKind.Comment) { var comment = cursor.TokMove().As <CommentToken>(); sources.Add(new TokenSource(comment)); if (comment.IsOpenBlock) { PostError(comment, TexlStrings.ErrMissingEndOfBlockComment); } _comments.Add(comment); triviaFound = true; } } while (triviaFound); if (sources.Count() == 1) { return(sources.Single()); } else { return(new SpreadSource(sources)); } }
// Parses the next (maximal) expression with precedence >= precMin. private TexlNode ParseExpr(Precedence precMin, TexlNode node = null) { // ParseOperand may accept PrefixUnary and higher, so ParseExpr should never be called // with precMin > Precedence.PrefixUnary - it will not correctly handle those cases. Contracts.Assert(Precedence.None <= precMin && precMin <= Precedence.PrefixUnary); try { // The parser is recursive. Deeply nested invocations (over 200 deep) and other // intentionally miscrafted rules can throw it off, causing stack overflows. // Ensure the product doesn't crash in such situations, but instead post // corresponding parse errors. if (node == null) { if (++_depth > _maxAllowedExpressionDepth) { return(CreateError(_curs.TokMove(), TexlStrings.ErrRuleNestedTooDeeply)); } // Get the left operand. node = ParseOperand(); } // Process operators and right operands as long as the precedence bound is satisfied. for (;;) { var leftTrivia = ParseTrivia(); Contracts.AssertValue(node); Token tok; TexlNode right; Identifier identifier; ITexlSource rightTrivia; switch (_curs.TidCur) { case TokKind.PercentSign: Contracts.Assert(precMin <= Precedence.PostfixUnary); tok = _curs.TokMove(); node = new UnaryOpNode( ref _idNext, tok, new SourceList(new NodeSource(node), new TokenSource(tok)), UnaryOp.Percent, node); break; case TokKind.Dot: case TokKind.Bang: Contracts.Assert(precMin <= Precedence.Primary); DottedNameNode leftDotted; if ((leftDotted = node as DottedNameNode) != null && leftDotted.Token.Kind != _curs.TidCur && leftDotted.Token.Kind != TokKind.BracketOpen) { // Can't mix and match separators. E.g. A.B!C is invalid. goto case TokKind.False; } tok = _curs.TokMove(); rightTrivia = ParseTrivia(); identifier = ParseIdentifier(); node = new DottedNameNode( ref _idNext, tok, new SourceList( new NodeSource(node), new TokenSource(tok), new SpreadSource(rightTrivia), new IdentifierSource(identifier)), node, identifier, null); if (node.Depth > _maxAllowedExpressionDepth) { return(CreateError(node.Token, TexlStrings.ErrRuleNestedTooDeeply)); } break; case TokKind.Caret: Contracts.Assert(precMin <= Precedence.Power); node = ParseBinary(node, leftTrivia, BinaryOp.Power, Precedence.PrefixUnary); break; case TokKind.Mul: if (precMin > Precedence.Mul) { goto default; } node = ParseBinary(node, leftTrivia, BinaryOp.Mul, Precedence.Mul + 1); break; case TokKind.Div: if (precMin > Precedence.Mul) { goto default; } tok = _curs.TokMove(); rightTrivia = ParseTrivia(); right = ParseExpr(Precedence.Mul + 1); node = MakeBinary(BinaryOp.Div, node, leftTrivia, tok, rightTrivia, right); break; case TokKind.Sub: if (precMin > Precedence.Add) { goto default; } tok = _curs.TokMove(); rightTrivia = ParseTrivia(); right = ParseExpr(Precedence.Add + 1); right = new UnaryOpNode(ref _idNext, tok, right.SourceList, UnaryOp.Minus, right); node = MakeBinary(BinaryOp.Add, node, leftTrivia, tok, rightTrivia, right); break; case TokKind.Add: if (precMin > Precedence.Add) { goto default; } node = ParseBinary(node, leftTrivia, BinaryOp.Add, Precedence.Add + 1); break; case TokKind.Ampersand: if (precMin > Precedence.Concat) { goto default; } node = ParseBinary(node, leftTrivia, BinaryOp.Concat, Precedence.Concat + 1); break; case TokKind.KeyAnd: case TokKind.And: if (precMin > Precedence.And) { goto default; } node = ParseBinary(node, leftTrivia, BinaryOp.And, Precedence.And + 1); break; case TokKind.KeyOr: case TokKind.Or: if (precMin > Precedence.Or) { goto default; } node = ParseBinary(node, leftTrivia, BinaryOp.Or, Precedence.Or + 1); break; // Comparison operators // expr = expr // expr <> expr case TokKind.Equ: if (precMin > Precedence.Compare) { goto default; } node = ParseBinary(node, leftTrivia, BinaryOp.Equal, Precedence.Compare + 1); break; case TokKind.LssGrt: if (precMin > Precedence.Compare) { goto default; } node = ParseBinary(node, leftTrivia, BinaryOp.NotEqual, Precedence.Compare + 1); break; // expr < expr case TokKind.Lss: if (precMin > Precedence.Compare) { goto default; } node = ParseBinary(node, leftTrivia, BinaryOp.Less, Precedence.Compare + 1); break; // expr <= expr case TokKind.LssEqu: if (precMin > Precedence.Compare) { goto default; } node = ParseBinary(node, leftTrivia, BinaryOp.LessEqual, Precedence.Compare + 1); break; // expr > expr case TokKind.Grt: if (precMin > Precedence.Compare) { goto default; } node = ParseBinary(node, leftTrivia, BinaryOp.Greater, Precedence.Compare + 1); break; // expr >= expr case TokKind.GrtEqu: if (precMin > Precedence.Compare) { goto default; } node = ParseBinary(node, leftTrivia, BinaryOp.GreaterEqual, Precedence.Compare + 1); break; case TokKind.Ident: case TokKind.NumLit: case TokKind.StrLit: case TokKind.True: case TokKind.False: PostError(_curs.TokCur, TexlStrings.ErrOperatorExpected); tok = _curs.TokMove(); rightTrivia = ParseTrivia(); right = ParseExpr(Precedence.Error); node = MakeBinary(BinaryOp.Error, node, leftTrivia, tok, rightTrivia, right); break; case TokKind.ParenOpen: DottedNameNode dotted; if ((dotted = node as DottedNameNode) == null || !dotted.HasPossibleNamespaceQualifier) { goto default; } node = ParseInvocationWithNamespace(dotted); break; case TokKind.In: if (precMin > Precedence.In) { goto default; } node = ParseBinary(node, leftTrivia, BinaryOp.In, Precedence.In + 1); break; case TokKind.Exactin: if (precMin > Precedence.In) { goto default; } node = ParseBinary(node, leftTrivia, BinaryOp.Exactin, Precedence.In + 1); break; case TokKind.As: if (precMin > Precedence.As) { goto default; } node = ParseAs(node, leftTrivia); break; case TokKind.Semicolon: // Only allow this when expression chaining is enabled (e.g. in behavior rules). if ((_flags & Flags.EnableExpressionChaining) == 0) { goto case TokKind.False; } if (precMin > Precedence.None) { goto default; } node = ParseExprChain(node, leftTrivia); break; case TokKind.BracketOpen: // Note we explicitly forbid [@foo][@bar], and also A!B!C[@foo], since these are syntactically nonsensical at the moment. FirstNameNode first; if ((first = node as FirstNameNode) == null || first.Ident.AtToken != null || _curs.TidPeek() != TokKind.At) { goto default; } node = ParseScopeField(first); break; case TokKind.Comment: Contracts.Assert(false, "A stray comment was found"); _curs.TokMove(); return(node); case TokKind.Eof: if (_after == null) { _after = new SourceList(leftTrivia); } else { _after = new SourceList(new SpreadSource(_after.Sources), new SpreadSource(leftTrivia)); } return(node); default: AddExtraTrivia(leftTrivia); return(node); } } } finally { --_depth; } }