yarp 0.12.0 → 0.13.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
- data/CHANGELOG.md +29 -8
- data/CONTRIBUTING.md +2 -2
- data/Makefile +5 -5
- data/README.md +11 -12
- data/config.yml +6 -2
- data/docs/build_system.md +21 -21
- data/docs/building.md +4 -4
- data/docs/configuration.md +25 -21
- data/docs/design.md +2 -2
- data/docs/encoding.md +17 -17
- data/docs/fuzzing.md +4 -4
- data/docs/heredocs.md +3 -3
- data/docs/mapping.md +94 -94
- data/docs/ripper.md +4 -4
- data/docs/ruby_api.md +11 -11
- data/docs/serialization.md +17 -16
- data/docs/testing.md +6 -6
- data/ext/prism/api_node.c +4725 -0
- data/ext/{yarp → prism}/api_pack.c +82 -82
- data/ext/{yarp → prism}/extconf.rb +13 -13
- data/ext/{yarp → prism}/extension.c +175 -168
- data/ext/prism/extension.h +18 -0
- data/include/prism/ast.h +1932 -0
- data/include/prism/defines.h +45 -0
- data/include/prism/diagnostic.h +231 -0
- data/include/{yarp/enc/yp_encoding.h → prism/enc/pm_encoding.h} +40 -40
- data/include/prism/node.h +41 -0
- data/include/prism/pack.h +141 -0
- data/include/{yarp → prism}/parser.h +143 -142
- data/include/prism/regexp.h +19 -0
- data/include/prism/unescape.h +48 -0
- data/include/prism/util/pm_buffer.h +51 -0
- data/include/{yarp/util/yp_char.h → prism/util/pm_char.h} +20 -20
- data/include/{yarp/util/yp_constant_pool.h → prism/util/pm_constant_pool.h} +26 -22
- data/include/{yarp/util/yp_list.h → prism/util/pm_list.h} +21 -21
- data/include/prism/util/pm_memchr.h +14 -0
- data/include/{yarp/util/yp_newline_list.h → prism/util/pm_newline_list.h} +11 -11
- data/include/prism/util/pm_state_stack.h +24 -0
- data/include/{yarp/util/yp_string.h → prism/util/pm_string.h} +20 -20
- data/include/prism/util/pm_string_list.h +25 -0
- data/include/{yarp/util/yp_strpbrk.h → prism/util/pm_strpbrk.h} +7 -7
- data/include/prism/version.h +4 -0
- data/include/prism.h +82 -0
- data/lib/prism/compiler.rb +465 -0
- data/lib/prism/debug.rb +157 -0
- data/lib/{yarp/desugar_visitor.rb → prism/desugar_compiler.rb} +4 -2
- data/lib/prism/dispatcher.rb +2051 -0
- data/lib/prism/dsl.rb +750 -0
- data/lib/{yarp → prism}/ffi.rb +66 -67
- data/lib/{yarp → prism}/lex_compat.rb +40 -43
- data/lib/{yarp/mutation_visitor.rb → prism/mutation_compiler.rb} +3 -3
- data/lib/{yarp → prism}/node.rb +2012 -2593
- data/lib/prism/node_ext.rb +55 -0
- data/lib/prism/node_inspector.rb +68 -0
- data/lib/{yarp → prism}/pack.rb +1 -1
- data/lib/{yarp → prism}/parse_result/comments.rb +1 -1
- data/lib/{yarp → prism}/parse_result/newlines.rb +1 -1
- data/lib/prism/parse_result.rb +266 -0
- data/lib/{yarp → prism}/pattern.rb +14 -14
- data/lib/{yarp → prism}/ripper_compat.rb +5 -5
- data/lib/{yarp → prism}/serialize.rb +12 -7
- data/lib/prism/visitor.rb +470 -0
- data/lib/prism.rb +64 -0
- data/lib/yarp.rb +2 -614
- data/src/diagnostic.c +213 -208
- data/src/enc/pm_big5.c +52 -0
- data/src/enc/pm_euc_jp.c +58 -0
- data/src/enc/{yp_gbk.c → pm_gbk.c} +16 -16
- data/src/enc/pm_shift_jis.c +56 -0
- data/src/enc/{yp_tables.c → pm_tables.c} +69 -69
- data/src/enc/{yp_unicode.c → pm_unicode.c} +40 -40
- data/src/enc/pm_windows_31j.c +56 -0
- data/src/node.c +1293 -1233
- data/src/pack.c +247 -247
- data/src/prettyprint.c +1479 -1479
- data/src/{yarp.c → prism.c} +5205 -5083
- data/src/regexp.c +132 -132
- data/src/serialize.c +1121 -1121
- data/src/token_type.c +169 -167
- data/src/unescape.c +106 -87
- data/src/util/pm_buffer.c +103 -0
- data/src/util/{yp_char.c → pm_char.c} +72 -72
- data/src/util/{yp_constant_pool.c → pm_constant_pool.c} +85 -64
- data/src/util/{yp_list.c → pm_list.c} +10 -10
- data/src/util/{yp_memchr.c → pm_memchr.c} +6 -4
- data/src/util/{yp_newline_list.c → pm_newline_list.c} +21 -21
- data/src/util/{yp_state_stack.c → pm_state_stack.c} +4 -4
- data/src/util/{yp_string.c → pm_string.c} +38 -38
- data/src/util/pm_string_list.c +29 -0
- data/src/util/{yp_strncasecmp.c → pm_strncasecmp.c} +1 -1
- data/src/util/{yp_strpbrk.c → pm_strpbrk.c} +8 -8
- data/yarp.gemspec +68 -59
- metadata +70 -61
- data/ext/yarp/api_node.c +0 -4728
- data/ext/yarp/extension.h +0 -18
- data/include/yarp/ast.h +0 -1929
- data/include/yarp/defines.h +0 -45
- data/include/yarp/diagnostic.h +0 -226
- data/include/yarp/node.h +0 -42
- data/include/yarp/pack.h +0 -141
- data/include/yarp/regexp.h +0 -19
- data/include/yarp/unescape.h +0 -44
- data/include/yarp/util/yp_buffer.h +0 -51
- data/include/yarp/util/yp_memchr.h +0 -14
- data/include/yarp/util/yp_state_stack.h +0 -24
- data/include/yarp/util/yp_string_list.h +0 -25
- data/include/yarp/version.h +0 -4
- data/include/yarp.h +0 -82
- data/src/enc/yp_big5.c +0 -52
- data/src/enc/yp_euc_jp.c +0 -58
- data/src/enc/yp_shift_jis.c +0 -56
- data/src/enc/yp_windows_31j.c +0 -56
- data/src/util/yp_buffer.c +0 -101
- data/src/util/yp_string_list.c +0 -29
data/lib/yarp.rb
CHANGED
|
@@ -1,616 +1,4 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
# conjunction with locations to allow them to resolve line numbers and source
|
|
6
|
-
# ranges.
|
|
7
|
-
class Source
|
|
8
|
-
attr_reader :source, :offsets
|
|
9
|
-
|
|
10
|
-
def initialize(source, offsets = compute_offsets(source))
|
|
11
|
-
@source = source
|
|
12
|
-
@offsets = offsets
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def slice(offset, length)
|
|
16
|
-
source.byteslice(offset, length)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def line(value)
|
|
20
|
-
offsets.bsearch_index { |offset| offset > value } || offsets.length
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def line_offset(value)
|
|
24
|
-
offsets[line(value) - 1]
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def column(value)
|
|
28
|
-
value - offsets[line(value) - 1]
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
private
|
|
32
|
-
|
|
33
|
-
def compute_offsets(code)
|
|
34
|
-
offsets = [0]
|
|
35
|
-
code.b.scan("\n") { offsets << $~.end(0) }
|
|
36
|
-
offsets
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# This represents a location in the source.
|
|
41
|
-
class Location
|
|
42
|
-
# A Source object that is used to determine more information from the given
|
|
43
|
-
# offset and length.
|
|
44
|
-
protected attr_reader :source
|
|
45
|
-
|
|
46
|
-
# The byte offset from the beginning of the source where this location
|
|
47
|
-
# starts.
|
|
48
|
-
attr_reader :start_offset
|
|
49
|
-
|
|
50
|
-
# The length of this location in bytes.
|
|
51
|
-
attr_reader :length
|
|
52
|
-
|
|
53
|
-
# The list of comments attached to this location
|
|
54
|
-
attr_reader :comments
|
|
55
|
-
|
|
56
|
-
def initialize(source, start_offset, length)
|
|
57
|
-
@source = source
|
|
58
|
-
@start_offset = start_offset
|
|
59
|
-
@length = length
|
|
60
|
-
@comments = []
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# Create a new location object with the given options.
|
|
64
|
-
def copy(**options)
|
|
65
|
-
Location.new(
|
|
66
|
-
options.fetch(:source) { source },
|
|
67
|
-
options.fetch(:start_offset) { start_offset },
|
|
68
|
-
options.fetch(:length) { length }
|
|
69
|
-
)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Returns a string representation of this location.
|
|
73
|
-
def inspect
|
|
74
|
-
"#<YARP::Location @start_offset=#{@start_offset} @length=#{@length}>"
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# The source code that this location represents.
|
|
78
|
-
def slice
|
|
79
|
-
source.slice(start_offset, length)
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# The byte offset from the beginning of the source where this location ends.
|
|
83
|
-
def end_offset
|
|
84
|
-
start_offset + length
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# The line number where this location starts.
|
|
88
|
-
def start_line
|
|
89
|
-
source.line(start_offset)
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# The content of the line where this location starts before this location.
|
|
93
|
-
def start_line_slice
|
|
94
|
-
offset = source.line_offset(start_offset)
|
|
95
|
-
source.slice(offset, start_offset - offset)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# The line number where this location ends.
|
|
99
|
-
def end_line
|
|
100
|
-
source.line(end_offset - 1)
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
# The column number in bytes where this location starts from the start of
|
|
104
|
-
# the line.
|
|
105
|
-
def start_column
|
|
106
|
-
source.column(start_offset)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# The column number in bytes where this location ends from the start of the
|
|
110
|
-
# line.
|
|
111
|
-
def end_column
|
|
112
|
-
source.column(end_offset)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def deconstruct_keys(keys)
|
|
116
|
-
{ start_offset: start_offset, end_offset: end_offset }
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def pretty_print(q)
|
|
120
|
-
q.text("(#{start_offset}...#{end_offset})")
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def ==(other)
|
|
124
|
-
other.is_a?(Location) &&
|
|
125
|
-
other.start_offset == start_offset &&
|
|
126
|
-
other.end_offset == end_offset
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# Returns a new location that stretches from this location to the given
|
|
130
|
-
# other location. Raises an error if this location is not before the other
|
|
131
|
-
# location or if they don't share the same source.
|
|
132
|
-
def join(other)
|
|
133
|
-
raise "Incompatible sources" if source != other.source
|
|
134
|
-
raise "Incompatible locations" if start_offset > other.start_offset
|
|
135
|
-
|
|
136
|
-
Location.new(source, start_offset, other.end_offset - start_offset)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def self.null
|
|
140
|
-
new(0, 0)
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# This represents a comment that was encountered during parsing.
|
|
145
|
-
class Comment
|
|
146
|
-
TYPES = [:inline, :embdoc, :__END__]
|
|
147
|
-
|
|
148
|
-
attr_reader :type, :location
|
|
149
|
-
|
|
150
|
-
def initialize(type, location)
|
|
151
|
-
@type = type
|
|
152
|
-
@location = location
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def deconstruct_keys(keys)
|
|
156
|
-
{ type: type, location: location }
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# Returns true if the comment happens on the same line as other code and false if the comment is by itself
|
|
160
|
-
def trailing?
|
|
161
|
-
type == :inline && !location.start_line_slice.strip.empty?
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def inspect
|
|
165
|
-
"#<YARP::Comment @type=#{@type.inspect} @location=#{@location.inspect}>"
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
# This represents an error that was encountered during parsing.
|
|
170
|
-
class ParseError
|
|
171
|
-
attr_reader :message, :location
|
|
172
|
-
|
|
173
|
-
def initialize(message, location)
|
|
174
|
-
@message = message
|
|
175
|
-
@location = location
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def deconstruct_keys(keys)
|
|
179
|
-
{ message: message, location: location }
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def inspect
|
|
183
|
-
"#<YARP::ParseError @message=#{@message.inspect} @location=#{@location.inspect}>"
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
# This represents a warning that was encountered during parsing.
|
|
188
|
-
class ParseWarning
|
|
189
|
-
attr_reader :message, :location
|
|
190
|
-
|
|
191
|
-
def initialize(message, location)
|
|
192
|
-
@message = message
|
|
193
|
-
@location = location
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
def deconstruct_keys(keys)
|
|
197
|
-
{ message: message, location: location }
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
def inspect
|
|
201
|
-
"#<YARP::ParseWarning @message=#{@message.inspect} @location=#{@location.inspect}>"
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
# A class that knows how to walk down the tree. None of the individual visit
|
|
206
|
-
# methods are implemented on this visitor, so it forces the consumer to
|
|
207
|
-
# implement each one that they need. For a default implementation that
|
|
208
|
-
# continues walking the tree, see the Visitor class.
|
|
209
|
-
class BasicVisitor
|
|
210
|
-
def visit(node)
|
|
211
|
-
node&.accept(self)
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
def visit_all(nodes)
|
|
215
|
-
nodes.map { |node| visit(node) }
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
def visit_child_nodes(node)
|
|
219
|
-
visit_all(node.child_nodes)
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
class Visitor < BasicVisitor
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
# This represents the result of a call to ::parse or ::parse_file. It contains
|
|
227
|
-
# the AST, any comments that were encounters, and any errors that were
|
|
228
|
-
# encountered.
|
|
229
|
-
class ParseResult
|
|
230
|
-
attr_reader :value, :comments, :errors, :warnings, :source
|
|
231
|
-
|
|
232
|
-
def initialize(value, comments, errors, warnings, source)
|
|
233
|
-
@value = value
|
|
234
|
-
@comments = comments
|
|
235
|
-
@errors = errors
|
|
236
|
-
@warnings = warnings
|
|
237
|
-
@source = source
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
def deconstruct_keys(keys)
|
|
241
|
-
{ value: value, comments: comments, errors: errors, warnings: warnings }
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
def success?
|
|
245
|
-
errors.empty?
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def failure?
|
|
249
|
-
!success?
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
# This represents a token from the Ruby source.
|
|
254
|
-
class Token
|
|
255
|
-
attr_reader :type, :value, :location
|
|
256
|
-
|
|
257
|
-
def initialize(type, value, location)
|
|
258
|
-
@type = type
|
|
259
|
-
@value = value
|
|
260
|
-
@location = location
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
def deconstruct_keys(keys)
|
|
264
|
-
{ type: type, value: value, location: location }
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
def pretty_print(q)
|
|
268
|
-
q.group do
|
|
269
|
-
q.text(type.to_s)
|
|
270
|
-
self.location.pretty_print(q)
|
|
271
|
-
q.text("(")
|
|
272
|
-
q.nest(2) do
|
|
273
|
-
q.breakable("")
|
|
274
|
-
q.pp(value)
|
|
275
|
-
end
|
|
276
|
-
q.breakable("")
|
|
277
|
-
q.text(")")
|
|
278
|
-
end
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
def ==(other)
|
|
282
|
-
other.is_a?(Token) &&
|
|
283
|
-
other.type == type &&
|
|
284
|
-
other.value == value
|
|
285
|
-
end
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
# This represents a node in the tree.
|
|
289
|
-
class Node
|
|
290
|
-
attr_reader :location
|
|
291
|
-
|
|
292
|
-
def newline?
|
|
293
|
-
@newline ? true : false
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
def set_newline_flag(newline_marked)
|
|
297
|
-
line = location.start_line
|
|
298
|
-
unless newline_marked[line]
|
|
299
|
-
newline_marked[line] = true
|
|
300
|
-
@newline = true
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
# Slice the location of the node from the source.
|
|
305
|
-
def slice
|
|
306
|
-
location.slice
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
def pretty_print(q)
|
|
310
|
-
q.text(inspect.chomp)
|
|
311
|
-
end
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
# This object is responsible for generating the output for the inspect method
|
|
315
|
-
# implementations of child nodes.
|
|
316
|
-
class NodeInspector
|
|
317
|
-
attr_reader :prefix, :output
|
|
318
|
-
|
|
319
|
-
def initialize(prefix = "")
|
|
320
|
-
@prefix = prefix
|
|
321
|
-
@output = +""
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
# Appends a line to the output with the current prefix.
|
|
325
|
-
def <<(line)
|
|
326
|
-
output << "#{prefix}#{line}"
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
# This generates a string that is used as the header of the inspect output
|
|
330
|
-
# for any given node.
|
|
331
|
-
def header(node)
|
|
332
|
-
output = +"@ #{node.class.name.split("::").last} ("
|
|
333
|
-
output << "location: (#{node.location.start_offset}...#{node.location.end_offset})"
|
|
334
|
-
output << ", newline: true" if node.newline?
|
|
335
|
-
output << ")\n"
|
|
336
|
-
output
|
|
337
|
-
end
|
|
338
|
-
|
|
339
|
-
# Generates a string that represents a list of nodes. It handles properly
|
|
340
|
-
# using the box drawing characters to make the output look nice.
|
|
341
|
-
def list(prefix, nodes)
|
|
342
|
-
output = +"(length: #{nodes.length})\n"
|
|
343
|
-
last_index = nodes.length - 1
|
|
344
|
-
|
|
345
|
-
nodes.each_with_index do |node, index|
|
|
346
|
-
pointer, preadd = (index == last_index) ? ["└── ", " "] : ["├── ", "│ "]
|
|
347
|
-
node_prefix = "#{prefix}#{preadd}"
|
|
348
|
-
output << node.inspect(NodeInspector.new(node_prefix)).sub(node_prefix, "#{prefix}#{pointer}")
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
output
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
# Generates a string that represents a location field on a node.
|
|
355
|
-
def location(value)
|
|
356
|
-
if value
|
|
357
|
-
"(#{value.start_offset}...#{value.end_offset}) = #{value.slice.inspect}"
|
|
358
|
-
else
|
|
359
|
-
"∅"
|
|
360
|
-
end
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
# Generates a string that represents a child node.
|
|
364
|
-
def child_node(node, append)
|
|
365
|
-
node.inspect(child_inspector(append)).delete_prefix(prefix)
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
# Returns a new inspector that can be used to inspect a child node.
|
|
369
|
-
def child_inspector(append)
|
|
370
|
-
NodeInspector.new("#{prefix}#{append}")
|
|
371
|
-
end
|
|
372
|
-
|
|
373
|
-
# Returns the output as a string.
|
|
374
|
-
def to_str
|
|
375
|
-
output
|
|
376
|
-
end
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
# Load the serialized AST using the source as a reference into a tree.
|
|
380
|
-
def self.load(source, serialized)
|
|
381
|
-
Serialize.load(source, serialized)
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
# This module is used for testing and debugging and is not meant to be used by
|
|
385
|
-
# consumers of this library.
|
|
386
|
-
module Debug
|
|
387
|
-
class ISeq
|
|
388
|
-
attr_reader :parts
|
|
389
|
-
|
|
390
|
-
def initialize(parts)
|
|
391
|
-
@parts = parts
|
|
392
|
-
end
|
|
393
|
-
|
|
394
|
-
def type
|
|
395
|
-
parts[0]
|
|
396
|
-
end
|
|
397
|
-
|
|
398
|
-
def local_table
|
|
399
|
-
parts[10]
|
|
400
|
-
end
|
|
401
|
-
|
|
402
|
-
def instructions
|
|
403
|
-
parts[13]
|
|
404
|
-
end
|
|
405
|
-
|
|
406
|
-
def each_child
|
|
407
|
-
instructions.each do |instruction|
|
|
408
|
-
# Only look at arrays. Other instructions are line numbers or
|
|
409
|
-
# tracepoint events.
|
|
410
|
-
next unless instruction.is_a?(Array)
|
|
411
|
-
|
|
412
|
-
instruction.each do |opnd|
|
|
413
|
-
# Only look at arrays. Other operands are literals.
|
|
414
|
-
next unless opnd.is_a?(Array)
|
|
415
|
-
|
|
416
|
-
# Only look at instruction sequences. Other operands are literals.
|
|
417
|
-
next unless opnd[0] == "YARVInstructionSequence/SimpleDataFormat"
|
|
418
|
-
|
|
419
|
-
yield ISeq.new(opnd)
|
|
420
|
-
end
|
|
421
|
-
end
|
|
422
|
-
end
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
# For the given source, compiles with CRuby and returns a list of all of the
|
|
426
|
-
# sets of local variables that were encountered.
|
|
427
|
-
def self.cruby_locals(source)
|
|
428
|
-
verbose = $VERBOSE
|
|
429
|
-
$VERBOSE = nil
|
|
430
|
-
|
|
431
|
-
begin
|
|
432
|
-
locals = []
|
|
433
|
-
stack = [ISeq.new(RubyVM::InstructionSequence.compile(source).to_a)]
|
|
434
|
-
|
|
435
|
-
while (iseq = stack.pop)
|
|
436
|
-
if iseq.type != :once
|
|
437
|
-
names = iseq.local_table
|
|
438
|
-
|
|
439
|
-
# CRuby will push on a special local variable when there are keyword
|
|
440
|
-
# arguments. We get rid of that here.
|
|
441
|
-
names = names.grep_v(Integer)
|
|
442
|
-
|
|
443
|
-
# For some reason, CRuby occasionally pushes this special local
|
|
444
|
-
# variable when there are splat arguments. We get rid of that here.
|
|
445
|
-
names = names.grep_v(:"#arg_rest")
|
|
446
|
-
|
|
447
|
-
# Now push them onto the list of locals.
|
|
448
|
-
locals << names
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
iseq.each_child { |child| stack << child }
|
|
452
|
-
end
|
|
453
|
-
|
|
454
|
-
locals
|
|
455
|
-
ensure
|
|
456
|
-
$VERBOSE = verbose
|
|
457
|
-
end
|
|
458
|
-
end
|
|
459
|
-
|
|
460
|
-
# For the given source, parses with YARP and returns a list of all of the
|
|
461
|
-
# sets of local variables that were encountered.
|
|
462
|
-
def self.yarp_locals(source)
|
|
463
|
-
locals = []
|
|
464
|
-
stack = [YARP.parse(source).value]
|
|
465
|
-
|
|
466
|
-
while (node = stack.pop)
|
|
467
|
-
case node
|
|
468
|
-
when BlockNode, DefNode, LambdaNode
|
|
469
|
-
names = node.locals
|
|
470
|
-
|
|
471
|
-
params = node.parameters
|
|
472
|
-
params = params&.parameters unless node.is_a?(DefNode)
|
|
473
|
-
|
|
474
|
-
# YARP places parameters in the same order that they appear in the
|
|
475
|
-
# source. CRuby places them in the order that they need to appear
|
|
476
|
-
# according to their own internal calling convention. We mimic that
|
|
477
|
-
# order here so that we can compare properly.
|
|
478
|
-
if params
|
|
479
|
-
sorted = [
|
|
480
|
-
*params.requireds.grep(RequiredParameterNode).map(&:name),
|
|
481
|
-
*params.optionals.map(&:name),
|
|
482
|
-
*((params.rest.name || :*) if params.rest && params.rest.operator != ","),
|
|
483
|
-
*params.posts.grep(RequiredParameterNode).map(&:name),
|
|
484
|
-
*params.keywords.reject(&:value).map(&:name),
|
|
485
|
-
*params.keywords.select(&:value).map(&:name)
|
|
486
|
-
]
|
|
487
|
-
|
|
488
|
-
# TODO: When we get a ... parameter, we should be pushing * and &
|
|
489
|
-
# onto the local list. We don't do that yet, so we need to add them
|
|
490
|
-
# in here.
|
|
491
|
-
if params.keyword_rest.is_a?(ForwardingParameterNode)
|
|
492
|
-
sorted.push(:*, :&, :"...")
|
|
493
|
-
end
|
|
494
|
-
|
|
495
|
-
# Recurse down the parameter tree to find any destructured
|
|
496
|
-
# parameters and add them after the other parameters.
|
|
497
|
-
param_stack = params.requireds.concat(params.posts).grep(RequiredDestructuredParameterNode).reverse
|
|
498
|
-
while (param = param_stack.pop)
|
|
499
|
-
case param
|
|
500
|
-
when RequiredDestructuredParameterNode
|
|
501
|
-
param_stack.concat(param.parameters.reverse)
|
|
502
|
-
when RequiredParameterNode
|
|
503
|
-
sorted << param.name
|
|
504
|
-
when SplatNode
|
|
505
|
-
sorted << param.expression.name if param.expression
|
|
506
|
-
end
|
|
507
|
-
end
|
|
508
|
-
|
|
509
|
-
names = sorted.concat(names - sorted)
|
|
510
|
-
end
|
|
511
|
-
|
|
512
|
-
locals << names
|
|
513
|
-
when ClassNode, ModuleNode, ProgramNode, SingletonClassNode
|
|
514
|
-
locals << node.locals
|
|
515
|
-
when ForNode
|
|
516
|
-
locals << []
|
|
517
|
-
when PostExecutionNode
|
|
518
|
-
locals.push([], [])
|
|
519
|
-
when InterpolatedRegularExpressionNode
|
|
520
|
-
locals << [] if node.once?
|
|
521
|
-
end
|
|
522
|
-
|
|
523
|
-
stack.concat(node.child_nodes.compact)
|
|
524
|
-
end
|
|
525
|
-
|
|
526
|
-
locals
|
|
527
|
-
end
|
|
528
|
-
|
|
529
|
-
def self.newlines(source)
|
|
530
|
-
YARP.parse(source).source.offsets
|
|
531
|
-
end
|
|
532
|
-
|
|
533
|
-
def self.parse_serialize_file(filepath)
|
|
534
|
-
parse_serialize_file_metadata(filepath, [filepath.bytesize, filepath.b, 0].pack("LA*L"))
|
|
535
|
-
end
|
|
536
|
-
end
|
|
537
|
-
|
|
538
|
-
# Marking this as private so that consumers don't see it. It makes it a little
|
|
539
|
-
# annoying for testing since you have to const_get it to access the methods,
|
|
540
|
-
# but at least this way it's clear it's not meant for consumers.
|
|
541
|
-
private_constant :Debug
|
|
542
|
-
end
|
|
543
|
-
|
|
544
|
-
require_relative "yarp/lex_compat"
|
|
545
|
-
require_relative "yarp/mutation_visitor"
|
|
546
|
-
require_relative "yarp/desugar_visitor"
|
|
547
|
-
require_relative "yarp/node"
|
|
548
|
-
require_relative "yarp/ripper_compat"
|
|
549
|
-
require_relative "yarp/serialize"
|
|
550
|
-
require_relative "yarp/pack"
|
|
551
|
-
require_relative "yarp/pattern"
|
|
552
|
-
|
|
553
|
-
require_relative "yarp/parse_result/comments"
|
|
554
|
-
require_relative "yarp/parse_result/newlines"
|
|
555
|
-
|
|
556
|
-
if RUBY_ENGINE == "ruby" and !ENV["YARP_FFI_BACKEND"]
|
|
557
|
-
require "yarp/yarp"
|
|
558
|
-
else
|
|
559
|
-
require_relative "yarp/ffi"
|
|
560
|
-
end
|
|
561
|
-
|
|
562
|
-
# Reopening the YARP module after yarp/node is required so that constant
|
|
563
|
-
# reflection APIs will find the constants defined in the node file before these.
|
|
564
|
-
# This block is meant to contain extra APIs we define on YARP nodes that aren't
|
|
565
|
-
# templated and are meant as convenience methods.
|
|
566
|
-
module YARP
|
|
567
|
-
class FloatNode < Node
|
|
568
|
-
# Returns the value of the node as a Ruby Float.
|
|
569
|
-
def value
|
|
570
|
-
Float(slice)
|
|
571
|
-
end
|
|
572
|
-
end
|
|
573
|
-
|
|
574
|
-
class ImaginaryNode < Node
|
|
575
|
-
# Returns the value of the node as a Ruby Complex.
|
|
576
|
-
def value
|
|
577
|
-
Complex(0, numeric.value)
|
|
578
|
-
end
|
|
579
|
-
end
|
|
580
|
-
|
|
581
|
-
class IntegerNode < Node
|
|
582
|
-
# Returns the value of the node as a Ruby Integer.
|
|
583
|
-
def value
|
|
584
|
-
Integer(slice)
|
|
585
|
-
end
|
|
586
|
-
end
|
|
587
|
-
|
|
588
|
-
class InterpolatedRegularExpressionNode < Node
|
|
589
|
-
# Returns a numeric value that represents the flags that were used to create
|
|
590
|
-
# the regular expression.
|
|
591
|
-
def options
|
|
592
|
-
o = flags & 0b111
|
|
593
|
-
o |= Regexp::FIXEDENCODING if flags.anybits?(RegularExpressionFlags::EUC_JP | RegularExpressionFlags::WINDOWS_31J | RegularExpressionFlags::UTF_8)
|
|
594
|
-
o |= Regexp::NOENCODING if flags.anybits?(RegularExpressionFlags::ASCII_8BIT)
|
|
595
|
-
o
|
|
596
|
-
end
|
|
597
|
-
end
|
|
598
|
-
|
|
599
|
-
class RationalNode < Node
|
|
600
|
-
# Returns the value of the node as a Ruby Rational.
|
|
601
|
-
def value
|
|
602
|
-
Rational(slice.chomp("r"))
|
|
603
|
-
end
|
|
604
|
-
end
|
|
605
|
-
|
|
606
|
-
class RegularExpressionNode < Node
|
|
607
|
-
# Returns a numeric value that represents the flags that were used to create
|
|
608
|
-
# the regular expression.
|
|
609
|
-
def options
|
|
610
|
-
o = flags & 0b111
|
|
611
|
-
o |= Regexp::FIXEDENCODING if flags.anybits?(RegularExpressionFlags::EUC_JP | RegularExpressionFlags::WINDOWS_31J | RegularExpressionFlags::UTF_8)
|
|
612
|
-
o |= Regexp::NOENCODING if flags.anybits?(RegularExpressionFlags::ASCII_8BIT)
|
|
613
|
-
o
|
|
614
|
-
end
|
|
615
|
-
end
|
|
616
|
-
end
|
|
3
|
+
require "prism"
|
|
4
|
+
YARP = Prism
|