Add support for logical expressions

This commit is contained in:
Charles-P. Clermont
2026-01-26 16:53:38 -05:00
parent e911eea3df
commit 429711fd82
5 changed files with 104 additions and 4 deletions
+9 -1
View File
@@ -13,6 +13,14 @@ module Liquid
def evaluate(context)
left = value(left_node, context)
# logical relation short circuiting
if operator == 'and'
return left && value(right_node, context)
elsif operator == 'or'
return left || value(right_node, context)
end
right = value(right_node, context)
case operator
@@ -38,7 +46,7 @@ module Liquid
end
def to_s
"(#{left_node} #{operator} #{right_node})"
"(#{left_node.inspect} #{operator} #{right_node.inspect})"
end
private
+20 -2
View File
@@ -47,12 +47,30 @@ module Liquid
tok[0] == type
end
# expression := equality
# expression := logical
# logical := equality (("and" | "or") equality)*
# equality := comparison (("==" | "!=" | "<>") comparison)*
# comparison := primary ((">=" | ">" | "<" | "<=" | ... ) primary)*
# primary := string | number | variable_lookup | range | boolean
def expression
equality
logical
end
# Logical relations in Liquid, unlike other languages, are right-to-left
# associative. This creates a right-leaning tree and is why the method
# looks a bit more complicated
#
# `a == b and b or c` is evaluated like (a and (b or c))
def logical
expr = equality
while (operator = id?('and') || id?('or'))
if expr.is_a?(BinaryExpression) && (expr.operator == 'and' || expr.operator == 'or')
expr.right_node = BinaryExpression.new(expr.right_node, operator, equality)
else
expr = BinaryExpression.new(expr, operator, equality)
end
end
expr
end
def equality
+1 -1
View File
@@ -99,7 +99,7 @@ module Liquid
parser = @parse_context.new_parser(markup)
loop do
expr = BinaryExpression.new(@left, '==', Condition.parse_expression(parser))
expr = BinaryExpression.new(@left, '==', parser.equality)
block = Condition.new(expr)
block.attach(body)
@blocks << block