Beispiel #1
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            if (!slice.Match(m_StartTag))
            {
                return(false);
            }

            var rawContent = new StringBuilder();

            var current = slice.CurrentChar;

            while (!rawContent.ToString().EndsWith(m_EndTag))
            {
                if (slice.IsEmpty)
                {
                    return(false);
                }

                rawContent.Append(current);
                current = slice.NextChar();
            }

            processor.Inline = new ProtectedTagsData(rawContent.ToString());

            return(true);
        }
Beispiel #2
0
        /// <summary>
        /// Tries to match partial tag from the current slice
        /// </summary>
        /// <param name="processor">The processor</param>
        /// <param name="slice">The slice</param>
        /// <returns>If a match was found</returns>
        public override bool Match(Processor processor, ref StringSlice slice)
        {
            var tagStart = slice.Start - processor.CurrentTags.StartTag.Length;
            var index    = slice.Start;

            while (slice[index].IsWhitespace())
            {
                index++;
            }

            var match = slice[index];

            if (match == TagId)
            {
                index++;
                while (slice[index].IsWhitespace())
                {
                    index++;
                }

                slice.Start = index;
                var startIndex = slice.Start;
                var partialTag = new PartialToken
                {
                    LineIndent           = processor.HasSeenNonSpaceOnLine ? 0 : processor.LineIndent,
                    TagStartPosition     = tagStart,
                    ContentStartPosition = startIndex,
                    IsClosed             = false
                };
                processor.CurrentToken = partialTag;

                while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag))
                {
                    slice.NextChar();
                }

                if (slice.IsEmpty)
                {
                    return(false);
                }

                var content = new StringSlice(slice.Text, startIndex, slice.Start - 1);
                content.TrimEnd();
                var contentEnd = content.End + 1;

                partialTag.ContentEndPosition = contentEnd;
                partialTag.TagEndPosition     = slice.Start + processor.CurrentTags.EndTag.Length;
                partialTag.IsClosed           = true;
                slice.Start += processor.CurrentTags.EndTag.Length;
                return(true);
            }

            return(false);
        }
Beispiel #3
0
        /// <summary>
        /// Tries to open a section tag using the slice
        /// </summary>
        /// <param name="processor">The processor</param>
        /// <param name="slice">The slice</param>
        /// <returns>The result of the match</returns>
        public override ParserState TryOpenBlock(Processor processor, ref StringSlice slice)
        {
            var tagStart = slice.Start - processor.CurrentTags.StartTag.Length;
            var index    = slice.Start;

            while (slice[index].IsWhitespace())
            {
                index++;
            }

            var match = slice[index];

            if (match == OpeningTagDelimiter)
            {
                slice.Start = index + 1; // Skip delimiter

                while (slice.CurrentChar.IsWhitespace())
                {
                    slice.NextChar();
                }

                var startIndex = slice.Start;

                // Take characters until closing tag
                while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag))
                {
                    slice.NextChar();
                }

                var sectionName          = slice.ToString(startIndex, slice.Start).TrimEnd();
                var contentStartPosition = slice.Start + processor.CurrentTags.EndTag.Length;

                var sectionTag = new SectionToken
                {
                    SectionName          = sectionName,
                    StartPosition        = tagStart,
                    ContentStartPosition = contentStartPosition,
                    Parser   = this,
                    IsClosed = false
                };

                processor.CurrentToken = sectionTag;

                slice.Start = contentStartPosition;

                return(ParserState.Break);
            }

            return(ParserState.Continue);
        }
Beispiel #4
0
        /// <summary>
        /// Try to create a block close tag
        /// </summary>
        /// <param name="processor">The processor</param>
        /// <param name="slice">the slice</param>
        /// <param name="token">the current block tag</param>
        /// <returns>If the close was successful</returns>
        public override bool TryClose(Processor processor, ref StringSlice slice, BlockToken token)
        {
            var sectionTag = (SectionToken)token;

            while (slice.CurrentChar.IsWhitespace())
            {
                slice.NextChar();
            }

            var blockStart = slice.Start - processor.CurrentTags.StartTag.Length;

            slice.Start = slice.Start + 1; // Skip the slash

            while (slice.CurrentChar.IsWhitespace())
            {
                slice.NextChar();
            }

            var startIndex = slice.Start;

            // Take characters until closing tag
            while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag))
            {
                slice.NextChar();
            }

            var sectionName = slice.ToString(startIndex, slice.Start).TrimEnd();

            if (sectionTag.SectionName == sectionName)
            {
                var tagEnd = slice.Start + processor.CurrentTags.EndTag.Length;

                var endTag = new SectionEndToken
                {
                    SectionName        = sectionName,
                    EndPosition        = tagEnd,
                    ContentEndPosition = blockStart,
                    IsClosed           = true
                };

                processor.CurrentToken = endTag;

                slice.Start += processor.CurrentTags.EndTag.Length;
                return(true);
            }

            throw new StubbleException($"Cannot close Block '{sectionName}' at {blockStart}. There is already an unclosed Block '{sectionTag.SectionName}'");
        }
Beispiel #5
0
        /// <summary>
        /// Tries to match a comment tag from the provided slice
        /// </summary>
        /// <param name="processor">The processor</param>
        /// <param name="slice">The slice</param>
        /// <returns>If a comment tag was matched</returns>
        public override bool Match(Processor processor, ref StringSlice slice)
        {
            var tagStart = slice.Start - processor.CurrentTags.StartTag.Length;
            var index    = slice.Start;

            while (slice[index].IsWhitespace())
            {
                index++;
            }

            var match = slice[index];

            if (match == TagId)
            {
                slice.Start = index;
                var startIndex = index + 1;

                var commentTag = new CommentToken
                {
                    TagStartPosition     = tagStart,
                    ContentStartPosition = startIndex,
                    IsClosed             = false
                };
                processor.CurrentToken = commentTag;

                while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag))
                {
                    slice.NextChar();
                }

                if (slice.IsEmpty)
                {
                    return(false);
                }

                commentTag.TagEndPosition     = slice.Start + processor.CurrentTags.EndTag.Length;
                commentTag.ContentEndPosition = slice.Start;
                commentTag.IsClosed           = true;
                slice.Start += processor.CurrentTags.EndTag.Length;
                return(true);
            }

            return(false);
        }
Beispiel #6
0
        private BlockState TryParseTagType16(BlockProcessor state, StringSlice line, int startColumn, int startPosition)
        {
            char c;

            c = line.CurrentChar;
            if (c == '!')
            {
                c = line.NextChar();
                if (c == '-' && line.PeekChar() == '-')
                {
                    return(CreateHtmlBlock(state, HtmlBlockType.Comment, startColumn, startPosition)); // group 2
                }
                if (c.IsAlphaUpper())
                {
                    return(CreateHtmlBlock(state, HtmlBlockType.DocumentType, startColumn, startPosition)); // group 4
                }
                if (c == '[' && line.Match("CDATA[", 1))
                {
                    return(CreateHtmlBlock(state, HtmlBlockType.CData, startColumn, startPosition)); // group 5
                }

                return(BlockState.None);
            }

            if (c == '?')
            {
                return(CreateHtmlBlock(state, HtmlBlockType.ProcessingInstruction, startColumn, startPosition)); // group 3
            }

            var hasLeadingClose = c == '/';

            if (hasLeadingClose)
            {
                c = line.NextChar();
            }

            var tag   = new char[10];
            var count = 0;

            for (; count < tag.Length; count++)
            {
                if (!c.IsAlphaNumeric())
                {
                    break;
                }
                tag[count] = char.ToLowerInvariant(c);
                c          = line.NextChar();
            }

            if (
                !(c == '>' || (!hasLeadingClose && c == '/' && line.PeekChar() == '>') || c.IsWhitespace() ||
                  c == '\0'))
            {
                return(BlockState.None);
            }

            if (count == 0)
            {
                return(BlockState.None);
            }

            var tagName  = new string(tag, 0, count);
            var tagIndex = Array.BinarySearch(HtmlTags, tagName, StringComparer.Ordinal);

            if (tagIndex < 0)
            {
                return(BlockState.None);
            }

            // Cannot start with </script </pre or </style
            if ((tagIndex == 49 || tagIndex == 50 || tagIndex == 53))
            {
                if (c == '/' || hasLeadingClose)
                {
                    return(BlockState.None);
                }
                return(CreateHtmlBlock(state, HtmlBlockType.ScriptPreOrStyle, startColumn, startPosition));
            }

            return(CreateHtmlBlock(state, HtmlBlockType.InterruptingBlock, startColumn, startPosition));
        }
        public override bool Match(Processor processor, ref StringSlice slice)
        {
            if (processor is null)
            {
                throw new System.ArgumentNullException(nameof(processor));
            }

            var tagStart = slice.Start - processor.CurrentTags.StartTag.Length;
            var index    = slice.Start;

            while (slice[index].IsWhitespace())
            {
                index++;
            }

            var nameStart = index;

            // Skip whitespace or until end tag
            while (!slice[index].IsWhitespace() && !slice.Match(processor.CurrentTags.EndTag, index - slice.Start))
            {
                index++;
            }

            var name = slice.ToString(nameStart, index);

            // Skip whitespace or until end tag
            while (slice[index].IsWhitespace() && !slice.Match(processor.CurrentTags.EndTag, index - slice.Start))
            {
                index++;
            }

            if (!_helperMap.TryGetValue(name, out var helperRef))
            {
                return(false);
            }

            int contentEnd;
            var argsList = ImmutableArray <HelperArgument> .Empty;

            if (helperRef.ArgumentTypes.Length > 1)
            {
                var argsStart = index;
                slice.Start = index;

                while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag))
                {
                    slice.NextChar();
                }

                var args = new StringSlice(slice.Text, argsStart, slice.Start - 1);
                args.TrimEnd();
                contentEnd = args.End + 1;

                argsList = ParseArguments(new StringSlice(args.Text, args.Start, args.End));
            }
            else
            {
                while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag))
                {
                    slice.NextChar();
                }

                contentEnd = slice.Start;
            }

            if (!slice.Match(processor.CurrentTags.EndTag))
            {
                throw new StubbleException($"Unclosed Tag at {slice.Start.ToString(CultureInfo.InvariantCulture)}");
            }

            var tag = new HelperToken
            {
                TagStartPosition     = tagStart,
                ContentStartPosition = nameStart,
                Name = name,
                Args = argsList,
                ContentEndPosition = contentEnd,
                TagEndPosition     = slice.Start + processor.CurrentTags.EndTag.Length,
                IsClosed           = true
            };

            slice.Start += processor.CurrentTags.EndTag.Length;

            processor.CurrentToken          = tag;
            processor.HasSeenNonSpaceOnLine = true;

            return(true);
        }
Beispiel #8
0
        private BlockState TryParseTagType16(BlockProcessor state, StringSlice line, int startColumn, int startPosition)
        {
            char c;

            c = line.CurrentChar;
            if (c == '!')
            {
                c = line.NextChar();
                if (c == '-' && line.PeekChar() == '-')
                {
                    return(CreateHtmlBlock(state, HtmlBlockType.Comment, startColumn, startPosition)); // group 2
                }
                if (c.IsAlphaUpper())
                {
                    return(CreateHtmlBlock(state, HtmlBlockType.DocumentType, startColumn, startPosition)); // group 4
                }
                if (c == '[' && line.Match("CDATA[", 1))
                {
                    return(CreateHtmlBlock(state, HtmlBlockType.CData, startColumn, startPosition)); // group 5
                }

                return(BlockState.None);
            }

            if (c == '?')
            {
                return(CreateHtmlBlock(state, HtmlBlockType.ProcessingInstruction, startColumn, startPosition)); // group 3
            }

            var hasLeadingClose = c == '/';

            if (hasLeadingClose)
            {
                c = line.NextChar();
            }

            Span <char> tag   = stackalloc char[10];
            var         count = 0;

            for (; count < tag.Length; count++)
            {
                if (!c.IsAlphaNumeric())
                {
                    break;
                }
                tag[count] = char.ToLowerInvariant(c);
                c          = line.NextChar();
            }

            if (
                !(c == '>' || (!hasLeadingClose && c == '/' && line.PeekChar() == '>') || c.IsWhitespace() ||
                  c == '\0'))
            {
                return(BlockState.None);
            }

            if (count == 0)
            {
                return(BlockState.None);
            }

            if (!HtmlTags.TryMatchExact(tag.Slice(0, count), out var match))
            {
                return(BlockState.None);
            }

            int tagIndex = match.Value;

            // Cannot start with </script </pre or </style or </textArea
            if ((tagIndex == 49 || tagIndex == 50 || tagIndex == 53 || tagIndex == 56))
            {
                if (c == '/' || hasLeadingClose)
                {
                    return(BlockState.None);
                }
                return(CreateHtmlBlock(state, HtmlBlockType.ScriptPreOrStyle, startColumn, startPosition));
            }

            return(CreateHtmlBlock(state, HtmlBlockType.InterruptingBlock, startColumn, startPosition));
        }
Beispiel #9
0
        /// <summary>
        /// Parse a literal tag from the slice
        /// </summary>
        /// <param name="processor">The processor</param>
        /// <param name="slice">The slice</param>
        /// <returns>The result of the match</returns>
        public LiteralTagResult Match(Processor processor, ref StringSlice slice)
        {
            var c = slice.CurrentChar;

            var start = slice.Start;
            var index = slice.Start;

            var tag = new LiteralToken
            {
                ContentStartPosition = index
            };

            processor.CurrentToken = tag;

            while (c != '\0')
            {
                if (slice.Match(processor.CurrentTags.StartTag))
                {
                    if (tag.ContentStartPosition == slice.Start)
                    {
                        return(LiteralTagResult.NoContent);
                    }

                    tag.ContentEndPosition = slice.Start;
                    tag.IsWhitespace       = new StringSlice(slice.Text, tag.ContentStartPosition, tag.ContentEndPosition - 1).IsEmptyOrWhitespace();

                    tag.IsClosed = true;
                    return(LiteralTagResult.TagStart);
                }

                // If this is whitespace then increase the start pointer by one
                if (c.IsWhitespace())
                {
                    if (!processor.HasSeenNonSpaceOnLine)
                    {
                        processor.LineIndent++;
                    }

                    start++;
                }
                else
                {
                    processor.HasSeenNonSpaceOnLine = true;
                }

                if (slice.IsNewLine())
                {
                    int endIndex;
                    if (c == '\n')
                    {
                        endIndex = slice.Start + 1;
                    }
                    else
                    {
                        endIndex = slice.Start + 2;
                    }

                    tag.ContentEndPosition = endIndex;
                    tag.IsWhitespace       = new StringSlice(slice.Text, tag.ContentStartPosition, tag.ContentEndPosition - 1).IsEmptyOrWhitespace();

                    tag.IsClosed = true;
                    return(LiteralTagResult.NewLine);
                }

                c = slice.NextChar();
            }

            if (tag.ContentStartPosition == slice.Start)
            {
                return(LiteralTagResult.NoContent);
            }

            tag.ContentEndPosition = slice.Start;
            tag.IsWhitespace       = new StringSlice(slice.Text, tag.ContentStartPosition, tag.ContentEndPosition - 1).IsEmptyOrWhitespace();

            tag.IsClosed = true;

            return(LiteralTagResult.EndOfFile);
        }
Beispiel #10
0
        /// <summary>
        /// Tries to match interpolation tags from the current slice
        /// </summary>
        /// <param name="processor">The processor</param>
        /// <param name="slice">The slice</param>
        /// <returns>If the match was successful</returns>
        public override bool Match(Processor processor, ref StringSlice slice)
        {
            var tagStart         = slice.Start - processor.CurrentTags.StartTag.Length;
            var index            = slice.Start;
            var escapeResult     = true;
            var isTripleMustache = false;

            while (slice[index].IsWhitespace())
            {
                index++;
            }

            var match = slice[index];

            if (match == '&')
            {
                escapeResult = false;
                index++;
            }
            else if (match == '{')
            {
                escapeResult     = false;
                isTripleMustache = true;
                index++;
            }

            while (slice[index].IsWhitespace())
            {
                index++;
            }

            slice.Start = index;
            var startIndex = index;

            var endTag = isTripleMustache ? '}' + processor.CurrentTags.EndTag : processor.CurrentTags.EndTag;

            while (!slice.IsEmpty && !slice.Match(endTag))
            {
                slice.NextChar();
            }

            var content = new StringSlice(slice.Text, startIndex, slice.Start - 1);

            content.TrimEnd();
            var contentEnd = content.End + 1;

            var tag = new InterpolationToken
            {
                EscapeResult         = escapeResult,
                TagStartPosition     = tagStart,
                ContentStartPosition = startIndex,
                IsClosed             = true
            };

            if (!slice.Match(endTag))
            {
                throw new StubbleException($"Unclosed Tag at {slice.Start.ToString()}");
            }

            tag.ContentEndPosition = contentEnd;
            tag.TagEndPosition     = slice.Start + endTag.Length;
            slice.Start           += endTag.Length;

            processor.CurrentToken          = tag;
            processor.HasSeenNonSpaceOnLine = true;

            return(true);
        }
Beispiel #11
0
        /// <summary>
        /// Tries to match delimiter tags from the current slice
        /// </summary>
        /// <param name="processor">The processor</param>
        /// <param name="slice">The slice</param>
        /// <returns>If the match was successful</returns>
        public override bool Match(Processor processor, ref StringSlice slice)
        {
            var tagStart = slice.Start - processor.CurrentTags.StartTag.Length;
            var index    = slice.Start;

            while (slice[index].IsWhitespace())
            {
                index++;
            }

            var match = slice[index];

            if (match == openingTagDelimiter[0])
            {
                index++;
                while (slice[index].IsWhitespace())
                {
                    index++;
                }

                slice.Start = index;
                var startIndex = slice.Start;

                // Take Characters that aren't whitespace
                while (!slice.CurrentChar.IsWhitespace())
                {
                    slice.NextChar();
                }

                var startTag = slice.ToString(startIndex, slice.Start);

                // Skip Whitespace any
                while (slice.CurrentChar.IsWhitespace())
                {
                    slice.NextChar();
                }

                var endTagStartIndex = slice.Start;

                // Take characters until end delimiter;
                var closingTag = closingTagDelimiter[0] + processor.CurrentTags.EndTag;
                while (!slice.IsEmpty && !slice.Match(closingTag))
                {
                    slice.NextChar();
                }

                var endTag = new StringSlice(slice.Text, endTagStartIndex, slice.Start - 1);
                endTag.TrimEnd();
                var contentEnd = endTag.End + 1;

                var tag = new DelimiterToken
                {
                    TagStartPosition     = tagStart,
                    ContentStartPosition = startIndex,
                    ContentEndPosition   = contentEnd,
                    TagEndPosition       = slice.Start + closingTag.Length,
                    StartTag             = startTag,
                    EndTag   = endTag.ToString(),
                    IsClosed = true
                };

                processor.CurrentToken = tag;

                processor.CurrentTags = new Classes.Tags(tag.StartTag, tag.EndTag);
                slice.Start          += closingTag.Length;

                return(true);
            }

            return(false);
        }