element function

Parser element()

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