Пример #1
0
        /// <summary>
        /// Parse a <see cref="Cast"/> operator.
        /// </summary>
        public static Cast Parse(Parser parser, CodeObject parent, ParseFlags flags)
        {
            // Verify that we have a pattern of "(Type)", otherwise abort (so the expression parens logic can try parsing it).
            // Do NOT set the ParseFlags.Type flag here, because it might not be a type (it might be "(A * B)").
            // Also, verify that we're not inside a directive expression - casts aren't legal there.
            if (TypeRefBase.PeekType(parser, parser.PeekNextToken(), false, flags) && !parser.InDirectiveExpression)
            {
                Token last = parser.LastPeekedToken;
                if (last != null && last.Text == ParseTokenEnd)
                {
                    // Verify that the cast is either followed by a non-symbol, or various legal symbols (those that
                    // can only be unary operators).
                    Token next = parser.PeekNextToken();
                    if (next != null)
                    {
                        if (!next.IsSymbol || next.Text == ParseTokenStartGroup || next.Text == Complement.ParseToken ||
                            next.Text == Increment.ParseToken || next.Text == Decrement.ParseToken || next.Text == Not.ParseToken)
                        {
                            return(new Cast(parser, parent));
                        }

                        // In the case of the Negative and Positive unary operators following the Cast,
                        // it's impossible to be sure they aren't actually binary operators, such as "(v1)-v2" or
                        // "(v1)-(v2)" (yes, programmers actually write code like that for some reason!).
                        // For now, assume if the operator is followed by a space and/or a '(', it's a binary
                        // operator (so we don't have a Cast).  This will cover most cases, but not 100%.
                        // Any parse issues could be easily worked around by adding parens around the entire right
                        // expression being cast, such as "(v1)(-(v2))" to force a cast, or around either both sides
                        // or neither side of a binary operator to avoid a cast.
                        if (next.Text == Positive.ParseToken || next.Text == Negative.ParseToken)
                        {
                            next = parser.PeekNextToken();
                            if (next.Text != ParseTokenStartGroup && next.LeadingWhitespace.Length == 0)
                            {
                                return(new Cast(parser, parent));
                            }
                        }
                    }

                    // Otherwise, fail and treat it as a grouped expression instead.
                }
            }
            return(null);
        }
        /// <summary>
        /// Peek ahead at the input tokens to determine if they look like a valid LocalDecl.
        /// </summary>
        public static bool PeekLocalDecl(Parser parser)
        {
            bool valid = false;

            // Validate that we have what appears to be a valid Type followed by an identifier
            if (TypeRefBase.PeekType(parser, parser.Token, false, ParseFlags.Type))
            {
                Token next = parser.LastPeekedToken;
                if (next != null && next.IsIdentifier)
                {
                    // Also validate that it's followed by one of ";=),"
                    if (";=),".Contains(parser.PeekNextTokenText()))
                    {
                        valid = true;
                    }
                }
            }

            return(valid);
        }
        /// <summary>
        /// Parse a <see cref="NewObject"/> or <see cref="NewArray"/> operator.
        /// </summary>
        public static NewOperator Parse(Parser parser, CodeObject parent, ParseFlags flags)
        {
            // Abort if our parent is a TypeDecl (the 'new' is probably part of a method declaration)
            if (parent is TypeDecl)
            {
                return(null);
            }

            NewOperator result = null;

            // Peek ahead to see if we have a valid non-array type
            TypeRefBase.PeekType(parser, parser.PeekNextToken(), true, flags | ParseFlags.Type);
            Token token = parser.LastPeekedToken;

            if (token != null)
            {
                // If we found a '[', assume NewArray
                if (token.Text == NewArray.ParseTokenStart)
                {
                    result = new NewArray(parser, parent);
                }
                // If we found '(' or '{', assume NewObject
                else if (token.Text == ParameterDecl.ParseTokenStart || token.Text == Initializer.ParseTokenStart)
                {
                    result = new NewObject(parser, parent);
                }
            }

            // Last chance - invalid code might still parse better as a NewObject, so assume that's
            // what it is if our parent is a VariableDecl.
            if (result == null && parent is VariableDecl)
            {
                result = new NewObject(parser, parent);
            }

            // If we didn't create an object, return null (the 'new' is probably part of a method declaration)
            return(result);
        }
        protected static bool PeekConditional(Parser parser, CodeObject parent, int colonCount, ParseFlags flags)
        {
            // Unfortunately, determining if the '?' is definitely part of a '?:' pair as opposed to a nullable type declaration
            // isn't easy - in fact, it's the single hardest thing to parse in the entire language.  Nicely formatted code always
            // has a space before it in the first case, and not in the second, but code is often poorly formatted.  The only way
            // to be sure how to parse it is to peek ahead looking for the matching ':'.  We can parse in a very simplified manner
            // for efficiency, just keeping track of '()', '[]', '{}' pairs, aborting if we hit a ';' anywhere, or a ',' that's
            // not in a nested scope, or finally if we find the matching ':' (not in a nested scope).  If we're in the '?' clause
            // of a nested Conditional without parens, then we need to find an extra ':' for each nested level (colonCount).
            // One more thing - we have to handle '<>' with generic arguments in order to avoid aborting on a possible ','
            // inside them, but we also have to avoid any confusion with LessThan/GreatherThan operators.

            bool           firstToken = true;
            Stack <string> stack      = new Stack <string>(8);

            while (true)
            {
                Token next = parser.PeekNextToken();
                if (next == null)
                {
                    break;
                }
check:
                if (next.IsSymbol)
                {
                    // Abort if any invalid symbols appear immediately after the '?'
                    if (firstToken)
                    {
                        firstToken = false;
                        if (">)]};,".Contains(next.Text))
                        {
                            break;
                        }
                    }

                    // If we have a '<', skip over any possible generic type parameters so that we don't abort on a ','
                    if (next.Text == TypeRefBase.ParseTokenArgumentStart)
                    {
                        TypeRefBase.PeekTypeArguments(parser, TypeRefBase.ParseTokenArgumentEnd, flags);
                        next = parser.LastPeekedToken;
                    }

                    // Keep track of nested parens, brackets (new arrays), braces (initializers or generics in doc comments)
                    string nextText = next.Text;
                    if (nextText == ParseTokenStartGroup || nextText == NewArray.ParseTokenStart || nextText == Initializer.ParseTokenStart)
                    {
                        stack.Push(nextText);
                    }
                    else if (nextText == ParseTokenEndGroup)
                    {
                        // If we hit an unexpected ')', abort
                        if (stack.Count == 0 || stack.Peek() != ParseTokenStartGroup)
                        {
                            break;
                        }
                        stack.Pop();
                    }
                    else if (nextText == NewArray.ParseTokenEnd)
                    {
                        // If we hit an unexpected ']', abort
                        if (stack.Count == 0 || stack.Peek() != NewArray.ParseTokenStart)
                        {
                            break;
                        }
                        stack.Pop();
                    }
                    else if (nextText == Initializer.ParseTokenEnd)
                    {
                        // If we hit an unexpected '}', abort
                        if (stack.Count == 0 || stack.Peek() != Initializer.ParseTokenStart)
                        {
                            break;
                        }
                        stack.Pop();
                    }
                    else if (nextText == ParseToken1)
                    {
                        // If we found a '?', recursively call this routine to process it (in order to
                        // differentiate between a nested nullable type or another Conditional).
                        if (!PeekConditional(parser, parent, 1, flags))
                        {
                            // If it wasn't a Conditional, get the last token and check it
                            next = parser.LastPeekedToken;
                            goto check;
                        }
                    }
                    else if (stack.Count == 0)
                    {
                        // We're not inside any nested parens/brackets/braces/angle brackets.  Check for certain symbols:

                        // Terminate on a ',' or ';' (we ignore if nested because of anonymous method bodies)
                        if (nextText == ParseTokenSeparator || nextText == Statement.ParseTokenTerminator)
                        {
                            break;
                        }

                        // Process a ':'
                        if (nextText == ParseToken2)
                        {
                            // Assume we have a valid Conditional if the expected number of colons has been found
                            if (--colonCount == 0)
                            {
                                return(true);
                            }
                        }
                    }
                }
                else if (next.Text == NewOperator.ParseToken)
                {
                    // If we found a 'new', treat the following as a type (in order to avoid any trailing '?' of
                    // a nullable type from being treated as a nested conditional).
                    TypeRefBase.PeekType(parser, parser.PeekNextToken(), true, flags | ParseFlags.Type);
                    // Whether it worked or not, pick up with the last peeked token
                    next       = parser.LastPeekedToken;
                    firstToken = false;
                    goto check;
                }
                firstToken = false;
            }
            return(false);
        }