예제 #1
0
        /// <summary>
        /// Register a Lava Tag with the Fluid Parser.
        /// </summary>
        /// <param name="tagName"></param>
        public void RegisterLavaTag(string tagName, LavaTagFormatSpecifier format = LavaTagFormatSpecifier.LiquidTag)
        {
            // Create a parser for the Lava tag that does the following:
            // 1. Processes all of the content from the end of the open tag until the end of the closing tag.
            // 2. Captures any optional attributes that are contained in the open tag.
            // 3. Create a new Statement that will execute when the tag is rendered.
            // 4. Throw an exception if the Tag is malformed.
            Parser <LavaTagResult> tokenEndParser;

            var    registerTagName = tagName;
            string errorMessage;

            if (format == LavaTagFormatSpecifier.LavaShortcode)
            {
                tokenEndParser = LavaTagParsers.LavaShortcodeEnd();
                tagName        = tagName.Substring(0, tagName.Length - "_".Length);
                errorMessage   = $"Invalid '{{[ {tagName} ]}}' shortcode tag";
            }
            else
            {
                tokenEndParser = LavaTokenEnd;
                errorMessage   = $"Invalid '{{% {tagName} %}}' tag";
            }

            var lavaTag = AnyCharBefore(tokenEndParser, canBeEmpty: true)
                          .AndSkip(tokenEndParser)
                          .Then <Statement>(x => new FluidLavaTagStatement(tagName, format, x))
                          .ElseError(errorMessage);

            this.RegisteredTags[registerTagName] = lavaTag;
        }
예제 #2
0
        /// <summary>
        /// Register a Lava Block with the Fluid Parser.
        /// </summary>
        /// <param name="tagName"></param>
        public void RegisterLavaBlock(string tagName, LavaTagFormatSpecifier format = LavaTagFormatSpecifier.LiquidTag)
        {
            // Create a parser for the Lava block that does the following:
            // 1. Captures any optional attributes that are contained in the open tag.
            // 2. Creates a new Statement that will execute when the block is rendered. The Statement captures the block content
            //    as literal text, so that it can be scanned and tokenized by the Lava library before being passed back to Fluid
            //    for final rendering.
            // 3. Throw an exception if the Block is malformed.
            Parser <LavaTagResult> tokenEndParser;

            var    registerTagName = tagName;
            string errorMessage;

            if (format == LavaTagFormatSpecifier.LavaShortcode)
            {
                tokenEndParser = LavaTagParsers.LavaShortcodeEnd();
                tagName        = tagName.Substring(0, tagName.Length - "_".Length);
                errorMessage   = $"Invalid '{{[ {tagName} ]}}' shortcode block";
            }
            else
            {
                tokenEndParser = LavaTokenEnd;
                errorMessage   = $"Invalid '{{% {tagName} %}}' block";
            }

            var lavaBlock = AnyCharBefore(tokenEndParser, canBeEmpty: true)
                            .AndSkip(tokenEndParser)
                            .And(new LavaTagParsers.LavaBlockContentParser(tagName, format))
                            .Then <Statement>(x => new FluidLavaBlockStatement(this, tagName, format, x.Item1, x.Item2))
                            .ElseError(errorMessage);

            RegisteredTags[registerTagName] = lavaBlock;
        }
예제 #3
0
        /// <summary>
        /// Replace the default Fluid comment block to allow empty content.
        /// </summary>
        private void RegisterLavaCommentTag()
        {
            var commentTag = LavaTagParsers.LavaTagEnd()
                             .SkipAnd(AnyCharBefore(CreateTag("endcomment"), canBeEmpty: true))
                             .AndSkip(CreateTag("endcomment").ElseError($"'{{% endcomment %}}' was expected"))
                             .Then <Statement>(x => new CommentStatement(x))
                             .ElseError("Invalid 'comment' tag");

            RegisteredTags["comment"] = commentTag;
        }
예제 #4
0
        private void CreateLavaDocumentParsers()
        {
            // Define the top-level parsers.
            var anyTags   = CreateKnownTagsParser(throwOnUnknownTag: false);
            var knownTags = CreateKnownTagsParser(throwOnUnknownTag: true);

            var outputElement = OutputStart.SkipAnd(FilterExpression.And(OutputEnd.ElseError(ErrorMessages.ExpectedOutputEnd))
                                                    .Then <Statement>(x => new OutputStatement(x.Item1)));

            var textElement = AnyCharBefore(OutputStart.Or(LavaTagParsers.LavaTagStart().AsFluidTagResultParser()).Or(LavaTagParsers.LavaShortcodeStart().AsFluidTagResultParser()))
                              .Then <Statement>((ctx, x) =>
            {
                // Keep track of each text span such that whitespace trimming can be applied
                var p = ( FluidParseContext )ctx;

                var result = new TextSpanStatement(x);

                p.PreviousTextSpanStatement = result;

                if (p.StripNextTextSpanStatement)
                {
                    result.StripLeft             = true;
                    p.StripNextTextSpanStatement = false;
                }

                result.PreviousIsTag    = p.PreviousIsTag;
                result.PreviousIsOutput = p.PreviousIsOutput;

                return(result);
            });

            // Define parsers for Lava block/inline comments.
            var blockCommentElement = Terms.Text("/-").SkipAnd(AnyCharBefore(Terms.Text("-/")));
            var lineCommentElement  = Terms.Text("/-").SkipAnd(AnyCharBefore(Terms.Char('\r').SkipAnd(Terms.Char('\n'))));

            var commentElement = blockCommentElement.Or(lineCommentElement);

            // Set the parser to be used for a block element.
            // This parser returns an empty result when an unknown tag is found.
            _anyTagsListParser = ZeroOrMany(outputElement.Or(anyTags).Or(textElement));

            // Set the parser to be used for the entire template.
            // This parser raises an exception when an unknown tag is found.
            _knownTagsListParser = ZeroOrMany(outputElement.Or(knownTags).Or(textElement));
        }
예제 #5
0
            public override bool Parse(ParseContext context, ref ParseResult <LavaTagResult> result)
            {
                var start = context.Scanner.Cursor.Position;

                if (_skipWhiteSpace)
                {
                    context.SkipWhiteSpace();
                }

                Parser <(LavaTagResult, string, TextSpan, LavaTagResult)> tagParser;

                if (_tagFormat == LavaTagFormatSpecifier.LavaShortcode)
                {
                    tagParser = LavaTagParsers.LavaShortcodeStart()
                                .And(Terms.Text(_tagName))
                                .And(AnyCharBefore(LavaTagParsers.LavaShortcodeEnd(), canBeEmpty: true))
                                .And(LavaTagParsers.LavaShortcodeEnd());
                }
                else
                {
                    tagParser = LavaTagParsers.LavaTagStart()
                                .And(Terms.Text(_tagName)).
                                And(AnyCharBefore(LavaFluidParser.LavaTokenEnd, canBeEmpty: true))
                                .And(LavaFluidParser.LavaTokenEnd);
                }

                var tagResult = new ParseResult <(LavaTagResult, string, TextSpan, LavaTagResult)>();

                var parseSucceeded = tagParser.Parse(context, ref tagResult);

                if (!parseSucceeded)
                {
                    context.Scanner.Cursor.ResetPosition(start);
                    return(false);
                }

                var lavaResult = new LavaTagResult(tagResult.Value.Item1.TagResult,
                                                   new TextSpan(context.Scanner.Buffer, start.Offset, context.Scanner.Cursor.Position.Offset - start.Offset),
                                                   _tagFormat);

                result.Set(start.Offset, context.Scanner.Cursor.Offset, lavaResult);

                return(true);
            }
예제 #6
0
        /// <summary>
        /// Create a parser for the set of tags and shortcodes that have been defined.
        /// </summary>
        /// <param name="throwOnUnknownTag">If true, undefined tags return null rather than throwing an exception.</param>
        /// <returns></returns>
        /// <remarks>The option to ignore unknown tags can be used to ignore output tags that are not defined until the render process.</remarks>
        private Parser <Statement> CreateKnownTagsParser(bool throwOnUnknownTag)
        {
            var parser = OneOf(
                LavaTagParsers.LavaTagStart()
                .SkipAnd(Identifier.ElseError(ErrorMessages.IdentifierAfterTagStart)
                         .Switch((context, tagName) =>
            {
                if (RegisteredTags.TryGetValue(tagName, out var tag))
                {
                    return(tag);
                }
                else if (throwOnUnknownTag)
                {
                    throw new global::Fluid.ParseException($"Unknown tag '{tagName}' at {context.Scanner.Cursor.Position}");
                }

                return(null);
            })
                         ),
                LavaTagParsers.LavaShortcodeStart()
                .SkipAnd(Identifier.ElseError(ErrorMessages.IdentifierAfterTagStart)
                         .Switch((context, tagName) =>
            {
                var shortcodeTagName = tagName + "_";

                if (RegisteredTags.TryGetValue(shortcodeTagName, out var shortcode))
                {
                    return(shortcode);
                }

                throw new global::Fluid.ParseException($"Unknown shortcode '{tagName}' at {context.Scanner.Cursor.Position}");
            })
                         )
                );

            return(parser);
        }