Exemplo n.º 1
0
        protected bool TryRecover(RecoveryModes mode,
                                  Predicate <char> condition,
                                  bool allowTransition,
                                  SpanFactory previousSpanFactory)
        {
            bool anyNewLines = false;

            while (!EndOfFile && !condition(CurrentCharacter))
            {
                // Eat whitespace
                Context.AcceptWhiteSpace(false);

                if (mode.HasFlag(RecoveryModes.Markup))
                {
                    // If we see new lines before a '<' then assume it's markup.
                    // This isn't 100% correct but it is the 80/90% case when typing razor code
                    if (anyNewLines && Context.MarkupParser.IsStartTag())
                    {
                        return(true);
                    }

                    // End tags are mostly unambiguous
                    // REVIEW: Does this make sense?
                    if (Context.MarkupParser.IsEndTag())
                    {
                        return(true);
                    }
                }

                if (mode.HasFlag(RecoveryModes.Code))
                {
                    if (TryRecover(allowTransition, previousSpanFactory))
                    {
                        return(true);
                    }
                }

                if (mode.HasFlag(RecoveryModes.Transition))
                {
                    if (CurrentCharacter == RazorParser.TransitionCharacter)
                    {
                        return(true);
                    }
                }

                anyNewLines = ParserHelpers.IsNewLine(CurrentCharacter);

                Context.AcceptCurrent();
            }
            return(false);
        }
Exemplo n.º 2
0
        private void ParseEndPsuedoTag(Stack <TagInfo> tags, TagInfo tag, bool inDocument)
        {
            Debug.Assert(tag.IsEndTag, "ParseEndPsuedoTag requires an end tag");

            // Collect what we've seen so far, since the "</text>" is not a markup span, it's a transition span
            Span prev = null;

            if (HaveContent)
            {
                prev = MarkupSpan.Create(Context);
                Context.ResetBuffers();
            }

            // Accept the "</text>"
            Context.Expect("<");
            Context.AcceptWhiteSpace(includeNewLines: true);
            Context.Expect("/text");

            bool complete = CurrentCharacter == '>';

            if (!complete)
            {
                OnError(tag.Start, RazorResources.ParseError_TextTagCannotContainAttributes);
            }
            else
            {
                Context.AcceptCurrent();
            }

            // Remove the tag
            UpdateTagStack(tags, tag, !inDocument);

            if (tags.Count == 0)
            {
                // That was the top-level tag, output the markup then a transition span for the </text>
                if (prev != null)
                {
                    Output(prev);
                }
                End(TransitionSpan.Create(Context, hidden: false, acceptedCharacters: complete ? AcceptedCharacters.None : AcceptedCharacters.Any));
            }
            else
            {
                // Wasn't top-level, so resume the original span
                Context.ResumeSpan(prev);
            }
        }
Exemplo n.º 3
0
        protected virtual void AcceptWhiteSpaceByLines()
        {
            Debug.Assert(InTemporaryBuffer);

            // Eat whitespace until a non-whitespace character,
            while (Char.IsWhiteSpace(CurrentCharacter))
            {
                Context.AcceptWhiteSpace(includeNewLines: false);

                // Stopped because of a newline, so accept the newline, then accept the temporary buffer and start it again
                if (Char.IsWhiteSpace(CurrentCharacter))
                {
                    Context.AcceptLine(includeNewLineSequence: true);
                    Context.AcceptTemporaryBuffer();
                    Context.StartTemporaryBuffer();
                }
            }
        }
Exemplo n.º 4
0
        protected bool TryParseComment(SpanFactory previousSpanFactory)
        {
            // Check for comment
            using (Context.StartTemporaryBuffer()) {
                Context.AcceptWhiteSpace(includeNewLines: true);
                if (Context.Peek(RazorParser.StartCommentSequence, caseSensitive: true))
                {
                    Context.AcceptTemporaryBuffer();
                    End(previousSpanFactory);
                }
                else
                {
                    return(false);
                }
            }

            ParseComment();
            return(true);
        }
Exemplo n.º 5
0
        /// <summary>
        /// Parses the modeltype statement.
        /// </summary>
        /// <param name="block">The code block.</param>
        public bool ParseModelTypeStatement(CodeBlockInfo block)
        {
            Contract.Requires(block != null);

            using (StartBlock(BlockType.Directive))
            {
                block.ResumeSpans(Context);

                SourceLocation location       = CurrentLocation;
                bool           readWhitespace = RequireSingleWhiteSpace();

                End(MetaCodeSpan.Create(Context, false, readWhitespace ? AcceptedCharacters.None : AcceptedCharacters.Any));

                if (_modelOrInheritsStatementFound)
                {
                    OnError(location, "The modeltype or inherits keywords can only appear once.");
                }

                _modelOrInheritsStatementFound = true;

                // Accept Whitespace up to the new line or non-whitespace character
                Context.AcceptWhiteSpace(false);

                string typeName = null;
                if (ParserHelpers.IsIdentifierStart(CurrentCharacter))
                {
                    using (Context.StartTemporaryBuffer())
                    {
                        Context.AcceptUntil(ParserHelpers.IsNewLine);
                        typeName = Context.ContentBuffer.ToString();
                        Context.AcceptTemporaryBuffer();
                    }
                    Context.AcceptNewLine();
                }
                else
                {
                    OnError(location, "Expected model identifier.");
                }
                End(new ModelSpan(Context, typeName));
            }
            return(false);
        }
Exemplo n.º 6
0
        private bool ParseModelStatement(CodeBlockInfo block)
        {
            using (StartBlock(BlockType.Directive)) {
                block.ResumeSpans(Context);

                SourceLocation endModelLocation = CurrentLocation;
                bool           readWhitespace   = RequireSingleWhiteSpace();

                End(MetaCodeSpan.Create(Context, hidden: false, acceptedCharacters: readWhitespace ? AcceptedCharacters.None : AcceptedCharacters.Any));

                if (_modelStatementFound)
                {
                    OnError(endModelLocation, String.Format(CultureInfo.CurrentCulture, MvcResources.MvcRazorCodeParser_OnlyOneModelStatementIsAllowed, ModelTypeKeyword));
                }

                _modelStatementFound = true;

                // Accept Whitespace up to the new line or non-whitespace character
                Context.AcceptWhiteSpace(includeNewLines: false);

                string typeName = null;
                if (ParserHelpers.IsIdentifierStart(CurrentCharacter))
                {
                    using (Context.StartTemporaryBuffer()) {
                        Context.AcceptUntil(c => ParserHelpers.IsNewLine(c));
                        typeName = Context.ContentBuffer.ToString();
                        Context.AcceptTemporaryBuffer();
                    }
                    Context.AcceptNewLine();
                }
                else
                {
                    OnError(endModelLocation, String.Format(CultureInfo.CurrentCulture, MvcResources.MvcRazorCodeParser_ModelKeywordMustBeFollowedByTypeName, ModelTypeKeyword));
                }
                CheckForInheritsAndModelStatements();
                End(new ModelSpan(Context, typeName));
            }
            return(false);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Parses the model statement.
        /// </summary>
        /// <param name="block">The code block.</param>
        private bool ParseModelStatement(CodeBlockInfo block)
        {
            var location = CurrentLocation;

            bool readWhiteSpace = RequireSingleWhiteSpace();

            End(MetaCodeSpan.Create(Context, false, readWhiteSpace ? AcceptedCharacters.None : AcceptedCharacters.Any));

            if (_modelOrInheritsStatementFound)
            {
                OnError(location, "The model or inherits keywords can only appear once.");
            }

            _modelOrInheritsStatementFound = true;

            Context.AcceptWhiteSpace(false);

            string typeName = null;

            if (ParserHelpers.IsIdentifierStart(CurrentCharacter))
            {
                using (Context.StartTemporaryBuffer())
                {
                    Context.AcceptUntil(ParserHelpers.IsNewLine);
                    typeName = Context.ContentBuffer.ToString();
                    Context.AcceptTemporaryBuffer();
                }
                Context.AcceptNewLine();
            }
            else
            {
                OnError(location, "Expected model identifier.");
            }
            End(new ModelSpan(Context, typeName));
            return(false);
        }
        private bool ParseResourceStatement(CodeBlockInfo block)
        {
            End(MetaCodeSpan.Create);

            SourceLocation endModelLocation = CurrentLocation;

            if (_modelStatementFound)
            {
                OnError(endModelLocation, String.Format(CultureInfo.CurrentCulture, "Only one '{0}' statement is allowed in a file.", ResourceKeyword));
            }

            _modelStatementFound = true;

            // Accept Whitespace up to the new line or non-whitespace character
            Context.AcceptWhiteSpace(false);

            string typeName = null;

            if (ParserHelpers.IsIdentifierStart(CurrentCharacter))
            {
                using (Context.StartTemporaryBuffer())
                {
                    // Accept a dotted-identifier, but allow <>
                    AcceptTypeName();
                    typeName = Context.ContentBuffer.ToString();
                    Context.AcceptTemporaryBuffer();
                }
            }
            else
            {
                OnError(endModelLocation, String.Format(CultureInfo.CurrentCulture, "The '{0}' keyword must be followed by a type name on the same line.", ResourceKeyword));
            }
            CheckForInheritsAndResourceStatements();
            End(ResourceSpan.Create(Context, typeName));
            return(false);
        }
Exemplo n.º 9
0
        private bool ParseStartPsuedoTag(Stack <TagInfo> tags, TagInfo tag)
        {
            Debug.Assert(!tag.IsEndTag, "ParseStartPsuedoTag requires a start tag");

            // Output what we've seen so far, since the "<text>" is not a markup token, it's a transition token
            if (HaveContent)
            {
                End(MarkupSpan.Create);
            }

            // Accept the "<text>"
            Context.Expect("<");
            Context.AcceptWhiteSpace(includeNewLines: true);
            Context.Expect("text");

            bool isValid = false;

            using (Context.StartTemporaryBuffer()) {
                Context.AcceptWhiteSpace(includeNewLines: true);
                if (CurrentCharacter == '/' || CurrentCharacter == '>')
                {
                    isValid = true;
                    Context.AcceptTemporaryBuffer();
                }
            }

            bool transitionComplete = false;
            bool isEmpty            = false;

            if (!isValid)
            {
                OnError(tag.Start, RazorResources.ParseError_TextTagCannotContainAttributes);
            }
            else
            {
                isEmpty = CurrentCharacter == '/';
                Context.AcceptCurrent();
                if (isEmpty)
                {
                    if (CurrentCharacter != '>')
                    {
                        OnError(CurrentLocation, RazorResources.ParseError_SlashInEmptyTagMustBeFollowedByCloseAngle);
                    }
                    else
                    {
                        transitionComplete = true;
                        Context.AcceptCurrent();
                    }
                }
                else
                {
                    transitionComplete = true;
                }
            }

            // Output the transition
            End(TransitionSpan.Create(Context, hidden: false, acceptedCharacters: transitionComplete ? AcceptedCharacters.None : AcceptedCharacters.Any));

            // Push it on to the stack and continue
            if (!isEmpty)
            {
                tags.Push(tag);
            }
            return(isEmpty);
        }
Exemplo n.º 10
0
        private bool ParseTagBlock(bool inDocument)
        {
            // For tracking end tags
            Stack <TagInfo> tags = new Stack <TagInfo>();
            bool            startedByPseudoTag = false;

            bool?canGrow = null;

            do
            {
                // Append until the next tag, processing code as we find it
                AppendUntilAndParseCode(c => c == '<');

                // Read the tag name in lookahead since we might not actually want to accept it
                TagInfo tag = ParseStartOfTag();

                // Special case for "<text>" tag as the first tag we've seen
                if (IsPsuedoTagValidHere(inDocument, tags, startedByPseudoTag, tag))
                {
                    if (!tag.IsEndTag)
                    {
                        startedByPseudoTag = true; // Set a flag to indicate that a </text> is a valid end tag
                        if (ParseStartPsuedoTag(tags, tag))
                        {
                            // Can't just do "canGrow = !ParseStartPsuedoTag(...)" because we can't canGrow to
                            // stay null if we get false from ParseStartPsuedoTag
                            canGrow = false;
                        }
                    }
                    else
                    {
                        ParseEndPsuedoTag(tags, tag, inDocument);
                        canGrow = false;
                    }
                }
                // It wasn't a "<text>" OR it was but it was within a block, so we don't do anything special
                else
                {
                    // We're at the '<'
                    Context.AcceptCurrent(); // "<"

                    if (tag.IsEndTag)
                    {
                        Context.AcceptCurrent();               // "/"
                    }
                    Context.AcceptCharacters(tag.Name.Length); // tag name

                    // Invalid tag name? Not a real tag
                    if (!String.IsNullOrEmpty(tag.Name))
                    {
                        // What kind of tag is it
                        bool?unterminated = null;
                        switch (tag.Name[0])
                        {
                        case '!':
                            unterminated = ParseBangTag(tag.Name);
                            break;

                        case '?':
                            unterminated = ParseProcessingInstruction();
                            break;

                        default:
                            if (tag.IsEndTag)
                            {
                                unterminated = ParseEndTag(tags, tag, inDocument);
                            }
                            else
                            {
                                unterminated = ParseStartTag(tags, tag);
                            }
                            break;
                        }
                        if (tags.Count == 0 && unterminated != null)
                        {
                            canGrow = unterminated.Value;
                        }
                    }
                    else
                    {
                        canGrow = true;
                        if (tags.Count == 0)
                        {
                            OnError(CurrentLocation, RazorResources.ParseError_OuterTagMissingName);
                        }
                    }
                }
            } while (!EndOfFile && tags.Count > 0);

            if (canGrow == null)
            {
                canGrow = tags.Count > 0;
            }

            if (tags.Count > 0)
            {
                // Ended because of EOF, not matching close tag.  Throw error for last tag
                while (tags.Count > 1)
                {
                    tags.Pop();
                }
                TagInfo tag = tags.Pop();
                OnError(tag.Start, RazorResources.ParseError_MissingEndTag, tag.Name);
            }

            // Add the remaining whitespace (up to and including the next newline) to the token if run-time mode
            if (!DesignTimeMode)
            {
                // Dev10 Bug 884969 - Emit space between markup and code
                Context.AcceptWhiteSpace(includeNewLines: false);
                if (Char.IsWhiteSpace(CurrentCharacter))
                {
                    Context.AcceptLine(includeNewLineSequence: true);
                }
            }
            else if (canGrow.Value)
            {
                Context.AcceptWhiteSpace(includeNewLines: false);
                if (ParserHelpers.IsNewLine(CurrentCharacter))
                {
                    Context.AcceptNewLine();
                }
            }

            return(canGrow.Value);
        }
Exemplo n.º 11
0
        public override void ParseBlock()
        {
            if (Context == null)
            {
                throw new InvalidOperationException(RazorResources.Parser_Context_Not_Set);
            }

            using (StartBlock(BlockType.Markup)) {
                // An HTML block starts with a start tag and ends with the matching end tag. For example, each of the following lines are HTML blocks:
                //  <li>foo</li>
                //  <li>foo<b>bar</b></li>
                //  <text>This block uses the <pre>&lt;text&gt;</pre> pseudo-tag which is not emitted, but is used for balancing tags</text>
                // Or, if it starts with a ":", then it is part of the "@:" single-line markup construct.  The ":" is discarded and the rest of the line is markup
                // For example, each of the following lines are HTML blocks:
                //  :this is all markup, except for the initial ':'
                //  :you <b>can</b> put tags in here <em>and</em> they don't have to be <img src="foo.jpg"> balanced!

                SpanFactory spanFactory = MarkupSpan.Create;

                Context.AcceptWhiteSpace(includeNewLines: true);
                if (CurrentCharacter == RazorParser.TransitionCharacter)
                {
                    if (HaveContent)
                    {
                        End(MarkupSpan.Create);
                    }
                    Context.AcceptCurrent();
                    Debug.Assert(HaveContent);
                    End(TransitionSpan.Create(Context, hidden: false, acceptedCharacters: AcceptedCharacters.None));
                    if (CurrentCharacter == RazorParser.TransitionCharacter)
                    {
                        // Second "@" is for VB
                        // TODO: Refactor!!!
                        Context.AcceptCurrent();
                        End(MetaCodeSpan.Create);
                    }
                }

                bool complete = false;
                if (CurrentCharacter == ':')
                {
                    // Parse a single line of markup
                    spanFactory = SingleLineMarkupSpan.Create;
                    Context.WhiteSpaceIsImportantToAncestorBlock = true;
                    complete = !ParseSingleLineBlock();
                    Context.WhiteSpaceIsImportantToAncestorBlock = false;
                }
                else if (CurrentCharacter == '<')
                {
                    complete = !ParseTagBlock(false);
                }
                else
                {
                    // First non-whitespace character in a block must be a start tag or ':'
                    OnError(CurrentLocation, RazorResources.ParseError_MarkupBlock_Must_Start_With_Tag);
                    return;
                }

                // Output anything that's left over
                // If we have content ==> Output
                // If the previous span can't grow ==> Output UNLESS we're "complete"
                if ((!complete && !Context.PreviousSpanCanGrow) || HaveContent)
                {
                    Span span = spanFactory(Context);
                    span.AcceptedCharacters = complete ? AcceptedCharacters.None : AcceptedCharacters.Any;
                    End(span);
                }
            }
        }
Exemplo n.º 12
0
        protected virtual bool BalanceBrackets(bool allowTransition, SpanFactory spanFactory, bool appendOuter, char?bracket, bool useTemporaryBuffer)
        {
            spanFactory = spanFactory ?? CodeSpan.Create;

            if (useTemporaryBuffer)
            {
                Context.StartTemporaryBuffer();
            }

            int  nesting           = 0; // Nesting level
            bool callerReadBracket = true;

            if (bracket == null)
            {
                callerReadBracket = false;
                bracket           = CurrentCharacter;
            }
            else
            {
                // The caller already read the bracket, so start at nesting level 1, and also, don't append the outer bracket
                nesting = 1;
            }
            char terminator = _bracketPairs[bracket.Value];

            do
            {
                // Gather whitespace
                Context.StartTemporaryBuffer();
                AcceptWhiteSpaceByLines();

                if (CurrentCharacter == RazorParser.TransitionCharacter)
                {
                    if (Context.Peek(RazorParser.StartCommentSequence, caseSensitive: true))
                    {
                        Context.AcceptTemporaryBuffer();
                        if (useTemporaryBuffer)
                        {
                            Context.AcceptTemporaryBuffer();
                        }
                        End(spanFactory);
                        ParseComment();
                        if (useTemporaryBuffer)
                        {
                            Context.StartTemporaryBuffer();
                        }
                    }
                    else if (allowTransition)
                    {
                        Context.RejectTemporaryBuffer();
                        if (!HandleTransition(spanFactory))
                        {
                            Context.AcceptWhiteSpace(includeNewLines: true);
                            if (!TryAcceptStringOrComment())
                            {
                                Context.AssertCurrent(RazorParser.TransitionCharacter);
                                Context.AcceptCurrent();
                            }
                        }
                        else if (useTemporaryBuffer)
                        {
                            // Start a new outer temporary buffer
                            Context.StartTemporaryBuffer();
                        }
                    }
                    else
                    {
                        Context.AcceptTemporaryBuffer();
                        if (!TryAcceptStringOrComment())
                        {
                            Context.AcceptCurrent();
                        }
                    }
                }
                else
                {
                    Context.AcceptTemporaryBuffer();
                }

                AcceptUntilUnquoted(c => Char.IsWhiteSpace(c) || c == bracket || c == terminator || c == RazorParser.TransitionCharacter);
                if (CurrentCharacter == terminator)
                {
                    // If the nesting level is 1 and no bracket was specified, don't read the terminator, but we are done
                    if (nesting == 1 && callerReadBracket)
                    {
                        nesting--;
                    }
                    else
                    {
                        AcceptOrSkipCurrent(appendOuter, --nesting);
                    }
                }
                else if (CurrentCharacter == bracket)
                {
                    AcceptOrSkipCurrent(appendOuter, nesting++);
                }
            } while (!EndOfFile && nesting > 0);

            if (useTemporaryBuffer)
            {
                if (nesting > 0)
                {
                    Context.RejectTemporaryBuffer();
                }
                else
                {
                    Context.AcceptTemporaryBuffer();
                }
            }

            Debug.Assert(!InTemporaryBuffer);
            return(nesting == 0); // Return a boolean indicating if we exited because of EOF or because of the end bracket
        }