/// <summary>
        /// Deep-clone the code object.
        /// </summary>
        public override CodeObject Clone()
        {
            Unrecognized clone = (Unrecognized)base.Clone();

            clone._expressions = ChildListHelpers.Clone(_expressions, clone);
            return(clone);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Parse an expression, stopping when default terminators, or the specified terminators, or a higher-precedence
        /// operator is encountered.
        /// </summary>
        /// <param name="parser">The parser object.</param>
        /// <param name="parent">The parent object.</param>
        /// <param name="isTopLevel">True if EOL comments can be associated with the expression during parsing - generally
        /// true if the parent is a statement or expression list, but with some exceptions.</param>
        /// <param name="terminator">Optional terminating characters (null if none).</param>
        /// <param name="flags">Parsing flags.</param>
        /// <returns>The parsed <see cref="Expression"/>.</returns>
        public static Expression Parse(Parser parser, CodeObject parent, bool isTopLevel, string terminator, ParseFlags flags)
        {
            // Save the starting token of the expression for later
            Token startingToken = parser.Token;

            // Start a new Unused list in the parser
            parser.PushUnusedList();

            // Parse an expression, which can be in one of the following formats:
            //   - An identifier token, optionally followed by an operator (which is parsed only if precedence rules determine it should be)
            //   - An operator (which will itself look for previous and/or following expressions when parsed)
            //   - An open paren, expression, close paren sequence (handled by the installed parse-point above), optionally followed by an operator
            // Any other sequence will cause parsing of the expression to cease.
            // The expression will be terminated by any of ';,}]', or other specified terminator.

            // Create a string of possible terminators (assuming 1 char terminators for now)
            string terminators = Statement.ParseTokenTerminator + ParseTokenSeparator + Block.ParseTokenEnd + Index.ParseTokenEnd + terminator;

            // Keep a reference to the last token so we can move any skipped non-EOL comments to the expression later
            Token lastToken = parser.LastToken;

            // Loop until EOF or we find a terminator, or for directive expressions stop if we find a comment or a token on a new line.
            // NOTE: Keep this logic in sync with the 'if' statement further down in the loop that checks for termination.
            while (parser.TokenText != null &&
                   (parser.TokenText.Length != 1 || terminators.IndexOf(parser.TokenText[0]) < 0) &&
                   (!parser.InDirectiveExpression || ((parser.LastToken.TrailingComments == null || parser.LastToken.TrailingComments.Count == 0) && !parser.Token.IsFirstOnLine)))
            {
process_next:
                bool skipTerminationCheck = false;

                // Process the current token (will process operators)
                CodeObject obj = parser.ProcessToken(parent, flags | ParseFlags.Expression);
                if (obj != null)
                {
                    // If we got something, save it for later.
                    // Don't move any EOL comments here - they should have already been processed.

                    if (obj is CompilerDirective)
                    {
                        // If we have a compiler directive, and there's a preceeding unused object, add it there
                        CodeObject lastUnusedCodeObject = parser.LastUnusedCodeObject;
                        if (lastUnusedCodeObject != null && !(lastUnusedCodeObject is CompilerDirective))
                        {
                            lastUnusedCodeObject.AttachAnnotation((CompilerDirective)obj, AnnotationFlags.IsPostfix);
                        }
                        else
                        {
                            parser.AddUnused(obj);  // Add the object to the unused list
                            skipTerminationCheck = true;
                        }
                    }
                    else
                    {
                        obj.ParseUnusedAnnotations(parser, parent, true);  // Parse any annotations from the Unused list
                        parser.AddUnused(obj);                             // Add the object to the unused list
                    }
                }

                // Stop if EOF or we find a terminator, or for directive expressions stop if we find a comment or a token on a new line.
                // NOTE: Keep this logic in sync with that in the condition of the parent 'while' loop.
                if (parser.TokenText == null ||
                    (parser.TokenText.Length == 1 && terminators.IndexOf(parser.TokenText[0]) >= 0) ||
                    (parser.InDirectiveExpression && ((parser.LastToken.TrailingComments != null && parser.LastToken.TrailingComments.Count != 0) || parser.Token.IsFirstOnLine)))
                {
                    // Don't abort here on a '{' terminator if we're in a doc comment and we appear to have type arguments using
                    // braces (as opposed to an Initializer after a NewObject).  Go process the next object immediately instead.
                    if (parser.InDocComment && parser.TokenText == TypeRefBase.ParseTokenAltArgumentStart && parser.HasUnusedIdentifier &&
                        TypeRefBase.PeekTypeArguments(parser, TypeRefBase.ParseTokenAltArgumentEnd, flags))
                    {
                        goto process_next;
                    }
                    break;
                }

                // If the current token is the start of a compiler directive, check for special situations in which we want to skip
                // the termination check below.  This allows the directive to be attached to preceeding code objects such as literals
                // or operators, while not attaching to simple name or type expressions which might be part of a namespace or type header.
                if (parser.TokenText == CompilerDirective.ParseToken)
                {
                    CodeObject lastUnusedCodeObject = parser.LastUnusedCodeObject;
                    if (lastUnusedCodeObject is Literal || lastUnusedCodeObject is Operator)
                    {
                        skipTerminationCheck = true;
                        // Also, capture any pending trailing comments
                        if (obj != null)
                        {
                            obj.MoveCommentsAsPost(parser.LastToken);
                        }
                    }
                }

                // If we don't have a specific terminating character, then we're parsing a sub-expression and we should stop when we
                // get to an invalid operator, or an operator of greater precedence.  Skip this check if we just parsed a compiler
                // directive and didn't have a preceeding code object to attach it to, or if we're about to parse a compiler directive
                // and we have an unused code object that we'd like to attach it to.
                if (terminator == null && !skipTerminationCheck)
                {
                    // Check for '{' when used inside a doc comment in a generic type constructor or generic method instance
                    if (parser.InDocComment && parser.TokenText == TypeRefBase.ParseTokenAltArgumentStart &&
                        TypeRefBase.PeekTypeArguments(parser, TypeRefBase.ParseTokenAltArgumentEnd, flags))
                    {
                        continue;
                    }

                    // Check if the current token represents a valid operator
                    Parser.OperatorInfo operatorInfo = parser.GetOperatorInfoForToken();

                    // If the current token doesn't look like a valid operator, we're done with the expression
                    if (operatorInfo == null)
                    {
                        break;
                    }

                    // We have an operator - check if our parent is also an operator
                    if (parent is Operator)
                    {
                        // Special cases for Types:  Some operator symbols are overloaded and can also be part
                        // of a type name.  We must detect these here, and continue processing in these cases,
                        // skipping the operator precedence checks below that terminate the current expression.

                        // Check for '[' when used in an array type name
                        if (parser.TokenText == TypeRefBase.ParseTokenArrayStart && TypeRefBase.PeekArrayRanks(parser))
                        {
                            continue;
                        }

                        // Check for '<' when used in a generic type constructor or generic method instance
                        if (parser.TokenText == TypeRefBase.ParseTokenArgumentStart && TypeRefBase.PeekTypeArguments(parser, TypeRefBase.ParseTokenArgumentEnd, flags))
                        {
                            continue;
                        }

                        // Do NOT check for '?' used for nullable types, because it applies to the entire
                        // expression on the left, so we DO want to terminate processing.

                        // Determine the precedence of the parent operator
                        // NOTE: See the bottom of Operator.cs for a quick-reference of operator precedence.
                        int parentPrecedence = ((Operator)parent).GetPrecedence();

                        // Stop parsing if the parent operator has higher precedence
                        if (parentPrecedence < operatorInfo.Precedence)
                        {
                            break;
                        }

                        // If the parent has the same precedence, stop parsing if the operator is left-associative
                        if (parentPrecedence == operatorInfo.Precedence && operatorInfo.LeftAssociative)
                        {
                            break;
                        }
                    }
                }
            }

            // Get the expression
            Expression expression = parser.RemoveLastUnusedExpression();

            if (expression != null)
            {
                // Attach any skipped non-EOL comments from the front of the expression, but only if we're a top-level expression
                // (otherwise, comments that preceed a sub-expression will get attached to an outer expression instead).  This
                // prevents lost comments in places such as between a 'return' and the expression that follows.
                if (isTopLevel)
                {
                    expression.MoveAllComments(lastToken);
                }

                // If this is a top-level expression or if the next token is a close paren, move any trailing comments on the last
                // token of the expression as post comments. This prevents lost comments in places such as when some trailing parts of
                // an 'if' conditional expression are commented-out, or the trailing parts of any sub-expression before a close paren.
                if ((isTopLevel || parser.TokenText == ParseTokenEndGroup) && parser.LastToken.HasTrailingComments && !parser.InDirectiveExpression)
                {
                    expression.MoveCommentsAsPost(parser.LastToken);
                }
            }

            // Flush remaining unused objects as Unrecognized objects
            while (parser.HasUnused)
            {
                Expression preceedingUnused = parser.RemoveLastUnusedExpression(true);
                if (preceedingUnused != null)
                {
                    if (expression == null)
                    {
                        expression = new Unrecognized(false, parser.InDocComment, preceedingUnused);
                    }
                    else if (expression is Unrecognized && !expression.HasParens)
                    {
                        ((Unrecognized)expression).AddLeft(preceedingUnused);
                    }
                    else
                    {
                        expression = new Unrecognized(false, parser.InDocComment, preceedingUnused, expression);
                    }
                }
                else
                {
                    // If we have no expression to put them on, then parse any preceeding compiler directives into a temp object for later retrieval
                    if (expression == null)
                    {
                        expression = new TempExpr();
                    }
                    expression.ParseUnusedAnnotations(parser, parent, true);
                    break;
                }
            }
            if (expression is Unrecognized)
            {
                ((Unrecognized)expression).UpdateMessage();
            }

            parser.Unused.Clear();

            // Restore the previous Unused list in the parser
            parser.PopUnusedList();

            if (expression != null)
            {
                // Get any EOL comments
                if (parser.LastToken.HasTrailingComments)
                {
                    expression.MoveEOLComment(parser.LastToken);
                }

                // Set the parent starting token to the beginning of the expression
                parser.ParentStartingToken = startingToken;
            }

            return(expression);
        }