Rename Parser#expression_node -> Parser#expression

This commit is contained in:
Charles-P. Clermont
2026-01-26 16:52:17 -05:00
parent 6cfcd6cac0
commit c9ae128354
11 changed files with 73 additions and 56 deletions
+11 -6
View File
@@ -2,8 +2,6 @@
## 6.0.0 ## 6.0.0
### Architectural changes
### Features ### Features
* (TODO) Add support for boolean expressions everywhere * (TODO) Add support for boolean expressions everywhere
* As variable output `{{ a or b }}` * As variable output `{{ a or b }}`
@@ -21,6 +19,13 @@
- (TODO) Add support for parenthesized expressions - (TODO) Add support for parenthesized expressions
* e.g. `(a or b) and c` * e.g. `(a or b) and c`
### Architectural changes
* `parse_expression` and `safe_parse_expression` have been removed from `Tag` and `ParseContext`
* `Parser` methods now produce AST nodes instead of strings
* `Parser#expression` produces a value,
* `Parser#string` produces a string,
* etc.
### Breaking changes ### Breaking changes
* The Environment's `error_mode` option has been removed. * The Environment's `error_mode` option has been removed.
* `:warn` is no longer supported * `:warn` is no longer supported
@@ -28,14 +33,14 @@
* `:strict` and `strict_parse` is no longer supported * `:strict` and `strict_parse` is no longer supported
* `strict2_parse` is renamed to `parse_markup` * `strict2_parse` is renamed to `parse_markup`
* The `warnings` system has been removed. * The `warnings` system has been removed.
* `safe_parse_expression` has been moved to `Parser.expression_node` * `Parser#expression` is renamed to `Parser#expression_string`
* `parse_expression` methods have been moved to `Parser#unsafe_parse_expression` * `safe_parse_expression` methods are replaced by `Parser#expression`
* Use `Parser#expression_node`, `Parser#string`, etc. instead * `parse_expression` methods are replaced by `Parser#unsafe_parse_expression`
### Migrating from `^5.11.0` ### Migrating from `^5.11.0`
- In custom tags that include `ParserSwitching`, rename `strict2_parse` to `parse_markup` - In custom tags that include `ParserSwitching`, rename `strict2_parse` to `parse_markup`
- Remove code depending on `:error_mode` - Remove code depending on `:error_mode`
- Replace `safe_parse_expression` calls with `Parser.expression_node` - Replace `safe_parse_expression` calls with `Parser#expression`
## 5.11.0 ## 5.11.0
* Revert the Inline Snippets tag (#2001), treat its inclusion in the latest Liquid release as a bug, and allow for feedback on RFC#1916 to better support Liquid developers [Guilherme Carreiro] * Revert the Inline Snippets tag (#2001), treat its inclusion in the latest Liquid release as a bug, and allow for feedback on RFC#1916 to better support Liquid developers [Guilherme Carreiro]
+1 -1
View File
@@ -47,7 +47,7 @@ module Liquid
tok[0] == type tok[0] == type
end end
def expression_node def expression
parse_expression(expression_string) parse_expression(expression_string)
end end
+2 -2
View File
@@ -85,7 +85,7 @@ module Liquid
def parse_markup(markup) def parse_markup(markup)
parser = @parse_context.new_parser(markup) parser = @parse_context.new_parser(markup)
@left = parser.expression_node @left = parser.expression
parser.consume(:end_of_string) parser.consume(:end_of_string)
end end
@@ -99,7 +99,7 @@ module Liquid
parser = @parse_context.new_parser(markup) parser = @parse_context.new_parser(markup)
loop do loop do
expr = parser.expression_node expr = parser.expression
block = Condition.new(@left, '==', expr) block = Condition.new(@left, '==', expr)
block.attach(body) block.attach(body)
@blocks << block @blocks << block
+3 -3
View File
@@ -61,14 +61,14 @@ module Liquid
raise SyntaxError, options[:locale].t("errors.syntax.cycle") if p.look(:end_of_string) raise SyntaxError, options[:locale].t("errors.syntax.cycle") if p.look(:end_of_string)
first_expression = p.expression_node first_expression = p.expression
if p.look(:colon) if p.look(:colon)
# cycle name: expr1, expr2, ... # cycle name: expr1, expr2, ...
@name = first_expression @name = first_expression
@is_named = true @is_named = true
p.consume(:colon) p.consume(:colon)
# After the colon, parse the first variable (required for named cycles) # After the colon, parse the first variable (required for named cycles)
@variables << maybe_dup_lookup(p.expression_node) @variables << maybe_dup_lookup(p.expression)
else else
# cycle expr1, expr2, ... # cycle expr1, expr2, ...
@variables << maybe_dup_lookup(first_expression) @variables << maybe_dup_lookup(first_expression)
@@ -78,7 +78,7 @@ module Liquid
while p.consume?(:comma) while p.consume?(:comma)
break if p.look(:end_of_string) break if p.look(:end_of_string)
@variables << maybe_dup_lookup(p.expression_node) @variables << maybe_dup_lookup(p.expression)
end end
p.consume(:end_of_string) p.consume(:end_of_string)
+3 -3
View File
@@ -84,8 +84,8 @@ module Liquid
def parse_markup(markup) def parse_markup(markup)
p = @parse_context.new_parser(markup) p = @parse_context.new_parser(markup)
@template_name_expr = p.expression_node @template_name_expr = p.expression
@variable_name_expr = p.expression_node if p.id?("for") || p.id?("with") @variable_name_expr = p.expression if p.id?("for") || p.id?("with")
@alias_name = p.consume(:id) if p.id?("as") @alias_name = p.consume(:id) if p.id?("as")
p.consume?(:comma) p.consume?(:comma)
@@ -94,7 +94,7 @@ module Liquid
while p.look(:id) while p.look(:id)
key = p.consume key = p.consume
p.consume(:colon) p.consume(:colon)
@attributes[key] = p.expression_node @attributes[key] = p.expression
p.consume?(:comma) p.consume?(:comma)
end end
+2 -2
View File
@@ -89,7 +89,7 @@ module Liquid
@template_name_expr = template_name(p) @template_name_expr = template_name(p)
with_or_for = p.id?("for") || p.id?("with") with_or_for = p.id?("for") || p.id?("with")
@variable_name_expr = p.expression_node if with_or_for @variable_name_expr = p.expression if with_or_for
@alias_name = p.consume(:id) if p.id?("as") @alias_name = p.consume(:id) if p.id?("as")
@is_for_loop = (with_or_for == FOR) @is_for_loop = (with_or_for == FOR)
@@ -99,7 +99,7 @@ module Liquid
while p.look(:id) while p.look(:id)
key = p.consume key = p.consume
p.consume(:colon) p.consume(:colon)
@attributes[key] = p.expression_node @attributes[key] = p.expression
p.consume?(:comma) p.consume?(:comma)
end end
+2 -2
View File
@@ -42,7 +42,7 @@ module Liquid
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in") raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in")
end end
@collection_name = p.expression_node @collection_name = p.expression
p.consume?(:comma) p.consume?(:comma)
@@ -54,7 +54,7 @@ module Liquid
end end
p.consume(:colon) p.consume(:colon)
@attributes[key] = p.expression_node @attributes[key] = p.expression
p.consume?(:comma) p.consume?(:comma)
end end
+3 -3
View File
@@ -47,7 +47,7 @@ module Liquid
return if p.look(:end_of_string) return if p.look(:end_of_string)
@name = p.expression_node @name = p.expression
@filters << parse_filter_expressions(p) while p.consume?(:pipe) @filters << parse_filter_expressions(p) while p.consume?(:pipe)
p.consume(:end_of_string) p.consume(:end_of_string)
end end
@@ -121,10 +121,10 @@ module Liquid
if p.look(:id) && p.look(:colon, 1) if p.look(:id) && p.look(:colon, 1)
key = p.consume(:id) key = p.consume(:id)
p.consume(:colon) p.consume(:colon)
value = p.expression_node value = p.expression
keyword_arguments[key] = value keyword_arguments[key] = value
else else
positional_arguments << p.expression_node positional_arguments << p.expression
end end
end end
+32 -21
View File
@@ -6,7 +6,7 @@ require "benchmark/ips"
require 'liquid' require 'liquid'
RubyVM::YJIT.enable RubyVM::YJIT.enable if defined?(RubyVM::YJIT)
STRING_MARKUPS = [ STRING_MARKUPS = [
"\"foo\"", "\"foo\"",
@@ -45,22 +45,14 @@ NUMBER_MARKUPS = [
RANGE_MARKUPS = [ RANGE_MARKUPS = [
"(1..30)", "(1..30)",
"(1...30)",
"(1..30..5)",
"(1.0...30.0)",
"(1.........30)",
"(1..foo)", "(1..foo)",
"(foo..30)", "(foo..30)",
"(foo..bar)", "(foo..bar)",
"(foo...bar...100)",
"(foo...bar...100.0)",
] ]
LITERAL_MARKUPS = [ LITERAL_MARKUPS = [
nil,
'nil', 'nil',
'null', 'null',
'',
'true', 'true',
'false', 'false',
'blank', 'blank',
@@ -75,20 +67,39 @@ MARKUPS = {
"range" => RANGE_MARKUPS, "range" => RANGE_MARKUPS,
} }
Benchmark.ips do |x| module Liquid
x.config(time: 5, warmup: 5) Benchmark.ips do |x|
x.config(time: 5, warmup: 5)
MARKUPS.each do |type, markups| ss = StringScanner.new('')
x.report("Liquid::Expression#parse: #{type}") do
markups.each do |markup| MARKUPS.each do |type, markups|
Liquid::Expression.parse(markup) x.report("#{type} - Liquid::Expression#parse") do
markups.each do |markup|
ss.string = markup
Expression.parse(markup, ss)
end
end
x.report("#{type} - Liquid::Parser#expression") do
markups.each do |markup|
ss.string = markup
Parser.new(ss).expression
end
end
x.report("#{type} - Liquid::Expression.parse(Parser#expression_string)") do
markups.each do |markup|
ss.string = markup
Expression.parse(Parser.new(ss).expression_string, ss)
end
end
end
x.report("Liquid::Expression#parse: all") do
MARKUPS.values.flatten.each do |markup|
Expression.parse(markup)
end end
end end
end end
x.report("Liquid::Expression#parse: all") do
MARKUPS.values.flatten.each do |markup|
Liquid::Expression.parse(markup)
end
end
end end
+4 -3
View File
@@ -6,7 +6,7 @@ require "benchmark/ips"
require 'liquid' require 'liquid'
RubyVM::YJIT.enable RubyVM::YJIT.enable if defined?(RubyVM::YJIT)
EXPRESSIONS = [ EXPRESSIONS = [
"foo[1..2].baz", "foo[1..2].baz",
@@ -31,11 +31,12 @@ EXPRESSIONS = [
Benchmark.ips do |x| Benchmark.ips do |x|
x.config(time: 10, warmup: 5) x.config(time: 10, warmup: 5)
ss = StringScanner.new('')
x.report("Liquid::Lexer#tokenize") do x.report("Liquid::Lexer#tokenize") do
EXPRESSIONS.each do |expr| EXPRESSIONS.each do |expr|
l = Liquid::Lexer.new(expr) ss.string = expr
l.tokenize Liquid::Lexer.tokenize(ss)
end end
end end
+10 -10
View File
@@ -5,45 +5,45 @@ require 'test_helper'
class ParseContextUnitTest < Minitest::Test class ParseContextUnitTest < Minitest::Test
include Liquid include Liquid
def test_parser_expression_node_with_variable_lookup def test_parser_expression_with_variable_lookup
parser = parse_context.new_parser('product.title') parser = parse_context.new_parser('product.title')
result = parser.expression_node result = parser.expression
assert_instance_of(VariableLookup, result) assert_instance_of(VariableLookup, result)
assert_equal('product', result.name) assert_equal('product', result.name)
assert_equal(['title'], result.lookups) assert_equal(['title'], result.lookups)
end end
def test_parser_expression_node_raises_syntax_error_for_invalid_expression def test_parser_expression_raises_syntax_error_for_invalid_expression
parser = parse_context.new_parser('') parser = parse_context.new_parser('')
error = assert_raises(Liquid::SyntaxError) do error = assert_raises(Liquid::SyntaxError) do
parser.expression_node parser.expression
end end
assert_match(/is not a valid expression/, error.message) assert_match(/is not a valid expression/, error.message)
end end
def test_parse_expression_with_variable_lookup def test_parse_expression_with_variable_lookup
result = parse_context.new_parser('product.title').expression_node result = parse_context.new_parser('product.title').expression
assert_instance_of(VariableLookup, result) assert_instance_of(VariableLookup, result)
assert_equal('product', result.name) assert_equal('product', result.name)
assert_equal(['title'], result.lookups) assert_equal(['title'], result.lookups)
end end
def test_parser_expression_node_advances_parser_pointer def test_parser_expression_advances_parser_pointer
parser = parse_context.new_parser('foo, bar') parser = parse_context.new_parser('foo, bar')
# parser.expression_node consumes "foo" # parser.expression consumes "foo"
first_result = parser.expression_node first_result = parser.expression
assert_instance_of(VariableLookup, first_result) assert_instance_of(VariableLookup, first_result)
assert_equal('foo', first_result.name) assert_equal('foo', first_result.name)
parser.consume(:comma) parser.consume(:comma)
# parser.expression_node consumes "bar" # parser.expression consumes "bar"
second_result = parser.expression_node second_result = parser.expression
assert_instance_of(VariableLookup, second_result) assert_instance_of(VariableLookup, second_result)
assert_equal('bar', second_result.name) assert_equal('bar', second_result.name)