mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-18 02:10:41 -07:00
Use BinaryExpression instead of Condition for comparisons
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user