Update compilers to use LR.method() calls

Replace inline helper calls with LR runtime methods:
- __to_s__() -> LR.to_s()
- __to_number__() -> LR.to_number()
- __to_integer__() -> LR.to_integer()
- __truthy__() -> LR.truthy?()
- __output_value__() -> LR.output()
- __lookup__() -> LR.lookup()

Filter compiler now generates calls like:
- LR.escape_html(), LR.url_encode(), LR.base64_encode()
- LR.truncate(), LR.truncatewords(), LR.slice()
- LR.default(), LR.date()

This reduces generated code size significantly since helpers
are defined once in the pre-loaded runtime.
This commit is contained in:
Tobi Lutke
2025-12-31 12:20:26 -04:00
parent 154343e64f
commit 560d2ce9d4
7 changed files with 62 additions and 65 deletions
+1 -1
View File
@@ -66,7 +66,7 @@ module Liquid
# If no operator, just check truthiness # If no operator, just check truthiness
if op.nil? if op.nil?
left_expr = ExpressionCompiler.compile(left, compiler) left_expr = ExpressionCompiler.compile(left, compiler)
return "__truthy__(#{left_expr})" return "LR.truthy?(#{left_expr})"
end end
# Compile left and right expressions # Compile left and right expressions
+6 -6
View File
@@ -63,16 +63,16 @@ module Liquid
lookup.lookups.each_with_index do |key, index| lookup.lookups.each_with_index do |key, index|
if key.is_a?(VariableLookup) || key.is_a?(RangeLookup) if key.is_a?(VariableLookup) || key.is_a?(RangeLookup)
# Dynamic key like foo[expr] # Dynamic key like foo[expr]
base = "__lookup__.call(#{base}, #{compile(key, compiler)})" base = "LR.lookup(#{base}, #{compile(key, compiler)}, __context__)"
elsif key.is_a?(Integer) elsif key.is_a?(Integer)
# Numeric index like foo[0] # Numeric index like foo[0]
base = "__lookup__.call(#{base}, #{key})" base = "LR.lookup(#{base}, #{key}, __context__)"
elsif key.is_a?(String) elsif key.is_a?(String)
# Always use __lookup__ which tries key access first, # Always use LR.lookup which tries key access first,
# then falls back to method call for command methods (first, last, size) # then falls back to method call for command methods (first, last, size)
base = "__lookup__.call(#{base}, #{key.inspect})" base = "LR.lookup(#{base}, #{key.inspect}, __context__)"
else else
base = "__lookup__.call(#{base}, #{compile(key, compiler)})" base = "LR.lookup(#{base}, #{compile(key, compiler)}, __context__)"
end end
end end
@@ -88,7 +88,7 @@ module Liquid
end_expr = compile(range.end_obj, compiler) end_expr = compile(range.end_obj, compiler)
# Convert to integers and create range # Convert to integers and create range
"(__to_integer__(#{start_expr})...__to_integer__(#{end_expr})).to_a" "(LR.to_integer(#{start_expr})...LR.to_integer(#{end_expr})).to_a"
end end
# Compile a method literal (blank/empty) # Compile a method literal (blank/empty)
+48 -51
View File
@@ -10,129 +10,127 @@ module Liquid
# Standard filters that map directly to Ruby methods or simple expressions # Standard filters that map directly to Ruby methods or simple expressions
SIMPLE_FILTERS = { SIMPLE_FILTERS = {
'size' => ->(input, _args, _kwargs, _compiler) { "(#{input}.respond_to?(:size) ? #{input}.size : 0)" }, 'size' => ->(input, _args, _kwargs, _compiler) { "(#{input}.respond_to?(:size) ? #{input}.size : 0)" },
'downcase' => ->(input, _args, _kwargs, _compiler) { "__to_s__(#{input}).downcase" }, 'downcase' => ->(input, _args, _kwargs, _compiler) { "LR.to_s(#{input}).downcase" },
'upcase' => ->(input, _args, _kwargs, _compiler) { "__to_s__(#{input}).upcase" }, 'upcase' => ->(input, _args, _kwargs, _compiler) { "LR.to_s(#{input}).upcase" },
'capitalize' => ->(input, _args, _kwargs, _compiler) { "__to_s__(#{input}).capitalize" }, 'capitalize' => ->(input, _args, _kwargs, _compiler) { "LR.to_s(#{input}).capitalize" },
'strip' => ->(input, _args, _kwargs, _compiler) { "__to_s__(#{input}).strip" }, 'strip' => ->(input, _args, _kwargs, _compiler) { "LR.to_s(#{input}).strip" },
'lstrip' => ->(input, _args, _kwargs, _compiler) { "__to_s__(#{input}).lstrip" }, 'lstrip' => ->(input, _args, _kwargs, _compiler) { "LR.to_s(#{input}).lstrip" },
'rstrip' => ->(input, _args, _kwargs, _compiler) { "__to_s__(#{input}).rstrip" }, 'rstrip' => ->(input, _args, _kwargs, _compiler) { "LR.to_s(#{input}).rstrip" },
'reverse' => ->(input, _args, _kwargs, _compiler) { "(#{input}.is_a?(Array) ? #{input}.reverse : __to_s__(#{input}).reverse)" }, 'reverse' => ->(input, _args, _kwargs, _compiler) { "(#{input}.is_a?(Array) ? #{input}.reverse : LR.to_s(#{input}).reverse)" },
'first' => ->(input, _args, _kwargs, _compiler) { "(#{input}.respond_to?(:first) ? #{input}.first : nil)" }, 'first' => ->(input, _args, _kwargs, _compiler) { "(#{input}.respond_to?(:first) ? #{input}.first : nil)" },
'last' => ->(input, _args, _kwargs, _compiler) { "(#{input}.respond_to?(:last) ? #{input}.last : nil)" }, 'last' => ->(input, _args, _kwargs, _compiler) { "(#{input}.respond_to?(:last) ? #{input}.last : nil)" },
'uniq' => ->(input, _args, _kwargs, _compiler) { "(#{input}.respond_to?(:uniq) ? #{input}.uniq : #{input})" }, 'uniq' => ->(input, _args, _kwargs, _compiler) { "(#{input}.respond_to?(:uniq) ? #{input}.uniq : #{input})" },
'compact' => ->(input, _args, _kwargs, _compiler) { "(#{input}.respond_to?(:compact) ? #{input}.compact : #{input})" }, 'compact' => ->(input, _args, _kwargs, _compiler) { "(#{input}.respond_to?(:compact) ? #{input}.compact : #{input})" },
'flatten' => ->(input, _args, _kwargs, _compiler) { "(#{input}.respond_to?(:flatten) ? #{input}.flatten : #{input})" }, 'flatten' => ->(input, _args, _kwargs, _compiler) { "(#{input}.respond_to?(:flatten) ? #{input}.flatten : #{input})" },
'sort' => ->(input, _args, _kwargs, _compiler) { "(#{input}.respond_to?(:sort) ? #{input}.sort : #{input})" }, 'sort' => ->(input, _args, _kwargs, _compiler) { "(#{input}.respond_to?(:sort) ? #{input}.sort : #{input})" },
'abs' => ->(input, _args, _kwargs, _compiler) { "__to_number__(#{input}).abs" }, 'abs' => ->(input, _args, _kwargs, _compiler) { "LR.to_number(#{input}).abs" },
'ceil' => ->(input, _args, _kwargs, _compiler) { "__to_number__(#{input}).ceil.to_i" }, 'ceil' => ->(input, _args, _kwargs, _compiler) { "LR.to_number(#{input}).ceil.to_i" },
'floor' => ->(input, _args, _kwargs, _compiler) { "__to_number__(#{input}).floor.to_i" }, 'floor' => ->(input, _args, _kwargs, _compiler) { "LR.to_number(#{input}).floor.to_i" },
'escape' => ->(input, _args, _kwargs, _compiler) { "(#{input}.nil? ? nil : CGI.escapeHTML(__to_s__(#{input})))" }, 'escape' => ->(input, _args, _kwargs, _compiler) { "(#{input}.nil? ? nil : LR.escape_html(#{input}))" },
'h' => ->(input, _args, _kwargs, _compiler) { "(#{input}.nil? ? nil : CGI.escapeHTML(__to_s__(#{input})))" }, 'h' => ->(input, _args, _kwargs, _compiler) { "(#{input}.nil? ? nil : LR.escape_html(#{input}))" },
'url_encode' => ->(input, _args, _kwargs, _compiler) { "(#{input}.nil? ? nil : CGI.escape(__to_s__(#{input})))" }, 'url_encode' => ->(input, _args, _kwargs, _compiler) { "(#{input}.nil? ? nil : LR.url_encode(#{input}))" },
'url_decode' => ->(input, _args, _kwargs, _compiler) { "(#{input}.nil? ? nil : CGI.unescape(__to_s__(#{input})))" }, 'url_decode' => ->(input, _args, _kwargs, _compiler) { "(#{input}.nil? ? nil : LR.url_decode(#{input}))" },
'base64_encode' => ->(input, _args, _kwargs, _compiler) { "Base64.strict_encode64(__to_s__(#{input}))" }, 'base64_encode' => ->(input, _args, _kwargs, _compiler) { "LR.base64_encode(#{input})" },
'base64_decode' => ->(input, _args, _kwargs, _compiler) { "Base64.strict_decode64(__to_s__(#{input}))" }, 'base64_decode' => ->(input, _args, _kwargs, _compiler) { "LR.base64_decode(#{input})" },
'base64_url_safe_encode' => ->(input, _args, _kwargs, _compiler) { "Base64.urlsafe_encode64(__to_s__(#{input}))" }, 'base64_url_safe_encode' => ->(input, _args, _kwargs, _compiler) { "LR.base64_url_safe_encode(#{input})" },
'base64_url_safe_decode' => ->(input, _args, _kwargs, _compiler) { "Base64.urlsafe_decode64(__to_s__(#{input}))" }, 'base64_url_safe_decode' => ->(input, _args, _kwargs, _compiler) { "LR.base64_url_safe_decode(#{input})" },
'strip_html' => ->(input, _args, _kwargs, _compiler) { 'strip_html' => ->(input, _args, _kwargs, _compiler) { "LR.strip_html(#{input})" },
"__to_s__(#{input}).gsub(%r{<script.*?</script>|<!--.*?-->|<style.*?</style>}m, '').gsub(/<.*?>/m, '')" 'strip_newlines' => ->(input, _args, _kwargs, _compiler) { "LR.to_s(#{input}).gsub(/\\r?\\n/, '')" },
}, 'newline_to_br' => ->(input, _args, _kwargs, _compiler) { "LR.to_s(#{input}).gsub(/\\r?\\n/, \"<br />\\n\")" },
'strip_newlines' => ->(input, _args, _kwargs, _compiler) { "__to_s__(#{input}).gsub(/\\r?\\n/, '')" },
'newline_to_br' => ->(input, _args, _kwargs, _compiler) { "__to_s__(#{input}).gsub(/\\r?\\n/, \"<br />\\n\")" },
}.freeze }.freeze
# Filters with arguments that need special handling # Filters with arguments that need special handling
# All use LR.method() calls to pre-loaded runtime
PARAMETERIZED_FILTERS = { PARAMETERIZED_FILTERS = {
'append' => ->(input, args, _kwargs, compiler) { 'append' => ->(input, args, _kwargs, compiler) {
arg = compile_arg(args[0], compiler) arg = compile_arg(args[0], compiler)
"__to_s__(#{input}) + __to_s__(#{arg})" "LR.to_s(#{input}) + LR.to_s(#{arg})"
}, },
'prepend' => ->(input, args, _kwargs, compiler) { 'prepend' => ->(input, args, _kwargs, compiler) {
arg = compile_arg(args[0], compiler) arg = compile_arg(args[0], compiler)
"__to_s__(#{arg}) + __to_s__(#{input})" "LR.to_s(#{arg}) + LR.to_s(#{input})"
}, },
'plus' => ->(input, args, _kwargs, compiler) { 'plus' => ->(input, args, _kwargs, compiler) {
arg = compile_arg(args[0], compiler) arg = compile_arg(args[0], compiler)
"(__to_number__(#{input}) + __to_number__(#{arg}))" "(LR.to_number(#{input}) + LR.to_number(#{arg}))"
}, },
'minus' => ->(input, args, _kwargs, compiler) { 'minus' => ->(input, args, _kwargs, compiler) {
arg = compile_arg(args[0], compiler) arg = compile_arg(args[0], compiler)
"(__to_number__(#{input}) - __to_number__(#{arg}))" "(LR.to_number(#{input}) - LR.to_number(#{arg}))"
}, },
'times' => ->(input, args, _kwargs, compiler) { 'times' => ->(input, args, _kwargs, compiler) {
arg = compile_arg(args[0], compiler) arg = compile_arg(args[0], compiler)
"(__to_number__(#{input}) * __to_number__(#{arg}))" "(LR.to_number(#{input}) * LR.to_number(#{arg}))"
}, },
'divided_by' => ->(input, args, _kwargs, compiler) { 'divided_by' => ->(input, args, _kwargs, compiler) {
arg = compile_arg(args[0], compiler) arg = compile_arg(args[0], compiler)
"(__to_number__(#{input}) / __to_number__(#{arg}))" "(LR.to_number(#{input}) / LR.to_number(#{arg}))"
}, },
'modulo' => ->(input, args, _kwargs, compiler) { 'modulo' => ->(input, args, _kwargs, compiler) {
arg = compile_arg(args[0], compiler) arg = compile_arg(args[0], compiler)
"(__to_number__(#{input}) % __to_number__(#{arg}))" "(LR.to_number(#{input}) % LR.to_number(#{arg}))"
}, },
'round' => ->(input, args, _kwargs, compiler) { 'round' => ->(input, args, _kwargs, compiler) {
if args.empty? if args.empty?
"__to_number__(#{input}).round.to_i" "LR.to_number(#{input}).round.to_i"
else else
arg = compile_arg(args[0], compiler) arg = compile_arg(args[0], compiler)
"__to_number__(#{input}).round(__to_number__(#{arg}))" "LR.to_number(#{input}).round(LR.to_number(#{arg}))"
end end
}, },
'at_least' => ->(input, args, _kwargs, compiler) { 'at_least' => ->(input, args, _kwargs, compiler) {
arg = compile_arg(args[0], compiler) arg = compile_arg(args[0], compiler)
"[__to_number__(#{input}), __to_number__(#{arg})].max" "[LR.to_number(#{input}), LR.to_number(#{arg})].max"
}, },
'at_most' => ->(input, args, _kwargs, compiler) { 'at_most' => ->(input, args, _kwargs, compiler) {
arg = compile_arg(args[0], compiler) arg = compile_arg(args[0], compiler)
"[__to_number__(#{input}), __to_number__(#{arg})].min" "[LR.to_number(#{input}), LR.to_number(#{arg})].min"
}, },
'default' => ->(input, args, kwargs, compiler) { 'default' => ->(input, args, kwargs, compiler) {
default_val = args.empty? ? "''" : compile_arg(args[0], compiler) default_val = args.empty? ? "''" : compile_arg(args[0], compiler)
allow_false = kwargs && kwargs['allow_false'] ? compile_arg(kwargs['allow_false'], compiler) : 'false' allow_false = kwargs && kwargs['allow_false'] ? "allow_false: #{compile_arg(kwargs['allow_false'], compiler)}" : ''
"(if #{allow_false} then (#{input}.nil? || (#{input}.respond_to?(:empty?) && #{input}.empty?)) else (!__truthy__(#{input}) || (#{input}.respond_to?(:empty?) && #{input}.empty?)) end) ? #{default_val} : #{input}" "LR.default(#{input}, #{default_val}#{allow_false.empty? ? '' : ', ' + allow_false})"
}, },
'split' => ->(input, args, _kwargs, compiler) { 'split' => ->(input, args, _kwargs, compiler) {
pattern = args.empty? ? "' '" : compile_arg(args[0], compiler) pattern = args.empty? ? "' '" : compile_arg(args[0], compiler)
"__to_s__(#{input}).split(__to_s__(#{pattern}))" "LR.to_s(#{input}).split(LR.to_s(#{pattern}))"
}, },
'join' => ->(input, args, _kwargs, compiler) { 'join' => ->(input, args, _kwargs, compiler) {
glue = args.empty? ? "' '" : compile_arg(args[0], compiler) glue = args.empty? ? "' '" : compile_arg(args[0], compiler)
"(#{input}.is_a?(Array) ? #{input}.map { |i| __to_s__(i) }.join(__to_s__(#{glue})) : __to_s__(#{input}))" "(#{input}.is_a?(Array) ? #{input}.map { |i| LR.to_s(i) }.join(LR.to_s(#{glue})) : LR.to_s(#{input}))"
}, },
'replace' => ->(input, args, _kwargs, compiler) { 'replace' => ->(input, args, _kwargs, compiler) {
string = compile_arg(args[0], compiler) string = compile_arg(args[0], compiler)
replacement = args.length > 1 ? compile_arg(args[1], compiler) : "''" replacement = args.length > 1 ? compile_arg(args[1], compiler) : "''"
"__to_s__(#{input}).gsub(__to_s__(#{string}), __to_s__(#{replacement}))" "LR.to_s(#{input}).gsub(LR.to_s(#{string}), LR.to_s(#{replacement}))"
}, },
'replace_first' => ->(input, args, _kwargs, compiler) { 'replace_first' => ->(input, args, _kwargs, compiler) {
string = compile_arg(args[0], compiler) string = compile_arg(args[0], compiler)
replacement = args.length > 1 ? compile_arg(args[1], compiler) : "''" replacement = args.length > 1 ? compile_arg(args[1], compiler) : "''"
"__to_s__(#{input}).sub(__to_s__(#{string}), __to_s__(#{replacement}))" "LR.to_s(#{input}).sub(LR.to_s(#{string}), LR.to_s(#{replacement}))"
}, },
'remove' => ->(input, args, _kwargs, compiler) { 'remove' => ->(input, args, _kwargs, compiler) {
string = compile_arg(args[0], compiler) string = compile_arg(args[0], compiler)
"__to_s__(#{input}).gsub(__to_s__(#{string}), '')" "LR.to_s(#{input}).gsub(LR.to_s(#{string}), '')"
}, },
'remove_first' => ->(input, args, _kwargs, compiler) { 'remove_first' => ->(input, args, _kwargs, compiler) {
string = compile_arg(args[0], compiler) string = compile_arg(args[0], compiler)
"__to_s__(#{input}).sub(__to_s__(#{string}), '')" "LR.to_s(#{input}).sub(LR.to_s(#{string}), '')"
}, },
'truncate' => ->(input, args, _kwargs, compiler) { 'truncate' => ->(input, args, _kwargs, compiler) {
length = args.empty? ? "50" : compile_arg(args[0], compiler) length = args.empty? ? "50" : compile_arg(args[0], compiler)
ellipsis = args.length > 1 ? compile_arg(args[1], compiler) : "'...'" ellipsis = args.length > 1 ? compile_arg(args[1], compiler) : "'...'"
var = compiler.generate_var_name("trunc") "LR.truncate(#{input}, #{length}, #{ellipsis})"
"(lambda { |#{var}_input, #{var}_len, #{var}_ell| #{var}_str = __to_s__(#{var}_input); #{var}_ell_str = __to_s__(#{var}_ell); #{var}_l = [#{var}_len.to_i - #{var}_ell_str.length, 0].max; #{var}_str.length > #{var}_len.to_i ? #{var}_str[0, #{var}_l] + #{var}_ell_str : #{var}_str }).call(#{input}, #{length}, #{ellipsis})"
}, },
'truncatewords' => ->(input, args, _kwargs, compiler) { 'truncatewords' => ->(input, args, _kwargs, compiler) {
words = args.empty? ? "15" : compile_arg(args[0], compiler) words = args.empty? ? "15" : compile_arg(args[0], compiler)
ellipsis = args.length > 1 ? compile_arg(args[1], compiler) : "'...'" ellipsis = args.length > 1 ? compile_arg(args[1], compiler) : "'...'"
"(lambda { |input, num_words, ell| words = __to_s__(input).split(' ', [num_words.to_i, 1].max + 1); words.length > [num_words.to_i, 1].max ? words[0, [num_words.to_i, 1].max].join(' ') + __to_s__(ell) : input.to_s }).call(#{input}, #{words}, #{ellipsis})" "LR.truncatewords(#{input}, #{words}, #{ellipsis})"
}, },
'slice' => ->(input, args, _kwargs, compiler) { 'slice' => ->(input, args, _kwargs, compiler) {
offset = compile_arg(args[0], compiler) offset = compile_arg(args[0], compiler)
length = args.length > 1 ? compile_arg(args[1], compiler) : "1" length = args.length > 1 ? compile_arg(args[1], compiler) : "1"
"(#{input}.is_a?(Array) ? (#{input}.slice(__to_integer__(#{offset}), __to_integer__(#{length})) || []) : (__to_s__(#{input}).slice(__to_integer__(#{offset}), __to_integer__(#{length})) || ''))" "LR.slice(#{input}, #{offset}, #{length})"
}, },
'map' => ->(input, args, _kwargs, compiler) { 'map' => ->(input, args, _kwargs, compiler) {
property = compile_arg(args[0], compiler) property = compile_arg(args[0], compiler)
@@ -144,7 +142,7 @@ module Liquid
target = compile_arg(args[1], compiler) target = compile_arg(args[1], compiler)
"(#{input}.is_a?(Array) ? #{input}.select { |item| item.respond_to?(:[]) && item[#{property}] == #{target} } : [])" "(#{input}.is_a?(Array) ? #{input}.select { |item| item.respond_to?(:[]) && item[#{property}] == #{target} } : [])"
else else
"(#{input}.is_a?(Array) ? #{input}.select { |item| item.respond_to?(:[]) && __truthy__(item[#{property}]) } : [])" "(#{input}.is_a?(Array) ? #{input}.select { |item| item.respond_to?(:[]) && LR.truthy?(item[#{property}]) } : [])"
end end
}, },
'reject' => ->(input, args, _kwargs, compiler) { 'reject' => ->(input, args, _kwargs, compiler) {
@@ -153,7 +151,7 @@ module Liquid
target = compile_arg(args[1], compiler) target = compile_arg(args[1], compiler)
"(#{input}.is_a?(Array) ? #{input}.reject { |item| item.respond_to?(:[]) && item[#{property}] == #{target} } : [])" "(#{input}.is_a?(Array) ? #{input}.reject { |item| item.respond_to?(:[]) && item[#{property}] == #{target} } : [])"
else else
"(#{input}.is_a?(Array) ? #{input}.reject { |item| item.respond_to?(:[]) && __truthy__(item[#{property}]) } : [])" "(#{input}.is_a?(Array) ? #{input}.reject { |item| item.respond_to?(:[]) && LR.truthy?(item[#{property}]) } : [])"
end end
}, },
'concat' => ->(input, args, _kwargs, compiler) { 'concat' => ->(input, args, _kwargs, compiler) {
@@ -170,11 +168,10 @@ module Liquid
}, },
'date' => ->(input, args, _kwargs, compiler) { 'date' => ->(input, args, _kwargs, compiler) {
format = compile_arg(args[0], compiler) format = compile_arg(args[0], compiler)
# This is a simplified version - full date parsing is complex "LR.date(#{input}, #{format})"
"(lambda { |input, fmt| return input if fmt.to_s.empty?; d = case input; when Time, Date, DateTime then input; when 'now', 'today' then Time.now; when /\\A\\d+\\z/, Integer then Time.at(input.to_i); when String then (Time.parse(input) rescue input); else input; end; d.respond_to?(:strftime) ? d.strftime(fmt.to_s) : input }.call(#{input}, #{format}))"
}, },
'escape_once' => ->(input, _args, _kwargs, _compiler) { 'escape_once' => ->(input, _args, _kwargs, _compiler) {
"__to_s__(#{input}).gsub(/[\"><']|&(?!([a-zA-Z]+|(#\\d+));)/) { |c| {'&'=>'&amp;', '>'=>'&gt;', '<'=>'&lt;', '\"'=>'&quot;', \"'\"=>'&#39;'}[c] || c }" "LR.escape_once(#{input})"
}, },
}.freeze }.freeze
+1 -1
View File
@@ -32,7 +32,7 @@ module Liquid
code.line "case #{cycle_var} % #{variables.size}" code.line "case #{cycle_var} % #{variables.size}"
variables.each_with_index do |var, idx| variables.each_with_index do |var, idx|
var_expr = ExpressionCompiler.compile(var, compiler) var_expr = ExpressionCompiler.compile(var, compiler)
code.line "when #{idx} then __output__ << __to_s__(#{var_expr})" code.line "when #{idx} then __output__ << LR.to_s(#{var_expr})"
end end
code.line "end" code.line "end"
+2 -2
View File
@@ -69,9 +69,9 @@ module Liquid
if tag.limit if tag.limit
limit_expr = ExpressionCompiler.compile(tag.limit, compiler) limit_expr = ExpressionCompiler.compile(tag.limit, compiler)
code.line "#{coll_var} = (#{coll_var}.respond_to?(:slice) ? #{coll_var}.slice(__to_integer__(#{from_expr}), __to_integer__(#{limit_expr})) : #{coll_var}) || []" code.line "#{coll_var} = (#{coll_var}.respond_to?(:slice) ? #{coll_var}.slice(LR.to_integer(#{from_expr}), LR.to_integer(#{limit_expr})) : #{coll_var}) || []"
else else
code.line "#{coll_var} = (#{coll_var}.respond_to?(:drop) ? #{coll_var}.drop(__to_integer__(#{from_expr})) : #{coll_var}) || []" code.line "#{coll_var} = (#{coll_var}.respond_to?(:drop) ? #{coll_var}.drop(LR.to_integer(#{from_expr})) : #{coll_var}) || []"
end end
end end
+3 -3
View File
@@ -36,13 +36,13 @@ module Liquid
offset_expr = ExpressionCompiler.compile(offset, compiler) offset_expr = ExpressionCompiler.compile(offset, compiler)
if limit if limit
limit_expr = ExpressionCompiler.compile(limit, compiler) limit_expr = ExpressionCompiler.compile(limit, compiler)
code.line "#{coll_var} = #{coll_var}.slice(__to_integer__(#{offset_expr}), __to_integer__(#{limit_expr})) || []" code.line "#{coll_var} = #{coll_var}.slice(LR.to_integer(#{offset_expr}), LR.to_integer(#{limit_expr})) || []"
else else
code.line "#{coll_var} = #{coll_var}.drop(__to_integer__(#{offset_expr}))" code.line "#{coll_var} = #{coll_var}.drop(LR.to_integer(#{offset_expr}))"
end end
elsif limit elsif limit
limit_expr = ExpressionCompiler.compile(limit, compiler) limit_expr = ExpressionCompiler.compile(limit, compiler)
code.line "#{coll_var} = #{coll_var}.first(__to_integer__(#{limit_expr}))" code.line "#{coll_var} = #{coll_var}.first(LR.to_integer(#{limit_expr}))"
end end
end end
+1 -1
View File
@@ -14,7 +14,7 @@ module Liquid
# @param code [CodeGenerator] The code generator # @param code [CodeGenerator] The code generator
def self.compile(variable, compiler, code) def self.compile(variable, compiler, code)
value_expr = compile_to_expression(variable, compiler) value_expr = compile_to_expression(variable, compiler)
code.line "__output__ << __output_value__(#{value_expr})" code.line "__output__ << LR.output(#{value_expr})"
end end
# Compile a Variable node to a Ruby expression (without output) # Compile a Variable node to a Ruby expression (without output)