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); }
/// <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); }
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)); }