prism 0.20.0 → 0.22.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 +37 -2
- data/docs/parser_translation.md +1 -1
- data/docs/releasing.md +18 -0
- data/ext/prism/extension.c +44 -12
- data/ext/prism/extension.h +1 -1
- data/include/prism/ast.h +1 -1
- data/include/prism/diagnostic.h +8 -2
- data/include/prism/parser.h +1 -1
- data/include/prism/util/pm_constant_pool.h +11 -0
- data/include/prism/version.h +2 -2
- data/lib/prism/ffi.rb +7 -2
- data/lib/prism/lex_compat.rb +16 -1
- data/lib/prism/node.rb +212 -32
- data/lib/prism/parse_result.rb +2 -1
- data/lib/prism/ripper_compat.rb +98 -20
- data/lib/prism/serialize.rb +3 -1
- data/lib/prism/translation/parser/compiler.rb +104 -97
- data/lib/prism/translation/parser.rb +27 -13
- data/prism.gemspec +2 -2
- data/src/diagnostic.c +10 -4
- data/src/encoding.c +1 -1
- data/src/prism.c +259 -207
- data/src/util/pm_constant_pool.c +25 -0
- data/src/util/pm_string.c +0 -7
- metadata +4 -4
data/lib/prism/parse_result.rb
CHANGED
@@ -91,7 +91,8 @@ module Prism
|
|
91
91
|
class Location
|
92
92
|
# A Source object that is used to determine more information from the given
|
93
93
|
# offset and length.
|
94
|
-
|
94
|
+
attr_reader :source
|
95
|
+
protected :source
|
95
96
|
|
96
97
|
# The byte offset from the beginning of the source where this location
|
97
98
|
# starts.
|
data/lib/prism/ripper_compat.rb
CHANGED
@@ -97,6 +97,8 @@ module Prism
|
|
97
97
|
result.errors.each do |error|
|
98
98
|
on_parse_error(error.message)
|
99
99
|
end
|
100
|
+
|
101
|
+
nil
|
100
102
|
else
|
101
103
|
result.value.accept(self)
|
102
104
|
end
|
@@ -106,41 +108,91 @@ module Prism
|
|
106
108
|
# Visitor methods
|
107
109
|
############################################################################
|
108
110
|
|
111
|
+
# Visit an ArrayNode node.
|
112
|
+
def visit_array_node(node)
|
113
|
+
elements = visit_elements(node.elements) unless node.elements.empty?
|
114
|
+
bounds(node.location)
|
115
|
+
on_array(elements)
|
116
|
+
end
|
117
|
+
|
109
118
|
# Visit a CallNode node.
|
110
119
|
def visit_call_node(node)
|
111
|
-
if
|
112
|
-
|
113
|
-
|
120
|
+
if node.variable_call?
|
121
|
+
if node.message.match?(/^[[:alpha:]_]/)
|
122
|
+
bounds(node.message_loc)
|
123
|
+
return on_vcall(on_ident(node.message))
|
124
|
+
end
|
114
125
|
|
115
|
-
|
116
|
-
|
126
|
+
raise NotImplementedError, "Non-alpha variable call"
|
127
|
+
end
|
128
|
+
|
129
|
+
if node.opening_loc.nil?
|
130
|
+
left = visit(node.receiver)
|
131
|
+
if node.arguments&.arguments&.length == 1
|
132
|
+
right = visit(node.arguments.arguments.first)
|
133
|
+
|
134
|
+
on_binary(left, node.name, right)
|
135
|
+
elsif !node.arguments || node.arguments.empty?
|
136
|
+
on_unary(node.name, left)
|
137
|
+
else
|
138
|
+
raise NotImplementedError, "More than two arguments for operator"
|
139
|
+
end
|
117
140
|
else
|
118
|
-
raise NotImplementedError
|
141
|
+
raise NotImplementedError, "Non-nil opening_loc"
|
119
142
|
end
|
120
143
|
end
|
121
144
|
|
122
145
|
# Visit a FloatNode node.
|
123
146
|
def visit_float_node(node)
|
124
|
-
|
125
|
-
on_float(node.slice)
|
147
|
+
visit_number(node) { |text| on_float(text) }
|
126
148
|
end
|
127
149
|
|
128
150
|
# Visit a ImaginaryNode node.
|
129
151
|
def visit_imaginary_node(node)
|
130
|
-
|
131
|
-
on_imaginary(node.slice)
|
152
|
+
visit_number(node) { |text| on_imaginary(text) }
|
132
153
|
end
|
133
154
|
|
134
155
|
# Visit an IntegerNode node.
|
135
156
|
def visit_integer_node(node)
|
157
|
+
visit_number(node) { |text| on_int(text) }
|
158
|
+
end
|
159
|
+
|
160
|
+
# Visit a ParenthesesNode node.
|
161
|
+
def visit_parentheses_node(node)
|
162
|
+
body =
|
163
|
+
if node.body.nil?
|
164
|
+
on_stmts_add(on_stmts_new, on_void_stmt)
|
165
|
+
else
|
166
|
+
visit(node.body)
|
167
|
+
end
|
168
|
+
|
169
|
+
bounds(node.location)
|
170
|
+
on_paren(body)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Visit a ProgramNode node.
|
174
|
+
def visit_program_node(node)
|
175
|
+
statements = visit(node.statements)
|
176
|
+
bounds(node.location)
|
177
|
+
on_program(statements)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Visit a RangeNode node.
|
181
|
+
def visit_range_node(node)
|
182
|
+
left = visit(node.left)
|
183
|
+
right = visit(node.right)
|
184
|
+
|
136
185
|
bounds(node.location)
|
137
|
-
|
186
|
+
if node.exclude_end?
|
187
|
+
on_dot3(left, right)
|
188
|
+
else
|
189
|
+
on_dot2(left, right)
|
190
|
+
end
|
138
191
|
end
|
139
192
|
|
140
193
|
# Visit a RationalNode node.
|
141
194
|
def visit_rational_node(node)
|
142
|
-
|
143
|
-
on_rational(node.slice)
|
195
|
+
visit_number(node) { |text| on_rational(text) }
|
144
196
|
end
|
145
197
|
|
146
198
|
# Visit a StatementsNode node.
|
@@ -151,13 +203,6 @@ module Prism
|
|
151
203
|
end
|
152
204
|
end
|
153
205
|
|
154
|
-
# Visit a ProgramNode node.
|
155
|
-
def visit_program_node(node)
|
156
|
-
statements = visit(node.statements)
|
157
|
-
bounds(node.location)
|
158
|
-
on_program(statements)
|
159
|
-
end
|
160
|
-
|
161
206
|
############################################################################
|
162
207
|
# Entrypoints for subclasses
|
163
208
|
############################################################################
|
@@ -174,6 +219,32 @@ module Prism
|
|
174
219
|
|
175
220
|
private
|
176
221
|
|
222
|
+
# Visit a list of elements, like the elements of an array or arguments.
|
223
|
+
def visit_elements(elements)
|
224
|
+
bounds(elements.first.location)
|
225
|
+
elements.inject(on_args_new) do |args, element|
|
226
|
+
on_args_add(args, visit(element))
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# Visit a node that represents a number. We need to explicitly handle the
|
231
|
+
# unary - operator.
|
232
|
+
def visit_number(node)
|
233
|
+
slice = node.slice
|
234
|
+
location = node.location
|
235
|
+
|
236
|
+
if slice[0] == "-"
|
237
|
+
bounds_values(location.start_line, location.start_column + 1)
|
238
|
+
value = yield slice[1..-1]
|
239
|
+
|
240
|
+
bounds(node.location)
|
241
|
+
on_unary(RUBY_ENGINE == "jruby" ? :- : :-@, value)
|
242
|
+
else
|
243
|
+
bounds(location)
|
244
|
+
yield slice
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
177
248
|
# This method is responsible for updating lineno and column information
|
178
249
|
# to reflect the current node.
|
179
250
|
#
|
@@ -184,6 +255,13 @@ module Prism
|
|
184
255
|
@column = location.start_column
|
185
256
|
end
|
186
257
|
|
258
|
+
# If we need to do something unusual, we can directly update the line number
|
259
|
+
# and column to reflect the current node.
|
260
|
+
def bounds_values(lineno, column)
|
261
|
+
@lineno = lineno
|
262
|
+
@column = column
|
263
|
+
end
|
264
|
+
|
187
265
|
# Lazily initialize the parse result.
|
188
266
|
def result
|
189
267
|
@result ||= Prism.parse(source)
|
data/lib/prism/serialize.rb
CHANGED
@@ -27,7 +27,7 @@ module Prism
|
|
27
27
|
|
28
28
|
# The minor version of prism that we are expecting to find in the serialized
|
29
29
|
# strings.
|
30
|
-
MINOR_VERSION =
|
30
|
+
MINOR_VERSION = 22
|
31
31
|
|
32
32
|
# The patch version of prism that we are expecting to find in the serialized
|
33
33
|
# strings.
|
@@ -244,6 +244,8 @@ module Prism
|
|
244
244
|
case level
|
245
245
|
when 0
|
246
246
|
:fatal
|
247
|
+
when 1
|
248
|
+
:argument
|
247
249
|
else
|
248
250
|
raise "Unknown level: #{level}"
|
249
251
|
end
|
@@ -105,14 +105,18 @@ module Prism
|
|
105
105
|
# { a: 1 }
|
106
106
|
# ^^^^
|
107
107
|
def visit_assoc_node(node)
|
108
|
-
if
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
108
|
+
if in_pattern
|
109
|
+
if node.value.is_a?(ImplicitNode)
|
110
|
+
if node.key.is_a?(SymbolNode)
|
111
|
+
builder.match_hash_var([node.key.unescaped, srange(node.key.location)])
|
112
|
+
else
|
113
|
+
builder.match_hash_var_from_str(token(node.key.opening_loc), visit_all(node.key.parts), token(node.key.closing_loc))
|
114
|
+
end
|
113
115
|
else
|
114
|
-
builder.
|
116
|
+
builder.pair_keyword([node.key.unescaped, srange(node.key.location)], visit(node.value))
|
115
117
|
end
|
118
|
+
elsif node.value.is_a?(ImplicitNode)
|
119
|
+
builder.pair_label([node.key.unescaped, srange(node.key.location)])
|
116
120
|
elsif node.operator_loc
|
117
121
|
builder.pair(visit(node.key), token(node.operator_loc), visit(node.value))
|
118
122
|
elsif node.key.is_a?(SymbolNode) && node.key.opening_loc.nil?
|
@@ -241,53 +245,51 @@ module Prism
|
|
241
245
|
block = nil
|
242
246
|
end
|
243
247
|
|
248
|
+
if node.call_operator_loc.nil?
|
249
|
+
case name
|
250
|
+
when :!
|
251
|
+
return visit_block(builder.not_op(token(node.message_loc), token(node.opening_loc), visit(node.receiver), token(node.closing_loc)), block)
|
252
|
+
when :[]
|
253
|
+
return visit_block(builder.index(visit(node.receiver), token(node.opening_loc), visit_all(arguments), token(node.closing_loc)), block)
|
254
|
+
when :[]=
|
255
|
+
if node.message != "[]=" && node.arguments && block.nil? && !node.safe_navigation?
|
256
|
+
return visit_block(
|
257
|
+
builder.assign(
|
258
|
+
builder.index_asgn(
|
259
|
+
visit(node.receiver),
|
260
|
+
token(node.opening_loc),
|
261
|
+
visit_all(node.arguments.arguments[...-1]),
|
262
|
+
token(node.closing_loc),
|
263
|
+
),
|
264
|
+
srange_find(node.message_loc.end_offset, node.arguments.arguments.last.location.start_offset, ["="]),
|
265
|
+
visit(node.arguments.arguments.last)
|
266
|
+
),
|
267
|
+
block
|
268
|
+
)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
message_loc = node.message_loc
|
274
|
+
call_operator_loc = node.call_operator_loc
|
275
|
+
call_operator = [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)] if call_operator_loc
|
276
|
+
|
244
277
|
visit_block(
|
245
|
-
if name
|
246
|
-
builder.
|
247
|
-
|
248
|
-
|
249
|
-
visit(node.
|
250
|
-
token(node.closing_loc)
|
278
|
+
if name.end_with?("=") && !message_loc.slice.end_with?("=") && node.arguments && block.nil?
|
279
|
+
builder.assign(
|
280
|
+
builder.attr_asgn(visit(node.receiver), call_operator, token(message_loc)),
|
281
|
+
srange_find(message_loc.end_offset, node.arguments.location.start_offset, ["="]),
|
282
|
+
visit(node.arguments.arguments.last)
|
251
283
|
)
|
252
|
-
|
253
|
-
builder.
|
284
|
+
else
|
285
|
+
builder.call_method(
|
254
286
|
visit(node.receiver),
|
287
|
+
call_operator,
|
288
|
+
message_loc ? [node.name, srange(message_loc)] : nil,
|
255
289
|
token(node.opening_loc),
|
256
290
|
visit_all(arguments),
|
257
291
|
token(node.closing_loc)
|
258
292
|
)
|
259
|
-
elsif name == :[]= && node.message != "[]=" && node.arguments && block.nil?
|
260
|
-
builder.assign(
|
261
|
-
builder.index_asgn(
|
262
|
-
visit(node.receiver),
|
263
|
-
token(node.opening_loc),
|
264
|
-
visit_all(node.arguments.arguments[...-1]),
|
265
|
-
token(node.closing_loc),
|
266
|
-
),
|
267
|
-
srange_find(node.message_loc.end_offset, node.arguments.arguments.last.location.start_offset, ["="]),
|
268
|
-
visit(node.arguments.arguments.last)
|
269
|
-
)
|
270
|
-
else
|
271
|
-
message_loc = node.message_loc
|
272
|
-
call_operator_loc = node.call_operator_loc
|
273
|
-
call_operator = [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)] if call_operator_loc
|
274
|
-
|
275
|
-
if name.end_with?("=") && !message_loc.slice.end_with?("=") && node.arguments && block.nil?
|
276
|
-
builder.assign(
|
277
|
-
builder.attr_asgn(visit(node.receiver), call_operator, token(message_loc)),
|
278
|
-
srange_find(message_loc.end_offset, node.arguments.location.start_offset, ["="]),
|
279
|
-
visit(node.arguments.arguments.last)
|
280
|
-
)
|
281
|
-
else
|
282
|
-
builder.call_method(
|
283
|
-
visit(node.receiver),
|
284
|
-
call_operator,
|
285
|
-
message_loc ? [node.name, srange(message_loc)] : nil,
|
286
|
-
token(node.opening_loc),
|
287
|
-
visit_all(arguments),
|
288
|
-
token(node.closing_loc)
|
289
|
-
)
|
290
|
-
end
|
291
293
|
end,
|
292
294
|
block
|
293
295
|
)
|
@@ -519,8 +521,6 @@ module Prism
|
|
519
521
|
# def self.foo; end
|
520
522
|
# ^^^^^^^^^^^^^^^^^
|
521
523
|
def visit_def_node(node)
|
522
|
-
forwarding = find_forwarding(node.parameters)
|
523
|
-
|
524
524
|
if node.equal_loc
|
525
525
|
if node.receiver
|
526
526
|
builder.def_endless_singleton(
|
@@ -530,7 +530,7 @@ module Prism
|
|
530
530
|
token(node.name_loc),
|
531
531
|
builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
|
532
532
|
token(node.equal_loc),
|
533
|
-
node.body&.accept(copy_compiler(forwarding:
|
533
|
+
node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters)))
|
534
534
|
)
|
535
535
|
else
|
536
536
|
builder.def_endless_method(
|
@@ -538,7 +538,7 @@ module Prism
|
|
538
538
|
token(node.name_loc),
|
539
539
|
builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
|
540
540
|
token(node.equal_loc),
|
541
|
-
node.body&.accept(copy_compiler(forwarding:
|
541
|
+
node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters)))
|
542
542
|
)
|
543
543
|
end
|
544
544
|
elsif node.receiver
|
@@ -548,7 +548,7 @@ module Prism
|
|
548
548
|
token(node.operator_loc),
|
549
549
|
token(node.name_loc),
|
550
550
|
builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
|
551
|
-
node.body&.accept(copy_compiler(forwarding:
|
551
|
+
node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))),
|
552
552
|
token(node.end_keyword_loc)
|
553
553
|
)
|
554
554
|
else
|
@@ -556,7 +556,7 @@ module Prism
|
|
556
556
|
token(node.def_keyword_loc),
|
557
557
|
token(node.name_loc),
|
558
558
|
builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
|
559
|
-
node.body&.accept(copy_compiler(forwarding:
|
559
|
+
node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))),
|
560
560
|
token(node.end_keyword_loc)
|
561
561
|
)
|
562
562
|
end
|
@@ -614,9 +614,7 @@ module Prism
|
|
614
614
|
# foo => [*, bar, *]
|
615
615
|
# ^^^^^^^^^^^
|
616
616
|
def visit_find_pattern_node(node)
|
617
|
-
elements = [*node.requireds]
|
618
|
-
elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
|
619
|
-
elements.concat(node.posts)
|
617
|
+
elements = [node.left, *node.requireds, node.right]
|
620
618
|
|
621
619
|
if node.constant
|
622
620
|
builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.find_pattern(nil, visit_all(elements), nil), token(node.closing_loc))
|
@@ -993,24 +991,24 @@ module Prism
|
|
993
991
|
|
994
992
|
# -> {}
|
995
993
|
def visit_lambda_node(node)
|
994
|
+
parameters = node.parameters
|
995
|
+
|
996
996
|
builder.block(
|
997
997
|
builder.call_lambda(token(node.operator_loc)),
|
998
998
|
[node.opening, srange(node.opening_loc)],
|
999
|
-
if
|
1000
|
-
if node.parameters.is_a?(NumberedParametersNode)
|
1001
|
-
visit(node.parameters)
|
1002
|
-
else
|
1003
|
-
builder.args(
|
1004
|
-
token(node.parameters.opening_loc),
|
1005
|
-
visit(node.parameters),
|
1006
|
-
token(node.parameters.closing_loc),
|
1007
|
-
false
|
1008
|
-
)
|
1009
|
-
end
|
1010
|
-
else
|
999
|
+
if parameters.nil?
|
1011
1000
|
builder.args(nil, [], nil, false)
|
1001
|
+
elsif node.parameters.is_a?(NumberedParametersNode)
|
1002
|
+
visit(node.parameters)
|
1003
|
+
else
|
1004
|
+
builder.args(
|
1005
|
+
token(node.parameters.opening_loc),
|
1006
|
+
visit(node.parameters),
|
1007
|
+
token(node.parameters.closing_loc),
|
1008
|
+
false
|
1009
|
+
)
|
1012
1010
|
end,
|
1013
|
-
node.body&.accept(copy_compiler(forwarding: find_forwarding(
|
1011
|
+
node.body&.accept(copy_compiler(forwarding: parameters.is_a?(NumberedParametersNode) ? [] : find_forwarding(parameters&.parameters))),
|
1014
1012
|
[node.closing, srange(node.closing_loc)]
|
1015
1013
|
)
|
1016
1014
|
end
|
@@ -1064,12 +1062,22 @@ module Prism
|
|
1064
1062
|
|
1065
1063
|
# foo in bar
|
1066
1064
|
# ^^^^^^^^^^
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1065
|
+
if RUBY_VERSION >= "3.0"
|
1066
|
+
def visit_match_predicate_node(node)
|
1067
|
+
builder.match_pattern_p(
|
1068
|
+
visit(node.value),
|
1069
|
+
token(node.operator_loc),
|
1070
|
+
within_pattern { |compiler| node.pattern.accept(compiler) }
|
1071
|
+
)
|
1072
|
+
end
|
1073
|
+
else
|
1074
|
+
def visit_match_predicate_node(node)
|
1075
|
+
builder.match_pattern(
|
1076
|
+
visit(node.value),
|
1077
|
+
token(node.operator_loc),
|
1078
|
+
within_pattern { |compiler| node.pattern.accept(compiler) }
|
1079
|
+
)
|
1080
|
+
end
|
1073
1081
|
end
|
1074
1082
|
|
1075
1083
|
# foo => bar
|
@@ -1096,7 +1104,7 @@ module Prism
|
|
1096
1104
|
# case of a syntax error. The parser gem doesn't have such a concept, so
|
1097
1105
|
# we invent our own here.
|
1098
1106
|
def visit_missing_node(node)
|
1099
|
-
|
1107
|
+
::AST::Node.new(:missing, [], location: ::Parser::Source::Map.new(srange(node.location)))
|
1100
1108
|
end
|
1101
1109
|
|
1102
1110
|
# module Foo; end
|
@@ -1727,29 +1735,29 @@ module Prism
|
|
1727
1735
|
# Visit a block node on a call.
|
1728
1736
|
def visit_block(call, block)
|
1729
1737
|
if block
|
1738
|
+
parameters = block.parameters
|
1739
|
+
|
1730
1740
|
builder.block(
|
1731
1741
|
call,
|
1732
1742
|
token(block.opening_loc),
|
1733
|
-
if
|
1734
|
-
if parameters.is_a?(NumberedParametersNode)
|
1735
|
-
visit(parameters)
|
1736
|
-
else
|
1737
|
-
builder.args(
|
1738
|
-
token(parameters.opening_loc),
|
1739
|
-
if procarg0?(parameters.parameters)
|
1740
|
-
parameter = parameters.parameters.requireds.first
|
1741
|
-
[builder.procarg0(visit(parameter))].concat(visit_all(parameters.locals))
|
1742
|
-
else
|
1743
|
-
visit(parameters)
|
1744
|
-
end,
|
1745
|
-
token(parameters.closing_loc),
|
1746
|
-
false
|
1747
|
-
)
|
1748
|
-
end
|
1749
|
-
else
|
1743
|
+
if parameters.nil?
|
1750
1744
|
builder.args(nil, [], nil, false)
|
1745
|
+
elsif parameters.is_a?(NumberedParametersNode)
|
1746
|
+
visit(parameters)
|
1747
|
+
else
|
1748
|
+
builder.args(
|
1749
|
+
token(parameters.opening_loc),
|
1750
|
+
if procarg0?(parameters.parameters)
|
1751
|
+
parameter = parameters.parameters.requireds.first
|
1752
|
+
[builder.procarg0(visit(parameter))].concat(visit_all(parameters.locals))
|
1753
|
+
else
|
1754
|
+
visit(parameters)
|
1755
|
+
end,
|
1756
|
+
token(parameters.closing_loc),
|
1757
|
+
false
|
1758
|
+
)
|
1751
1759
|
end,
|
1752
|
-
block.body&.accept(copy_compiler(forwarding: find_forwarding(
|
1760
|
+
block.body&.accept(copy_compiler(forwarding: parameters.is_a?(NumberedParametersNode) ? [] : find_forwarding(parameters&.parameters))),
|
1753
1761
|
token(block.closing_loc)
|
1754
1762
|
)
|
1755
1763
|
else
|
@@ -1762,9 +1770,9 @@ module Prism
|
|
1762
1770
|
children = []
|
1763
1771
|
node.parts.each do |part|
|
1764
1772
|
pushing =
|
1765
|
-
if part.is_a?(StringNode) && part.unescaped.
|
1766
|
-
unescaped = part.unescaped.
|
1767
|
-
escaped = part.content.
|
1773
|
+
if part.is_a?(StringNode) && part.unescaped.include?("\n")
|
1774
|
+
unescaped = part.unescaped.lines(chomp: true)
|
1775
|
+
escaped = part.content.lines(chomp: true)
|
1768
1776
|
|
1769
1777
|
escaped_lengths =
|
1770
1778
|
if node.opening.end_with?("'")
|
@@ -1779,7 +1787,6 @@ module Prism
|
|
1779
1787
|
unescaped.zip(escaped_lengths).map do |unescaped_line, escaped_length|
|
1780
1788
|
end_offset = start_offset + (escaped_length || 0)
|
1781
1789
|
inner_part = builder.string_internal(["#{unescaped_line}\n", srange_offsets(start_offset, end_offset)])
|
1782
|
-
|
1783
1790
|
start_offset = end_offset
|
1784
1791
|
inner_part
|
1785
1792
|
end
|
@@ -26,7 +26,7 @@ module Prism
|
|
26
26
|
Racc_debug_parser = false # :nodoc:
|
27
27
|
|
28
28
|
def version # :nodoc:
|
29
|
-
|
29
|
+
34
|
30
30
|
end
|
31
31
|
|
32
32
|
# The default encoding for Ruby files is UTF-8.
|
@@ -42,9 +42,10 @@ module Prism
|
|
42
42
|
@source_buffer = source_buffer
|
43
43
|
source = source_buffer.source
|
44
44
|
|
45
|
-
|
45
|
+
offset_cache = build_offset_cache(source)
|
46
|
+
result = unwrap(Prism.parse(source, filepath: source_buffer.name), offset_cache)
|
46
47
|
|
47
|
-
build_ast(result.value,
|
48
|
+
build_ast(result.value, offset_cache)
|
48
49
|
ensure
|
49
50
|
@source_buffer = nil
|
50
51
|
end
|
@@ -55,7 +56,7 @@ module Prism
|
|
55
56
|
source = source_buffer.source
|
56
57
|
|
57
58
|
offset_cache = build_offset_cache(source)
|
58
|
-
result = unwrap(Prism.parse(source, filepath: source_buffer.name))
|
59
|
+
result = unwrap(Prism.parse(source, filepath: source_buffer.name), offset_cache)
|
59
60
|
|
60
61
|
[
|
61
62
|
build_ast(result.value, offset_cache),
|
@@ -67,17 +68,23 @@ module Prism
|
|
67
68
|
|
68
69
|
# Parses a source buffer and returns the AST, the source code comments,
|
69
70
|
# and the tokens emitted by the lexer.
|
70
|
-
def tokenize(source_buffer,
|
71
|
+
def tokenize(source_buffer, recover = false)
|
71
72
|
@source_buffer = source_buffer
|
72
73
|
source = source_buffer.source
|
73
74
|
|
74
75
|
offset_cache = build_offset_cache(source)
|
75
|
-
result =
|
76
|
+
result =
|
77
|
+
begin
|
78
|
+
unwrap(Prism.parse_lex(source, filepath: source_buffer.name), offset_cache)
|
79
|
+
rescue ::Parser::SyntaxError
|
80
|
+
raise if !recover
|
81
|
+
end
|
76
82
|
|
77
83
|
program, tokens = result.value
|
84
|
+
ast = build_ast(program, offset_cache) if result.success?
|
78
85
|
|
79
86
|
[
|
80
|
-
|
87
|
+
ast,
|
81
88
|
build_comments(result.comments, offset_cache),
|
82
89
|
build_tokens(tokens, offset_cache)
|
83
90
|
]
|
@@ -93,16 +100,23 @@ module Prism
|
|
93
100
|
|
94
101
|
private
|
95
102
|
|
103
|
+
# This is a hook to allow consumers to disable some errors if they don't
|
104
|
+
# want them to block creating the syntax tree.
|
105
|
+
def valid_error?(error)
|
106
|
+
true
|
107
|
+
end
|
108
|
+
|
96
109
|
# If there was a error generated during the parse, then raise an
|
97
110
|
# appropriate syntax error. Otherwise return the result.
|
98
|
-
def unwrap(result)
|
99
|
-
|
111
|
+
def unwrap(result, offset_cache)
|
112
|
+
result.errors.each do |error|
|
113
|
+
next unless valid_error?(error)
|
100
114
|
|
101
|
-
|
102
|
-
|
115
|
+
location = build_range(error.location, offset_cache)
|
116
|
+
diagnostics.process(Diagnostic.new(error.message, location))
|
117
|
+
end
|
103
118
|
|
104
|
-
|
105
|
-
raise ::Parser::SyntaxError, diagnostic
|
119
|
+
result
|
106
120
|
end
|
107
121
|
|
108
122
|
# Prism deals with offsets in bytes, while the parser gem deals with
|
data/prism.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
spec.name = "prism"
|
5
|
-
spec.version = "0.
|
5
|
+
spec.version = "0.22.0"
|
6
6
|
spec.authors = ["Shopify"]
|
7
7
|
spec.email = ["ruby@shopify.com"]
|
8
8
|
|
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.homepage = "https://github.com/ruby/prism"
|
11
11
|
spec.license = "MIT"
|
12
12
|
|
13
|
-
spec.required_ruby_version = ">=
|
13
|
+
spec.required_ruby_version = ">= 2.7.0"
|
14
14
|
|
15
15
|
spec.require_paths = ["lib"]
|
16
16
|
spec.files = [
|
data/src/diagnostic.c
CHANGED
@@ -63,7 +63,8 @@ typedef struct {
|
|
63
63
|
*
|
64
64
|
* For errors, they are:
|
65
65
|
*
|
66
|
-
* * `PM_ERROR_LEVEL_FATAL` - The level for
|
66
|
+
* * `PM_ERROR_LEVEL_FATAL` - The default level for errors.
|
67
|
+
* * `PM_ERROR_LEVEL_ARGUMENT` - Errors that should raise ArgumentError.
|
67
68
|
*
|
68
69
|
* For warnings, they are:
|
69
70
|
*
|
@@ -71,9 +72,13 @@ typedef struct {
|
|
71
72
|
* * `PM_WARNING_LEVEL_VERBOSE` - Warnings that appear with `-w`, as in `ruby -w -c -e 'code'`.
|
72
73
|
*/
|
73
74
|
static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
|
75
|
+
// Special error that can be replaced
|
74
76
|
[PM_ERR_CANNOT_PARSE_EXPRESSION] = { "cannot parse the expression", PM_ERROR_LEVEL_FATAL },
|
75
77
|
|
76
|
-
// Errors
|
78
|
+
// Errors that should raise argument errors
|
79
|
+
[PM_ERR_INVALID_ENCODING_MAGIC_COMMENT] = { "unknown or invalid encoding in the magic comment", PM_ERROR_LEVEL_ARGUMENT },
|
80
|
+
|
81
|
+
// Errors that should raise syntax errors
|
77
82
|
[PM_ERR_ALIAS_ARGUMENT] = { "invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", PM_ERROR_LEVEL_FATAL },
|
78
83
|
[PM_ERR_AMPAMPEQ_MULTI_ASSIGN] = { "unexpected `&&=` in a multiple assignment", PM_ERROR_LEVEL_FATAL },
|
79
84
|
[PM_ERR_ARGUMENT_AFTER_BLOCK] = { "unexpected argument after a block argument", PM_ERROR_LEVEL_FATAL },
|
@@ -187,15 +192,16 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
|
|
187
192
|
[PM_ERR_INCOMPLETE_QUESTION_MARK] = { "incomplete expression at `?`", PM_ERROR_LEVEL_FATAL },
|
188
193
|
[PM_ERR_INCOMPLETE_VARIABLE_CLASS] = { "incomplete class variable", PM_ERROR_LEVEL_FATAL },
|
189
194
|
[PM_ERR_INCOMPLETE_VARIABLE_INSTANCE] = { "incomplete instance variable", PM_ERROR_LEVEL_FATAL },
|
190
|
-
[PM_ERR_INVALID_ENCODING_MAGIC_COMMENT] = { "unknown or invalid encoding in the magic comment", PM_ERROR_LEVEL_FATAL },
|
191
195
|
[PM_ERR_INVALID_FLOAT_EXPONENT] = { "invalid exponent", PM_ERROR_LEVEL_FATAL },
|
192
196
|
[PM_ERR_INVALID_NUMBER_BINARY] = { "invalid binary number", PM_ERROR_LEVEL_FATAL },
|
193
197
|
[PM_ERR_INVALID_NUMBER_DECIMAL] = { "invalid decimal number", PM_ERROR_LEVEL_FATAL },
|
194
198
|
[PM_ERR_INVALID_NUMBER_HEXADECIMAL] = { "invalid hexadecimal number", PM_ERROR_LEVEL_FATAL },
|
195
199
|
[PM_ERR_INVALID_NUMBER_OCTAL] = { "invalid octal number", PM_ERROR_LEVEL_FATAL },
|
196
200
|
[PM_ERR_INVALID_NUMBER_UNDERSCORE] = { "invalid underscore placement in number", PM_ERROR_LEVEL_FATAL },
|
201
|
+
[PM_ERR_INVALID_CHARACTER] = { "invalid character 0x%X", PM_ERROR_LEVEL_FATAL },
|
202
|
+
[PM_ERR_INVALID_MULTIBYTE_CHARACTER] = { "invalid multibyte character 0x%X", PM_ERROR_LEVEL_FATAL },
|
203
|
+
[PM_ERR_INVALID_PRINTABLE_CHARACTER] = { "invalid character `%c`", PM_ERROR_LEVEL_FATAL },
|
197
204
|
[PM_ERR_INVALID_PERCENT] = { "invalid `%` token", PM_ERROR_LEVEL_FATAL }, // TODO WHAT?
|
198
|
-
[PM_ERR_INVALID_TOKEN] = { "invalid token", PM_ERROR_LEVEL_FATAL }, // TODO WHAT?
|
199
205
|
[PM_ERR_INVALID_VARIABLE_GLOBAL] = { "invalid global variable", PM_ERROR_LEVEL_FATAL },
|
200
206
|
[PM_ERR_IT_NOT_ALLOWED] = { "`it` is not allowed when an ordinary parameter is defined", PM_ERROR_LEVEL_FATAL },
|
201
207
|
[PM_ERR_LAMBDA_OPEN] = { "expected a `do` keyword or a `{` to open the lambda block", PM_ERROR_LEVEL_FATAL },
|
data/src/encoding.c
CHANGED
@@ -2252,7 +2252,7 @@ static const uint8_t pm_utf_8_dfa[] = {
|
|
2252
2252
|
*/
|
2253
2253
|
static pm_unicode_codepoint_t
|
2254
2254
|
pm_utf_8_codepoint(const uint8_t *b, ptrdiff_t n, size_t *width) {
|
2255
|
-
assert(n >=
|
2255
|
+
assert(n >= 0);
|
2256
2256
|
size_t maximum = (size_t) n;
|
2257
2257
|
|
2258
2258
|
uint32_t codepoint;
|