prism 0.29.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|