element function
Optimized element parser using lookahead to avoid unnecessary backtracking.
Instead of trying all parsers in order, we first check the delimiter:
{{-> variable-like parsers (including custom tags like super()){%-> tag/block parsers- otherwise -> text
The lookahead includes whitespace-trimming variants ({%-, -%}, {{-, -}}) to properly handle Liquid's whitespace control syntax.
Implementation
Parser element() {
// Separate custom parsers by their delimiter type.
// Custom parsers that use {{ }} syntax (like SuperTag) go in variable branch,
// those using {% %} syntax (default) go in tag branch.
final varCustomParsers = <Parser>[];
final tagCustomParsers = <Parser>[];
for (final customParser in TagRegistry.customParsers) {
if (customParser.delimiterType == TagDelimiterType.variable) {
varCustomParsers.add(customParser.parser());
} else {
tagCustomParsers.add(customParser.parser());
}
}
// Variable-like elements (start with {{ or {{-)
// Try custom parsers first (like {{ super() }}) then standard variable
final variableElements = [
...varCustomParsers,
ref0(variable),
].toChoiceParser();
// Tag/block parsers (start with {% or {%-)
final tagElements = [
ref0(ifBlock),
ref0(forBlock),
ref0(caseBlock),
ref0(whenBlock),
ref0(elseBlockForCase),
ref0(elseBlockForFor),
ref0(hashBlockComment),
...tagCustomParsers,
ref0(tag),
].toChoiceParser();
// Text parser (anything not starting with {{ or {%)
final textElement = ref0(text);
// Lookahead parsers for delimiters.
// The whitespace-stripping variants ({%-, {{-) need .trim() to consume leading
// whitespace - this implements Liquid's whitespace control at parse time.
final tagLookahead = (string('{%-').trim() | string('{%')).and();
final varLookahead = (string('{{-').trim() | string('{{')).and();
// Use lookahead to determine which parser to use
// This avoids trying all block parsers when we're looking at plain text
return (
// If we see '{{' or '{{-', parse as variable-like element
(varLookahead & variableElements).pick(1) |
// If we see '{%' or '{%-', parse as tag/block
(tagLookahead & tagElements).pick(1) |
// Otherwise, parse as text
textElement)
.labeled('element');
}