prism 1.3.0 → 1.5.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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -1
  3. data/Makefile +2 -1
  4. data/README.md +1 -0
  5. data/config.yml +273 -37
  6. data/docs/parser_translation.md +8 -23
  7. data/docs/releasing.md +1 -1
  8. data/docs/ripper_translation.md +1 -1
  9. data/docs/ruby_api.md +1 -1
  10. data/ext/prism/api_node.c +1816 -1303
  11. data/ext/prism/extension.c +244 -110
  12. data/ext/prism/extension.h +4 -4
  13. data/include/prism/ast.h +291 -49
  14. data/include/prism/defines.h +4 -1
  15. data/include/prism/diagnostic.h +4 -0
  16. data/include/prism/options.h +89 -3
  17. data/include/prism/regexp.h +2 -2
  18. data/include/prism/util/pm_buffer.h +18 -0
  19. data/include/prism/util/pm_integer.h +4 -0
  20. data/include/prism/util/pm_list.h +6 -0
  21. data/include/prism/util/pm_string.h +12 -2
  22. data/include/prism/version.h +2 -2
  23. data/include/prism.h +41 -16
  24. data/lib/prism/compiler.rb +456 -151
  25. data/lib/prism/desugar_compiler.rb +1 -0
  26. data/lib/prism/dispatcher.rb +16 -0
  27. data/lib/prism/dot_visitor.rb +21 -1
  28. data/lib/prism/dsl.rb +13 -2
  29. data/lib/prism/ffi.rb +62 -34
  30. data/lib/prism/inspect_visitor.rb +5 -1
  31. data/lib/prism/lex_compat.rb +1 -0
  32. data/lib/prism/mutation_compiler.rb +3 -0
  33. data/lib/prism/node.rb +554 -345
  34. data/lib/prism/node_ext.rb +4 -1
  35. data/lib/prism/pack.rb +2 -0
  36. data/lib/prism/parse_result/comments.rb +1 -0
  37. data/lib/prism/parse_result/errors.rb +1 -0
  38. data/lib/prism/parse_result/newlines.rb +2 -1
  39. data/lib/prism/parse_result.rb +53 -0
  40. data/lib/prism/pattern.rb +1 -0
  41. data/lib/prism/polyfill/append_as_bytes.rb +15 -0
  42. data/lib/prism/polyfill/scan_byte.rb +14 -0
  43. data/lib/prism/polyfill/warn.rb +42 -0
  44. data/lib/prism/reflection.rb +5 -2
  45. data/lib/prism/relocation.rb +1 -0
  46. data/lib/prism/serialize.rb +1275 -783
  47. data/lib/prism/string_query.rb +1 -0
  48. data/lib/prism/translation/parser/builder.rb +62 -0
  49. data/lib/prism/translation/parser/compiler.rb +230 -152
  50. data/lib/prism/translation/parser/lexer.rb +446 -64
  51. data/lib/prism/translation/parser.rb +64 -4
  52. data/lib/prism/translation/parser33.rb +1 -0
  53. data/lib/prism/translation/parser34.rb +1 -0
  54. data/lib/prism/translation/parser35.rb +13 -0
  55. data/lib/prism/translation/parser_current.rb +24 -0
  56. data/lib/prism/translation/ripper/sexp.rb +1 -0
  57. data/lib/prism/translation/ripper.rb +30 -4
  58. data/lib/prism/translation/ruby_parser.rb +291 -7
  59. data/lib/prism/translation.rb +3 -0
  60. data/lib/prism/visitor.rb +457 -152
  61. data/lib/prism.rb +5 -3
  62. data/prism.gemspec +9 -1
  63. data/rbi/prism/dsl.rbi +9 -6
  64. data/rbi/prism/node.rbi +43 -16
  65. data/rbi/prism/parse_result.rbi +17 -0
  66. data/rbi/prism/translation/parser35.rbi +6 -0
  67. data/rbi/prism.rbi +39 -36
  68. data/sig/prism/dispatcher.rbs +3 -0
  69. data/sig/prism/dsl.rbs +7 -5
  70. data/sig/prism/node.rbs +461 -37
  71. data/sig/prism/node_ext.rbs +84 -17
  72. data/sig/prism/parse_result/comments.rbs +38 -0
  73. data/sig/prism/parse_result.rbs +14 -0
  74. data/sig/prism/reflection.rbs +1 -1
  75. data/sig/prism/serialize.rbs +4 -2
  76. data/sig/prism.rbs +22 -1
  77. data/src/diagnostic.c +9 -3
  78. data/src/node.c +23 -0
  79. data/src/options.c +33 -2
  80. data/src/prettyprint.c +32 -0
  81. data/src/prism.c +620 -242
  82. data/src/serialize.c +8 -0
  83. data/src/token_type.c +36 -34
  84. data/src/util/pm_buffer.c +40 -0
  85. data/src/util/pm_constant_pool.c +6 -2
  86. data/src/util/pm_strncasecmp.c +13 -1
  87. metadata +11 -7
@@ -1,9 +1,15 @@
1
1
  # frozen_string_literal: true
2
+ # :markup: markdown
2
3
 
3
4
  begin
5
+ required_version = ">= 3.3.7.2"
6
+ gem "parser", required_version
4
7
  require "parser"
5
8
  rescue LoadError
6
- warn(%q{Error: Unable to load parser. Add `gem "parser"` to your Gemfile.})
9
+ warn(<<~MSG)
10
+ Error: Unable to load parser #{required_version}. \
11
+ Add `gem "parser"` to your Gemfile or run `bundle update parser`.
12
+ MSG
7
13
  exit(1)
8
14
  end
9
15
 
@@ -33,6 +39,43 @@ module Prism
33
39
 
34
40
  Racc_debug_parser = false # :nodoc:
35
41
 
42
+ # The `builder` argument is used to create the parser using our custom builder class by default.
43
+ #
44
+ # By using the `:parser` keyword argument, you can translate in a way that is compatible with
45
+ # the Parser gem using any parser.
46
+ #
47
+ # For example, in RuboCop for Ruby LSP, the following approach can be used to improve performance
48
+ # by reusing a pre-parsed `Prism::ParseLexResult`:
49
+ #
50
+ # class PrismPreparsed
51
+ # def initialize(prism_result)
52
+ # @prism_result = prism_result
53
+ # end
54
+ #
55
+ # def parse_lex(source, **options)
56
+ # @prism_result
57
+ # end
58
+ # end
59
+ #
60
+ # prism_preparsed = PrismPreparsed.new(prism_result)
61
+ #
62
+ # Prism::Translation::Ruby34.new(builder, parser: prism_preparsed)
63
+ #
64
+ # In an object passed to the `:parser` keyword argument, the `parse` and `parse_lex` methods
65
+ # should be implemented as needed.
66
+ #
67
+ def initialize(builder = Prism::Translation::Parser::Builder.new, parser: Prism)
68
+ if !builder.is_a?(Prism::Translation::Parser::Builder)
69
+ warn(<<~MSG, uplevel: 1, category: :deprecated)
70
+ [deprecation]: The builder passed to `Prism::Translation::Parser.new` is not a \
71
+ `Prism::Translation::Parser::Builder` subclass. This will raise in the next major version.
72
+ MSG
73
+ end
74
+ @parser = parser
75
+
76
+ super(builder)
77
+ end
78
+
36
79
  def version # :nodoc:
37
80
  34
38
81
  end
@@ -51,7 +94,7 @@ module Prism
51
94
  source = source_buffer.source
52
95
 
53
96
  offset_cache = build_offset_cache(source)
54
- result = unwrap(Prism.parse(source, filepath: source_buffer.name, version: convert_for_prism(version), partial_script: true, encoding: false), offset_cache)
97
+ result = unwrap(@parser.parse(source, **prism_options), offset_cache)
55
98
 
56
99
  build_ast(result.value, offset_cache)
57
100
  ensure
@@ -64,7 +107,7 @@ module Prism
64
107
  source = source_buffer.source
65
108
 
66
109
  offset_cache = build_offset_cache(source)
67
- result = unwrap(Prism.parse(source, filepath: source_buffer.name, version: convert_for_prism(version), partial_script: true, encoding: false), offset_cache)
110
+ result = unwrap(@parser.parse(source, **prism_options), offset_cache)
68
111
 
69
112
  [
70
113
  build_ast(result.value, offset_cache),
@@ -83,7 +126,7 @@ module Prism
83
126
  offset_cache = build_offset_cache(source)
84
127
  result =
85
128
  begin
86
- unwrap(Prism.parse_lex(source, filepath: source_buffer.name, version: convert_for_prism(version), partial_script: true, encoding: false), offset_cache)
129
+ unwrap(@parser.parse_lex(source, **prism_options), offset_cache)
87
130
  rescue ::Parser::SyntaxError
88
131
  raise if !recover
89
132
  end
@@ -285,6 +328,20 @@ module Prism
285
328
  )
286
329
  end
287
330
 
331
+ # Options for how prism should parse/lex the source.
332
+ def prism_options
333
+ options = {
334
+ filepath: @source_buffer.name,
335
+ version: convert_for_prism(version),
336
+ partial_script: true,
337
+ }
338
+ # The parser gem always encodes to UTF-8, unless it is binary.
339
+ # https://github.com/whitequark/parser/blob/v3.3.6.0/lib/parser/source/buffer.rb#L80-L107
340
+ options[:encoding] = false if @source_buffer.source.encoding != Encoding::BINARY
341
+
342
+ options
343
+ end
344
+
288
345
  # Converts the version format handled by Parser to the format handled by Prism.
289
346
  def convert_for_prism(version)
290
347
  case version
@@ -292,11 +349,14 @@ module Prism
292
349
  "3.3.1"
293
350
  when 34
294
351
  "3.4.0"
352
+ when 35
353
+ "3.5.0"
295
354
  else
296
355
  "latest"
297
356
  end
298
357
  end
299
358
 
359
+ require_relative "parser/builder"
300
360
  require_relative "parser/compiler"
301
361
  require_relative "parser/lexer"
302
362
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # :markup: markdown
2
3
 
3
4
  module Prism
4
5
  module Translation
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # :markup: markdown
2
3
 
3
4
  module Prism
4
5
  module Translation
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ # :markup: markdown
3
+
4
+ module Prism
5
+ module Translation
6
+ # This class is the entry-point for Ruby 3.5 of `Prism::Translation::Parser`.
7
+ class Parser35 < Parser
8
+ def version # :nodoc:
9
+ 35
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ # :markup: markdown
3
+ # typed: ignore
4
+
5
+ #
6
+ module Prism
7
+ module Translation
8
+ case RUBY_VERSION
9
+ when /^3\.3\./
10
+ ParserCurrent = Parser33
11
+ when /^3\.4\./
12
+ ParserCurrent = Parser34
13
+ when /^3\.5\./
14
+ ParserCurrent = Parser35
15
+ else
16
+ # Keep this in sync with released Ruby.
17
+ parser = Parser34
18
+ major, minor, _patch = Gem::Version.new(RUBY_VERSION).segments
19
+ warn "warning: `Prism::Translation::Current` is loading #{parser.name}, " \
20
+ "but you are running #{major}.#{minor}."
21
+ ParserCurrent = parser
22
+ end
23
+ end
24
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # :markup: markdown
2
3
 
3
4
  require_relative "../ripper"
4
5
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # :markup: markdown
2
3
 
3
4
  require "ripper"
4
5
 
@@ -1045,10 +1046,20 @@ module Prism
1045
1046
  bounds(node.location)
1046
1047
  on_unary(node.name, receiver)
1047
1048
  when :!
1048
- receiver = visit(node.receiver)
1049
+ if node.message == "not"
1050
+ receiver =
1051
+ if !node.receiver.is_a?(ParenthesesNode) || !node.receiver.body.nil?
1052
+ visit(node.receiver)
1053
+ end
1049
1054
 
1050
- bounds(node.location)
1051
- on_unary(node.message == "not" ? :not : :!, receiver)
1055
+ bounds(node.location)
1056
+ on_unary(:not, receiver)
1057
+ else
1058
+ receiver = visit(node.receiver)
1059
+
1060
+ bounds(node.location)
1061
+ on_unary(:!, receiver)
1062
+ end
1052
1063
  when *BINARY_OPERATORS
1053
1064
  receiver = visit(node.receiver)
1054
1065
  value = visit(node.arguments.arguments.first)
@@ -1605,8 +1616,23 @@ module Prism
1605
1616
  # defined?(a)
1606
1617
  # ^^^^^^^^^^^
1607
1618
  def visit_defined_node(node)
1619
+ expression = visit(node.value)
1620
+
1621
+ # Very weird circumstances here where something like:
1622
+ #
1623
+ # defined?
1624
+ # (1)
1625
+ #
1626
+ # gets parsed in Ruby as having only the `1` expression but in Ripper it
1627
+ # gets parsed as having a parentheses node. In this case we need to
1628
+ # synthesize that node to match Ripper's behavior.
1629
+ if node.lparen_loc && node.keyword_loc.join(node.lparen_loc).slice.include?("\n")
1630
+ bounds(node.lparen_loc.join(node.rparen_loc))
1631
+ expression = on_paren(on_stmts_add(on_stmts_new, expression))
1632
+ end
1633
+
1608
1634
  bounds(node.location)
1609
- on_defined(visit(node.value))
1635
+ on_defined(expression)
1610
1636
  end
1611
1637
 
1612
1638
  # if foo then bar else baz end