liquid 5.6.0 → 5.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +44 -2
- data/README.md +1 -1
- data/lib/liquid/context.rb +5 -1
- data/lib/liquid/expression.rb +97 -21
- data/lib/liquid/lexer.rb +63 -127
- data/lib/liquid/parse_context.rb +25 -3
- data/lib/liquid/parser.rb +2 -2
- data/lib/liquid/range_lookup.rb +3 -3
- data/lib/liquid/standardfilters.rb +167 -61
- data/lib/liquid/tags/cycle.rb +7 -1
- data/lib/liquid/tags/for.rb +1 -1
- data/lib/liquid/tags/if.rb +1 -1
- data/lib/liquid/tokenizer.rb +123 -13
- data/lib/liquid/utils.rb +96 -0
- data/lib/liquid/variable.rb +3 -3
- data/lib/liquid/variable_lookup.rb +13 -5
- data/lib/liquid/version.rb +1 -1
- data/lib/liquid.rb +4 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 72a0f18697a90c81db846fc86027cbec36198105fb458d40989f88b1acd55687
|
4
|
+
data.tar.gz: ba8f6ecc9612f737f954006109b91759ea7cea41074de2ab38e73221d4e18be2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad608314f023c78123d3cf57a3c9b54229ec7dca712f1b3897218c27880e5f78d7188cdce775d15759960803a31c5e7d1adc81c9282ab784a6f6fa50efeb051e
|
7
|
+
data.tar.gz: 6988a403789ca9a3a3d3d48e5cba0639f81226b3cd8af477dcce1d8eeb9db3693e90b373ea391f5d458a723ec40d7c0c6d28f7e445d3d0562b219e9cbcfff1ee
|
data/History.md
CHANGED
@@ -1,11 +1,53 @@
|
|
1
1
|
# Liquid Change Log
|
2
2
|
|
3
|
-
## 5.
|
3
|
+
## 5.8.0 (unreleased)
|
4
|
+
|
5
|
+
## 5.7.0 2025-01-16
|
6
|
+
|
7
|
+
### Features
|
8
|
+
* Add `find`, `find_index`, `has`, and `reject` filters to arrays
|
9
|
+
* Compatibility with Ruby 3.4
|
10
|
+
|
11
|
+
## 5.6.4 2025-01-14
|
4
12
|
|
5
13
|
### Fixes
|
14
|
+
* Add a default `string_scanner` to avoid errors with `Liquid::VariableLookup.parse("foo.bar")` [Ian Ker-Seymer]
|
6
15
|
|
7
|
-
|
16
|
+
## 5.6.3 2025-01-13
|
17
|
+
* Remove `lru_redux` dependency [Michael Go]
|
18
|
+
|
19
|
+
## 5.6.2 2025-01-13
|
20
|
+
|
21
|
+
### Fixes
|
22
|
+
* Preserve the old behavior of requiring floats to start with a digit [Michael Go]
|
8
23
|
|
24
|
+
## 5.6.1 2025-01-13
|
25
|
+
|
26
|
+
### Performance improvements
|
27
|
+
* Faster Expression parser / Tokenizer with StringScanner [Michael Go]
|
28
|
+
|
29
|
+
## 5.6.0 2024-12-19
|
30
|
+
|
31
|
+
### Architectural changes
|
32
|
+
* Added new `Environment` class to manage configuration and state that was previously stored in `Template` [Ian Ker-Seymer]
|
33
|
+
* Moved tag registration from `Template` to `Environment` [Ian Ker-Seymer]
|
34
|
+
* Removed `StrainerFactory` in favor of `Environment`-based strainer creation [Ian Ker-Seymer]
|
35
|
+
* Consolidated standard tags into a new `Tags` module with `STANDARD_TAGS` constant [Ian Ker-Seymer]
|
36
|
+
|
37
|
+
### Performance improvements
|
38
|
+
* Optimized `Lexer` with a new `Lexer2` implementation using jump tables for faster tokenization, requires Ruby 3.4 [Ian Ker-Seymer]
|
39
|
+
* Improved variable rendering with specialized handling for different types [Michael Go]
|
40
|
+
* Reduced array allocations by using frozen empty constants [Michael Go]
|
41
|
+
|
42
|
+
### API changes
|
43
|
+
* Deprecated several `Template` class methods in favor of `Environment` methods [Ian Ker-Seymer]
|
44
|
+
* Added deprecation warnings system [Ian Ker-Seymer]
|
45
|
+
* Changed how filters and tags are registered to use Environment [Ian Ker-Seymer]
|
46
|
+
|
47
|
+
### Fixes
|
48
|
+
* Fixed table row handling of break interrupts [Alex Coco]
|
49
|
+
* Improved variable output handling for arrays [Ian Ker-Seymer]
|
50
|
+
* Fix Tokenizer to handle null source value (#1873) [Bahar Pourazar]
|
9
51
|
|
10
52
|
## 5.5.0 2024-03-21
|
11
53
|
|
data/README.md
CHANGED
@@ -91,7 +91,7 @@ Liquid::Template.parse(<<~LIQUID, environment: email_environment)
|
|
91
91
|
LIQUID
|
92
92
|
```
|
93
93
|
|
94
|
-
By using Environments, you ensure that custom tags and filters are only available in the contexts where they are needed, making your Liquid templates more robust and easier to manage.
|
94
|
+
By using Environments, you ensure that custom tags and filters are only available in the contexts where they are needed, making your Liquid templates more robust and easier to manage. For smaller projects, a global environment is available via `Liquid::Environment.default`.
|
95
95
|
|
96
96
|
### Error Modes
|
97
97
|
|
data/lib/liquid/context.rb
CHANGED
@@ -40,6 +40,10 @@ module Liquid
|
|
40
40
|
@global_filter = nil
|
41
41
|
@disabled_tags = {}
|
42
42
|
|
43
|
+
# Instead of constructing new StringScanner objects for each Expression parse,
|
44
|
+
# we recycle the same one.
|
45
|
+
@string_scanner = StringScanner.new("")
|
46
|
+
|
43
47
|
@registers.static[:cached_partials] ||= {}
|
44
48
|
@registers.static[:file_system] ||= environment.file_system
|
45
49
|
@registers.static[:template_factory] ||= Liquid::TemplateFactory.new
|
@@ -176,7 +180,7 @@ module Liquid
|
|
176
180
|
# Example:
|
177
181
|
# products == empty #=> products.empty?
|
178
182
|
def [](expression)
|
179
|
-
evaluate(Expression.parse(expression))
|
183
|
+
evaluate(Expression.parse(expression, @string_scanner))
|
180
184
|
end
|
181
185
|
|
182
186
|
def key?(key)
|
data/lib/liquid/expression.rb
CHANGED
@@ -10,37 +10,113 @@ module Liquid
|
|
10
10
|
'true' => true,
|
11
11
|
'false' => false,
|
12
12
|
'blank' => '',
|
13
|
-
'empty' => ''
|
13
|
+
'empty' => '',
|
14
|
+
# in lax mode, minus sign can be a VariableLookup
|
15
|
+
# For simplicity and performace, we treat it like a literal
|
16
|
+
'-' => VariableLookup.parse("-", nil).freeze,
|
14
17
|
}.freeze
|
15
18
|
|
16
|
-
|
17
|
-
|
19
|
+
DOT = ".".ord
|
20
|
+
ZERO = "0".ord
|
21
|
+
NINE = "9".ord
|
22
|
+
DASH = "-".ord
|
18
23
|
|
19
24
|
# Use an atomic group (?>...) to avoid pathological backtracing from
|
20
25
|
# malicious input as described in https://github.com/Shopify/liquid/issues/1357
|
21
|
-
RANGES_REGEX
|
26
|
+
RANGES_REGEX = /\A\(\s*(?>(\S+)\s*\.\.)\s*(\S+)\s*\)\z/
|
27
|
+
INTEGER_REGEX = /\A(-?\d+)\z/
|
28
|
+
FLOAT_REGEX = /\A(-?\d+)\.\d+\z/
|
22
29
|
|
23
|
-
|
24
|
-
|
30
|
+
class << self
|
31
|
+
def parse(markup, ss = StringScanner.new(""), cache = nil)
|
32
|
+
return unless markup
|
25
33
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
34
|
+
markup = markup.strip # markup can be a frozen string
|
35
|
+
|
36
|
+
if (markup.start_with?('"') && markup.end_with?('"')) ||
|
37
|
+
(markup.start_with?("'") && markup.end_with?("'"))
|
38
|
+
return markup[1..-2]
|
39
|
+
elsif LITERALS.key?(markup)
|
40
|
+
return LITERALS[markup]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Cache only exists during parsing
|
44
|
+
if cache
|
45
|
+
return cache[markup] if cache.key?(markup)
|
46
|
+
|
47
|
+
cache[markup] = inner_parse(markup, ss, cache).freeze
|
48
|
+
else
|
49
|
+
inner_parse(markup, ss, nil).freeze
|
50
|
+
end
|
30
51
|
end
|
31
52
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
53
|
+
def inner_parse(markup, ss, cache)
|
54
|
+
if (markup.start_with?("(") && markup.end_with?(")")) && markup =~ RANGES_REGEX
|
55
|
+
return RangeLookup.parse(
|
56
|
+
Regexp.last_match(1),
|
57
|
+
Regexp.last_match(2),
|
58
|
+
ss,
|
59
|
+
cache,
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
if (num = parse_number(markup, ss))
|
64
|
+
num
|
65
|
+
else
|
66
|
+
VariableLookup.parse(markup, ss, cache)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_number(markup, ss)
|
71
|
+
# check if the markup is simple integer or float
|
72
|
+
case markup
|
73
|
+
when INTEGER_REGEX
|
74
|
+
return Integer(markup, 10)
|
75
|
+
when FLOAT_REGEX
|
76
|
+
return markup.to_f
|
77
|
+
end
|
78
|
+
|
79
|
+
ss.string = markup
|
80
|
+
# the first byte must be a digit or a dash
|
81
|
+
byte = ss.scan_byte
|
82
|
+
|
83
|
+
return false if byte != DASH && (byte < ZERO || byte > NINE)
|
84
|
+
|
85
|
+
if byte == DASH
|
86
|
+
peek_byte = ss.peek_byte
|
87
|
+
|
88
|
+
# if it starts with a dash, the next byte must be a digit
|
89
|
+
return false if peek_byte.nil? || !(peek_byte >= ZERO && peek_byte <= NINE)
|
90
|
+
end
|
91
|
+
|
92
|
+
# The markup could be a float with multiple dots
|
93
|
+
first_dot_pos = nil
|
94
|
+
num_end_pos = nil
|
95
|
+
|
96
|
+
while (byte = ss.scan_byte)
|
97
|
+
return false if byte != DOT && (byte < ZERO || byte > NINE)
|
98
|
+
|
99
|
+
# we found our number and now we are just scanning the rest of the string
|
100
|
+
next if num_end_pos
|
101
|
+
|
102
|
+
if byte == DOT
|
103
|
+
if first_dot_pos.nil?
|
104
|
+
first_dot_pos = ss.pos
|
105
|
+
else
|
106
|
+
# we found another dot, so we know that the number ends here
|
107
|
+
num_end_pos = ss.pos - 1
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
num_end_pos = markup.length if ss.eos?
|
113
|
+
|
114
|
+
if num_end_pos
|
115
|
+
# number ends with a number "123.123"
|
116
|
+
markup.byteslice(0, num_end_pos).to_f
|
42
117
|
else
|
43
|
-
|
118
|
+
# number ends with a dot "123."
|
119
|
+
markup.byteslice(0, first_dot_pos).to_f
|
44
120
|
end
|
45
121
|
end
|
46
122
|
end
|
data/lib/liquid/lexer.rb
CHANGED
@@ -1,66 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "strscan"
|
4
|
-
|
5
3
|
module Liquid
|
6
|
-
class
|
7
|
-
SPECIALS = {
|
8
|
-
'|' => :pipe,
|
9
|
-
'.' => :dot,
|
10
|
-
':' => :colon,
|
11
|
-
',' => :comma,
|
12
|
-
'[' => :open_square,
|
13
|
-
']' => :close_square,
|
14
|
-
'(' => :open_round,
|
15
|
-
')' => :close_round,
|
16
|
-
'?' => :question,
|
17
|
-
'-' => :dash,
|
18
|
-
}.freeze
|
19
|
-
IDENTIFIER = /[a-zA-Z_][\w-]*\??/
|
20
|
-
SINGLE_STRING_LITERAL = /'[^\']*'/
|
21
|
-
DOUBLE_STRING_LITERAL = /"[^\"]*"/
|
22
|
-
STRING_LITERAL = Regexp.union(SINGLE_STRING_LITERAL, DOUBLE_STRING_LITERAL)
|
23
|
-
NUMBER_LITERAL = /-?\d+(\.\d+)?/
|
24
|
-
DOTDOT = /\.\./
|
25
|
-
COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains(?=\s)/
|
26
|
-
WHITESPACE_OR_NOTHING = /\s*/
|
27
|
-
|
28
|
-
def initialize(input)
|
29
|
-
@ss = StringScanner.new(input)
|
30
|
-
end
|
31
|
-
|
32
|
-
def tokenize
|
33
|
-
@output = []
|
34
|
-
|
35
|
-
until @ss.eos?
|
36
|
-
@ss.skip(WHITESPACE_OR_NOTHING)
|
37
|
-
break if @ss.eos?
|
38
|
-
tok = if (t = @ss.scan(COMPARISON_OPERATOR))
|
39
|
-
[:comparison, t]
|
40
|
-
elsif (t = @ss.scan(STRING_LITERAL))
|
41
|
-
[:string, t]
|
42
|
-
elsif (t = @ss.scan(NUMBER_LITERAL))
|
43
|
-
[:number, t]
|
44
|
-
elsif (t = @ss.scan(IDENTIFIER))
|
45
|
-
[:id, t]
|
46
|
-
elsif (t = @ss.scan(DOTDOT))
|
47
|
-
[:dotdot, t]
|
48
|
-
else
|
49
|
-
c = @ss.getch
|
50
|
-
if (s = SPECIALS[c])
|
51
|
-
[s, c]
|
52
|
-
else
|
53
|
-
raise SyntaxError, "Unexpected character #{c}"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
@output << tok
|
57
|
-
end
|
58
|
-
|
59
|
-
@output << [:end_of_string]
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
class Lexer2
|
4
|
+
class Lexer
|
64
5
|
CLOSE_ROUND = [:close_round, ")"].freeze
|
65
6
|
CLOSE_SQUARE = [:close_square, "]"].freeze
|
66
7
|
COLON = [:colon, ":"].freeze
|
@@ -92,6 +33,7 @@ module Liquid
|
|
92
33
|
SINGLE_COMPARISON_TOKENS = [].tap do |table|
|
93
34
|
table["<".ord] = COMPARISON_LESS_THAN
|
94
35
|
table[">".ord] = COMPARISON_GREATER_THAN
|
36
|
+
table.freeze
|
95
37
|
end
|
96
38
|
|
97
39
|
TWO_CHARS_COMPARISON_JUMP_TABLE = [].tap do |table|
|
@@ -103,18 +45,17 @@ module Liquid
|
|
103
45
|
sub_table["=".ord] = COMPARISION_NOT_EQUAL
|
104
46
|
sub_table.freeze
|
105
47
|
end
|
48
|
+
table.freeze
|
106
49
|
end
|
107
50
|
|
108
51
|
COMPARISON_JUMP_TABLE = [].tap do |table|
|
109
52
|
table["<".ord] = [].tap do |sub_table|
|
110
53
|
sub_table["=".ord] = COMPARISON_LESS_THAN_OR_EQUAL
|
111
54
|
sub_table[">".ord] = COMPARISON_NOT_EQUAL_ALT
|
112
|
-
RUBY_WHITESPACE.each { |c| sub_table[c.ord] = COMPARISON_LESS_THAN }
|
113
55
|
sub_table.freeze
|
114
56
|
end
|
115
57
|
table[">".ord] = [].tap do |sub_table|
|
116
58
|
sub_table["=".ord] = COMPARISON_GREATER_THAN_OR_EQUAL
|
117
|
-
RUBY_WHITESPACE.each { |c| sub_table[c.ord] = COMPARISON_GREATER_THAN }
|
118
59
|
sub_table.freeze
|
119
60
|
end
|
120
61
|
table.freeze
|
@@ -157,81 +98,76 @@ module Liquid
|
|
157
98
|
table.freeze
|
158
99
|
end
|
159
100
|
|
160
|
-
def initialize(input)
|
161
|
-
@ss = StringScanner.new(input)
|
162
|
-
end
|
163
|
-
|
164
101
|
# rubocop:disable Metrics/BlockNesting
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
102
|
+
class << self
|
103
|
+
def tokenize(ss)
|
104
|
+
output = []
|
105
|
+
|
106
|
+
until ss.eos?
|
107
|
+
ss.skip(WHITESPACE_OR_NOTHING)
|
108
|
+
|
109
|
+
break if ss.eos?
|
110
|
+
|
111
|
+
start_pos = ss.pos
|
112
|
+
peeked = ss.peek_byte
|
113
|
+
|
114
|
+
if (special = SPECIAL_TABLE[peeked])
|
115
|
+
ss.scan_byte
|
116
|
+
# Special case for ".."
|
117
|
+
if special == DOT && ss.peek_byte == DOT_ORD
|
118
|
+
ss.scan_byte
|
119
|
+
output << DOTDOT
|
120
|
+
elsif special == DASH
|
121
|
+
# Special case for negative numbers
|
122
|
+
if (peeked_byte = ss.peek_byte) && NUMBER_TABLE[peeked_byte]
|
123
|
+
ss.pos -= 1
|
124
|
+
output << [:number, ss.scan(NUMBER_LITERAL)]
|
125
|
+
else
|
126
|
+
output << special
|
127
|
+
end
|
187
128
|
else
|
188
|
-
|
129
|
+
output << special
|
189
130
|
end
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
if (peeked_byte = @ss.peek_byte) && (found = sub_table[peeked_byte])
|
204
|
-
@output << found
|
205
|
-
@ss.scan_byte
|
206
|
-
else
|
207
|
-
@output << SINGLE_COMPARISON_TOKENS[peeked]
|
208
|
-
end
|
209
|
-
else
|
210
|
-
type, pattern = NEXT_MATCHER_JUMP_TABLE[peeked]
|
211
|
-
|
212
|
-
if type && (t = @ss.scan(pattern))
|
213
|
-
# Special case for "contains"
|
214
|
-
@output << if type == :id && t == "contains" && @output.last&.first != :dot
|
215
|
-
COMPARISON_CONTAINS
|
131
|
+
elsif (sub_table = TWO_CHARS_COMPARISON_JUMP_TABLE[peeked])
|
132
|
+
ss.scan_byte
|
133
|
+
if (peeked_byte = ss.peek_byte) && (found = sub_table[peeked_byte])
|
134
|
+
output << found
|
135
|
+
ss.scan_byte
|
136
|
+
else
|
137
|
+
raise_syntax_error(start_pos, ss)
|
138
|
+
end
|
139
|
+
elsif (sub_table = COMPARISON_JUMP_TABLE[peeked])
|
140
|
+
ss.scan_byte
|
141
|
+
if (peeked_byte = ss.peek_byte) && (found = sub_table[peeked_byte])
|
142
|
+
output << found
|
143
|
+
ss.scan_byte
|
216
144
|
else
|
217
|
-
[
|
145
|
+
output << SINGLE_COMPARISON_TOKENS[peeked]
|
218
146
|
end
|
219
147
|
else
|
220
|
-
|
148
|
+
type, pattern = NEXT_MATCHER_JUMP_TABLE[peeked]
|
149
|
+
|
150
|
+
if type && (t = ss.scan(pattern))
|
151
|
+
# Special case for "contains"
|
152
|
+
output << if type == :id && t == "contains" && output.last&.first != :dot
|
153
|
+
COMPARISON_CONTAINS
|
154
|
+
else
|
155
|
+
[type, t]
|
156
|
+
end
|
157
|
+
else
|
158
|
+
raise_syntax_error(start_pos, ss)
|
159
|
+
end
|
221
160
|
end
|
222
161
|
end
|
162
|
+
# rubocop:enable Metrics/BlockNesting
|
163
|
+
output << EOS
|
223
164
|
end
|
224
|
-
# rubocop:enable Metrics/BlockNesting
|
225
165
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
# the character could be a UTF-8 character, use getch to get all the bytes
|
232
|
-
raise SyntaxError, "Unexpected character #{@ss.getch}"
|
166
|
+
def raise_syntax_error(start_pos, ss)
|
167
|
+
ss.pos = start_pos
|
168
|
+
# the character could be a UTF-8 character, use getch to get all the bytes
|
169
|
+
raise SyntaxError, "Unexpected character #{ss.getch}"
|
170
|
+
end
|
233
171
|
end
|
234
172
|
end
|
235
|
-
|
236
|
-
Lexer = StringScanner.instance_methods.include?(:scan_byte) ? Lexer2 : Lexer1
|
237
173
|
end
|
data/lib/liquid/parse_context.rb
CHANGED
@@ -12,6 +12,18 @@ module Liquid
|
|
12
12
|
@locale = @template_options[:locale] ||= I18n.new
|
13
13
|
@warnings = []
|
14
14
|
|
15
|
+
# constructing new StringScanner in Lexer, Tokenizer, etc is expensive
|
16
|
+
# This StringScanner will be shared by all of them
|
17
|
+
@string_scanner = StringScanner.new("")
|
18
|
+
|
19
|
+
@expression_cache = if options[:expression_cache].nil?
|
20
|
+
{}
|
21
|
+
elsif options[:expression_cache].respond_to?(:[]) && options[:expression_cache].respond_to?(:[]=)
|
22
|
+
options[:expression_cache]
|
23
|
+
elsif options[:expression_cache]
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
|
15
27
|
self.depth = 0
|
16
28
|
self.partial = false
|
17
29
|
end
|
@@ -24,12 +36,22 @@ module Liquid
|
|
24
36
|
Liquid::BlockBody.new
|
25
37
|
end
|
26
38
|
|
27
|
-
def
|
28
|
-
|
39
|
+
def new_parser(input)
|
40
|
+
@string_scanner.string = input
|
41
|
+
Parser.new(@string_scanner)
|
42
|
+
end
|
43
|
+
|
44
|
+
def new_tokenizer(source, start_line_number: nil, for_liquid_tag: false)
|
45
|
+
Tokenizer.new(
|
46
|
+
source: source,
|
47
|
+
string_scanner: @string_scanner,
|
48
|
+
line_number: start_line_number,
|
49
|
+
for_liquid_tag: for_liquid_tag,
|
50
|
+
)
|
29
51
|
end
|
30
52
|
|
31
53
|
def parse_expression(markup)
|
32
|
-
Expression.parse(markup)
|
54
|
+
Expression.parse(markup, @string_scanner, @expression_cache)
|
33
55
|
end
|
34
56
|
|
35
57
|
def partial=(value)
|
data/lib/liquid/parser.rb
CHANGED
data/lib/liquid/range_lookup.rb
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
module Liquid
|
4
4
|
class RangeLookup
|
5
|
-
def self.parse(start_markup, end_markup)
|
6
|
-
start_obj = Expression.parse(start_markup)
|
7
|
-
end_obj = Expression.parse(end_markup)
|
5
|
+
def self.parse(start_markup, end_markup, string_scanner, cache = nil)
|
6
|
+
start_obj = Expression.parse(start_markup, string_scanner, cache)
|
7
|
+
end_obj = Expression.parse(end_markup, string_scanner, cache)
|
8
8
|
if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
|
9
9
|
new(start_obj, end_obj)
|
10
10
|
else
|