prism 0.29.0 → 0.30.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -1
  3. data/CONTRIBUTING.md +0 -4
  4. data/README.md +1 -0
  5. data/config.yml +66 -9
  6. data/docs/fuzzing.md +1 -1
  7. data/docs/ripper_translation.md +22 -0
  8. data/ext/prism/api_node.c +30 -12
  9. data/ext/prism/extension.c +107 -372
  10. data/ext/prism/extension.h +1 -1
  11. data/include/prism/ast.h +138 -70
  12. data/include/prism/diagnostic.h +7 -2
  13. data/include/prism/node.h +0 -21
  14. data/include/prism/parser.h +23 -25
  15. data/include/prism/regexp.h +17 -8
  16. data/include/prism/static_literals.h +3 -2
  17. data/include/prism/util/pm_char.h +1 -2
  18. data/include/prism/util/pm_constant_pool.h +0 -8
  19. data/include/prism/util/pm_integer.h +16 -9
  20. data/include/prism/util/pm_string.h +0 -8
  21. data/include/prism/version.h +2 -2
  22. data/include/prism.h +0 -11
  23. data/lib/prism/compiler.rb +3 -0
  24. data/lib/prism/dispatcher.rb +14 -0
  25. data/lib/prism/dot_visitor.rb +22 -3
  26. data/lib/prism/dsl.rb +7 -2
  27. data/lib/prism/ffi.rb +24 -3
  28. data/lib/prism/inspect_visitor.rb +10 -8
  29. data/lib/prism/mutation_compiler.rb +6 -1
  30. data/lib/prism/node.rb +166 -241
  31. data/lib/prism/node_ext.rb +21 -5
  32. data/lib/prism/parse_result/comments.rb +0 -7
  33. data/lib/prism/parse_result/newlines.rb +101 -11
  34. data/lib/prism/parse_result.rb +17 -0
  35. data/lib/prism/reflection.rb +3 -1
  36. data/lib/prism/serialize.rb +80 -67
  37. data/lib/prism/translation/parser/compiler.rb +134 -114
  38. data/lib/prism/translation/parser.rb +6 -1
  39. data/lib/prism/translation/ripper.rb +8 -6
  40. data/lib/prism/translation/ruby_parser.rb +23 -5
  41. data/lib/prism/visitor.rb +3 -0
  42. data/lib/prism.rb +0 -4
  43. data/prism.gemspec +1 -4
  44. data/rbi/prism/node.rbi +63 -6
  45. data/rbi/prism/visitor.rbi +3 -0
  46. data/rbi/prism.rbi +6 -0
  47. data/sig/prism/dsl.rbs +4 -1
  48. data/sig/prism/mutation_compiler.rbs +1 -0
  49. data/sig/prism/node.rbs +28 -4
  50. data/sig/prism/visitor.rbs +1 -0
  51. data/sig/prism.rbs +21 -0
  52. data/src/diagnostic.c +27 -17
  53. data/src/node.c +408 -1666
  54. data/src/prettyprint.c +49 -6
  55. data/src/prism.c +958 -991
  56. data/src/regexp.c +133 -68
  57. data/src/serialize.c +6 -1
  58. data/src/static_literals.c +63 -84
  59. data/src/token_type.c +2 -2
  60. data/src/util/pm_constant_pool.c +0 -8
  61. data/src/util/pm_integer.c +39 -11
  62. data/src/util/pm_string.c +0 -12
  63. data/src/util/pm_strpbrk.c +32 -6
  64. metadata +2 -5
  65. data/include/prism/util/pm_string_list.h +0 -44
  66. data/lib/prism/debug.rb +0 -249
  67. 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
- builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.array_pattern(nil, visited, nil), token(node.closing_loc))
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 node.key.is_a?(SymbolNode)
111
- builder.match_hash_var([node.key.unescaped, srange(node.key.location)])
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(node.key.opening_loc), visit_all(node.key.parts), token(node.key.closing_loc))
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.pair_keyword([node.key.unescaped, srange(node.key.location)], visit(node.value))
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
- [node.key.unescaped, srange(node.key)],
122
- builder.ident([value.name, srange(node.key.value_loc)]).updated(:lvar)
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([node.key.unescaped, srange(node.key.location)])
137
+ builder.pair_label([key.unescaped, srange(key.location)])
126
138
  end
127
139
  elsif node.operator_loc
128
- builder.pair(visit(node.key), token(node.operator_loc), visit(node.value))
129
- elsif node.key.is_a?(SymbolNode) && node.key.opening_loc.nil?
130
- builder.pair_keyword([node.key.unescaped, srange(node.key.location)], visit(node.value))
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 node.key.is_a?(SymbolNode)
134
- [builder.string_internal([node.key.unescaped, srange(node.key.value_loc)])]
145
+ if key.is_a?(SymbolNode)
146
+ [builder.string_internal([key.unescaped, srange(key.value_loc)])]
135
147
  else
136
- visit_all(node.key.parts)
148
+ visit_all(key.parts)
137
149
  end
138
150
 
139
- builder.pair_quoted(token(node.key.opening_loc), parts, token(node.key.closing_loc), visit(node.value))
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 node.value.nil? && forwarding.include?(:**)
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))
@@ -881,7 +895,7 @@ module Prism
881
895
  # 1i
882
896
  # ^^
883
897
  def visit_imaginary_node(node)
884
- visit_numeric(node, builder.complex([imaginary_value(node), srange(node.location)]))
898
+ visit_numeric(node, builder.complex([Complex(0, node.numeric.value), srange(node.location)]))
885
899
  end
886
900
 
887
901
  # { foo: }
@@ -1064,36 +1078,7 @@ module Prism
1064
1078
  # ^^^^^^^^^^^^
1065
1079
  def visit_interpolated_string_node(node)
1066
1080
  if node.heredoc?
1067
- children, closing = visit_heredoc(node)
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
1081
+ return visit_heredoc(node) { |children, closing| builder.string_compose(token(node.opening_loc), children, closing) }
1097
1082
  end
1098
1083
 
1099
1084
  parts = if node.parts.one? { |part| part.type == :string_node }
@@ -1137,8 +1122,7 @@ module Prism
1137
1122
  # ^^^^^^^^^^^^
1138
1123
  def visit_interpolated_x_string_node(node)
1139
1124
  if node.heredoc?
1140
- children, closing = visit_heredoc(node)
1141
- builder.xstring_compose(token(node.opening_loc), children, closing)
1125
+ visit_heredoc(node) { |children, closing| builder.xstring_compose(token(node.opening_loc), children, closing) }
1142
1126
  else
1143
1127
  builder.xstring_compose(
1144
1128
  token(node.opening_loc),
@@ -1148,6 +1132,12 @@ module Prism
1148
1132
  end
1149
1133
  end
1150
1134
 
1135
+ # -> { it }
1136
+ # ^^
1137
+ def visit_it_local_variable_read_node(node)
1138
+ builder.ident([:it, srange(node.location)]).updated(:lvar)
1139
+ end
1140
+
1151
1141
  # -> { it }
1152
1142
  # ^^^^^^^^^
1153
1143
  def visit_it_parameters_node(node)
@@ -1201,14 +1191,7 @@ module Prism
1201
1191
  # foo
1202
1192
  # ^^^
1203
1193
  def visit_local_variable_read_node(node)
1204
- name = node.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)
1194
+ builder.ident([node.name, srange(node.location)]).updated(:lvar)
1212
1195
  end
1213
1196
 
1214
1197
  # foo = 1
@@ -1312,13 +1295,9 @@ module Prism
1312
1295
  # foo, bar = baz
1313
1296
  # ^^^^^^^^
1314
1297
  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
1298
  builder.multi_lhs(
1320
1299
  token(node.lparen_loc),
1321
- visit_all(elements),
1300
+ visit_all(multi_target_elements(node)),
1322
1301
  token(node.rparen_loc)
1323
1302
  )
1324
1303
  end
@@ -1326,9 +1305,11 @@ module Prism
1326
1305
  # foo, bar = baz
1327
1306
  # ^^^^^^^^^^^^^^
1328
1307
  def visit_multi_write_node(node)
1329
- elements = [*node.lefts]
1330
- elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
1331
- elements.concat(node.rights)
1308
+ elements = multi_target_elements(node)
1309
+
1310
+ if elements.length == 1 && elements.first.is_a?(MultiTargetNode)
1311
+ elements = multi_target_elements(elements.first)
1312
+ end
1332
1313
 
1333
1314
  builder.multi_assign(
1334
1315
  builder.multi_lhs(
@@ -1409,12 +1390,12 @@ module Prism
1409
1390
 
1410
1391
  if node.requireds.any?
1411
1392
  node.requireds.each do |required|
1412
- if required.is_a?(RequiredParameterNode)
1413
- params << visit(required)
1414
- else
1415
- compiler = copy_compiler(in_destructure: true)
1416
- params << required.accept(compiler)
1417
- end
1393
+ params <<
1394
+ if required.is_a?(RequiredParameterNode)
1395
+ visit(required)
1396
+ else
1397
+ required.accept(copy_compiler(in_destructure: true))
1398
+ end
1418
1399
  end
1419
1400
  end
1420
1401
 
@@ -1423,12 +1404,12 @@ module Prism
1423
1404
 
1424
1405
  if node.posts.any?
1425
1406
  node.posts.each do |post|
1426
- if post.is_a?(RequiredParameterNode)
1427
- params << visit(post)
1428
- else
1429
- compiler = copy_compiler(in_destructure: true)
1430
- params << post.accept(compiler)
1431
- end
1407
+ params <<
1408
+ if post.is_a?(RequiredParameterNode)
1409
+ visit(post)
1410
+ else
1411
+ post.accept(copy_compiler(in_destructure: true))
1412
+ end
1432
1413
  end
1433
1414
  end
1434
1415
 
@@ -1514,7 +1495,7 @@ module Prism
1514
1495
  # 1r
1515
1496
  # ^^
1516
1497
  def visit_rational_node(node)
1517
- visit_numeric(node, builder.rational([rational_value(node), srange(node.location)]))
1498
+ visit_numeric(node, builder.rational([node.value, srange(node.location)]))
1518
1499
  end
1519
1500
 
1520
1501
  # redo
@@ -1526,9 +1507,20 @@ module Prism
1526
1507
  # /foo/
1527
1508
  # ^^^^^
1528
1509
  def visit_regular_expression_node(node)
1510
+ content = node.content
1511
+ parts =
1512
+ if content.include?("\n")
1513
+ offset = node.content_loc.start_offset
1514
+ content.lines.map do |line|
1515
+ builder.string_internal([line, srange_offsets(offset, offset += line.bytesize)])
1516
+ end
1517
+ else
1518
+ [builder.string_internal(token(node.content_loc))]
1519
+ end
1520
+
1529
1521
  builder.regexp_compose(
1530
1522
  token(node.opening_loc),
1531
- [builder.string_internal(token(node.content_loc))],
1523
+ parts,
1532
1524
  [node.closing[0], srange_offsets(node.closing_loc.start_offset, node.closing_loc.start_offset + 1)],
1533
1525
  builder.regexp_options([node.closing[1..], srange_offsets(node.closing_loc.start_offset + 1, node.closing_loc.end_offset)])
1534
1526
  )
@@ -1674,10 +1666,11 @@ module Prism
1674
1666
  # ^^^^^
1675
1667
  def visit_string_node(node)
1676
1668
  if node.heredoc?
1677
- children, closing = visit_heredoc(node.to_interpolated)
1678
- builder.string_compose(token(node.opening_loc), children, closing)
1669
+ visit_heredoc(node.to_interpolated) { |children, closing| builder.string_compose(token(node.opening_loc), children, closing) }
1679
1670
  elsif node.opening == "?"
1680
1671
  builder.character([node.unescaped, srange(node.location)])
1672
+ elsif node.opening&.start_with?("%") && node.unescaped.empty?
1673
+ builder.string_compose(token(node.opening_loc), [], token(node.closing_loc))
1681
1674
  else
1682
1675
  content_lines = node.content.lines
1683
1676
  unescaped_lines = node.unescaped.lines
@@ -1877,8 +1870,7 @@ module Prism
1877
1870
  # ^^^^^
1878
1871
  def visit_x_string_node(node)
1879
1872
  if node.heredoc?
1880
- children, closing = visit_heredoc(node.to_interpolated)
1881
- builder.xstring_compose(token(node.opening_loc), children, closing)
1873
+ visit_heredoc(node.to_interpolated) { |children, closing| builder.xstring_compose(token(node.opening_loc), children, closing) }
1882
1874
  else
1883
1875
  parts = if node.unescaped.lines.one?
1884
1876
  [builder.string_internal([node.unescaped, srange(node.content_loc)])]
@@ -1940,10 +1932,12 @@ module Prism
1940
1932
  forwarding
1941
1933
  end
1942
1934
 
1943
- # Because we have mutated the AST to allow for newlines in the middle of
1944
- # a rational, we need to manually handle the value here.
1945
- def imaginary_value(node)
1946
- Complex(0, node.numeric.is_a?(RationalNode) ? rational_value(node.numeric) : node.numeric.value)
1935
+ # Returns the set of targets for a MultiTargetNode or a MultiWriteNode.
1936
+ def multi_target_elements(node)
1937
+ elements = [*node.lefts]
1938
+ elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
1939
+ elements.concat(node.rights)
1940
+ elements
1947
1941
  end
1948
1942
 
1949
1943
  # Negate the value of a numeric node. This is a special case where you
@@ -1955,7 +1949,9 @@ module Prism
1955
1949
  case receiver.type
1956
1950
  when :integer_node, :float_node
1957
1951
  receiver.copy(value: -receiver.value, location: message_loc.join(receiver.location))
1958
- when :rational_node, :imaginary_node
1952
+ when :rational_node
1953
+ receiver.copy(numerator: -receiver.numerator, location: message_loc.join(receiver.location))
1954
+ when :imaginary_node
1959
1955
  receiver.copy(numeric: numeric_negate(message_loc, receiver.numeric), location: message_loc.join(receiver.location))
1960
1956
  end
1961
1957
  end
@@ -1974,16 +1970,6 @@ module Prism
1974
1970
  parameters.block.nil?
1975
1971
  end
1976
1972
 
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
1973
  # Locations in the parser gem AST are generated using this class. We
1988
1974
  # store a reference to its constant to make it slightly faster to look
1989
1975
  # up.
@@ -2006,7 +1992,7 @@ module Prism
2006
1992
  # Note that end_offset is allowed to be nil, in which case this will
2007
1993
  # search until the end of the string.
2008
1994
  def srange_find(start_offset, end_offset, tokens)
2009
- if (match = source_buffer.source.byteslice(start_offset...end_offset).match(/(\s*)(#{tokens.join("|")})/))
1995
+ if (match = source_buffer.source.byteslice(start_offset...end_offset).match(/\A(\s*)(#{tokens.join("|")})/))
2010
1996
  _, whitespace, token = *match
2011
1997
  token_offset = start_offset + whitespace.bytesize
2012
1998
 
@@ -2037,7 +2023,8 @@ module Prism
2037
2023
  token(parameters.opening_loc),
2038
2024
  if procarg0?(parameters.parameters)
2039
2025
  parameter = parameters.parameters.requireds.first
2040
- [builder.procarg0(visit(parameter))].concat(visit_all(parameters.locals))
2026
+ visited = parameter.is_a?(RequiredParameterNode) ? visit(parameter) : parameter.accept(copy_compiler(in_destructure: true))
2027
+ [builder.procarg0(visited)].concat(visit_all(parameters.locals))
2041
2028
  else
2042
2029
  visit(parameters)
2043
2030
  end,
@@ -2053,29 +2040,55 @@ module Prism
2053
2040
  end
2054
2041
  end
2055
2042
 
2043
+ # The parser gem automatically converts \r\n to \n, meaning our offsets
2044
+ # need to be adjusted to always subtract 1 from the length.
2045
+ def chomped_bytesize(line)
2046
+ chomped = line.chomp
2047
+ chomped.bytesize + (chomped == line ? 0 : 1)
2048
+ end
2049
+
2056
2050
  # Visit a heredoc that can be either a string or an xstring.
2057
2051
  def visit_heredoc(node)
2058
2052
  children = Array.new
2053
+ indented = false
2054
+
2055
+ # If this is a dedenting heredoc, then we need to insert the opening
2056
+ # content into the children as well.
2057
+ if node.opening.start_with?("<<~") && node.parts.length > 0 && !node.parts.first.is_a?(StringNode)
2058
+ location = node.parts.first.location
2059
+ location = location.copy(start_offset: location.start_offset - location.start_line_slice.bytesize)
2060
+ children << builder.string_internal(token(location))
2061
+ indented = true
2062
+ end
2063
+
2059
2064
  node.parts.each do |part|
2060
2065
  pushing =
2061
2066
  if part.is_a?(StringNode) && part.unescaped.include?("\n")
2062
- unescaped = part.unescaped.lines(chomp: true)
2063
- escaped = part.content.lines(chomp: true)
2067
+ unescaped = part.unescaped.lines
2068
+ escaped = part.content.lines
2064
2069
 
2065
- escaped_lengths =
2066
- if node.opening.end_with?("'")
2067
- escaped.map { |line| line.bytesize + 1 }
2068
- else
2069
- escaped.chunk_while { |before, after| before.match?(/(?<!\\)\\$/) }.map { |line| line.join.bytesize + line.length }
2070
+ escaped_lengths = []
2071
+ normalized_lengths = []
2072
+
2073
+ if node.opening.end_with?("'")
2074
+ escaped.each do |line|
2075
+ escaped_lengths << line.bytesize
2076
+ normalized_lengths << chomped_bytesize(line)
2070
2077
  end
2078
+ else
2079
+ escaped
2080
+ .chunk_while { |before, after| before.match?(/(?<!\\)\\\r?\n$/) }
2081
+ .each do |lines|
2082
+ escaped_lengths << lines.sum(&:bytesize)
2083
+ normalized_lengths << lines.sum { |line| chomped_bytesize(line) }
2084
+ end
2085
+ end
2071
2086
 
2072
2087
  start_offset = part.location.start_offset
2073
- end_offset = nil
2074
2088
 
2075
- unescaped.zip(escaped_lengths).map do |unescaped_line, escaped_length|
2076
- end_offset = start_offset + (escaped_length || 0)
2077
- inner_part = builder.string_internal(["#{unescaped_line}\n", srange_offsets(start_offset, end_offset)])
2078
- start_offset = end_offset
2089
+ unescaped.map.with_index do |unescaped_line, index|
2090
+ inner_part = builder.string_internal([unescaped_line, srange_offsets(start_offset, start_offset + normalized_lengths.fetch(index, 0))])
2091
+ start_offset += escaped_lengths.fetch(index, 0)
2079
2092
  inner_part
2080
2093
  end
2081
2094
  else
@@ -2086,7 +2099,12 @@ module Prism
2086
2099
  if child.type == :str && child.children.last == ""
2087
2100
  # nothing
2088
2101
  elsif child.type == :str && children.last && children.last.type == :str && !children.last.children.first.end_with?("\n")
2089
- children.last.children.first << child.children.first
2102
+ appendee = children[-1]
2103
+
2104
+ location = appendee.loc
2105
+ location = location.with_expression(location.expression.join(child.loc.expression))
2106
+
2107
+ children[-1] = appendee.updated(:str, [appendee.children.first << child.children.first], location: location)
2090
2108
  else
2091
2109
  children << child
2092
2110
  end
@@ -2095,8 +2113,10 @@ module Prism
2095
2113
 
2096
2114
  closing = node.closing
2097
2115
  closing_t = [closing.chomp, srange_offsets(node.closing_loc.start_offset, node.closing_loc.end_offset - (closing[/\s+$/]&.length || 0))]
2116
+ composed = yield children, closing_t
2098
2117
 
2099
- [children, closing_t]
2118
+ composed = composed.updated(nil, children[1..-1]) if indented
2119
+ composed
2100
2120
  end
2101
2121
 
2102
2122
  # Visit a numeric node and account for the optional sign.
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parser"
3
+ begin
4
+ require "parser"
5
+ rescue LoadError
6
+ warn(%q{Error: Unable to load parser. Add `gem "parser"` to your Gemfile.})
7
+ exit(1)
8
+ end
4
9
 
5
10
  module Prism
6
11
  module Translation
@@ -2217,6 +2217,13 @@ module Prism
2217
2217
  end
2218
2218
  end
2219
2219
 
2220
+ # -> { it }
2221
+ # ^^
2222
+ def visit_it_local_variable_read_node(node)
2223
+ bounds(node.location)
2224
+ on_vcall(on_ident(node.slice))
2225
+ end
2226
+
2220
2227
  # -> { it }
2221
2228
  # ^^^^^^^^^
2222
2229
  def visit_it_parameters_node(node)
@@ -2312,12 +2319,7 @@ module Prism
2312
2319
  # ^^^
2313
2320
  def visit_local_variable_read_node(node)
2314
2321
  bounds(node.location)
2315
-
2316
- if node.name == :"0it"
2317
- on_vcall(on_ident(node.slice))
2318
- else
2319
- on_var_ref(on_ident(node.slice))
2320
- end
2322
+ on_var_ref(on_ident(node.slice))
2321
2323
  end
2322
2324
 
2323
2325
  # foo = 1
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "ruby_parser"
3
+ begin
4
+ require "ruby_parser"
5
+ rescue LoadError
6
+ warn(%q{Error: Unable to load ruby_parser. Add `gem "ruby_parser"` to your Gemfile.})
7
+ exit(1)
8
+ end
4
9
 
5
10
  module Prism
6
11
  module Translation
@@ -480,9 +485,9 @@ module Prism
480
485
  def visit_constant_path_target_node(node)
481
486
  inner =
482
487
  if node.parent.nil?
483
- s(node, :colon3, node.child.name)
488
+ s(node, :colon3, node.name)
484
489
  else
485
- s(node, :colon2, visit(node.parent), node.child.name)
490
+ s(node, :colon2, visit(node.parent), node.name)
486
491
  end
487
492
 
488
493
  s(node, :const, inner)
@@ -870,6 +875,8 @@ module Prism
870
875
  else
871
876
  visited << result
872
877
  end
878
+ elsif result[0] == :dstr
879
+ visited.concat(result[1..-1])
873
880
  else
874
881
  visited << result
875
882
  end
@@ -900,12 +907,23 @@ module Prism
900
907
  results << result
901
908
  state = :interpolated_content
902
909
  end
903
- else
904
- results << result
910
+ when :interpolated_content
911
+ if result.is_a?(Array) && result[0] == :str && results[-1][0] == :str && (results[-1].line_max == result.line)
912
+ results[-1][1] << result[1]
913
+ results[-1].line_max = result.line_max
914
+ else
915
+ results << result
916
+ end
905
917
  end
906
918
  end
907
919
  end
908
920
 
921
+ # -> { it }
922
+ # ^^
923
+ def visit_it_local_variable_read_node(node)
924
+ s(node, :call, nil, :it)
925
+ end
926
+
909
927
  # foo(bar: baz)
910
928
  # ^^^^^^^^
911
929
  def visit_keyword_hash_node(node)
data/lib/prism/visitor.rb CHANGED
@@ -313,6 +313,9 @@ module Prism
313
313
  # Visit a InterpolatedXStringNode node
314
314
  alias visit_interpolated_x_string_node visit_child_nodes
315
315
 
316
+ # Visit a ItLocalVariableReadNode node
317
+ alias visit_it_local_variable_read_node visit_child_nodes
318
+
316
319
  # Visit a ItParametersNode node
317
320
  alias visit_it_parameters_node visit_child_nodes
318
321
 
data/lib/prism.rb CHANGED
@@ -13,7 +13,6 @@ module Prism
13
13
 
14
14
  autoload :BasicVisitor, "prism/visitor"
15
15
  autoload :Compiler, "prism/compiler"
16
- autoload :Debug, "prism/debug"
17
16
  autoload :DesugarCompiler, "prism/desugar_compiler"
18
17
  autoload :Dispatcher, "prism/dispatcher"
19
18
  autoload :DotVisitor, "prism/dot_visitor"
@@ -32,7 +31,6 @@ module Prism
32
31
  # Some of these constants are not meant to be exposed, so marking them as
33
32
  # private here.
34
33
 
35
- private_constant :Debug
36
34
  private_constant :LexCompat
37
35
  private_constant :LexRipper
38
36
 
@@ -71,8 +69,6 @@ require_relative "prism/polyfill/byteindex"
71
69
  require_relative "prism/node"
72
70
  require_relative "prism/node_ext"
73
71
  require_relative "prism/parse_result"
74
- require_relative "prism/parse_result/comments"
75
- require_relative "prism/parse_result/newlines"
76
72
 
77
73
  # This is a Ruby implementation of the prism parser. If we're running on CRuby
78
74
  # and we haven't explicitly set the PRISM_FFI_BACKEND environment variable, then
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.29.0"
5
+ spec.version = "0.30.0"
6
6
  spec.authors = ["Shopify"]
7
7
  spec.email = ["ruby@shopify.com"]
8
8
 
@@ -65,12 +65,10 @@ Gem::Specification.new do |spec|
65
65
  "include/prism/util/pm_newline_list.h",
66
66
  "include/prism/util/pm_strncasecmp.h",
67
67
  "include/prism/util/pm_string.h",
68
- "include/prism/util/pm_string_list.h",
69
68
  "include/prism/util/pm_strpbrk.h",
70
69
  "include/prism/version.h",
71
70
  "lib/prism.rb",
72
71
  "lib/prism/compiler.rb",
73
- "lib/prism/debug.rb",
74
72
  "lib/prism/desugar_compiler.rb",
75
73
  "lib/prism/dispatcher.rb",
76
74
  "lib/prism/dot_visitor.rb",
@@ -149,7 +147,6 @@ Gem::Specification.new do |spec|
149
147
  "src/util/pm_list.c",
150
148
  "src/util/pm_memchr.c",
151
149
  "src/util/pm_newline_list.c",
152
- "src/util/pm_string_list.c",
153
150
  "src/util/pm_string.c",
154
151
  "src/util/pm_strncasecmp.c",
155
152
  "src/util/pm_strpbrk.c"