prism 0.29.0 → 1.3.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 +115 -1
- data/CONTRIBUTING.md +0 -4
- data/Makefile +1 -1
- data/README.md +4 -0
- data/config.yml +920 -148
- data/docs/build_system.md +8 -11
- data/docs/fuzzing.md +1 -1
- data/docs/parsing_rules.md +4 -1
- data/docs/relocation.md +34 -0
- data/docs/ripper_translation.md +22 -0
- data/docs/serialization.md +3 -0
- data/ext/prism/api_node.c +2863 -2079
- data/ext/prism/extconf.rb +14 -37
- data/ext/prism/extension.c +241 -391
- data/ext/prism/extension.h +2 -2
- data/include/prism/ast.h +2156 -453
- data/include/prism/defines.h +58 -7
- data/include/prism/diagnostic.h +24 -6
- data/include/prism/node.h +0 -21
- data/include/prism/options.h +94 -3
- data/include/prism/parser.h +82 -40
- data/include/prism/regexp.h +18 -8
- data/include/prism/static_literals.h +3 -2
- data/include/prism/util/pm_char.h +1 -2
- data/include/prism/util/pm_constant_pool.h +0 -8
- data/include/prism/util/pm_integer.h +22 -15
- data/include/prism/util/pm_newline_list.h +11 -0
- data/include/prism/util/pm_string.h +28 -12
- data/include/prism/version.h +3 -3
- data/include/prism.h +47 -11
- data/lib/prism/compiler.rb +3 -0
- data/lib/prism/desugar_compiler.rb +111 -74
- data/lib/prism/dispatcher.rb +16 -1
- data/lib/prism/dot_visitor.rb +55 -34
- data/lib/prism/dsl.rb +660 -468
- data/lib/prism/ffi.rb +113 -8
- data/lib/prism/inspect_visitor.rb +296 -64
- data/lib/prism/lex_compat.rb +1 -1
- data/lib/prism/mutation_compiler.rb +11 -6
- data/lib/prism/node.rb +4262 -5023
- data/lib/prism/node_ext.rb +91 -14
- data/lib/prism/parse_result/comments.rb +0 -7
- data/lib/prism/parse_result/errors.rb +65 -0
- data/lib/prism/parse_result/newlines.rb +101 -11
- data/lib/prism/parse_result.rb +183 -6
- data/lib/prism/reflection.rb +12 -10
- data/lib/prism/relocation.rb +504 -0
- data/lib/prism/serialize.rb +496 -609
- data/lib/prism/string_query.rb +30 -0
- data/lib/prism/translation/parser/compiler.rb +185 -155
- data/lib/prism/translation/parser/lexer.rb +26 -4
- data/lib/prism/translation/parser.rb +9 -4
- data/lib/prism/translation/ripper.rb +23 -25
- data/lib/prism/translation/ruby_parser.rb +86 -17
- data/lib/prism/visitor.rb +3 -0
- data/lib/prism.rb +6 -8
- data/prism.gemspec +9 -5
- data/rbi/prism/dsl.rbi +521 -0
- data/rbi/prism/node.rbi +1115 -1120
- data/rbi/prism/parse_result.rbi +29 -0
- data/rbi/prism/string_query.rbi +12 -0
- data/rbi/prism/visitor.rbi +3 -0
- data/rbi/prism.rbi +36 -30
- data/sig/prism/dsl.rbs +190 -303
- data/sig/prism/mutation_compiler.rbs +1 -0
- data/sig/prism/node.rbs +678 -632
- data/sig/prism/parse_result.rbs +22 -0
- data/sig/prism/relocation.rbs +185 -0
- data/sig/prism/string_query.rbs +11 -0
- data/sig/prism/visitor.rbs +1 -0
- data/sig/prism.rbs +103 -64
- data/src/diagnostic.c +64 -28
- data/src/node.c +502 -1739
- data/src/options.c +76 -27
- data/src/prettyprint.c +188 -112
- data/src/prism.c +3376 -2293
- data/src/regexp.c +208 -71
- data/src/serialize.c +182 -50
- data/src/static_literals.c +64 -85
- data/src/token_type.c +4 -4
- data/src/util/pm_char.c +1 -1
- data/src/util/pm_constant_pool.c +0 -8
- data/src/util/pm_integer.c +53 -25
- data/src/util/pm_newline_list.c +29 -0
- data/src/util/pm_string.c +131 -80
- data/src/util/pm_strpbrk.c +32 -6
- metadata +11 -7
- data/include/prism/util/pm_string_list.h +0 -44
- data/lib/prism/debug.rb +0 -249
- data/lib/prism/translation/parser/rubocop.rb +0 -73
- data/src/util/pm_string_list.c +0 -28
@@ -90,7 +90,11 @@ module Prism
|
|
90
90
|
end
|
91
91
|
|
92
92
|
if node.constant
|
93
|
-
|
93
|
+
if visited.empty?
|
94
|
+
builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.array_pattern(token(node.opening_loc), visited, token(node.closing_loc)), token(node.closing_loc))
|
95
|
+
else
|
96
|
+
builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.array_pattern(nil, visited, nil), token(node.closing_loc))
|
97
|
+
end
|
94
98
|
else
|
95
99
|
builder.array_pattern(token(node.opening_loc), visited, token(node.closing_loc))
|
96
100
|
end
|
@@ -105,38 +109,46 @@ module Prism
|
|
105
109
|
# { a: 1 }
|
106
110
|
# ^^^^
|
107
111
|
def visit_assoc_node(node)
|
112
|
+
key = node.key
|
113
|
+
|
108
114
|
if in_pattern
|
109
115
|
if node.value.is_a?(ImplicitNode)
|
110
|
-
if
|
111
|
-
|
116
|
+
if key.is_a?(SymbolNode)
|
117
|
+
if key.opening.nil?
|
118
|
+
builder.match_hash_var([key.unescaped, srange(key.location)])
|
119
|
+
else
|
120
|
+
builder.match_hash_var_from_str(token(key.opening_loc), [builder.string_internal([key.unescaped, srange(key.value_loc)])], token(key.closing_loc))
|
121
|
+
end
|
112
122
|
else
|
113
|
-
builder.match_hash_var_from_str(token(
|
123
|
+
builder.match_hash_var_from_str(token(key.opening_loc), visit_all(key.parts), token(key.closing_loc))
|
114
124
|
end
|
125
|
+
elsif key.opening.nil?
|
126
|
+
builder.pair_keyword([key.unescaped, srange(key.location)], visit(node.value))
|
115
127
|
else
|
116
|
-
builder.
|
128
|
+
builder.pair_quoted(token(key.opening_loc), [builder.string_internal([key.unescaped, srange(key.value_loc)])], token(key.closing_loc), visit(node.value))
|
117
129
|
end
|
118
130
|
elsif node.value.is_a?(ImplicitNode)
|
119
131
|
if (value = node.value.value).is_a?(LocalVariableReadNode)
|
120
132
|
builder.pair_keyword(
|
121
|
-
[
|
122
|
-
builder.ident([value.name, srange(
|
133
|
+
[key.unescaped, srange(key)],
|
134
|
+
builder.ident([value.name, srange(key.value_loc)]).updated(:lvar)
|
123
135
|
)
|
124
136
|
else
|
125
|
-
builder.pair_label([
|
137
|
+
builder.pair_label([key.unescaped, srange(key.location)])
|
126
138
|
end
|
127
139
|
elsif node.operator_loc
|
128
|
-
builder.pair(visit(
|
129
|
-
elsif
|
130
|
-
builder.pair_keyword([
|
140
|
+
builder.pair(visit(key), token(node.operator_loc), visit(node.value))
|
141
|
+
elsif key.is_a?(SymbolNode) && key.opening_loc.nil?
|
142
|
+
builder.pair_keyword([key.unescaped, srange(key.location)], visit(node.value))
|
131
143
|
else
|
132
144
|
parts =
|
133
|
-
if
|
134
|
-
[builder.string_internal([
|
145
|
+
if key.is_a?(SymbolNode)
|
146
|
+
[builder.string_internal([key.unescaped, srange(key.value_loc)])]
|
135
147
|
else
|
136
|
-
visit_all(
|
148
|
+
visit_all(key.parts)
|
137
149
|
end
|
138
150
|
|
139
|
-
builder.pair_quoted(token(
|
151
|
+
builder.pair_quoted(token(key.opening_loc), parts, token(key.closing_loc), visit(node.value))
|
140
152
|
end
|
141
153
|
end
|
142
154
|
|
@@ -146,7 +158,9 @@ module Prism
|
|
146
158
|
# { **foo }
|
147
159
|
# ^^^^^
|
148
160
|
def visit_assoc_splat_node(node)
|
149
|
-
if
|
161
|
+
if in_pattern
|
162
|
+
builder.match_rest(token(node.operator_loc), token(node.value&.location))
|
163
|
+
elsif node.value.nil? && forwarding.include?(:**)
|
150
164
|
builder.forwarded_kwrestarg(token(node.operator_loc))
|
151
165
|
else
|
152
166
|
builder.kwsplat(token(node.operator_loc), visit(node.value))
|
@@ -167,17 +181,17 @@ module Prism
|
|
167
181
|
if (rescue_clause = node.rescue_clause)
|
168
182
|
begin
|
169
183
|
find_start_offset = (rescue_clause.reference&.location || rescue_clause.exceptions.last&.location || rescue_clause.keyword_loc).end_offset
|
170
|
-
find_end_offset = (rescue_clause.statements&.location&.start_offset || rescue_clause.
|
184
|
+
find_end_offset = (rescue_clause.statements&.location&.start_offset || rescue_clause.subsequent&.location&.start_offset || (find_start_offset + 1))
|
171
185
|
|
172
186
|
rescue_bodies << builder.rescue_body(
|
173
187
|
token(rescue_clause.keyword_loc),
|
174
188
|
rescue_clause.exceptions.any? ? builder.array(nil, visit_all(rescue_clause.exceptions), nil) : nil,
|
175
189
|
token(rescue_clause.operator_loc),
|
176
190
|
visit(rescue_clause.reference),
|
177
|
-
srange_find(find_start_offset, find_end_offset,
|
191
|
+
srange_find(find_start_offset, find_end_offset, ";"),
|
178
192
|
visit(rescue_clause.statements)
|
179
193
|
)
|
180
|
-
end until (rescue_clause = rescue_clause.
|
194
|
+
end until (rescue_clause = rescue_clause.subsequent).nil?
|
181
195
|
end
|
182
196
|
|
183
197
|
begin_body =
|
@@ -280,7 +294,7 @@ module Prism
|
|
280
294
|
visit_all(arguments),
|
281
295
|
token(node.closing_loc),
|
282
296
|
),
|
283
|
-
srange_find(node.message_loc.end_offset, node.arguments.arguments.last.location.start_offset,
|
297
|
+
srange_find(node.message_loc.end_offset, node.arguments.arguments.last.location.start_offset, "="),
|
284
298
|
visit(node.arguments.arguments.last)
|
285
299
|
),
|
286
300
|
block
|
@@ -297,7 +311,7 @@ module Prism
|
|
297
311
|
if name.end_with?("=") && !message_loc.slice.end_with?("=") && node.arguments && block.nil?
|
298
312
|
builder.assign(
|
299
313
|
builder.attr_asgn(visit(node.receiver), call_operator, token(message_loc)),
|
300
|
-
srange_find(message_loc.end_offset, node.arguments.location.start_offset,
|
314
|
+
srange_find(message_loc.end_offset, node.arguments.location.start_offset, "="),
|
301
315
|
visit(node.arguments.arguments.last)
|
302
316
|
)
|
303
317
|
else
|
@@ -396,8 +410,8 @@ module Prism
|
|
396
410
|
token(node.case_keyword_loc),
|
397
411
|
visit(node.predicate),
|
398
412
|
visit_all(node.conditions),
|
399
|
-
token(node.
|
400
|
-
visit(node.
|
413
|
+
token(node.else_clause&.else_keyword_loc),
|
414
|
+
visit(node.else_clause),
|
401
415
|
token(node.end_keyword_loc)
|
402
416
|
)
|
403
417
|
end
|
@@ -409,8 +423,8 @@ module Prism
|
|
409
423
|
token(node.case_keyword_loc),
|
410
424
|
visit(node.predicate),
|
411
425
|
visit_all(node.conditions),
|
412
|
-
token(node.
|
413
|
-
visit(node.
|
426
|
+
token(node.else_clause&.else_keyword_loc),
|
427
|
+
visit(node.else_clause),
|
414
428
|
token(node.end_keyword_loc)
|
415
429
|
)
|
416
430
|
end
|
@@ -719,10 +733,10 @@ module Prism
|
|
719
733
|
visit(node.index),
|
720
734
|
token(node.in_keyword_loc),
|
721
735
|
visit(node.collection),
|
722
|
-
if node.do_keyword_loc
|
723
|
-
token(
|
736
|
+
if (do_keyword_loc = node.do_keyword_loc)
|
737
|
+
token(do_keyword_loc)
|
724
738
|
else
|
725
|
-
srange_find(node.collection.location.end_offset, (node.statements&.location || node.end_keyword_loc).start_offset,
|
739
|
+
srange_find(node.collection.location.end_offset, (node.statements&.location || node.end_keyword_loc).start_offset, ";")
|
726
740
|
end,
|
727
741
|
visit(node.statements),
|
728
742
|
token(node.end_keyword_loc)
|
@@ -844,26 +858,26 @@ module Prism
|
|
844
858
|
visit(node.predicate),
|
845
859
|
token(node.then_keyword_loc),
|
846
860
|
visit(node.statements),
|
847
|
-
token(node.
|
848
|
-
visit(node.
|
861
|
+
token(node.subsequent.else_keyword_loc),
|
862
|
+
visit(node.subsequent)
|
849
863
|
)
|
850
864
|
elsif node.if_keyword_loc.start_offset == node.location.start_offset
|
851
865
|
builder.condition(
|
852
866
|
token(node.if_keyword_loc),
|
853
867
|
visit(node.predicate),
|
854
|
-
if node.then_keyword_loc
|
855
|
-
token(
|
868
|
+
if (then_keyword_loc = node.then_keyword_loc)
|
869
|
+
token(then_keyword_loc)
|
856
870
|
else
|
857
|
-
srange_find(node.predicate.location.end_offset, (node.statements&.location || node.
|
871
|
+
srange_find(node.predicate.location.end_offset, (node.statements&.location || node.subsequent&.location || node.end_keyword_loc).start_offset, ";")
|
858
872
|
end,
|
859
873
|
visit(node.statements),
|
860
|
-
case node.
|
874
|
+
case node.subsequent
|
861
875
|
when IfNode
|
862
|
-
token(node.
|
876
|
+
token(node.subsequent.if_keyword_loc)
|
863
877
|
when ElseNode
|
864
|
-
token(node.
|
878
|
+
token(node.subsequent.else_keyword_loc)
|
865
879
|
end,
|
866
|
-
visit(node.
|
880
|
+
visit(node.subsequent),
|
867
881
|
if node.if_keyword != "elsif"
|
868
882
|
token(node.end_keyword_loc)
|
869
883
|
end
|
@@ -871,7 +885,7 @@ module Prism
|
|
871
885
|
else
|
872
886
|
builder.condition_mod(
|
873
887
|
visit(node.statements),
|
874
|
-
visit(node.
|
888
|
+
visit(node.subsequent),
|
875
889
|
token(node.if_keyword_loc),
|
876
890
|
visit(node.predicate)
|
877
891
|
)
|
@@ -881,7 +895,7 @@ module Prism
|
|
881
895
|
# 1i
|
882
896
|
# ^^
|
883
897
|
def visit_imaginary_node(node)
|
884
|
-
visit_numeric(node, builder.complex([
|
898
|
+
visit_numeric(node, builder.complex([Complex(0, node.numeric.value), srange(node.location)]))
|
885
899
|
end
|
886
900
|
|
887
901
|
# { foo: }
|
@@ -917,7 +931,11 @@ module Prism
|
|
917
931
|
token(node.in_loc),
|
918
932
|
pattern,
|
919
933
|
guard,
|
920
|
-
|
934
|
+
if (then_loc = node.then_loc)
|
935
|
+
token(then_loc)
|
936
|
+
else
|
937
|
+
srange_find(node.pattern.location.end_offset, node.statements&.location&.start_offset, ";")
|
938
|
+
end,
|
921
939
|
visit(node.statements)
|
922
940
|
)
|
923
941
|
end
|
@@ -1064,36 +1082,7 @@ module Prism
|
|
1064
1082
|
# ^^^^^^^^^^^^
|
1065
1083
|
def visit_interpolated_string_node(node)
|
1066
1084
|
if node.heredoc?
|
1067
|
-
children, closing
|
1068
|
-
opening = token(node.opening_loc)
|
1069
|
-
|
1070
|
-
start_offset = node.opening_loc.end_offset + 1
|
1071
|
-
end_offset = node.parts.first.location.start_offset
|
1072
|
-
|
1073
|
-
# In the below case, the offsets should be the same:
|
1074
|
-
#
|
1075
|
-
# <<~HEREDOC
|
1076
|
-
# a #{b}
|
1077
|
-
# HEREDOC
|
1078
|
-
#
|
1079
|
-
# But in this case, the end_offset would be greater than the start_offset:
|
1080
|
-
#
|
1081
|
-
# <<~HEREDOC
|
1082
|
-
# #{b}
|
1083
|
-
# HEREDOC
|
1084
|
-
#
|
1085
|
-
# So we need to make sure the result node's heredoc range is correct, without updating the children
|
1086
|
-
result = if start_offset < end_offset
|
1087
|
-
# We need to add a padding string to ensure that the heredoc has correct range for its body
|
1088
|
-
padding_string_node = builder.string_internal(["", srange_offsets(start_offset, end_offset)])
|
1089
|
-
node_with_correct_location = builder.string_compose(opening, [padding_string_node, *children], closing)
|
1090
|
-
# But the padding string should not be included in the final AST, so we need to update the result's children
|
1091
|
-
node_with_correct_location.updated(:dstr, children)
|
1092
|
-
else
|
1093
|
-
builder.string_compose(opening, children, closing)
|
1094
|
-
end
|
1095
|
-
|
1096
|
-
return result
|
1085
|
+
return visit_heredoc(node) { |children, closing| builder.string_compose(token(node.opening_loc), children, closing) }
|
1097
1086
|
end
|
1098
1087
|
|
1099
1088
|
parts = if node.parts.one? { |part| part.type == :string_node }
|
@@ -1137,8 +1126,7 @@ module Prism
|
|
1137
1126
|
# ^^^^^^^^^^^^
|
1138
1127
|
def visit_interpolated_x_string_node(node)
|
1139
1128
|
if node.heredoc?
|
1140
|
-
children, closing
|
1141
|
-
builder.xstring_compose(token(node.opening_loc), children, closing)
|
1129
|
+
visit_heredoc(node) { |children, closing| builder.xstring_compose(token(node.opening_loc), children, closing) }
|
1142
1130
|
else
|
1143
1131
|
builder.xstring_compose(
|
1144
1132
|
token(node.opening_loc),
|
@@ -1148,6 +1136,12 @@ module Prism
|
|
1148
1136
|
end
|
1149
1137
|
end
|
1150
1138
|
|
1139
|
+
# -> { it }
|
1140
|
+
# ^^
|
1141
|
+
def visit_it_local_variable_read_node(node)
|
1142
|
+
builder.ident([:it, srange(node.location)]).updated(:lvar)
|
1143
|
+
end
|
1144
|
+
|
1151
1145
|
# -> { it }
|
1152
1146
|
# ^^^^^^^^^
|
1153
1147
|
def visit_it_parameters_node(node)
|
@@ -1201,14 +1195,7 @@ module Prism
|
|
1201
1195
|
# foo
|
1202
1196
|
# ^^^
|
1203
1197
|
def visit_local_variable_read_node(node)
|
1204
|
-
name
|
1205
|
-
|
1206
|
-
# This is just a guess. parser doesn't have support for the implicit
|
1207
|
-
# `it` variable yet, so we'll probably have to visit this once it
|
1208
|
-
# does.
|
1209
|
-
name = :it if name == :"0it"
|
1210
|
-
|
1211
|
-
builder.ident([name, srange(node.location)]).updated(:lvar)
|
1198
|
+
builder.ident([node.name, srange(node.location)]).updated(:lvar)
|
1212
1199
|
end
|
1213
1200
|
|
1214
1201
|
# foo = 1
|
@@ -1312,13 +1299,9 @@ module Prism
|
|
1312
1299
|
# foo, bar = baz
|
1313
1300
|
# ^^^^^^^^
|
1314
1301
|
def visit_multi_target_node(node)
|
1315
|
-
elements = [*node.lefts]
|
1316
|
-
elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
|
1317
|
-
elements.concat(node.rights)
|
1318
|
-
|
1319
1302
|
builder.multi_lhs(
|
1320
1303
|
token(node.lparen_loc),
|
1321
|
-
visit_all(
|
1304
|
+
visit_all(multi_target_elements(node)),
|
1322
1305
|
token(node.rparen_loc)
|
1323
1306
|
)
|
1324
1307
|
end
|
@@ -1326,9 +1309,11 @@ module Prism
|
|
1326
1309
|
# foo, bar = baz
|
1327
1310
|
# ^^^^^^^^^^^^^^
|
1328
1311
|
def visit_multi_write_node(node)
|
1329
|
-
elements =
|
1330
|
-
|
1331
|
-
elements.
|
1312
|
+
elements = multi_target_elements(node)
|
1313
|
+
|
1314
|
+
if elements.length == 1 && elements.first.is_a?(MultiTargetNode)
|
1315
|
+
elements = multi_target_elements(elements.first)
|
1316
|
+
end
|
1332
1317
|
|
1333
1318
|
builder.multi_assign(
|
1334
1319
|
builder.multi_lhs(
|
@@ -1409,12 +1394,12 @@ module Prism
|
|
1409
1394
|
|
1410
1395
|
if node.requireds.any?
|
1411
1396
|
node.requireds.each do |required|
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1397
|
+
params <<
|
1398
|
+
if required.is_a?(RequiredParameterNode)
|
1399
|
+
visit(required)
|
1400
|
+
else
|
1401
|
+
required.accept(copy_compiler(in_destructure: true))
|
1402
|
+
end
|
1418
1403
|
end
|
1419
1404
|
end
|
1420
1405
|
|
@@ -1423,12 +1408,12 @@ module Prism
|
|
1423
1408
|
|
1424
1409
|
if node.posts.any?
|
1425
1410
|
node.posts.each do |post|
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1411
|
+
params <<
|
1412
|
+
if post.is_a?(RequiredParameterNode)
|
1413
|
+
visit(post)
|
1414
|
+
else
|
1415
|
+
post.accept(copy_compiler(in_destructure: true))
|
1416
|
+
end
|
1432
1417
|
end
|
1433
1418
|
end
|
1434
1419
|
|
@@ -1514,7 +1499,7 @@ module Prism
|
|
1514
1499
|
# 1r
|
1515
1500
|
# ^^
|
1516
1501
|
def visit_rational_node(node)
|
1517
|
-
visit_numeric(node, builder.rational([
|
1502
|
+
visit_numeric(node, builder.rational([node.value, srange(node.location)]))
|
1518
1503
|
end
|
1519
1504
|
|
1520
1505
|
# redo
|
@@ -1526,9 +1511,20 @@ module Prism
|
|
1526
1511
|
# /foo/
|
1527
1512
|
# ^^^^^
|
1528
1513
|
def visit_regular_expression_node(node)
|
1514
|
+
content = node.content
|
1515
|
+
parts =
|
1516
|
+
if content.include?("\n")
|
1517
|
+
offset = node.content_loc.start_offset
|
1518
|
+
content.lines.map do |line|
|
1519
|
+
builder.string_internal([line, srange_offsets(offset, offset += line.bytesize)])
|
1520
|
+
end
|
1521
|
+
else
|
1522
|
+
[builder.string_internal(token(node.content_loc))]
|
1523
|
+
end
|
1524
|
+
|
1529
1525
|
builder.regexp_compose(
|
1530
1526
|
token(node.opening_loc),
|
1531
|
-
|
1527
|
+
parts,
|
1532
1528
|
[node.closing[0], srange_offsets(node.closing_loc.start_offset, node.closing_loc.start_offset + 1)],
|
1533
1529
|
builder.regexp_options([node.closing[1..], srange_offsets(node.closing_loc.start_offset + 1, node.closing_loc.end_offset)])
|
1534
1530
|
)
|
@@ -1674,10 +1670,11 @@ module Prism
|
|
1674
1670
|
# ^^^^^
|
1675
1671
|
def visit_string_node(node)
|
1676
1672
|
if node.heredoc?
|
1677
|
-
children, closing
|
1678
|
-
builder.string_compose(token(node.opening_loc), children, closing)
|
1673
|
+
visit_heredoc(node.to_interpolated) { |children, closing| builder.string_compose(token(node.opening_loc), children, closing) }
|
1679
1674
|
elsif node.opening == "?"
|
1680
1675
|
builder.character([node.unescaped, srange(node.location)])
|
1676
|
+
elsif node.opening&.start_with?("%") && node.unescaped.empty?
|
1677
|
+
builder.string_compose(token(node.opening_loc), [], token(node.closing_loc))
|
1681
1678
|
else
|
1682
1679
|
content_lines = node.content.lines
|
1683
1680
|
unescaped_lines = node.unescaped.lines
|
@@ -1788,19 +1785,19 @@ module Prism
|
|
1788
1785
|
builder.condition(
|
1789
1786
|
token(node.keyword_loc),
|
1790
1787
|
visit(node.predicate),
|
1791
|
-
if node.then_keyword_loc
|
1792
|
-
token(
|
1788
|
+
if (then_keyword_loc = node.then_keyword_loc)
|
1789
|
+
token(then_keyword_loc)
|
1793
1790
|
else
|
1794
|
-
srange_find(node.predicate.location.end_offset, (node.statements&.location || node.
|
1791
|
+
srange_find(node.predicate.location.end_offset, (node.statements&.location || node.else_clause&.location || node.end_keyword_loc).start_offset, ";")
|
1795
1792
|
end,
|
1796
|
-
visit(node.
|
1797
|
-
token(node.
|
1793
|
+
visit(node.else_clause),
|
1794
|
+
token(node.else_clause&.else_keyword_loc),
|
1798
1795
|
visit(node.statements),
|
1799
1796
|
token(node.end_keyword_loc)
|
1800
1797
|
)
|
1801
1798
|
else
|
1802
1799
|
builder.condition_mod(
|
1803
|
-
visit(node.
|
1800
|
+
visit(node.else_clause),
|
1804
1801
|
visit(node.statements),
|
1805
1802
|
token(node.keyword_loc),
|
1806
1803
|
visit(node.predicate)
|
@@ -1819,7 +1816,11 @@ module Prism
|
|
1819
1816
|
:until,
|
1820
1817
|
token(node.keyword_loc),
|
1821
1818
|
visit(node.predicate),
|
1822
|
-
|
1819
|
+
if (do_keyword_loc = node.do_keyword_loc)
|
1820
|
+
token(do_keyword_loc)
|
1821
|
+
else
|
1822
|
+
srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, ";")
|
1823
|
+
end,
|
1823
1824
|
visit(node.statements),
|
1824
1825
|
token(node.closing_loc)
|
1825
1826
|
)
|
@@ -1839,10 +1840,10 @@ module Prism
|
|
1839
1840
|
builder.when(
|
1840
1841
|
token(node.keyword_loc),
|
1841
1842
|
visit_all(node.conditions),
|
1842
|
-
if node.then_keyword_loc
|
1843
|
-
token(
|
1843
|
+
if (then_keyword_loc = node.then_keyword_loc)
|
1844
|
+
token(then_keyword_loc)
|
1844
1845
|
else
|
1845
|
-
srange_find(node.conditions.last.location.end_offset, node.statements&.location&.start_offset,
|
1846
|
+
srange_find(node.conditions.last.location.end_offset, node.statements&.location&.start_offset, ";")
|
1846
1847
|
end,
|
1847
1848
|
visit(node.statements)
|
1848
1849
|
)
|
@@ -1859,7 +1860,11 @@ module Prism
|
|
1859
1860
|
:while,
|
1860
1861
|
token(node.keyword_loc),
|
1861
1862
|
visit(node.predicate),
|
1862
|
-
|
1863
|
+
if (do_keyword_loc = node.do_keyword_loc)
|
1864
|
+
token(do_keyword_loc)
|
1865
|
+
else
|
1866
|
+
srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, ";")
|
1867
|
+
end,
|
1863
1868
|
visit(node.statements),
|
1864
1869
|
token(node.closing_loc)
|
1865
1870
|
)
|
@@ -1877,8 +1882,7 @@ module Prism
|
|
1877
1882
|
# ^^^^^
|
1878
1883
|
def visit_x_string_node(node)
|
1879
1884
|
if node.heredoc?
|
1880
|
-
children, closing
|
1881
|
-
builder.xstring_compose(token(node.opening_loc), children, closing)
|
1885
|
+
visit_heredoc(node.to_interpolated) { |children, closing| builder.xstring_compose(token(node.opening_loc), children, closing) }
|
1882
1886
|
else
|
1883
1887
|
parts = if node.unescaped.lines.one?
|
1884
1888
|
[builder.string_internal([node.unescaped, srange(node.content_loc)])]
|
@@ -1940,10 +1944,12 @@ module Prism
|
|
1940
1944
|
forwarding
|
1941
1945
|
end
|
1942
1946
|
|
1943
|
-
#
|
1944
|
-
|
1945
|
-
|
1946
|
-
|
1947
|
+
# Returns the set of targets for a MultiTargetNode or a MultiWriteNode.
|
1948
|
+
def multi_target_elements(node)
|
1949
|
+
elements = [*node.lefts]
|
1950
|
+
elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
|
1951
|
+
elements.concat(node.rights)
|
1952
|
+
elements
|
1947
1953
|
end
|
1948
1954
|
|
1949
1955
|
# Negate the value of a numeric node. This is a special case where you
|
@@ -1955,7 +1961,9 @@ module Prism
|
|
1955
1961
|
case receiver.type
|
1956
1962
|
when :integer_node, :float_node
|
1957
1963
|
receiver.copy(value: -receiver.value, location: message_loc.join(receiver.location))
|
1958
|
-
when :rational_node
|
1964
|
+
when :rational_node
|
1965
|
+
receiver.copy(numerator: -receiver.numerator, location: message_loc.join(receiver.location))
|
1966
|
+
when :imaginary_node
|
1959
1967
|
receiver.copy(numeric: numeric_negate(message_loc, receiver.numeric), location: message_loc.join(receiver.location))
|
1960
1968
|
end
|
1961
1969
|
end
|
@@ -1974,16 +1982,6 @@ module Prism
|
|
1974
1982
|
parameters.block.nil?
|
1975
1983
|
end
|
1976
1984
|
|
1977
|
-
# Because we have mutated the AST to allow for newlines in the middle of
|
1978
|
-
# a rational, we need to manually handle the value here.
|
1979
|
-
def rational_value(node)
|
1980
|
-
if node.numeric.is_a?(IntegerNode)
|
1981
|
-
Rational(node.numeric.value)
|
1982
|
-
else
|
1983
|
-
Rational(node.slice.gsub(/\s/, "").chomp("r"))
|
1984
|
-
end
|
1985
|
-
end
|
1986
|
-
|
1987
1985
|
# Locations in the parser gem AST are generated using this class. We
|
1988
1986
|
# store a reference to its constant to make it slightly faster to look
|
1989
1987
|
# up.
|
@@ -1999,18 +1997,16 @@ module Prism
|
|
1999
1997
|
Range.new(source_buffer, offset_cache[start_offset], offset_cache[end_offset])
|
2000
1998
|
end
|
2001
1999
|
|
2002
|
-
# Constructs a new source range by finding the given
|
2003
|
-
# given start offset and end offset. If the needle is not found, it
|
2000
|
+
# Constructs a new source range by finding the given character between
|
2001
|
+
# the given start offset and end offset. If the needle is not found, it
|
2004
2002
|
# returns nil. Importantly it does not search past newlines or comments.
|
2005
2003
|
#
|
2006
2004
|
# Note that end_offset is allowed to be nil, in which case this will
|
2007
2005
|
# search until the end of the string.
|
2008
|
-
def srange_find(start_offset, end_offset,
|
2009
|
-
if (match = source_buffer.source.byteslice(start_offset...end_offset)
|
2010
|
-
|
2011
|
-
|
2012
|
-
|
2013
|
-
[token, Range.new(source_buffer, offset_cache[token_offset], offset_cache[token_offset + token.bytesize])]
|
2006
|
+
def srange_find(start_offset, end_offset, character)
|
2007
|
+
if (match = source_buffer.source.byteslice(start_offset...end_offset)[/\A\s*#{character}/])
|
2008
|
+
final_offset = start_offset + match.bytesize
|
2009
|
+
[character, Range.new(source_buffer, offset_cache[final_offset - character.bytesize], offset_cache[final_offset])]
|
2014
2010
|
end
|
2015
2011
|
end
|
2016
2012
|
|
@@ -2037,7 +2033,8 @@ module Prism
|
|
2037
2033
|
token(parameters.opening_loc),
|
2038
2034
|
if procarg0?(parameters.parameters)
|
2039
2035
|
parameter = parameters.parameters.requireds.first
|
2040
|
-
|
2036
|
+
visited = parameter.is_a?(RequiredParameterNode) ? visit(parameter) : parameter.accept(copy_compiler(in_destructure: true))
|
2037
|
+
[builder.procarg0(visited)].concat(visit_all(parameters.locals))
|
2041
2038
|
else
|
2042
2039
|
visit(parameters)
|
2043
2040
|
end,
|
@@ -2053,29 +2050,55 @@ module Prism
|
|
2053
2050
|
end
|
2054
2051
|
end
|
2055
2052
|
|
2053
|
+
# The parser gem automatically converts \r\n to \n, meaning our offsets
|
2054
|
+
# need to be adjusted to always subtract 1 from the length.
|
2055
|
+
def chomped_bytesize(line)
|
2056
|
+
chomped = line.chomp
|
2057
|
+
chomped.bytesize + (chomped == line ? 0 : 1)
|
2058
|
+
end
|
2059
|
+
|
2056
2060
|
# Visit a heredoc that can be either a string or an xstring.
|
2057
2061
|
def visit_heredoc(node)
|
2058
2062
|
children = Array.new
|
2063
|
+
indented = false
|
2064
|
+
|
2065
|
+
# If this is a dedenting heredoc, then we need to insert the opening
|
2066
|
+
# content into the children as well.
|
2067
|
+
if node.opening.start_with?("<<~") && node.parts.length > 0 && !node.parts.first.is_a?(StringNode)
|
2068
|
+
location = node.parts.first.location
|
2069
|
+
location = location.copy(start_offset: location.start_offset - location.start_line_slice.bytesize)
|
2070
|
+
children << builder.string_internal(token(location))
|
2071
|
+
indented = true
|
2072
|
+
end
|
2073
|
+
|
2059
2074
|
node.parts.each do |part|
|
2060
2075
|
pushing =
|
2061
2076
|
if part.is_a?(StringNode) && part.unescaped.include?("\n")
|
2062
|
-
unescaped = part.unescaped.lines
|
2063
|
-
escaped = part.content.lines
|
2077
|
+
unescaped = part.unescaped.lines
|
2078
|
+
escaped = part.content.lines
|
2064
2079
|
|
2065
|
-
escaped_lengths =
|
2066
|
-
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2080
|
+
escaped_lengths = []
|
2081
|
+
normalized_lengths = []
|
2082
|
+
|
2083
|
+
if node.opening.end_with?("'")
|
2084
|
+
escaped.each do |line|
|
2085
|
+
escaped_lengths << line.bytesize
|
2086
|
+
normalized_lengths << chomped_bytesize(line)
|
2070
2087
|
end
|
2088
|
+
else
|
2089
|
+
escaped
|
2090
|
+
.chunk_while { |before, after| before.match?(/(?<!\\)\\\r?\n$/) }
|
2091
|
+
.each do |lines|
|
2092
|
+
escaped_lengths << lines.sum(&:bytesize)
|
2093
|
+
normalized_lengths << lines.sum { |line| chomped_bytesize(line) }
|
2094
|
+
end
|
2095
|
+
end
|
2071
2096
|
|
2072
2097
|
start_offset = part.location.start_offset
|
2073
|
-
end_offset = nil
|
2074
2098
|
|
2075
|
-
unescaped.
|
2076
|
-
|
2077
|
-
|
2078
|
-
start_offset = end_offset
|
2099
|
+
unescaped.map.with_index do |unescaped_line, index|
|
2100
|
+
inner_part = builder.string_internal([unescaped_line, srange_offsets(start_offset, start_offset + normalized_lengths.fetch(index, 0))])
|
2101
|
+
start_offset += escaped_lengths.fetch(index, 0)
|
2079
2102
|
inner_part
|
2080
2103
|
end
|
2081
2104
|
else
|
@@ -2086,7 +2109,12 @@ module Prism
|
|
2086
2109
|
if child.type == :str && child.children.last == ""
|
2087
2110
|
# nothing
|
2088
2111
|
elsif child.type == :str && children.last && children.last.type == :str && !children.last.children.first.end_with?("\n")
|
2089
|
-
|
2112
|
+
appendee = children[-1]
|
2113
|
+
|
2114
|
+
location = appendee.loc
|
2115
|
+
location = location.with_expression(location.expression.join(child.loc.expression))
|
2116
|
+
|
2117
|
+
children[-1] = appendee.updated(:str, [appendee.children.first << child.children.first], location: location)
|
2090
2118
|
else
|
2091
2119
|
children << child
|
2092
2120
|
end
|
@@ -2095,8 +2123,10 @@ module Prism
|
|
2095
2123
|
|
2096
2124
|
closing = node.closing
|
2097
2125
|
closing_t = [closing.chomp, srange_offsets(node.closing_loc.start_offset, node.closing_loc.end_offset - (closing[/\s+$/]&.length || 0))]
|
2126
|
+
composed = yield children, closing_t
|
2098
2127
|
|
2099
|
-
|
2128
|
+
composed = composed.updated(nil, children[1..-1]) if indented
|
2129
|
+
composed
|
2100
2130
|
end
|
2101
2131
|
|
2102
2132
|
# Visit a numeric node and account for the optional sign.
|