From 25f9224c856444746c1ffe961e24cc91697da0c6 Mon Sep 17 00:00:00 2001 From: Tobi Lutke Date: Wed, 11 Mar 2026 07:29:47 -0400 Subject: [PATCH] fast-path simple variable parsing: skip Lexer/Parser for plain dot-separated lookups --- lib/liquid/variable.rb | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/liquid/variable.rb b/lib/liquid/variable.rb index 21f65582..1088f58b 100644 --- a/lib/liquid/variable.rb +++ b/lib/liquid/variable.rb @@ -24,13 +24,37 @@ module Liquid include ParserSwitching + # Fast path regex: matches simple "name.lookup.chain" with no filters, no brackets, no quotes + # This avoids the full Lexer → Parser → Expression pipeline for the most common case + SIMPLE_VARIABLE = /\A\s*([a-zA-Z_][\w-]*(?:\.[a-zA-Z_][\w-]*)*)\s*\z/ + def initialize(markup, parse_context) @markup = markup @name = nil @parse_context = parse_context @line_number = parse_context.line_number - strict_parse_with_error_mode_fallback(markup) + # Fast path for simple variables like "product.title" (no filters, no brackets) + if markup =~ SIMPLE_VARIABLE + expr_markup = Regexp.last_match(1) + @filters = Const::EMPTY_ARRAY + if Expression::LITERALS.key?(expr_markup) + @name = Expression::LITERALS[expr_markup] + else + cache = parse_context.instance_variable_get(:@expression_cache) + if cache + @name = cache[expr_markup] || (cache[expr_markup] = VariableLookup.parse( + expr_markup, + parse_context.instance_variable_get(:@string_scanner), + cache, + ).freeze) + else + @name = VariableLookup.parse(expr_markup, StringScanner.new(""), nil).freeze + end + end + else + strict_parse_with_error_mode_fallback(markup) + end end def raw