Use BinaryExpression instead of Condition for comparisons

This commit is contained in:
Charles-P. Clermont
2026-01-26 16:52:31 -05:00
parent 0b890904f0
commit f60d3f0a41
10 changed files with 200 additions and 82 deletions
+1
View File
@@ -62,6 +62,7 @@ require 'liquid/interrupts'
require 'liquid/tags'
require "liquid/environment"
require 'liquid/lexer'
require 'liquid/method_literal'
require 'liquid/binary_expression'
require 'liquid/parser'
require 'liquid/i18n'
+55 -17
View File
@@ -2,39 +2,41 @@
module Liquid
class BinaryExpression
attr_reader :left, :operator, :right
attr_reader :operator
attr_accessor :left_node, :right_node
def initialize(left, operator, right)
@left = left
@left_node = left
@operator = operator
@right = right
@right_node = right
end
def evaluate(context)
left_value = value(left, context)
right_value = value(@right, context)
left = value(left_node, context)
right = value(right_node, context)
case operator
when '>'
left_value > right_value
left > right if can_compare?(left, right)
when '>='
left_value >= right_value
left >= right if can_compare?(left, right)
when '<'
left_value < right_value
left < right if can_compare?(left, right)
when '<='
left_value <= right_value
left <= right if can_compare?(left, right)
when '=='
left_value == right_value
equal_variables(left, right)
when '!=', '<>'
left_value != right_value
!equal_variables(left, right)
when 'contains'
if left_value && right_value && left_value.respond_to?(:include?)
right_value = right_value.to_s if left_value.is_a?(String)
left_value.include?(right_value)
else
false
end
contains(left, right)
end
rescue ::ArgumentError => e
raise Liquid::ArgumentError, e.message
end
def to_s
"(#{left_node} #{operator} #{right_node})"
end
private
@@ -42,5 +44,41 @@ module Liquid
def value(expr, context)
Utils.to_liquid_value(context.evaluate(expr))
end
def can_compare?(left, right)
left.respond_to?(operator) && right.respond_to?(operator) && !left.is_a?(Hash) && !right.is_a?(Hash)
end
def contains(left, right)
if left && right && left.respond_to?(:include?)
right = right.to_s if left.is_a?(String)
left.include?(right)
else
false
end
rescue Encoding::CompatibilityError
# "✅".b.include?("✅") raises Encoding::CompatibilityError despite being materially equal
left.b.include?(right.b)
end
def apply_method_literal(node, other)
other.send(node.method_name) if other.respond_to?(node.method_name)
end
def equal_variables(left, right)
return apply_method_literal(left, right) if left.is_a?(MethodLiteral)
return apply_method_literal(right, left) if right.is_a?(MethodLiteral)
left == right
end
class ParseTreeVisitor < Liquid::ParseTreeVisitor
def children
[
@node.left_node,
@node.right_node,
]
end
end
end
end
-10
View File
@@ -29,16 +29,6 @@ module Liquid
left.b.include?(right.b)
end,
}
class MethodLiteral
attr_reader :method_name, :to_s
def initialize(method_name, to_s)
@method_name = method_name
@to_s = to_s
end
end
@@method_literals = {
'blank' => MethodLiteral.new(:blank?, '').freeze,
'empty' => MethodLiteral.new(:empty?, '').freeze,
+2 -2
View File
@@ -9,8 +9,8 @@ module Liquid
'' => nil,
'true' => true,
'false' => false,
'blank' => '',
'empty' => '',
'blank' => MethodLiteral.new(:blank?, '').freeze,
'empty' => MethodLiteral.new(:empty?, '').freeze,
}.freeze
DOT = ".".ord
+16
View File
@@ -0,0 +1,16 @@
# frozen_string_literal: true
module Liquid
class MethodLiteral
attr_reader :method_name, :to_s
def initialize(method_name, to_s)
@method_name = method_name
@to_s = to_s
end
def to_liquid
to_s
end
end
end
+1 -7
View File
@@ -96,13 +96,7 @@ module Liquid
end
def parse_comparison(p)
a = parse_expression(p)
if (op = p.consume?(:comparison) || p.consume?(:equality))
b = parse_expression(p)
Condition.new(a, op, b)
else
Condition.new(a)
end
Condition.new(p.expression)
end
class ParseTreeVisitor < Liquid::ParseTreeVisitor