herb 0.7.1-arm64-darwin → 0.7.3-arm64-darwin
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
- data/Makefile +2 -0
- data/README.md +1 -1
- data/Rakefile +46 -1
- data/config.yml +714 -0
- data/ext/herb/error_helpers.c +27 -27
- data/ext/herb/extconf.rb +2 -1
- data/ext/herb/extension.c +6 -6
- data/ext/herb/extension_helpers.c +3 -3
- data/ext/herb/nodes.c +35 -35
- data/herb.gemspec +3 -0
- data/lib/herb/3.0/herb.bundle +0 -0
- data/lib/herb/3.1/herb.bundle +0 -0
- data/lib/herb/3.2/herb.bundle +0 -0
- data/lib/herb/3.3/herb.bundle +0 -0
- data/lib/herb/3.4/herb.bundle +0 -0
- data/lib/herb/engine/debug_visitor.rb +41 -21
- data/lib/herb/engine.rb +20 -6
- data/lib/herb/version.rb +1 -1
- data/sig/herb/engine/debug_visitor.rbs +3 -3
- data/sig/herb/engine.rbs +5 -0
- data/src/analyze.c +5 -9
- data/src/analyze_helpers.c +17 -6
- data/src/include/pretty_print.h +1 -1
- data/src/include/version.h +1 -1
- data/src/parser.c +6 -9
- data/src/pretty_print.c +1 -1
- data/templates/ext/herb/error_helpers.c.erb +85 -0
- data/templates/ext/herb/error_helpers.h.erb +12 -0
- data/templates/ext/herb/nodes.c.erb +90 -0
- data/templates/ext/herb/nodes.h.erb +9 -0
- data/templates/javascript/packages/core/src/errors.ts.erb +193 -0
- data/templates/javascript/packages/core/src/node-type-guards.ts.erb +325 -0
- data/templates/javascript/packages/core/src/nodes.ts.erb +414 -0
- data/templates/javascript/packages/core/src/visitor.ts.erb +29 -0
- data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +113 -0
- data/templates/javascript/packages/node/extension/error_helpers.h.erb +17 -0
- data/templates/javascript/packages/node/extension/nodes.cpp.erb +111 -0
- data/templates/javascript/packages/node/extension/nodes.h.erb +17 -0
- data/templates/lib/herb/ast/nodes.rb.erb +117 -0
- data/templates/lib/herb/errors.rb.erb +106 -0
- data/templates/lib/herb/visitor.rb.erb +28 -0
- data/templates/sig/serialized_ast_errors.rbs.erb +10 -0
- data/templates/sig/serialized_ast_nodes.rbs.erb +10 -0
- data/templates/src/ast_nodes.c.erb +145 -0
- data/templates/src/ast_pretty_print.c.erb +97 -0
- data/templates/src/errors.c.erb +245 -0
- data/templates/src/include/ast_nodes.h.erb +46 -0
- data/templates/src/include/ast_pretty_print.h.erb +14 -0
- data/templates/src/include/errors.h.erb +58 -0
- data/templates/src/visitor.c.erb +47 -0
- data/templates/template.rb +406 -0
- data/templates/wasm/error_helpers.cpp.erb +93 -0
- data/templates/wasm/error_helpers.h.erb +15 -0
- data/templates/wasm/nodes.cpp.erb +79 -0
- data/templates/wasm/nodes.h.erb +15 -0
- data/vendor/prism/Rakefile +75 -0
- data/vendor/prism/config.yml +4713 -0
- data/vendor/prism/include/prism/ast.h +8190 -0
- data/vendor/prism/include/prism/defines.h +260 -0
- data/vendor/prism/include/prism/diagnostic.h +455 -0
- data/vendor/prism/include/prism/encoding.h +283 -0
- data/vendor/prism/include/prism/node.h +129 -0
- data/vendor/prism/include/prism/options.h +482 -0
- data/vendor/prism/include/prism/pack.h +163 -0
- data/vendor/prism/include/prism/parser.h +933 -0
- data/vendor/prism/include/prism/prettyprint.h +34 -0
- data/vendor/prism/include/prism/regexp.h +43 -0
- data/vendor/prism/include/prism/static_literals.h +121 -0
- data/vendor/prism/include/prism/util/pm_buffer.h +236 -0
- data/vendor/prism/include/prism/util/pm_char.h +204 -0
- data/vendor/prism/include/prism/util/pm_constant_pool.h +218 -0
- data/vendor/prism/include/prism/util/pm_integer.h +130 -0
- data/vendor/prism/include/prism/util/pm_list.h +103 -0
- data/vendor/prism/include/prism/util/pm_memchr.h +29 -0
- data/vendor/prism/include/prism/util/pm_newline_list.h +113 -0
- data/vendor/prism/include/prism/util/pm_string.h +200 -0
- data/vendor/prism/include/prism/util/pm_strncasecmp.h +32 -0
- data/vendor/prism/include/prism/util/pm_strpbrk.h +46 -0
- data/vendor/prism/include/prism/version.h +29 -0
- data/vendor/prism/include/prism.h +408 -0
- data/vendor/prism/src/diagnostic.c +848 -0
- data/vendor/prism/src/encoding.c +5235 -0
- data/vendor/prism/src/node.c +8676 -0
- data/vendor/prism/src/options.c +328 -0
- data/vendor/prism/src/pack.c +509 -0
- data/vendor/prism/src/prettyprint.c +8941 -0
- data/vendor/prism/src/prism.c +23302 -0
- data/vendor/prism/src/regexp.c +790 -0
- data/vendor/prism/src/serialize.c +2268 -0
- data/vendor/prism/src/static_literals.c +617 -0
- data/vendor/prism/src/token_type.c +703 -0
- data/vendor/prism/src/util/pm_buffer.c +357 -0
- data/vendor/prism/src/util/pm_char.c +318 -0
- data/vendor/prism/src/util/pm_constant_pool.c +342 -0
- data/vendor/prism/src/util/pm_integer.c +670 -0
- data/vendor/prism/src/util/pm_list.c +49 -0
- data/vendor/prism/src/util/pm_memchr.c +35 -0
- data/vendor/prism/src/util/pm_newline_list.c +125 -0
- data/vendor/prism/src/util/pm_string.c +383 -0
- data/vendor/prism/src/util/pm_strncasecmp.c +36 -0
- data/vendor/prism/src/util/pm_strpbrk.c +206 -0
- data/vendor/prism/templates/ext/prism/api_node.c.erb +282 -0
- data/vendor/prism/templates/include/prism/ast.h.erb +226 -0
- data/vendor/prism/templates/include/prism/diagnostic.h.erb +130 -0
- data/vendor/prism/templates/java/org/prism/AbstractNodeVisitor.java.erb +22 -0
- data/vendor/prism/templates/java/org/prism/Loader.java.erb +434 -0
- data/vendor/prism/templates/java/org/prism/Nodes.java.erb +403 -0
- data/vendor/prism/templates/javascript/src/deserialize.js.erb +448 -0
- data/vendor/prism/templates/javascript/src/nodes.js.erb +197 -0
- data/vendor/prism/templates/javascript/src/visitor.js.erb +78 -0
- data/vendor/prism/templates/lib/prism/compiler.rb.erb +43 -0
- data/vendor/prism/templates/lib/prism/dispatcher.rb.erb +103 -0
- data/vendor/prism/templates/lib/prism/dot_visitor.rb.erb +189 -0
- data/vendor/prism/templates/lib/prism/dsl.rb.erb +133 -0
- data/vendor/prism/templates/lib/prism/inspect_visitor.rb.erb +131 -0
- data/vendor/prism/templates/lib/prism/mutation_compiler.rb.erb +19 -0
- data/vendor/prism/templates/lib/prism/node.rb.erb +515 -0
- data/vendor/prism/templates/lib/prism/reflection.rb.erb +136 -0
- data/vendor/prism/templates/lib/prism/serialize.rb.erb +602 -0
- data/vendor/prism/templates/lib/prism/visitor.rb.erb +55 -0
- data/vendor/prism/templates/rbi/prism/dsl.rbi.erb +68 -0
- data/vendor/prism/templates/rbi/prism/node.rbi.erb +164 -0
- data/vendor/prism/templates/rbi/prism/visitor.rbi.erb +18 -0
- data/vendor/prism/templates/sig/prism/_private/dot_visitor.rbs.erb +45 -0
- data/vendor/prism/templates/sig/prism/dsl.rbs.erb +31 -0
- data/vendor/prism/templates/sig/prism/mutation_compiler.rbs.erb +7 -0
- data/vendor/prism/templates/sig/prism/node.rbs.erb +132 -0
- data/vendor/prism/templates/sig/prism/visitor.rbs.erb +17 -0
- data/vendor/prism/templates/sig/prism.rbs.erb +89 -0
- data/vendor/prism/templates/src/diagnostic.c.erb +523 -0
- data/vendor/prism/templates/src/node.c.erb +333 -0
- data/vendor/prism/templates/src/prettyprint.c.erb +166 -0
- data/vendor/prism/templates/src/serialize.c.erb +406 -0
- data/vendor/prism/templates/src/token_type.c.erb +369 -0
- data/vendor/prism/templates/template.rb +689 -0
- metadata +112 -2
@@ -0,0 +1,602 @@
|
|
1
|
+
require "stringio"
|
2
|
+
require_relative "polyfill/unpack1"
|
3
|
+
|
4
|
+
module Prism
|
5
|
+
# A module responsible for deserializing parse results.
|
6
|
+
module Serialize
|
7
|
+
# The major version of prism that we are expecting to find in the serialized
|
8
|
+
# strings.
|
9
|
+
MAJOR_VERSION = 1
|
10
|
+
|
11
|
+
# The minor version of prism that we are expecting to find in the serialized
|
12
|
+
# strings.
|
13
|
+
MINOR_VERSION = 5
|
14
|
+
|
15
|
+
# The patch version of prism that we are expecting to find in the serialized
|
16
|
+
# strings.
|
17
|
+
PATCH_VERSION = 1
|
18
|
+
|
19
|
+
# Deserialize the dumped output from a request to parse or parse_file.
|
20
|
+
#
|
21
|
+
# The formatting of the source of this method is purposeful to illustrate
|
22
|
+
# the structure of the serialized data.
|
23
|
+
def self.load_parse(input, serialized, freeze)
|
24
|
+
input = input.dup
|
25
|
+
source = Source.for(input)
|
26
|
+
loader = Loader.new(source, serialized)
|
27
|
+
|
28
|
+
loader.load_header
|
29
|
+
encoding = loader.load_encoding
|
30
|
+
start_line = loader.load_varsint
|
31
|
+
offsets = loader.load_line_offsets(freeze)
|
32
|
+
|
33
|
+
source.replace_start_line(start_line)
|
34
|
+
source.replace_offsets(offsets)
|
35
|
+
|
36
|
+
comments = loader.load_comments(freeze)
|
37
|
+
magic_comments = loader.load_magic_comments(freeze)
|
38
|
+
data_loc = loader.load_optional_location_object(freeze)
|
39
|
+
errors = loader.load_errors(encoding, freeze)
|
40
|
+
warnings = loader.load_warnings(encoding, freeze)
|
41
|
+
cpool_base = loader.load_uint32
|
42
|
+
cpool_size = loader.load_varuint
|
43
|
+
|
44
|
+
constant_pool = ConstantPool.new(input, serialized, cpool_base, cpool_size)
|
45
|
+
|
46
|
+
node = loader.load_node(constant_pool, encoding, freeze)
|
47
|
+
loader.load_constant_pool(constant_pool)
|
48
|
+
raise unless loader.eof?
|
49
|
+
|
50
|
+
result = ParseResult.new(node, comments, magic_comments, data_loc, errors, warnings, source)
|
51
|
+
result.freeze if freeze
|
52
|
+
|
53
|
+
input.force_encoding(encoding)
|
54
|
+
|
55
|
+
# This is an extremely niche use-case where the file was marked as binary
|
56
|
+
# but it contained UTF-8-encoded characters. In that case we will actually
|
57
|
+
# put it back to UTF-8 to give the location APIs the best chance of being
|
58
|
+
# correct.
|
59
|
+
if !input.ascii_only? && input.encoding == Encoding::BINARY
|
60
|
+
input.force_encoding(Encoding::UTF_8)
|
61
|
+
input.force_encoding(Encoding::BINARY) unless input.valid_encoding?
|
62
|
+
end
|
63
|
+
|
64
|
+
if freeze
|
65
|
+
input.freeze
|
66
|
+
source.deep_freeze
|
67
|
+
end
|
68
|
+
|
69
|
+
result
|
70
|
+
end
|
71
|
+
|
72
|
+
# Deserialize the dumped output from a request to lex or lex_file.
|
73
|
+
#
|
74
|
+
# The formatting of the source of this method is purposeful to illustrate
|
75
|
+
# the structure of the serialized data.
|
76
|
+
def self.load_lex(input, serialized, freeze)
|
77
|
+
source = Source.for(input)
|
78
|
+
loader = Loader.new(source, serialized)
|
79
|
+
|
80
|
+
tokens = loader.load_tokens
|
81
|
+
encoding = loader.load_encoding
|
82
|
+
start_line = loader.load_varsint
|
83
|
+
offsets = loader.load_line_offsets(freeze)
|
84
|
+
|
85
|
+
source.replace_start_line(start_line)
|
86
|
+
source.replace_offsets(offsets)
|
87
|
+
|
88
|
+
comments = loader.load_comments(freeze)
|
89
|
+
magic_comments = loader.load_magic_comments(freeze)
|
90
|
+
data_loc = loader.load_optional_location_object(freeze)
|
91
|
+
errors = loader.load_errors(encoding, freeze)
|
92
|
+
warnings = loader.load_warnings(encoding, freeze)
|
93
|
+
raise unless loader.eof?
|
94
|
+
|
95
|
+
result = LexResult.new(tokens, comments, magic_comments, data_loc, errors, warnings, source)
|
96
|
+
|
97
|
+
tokens.each do |token|
|
98
|
+
token[0].value.force_encoding(encoding)
|
99
|
+
|
100
|
+
if freeze
|
101
|
+
token[0].deep_freeze
|
102
|
+
token.freeze
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
if freeze
|
107
|
+
source.deep_freeze
|
108
|
+
tokens.freeze
|
109
|
+
result.freeze
|
110
|
+
end
|
111
|
+
|
112
|
+
result
|
113
|
+
end
|
114
|
+
|
115
|
+
# Deserialize the dumped output from a request to parse_comments or
|
116
|
+
# parse_file_comments.
|
117
|
+
#
|
118
|
+
# The formatting of the source of this method is purposeful to illustrate
|
119
|
+
# the structure of the serialized data.
|
120
|
+
def self.load_parse_comments(input, serialized, freeze)
|
121
|
+
source = Source.for(input)
|
122
|
+
loader = Loader.new(source, serialized)
|
123
|
+
|
124
|
+
loader.load_header
|
125
|
+
loader.load_encoding
|
126
|
+
start_line = loader.load_varsint
|
127
|
+
|
128
|
+
source.replace_start_line(start_line)
|
129
|
+
|
130
|
+
result = loader.load_comments(freeze)
|
131
|
+
raise unless loader.eof?
|
132
|
+
|
133
|
+
source.deep_freeze if freeze
|
134
|
+
result
|
135
|
+
end
|
136
|
+
|
137
|
+
# Deserialize the dumped output from a request to parse_lex or
|
138
|
+
# parse_lex_file.
|
139
|
+
#
|
140
|
+
# The formatting of the source of this method is purposeful to illustrate
|
141
|
+
# the structure of the serialized data.
|
142
|
+
def self.load_parse_lex(input, serialized, freeze)
|
143
|
+
source = Source.for(input)
|
144
|
+
loader = Loader.new(source, serialized)
|
145
|
+
|
146
|
+
tokens = loader.load_tokens
|
147
|
+
loader.load_header
|
148
|
+
encoding = loader.load_encoding
|
149
|
+
start_line = loader.load_varsint
|
150
|
+
offsets = loader.load_line_offsets(freeze)
|
151
|
+
|
152
|
+
source.replace_start_line(start_line)
|
153
|
+
source.replace_offsets(offsets)
|
154
|
+
|
155
|
+
comments = loader.load_comments(freeze)
|
156
|
+
magic_comments = loader.load_magic_comments(freeze)
|
157
|
+
data_loc = loader.load_optional_location_object(freeze)
|
158
|
+
errors = loader.load_errors(encoding, freeze)
|
159
|
+
warnings = loader.load_warnings(encoding, freeze)
|
160
|
+
cpool_base = loader.load_uint32
|
161
|
+
cpool_size = loader.load_varuint
|
162
|
+
|
163
|
+
constant_pool = ConstantPool.new(input, serialized, cpool_base, cpool_size)
|
164
|
+
|
165
|
+
node = loader.load_node(constant_pool, encoding, freeze)
|
166
|
+
loader.load_constant_pool(constant_pool)
|
167
|
+
raise unless loader.eof?
|
168
|
+
|
169
|
+
value = [node, tokens]
|
170
|
+
result = ParseLexResult.new(value, comments, magic_comments, data_loc, errors, warnings, source)
|
171
|
+
|
172
|
+
tokens.each do |token|
|
173
|
+
token[0].value.force_encoding(encoding)
|
174
|
+
|
175
|
+
if freeze
|
176
|
+
token[0].deep_freeze
|
177
|
+
token.freeze
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
if freeze
|
182
|
+
source.deep_freeze
|
183
|
+
tokens.freeze
|
184
|
+
value.freeze
|
185
|
+
result.freeze
|
186
|
+
end
|
187
|
+
|
188
|
+
result
|
189
|
+
end
|
190
|
+
|
191
|
+
class ConstantPool # :nodoc:
|
192
|
+
attr_reader :size
|
193
|
+
|
194
|
+
def initialize(input, serialized, base, size)
|
195
|
+
@input = input
|
196
|
+
@serialized = serialized
|
197
|
+
@base = base
|
198
|
+
@size = size
|
199
|
+
@pool = Array.new(size, nil)
|
200
|
+
end
|
201
|
+
|
202
|
+
def get(index, encoding)
|
203
|
+
@pool[index] ||=
|
204
|
+
begin
|
205
|
+
offset = @base + index * 8
|
206
|
+
start = @serialized.unpack1("L", offset: offset)
|
207
|
+
length = @serialized.unpack1("L", offset: offset + 4)
|
208
|
+
|
209
|
+
if start.nobits?(1 << 31)
|
210
|
+
@input.byteslice(start, length).force_encoding(encoding).to_sym
|
211
|
+
else
|
212
|
+
@serialized.byteslice(start & ((1 << 31) - 1), length).force_encoding(encoding).to_sym
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
if RUBY_ENGINE == "truffleruby"
|
219
|
+
# StringIO is synchronized and that adds a high overhead on TruffleRuby.
|
220
|
+
class FastStringIO # :nodoc:
|
221
|
+
attr_accessor :pos
|
222
|
+
|
223
|
+
def initialize(string)
|
224
|
+
@string = string
|
225
|
+
@pos = 0
|
226
|
+
end
|
227
|
+
|
228
|
+
def getbyte
|
229
|
+
byte = @string.getbyte(@pos)
|
230
|
+
@pos += 1
|
231
|
+
byte
|
232
|
+
end
|
233
|
+
|
234
|
+
def read(n)
|
235
|
+
slice = @string.byteslice(@pos, n)
|
236
|
+
@pos += n
|
237
|
+
slice
|
238
|
+
end
|
239
|
+
|
240
|
+
def eof?
|
241
|
+
@pos >= @string.bytesize
|
242
|
+
end
|
243
|
+
end
|
244
|
+
else
|
245
|
+
FastStringIO = ::StringIO # :nodoc:
|
246
|
+
end
|
247
|
+
|
248
|
+
class Loader # :nodoc:
|
249
|
+
attr_reader :input, :io, :source
|
250
|
+
|
251
|
+
def initialize(source, serialized)
|
252
|
+
@input = source.source.dup
|
253
|
+
raise unless serialized.encoding == Encoding::BINARY
|
254
|
+
@io = FastStringIO.new(serialized)
|
255
|
+
@source = source
|
256
|
+
define_load_node_lambdas if RUBY_ENGINE != "ruby"
|
257
|
+
end
|
258
|
+
|
259
|
+
def eof?
|
260
|
+
io.getbyte
|
261
|
+
io.eof?
|
262
|
+
end
|
263
|
+
|
264
|
+
def load_constant_pool(constant_pool)
|
265
|
+
trailer = 0
|
266
|
+
|
267
|
+
constant_pool.size.times do |index|
|
268
|
+
start, length = io.read(8).unpack("L2")
|
269
|
+
trailer += length if start.anybits?(1 << 31)
|
270
|
+
end
|
271
|
+
|
272
|
+
io.read(trailer)
|
273
|
+
end
|
274
|
+
|
275
|
+
def load_header
|
276
|
+
raise "Invalid serialization" if io.read(5) != "PRISM"
|
277
|
+
raise "Invalid serialization" if io.read(3).unpack("C3") != [MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION]
|
278
|
+
raise "Invalid serialization (location fields must be included but are not)" if io.getbyte != 0
|
279
|
+
end
|
280
|
+
|
281
|
+
def load_encoding
|
282
|
+
encoding = Encoding.find(io.read(load_varuint))
|
283
|
+
@input = input.force_encoding(encoding).freeze
|
284
|
+
encoding
|
285
|
+
end
|
286
|
+
|
287
|
+
def load_line_offsets(freeze)
|
288
|
+
offsets = Array.new(load_varuint) { load_varuint }
|
289
|
+
offsets.freeze if freeze
|
290
|
+
offsets
|
291
|
+
end
|
292
|
+
|
293
|
+
def load_comments(freeze)
|
294
|
+
comments =
|
295
|
+
Array.new(load_varuint) do
|
296
|
+
comment =
|
297
|
+
case load_varuint
|
298
|
+
when 0 then InlineComment.new(load_location_object(freeze))
|
299
|
+
when 1 then EmbDocComment.new(load_location_object(freeze))
|
300
|
+
end
|
301
|
+
|
302
|
+
comment.freeze if freeze
|
303
|
+
comment
|
304
|
+
end
|
305
|
+
|
306
|
+
comments.freeze if freeze
|
307
|
+
comments
|
308
|
+
end
|
309
|
+
|
310
|
+
def load_magic_comments(freeze)
|
311
|
+
magic_comments =
|
312
|
+
Array.new(load_varuint) do
|
313
|
+
magic_comment =
|
314
|
+
MagicComment.new(
|
315
|
+
load_location_object(freeze),
|
316
|
+
load_location_object(freeze)
|
317
|
+
)
|
318
|
+
|
319
|
+
magic_comment.freeze if freeze
|
320
|
+
magic_comment
|
321
|
+
end
|
322
|
+
|
323
|
+
magic_comments.freeze if freeze
|
324
|
+
magic_comments
|
325
|
+
end
|
326
|
+
|
327
|
+
DIAGNOSTIC_TYPES = [
|
328
|
+
<%- errors.each do |error| -%>
|
329
|
+
<%= error.name.downcase.to_sym.inspect %>,
|
330
|
+
<%- end -%>
|
331
|
+
<%- warnings.each do |warning| -%>
|
332
|
+
<%= warning.name.downcase.to_sym.inspect %>,
|
333
|
+
<%- end -%>
|
334
|
+
].freeze
|
335
|
+
|
336
|
+
private_constant :DIAGNOSTIC_TYPES
|
337
|
+
|
338
|
+
def load_error_level
|
339
|
+
level = io.getbyte
|
340
|
+
|
341
|
+
case level
|
342
|
+
when 0
|
343
|
+
:syntax
|
344
|
+
when 1
|
345
|
+
:argument
|
346
|
+
when 2
|
347
|
+
:load
|
348
|
+
else
|
349
|
+
raise "Unknown level: #{level}"
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def load_errors(encoding, freeze)
|
354
|
+
errors =
|
355
|
+
Array.new(load_varuint) do
|
356
|
+
error =
|
357
|
+
ParseError.new(
|
358
|
+
DIAGNOSTIC_TYPES.fetch(load_varuint),
|
359
|
+
load_embedded_string(encoding),
|
360
|
+
load_location_object(freeze),
|
361
|
+
load_error_level
|
362
|
+
)
|
363
|
+
|
364
|
+
error.freeze if freeze
|
365
|
+
error
|
366
|
+
end
|
367
|
+
|
368
|
+
errors.freeze if freeze
|
369
|
+
errors
|
370
|
+
end
|
371
|
+
|
372
|
+
def load_warning_level
|
373
|
+
level = io.getbyte
|
374
|
+
|
375
|
+
case level
|
376
|
+
when 0
|
377
|
+
:default
|
378
|
+
when 1
|
379
|
+
:verbose
|
380
|
+
else
|
381
|
+
raise "Unknown level: #{level}"
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
def load_warnings(encoding, freeze)
|
386
|
+
warnings =
|
387
|
+
Array.new(load_varuint) do
|
388
|
+
warning =
|
389
|
+
ParseWarning.new(
|
390
|
+
DIAGNOSTIC_TYPES.fetch(load_varuint),
|
391
|
+
load_embedded_string(encoding),
|
392
|
+
load_location_object(freeze),
|
393
|
+
load_warning_level
|
394
|
+
)
|
395
|
+
|
396
|
+
warning.freeze if freeze
|
397
|
+
warning
|
398
|
+
end
|
399
|
+
|
400
|
+
warnings.freeze if freeze
|
401
|
+
warnings
|
402
|
+
end
|
403
|
+
|
404
|
+
def load_tokens
|
405
|
+
tokens = []
|
406
|
+
|
407
|
+
while (type = TOKEN_TYPES.fetch(load_varuint))
|
408
|
+
start = load_varuint
|
409
|
+
length = load_varuint
|
410
|
+
lex_state = load_varuint
|
411
|
+
|
412
|
+
location = Location.new(@source, start, length)
|
413
|
+
token = Token.new(@source, type, location.slice, location)
|
414
|
+
|
415
|
+
tokens << [token, lex_state]
|
416
|
+
end
|
417
|
+
|
418
|
+
tokens
|
419
|
+
end
|
420
|
+
|
421
|
+
# variable-length integer using https://en.wikipedia.org/wiki/LEB128
|
422
|
+
# This is also what protobuf uses: https://protobuf.dev/programming-guides/encoding/#varints
|
423
|
+
def load_varuint
|
424
|
+
n = io.getbyte
|
425
|
+
if n < 128
|
426
|
+
n
|
427
|
+
else
|
428
|
+
n -= 128
|
429
|
+
shift = 0
|
430
|
+
while (b = io.getbyte) >= 128
|
431
|
+
n += (b - 128) << (shift += 7)
|
432
|
+
end
|
433
|
+
n + (b << (shift + 7))
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
def load_varsint
|
438
|
+
n = load_varuint
|
439
|
+
(n >> 1) ^ (-(n & 1))
|
440
|
+
end
|
441
|
+
|
442
|
+
def load_integer
|
443
|
+
negative = io.getbyte != 0
|
444
|
+
length = load_varuint
|
445
|
+
|
446
|
+
value = 0
|
447
|
+
length.times { |index| value |= (load_varuint << (index * 32)) }
|
448
|
+
|
449
|
+
value = -value if negative
|
450
|
+
value
|
451
|
+
end
|
452
|
+
|
453
|
+
def load_double
|
454
|
+
io.read(8).unpack1("D")
|
455
|
+
end
|
456
|
+
|
457
|
+
def load_uint32
|
458
|
+
io.read(4).unpack1("L")
|
459
|
+
end
|
460
|
+
|
461
|
+
def load_optional_node(constant_pool, encoding, freeze)
|
462
|
+
if io.getbyte != 0
|
463
|
+
io.pos -= 1
|
464
|
+
load_node(constant_pool, encoding, freeze)
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
def load_embedded_string(encoding)
|
469
|
+
io.read(load_varuint).force_encoding(encoding).freeze
|
470
|
+
end
|
471
|
+
|
472
|
+
def load_string(encoding)
|
473
|
+
case (type = io.getbyte)
|
474
|
+
when 1
|
475
|
+
input.byteslice(load_varuint, load_varuint).force_encoding(encoding).freeze
|
476
|
+
when 2
|
477
|
+
load_embedded_string(encoding)
|
478
|
+
else
|
479
|
+
raise "Unknown serialized string type: #{type}"
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
def load_location_object(freeze)
|
484
|
+
location = Location.new(source, load_varuint, load_varuint)
|
485
|
+
location.freeze if freeze
|
486
|
+
location
|
487
|
+
end
|
488
|
+
|
489
|
+
def load_location(freeze)
|
490
|
+
return load_location_object(freeze) if freeze
|
491
|
+
(load_varuint << 32) | load_varuint
|
492
|
+
end
|
493
|
+
|
494
|
+
def load_optional_location(freeze)
|
495
|
+
load_location(freeze) if io.getbyte != 0
|
496
|
+
end
|
497
|
+
|
498
|
+
def load_optional_location_object(freeze)
|
499
|
+
load_location_object(freeze) if io.getbyte != 0
|
500
|
+
end
|
501
|
+
|
502
|
+
def load_constant(constant_pool, encoding)
|
503
|
+
index = load_varuint
|
504
|
+
constant_pool.get(index - 1, encoding)
|
505
|
+
end
|
506
|
+
|
507
|
+
def load_optional_constant(constant_pool, encoding)
|
508
|
+
index = load_varuint
|
509
|
+
constant_pool.get(index - 1, encoding) if index != 0
|
510
|
+
end
|
511
|
+
|
512
|
+
if RUBY_ENGINE == "ruby"
|
513
|
+
def load_node(constant_pool, encoding, freeze)
|
514
|
+
type = io.getbyte
|
515
|
+
node_id = load_varuint
|
516
|
+
location = load_location(freeze)
|
517
|
+
value = case type
|
518
|
+
<%- nodes.each_with_index do |node, index| -%>
|
519
|
+
when <%= index + 1 %> then
|
520
|
+
<%- if node.needs_serialized_length? -%>
|
521
|
+
load_uint32
|
522
|
+
<%- end -%>
|
523
|
+
<%= node.name %>.new(<%= ["source", "node_id", "location", "load_varuint", *node.fields.map { |field|
|
524
|
+
case field
|
525
|
+
when Prism::Template::NodeField then "load_node(constant_pool, encoding, freeze)"
|
526
|
+
when Prism::Template::OptionalNodeField then "load_optional_node(constant_pool, encoding, freeze)"
|
527
|
+
when Prism::Template::StringField then "load_string(encoding)"
|
528
|
+
when Prism::Template::NodeListField then "Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }"
|
529
|
+
when Prism::Template::ConstantField then "load_constant(constant_pool, encoding)"
|
530
|
+
when Prism::Template::OptionalConstantField then "load_optional_constant(constant_pool, encoding)"
|
531
|
+
when Prism::Template::ConstantListField then "Array.new(load_varuint) { load_constant(constant_pool, encoding) }.tap { |constants| constants.freeze if freeze }"
|
532
|
+
when Prism::Template::LocationField then "load_location(freeze)"
|
533
|
+
when Prism::Template::OptionalLocationField then "load_optional_location(freeze)"
|
534
|
+
when Prism::Template::UInt8Field then "io.getbyte"
|
535
|
+
when Prism::Template::UInt32Field then "load_varuint"
|
536
|
+
when Prism::Template::IntegerField then "load_integer"
|
537
|
+
when Prism::Template::DoubleField then "load_double"
|
538
|
+
else raise
|
539
|
+
end
|
540
|
+
}].join(", ") -%>)
|
541
|
+
<%- end -%>
|
542
|
+
end
|
543
|
+
|
544
|
+
value.freeze if freeze
|
545
|
+
value
|
546
|
+
end
|
547
|
+
else
|
548
|
+
def load_node(constant_pool, encoding, freeze)
|
549
|
+
@load_node_lambdas[io.getbyte].call(constant_pool, encoding, freeze)
|
550
|
+
end
|
551
|
+
|
552
|
+
def define_load_node_lambdas
|
553
|
+
@load_node_lambdas = [
|
554
|
+
nil,
|
555
|
+
<%- nodes.each do |node| -%>
|
556
|
+
-> (constant_pool, encoding, freeze) {
|
557
|
+
node_id = load_varuint
|
558
|
+
location = load_location(freeze)
|
559
|
+
<%- if node.needs_serialized_length? -%>
|
560
|
+
load_uint32
|
561
|
+
<%- end -%>
|
562
|
+
value = <%= node.name %>.new(<%= ["source", "node_id", "location", "load_varuint", *node.fields.map { |field|
|
563
|
+
case field
|
564
|
+
when Prism::Template::NodeField then "load_node(constant_pool, encoding, freeze)"
|
565
|
+
when Prism::Template::OptionalNodeField then "load_optional_node(constant_pool, encoding, freeze)"
|
566
|
+
when Prism::Template::StringField then "load_string(encoding)"
|
567
|
+
when Prism::Template::NodeListField then "Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }"
|
568
|
+
when Prism::Template::ConstantField then "load_constant(constant_pool, encoding)"
|
569
|
+
when Prism::Template::OptionalConstantField then "load_optional_constant(constant_pool, encoding)"
|
570
|
+
when Prism::Template::ConstantListField then "Array.new(load_varuint) { load_constant(constant_pool, encoding) }"
|
571
|
+
when Prism::Template::LocationField then "load_location(freeze)"
|
572
|
+
when Prism::Template::OptionalLocationField then "load_optional_location(freeze)"
|
573
|
+
when Prism::Template::UInt8Field then "io.getbyte"
|
574
|
+
when Prism::Template::UInt32Field then "load_varuint"
|
575
|
+
when Prism::Template::IntegerField then "load_integer"
|
576
|
+
when Prism::Template::DoubleField then "load_double"
|
577
|
+
else raise
|
578
|
+
end
|
579
|
+
}].join(", ") -%>)
|
580
|
+
value.freeze if freeze
|
581
|
+
value
|
582
|
+
},
|
583
|
+
<%- end -%>
|
584
|
+
]
|
585
|
+
end
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
# The token types that can be indexed by their enum values.
|
590
|
+
TOKEN_TYPES = [
|
591
|
+
nil,
|
592
|
+
<%- tokens.each do |token| -%>
|
593
|
+
<%= token.name.to_sym.inspect %>,
|
594
|
+
<%- end -%>
|
595
|
+
].freeze
|
596
|
+
|
597
|
+
private_constant :MAJOR_VERSION, :MINOR_VERSION, :PATCH_VERSION
|
598
|
+
private_constant :ConstantPool, :FastStringIO, :Loader, :TOKEN_TYPES
|
599
|
+
end
|
600
|
+
|
601
|
+
private_constant :Serialize
|
602
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Prism
|
2
|
+
# A class that knows how to walk down the tree. None of the individual visit
|
3
|
+
# methods are implemented on this visitor, so it forces the consumer to
|
4
|
+
# implement each one that they need. For a default implementation that
|
5
|
+
# continues walking the tree, see the Visitor class.
|
6
|
+
class BasicVisitor
|
7
|
+
# Calls `accept` on the given node if it is not `nil`, which in turn should
|
8
|
+
# call back into this visitor by calling the appropriate `visit_*` method.
|
9
|
+
def visit(node)
|
10
|
+
# @type self: _Visitor
|
11
|
+
node&.accept(self)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Visits each node in `nodes` by calling `accept` on each one.
|
15
|
+
def visit_all(nodes)
|
16
|
+
# @type self: _Visitor
|
17
|
+
nodes.each { |node| node&.accept(self) }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Visits the child nodes of `node` by calling `accept` on each one.
|
21
|
+
def visit_child_nodes(node)
|
22
|
+
# @type self: _Visitor
|
23
|
+
node.compact_child_nodes.each { |node| node.accept(self) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# A visitor is a class that provides a default implementation for every accept
|
28
|
+
# method defined on the nodes. This means it can walk a tree without the
|
29
|
+
# caller needing to define any special handling. This allows you to handle a
|
30
|
+
# subset of the tree, while still walking the whole tree.
|
31
|
+
#
|
32
|
+
# For example, to find all of the method calls that call the `foo` method, you
|
33
|
+
# could write:
|
34
|
+
#
|
35
|
+
# class FooCalls < Prism::Visitor
|
36
|
+
# def visit_call_node(node)
|
37
|
+
# if node.name == :foo
|
38
|
+
# # Do something with the node
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# # Call super so that the visitor continues walking the tree
|
42
|
+
# super
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
class Visitor < BasicVisitor
|
47
|
+
<%- nodes.each_with_index do |node, index| -%>
|
48
|
+
<%= "\n" if index != 0 -%>
|
49
|
+
# Visit a <%= node.name %> node
|
50
|
+
def visit_<%= node.human %>(node)
|
51
|
+
node.compact_child_nodes.each { |node| node.accept(self) }
|
52
|
+
end
|
53
|
+
<%- end -%>
|
54
|
+
end
|
55
|
+
end
|