diff --git a/Directory.Packages.props b/Directory.Packages.props
index f93bac92..24d821e6 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -11,12 +11,12 @@
-
+
-
+
diff --git a/Fluid.Tests/ParserTests.cs b/Fluid.Tests/ParserTests.cs
index c1984de0..2ac1c835 100644
--- a/Fluid.Tests/ParserTests.cs
+++ b/Fluid.Tests/ParserTests.cs
@@ -987,7 +987,8 @@ public void ShouldParseLiquidTag()
| upcase
%}";
- Assert.True(_parser.TryParse(source, out var template, out var errors), errors);
+ var parser = new FluidParser(new FluidParserOptions { AllowLiquidTag = true });
+ Assert.True(parser.TryParse(source, out var template, out var errors), errors);
var rendered = template.Render();
Assert.Contains("WELCOME TO THE LIQUID TAG", rendered);
}
@@ -1003,7 +1004,8 @@ public void ShouldParseLiquidTagWithBlocks()
%}
";
- Assert.True(_parser.TryParse(source, out var template, out var errors), errors);
+ var parser = new FluidParser(new FluidParserOptions { AllowLiquidTag = true });
+ Assert.True(parser.TryParse(source, out var template, out var errors), errors);
var rendered = template.Render();
Assert.Contains("WELCOME TO THE LIQUID TAG", rendered);
}
@@ -1091,11 +1093,11 @@ public void ShouldParseLiquidTagWithDifferentSpaces(string spaces)
{% liquid
for c in (1..3)
echo c
- endforSPACE%}SPACE{{chars}}SPACE
- """.Replace("SPACE", spaces);
+ endfor[SPACE]%}[SPACE]{{chars}}[SPACE]
+ """.Replace("[SPACE]", spaces);
- var _parser = new FluidParser();
- Assert.True(_parser.TryParse(source, out var template, out var errors), errors);
+ var parser = new FluidParser(new FluidParserOptions { AllowLiquidTag = true });
+ Assert.True(parser.TryParse(source, out var template, out var errors), errors);
var rendered = template.Render();
Assert.Contains("123", rendered);
}
diff --git a/Fluid/FluidParser.cs b/Fluid/FluidParser.cs
index b4818c55..66c4d981 100644
--- a/Fluid/FluidParser.cs
+++ b/Fluid/FluidParser.cs
@@ -56,11 +56,25 @@ public class FluidParser
protected readonly Deferred> KnownTagsList = Deferred>();
protected readonly Deferred> AnyTagsList = Deferred>();
- protected static readonly Parser OutputStart = TagParsers.OutputTagStart();
- protected static readonly Parser OutputEnd = TagParsers.OutputTagEnd(true);
- protected static readonly Parser TagStart = TagParsers.TagStart();
- protected static readonly Parser TagStartSpaced = TagParsers.TagStart(true);
- protected static readonly Parser TagEnd = TagParsers.TagEnd(true);
+ internal const string WhiteSpaceChars = "\t\n\v\f\r \u0085 \u2028\u2029 ";
+
+ protected static readonly Parser InlineOutputStart = TagParsers.OutputTagStart();
+ protected static readonly Parser InlineOutputEnd = TagParsers.OutputTagEnd();
+ protected static readonly Parser InlineTagStart = TagParsers.TagStart();
+ protected static readonly Parser InlineTagEnd = TagParsers.TagEnd();
+
+ protected static readonly Parser NoInlineOutputStart = NonInlineLiquidTagParsers.OutputTagStart();
+ protected static readonly Parser NoInlineOutputEnd = Literals.AnyOf(WhiteSpaceChars, minSize: 0).SkipAnd(NonInlineLiquidTagParsers.OutputTagEnd());
+ protected static readonly Parser NoInlineTagStart = NonInlineLiquidTagParsers.TagStart();
+ protected static readonly Parser NoInlineTagEnd = Literals.AnyOf(WhiteSpaceChars, minSize: 0).SkipAnd(NonInlineLiquidTagParsers.TagEnd());
+
+ protected readonly Parser OutputStart = InlineOutputStart;
+ protected readonly Parser OutputEnd = InlineOutputEnd;
+ protected readonly Parser TagStart = InlineTagStart;
+ protected readonly Parser TagEnd = InlineTagEnd;
+
+ protected static readonly Parser RawOutputStart = NonInlineLiquidTagParsers.OutputTagStart();
+ protected static readonly Parser RawTagStart = NonInlineLiquidTagParsers.TagStart();
protected static readonly LiteralExpression EmptyKeyword = new LiteralExpression(EmptyValue.Instance);
protected static readonly LiteralExpression BlankKeyword = new LiteralExpression(BlankValue.Instance);
@@ -73,6 +87,14 @@ public FluidParser() : this(new())
public FluidParser(FluidParserOptions parserOptions)
{
+ if (!parserOptions.AllowLiquidTag)
+ {
+ OutputStart = NoInlineOutputStart;
+ OutputEnd = NoInlineOutputEnd;
+ TagStart = NoInlineTagStart;
+ TagEnd = NoInlineTagEnd;
+ }
+
String.Name = "String";
Number.Name = "Number";
@@ -574,7 +596,7 @@ public FluidParser(FluidParserOptions parserOptions)
Grammar = KnownTagsList;
}
- public static Parser CreateTag(string tagName) => TagStart.SkipAnd(Terms.Text(tagName)).AndSkip(TagEnd);
+ public Parser CreateTag(string tagName) => TagStart.SkipAnd(Terms.Text(tagName)).AndSkip(TagEnd);
public void RegisterIdentifierTag(string tagName, Func> render)
{
diff --git a/Fluid/FluidParserOptions.cs b/Fluid/FluidParserOptions.cs
index fe1f1727..56af6bc2 100644
--- a/Fluid/FluidParserOptions.cs
+++ b/Fluid/FluidParserOptions.cs
@@ -14,5 +14,10 @@ public class FluidParserOptions
/// Gets whether parentheses are allowed in templates. Default is false.
///
public bool AllowParentheses { get; set; }
+
+ ///
+ /// Gets whether the inline liquid tag is allowed in templates. Default is false.
+ ///
+ public bool AllowLiquidTag { get; set; }
}
}
diff --git a/Fluid/Parser/TagParsers.cs b/Fluid/Parser/TagParsers.cs
index 1096d1a5..d7796470 100644
--- a/Fluid/Parser/TagParsers.cs
+++ b/Fluid/Parser/TagParsers.cs
@@ -15,10 +15,10 @@ public struct ForModifier
public readonly struct TagResult
{
- public static readonly TagResult TagOpen = new TagResult(true, false);
- public static readonly TagResult TagOpenTrim = new TagResult(true, true);
- public static readonly TagResult TagClose = new TagResult(false, false);
- public static readonly TagResult TagCloseTrim = new TagResult(false, true);
+ public static readonly TagResult TagOpen = new(true, false);
+ public static readonly TagResult TagOpenTrim = new(true, true);
+ public static readonly TagResult TagClose = new(false, false);
+ public static readonly TagResult TagCloseTrim = new(false, true);
public TagResult(bool open, bool trim)
{
@@ -32,28 +32,17 @@ public TagResult(bool open, bool trim)
public static class TagParsers
{
- public static Parser TagStart(bool skipWhiteSpace = false) => new TagStartParser(skipWhiteSpace);
- public static Parser TagEnd(bool skipWhiteSpace = false) => new TagEndParser(skipWhiteSpace);
- public static Parser OutputTagStart(bool skipWhiteSpace = false) => new OutputTagStartParser(skipWhiteSpace);
- public static Parser OutputTagEnd(bool skipWhiteSpace = false) => new OutputTagEndParser(skipWhiteSpace);
+ public static Parser TagStart() => new TagStartParser();
+ public static Parser TagEnd() => new TagEndParser();
+ public static Parser OutputTagStart() => new OutputTagStartParser();
+ public static Parser OutputTagEnd() => new OutputTagEndParser();
private sealed class TagStartParser : Parser
{
- private readonly bool _skipWhiteSpace;
- public TagStartParser(bool skipWhiteSpace = false)
- {
- _skipWhiteSpace = skipWhiteSpace;
- }
-
public override bool Parse(ParseContext context, ref ParseResult result)
{
context.EnterParser(this);
- if (_skipWhiteSpace)
- {
- context.SkipWhiteSpace();
- }
-
var start = context.Scanner.Cursor.Position;
var p = (FluidParseContext)context;
@@ -66,7 +55,11 @@ public override bool Parse(ParseContext context, ref ParseResult resu
return true;
}
+#if NET6_0_OR_GREATER
+ if (context.Scanner.ReadText("{%"))
+#else
if (context.Scanner.ReadChar('{') && context.Scanner.ReadChar('%'))
+#endif
{
var trim = context.Scanner.ReadChar('-');
@@ -98,21 +91,12 @@ public override bool Parse(ParseContext context, ref ParseResult resu
}
///
- /// Search for `%}`, `-%}` or `-}` to close a tag.
+ /// Search for `%}`, `-%}` to close a tag.
/// Also, if the tag is inside a `liquid` tag, it will only look for a new line to close the tag.
///
- private sealed class TagEndParser : Parser, ISeekable
+ private sealed class TagEndParser : Parser
{
- private readonly bool _skipWhiteSpace;
-
- public bool CanSeek { get; set; } = true;
- public bool SkipWhitespace { get; set; } = false;
- public char[] ExpectedChars { get; set; } = ['\r', '\n', '}', '-', '%', ' ', '\t'];
-
- public TagEndParser(bool skipWhiteSpace = false)
- {
- _skipWhiteSpace = skipWhiteSpace;
- }
+ public bool SkipWhitespace { get; set; } = true;
public override bool Parse(ParseContext context, ref ParseResult result)
{
@@ -120,7 +104,9 @@ public override bool Parse(ParseContext context, ref ParseResult resu
var newLineIsPresent = false;
- if (_skipWhiteSpace)
+ var start = context.Scanner.Cursor.Position;
+
+ if (SkipWhitespace)
{
if (p.InsideLiquidTag)
{
@@ -146,7 +132,6 @@ public override bool Parse(ParseContext context, ref ParseResult resu
}
}
- var start = context.Scanner.Cursor.Position;
bool trim;
if (p.InsideLiquidTag)
@@ -160,7 +145,11 @@ public override bool Parse(ParseContext context, ref ParseResult resu
{
trim = context.Scanner.ReadChar('-');
+#if NET6_0_OR_GREATER
+ if (context.Scanner.ReadText("%}"))
+#else
if (context.Scanner.ReadChar('%') && context.Scanner.ReadChar('}'))
+#endif
{
p.StripNextTextSpanStatement = trim;
p.PreviousTextSpanStatement = null;
@@ -169,7 +158,7 @@ public override bool Parse(ParseContext context, ref ParseResult resu
context.Scanner.Cursor.ResetPosition(start);
- result.Set(start.Offset, start.Offset, TagResult.TagClose);
+ result.Set(start.Offset, context.Scanner.Cursor.Offset, trim ? TagResult.TagCloseTrim : TagResult.TagClose);
return true;
}
@@ -180,7 +169,11 @@ public override bool Parse(ParseContext context, ref ParseResult resu
trim = context.Scanner.ReadChar('-');
+#if NET6_0_OR_GREATER
+ if (context.Scanner.ReadText("%}"))
+#else
if (context.Scanner.ReadChar('%') && context.Scanner.ReadChar('}'))
+#endif
{
p.StripNextTextSpanStatement = trim;
p.PreviousTextSpanStatement = null;
@@ -200,23 +193,197 @@ public override bool Parse(ParseContext context, ref ParseResult resu
private sealed class OutputTagStartParser : Parser
{
- public OutputTagStartParser(bool skipWhiteSpace = false)
+ public override bool Parse(ParseContext context, ref ParseResult result)
+ {
+ var start = context.Scanner.Cursor.Position;
+
+#if NET6_0_OR_GREATER
+ if (context.Scanner.ReadText("{{"))
+#else
+ if (context.Scanner.ReadChar('{') && context.Scanner.ReadChar('{'))
+#endif
+ {
+ var trim = context.Scanner.ReadChar('-');
+
+ var p = (FluidParseContext)context;
+
+ if (p.PreviousTextSpanStatement != null)
+ {
+ if (trim)
+ {
+ p.PreviousTextSpanStatement.StripRight = true;
+ }
+
+ p.PreviousTextSpanStatement.NextIsOutput = true;
+
+ p.PreviousTextSpanStatement = null;
+ }
+
+
+ result.Set(start.Offset, context.Scanner.Cursor.Offset, trim ? TagResult.TagOpenTrim : TagResult.TagOpen);
+ return true;
+ }
+ else
+ {
+ context.Scanner.Cursor.ResetPosition(start);
+ return false;
+ }
+ }
+ }
+
+ private sealed class OutputTagEndParser : Parser
+ {
+ public override bool Parse(ParseContext context, ref ParseResult result)
+ {
+ context.EnterParser(this);
+
+ var start = context.Scanner.Cursor.Position;
+
+ context.SkipWhiteSpace();
+
+ var trim = context.Scanner.ReadChar('-');
+
+#if NET6_0_OR_GREATER
+ if (context.Scanner.ReadText("}}"))
+#else
+ if (context.Scanner.ReadChar('}') && context.Scanner.ReadChar('}'))
+#endif
+ {
+ var p = (FluidParseContext)context;
+
+ p.StripNextTextSpanStatement = trim;
+ p.PreviousTextSpanStatement = null;
+ p.PreviousIsTag = false;
+ p.PreviousIsOutput = true;
+
+ result.Set(start.Offset, context.Scanner.Cursor.Offset, trim ? TagResult.TagCloseTrim : TagResult.TagClose);
+
+
+ context.ExitParser(this);
+ return true;
+ }
+ else
+ {
+ context.Scanner.Cursor.ResetPosition(start);
+
+ context.ExitParser(this);
+ return false;
+ }
+ }
+ }
+ }
+
+ public static class NonInlineLiquidTagParsers
+ {
+ public static Parser TagStart() => new TagStartParser();
+ public static Parser TagEnd() => new TagEndParser();
+ public static Parser OutputTagStart() => new OutputTagStartParser();
+ public static Parser OutputTagEnd() => new OutputTagEndParser();
+
+ private sealed class TagStartParser : Parser, ISeekable
+ {
+ public bool CanSeek => true;
+ public char[] ExpectedChars { get; set; } = ['{'];
+ public bool SkipWhitespace { get; } = false;
+
+ public override bool Parse(ParseContext context, ref ParseResult result)
{
- SkipWhitespace = skipWhiteSpace;
+ context.EnterParser(this);
+
+ var start = context.Scanner.Cursor.Position;
+
+ var p = (FluidParseContext)context;
+
+#if NET6_0_OR_GREATER
+ if (context.Scanner.ReadText("{%"))
+#else
+ if (context.Scanner.ReadChar('{') && context.Scanner.ReadChar('%'))
+#endif
+ {
+ var trim = context.Scanner.ReadChar('-');
+
+ if (p.PreviousTextSpanStatement != null)
+ {
+ if (trim)
+ {
+ p.PreviousTextSpanStatement.StripRight = true;
+ }
+
+ p.PreviousTextSpanStatement.NextIsTag = true;
+
+ p.PreviousTextSpanStatement = null;
+ }
+
+ result.Set(start.Offset, context.Scanner.Cursor.Offset, trim ? TagResult.TagOpenTrim : TagResult.TagOpen);
+
+ context.ExitParser(this);
+ return true;
+ }
+ else
+ {
+ context.Scanner.Cursor.ResetPosition(start);
+
+ context.ExitParser(this);
+ return false;
+ }
}
+ }
- public bool SkipWhitespace { get; }
+ private sealed class TagEndParser : Parser, ISeekable
+ {
+ public bool CanSeek => true;
+ public char[] ExpectedChars { get; set; } = ['-', '%'];
+ public bool SkipWhitespace { get; set; } = false;
public override bool Parse(ParseContext context, ref ParseResult result)
{
+ var p = (FluidParseContext)context;
+
var start = context.Scanner.Cursor.Position;
- if (SkipWhitespace)
+ bool trim;
+
+ trim = context.Scanner.ReadChar('-');
+
+#if NET6_0_OR_GREATER
+ if (context.Scanner.ReadText("%}"))
+#else
+ if (context.Scanner.ReadChar('%') && context.Scanner.ReadChar('}'))
+#endif
+ {
+ p.StripNextTextSpanStatement = trim;
+ p.PreviousTextSpanStatement = null;
+ p.PreviousIsTag = true;
+ p.PreviousIsOutput = false;
+
+ result.Set(start.Offset, context.Scanner.Cursor.Offset, trim ? TagResult.TagCloseTrim : TagResult.TagClose);
+ return true;
+ }
+ else
{
- context.SkipWhiteSpace();
+ context.Scanner.Cursor.ResetPosition(start);
+ return false;
}
+ }
+ }
+ private sealed class OutputTagStartParser : Parser, ISeekable
+ {
+ public bool CanSeek => true;
+
+ public char[] ExpectedChars { get; set; } = ['{'];
+
+ public bool SkipWhitespace { get; } = false;
+
+ public override bool Parse(ParseContext context, ref ParseResult result)
+ {
+ var start = context.Scanner.Cursor.Position;
+
+#if NET6_0_OR_GREATER
+ if (context.Scanner.ReadText("{{"))
+#else
if (context.Scanner.ReadChar('{') && context.Scanner.ReadChar('{'))
+#endif
{
var trim = context.Scanner.ReadChar('-');
@@ -248,31 +415,25 @@ public override bool Parse(ParseContext context, ref ParseResult resu
private sealed class OutputTagEndParser : Parser, ISeekable
{
- public OutputTagEndParser(bool skipWhiteSpace = false)
- {
- SkipWhitespace = skipWhiteSpace;
- }
-
public bool CanSeek => true;
public char[] ExpectedChars { get; set; } = ['-', '}'];
- public bool SkipWhitespace { get; }
+ public bool SkipWhitespace { get; } = false;
public override bool Parse(ParseContext context, ref ParseResult result)
{
context.EnterParser(this);
- if (SkipWhitespace)
- {
- context.SkipWhiteSpace();
- }
-
var start = context.Scanner.Cursor.Position;
var trim = context.Scanner.ReadChar('-');
+#if NET6_0_OR_GREATER
+ if (context.Scanner.ReadText("}}"))
+#else
if (context.Scanner.ReadChar('}') && context.Scanner.ReadChar('}'))
+#endif
{
var p = (FluidParseContext)context;
@@ -297,4 +458,5 @@ public override bool Parse(ParseContext context, ref ParseResult resu
}
}
}
+
}
diff --git a/README.md b/README.md
index 684f78e4..3a216f5d 100644
--- a/README.md
+++ b/README.md
@@ -1130,43 +1130,43 @@ Run it locally to analyze the time it takes to execute specific templates.
#### Results
Fluid is faster and allocates less memory than all other well-known .NET Liquid parsers.
-For parsing, Fluid is 20% faster than the second, Scriban, allocating 2 times less memory.
-For rendering, Fluid is 30% faster than the second, Handlebars, allocating half the memory, and 5 times faster than Scriban.
+For parsing, Fluid is 30% faster than the second best, Scriban, allocating half the memory.
+For rendering, Fluid is 20% faster than the second best, Handlebars, and allocating half the memory.
Compared to DotLiquid, Fluid renders 10 times faster, and allocates 34 times less memory.
``` text
-BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.2314)
+BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.3476)
12th Gen Intel Core i7-1260P, 1 CPU, 16 logical and 12 physical cores
-.NET SDK 9.0.100
- [Host] : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2
- ShortRun : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2
+.NET SDK 9.0.201
+ [Host] : .NET 9.0.3 (9.0.325.11113), X64 RyuJIT AVX2
+ ShortRun : .NET 9.0.3 (9.0.325.11113), X64 RyuJIT AVX2
Job=ShortRun IterationCount=3 LaunchCount=1
WarmupCount=3
-| Method | Mean | Error | StdDev | Ratio | Allocated | Alloc Ratio |
-|------------------- |--------------:|--------------:|------------:|---------:|------------:|------------:|
-| Fluid_Parse | 2.622 us | 1.4586 us | 0.0800 us | 1.00 | 2.83 KB | 1.00 |
-| Scriban_Parse | 3.149 us | 0.8304 us | 0.0455 us | 1.20 | 7.14 KB | 2.53 |
-| DotLiquid_Parse | 6.133 us | 1.5094 us | 0.0827 us | 2.34 | 16.21 KB | 5.73 |
-| LiquidNet_Parse | 23.112 us | 6.0582 us | 0.3321 us | 8.82 | 62.04 KB | 21.94 |
-| Handlebars_Parse | 2,662.991 us | 4,830.0818 us | 264.7531 us | 1,016.17 | 155.42 KB | 54.95 |
-| | | | | | | |
-| Fluid_ParseBig | 10.642 us | 2.0982 us | 0.1150 us | 1.00 | 11.66 KB | 1.00 |
-| Scriban_ParseBig | 18.546 us | 14.2197 us | 0.7794 us | 1.74 | 32.07 KB | 2.75 |
-| DotLiquid_ParseBig | 25.980 us | 8.1228 us | 0.4452 us | 2.44 | 94.36 KB | 8.10 |
-| LiquidNet_ParseBig | 11,175.713 us | 5,605.1094 us | 307.2350 us | 1,050.22 | 28542.56 KB | 2,448.69 |
-| | | | | | | |
-| Fluid_Render | 127.984 us | 46.8250 us | 2.5666 us | 1.00 | 95.87 KB | 1.00 |
-| Scriban_Render | 601.083 us | 86.9414 us | 4.7656 us | 4.70 | 498.66 KB | 5.20 |
-| DotLiquid_Render | 1,248.906 us | 231.9350 us | 12.7131 us | 9.76 | 3270.3 KB | 34.11 |
-| LiquidNet_Render | 903.463 us | 2,324.0151 us | 127.3871 us | 7.06 | 3126.47 KB | 32.61 |
-| Handlebars_Render | 170.182 us | 30.0175 us | 1.6454 us | 1.33 | 194.92 KB | 2.03 |
-```
-
-Tested on November 24, 2024 with
-- Scriban 5.12.0
-- DotLiquid 2.2.692
+| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio |
+|------------------- |--------------:|-------------:|------------:|---------:|--------:|----------:|---------:|--------:|------------:|------------:|
+| Fluid_Parse | 2.418 us | 1.770 us | 0.0970 us | 1.00 | 0.05 | 0.3090 | - | - | 2.84 KB | 1.00 |
+| Scriban_Parse | 3.156 us | 1.316 us | 0.0722 us | 1.31 | 0.05 | 0.7744 | 0.0267 | - | 7.14 KB | 2.51 |
+| DotLiquid_Parse | 6.216 us | 8.672 us | 0.4753 us | 2.57 | 0.19 | 1.7548 | 0.0229 | - | 16.15 KB | 5.68 |
+| LiquidNet_Parse | 23.808 us | 3.388 us | 0.1857 us | 9.86 | 0.34 | 6.7444 | 0.6104 | - | 62.04 KB | 21.82 |
+| Handlebars_Parse | 2,504.618 us | 6,081.515 us | 333.3484 us | 1,036.88 | 124.65 | 15.6250 | - | - | 153.81 KB | 54.09 |
+| | | | | | | | | | | |
+| Fluid_ParseBig | 11.383 us | 10.116 us | 0.5545 us | 1.00 | 0.06 | 1.2817 | 0.0305 | - | 11.86 KB | 1.00 |
+| Scriban_ParseBig | 17.779 us | 5.888 us | 0.3227 us | 1.56 | 0.07 | 3.4790 | 0.4883 | - | 32.07 KB | 2.70 |
+| DotLiquid_ParseBig | 25.596 us | 3.955 us | 0.2168 us | 2.25 | 0.09 | 10.2539 | 0.4578 | - | 94.24 KB | 7.95 |
+| LiquidNet_ParseBig | 11,786.566 us | 6,521.074 us | 357.4421 us | 1,037.03 | 50.83 | 3093.7500 | 15.6250 | - | 28542.55 KB | 2,406.75 |
+| | | | | | | | | | | |
+| Fluid_Render | 134.037 us | 101.115 us | 5.5425 us | 1.00 | 0.05 | 10.2539 | 0.4883 | - | 95.88 KB | 1.00 |
+| Scriban_Render | 633.523 us | 269.921 us | 14.7953 us | 4.73 | 0.19 | 68.3594 | 68.3594 | 68.3594 | 498.68 KB | 5.20 |
+| DotLiquid_Render | 1,402.392 us | 918.007 us | 50.3191 us | 10.47 | 0.49 | 361.3281 | 150.3906 | 29.2969 | 3272.13 KB | 34.13 |
+| LiquidNet_Render | 871.168 us | 726.955 us | 39.8469 us | 6.51 | 0.34 | 339.8438 | 160.1563 | - | 3126.39 KB | 32.61 |
+| Handlebars_Render | 161.000 us | 64.468 us | 3.5337 us | 1.20 | 0.05 | 20.9961 | 3.4180 | - | 194.92 KB | 2.03 |
+```
+
+Tested on 3/22/2025 with
+- Scriban 6.0.0
+- DotLiquid 2.3.107
- Liquid.NET 0.10.0
- Handlebars.Net 2.1.6
diff --git a/Versions.props b/Versions.props
index 0efa3b81..c2e25f31 100644
--- a/Versions.props
+++ b/Versions.props
@@ -1,12 +1,18 @@
-
- 9.0.0
- 8.0.5
+
+ 6.0.1
+
+
+
+ 6.0.1
+
+
+
+ 8.0.0
9.0.0
- 9.0.2