예제 #1
0
        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));
            }
        }
예제 #2
0
        // 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;
            }
        }