prism 1.1.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -1
  3. data/Makefile +1 -1
  4. data/config.yml +422 -3
  5. data/docs/build_system.md +8 -11
  6. data/docs/relocation.md +34 -0
  7. data/ext/prism/api_node.c +18 -10
  8. data/ext/prism/extconf.rb +13 -36
  9. data/ext/prism/extension.c +68 -0
  10. data/ext/prism/extension.h +1 -1
  11. data/include/prism/ast.h +427 -3
  12. data/include/prism/defines.h +22 -7
  13. data/include/prism/diagnostic.h +1 -0
  14. data/include/prism/parser.h +25 -12
  15. data/include/prism/version.h +2 -2
  16. data/include/prism.h +47 -0
  17. data/lib/prism/dot_visitor.rb +10 -0
  18. data/lib/prism/dsl.rb +4 -4
  19. data/lib/prism/ffi.rb +49 -2
  20. data/lib/prism/inspect_visitor.rb +2 -0
  21. data/lib/prism/node.rb +1839 -96
  22. data/lib/prism/parse_result/errors.rb +1 -1
  23. data/lib/prism/parse_result.rb +140 -3
  24. data/lib/prism/reflection.rb +2 -2
  25. data/lib/prism/relocation.rb +504 -0
  26. data/lib/prism/serialize.rb +17 -5
  27. data/lib/prism/string_query.rb +30 -0
  28. data/lib/prism/translation/parser/compiler.rb +36 -26
  29. data/lib/prism/translation/parser.rb +3 -3
  30. data/lib/prism/translation/ripper.rb +1 -5
  31. data/lib/prism/translation/ruby_parser.rb +14 -5
  32. data/lib/prism.rb +6 -4
  33. data/prism.gemspec +7 -1
  34. data/rbi/prism/dsl.rbi +4 -4
  35. data/rbi/prism/node.rbi +5118 -1030
  36. data/rbi/prism/parse_result.rbi +29 -0
  37. data/rbi/prism/string_query.rbi +12 -0
  38. data/rbi/prism.rbi +34 -34
  39. data/sig/prism/dsl.rbs +2 -2
  40. data/sig/prism/node.rbs +13 -98
  41. data/sig/prism/parse_result.rbs +20 -0
  42. data/sig/prism/relocation.rbs +185 -0
  43. data/sig/prism/string_query.rbs +11 -0
  44. data/src/diagnostic.c +3 -1
  45. data/src/node.c +18 -0
  46. data/src/prettyprint.c +32 -0
  47. data/src/prism.c +586 -195
  48. data/src/regexp.c +7 -3
  49. data/src/serialize.c +12 -0
  50. data/src/static_literals.c +1 -1
  51. data/src/util/pm_char.c +1 -1
  52. data/src/util/pm_string.c +1 -0
  53. metadata +9 -3
@@ -188,7 +188,7 @@ module Prism
188
188
  rescue_clause.exceptions.any? ? builder.array(nil, visit_all(rescue_clause.exceptions), nil) : nil,
189
189
  token(rescue_clause.operator_loc),
190
190
  visit(rescue_clause.reference),
191
- srange_find(find_start_offset, find_end_offset, [";"]),
191
+ srange_find(find_start_offset, find_end_offset, ";"),
192
192
  visit(rescue_clause.statements)
193
193
  )
194
194
  end until (rescue_clause = rescue_clause.subsequent).nil?
@@ -294,7 +294,7 @@ module Prism
294
294
  visit_all(arguments),
295
295
  token(node.closing_loc),
296
296
  ),
297
- 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, "="),
298
298
  visit(node.arguments.arguments.last)
299
299
  ),
300
300
  block
@@ -311,7 +311,7 @@ module Prism
311
311
  if name.end_with?("=") && !message_loc.slice.end_with?("=") && node.arguments && block.nil?
312
312
  builder.assign(
313
313
  builder.attr_asgn(visit(node.receiver), call_operator, token(message_loc)),
314
- srange_find(message_loc.end_offset, node.arguments.location.start_offset, ["="]),
314
+ srange_find(message_loc.end_offset, node.arguments.location.start_offset, "="),
315
315
  visit(node.arguments.arguments.last)
316
316
  )
317
317
  else
@@ -733,10 +733,10 @@ module Prism
733
733
  visit(node.index),
734
734
  token(node.in_keyword_loc),
735
735
  visit(node.collection),
736
- if node.do_keyword_loc
737
- token(node.do_keyword_loc)
736
+ if (do_keyword_loc = node.do_keyword_loc)
737
+ token(do_keyword_loc)
738
738
  else
739
- 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, ";")
740
740
  end,
741
741
  visit(node.statements),
742
742
  token(node.end_keyword_loc)
@@ -865,10 +865,10 @@ module Prism
865
865
  builder.condition(
866
866
  token(node.if_keyword_loc),
867
867
  visit(node.predicate),
868
- if node.then_keyword_loc
869
- token(node.then_keyword_loc)
868
+ if (then_keyword_loc = node.then_keyword_loc)
869
+ token(then_keyword_loc)
870
870
  else
871
- srange_find(node.predicate.location.end_offset, (node.statements&.location || node.subsequent&.location || node.end_keyword_loc).start_offset, [";"])
871
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.subsequent&.location || node.end_keyword_loc).start_offset, ";")
872
872
  end,
873
873
  visit(node.statements),
874
874
  case node.subsequent
@@ -931,7 +931,11 @@ module Prism
931
931
  token(node.in_loc),
932
932
  pattern,
933
933
  guard,
934
- srange_find(node.pattern.location.end_offset, node.statements&.location&.start_offset, [";", "then"]),
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,
935
939
  visit(node.statements)
936
940
  )
937
941
  end
@@ -1781,10 +1785,10 @@ module Prism
1781
1785
  builder.condition(
1782
1786
  token(node.keyword_loc),
1783
1787
  visit(node.predicate),
1784
- if node.then_keyword_loc
1785
- token(node.then_keyword_loc)
1788
+ if (then_keyword_loc = node.then_keyword_loc)
1789
+ token(then_keyword_loc)
1786
1790
  else
1787
- srange_find(node.predicate.location.end_offset, (node.statements&.location || node.else_clause&.location || node.end_keyword_loc).start_offset, [";"])
1791
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.else_clause&.location || node.end_keyword_loc).start_offset, ";")
1788
1792
  end,
1789
1793
  visit(node.else_clause),
1790
1794
  token(node.else_clause&.else_keyword_loc),
@@ -1812,7 +1816,11 @@ module Prism
1812
1816
  :until,
1813
1817
  token(node.keyword_loc),
1814
1818
  visit(node.predicate),
1815
- srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, [";", "do"]),
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,
1816
1824
  visit(node.statements),
1817
1825
  token(node.closing_loc)
1818
1826
  )
@@ -1832,10 +1840,10 @@ module Prism
1832
1840
  builder.when(
1833
1841
  token(node.keyword_loc),
1834
1842
  visit_all(node.conditions),
1835
- if node.then_keyword_loc
1836
- token(node.then_keyword_loc)
1843
+ if (then_keyword_loc = node.then_keyword_loc)
1844
+ token(then_keyword_loc)
1837
1845
  else
1838
- 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, ";")
1839
1847
  end,
1840
1848
  visit(node.statements)
1841
1849
  )
@@ -1852,7 +1860,11 @@ module Prism
1852
1860
  :while,
1853
1861
  token(node.keyword_loc),
1854
1862
  visit(node.predicate),
1855
- srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, [";", "do"]),
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,
1856
1868
  visit(node.statements),
1857
1869
  token(node.closing_loc)
1858
1870
  )
@@ -1985,18 +1997,16 @@ module Prism
1985
1997
  Range.new(source_buffer, offset_cache[start_offset], offset_cache[end_offset])
1986
1998
  end
1987
1999
 
1988
- # Constructs a new source range by finding the given tokens between the
1989
- # 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
1990
2002
  # returns nil. Importantly it does not search past newlines or comments.
1991
2003
  #
1992
2004
  # Note that end_offset is allowed to be nil, in which case this will
1993
2005
  # search until the end of the string.
1994
- def srange_find(start_offset, end_offset, tokens)
1995
- if (match = source_buffer.source.byteslice(start_offset...end_offset).match(/\A(\s*)(#{tokens.join("|")})/))
1996
- _, whitespace, token = *match
1997
- token_offset = start_offset + whitespace.bytesize
1998
-
1999
- [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])]
2000
2010
  end
2001
2011
  end
2002
2012
 
@@ -51,7 +51,7 @@ module Prism
51
51
  source = source_buffer.source
52
52
 
53
53
  offset_cache = build_offset_cache(source)
54
- result = unwrap(Prism.parse(source, filepath: source_buffer.name, version: convert_for_prism(version), scopes: [[]], encoding: false), offset_cache)
54
+ result = unwrap(Prism.parse(source, filepath: source_buffer.name, version: convert_for_prism(version), partial_script: true, encoding: false), offset_cache)
55
55
 
56
56
  build_ast(result.value, offset_cache)
57
57
  ensure
@@ -64,7 +64,7 @@ module Prism
64
64
  source = source_buffer.source
65
65
 
66
66
  offset_cache = build_offset_cache(source)
67
- result = unwrap(Prism.parse(source, filepath: source_buffer.name, version: convert_for_prism(version), scopes: [[]], encoding: false), offset_cache)
67
+ result = unwrap(Prism.parse(source, filepath: source_buffer.name, version: convert_for_prism(version), partial_script: true, encoding: false), offset_cache)
68
68
 
69
69
  [
70
70
  build_ast(result.value, offset_cache),
@@ -83,7 +83,7 @@ module Prism
83
83
  offset_cache = build_offset_cache(source)
84
84
  result =
85
85
  begin
86
- unwrap(Prism.parse_lex(source, filepath: source_buffer.name, version: convert_for_prism(version), scopes: [[]], encoding: false), offset_cache)
86
+ unwrap(Prism.parse_lex(source, filepath: source_buffer.name, version: convert_for_prism(version), partial_script: true, encoding: false), offset_cache)
87
87
  rescue ::Parser::SyntaxError
88
88
  raise if !recover
89
89
  end
@@ -3269,11 +3269,7 @@ module Prism
3269
3269
 
3270
3270
  # Lazily initialize the parse result.
3271
3271
  def result
3272
- @result ||=
3273
- begin
3274
- scopes = RUBY_VERSION >= "3.3.0" ? [] : [[]]
3275
- Prism.parse(source, scopes: scopes)
3276
- end
3272
+ @result ||= Prism.parse(source, partial_script: true)
3277
3273
  end
3278
3274
 
3279
3275
  ##########################################################################
@@ -881,6 +881,7 @@ module Prism
881
881
  # Visit the interpolated content of the string-like node.
882
882
  private def visit_interpolated_parts(parts)
883
883
  visited = []
884
+
884
885
  parts.each do |part|
885
886
  result = visit(part)
886
887
 
@@ -892,6 +893,7 @@ module Prism
892
893
  else
893
894
  visited << result
894
895
  end
896
+ visited << :space
895
897
  elsif result[0] == :dstr
896
898
  if !visited.empty? && part.parts[0].is_a?(StringNode)
897
899
  # If we are in the middle of an implicitly concatenated string,
@@ -907,8 +909,9 @@ module Prism
907
909
  end
908
910
 
909
911
  state = :beginning #: :beginning | :string_content | :interpolated_content
912
+ results = []
910
913
 
911
- visited.each_with_object([]) do |result, results|
914
+ visited.each_with_index do |result, index|
912
915
  case state
913
916
  when :beginning
914
917
  if result.is_a?(String)
@@ -923,7 +926,9 @@ module Prism
923
926
  state = :interpolated_content
924
927
  end
925
928
  when :string_content
926
- if result.is_a?(String)
929
+ if result == :space
930
+ # continue
931
+ elsif result.is_a?(String)
927
932
  results[0] << result
928
933
  elsif result.is_a?(Array) && result[0] == :str
929
934
  results[0] << result[1]
@@ -932,7 +937,9 @@ module Prism
932
937
  state = :interpolated_content
933
938
  end
934
939
  when :interpolated_content
935
- if result.is_a?(Array) && result[0] == :str && results[-1][0] == :str && (results[-1].line_max == result.line)
940
+ if result == :space
941
+ # continue
942
+ elsif visited[index - 1] != :space && result.is_a?(Array) && result[0] == :str && results[-1][0] == :str && (results[-1].line_max == result.line)
936
943
  results[-1][1] << result[1]
937
944
  results[-1].line_max = result.line_max
938
945
  else
@@ -940,6 +947,8 @@ module Prism
940
947
  end
941
948
  end
942
949
  end
950
+
951
+ results
943
952
  end
944
953
 
945
954
  # -> { it }
@@ -1596,13 +1605,13 @@ module Prism
1596
1605
  # Parse the given source and translate it into the seattlerb/ruby_parser
1597
1606
  # gem's Sexp format.
1598
1607
  def parse(source, filepath = "(string)")
1599
- translate(Prism.parse(source, filepath: filepath, scopes: [[]]), filepath)
1608
+ translate(Prism.parse(source, filepath: filepath, partial_script: true), filepath)
1600
1609
  end
1601
1610
 
1602
1611
  # Parse the given file and translate it into the seattlerb/ruby_parser
1603
1612
  # gem's Sexp format.
1604
1613
  def parse_file(filepath)
1605
- translate(Prism.parse_file(filepath, scopes: [[]]), filepath)
1614
+ translate(Prism.parse_file(filepath, partial_script: true), filepath)
1606
1615
  end
1607
1616
 
1608
1617
  class << self
data/lib/prism.rb CHANGED
@@ -24,7 +24,9 @@ module Prism
24
24
  autoload :Pack, "prism/pack"
25
25
  autoload :Pattern, "prism/pattern"
26
26
  autoload :Reflection, "prism/reflection"
27
+ autoload :Relocation, "prism/relocation"
27
28
  autoload :Serialize, "prism/serialize"
29
+ autoload :StringQuery, "prism/string_query"
28
30
  autoload :Translation, "prism/translation"
29
31
  autoload :Visitor, "prism/visitor"
30
32
 
@@ -75,13 +77,13 @@ require_relative "prism/parse_result"
75
77
  # it's going to require the built library. Otherwise, it's going to require a
76
78
  # module that uses FFI to call into the library.
77
79
  if RUBY_ENGINE == "ruby" and !ENV["PRISM_FFI_BACKEND"]
78
- require "prism/prism"
79
-
80
80
  # The C extension is the default backend on CRuby.
81
81
  Prism::BACKEND = :CEXT
82
- else
83
- require_relative "prism/ffi"
84
82
 
83
+ require "prism/prism"
84
+ else
85
85
  # The FFI backend is used on other Ruby implementations.
86
86
  Prism::BACKEND = :FFI
87
+
88
+ require_relative "prism/ffi"
87
89
  end
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 = "1.1.0"
5
+ spec.version = "1.3.0"
6
6
  spec.authors = ["Shopify"]
7
7
  spec.email = ["ruby@shopify.com"]
8
8
 
@@ -35,6 +35,7 @@ Gem::Specification.new do |spec|
35
35
  "docs/parser_translation.md",
36
36
  "docs/parsing_rules.md",
37
37
  "docs/releasing.md",
38
+ "docs/relocation.md",
38
39
  "docs/ripper_translation.md",
39
40
  "docs/ruby_api.md",
40
41
  "docs/ruby_parser_translation.md",
@@ -88,7 +89,9 @@ Gem::Specification.new do |spec|
88
89
  "lib/prism/polyfill/byteindex.rb",
89
90
  "lib/prism/polyfill/unpack1.rb",
90
91
  "lib/prism/reflection.rb",
92
+ "lib/prism/relocation.rb",
91
93
  "lib/prism/serialize.rb",
94
+ "lib/prism/string_query.rb",
92
95
  "lib/prism/translation.rb",
93
96
  "lib/prism/translation/parser.rb",
94
97
  "lib/prism/translation/parser33.rb",
@@ -109,6 +112,7 @@ Gem::Specification.new do |spec|
109
112
  "rbi/prism/node.rbi",
110
113
  "rbi/prism/parse_result.rbi",
111
114
  "rbi/prism/reflection.rbi",
115
+ "rbi/prism/string_query.rbi",
112
116
  "rbi/prism/translation/parser.rbi",
113
117
  "rbi/prism/translation/parser33.rbi",
114
118
  "rbi/prism/translation/parser34.rbi",
@@ -128,7 +132,9 @@ Gem::Specification.new do |spec|
128
132
  "sig/prism/parse_result.rbs",
129
133
  "sig/prism/pattern.rbs",
130
134
  "sig/prism/reflection.rbs",
135
+ "sig/prism/relocation.rbs",
131
136
  "sig/prism/serialize.rbs",
137
+ "sig/prism/string_query.rbs",
132
138
  "sig/prism/visitor.rbs",
133
139
  "src/diagnostic.c",
134
140
  "src/encoding.c",
data/rbi/prism/dsl.rbi CHANGED
@@ -451,14 +451,14 @@ module Prism::DSL
451
451
  sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, predicate: Prism::Node, then_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode), else_clause: T.nilable(Prism::ElseNode), end_keyword_loc: T.nilable(Prism::Location)).returns(Prism::UnlessNode) }
452
452
  def unless_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, predicate: default_node(source, location), then_keyword_loc: nil, statements: nil, else_clause: nil, end_keyword_loc: nil); end
453
453
 
454
- sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, closing_loc: T.nilable(Prism::Location), predicate: Prism::Node, statements: T.nilable(Prism::StatementsNode)).returns(Prism::UntilNode) }
455
- def until_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, closing_loc: nil, predicate: default_node(source, location), statements: nil); end
454
+ sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, do_keyword_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location), predicate: Prism::Node, statements: T.nilable(Prism::StatementsNode)).returns(Prism::UntilNode) }
455
+ def until_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, do_keyword_loc: nil, closing_loc: nil, predicate: default_node(source, location), statements: nil); end
456
456
 
457
457
  sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, conditions: T::Array[Prism::Node], then_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode)).returns(Prism::WhenNode) }
458
458
  def when_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, conditions: [], then_keyword_loc: nil, statements: nil); end
459
459
 
460
- sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, closing_loc: T.nilable(Prism::Location), predicate: Prism::Node, statements: T.nilable(Prism::StatementsNode)).returns(Prism::WhileNode) }
461
- def while_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, closing_loc: nil, predicate: default_node(source, location), statements: nil); end
460
+ sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, do_keyword_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location), predicate: Prism::Node, statements: T.nilable(Prism::StatementsNode)).returns(Prism::WhileNode) }
461
+ def while_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, do_keyword_loc: nil, closing_loc: nil, predicate: default_node(source, location), statements: nil); end
462
462
 
463
463
  sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, content_loc: Prism::Location, closing_loc: Prism::Location, unescaped: String).returns(Prism::XStringNode) }
464
464
  def x_string_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, content_loc: location, closing_loc: location, unescaped: ""); end