liquid2 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +11 -0
- data/README.md +8 -2
- data/lib/liquid2/context.rb +4 -2
- data/lib/liquid2/environment.rb +141 -11
- data/lib/liquid2/filters/slice.rb +40 -0
- data/lib/liquid2/parser.rb +8 -11
- data/lib/liquid2/scanner.rb +102 -91
- data/lib/liquid2/version.rb +1 -1
- data/performance/benchmark.rb +0 -6
- data/sig/liquid2.rbs +166 -18
- data.tar.gz.sig +0 -0
- metadata +1 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 474a8dc5c84a97344741bc74d459cc904f1da7025af450c834fb8d416eb98518
|
4
|
+
data.tar.gz: 2dda5436fd261f0208123baa7f7f917f2406531e0bf295db085528a8fa7a5baa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 489dd5089b45f07779de0ee25ee4a8fa5b028a8721bf1423a6ca6884ebde6647d36e9e9450e650c2816c1a63a5b6f1554d8e2d8fa7712c106792a3dbeefe0205
|
7
|
+
data.tar.gz: 7b5e904527625b34d0e177168abbd00198f393b303b9504ee68a37cda3a2f1ef7a407333b15dec96907271610ca715f0e08f49c695466f8f7ddff22a1056b6d4
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## [0.4.0] - 25-08-11
|
2
|
+
|
3
|
+
- Fixed a bug where the parser would raise a `Liquid2::LiquidSyntaxError` if environment arguments `markup_out_end` and `markup_tag_end` where identical. See [#23](https://github.com/jg-rp/ruby-liquid2/issues/23).
|
4
|
+
- Added `Liquid2::Environment.persistent_namespaces`. It is an array of symbols indicating which namespaces from `Liquid2::RenderContext.tag_namespaces` should be preserved when calling `Liquid2::RenderContext#copy`. This is important for some tags - like `{% extends %}` - that need to share state with partial templates rendered with `{% render %}`.
|
5
|
+
- Added the `auto_trim` argument to `Liquid2::Environment`. `auto_trim` can be `'-'`, `'~'` or `nil` (the default). When not `nil`, it sets the automatic whitespace trimming applied to the left side of template text when no explicit whitespace control is given. `+` is also available as whitespace control in tags, outputs statements and comments. A `+` will ensure no trimming is applied, even if `auto_trim` is set.
|
6
|
+
|
7
|
+
## [0.3.1] - 25-06-24
|
8
|
+
|
9
|
+
- Added support for custom markup delimiters. See [#16](https://github.com/jg-rp/ruby-liquid2/pull/16).
|
10
|
+
- Added the `range` filter. `range` is an array slicing filter that takes optional start and end indexes, and an optional step argument, any of which can be negative. See [#18](https://github.com/jg-rp/ruby-liquid2/pull/18).
|
11
|
+
|
1
12
|
## [0.3.0] - 25-05-29
|
2
13
|
|
3
14
|
- Fixed static analysis of lambda expressions (arrow functions). Previously we were not including lambda parameters in the scope of the expression. See [#12](https://github.com/jg-rp/ruby-liquid2/issues/12).
|
data/README.md
CHANGED
@@ -36,7 +36,7 @@ Liquid templates for Ruby, with some extra features.
|
|
36
36
|
Add `'liquid2'` to your Gemfile:
|
37
37
|
|
38
38
|
```
|
39
|
-
gem 'liquid2', '~> 0.
|
39
|
+
gem 'liquid2', '~> 0.4.0'
|
40
40
|
```
|
41
41
|
|
42
42
|
Or
|
@@ -231,7 +231,13 @@ Integer and float literals can use scientific notation, like `1.2e3` or `1e-2`.
|
|
231
231
|
|
232
232
|
Liquid2 includes implementations of `{% extends %}` and `{% block %}` for template inheritance, `{% with %}` for block scoped variables and `{% macro %}` and `{% call %}` for defining parameterized blocks.
|
233
233
|
|
234
|
-
|
234
|
+
The following filters are included in Liquid2's default environment:
|
235
|
+
|
236
|
+
- `sort_numeric` - Sorts array elements by runs of digits found in their string representation.
|
237
|
+
- `json` - Outputs objects serialized in JSON format.
|
238
|
+
- `range`- An alternative to the standard `slice` filter that takes optional start and stop indexes, and an optional step, all of which can be negative.
|
239
|
+
|
240
|
+
See [Tags and filters](#tags-and-filters) for how to add, remove or alias tags and/or filters from your own Liquid2 environment.
|
235
241
|
|
236
242
|
## API
|
237
243
|
|
data/lib/liquid2/context.rb
CHANGED
@@ -219,8 +219,10 @@ module Liquid2
|
|
219
219
|
loop_carry: loop_carry,
|
220
220
|
local_namespace_carry: @assign_score)
|
221
221
|
|
222
|
-
|
223
|
-
|
222
|
+
@env.persistent_namespaces.each do |ns|
|
223
|
+
context.tag_namespace[ns] = @tag_namespace[ns] if @tag_namespace[ns]
|
224
|
+
end
|
225
|
+
|
224
226
|
context
|
225
227
|
end
|
226
228
|
|
data/lib/liquid2/environment.rb
CHANGED
@@ -38,15 +38,27 @@ require_relative "nodes/tags/with"
|
|
38
38
|
module Liquid2
|
39
39
|
# Template parsing and rendering configuration.
|
40
40
|
#
|
41
|
-
# A
|
41
|
+
# A Liquid2::Environment is where you might register custom tags and filters,
|
42
42
|
# or store global context data that should be available to all templates.
|
43
43
|
#
|
44
44
|
# `Liquid2.parse(source)` is equivalent to `Liquid2::Environment.new.parse(source)`.
|
45
45
|
class Environment
|
46
46
|
attr_reader :tags, :local_namespace_limit, :context_depth_limit, :loop_iteration_limit,
|
47
47
|
:output_stream_limit, :filters, :suppress_blank_control_flow_blocks,
|
48
|
-
:shorthand_indexes, :falsy_undefined, :arithmetic_operators
|
48
|
+
:shorthand_indexes, :falsy_undefined, :arithmetic_operators, :markup_comment_prefix,
|
49
|
+
:markup_comment_suffix, :markup_out_end, :markup_out_start, :markup_tag_end,
|
50
|
+
:markup_tag_start, :re_tag_name, :re_word, :re_int, :re_float,
|
51
|
+
:re_double_quote_string_special, :re_single_quote_string_special, :re_markup_start,
|
52
|
+
:re_markup_end, :re_markup_end_chars, :re_up_to_markup_start, :re_punctuation,
|
53
|
+
:re_up_to_inline_comment_end, :re_up_to_raw_end, :re_block_comment_chunk,
|
54
|
+
:re_up_to_doc_end, :re_line_statement_comment, :persistent_namespaces,
|
55
|
+
:universal_markup_end
|
49
56
|
|
57
|
+
# @param arithmetic_operators [bool] When `true`, arithmetic operators `+`, `-`, `*`, `/`, `%`
|
58
|
+
# and `**` are enabled.
|
59
|
+
# @auto_trim ['-' | '~' | nil] Whitespace trimming to apply to the left of text when
|
60
|
+
# neither `-` or `~` is given for any tag, output statement or comment. The default is
|
61
|
+
# `nil`, which means no automatic whitespace trimming is applied.
|
50
62
|
# @param context_depth_limit [Integer] The maximum number of times a render context can
|
51
63
|
# be extended or copied before a `Liquid2::LiquidResourceLimitError`` is raised.
|
52
64
|
# @param globals [Hash[String, untyped]?] Variables that are available to all templates
|
@@ -59,8 +71,23 @@ module Liquid2
|
|
59
71
|
# `Liquid2::LiquidResourceLimitError`` is raised.
|
60
72
|
# @param loop_iteration_limit [Integer?] The maximum number of loop iterations allowed
|
61
73
|
# before a `LiquidResourceLimitError` is raised.
|
74
|
+
# @param markup_comment_prefix [String] The string of characters that indicate the start of a
|
75
|
+
# Liquid comment. This should include a single trailing `#`. Additional, variable length
|
76
|
+
# hashes will be handled by the tokenizer. It is not possible to change comment syntax to not
|
77
|
+
# use `#`.
|
78
|
+
# @param markup_comment_suffix [String] The string of characters that indicate the end of a
|
79
|
+
# Liquid comment, excluding any hashes.
|
80
|
+
# @param markup_out_end [String] The string of characters that indicate the end of a Liquid
|
81
|
+
# output statement.
|
82
|
+
# @param markup_out_start [String] The string of characters that indicate the start of a Liquid
|
83
|
+
# output statement.
|
84
|
+
# @param markup_tag_end [String] The string of characters that indicate the end of a Liquid tag.
|
85
|
+
# @param markup_tag_start [String] The string of characters that indicate the start of a Liquid
|
86
|
+
# tag.
|
62
87
|
# @param output_stream_limit [Integer?] The maximum number of bytes that can be written
|
63
88
|
# to a template's output buffer before a `LiquidResourceLimitError` is raised.
|
89
|
+
# @param parser [singleton(Parser)] `Liquid2::Parser` or a subclass of it.
|
90
|
+
# @param scanner [singleton(Scanner)] `Liquid2::Scanner` or a subclass of it.
|
64
91
|
# @param shorthand_indexes [bool] When `true`, allow shorthand dotted array indexes as
|
65
92
|
# well as bracketed indexes in variable paths. Defaults to `false`.
|
66
93
|
# @param suppress_blank_control_flow_blocks [bool] When `true`, suppress blank control
|
@@ -70,15 +97,24 @@ module Liquid2
|
|
70
97
|
def initialize(
|
71
98
|
arithmetic_operators: false,
|
72
99
|
context_depth_limit: 30,
|
100
|
+
auto_trim: nil,
|
101
|
+
falsy_undefined: true,
|
73
102
|
globals: nil,
|
74
103
|
loader: nil,
|
75
104
|
local_namespace_limit: nil,
|
76
105
|
loop_iteration_limit: nil,
|
106
|
+
markup_comment_prefix: "{#",
|
107
|
+
markup_comment_suffix: "}",
|
108
|
+
markup_out_end: "}}",
|
109
|
+
markup_out_start: "{{",
|
110
|
+
markup_tag_end: "%}",
|
111
|
+
markup_tag_start: "{%",
|
77
112
|
output_stream_limit: nil,
|
113
|
+
parser: Parser,
|
114
|
+
scanner: Scanner,
|
78
115
|
shorthand_indexes: false,
|
79
116
|
suppress_blank_control_flow_blocks: true,
|
80
|
-
undefined: Undefined
|
81
|
-
falsy_undefined: true
|
117
|
+
undefined: Undefined
|
82
118
|
)
|
83
119
|
# A mapping of tag names to objects responding to `parse(token, parser)`.
|
84
120
|
@tags = {}
|
@@ -88,6 +124,10 @@ module Liquid2
|
|
88
124
|
# keyword argument.
|
89
125
|
@filters = {}
|
90
126
|
|
127
|
+
# An array of symbols indicating which namespaces from RenderContext.tag_namespaces
|
128
|
+
# should be preserved when using RenderContext#copy.
|
129
|
+
@persistent_namespaces = [:extends]
|
130
|
+
|
91
131
|
# When `true`, arithmetic operators `+`, `-`, `*`, `/`, `%` and `**` are enabled.
|
92
132
|
# Defaults to `false`.
|
93
133
|
@arithmetic_operators = arithmetic_operators
|
@@ -96,6 +136,11 @@ module Liquid2
|
|
96
136
|
# a Liquid2::LiquidResourceLimitError is raised.
|
97
137
|
@context_depth_limit = context_depth_limit
|
98
138
|
|
139
|
+
# The default whitespace trimming applied to the left of text content when neither
|
140
|
+
# `-` or `~` is specified. Defaults to `nil`, which means no automatic whitespace
|
141
|
+
# trimming.
|
142
|
+
@auto_trim = auto_trim
|
143
|
+
|
99
144
|
# Variables that are available to all templates rendered from this environment.
|
100
145
|
@globals = globals
|
101
146
|
|
@@ -116,9 +161,17 @@ module Liquid2
|
|
116
161
|
# before a `LiquidResourceLimitError` is raised.
|
117
162
|
@output_stream_limit = output_stream_limit
|
118
163
|
|
164
|
+
# Liquid2::Scanner or a subclass of it. This is used to tokenize Liquid source
|
165
|
+
# text before parsing it.
|
166
|
+
@scanner = scanner
|
167
|
+
|
168
|
+
# Liquid2::Parser or a subclass of it. The parser takes tokens from the scanner
|
169
|
+
# and produces an abstract syntax tree.
|
170
|
+
@parser = parser
|
171
|
+
|
119
172
|
# We reuse the same string scanner when parsing templates for improved performance.
|
120
173
|
# TODO: Is this going to cause issues in multi threaded environments?
|
121
|
-
@
|
174
|
+
@string_scanner = StringScanner.new("")
|
122
175
|
|
123
176
|
# When `true`, allow shorthand dotted array indexes as well as bracketed indexes
|
124
177
|
# in variable paths. Defaults to `false`.
|
@@ -136,6 +189,35 @@ module Liquid2
|
|
136
189
|
# raise an error when tested for truthiness.
|
137
190
|
@falsy_undefined = falsy_undefined
|
138
191
|
|
192
|
+
# The string of characters that indicate the start of a Liquid output statement.
|
193
|
+
@markup_out_start = markup_out_start
|
194
|
+
|
195
|
+
# The string of characters that indicate the end of a Liquid output statement.
|
196
|
+
@markup_out_end = markup_out_end
|
197
|
+
|
198
|
+
# The string of characters that indicate the start of a Liquid tag.
|
199
|
+
@markup_tag_start = markup_tag_start
|
200
|
+
|
201
|
+
# The string of characters that indicate the end of a Liquid tag.
|
202
|
+
@markup_tag_end = markup_tag_end
|
203
|
+
|
204
|
+
# Indicates if tag and output end delimiters are identical. This is used by the
|
205
|
+
# parser when parsing output statements.
|
206
|
+
@universal_markup_end = markup_tag_end == markup_out_end
|
207
|
+
|
208
|
+
# The string of characters that indicate the start of a Liquid comment. This should
|
209
|
+
# include a single trailing `#`. Additional, variable length hashes will be handled
|
210
|
+
# by the tokenizer. It is not possible to change comment syntax to not use `#`.
|
211
|
+
@markup_comment_prefix = markup_comment_prefix
|
212
|
+
|
213
|
+
# The string of characters that indicate the end of a Liquid comment, excluding any
|
214
|
+
# hashes.
|
215
|
+
@markup_comment_suffix = markup_comment_suffix
|
216
|
+
|
217
|
+
# You might need to override `setup_scanner` if you've specified custom markup
|
218
|
+
# delimiters and they conflict with standard punctuation.
|
219
|
+
setup_scanner
|
220
|
+
|
139
221
|
# Override `setup_tags_and_filters` in environment subclasses to configure custom
|
140
222
|
# tags and/or filters.
|
141
223
|
setup_tags_and_filters
|
@@ -145,11 +227,13 @@ module Liquid2
|
|
145
227
|
# @param source [String] template source text.
|
146
228
|
# @return [Template]
|
147
229
|
def parse(source, name: "", path: nil, up_to_date: nil, globals: nil, overlay: nil)
|
148
|
-
Template.new(
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
230
|
+
Template.new(
|
231
|
+
self,
|
232
|
+
source,
|
233
|
+
@parser.new(self, @scanner.tokenize(self, source, @string_scanner), source.length).parse,
|
234
|
+
name: name, path: path, up_to_date: up_to_date,
|
235
|
+
globals: make_globals(globals), overlay: overlay
|
236
|
+
)
|
153
237
|
rescue LiquidError => e
|
154
238
|
e.source = source unless e.source
|
155
239
|
e.template_name = name unless e.template_name || name.empty?
|
@@ -262,6 +346,7 @@ module Liquid2
|
|
262
346
|
register_filter("newline_to_br", Liquid2::Filters.method(:newline_to_br))
|
263
347
|
register_filter("plus", Liquid2::Filters.method(:plus))
|
264
348
|
register_filter("prepend", Liquid2::Filters.method(:prepend))
|
349
|
+
register_filter("range", Liquid2::Filters.method(:better_slice))
|
265
350
|
register_filter("reject", Liquid2::Filters.method(:reject))
|
266
351
|
register_filter("remove_first", Liquid2::Filters.method(:remove_first))
|
267
352
|
register_filter("remove_last", Liquid2::Filters.method(:remove_last))
|
@@ -292,13 +377,58 @@ module Liquid2
|
|
292
377
|
register_filter("where", Liquid2::Filters.method(:where))
|
293
378
|
end
|
294
379
|
|
380
|
+
# Compile regular expressions for use by the tokenizer attached to this environment.
|
381
|
+
def setup_scanner
|
382
|
+
# A regex pattern matching Liquid tag names. Should include `#` for inline comments.
|
383
|
+
@re_tag_name = /(?:[a-z][a-z_0-9]*|#)/
|
384
|
+
|
385
|
+
# A regex pattern matching keywords and/or variable/path names. Replace this if
|
386
|
+
# you want to disable Unicode characters in identifiers, for example.
|
387
|
+
@re_word = /[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*/
|
388
|
+
|
389
|
+
# Patterns matching literal integers and floats, possibly in scientific notation.
|
390
|
+
# You could simplify these to disable scientific notation.
|
391
|
+
@re_int = /-?\d+(?:[eE]\+?\d+)?/
|
392
|
+
@re_float = /((?:-?\d+\.\d+(?:[eE][+-]?\d+)?)|(-?\d+[eE]-\d+))/
|
393
|
+
|
394
|
+
# Patterns matching escape sequences, interpolation and end of string in string literals.
|
395
|
+
# You could remove `\$` from these to disable string interpolation.
|
396
|
+
@re_double_quote_string_special = /[\\"\$]/
|
397
|
+
@re_single_quote_string_special = /[\\'\$]/
|
398
|
+
|
399
|
+
# rubocop: disable Layout/LineLength
|
400
|
+
|
401
|
+
# A regex pattern matching the start of some Liquid markup. Could be the start of an
|
402
|
+
# output statement, tag or comment. Traditionally `{{`, `{%` and `{#`, respectively.
|
403
|
+
@re_markup_start = /#{Regexp.escape(@markup_out_start)}|#{Regexp.escape(@markup_tag_start)}|#{Regexp.escape(@markup_comment_prefix)}/
|
404
|
+
|
405
|
+
# A regex pattern matching the end of some Liquid markup. Could be the end of
|
406
|
+
# an output statement or tag. Traditionally `}}`, `%}`, respectively.
|
407
|
+
@re_markup_end = /#{Regexp.escape(@markup_out_end)}|#{Regexp.escape(@markup_tag_end)}/
|
408
|
+
|
409
|
+
# A regex pattern matching any one of the possible characters ending some Liquid
|
410
|
+
# markup. This is used to detect incomplete and malformed markup and provide
|
411
|
+
# helpful error messages.
|
412
|
+
@re_markup_end_chars = /[#{Regexp.escape((@markup_out_end + @markup_tag_end).each_char.uniq.join)}]/
|
413
|
+
|
414
|
+
@re_up_to_markup_start = /(?=#{Regexp.escape(@markup_out_start)}|#{Regexp.escape(@markup_tag_start)}|#{Regexp.escape(@markup_comment_prefix)})/
|
415
|
+
@re_punctuation = %r{(?!#{@re_markup_end})(\?|\[|\]|\|{1,2}|\.{1,2}|,|:|\(|\)|[<>=!]+|[+\-%*/]+(?!#{@re_markup_end_chars}))}
|
416
|
+
@re_up_to_inline_comment_end = /(?=([+\-~])?#{Regexp.escape(@markup_tag_end)})/
|
417
|
+
@re_up_to_raw_end = /(?=(#{Regexp.escape(@markup_tag_start)}[+\-~]?\s*endraw\s*[+\-~]?#{Regexp.escape(@markup_tag_end)}))/
|
418
|
+
@re_block_comment_chunk = /(#{Regexp.escape(@markup_tag_start)}[+\-~]?\s*(comment|raw|endcomment|endraw)\s*[+\-~]?#{Regexp.escape(@markup_tag_end)})/
|
419
|
+
@re_up_to_doc_end = /(?=(#{Regexp.escape(@markup_tag_start)}[+\-~]?\s*enddoc\s*[+\-~]?#{Regexp.escape(@markup_tag_end)}))/
|
420
|
+
@re_line_statement_comment = /(?=([\r\n]+|-?#{Regexp.escape(@markup_tag_end)}))/
|
421
|
+
|
422
|
+
# rubocop: enable Layout/LineLength
|
423
|
+
end
|
424
|
+
|
295
425
|
def undefined(name, node: nil)
|
296
426
|
@undefined.new(name, node: node)
|
297
427
|
end
|
298
428
|
|
299
429
|
# Trim _text_.
|
300
430
|
def trim(text, left_trim, right_trim)
|
301
|
-
case left_trim
|
431
|
+
case left_trim || @auto_trim
|
302
432
|
when "-"
|
303
433
|
text.lstrip!
|
304
434
|
when "~"
|
@@ -13,5 +13,45 @@ module Liquid2
|
|
13
13
|
Liquid2.to_s(left).slice(to_integer(start), to_integer(length)) || ""
|
14
14
|
end
|
15
15
|
end
|
16
|
+
|
17
|
+
def self.better_slice(
|
18
|
+
left,
|
19
|
+
start_ = :undefined, stop_ = :undefined, step_ = :undefined,
|
20
|
+
start: :undefined, stop: :undefined, step: :undefined
|
21
|
+
)
|
22
|
+
# Give priority to keyword arguments, default to nil if neither are given.
|
23
|
+
start = start_ == :undefined ? nil : start_ if start == :undefined
|
24
|
+
stop = stop_ == :undefined ? nil : stop_ if stop == :undefined
|
25
|
+
step = step_ == :undefined ? nil : step_ if step == :undefined
|
26
|
+
|
27
|
+
step = to_integer(step || 1)
|
28
|
+
length = left.length
|
29
|
+
return [] if length.zero? || step.zero?
|
30
|
+
|
31
|
+
start = to_integer(start) unless start.nil?
|
32
|
+
stop = to_integer(stop) unless stop.nil?
|
33
|
+
|
34
|
+
normalized_start = if start.nil?
|
35
|
+
step.negative? ? length - 1 : 0
|
36
|
+
elsif start&.negative?
|
37
|
+
[length + start, 0].max
|
38
|
+
else
|
39
|
+
[start, length - 1].min
|
40
|
+
end
|
41
|
+
|
42
|
+
normalized_stop = if stop.nil?
|
43
|
+
step.negative? ? -1 : length
|
44
|
+
elsif stop&.negative?
|
45
|
+
[length + stop, -1].max
|
46
|
+
else
|
47
|
+
[stop, length].min
|
48
|
+
end
|
49
|
+
|
50
|
+
# This does not work with Ruby 3.1
|
51
|
+
# left[(normalized_start...normalized_stop).step(step)]
|
52
|
+
#
|
53
|
+
# But this does.
|
54
|
+
(normalized_start...normalized_stop).step(step).map { |i| left[i] }
|
55
|
+
end
|
16
56
|
end
|
17
57
|
end
|
data/lib/liquid2/parser.rb
CHANGED
@@ -25,11 +25,12 @@ module Liquid2
|
|
25
25
|
# Liquid template parser.
|
26
26
|
class Parser
|
27
27
|
# Parse Liquid template text into a syntax tree.
|
28
|
+
# @param env [Environment]
|
28
29
|
# @param source [String]
|
29
30
|
# @return [Array[Node | String]]
|
30
31
|
def self.parse(env, source, scanner: nil)
|
31
32
|
new(env,
|
32
|
-
Liquid2::Scanner.tokenize(source, scanner || StringScanner.new("")),
|
33
|
+
Liquid2::Scanner.tokenize(env, source, scanner || StringScanner.new("")),
|
33
34
|
source.length).parse
|
34
35
|
end
|
35
36
|
|
@@ -42,6 +43,10 @@ module Liquid2
|
|
42
43
|
@pos = 0
|
43
44
|
@eof = [:token_eof, nil, length - 1]
|
44
45
|
@whitespace_carry = nil
|
46
|
+
|
47
|
+
# If both tags and output statements share the same end delimiter, we expect
|
48
|
+
# `:token_tag_end` to close an output statement as the scanner scans for tags first.
|
49
|
+
@output_end = @env.universal_markup_end ? :token_tag_end : :token_output_end
|
45
50
|
end
|
46
51
|
|
47
52
|
# Return the current token without advancing the pointer.
|
@@ -690,7 +695,7 @@ module Liquid2
|
|
690
695
|
def parse_output
|
691
696
|
expr = parse_filtered_expression
|
692
697
|
carry_whitespace_control
|
693
|
-
eat(
|
698
|
+
eat(@output_end)
|
694
699
|
Output.new(expr.token, expr)
|
695
700
|
end
|
696
701
|
|
@@ -824,15 +829,7 @@ module Liquid2
|
|
824
829
|
return parse_partial_arrow_function(expr)
|
825
830
|
end
|
826
831
|
|
827
|
-
|
828
|
-
unless BINARY_OPERATORS.member?(kind)
|
829
|
-
raise LiquidSyntaxError.new("expected an infix operator, found #{kind}", current)
|
830
|
-
end
|
831
|
-
|
832
|
-
expr = parse_infix_expression(expr)
|
833
|
-
end
|
834
|
-
|
835
|
-
eat(:token_rparen)
|
832
|
+
eat(:token_rparen, "unbalanced parentheses")
|
836
833
|
expr
|
837
834
|
end
|
838
835
|
|
data/lib/liquid2/scanner.rb
CHANGED
@@ -12,14 +12,6 @@ module Liquid2
|
|
12
12
|
class Scanner
|
13
13
|
attr_reader :tokens
|
14
14
|
|
15
|
-
RE_LINE_SPACE = /[ \t]+/
|
16
|
-
RE_WORD = /[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*/
|
17
|
-
RE_INT = /-?\d+(?:[eE]\+?\d+)?/
|
18
|
-
RE_FLOAT = /((?:-?\d+\.\d+(?:[eE][+-]?\d+)?)|(-?\d+[eE]-\d+))/
|
19
|
-
RE_PUNCTUATION = %r{\?|\[|\]|\|{1,2}|\.{1,2}|,|:|\(|\)|[<>=!]+|[+\-%*/]+(?![\}%])}
|
20
|
-
RE_SINGLE_QUOTE_STRING_SPECIAL = /[\\'\$]/
|
21
|
-
RE_DOUBLE_QUOTE_STRING_SPECIAL = /[\\"\$]/
|
22
|
-
|
23
15
|
# Keywords and symbols that get their own token kind.
|
24
16
|
TOKEN_MAP = {
|
25
17
|
"true" => :token_true,
|
@@ -68,15 +60,16 @@ module Liquid2
|
|
68
60
|
"**" => :token_pow
|
69
61
|
}.freeze
|
70
62
|
|
71
|
-
def self.tokenize(source, scanner)
|
72
|
-
lexer = new(source, scanner)
|
63
|
+
def self.tokenize(env, source, scanner)
|
64
|
+
lexer = new(env, source, scanner)
|
73
65
|
lexer.run
|
74
66
|
lexer.tokens
|
75
67
|
end
|
76
68
|
|
69
|
+
# @param env [Environment]
|
77
70
|
# @param source [String]
|
78
71
|
# @param scanner [StringScanner]
|
79
|
-
def initialize(source, scanner)
|
72
|
+
def initialize(env, source, scanner)
|
80
73
|
@source = source
|
81
74
|
@scanner = scanner
|
82
75
|
@scanner.string = @source
|
@@ -84,8 +77,33 @@ module Liquid2
|
|
84
77
|
# A pointer to the start of the current token.
|
85
78
|
@start = 0
|
86
79
|
|
87
|
-
# Tokens are arrays of (kind, value, start index)
|
80
|
+
# Tokens are arrays of (kind, value, start index).
|
81
|
+
# Sometimes we set value to `nil` when the symbol is unambiguous.
|
88
82
|
@tokens = [] # : Array[[Symbol, String?, Integer]]
|
83
|
+
|
84
|
+
@s_out_start = env.markup_out_start
|
85
|
+
@s_out_end = env.markup_out_end
|
86
|
+
@s_tag_start = env.markup_tag_start
|
87
|
+
@s_tag_end = env.markup_tag_end
|
88
|
+
@s_comment_prefix = env.markup_comment_prefix
|
89
|
+
@s_comment_suffix = env.markup_comment_suffix
|
90
|
+
|
91
|
+
@re_tag_name = env.re_tag_name
|
92
|
+
@re_word = env.re_word
|
93
|
+
@re_int = env.re_int
|
94
|
+
@re_float = env.re_float
|
95
|
+
@re_double_quote_string_special = env.re_double_quote_string_special
|
96
|
+
@re_single_quote_string_special = env.re_single_quote_string_special
|
97
|
+
@re_markup_start = env.re_markup_start
|
98
|
+
@re_markup_end = env.re_markup_end
|
99
|
+
@re_markup_end_chars = env.re_markup_end_chars
|
100
|
+
@re_up_to_markup_start = env.re_up_to_markup_start
|
101
|
+
@re_punctuation = env.re_punctuation
|
102
|
+
@re_up_to_inline_comment_end = env.re_up_to_inline_comment_end
|
103
|
+
@re_up_to_raw_end = env.re_up_to_raw_end
|
104
|
+
@re_block_comment_chunk = env.re_block_comment_chunk
|
105
|
+
@re_up_to_doc_end = env.re_up_to_doc_end
|
106
|
+
@re_line_statement_comment = env.re_line_statement_comment
|
89
107
|
end
|
90
108
|
|
91
109
|
def run
|
@@ -108,14 +126,13 @@ module Liquid2
|
|
108
126
|
end
|
109
127
|
|
110
128
|
def skip_line_trivia
|
111
|
-
@start = @scanner.pos if @scanner.skip(
|
129
|
+
@start = @scanner.pos if @scanner.skip(/[ \t]+/)
|
112
130
|
end
|
113
131
|
|
114
132
|
def accept_whitespace_control
|
115
133
|
ch = @scanner.peek(1)
|
116
134
|
|
117
|
-
|
118
|
-
when "-", "+", "~"
|
135
|
+
if ch == "-" || ch == "+" || ch == "~" # rubocop: disable Style/MultipleComparison
|
119
136
|
@scanner.pos += 1
|
120
137
|
@tokens << [:token_whitespace_control, ch, @start]
|
121
138
|
@start = @scanner.pos
|
@@ -126,22 +143,22 @@ module Liquid2
|
|
126
143
|
end
|
127
144
|
|
128
145
|
def lex_markup
|
129
|
-
case @scanner.scan(
|
130
|
-
when
|
146
|
+
case @scanner.scan(@re_markup_start)
|
147
|
+
when @s_comment_prefix
|
131
148
|
:lex_comment
|
132
|
-
when
|
149
|
+
when @s_out_start
|
133
150
|
@tokens << [:token_output_start, nil, @start]
|
134
151
|
@start = @scanner.pos
|
135
152
|
accept_whitespace_control
|
136
153
|
skip_trivia
|
137
154
|
:lex_expression
|
138
|
-
when
|
155
|
+
when @s_tag_start
|
139
156
|
@tokens << [:token_tag_start, nil, @start]
|
140
157
|
@start = @scanner.pos
|
141
158
|
accept_whitespace_control
|
142
159
|
skip_trivia
|
143
160
|
|
144
|
-
if (tag_name = @scanner.scan(
|
161
|
+
if (tag_name = @scanner.scan(@re_tag_name))
|
145
162
|
@tokens << [:token_tag_name, tag_name, @start]
|
146
163
|
@start = @scanner.pos
|
147
164
|
|
@@ -173,8 +190,7 @@ module Liquid2
|
|
173
190
|
:lex_expression
|
174
191
|
end
|
175
192
|
else
|
176
|
-
if @scanner.skip_until(
|
177
|
-
@scanner.pos -= 2
|
193
|
+
if @scanner.skip_until(@re_up_to_markup_start)
|
178
194
|
@tokens << [:token_other, @source.byteslice(@start...@scanner.pos), @start]
|
179
195
|
@start = @scanner.pos
|
180
196
|
:lex_markup
|
@@ -192,26 +208,27 @@ module Liquid2
|
|
192
208
|
def lex_expression
|
193
209
|
loop do
|
194
210
|
skip_trivia
|
195
|
-
if (value = @scanner.scan(
|
211
|
+
if (value = @scanner.scan(@re_float))
|
196
212
|
@tokens << [:token_float, value, @start]
|
197
213
|
@start = @scanner.pos
|
198
|
-
elsif (value = @scanner.scan(
|
214
|
+
elsif (value = @scanner.scan(@re_int))
|
199
215
|
@tokens << [:token_int, value, @start]
|
200
216
|
@start = @scanner.pos
|
201
|
-
elsif (value = @scanner.scan(
|
217
|
+
elsif (value = @scanner.scan(@re_punctuation))
|
202
218
|
@tokens << [TOKEN_MAP[value] || :token_unknown, value, @start]
|
203
219
|
@start = @scanner.pos
|
204
|
-
elsif (value = @scanner.scan(
|
220
|
+
elsif (value = @scanner.scan(@re_word))
|
205
221
|
@tokens << [TOKEN_MAP[value] || :token_word, value, @start]
|
206
222
|
@start = @scanner.pos
|
207
223
|
else
|
208
224
|
case @scanner.get_byte
|
209
225
|
when "'"
|
210
226
|
@start = @scanner.pos
|
211
|
-
scan_string("'", :token_single_quote_string,
|
227
|
+
scan_string("'", :token_single_quote_string, @re_single_quote_string_special)
|
212
228
|
when "\""
|
213
229
|
@start = @scanner.pos
|
214
|
-
scan_string("\"", :token_double_quote_string,
|
230
|
+
scan_string("\"", :token_double_quote_string,
|
231
|
+
@re_double_quote_string_special)
|
215
232
|
else
|
216
233
|
@scanner.pos -= 1
|
217
234
|
break
|
@@ -222,17 +239,17 @@ module Liquid2
|
|
222
239
|
accept_whitespace_control
|
223
240
|
|
224
241
|
# Miro benchmarks show no performance gain using scan_byte and peek_byte over scan here.
|
225
|
-
case @scanner.scan(
|
226
|
-
when
|
227
|
-
@tokens << [:token_output_end, nil, @start]
|
228
|
-
when "%}"
|
242
|
+
case @scanner.scan(@re_markup_end)
|
243
|
+
when @s_tag_end
|
229
244
|
@tokens << [:token_tag_end, nil, @start]
|
245
|
+
when @s_out_end
|
246
|
+
@tokens << [:token_output_end, nil, @start]
|
230
247
|
else
|
231
248
|
# Unexpected token
|
232
249
|
return nil if @scanner.eos?
|
233
250
|
|
234
|
-
if (ch = @scanner.scan(
|
235
|
-
raise LiquidSyntaxError.new("missing
|
251
|
+
if (ch = @scanner.scan(@re_markup_end_chars))
|
252
|
+
raise LiquidSyntaxError.new("missing markup delimiter detected",
|
236
253
|
[:token_unknown, ch, @start])
|
237
254
|
end
|
238
255
|
|
@@ -255,8 +272,7 @@ module Liquid2
|
|
255
272
|
|
256
273
|
wc = accept_whitespace_control
|
257
274
|
|
258
|
-
if @scanner.skip_until(/([+\-~]?)(\#{#{hash_count}}
|
259
|
-
@scanner.pos -= @scanner[0]&.length || 0
|
275
|
+
if @scanner.skip_until(/(?=([+\-~]?)(\#{#{hash_count}}#{Regexp.escape(@s_comment_suffix)}))/)
|
260
276
|
@tokens << [:token_comment, @source.byteslice(@start...@scanner.pos), @start]
|
261
277
|
@start = @scanner.pos
|
262
278
|
|
@@ -282,19 +298,18 @@ module Liquid2
|
|
282
298
|
end
|
283
299
|
|
284
300
|
def lex_inside_inline_comment
|
285
|
-
if @scanner.skip_until(
|
286
|
-
@scanner.pos -= @scanner.captures&.first.nil? ? 2 : 3
|
301
|
+
if @scanner.skip_until(@re_up_to_inline_comment_end)
|
287
302
|
@tokens << [:token_comment, @source.byteslice(@start...@scanner.pos), @start]
|
288
303
|
@start = @scanner.pos
|
289
304
|
end
|
290
305
|
|
291
306
|
accept_whitespace_control
|
292
307
|
|
293
|
-
case @scanner.scan(
|
294
|
-
when
|
295
|
-
@tokens << [:token_output_end, nil, @start]
|
296
|
-
when "%}"
|
308
|
+
case @scanner.scan(@re_markup_end)
|
309
|
+
when @s_tag_end
|
297
310
|
@tokens << [:token_tag_end, nil, @start]
|
311
|
+
when @s_out_end
|
312
|
+
@tokens << [:token_output_end, nil, @start]
|
298
313
|
else
|
299
314
|
# Unexpected token
|
300
315
|
return nil if @scanner.eos?
|
@@ -310,17 +325,16 @@ module Liquid2
|
|
310
325
|
skip_trivia
|
311
326
|
accept_whitespace_control
|
312
327
|
|
313
|
-
case @scanner.scan(
|
314
|
-
when
|
315
|
-
@tokens << [:token_output_end, nil, @start]
|
316
|
-
@start = @scanner.pos
|
317
|
-
when "%}"
|
328
|
+
case @scanner.scan(@re_markup_end)
|
329
|
+
when @s_tag_end
|
318
330
|
@tokens << [:token_tag_end, nil, @start]
|
319
331
|
@start = @scanner.pos
|
332
|
+
when @s_out_end
|
333
|
+
@tokens << [:token_output_end, nil, @start]
|
334
|
+
@start = @scanner.pos
|
320
335
|
end
|
321
336
|
|
322
|
-
if @scanner.skip_until(
|
323
|
-
@scanner.pos -= @scanner.captures&.first&.length || raise
|
337
|
+
if @scanner.skip_until(@re_up_to_raw_end)
|
324
338
|
@tokens << [:token_raw, @source.byteslice(@start...@scanner.pos), @start]
|
325
339
|
@start = @scanner.pos
|
326
340
|
end
|
@@ -332,22 +346,20 @@ module Liquid2
|
|
332
346
|
skip_trivia
|
333
347
|
accept_whitespace_control
|
334
348
|
|
335
|
-
case @scanner.scan(
|
336
|
-
when
|
337
|
-
@tokens << [:token_output_end, nil, @start]
|
338
|
-
@start = @scanner.pos
|
339
|
-
when "%}"
|
349
|
+
case @scanner.scan(@re_markup_end)
|
350
|
+
when @s_tag_end
|
340
351
|
@tokens << [:token_tag_end, nil, @start]
|
341
352
|
@start = @scanner.pos
|
353
|
+
when @s_out_end
|
354
|
+
@tokens << [:token_output_end, nil, @start]
|
355
|
+
@start = @scanner.pos
|
342
356
|
end
|
343
357
|
|
344
358
|
comment_depth = 1
|
345
359
|
raw_depth = 0
|
346
360
|
|
347
361
|
loop do
|
348
|
-
unless @scanner.skip_until(
|
349
|
-
break
|
350
|
-
end
|
362
|
+
break unless @scanner.skip_until(@re_block_comment_chunk)
|
351
363
|
|
352
364
|
tag_name = @scanner.captures&.last || raise
|
353
365
|
|
@@ -380,17 +392,16 @@ module Liquid2
|
|
380
392
|
skip_trivia
|
381
393
|
accept_whitespace_control
|
382
394
|
|
383
|
-
case @scanner.scan(
|
384
|
-
when
|
385
|
-
@tokens << [:token_output_end, nil, @start]
|
386
|
-
@start = @scanner.pos
|
387
|
-
when "%}"
|
395
|
+
case @scanner.scan(@re_markup_end)
|
396
|
+
when @s_tag_end
|
388
397
|
@tokens << [:token_tag_end, nil, @start]
|
389
398
|
@start = @scanner.pos
|
399
|
+
when @s_out_end
|
400
|
+
@tokens << [:token_output_end, nil, @start]
|
401
|
+
@start = @scanner.pos
|
390
402
|
end
|
391
403
|
|
392
|
-
if @scanner.skip_until(
|
393
|
-
@scanner.pos -= @scanner.captures&.first&.length || raise
|
404
|
+
if @scanner.skip_until(@re_up_to_doc_end)
|
394
405
|
@tokens << [:token_doc, @source.byteslice(@start...@scanner.pos), @start]
|
395
406
|
@start = @scanner.pos
|
396
407
|
end
|
@@ -401,21 +412,19 @@ module Liquid2
|
|
401
412
|
def lex_line_statements
|
402
413
|
skip_trivia # Leading newlines are OK
|
403
414
|
|
404
|
-
if (tag_name = @scanner.scan(
|
415
|
+
if (tag_name = @scanner.scan(@re_tag_name))
|
405
416
|
@tokens << [:token_tag_start, nil, @start]
|
406
417
|
@tokens << [:token_tag_name, tag_name, @start]
|
407
418
|
@start = @scanner.pos
|
408
419
|
|
409
|
-
if tag_name == "#" && @scanner.scan_until(
|
410
|
-
@scanner.pos -= @scanner.captures&.first&.length || raise
|
420
|
+
if tag_name == "#" && @scanner.scan_until(@re_line_statement_comment)
|
411
421
|
@tokens << [:token_comment, @source.byteslice(@start...@scanner.pos), @start]
|
412
422
|
@start = @scanner.pos
|
413
423
|
@tokens << [:token_tag_end, nil, @start]
|
414
424
|
:lex_line_statements
|
415
425
|
|
416
|
-
elsif tag_name == "comment" && @scanner.scan_until(/(endcomment)/)
|
426
|
+
elsif tag_name == "comment" && @scanner.scan_until(/(?=endcomment)/)
|
417
427
|
@tokens << [:token_tag_end, nil, @start]
|
418
|
-
@scanner.pos -= @scanner.captures&.first&.length || raise
|
419
428
|
@tokens << [:token_comment, @source.byteslice(@start...@scanner.pos), @start]
|
420
429
|
@start = @scanner.pos
|
421
430
|
:lex_line_statements
|
@@ -424,13 +433,13 @@ module Liquid2
|
|
424
433
|
end
|
425
434
|
else
|
426
435
|
accept_whitespace_control
|
427
|
-
case @scanner.scan(
|
428
|
-
when
|
429
|
-
@tokens << [:token_output_end, nil, @start]
|
430
|
-
@start = @scanner.pos
|
431
|
-
when "%}"
|
436
|
+
case @scanner.scan(@re_markup_end)
|
437
|
+
when @s_tag_end
|
432
438
|
@tokens << [:token_tag_end, nil, @start]
|
433
439
|
@start = @scanner.pos
|
440
|
+
when @s_out_end
|
441
|
+
@tokens << [:token_output_end, nil, @start]
|
442
|
+
@start = @scanner.pos
|
434
443
|
end
|
435
444
|
|
436
445
|
:lex_markup
|
@@ -444,26 +453,26 @@ module Liquid2
|
|
444
453
|
case @scanner.get_byte
|
445
454
|
when "'"
|
446
455
|
@start = @scanner.pos
|
447
|
-
scan_string("'", :token_single_quote_string,
|
456
|
+
scan_string("'", :token_single_quote_string, @re_single_quote_string_special)
|
448
457
|
when "\""
|
449
458
|
@start = @scanner.pos
|
450
|
-
scan_string("\"", :token_double_quote_string,
|
459
|
+
scan_string("\"", :token_double_quote_string, @re_double_quote_string_special)
|
451
460
|
when nil
|
452
461
|
# End of scanner. Unclosed expression or string literal.
|
453
462
|
break
|
454
463
|
|
455
464
|
else
|
456
465
|
@scanner.pos -= 1
|
457
|
-
if (value = @scanner.scan(
|
466
|
+
if (value = @scanner.scan(@re_float))
|
458
467
|
@tokens << [:token_float, value, @start]
|
459
468
|
@start = @scanner.pos
|
460
|
-
elsif (value = @scanner.scan(
|
469
|
+
elsif (value = @scanner.scan(@re_int))
|
461
470
|
@tokens << [:token_int, value, @start]
|
462
471
|
@start = @scanner.pos
|
463
|
-
elsif (value = @scanner.scan(
|
472
|
+
elsif (value = @scanner.scan(@re_punctuation))
|
464
473
|
@tokens << [TOKEN_MAP[value] || raise, nil, @start]
|
465
474
|
@start = @scanner.pos
|
466
|
-
elsif (value = @scanner.scan(
|
475
|
+
elsif (value = @scanner.scan(@re_word))
|
467
476
|
@tokens << [TOKEN_MAP[value] || :token_word, value, @start]
|
468
477
|
@start = @scanner.pos
|
469
478
|
elsif @scanner.scan(/(\r?\n)+/)
|
@@ -475,13 +484,13 @@ module Liquid2
|
|
475
484
|
# End of the line statement and enclosing `liquid` tag.
|
476
485
|
@tokens << [:token_tag_end, nil, @start]
|
477
486
|
accept_whitespace_control
|
478
|
-
case @scanner.scan(
|
479
|
-
when
|
480
|
-
@tokens << [:token_output_end, nil, @start]
|
481
|
-
@start = @scanner.pos
|
482
|
-
when "%}"
|
487
|
+
case @scanner.scan(@re_markup_end)
|
488
|
+
when @s_tag_end
|
483
489
|
@tokens << [:token_tag_end, nil, @start]
|
484
490
|
@start = @scanner.pos
|
491
|
+
when @s_out_end
|
492
|
+
@tokens << [:token_output_end, nil, @start]
|
493
|
+
@start = @scanner.pos
|
485
494
|
end
|
486
495
|
|
487
496
|
return :lex_markup
|
@@ -536,10 +545,12 @@ module Liquid2
|
|
536
545
|
case @scanner.get_byte
|
537
546
|
when "'"
|
538
547
|
@start = @scanner.pos
|
539
|
-
scan_string("'", :token_single_quote_string,
|
548
|
+
scan_string("'", :token_single_quote_string,
|
549
|
+
@re_single_quote_string_special)
|
540
550
|
when "\""
|
541
551
|
@start = @scanner.pos
|
542
|
-
scan_string("\"", :token_double_quote_string,
|
552
|
+
scan_string("\"", :token_double_quote_string,
|
553
|
+
@re_double_quote_string_special)
|
543
554
|
when "}"
|
544
555
|
@tokens << [:token_string_interpol_end, nil, @start]
|
545
556
|
@start = @scanner.pos
|
@@ -550,16 +561,16 @@ module Liquid2
|
|
550
561
|
[symbol, nil, start_of_string])
|
551
562
|
else
|
552
563
|
@scanner.pos -= 1
|
553
|
-
if (value = @scanner.scan(
|
564
|
+
if (value = @scanner.scan(@re_float))
|
554
565
|
@tokens << [:token_float, value, @start]
|
555
566
|
@start = @scanner.pos
|
556
|
-
elsif (value = @scanner.scan(
|
567
|
+
elsif (value = @scanner.scan(@re_int))
|
557
568
|
@tokens << [:token_int, value, @start]
|
558
569
|
@start = @scanner.pos
|
559
|
-
elsif (value = @scanner.scan(
|
570
|
+
elsif (value = @scanner.scan(@re_punctuation))
|
560
571
|
@tokens << [TOKEN_MAP[value] || raise, nil, @start]
|
561
572
|
@start = @scanner.pos
|
562
|
-
elsif (value = @scanner.scan(
|
573
|
+
elsif (value = @scanner.scan(@re_word))
|
563
574
|
@tokens << [TOKEN_MAP[value] || :token_word, value, @start]
|
564
575
|
@start = @scanner.pos
|
565
576
|
else
|
data/lib/liquid2/version.rb
CHANGED
data/performance/benchmark.rb
CHANGED
@@ -48,17 +48,11 @@ env = fixture.env
|
|
48
48
|
source = fixture.templates["index.liquid"]
|
49
49
|
template = env.get_template("index.liquid")
|
50
50
|
|
51
|
-
# scanner = StringScanner.new("")
|
52
|
-
|
53
51
|
Benchmark.ips do |x|
|
54
52
|
# Configure the number of seconds used during
|
55
53
|
# the warmup phase (default 2) and calculation phase (default 5)
|
56
54
|
x.config(warmup: 2, time: 5)
|
57
55
|
|
58
|
-
# x.report("tokenize (#{fixture.name}):") do
|
59
|
-
# Liquid2::Scanner.tokenize(source, scanner)
|
60
|
-
# end
|
61
|
-
|
62
56
|
x.report("parse (#{fixture.name}):") do
|
63
57
|
env.parse(source)
|
64
58
|
end
|
data/sig/liquid2.rbs
CHANGED
@@ -62,6 +62,8 @@ module Liquid2
|
|
62
62
|
# keyword argument.
|
63
63
|
@filters: Hash[String, [_Filter, (Integer | nil)]]
|
64
64
|
|
65
|
+
@persistent_namespaces: Array[Symbol]
|
66
|
+
|
65
67
|
@local_namespace_limit: Integer?
|
66
68
|
|
67
69
|
@context_depth_limit: Integer
|
@@ -82,12 +84,84 @@ module Liquid2
|
|
82
84
|
|
83
85
|
@globals: Hash[String, untyped]?
|
84
86
|
|
85
|
-
@scanner:
|
87
|
+
@scanner: singleton(Scanner)
|
88
|
+
|
89
|
+
@parser: singleton(Parser)
|
90
|
+
|
91
|
+
@string_scanner: StringScanner
|
86
92
|
|
87
93
|
@arithmetic_operators: bool
|
88
94
|
|
95
|
+
@auto_trim: '-' | '~' | nil
|
96
|
+
|
97
|
+
# The string of characters that indicate the start of a Liquid output statement.
|
98
|
+
@markup_out_start: String
|
99
|
+
|
100
|
+
# The string of characters that indicate the end of a Liquid output statement.
|
101
|
+
@markup_out_end: String
|
102
|
+
|
103
|
+
# The string of characters that indicate the start of a Liquid tag.
|
104
|
+
@markup_tag_start: String
|
105
|
+
|
106
|
+
# The string of characters that indicate the end of a Liquid tag.
|
107
|
+
@markup_tag_end: String
|
108
|
+
|
109
|
+
@universal_markup_end: bool
|
110
|
+
|
111
|
+
# The string of characters that indicate the start of a Liquid comment. This should
|
112
|
+
# include a single trailing `#`. Additional, variable length hashes will be handled
|
113
|
+
# by the tokenizer. It is not possible to change comment syntax to not use `#`.
|
114
|
+
@markup_comment_prefix: String
|
115
|
+
|
116
|
+
# The string of characters that indicate the end of a Liquid comment, excluding any
|
117
|
+
# hashes.
|
118
|
+
@markup_comment_suffix: String
|
119
|
+
|
120
|
+
# A regex pattern matching Liquid tag names. Should include `#` for inline comments.
|
121
|
+
@re_tag_name: Regexp
|
122
|
+
|
123
|
+
@re_word: Regexp
|
124
|
+
|
125
|
+
@re_int: Regexp
|
126
|
+
|
127
|
+
@re_float: Regexp
|
128
|
+
|
129
|
+
@re_double_quote_string_special: Regexp
|
130
|
+
|
131
|
+
@re_single_quote_string_special: Regexp
|
132
|
+
|
133
|
+
# A regex pattern matching the start of some Liquid markup. Could be the start of an
|
134
|
+
# output statement, tag or comment. Traditionally `{{`, `{%` and `{#`, respectively.
|
135
|
+
@re_markup_start: Regexp
|
136
|
+
|
137
|
+
# A regex pattern matching the end of some Liquid markup. Could be the end of
|
138
|
+
# an output statement or tag. Traditionally `}}`, `%}`, respectively.
|
139
|
+
# respectively.
|
140
|
+
@re_markup_end: Regexp
|
141
|
+
|
142
|
+
# A regex pattern matching any one of the possible characters ending some Liquid
|
143
|
+
# markup. This is used to detect incomplete and malformed markup and provide
|
144
|
+
# helpful error messages.
|
145
|
+
@re_markup_end_chars: Regexp
|
146
|
+
|
147
|
+
@re_up_to_markup_start: Regexp
|
148
|
+
|
149
|
+
@re_punctuation: Regexp
|
150
|
+
|
151
|
+
@re_up_to_inline_comment_end: Regexp
|
152
|
+
|
153
|
+
@re_up_to_raw_end: Regexp
|
154
|
+
|
155
|
+
@re_block_comment_chunk: Regexp
|
156
|
+
|
157
|
+
@re_up_to_doc_end: Regexp
|
158
|
+
|
159
|
+
@re_line_statement_comment: Regexp
|
160
|
+
|
89
161
|
attr_reader tags: Hash[String, _Tag]
|
90
162
|
|
163
|
+
attr_reader persistent_namespaces: Array[Symbol]
|
164
|
+
|
91
165
|
attr_reader local_namespace_limit: Integer?
|
92
166
|
|
93
167
|
attr_reader context_depth_limit: Integer
|
@@ -106,7 +180,53 @@ module Liquid2
|
|
106
180
|
|
107
181
|
attr_reader arithmetic_operators: bool
|
108
182
|
|
109
|
-
|
183
|
+
attr_reader markup_comment_prefix: String
|
184
|
+
|
185
|
+
attr_reader markup_comment_suffix: String
|
186
|
+
|
187
|
+
attr_reader markup_out_end: String
|
188
|
+
|
189
|
+
attr_reader markup_out_start: String
|
190
|
+
|
191
|
+
attr_reader markup_tag_end: String
|
192
|
+
|
193
|
+
attr_reader markup_tag_start: String
|
194
|
+
|
195
|
+
attr_reader universal_markup_end: bool
|
196
|
+
|
197
|
+
attr_reader re_tag_name: Regexp
|
198
|
+
|
199
|
+
attr_reader re_word: Regexp
|
200
|
+
|
201
|
+
attr_reader re_int: Regexp
|
202
|
+
|
203
|
+
attr_reader re_float: Regexp
|
204
|
+
|
205
|
+
attr_reader re_double_quote_string_special: Regexp
|
206
|
+
|
207
|
+
attr_reader re_single_quote_string_special: Regexp
|
208
|
+
|
209
|
+
attr_reader re_markup_start: Regexp
|
210
|
+
|
211
|
+
attr_reader re_markup_end: Regexp
|
212
|
+
|
213
|
+
attr_reader re_markup_end_chars: Regexp
|
214
|
+
|
215
|
+
attr_reader re_up_to_markup_start: Regexp
|
216
|
+
|
217
|
+
attr_reader re_punctuation: Regexp
|
218
|
+
|
219
|
+
attr_reader re_up_to_inline_comment_end: Regexp
|
220
|
+
|
221
|
+
attr_reader re_up_to_raw_end: Regexp
|
222
|
+
|
223
|
+
attr_reader re_block_comment_chunk: Regexp
|
224
|
+
|
225
|
+
attr_reader re_up_to_doc_end: Regexp
|
226
|
+
|
227
|
+
attr_reader re_line_statement_comment: Regexp
|
228
|
+
|
229
|
+
def initialize: (?arithmetic_operators: bool, ?context_depth_limit: ::Integer, ?auto_trim: '-' | '~' | nil, ?falsy_undefined: bool, ?globals: untyped?, ?loader: TemplateLoader?, ?local_namespace_limit: Integer?, ?loop_iteration_limit: Integer?, ?markup_comment_prefix: ::String, ?markup_comment_suffix: ::String, ?markup_out_end: ::String, ?markup_out_start: ::String, ?markup_tag_end: ::String, ?markup_tag_start: ::String, ?output_stream_limit: Integer?, ?parser: singleton(Parser), ?scanner: singleton(Scanner), ?shorthand_indexes: bool, ?suppress_blank_control_flow_blocks: bool, ?undefined: singleton(Undefined)) -> void
|
110
230
|
|
111
231
|
# @param source [String] template source text.
|
112
232
|
# @return [Template]
|
@@ -136,6 +256,8 @@ module Liquid2
|
|
136
256
|
def delete_tag: (String name) -> (_Tag | nil)
|
137
257
|
|
138
258
|
def setup_tags_and_filters: () -> void
|
259
|
+
|
260
|
+
def setup_scanner: () -> void
|
139
261
|
|
140
262
|
def undefined: (String name, ?node: _HasToken?) -> Undefined
|
141
263
|
|
@@ -171,37 +293,59 @@ module Liquid2
|
|
171
293
|
# A pointer to the start of the current token.
|
172
294
|
@start: Integer
|
173
295
|
|
174
|
-
|
175
|
-
@tokens: Array[[Symbol, String?, Integer]]
|
296
|
+
@s_out_start: String
|
176
297
|
|
177
|
-
|
298
|
+
@s_out_end: String
|
299
|
+
|
300
|
+
@s_tag_start: String
|
301
|
+
|
302
|
+
@s_tag_end: String
|
303
|
+
|
304
|
+
@s_comment_prefix: String
|
305
|
+
|
306
|
+
@s_comment_suffix: String
|
178
307
|
|
179
|
-
|
308
|
+
@re_tag_name: Regexp
|
180
309
|
|
181
|
-
|
310
|
+
@re_word: Regexp
|
182
311
|
|
183
|
-
|
312
|
+
@re_int: Regexp
|
184
313
|
|
185
|
-
|
314
|
+
@re_float: Regexp
|
186
315
|
|
187
|
-
|
316
|
+
@re_double_quote_string_special: Regexp
|
188
317
|
|
189
|
-
|
318
|
+
@re_single_quote_string_special: Regexp
|
190
319
|
|
191
|
-
|
320
|
+
@re_markup_start: Regexp
|
192
321
|
|
193
|
-
|
322
|
+
@re_markup_end: Regexp
|
194
323
|
|
195
|
-
|
324
|
+
@re_markup_end_chars: Regexp
|
325
|
+
|
326
|
+
@re_up_to_markup_start: Regexp
|
327
|
+
|
328
|
+
@re_punctuation: Regexp
|
329
|
+
|
330
|
+
@re_up_to_inline_comment_end: Regexp
|
331
|
+
@re_up_to_raw_end: Regexp
|
332
|
+
@re_block_comment_chunk: Regexp
|
333
|
+
@re_up_to_doc_end: Regexp
|
334
|
+
@re_line_statement_comment: Regexp
|
335
|
+
|
336
|
+
# Tokens are arrays of (kind, value, start index)
|
337
|
+
@tokens: Array[[Symbol, String?, Integer]]
|
338
|
+
|
339
|
+
attr_reader tokens: Array[[Symbol, String?, Integer]]
|
196
340
|
|
197
341
|
# Keywords and symbols that get their own token kind.
|
198
342
|
TOKEN_MAP: Hash[String, Symbol]
|
199
343
|
|
200
|
-
def self.tokenize: (String source, StringScanner scanner) -> Array[[Symbol, String?, Integer]]
|
344
|
+
def self.tokenize: (Environment env, String source, StringScanner scanner) -> Array[[Symbol, String?, Integer]]
|
201
345
|
|
202
346
|
# @param source [String]
|
203
347
|
# @param scanner [StringScanner]
|
204
|
-
def initialize: (String source, StringScanner scanner) -> void
|
348
|
+
def initialize: (Environment env, String source, StringScanner scanner) -> void
|
205
349
|
|
206
350
|
def run: () -> void
|
207
351
|
|
@@ -253,6 +397,8 @@ module Liquid2
|
|
253
397
|
|
254
398
|
@whitespace_carry: String?
|
255
399
|
|
400
|
+
@output_end: Symbol
|
401
|
+
|
256
402
|
# Parse Liquid template text into a syntax tree.
|
257
403
|
# @param source [String]
|
258
404
|
# @return [Array[Node | String]]
|
@@ -378,13 +524,13 @@ module Liquid2
|
|
378
524
|
|
379
525
|
MEMBERSHIP: 6
|
380
526
|
|
381
|
-
PREFIX: 7
|
382
|
-
|
383
527
|
ADD_SUB: 8
|
384
528
|
|
385
529
|
MUL_DIV: 9
|
386
530
|
|
387
531
|
POW: 10
|
532
|
+
|
533
|
+
PREFIX: 11
|
388
534
|
end
|
389
535
|
|
390
536
|
PRECEDENCES: Hash[Symbol, Integer]
|
@@ -1760,6 +1906,8 @@ module Liquid2
|
|
1760
1906
|
|
1761
1907
|
# Return the subsequence of _left_ starting at _start_ up to _length_.
|
1762
1908
|
def self.slice: (untyped left, untyped start, ?untyped length) -> untyped
|
1909
|
+
|
1910
|
+
def self.better_slice: (untyped left, ?untyped start_, ?untyped stop_, ?untyped step_, ?start: untyped, ?stop: untyped, ?step: untyped) -> untyped
|
1763
1911
|
|
1764
1912
|
# Return _left_ with all characters converted to uppercase.
|
1765
1913
|
# Coerce _left_ to a string if it is not one already.
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
metadata.gz.sig
CHANGED
Binary file
|