Files
liquid/lib/liquid/range_lookup.rb
T
Dylan Thacker-Smith e20df18eca [WIP] Add support for migrating liquid code away from several lax parse quirks
It transforms liquid code to equivalently parsed code, that doesn't depend
on lax parsing quirks.

Note that this transformation can hide the intent of the original code, so
it is best to review the changes, since removed syntax often would indicate
a bug in the original liquid code.
2022-11-16 17:35:29 -05:00

89 lines
2.3 KiB
Ruby

# frozen_string_literal: true
module Liquid
class RangeLookup
def self.parse(start_markup, end_markup)
start_obj = Expression.parse(start_markup)
end_obj = Expression.parse(end_markup)
if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
new(start_obj, end_obj)
else
begin
start_obj.to_i..end_obj.to_i
rescue NoMethodError
invalid_expr = start_markup unless start_obj.respond_to?(:to_i)
invalid_expr ||= end_markup unless end_obj.respond_to?(:to_i)
if invalid_expr
raise Liquid::SyntaxError, "Invalid expression type '#{invalid_expr}' in range expression"
end
raise
end
end
end
def self.lax_migrate(start_markup, end_markup)
new_start = Expression.lax_migrate(start_markup)
new_end = Expression.lax_migrate(end_markup)
# cast literals
start_obj = Expression.parse(new_start)
end_obj = Expression.parse(new_end)
if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
new_start = lax_migrate_range_expression(new_start, start_obj)
new_end = lax_migrate_range_expression(new_end, end_obj)
else
new_start = start_obj.to_i.to_s unless start_obj.is_a?(Integer)
new_end = end_obj.to_i.to_s unless end_obj.is_a?(Integer)
end
[new_start, new_end]
end
def self.lax_migrate_range_expression(markup, expression)
return markup if expression.respond_to?(:evaluate)
case expression
when Integer
markup
when NilClass, String
expression.to_i.to_s
else
Utils.to_integer(input).to_s
end
end
attr_reader :start_obj, :end_obj
def initialize(start_obj, end_obj)
@start_obj = start_obj
@end_obj = end_obj
end
def evaluate(context)
start_int = to_integer(context.evaluate(@start_obj))
end_int = to_integer(context.evaluate(@end_obj))
start_int..end_int
end
private
def to_integer(input)
case input
when Integer
input
when NilClass, String
input.to_i
else
Utils.to_integer(input)
end
end
class ParseTreeVisitor < Liquid::ParseTreeVisitor
def children
[@node.start_obj, @node.end_obj]
end
end
end
end