diff --git a/lib/liquid.rb b/lib/liquid.rb index 4d0a71a6..d069f6ca 100644 --- a/lib/liquid.rb +++ b/lib/liquid.rb @@ -62,6 +62,7 @@ require 'liquid/interrupts' require 'liquid/tags' require "liquid/environment" require 'liquid/lexer' +require 'liquid/binary_expression' require 'liquid/parser' require 'liquid/i18n' require 'liquid/drop' diff --git a/lib/liquid/binary_expression.rb b/lib/liquid/binary_expression.rb new file mode 100644 index 00000000..944099f7 --- /dev/null +++ b/lib/liquid/binary_expression.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module Liquid + class BinaryExpression + attr_reader :left, :operator, :right + + def initialize(left, operator, right) + @left = left + @operator = operator + @right = right + end + + def evaluate(context) + left_value = value(left, context) + right_value = value(@right, context) + + case operator + when '>' + left_value > right_value + when '>=' + left_value >= right_value + when '<' + left_value < right_value + when '<=' + left_value <= right_value + when '==' + left_value == right_value + when '!=', '<>' + left_value != right_value + 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 + end + end + + private + + def value(expr, context) + Utils.to_liquid_value(context.evaluate(expr)) + end + end +end diff --git a/lib/liquid/parser.rb b/lib/liquid/parser.rb index 7003af9b..66d7c423 100644 --- a/lib/liquid/parser.rb +++ b/lib/liquid/parser.rb @@ -47,7 +47,24 @@ module Liquid tok[0] == type end + # expression := comparison + # comparison := primary ((">=" | ">" | "<" | "<=" | ... ) primary)* + # primary := string | number | variable_lookup | range | boolean def expression + comparison + end + + # comparison := primary ((">=" | ">" | "<" | "<=" | ... ) primary)* + def comparison + expr = primary + while look(:comparison) + operator = consume + expr = BinaryExpression.new(expr, operator, primary) + end + expr + end + + def primary token = @tokens[@p] case token[0] when :id diff --git a/test/unit/parser_unit_test.rb b/test/unit/parser_unit_test.rb index cee971d1..a4d52d65 100644 --- a/test/unit/parser_unit_test.rb +++ b/test/unit/parser_unit_test.rb @@ -77,6 +77,30 @@ class ParserUnitTest < Minitest::Test assert_equal(0..5, p.expression) end + def test_comparison + p = new_parser("a > b") + expr = p.expression + assert(expr.is_a?(BinaryExpression)) + assert_equal('>', expr.operator) + assert(expr.left.is_a?(VariableLookup)) + assert_equal('a', expr.left.name) + assert(expr.right.is_a?(VariableLookup)) + assert_equal('b', expr.right.name) + + # BinaryExpression(>=) + # left: BinaryExpression(>) + # left: 10 + # right: 5 + # right: 4 + p = new_parser("10 > 5 >= 4") + expr = p.expression + assert(expr.is_a?(BinaryExpression)) + assert_equal('>=', expr.operator) + assert_equal(10, expr.left.left) + assert_equal(5, expr.left.right) + assert_equal(4, expr.right) + end + def test_number p = new_parser('-1 0 1 2.0') assert_equal(-1, p.number)