prism 0.15.1 → 0.17.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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -1
  3. data/Makefile +12 -0
  4. data/README.md +3 -1
  5. data/config.yml +66 -50
  6. data/docs/configuration.md +2 -0
  7. data/docs/fuzzing.md +1 -1
  8. data/docs/javascript.md +90 -0
  9. data/docs/releasing.md +27 -0
  10. data/docs/ruby_api.md +2 -0
  11. data/docs/serialization.md +28 -29
  12. data/ext/prism/api_node.c +856 -826
  13. data/ext/prism/api_pack.c +20 -9
  14. data/ext/prism/extension.c +494 -119
  15. data/ext/prism/extension.h +1 -1
  16. data/include/prism/ast.h +3157 -747
  17. data/include/prism/defines.h +40 -8
  18. data/include/prism/diagnostic.h +36 -3
  19. data/include/prism/enc/pm_encoding.h +119 -28
  20. data/include/prism/node.h +38 -30
  21. data/include/prism/options.h +204 -0
  22. data/include/prism/pack.h +44 -33
  23. data/include/prism/parser.h +445 -199
  24. data/include/prism/prettyprint.h +26 -0
  25. data/include/prism/regexp.h +16 -2
  26. data/include/prism/util/pm_buffer.h +102 -18
  27. data/include/prism/util/pm_char.h +162 -48
  28. data/include/prism/util/pm_constant_pool.h +128 -34
  29. data/include/prism/util/pm_list.h +68 -38
  30. data/include/prism/util/pm_memchr.h +18 -3
  31. data/include/prism/util/pm_newline_list.h +71 -28
  32. data/include/prism/util/pm_state_stack.h +25 -7
  33. data/include/prism/util/pm_string.h +115 -27
  34. data/include/prism/util/pm_string_list.h +25 -6
  35. data/include/prism/util/pm_strncasecmp.h +32 -0
  36. data/include/prism/util/pm_strpbrk.h +31 -17
  37. data/include/prism/version.h +28 -3
  38. data/include/prism.h +229 -36
  39. data/lib/prism/compiler.rb +5 -5
  40. data/lib/prism/debug.rb +43 -13
  41. data/lib/prism/desugar_compiler.rb +1 -1
  42. data/lib/prism/dispatcher.rb +27 -26
  43. data/lib/prism/dsl.rb +16 -16
  44. data/lib/prism/ffi.rb +138 -61
  45. data/lib/prism/lex_compat.rb +26 -16
  46. data/lib/prism/mutation_compiler.rb +11 -11
  47. data/lib/prism/node.rb +426 -227
  48. data/lib/prism/node_ext.rb +23 -16
  49. data/lib/prism/node_inspector.rb +1 -1
  50. data/lib/prism/pack.rb +79 -40
  51. data/lib/prism/parse_result/comments.rb +7 -2
  52. data/lib/prism/parse_result/newlines.rb +4 -0
  53. data/lib/prism/parse_result.rb +157 -21
  54. data/lib/prism/pattern.rb +14 -3
  55. data/lib/prism/ripper_compat.rb +28 -10
  56. data/lib/prism/serialize.rb +935 -307
  57. data/lib/prism/visitor.rb +9 -5
  58. data/lib/prism.rb +20 -2
  59. data/prism.gemspec +11 -2
  60. data/rbi/prism.rbi +7305 -0
  61. data/rbi/prism_static.rbi +196 -0
  62. data/sig/prism.rbs +4468 -0
  63. data/sig/prism_static.rbs +123 -0
  64. data/src/diagnostic.c +56 -53
  65. data/src/enc/pm_big5.c +1 -0
  66. data/src/enc/pm_euc_jp.c +1 -0
  67. data/src/enc/pm_gbk.c +1 -0
  68. data/src/enc/pm_shift_jis.c +1 -0
  69. data/src/enc/pm_tables.c +316 -80
  70. data/src/enc/pm_unicode.c +54 -9
  71. data/src/enc/pm_windows_31j.c +1 -0
  72. data/src/node.c +357 -345
  73. data/src/options.c +170 -0
  74. data/src/prettyprint.c +7697 -1643
  75. data/src/prism.c +1964 -1125
  76. data/src/regexp.c +153 -95
  77. data/src/serialize.c +432 -397
  78. data/src/token_type.c +3 -1
  79. data/src/util/pm_buffer.c +88 -23
  80. data/src/util/pm_char.c +103 -57
  81. data/src/util/pm_constant_pool.c +52 -22
  82. data/src/util/pm_list.c +12 -4
  83. data/src/util/pm_memchr.c +5 -3
  84. data/src/util/pm_newline_list.c +25 -63
  85. data/src/util/pm_state_stack.c +9 -3
  86. data/src/util/pm_string.c +95 -85
  87. data/src/util/pm_string_list.c +14 -15
  88. data/src/util/pm_strncasecmp.c +10 -3
  89. data/src/util/pm_strpbrk.c +25 -19
  90. metadata +12 -3
  91. data/docs/prism.png +0 -0
@@ -291,9 +291,6 @@ module Prism
291
291
  # Compile a KeywordHashNode node
292
292
  alias visit_keyword_hash_node visit_child_nodes
293
293
 
294
- # Compile a KeywordParameterNode node
295
- alias visit_keyword_parameter_node visit_child_nodes
296
-
297
294
  # Compile a KeywordRestParameterNode node
298
295
  alias visit_keyword_rest_parameter_node visit_child_nodes
299
296
 
@@ -354,6 +351,9 @@ module Prism
354
351
  # Compile a NumberedReferenceReadNode node
355
352
  alias visit_numbered_reference_read_node visit_child_nodes
356
353
 
354
+ # Compile a OptionalKeywordParameterNode node
355
+ alias visit_optional_keyword_parameter_node visit_child_nodes
356
+
357
357
  # Compile a OptionalParameterNode node
358
358
  alias visit_optional_parameter_node visit_child_nodes
359
359
 
@@ -393,8 +393,8 @@ module Prism
393
393
  # Compile a RegularExpressionNode node
394
394
  alias visit_regular_expression_node visit_child_nodes
395
395
 
396
- # Compile a RequiredDestructuredParameterNode node
397
- alias visit_required_destructured_parameter_node visit_child_nodes
396
+ # Compile a RequiredKeywordParameterNode node
397
+ alias visit_required_keyword_parameter_node visit_child_nodes
398
398
 
399
399
  # Compile a RequiredParameterNode node
400
400
  alias visit_required_parameter_node visit_child_nodes
data/lib/prism/debug.rb CHANGED
@@ -4,7 +4,9 @@ module Prism
4
4
  # This module is used for testing and debugging and is not meant to be used by
5
5
  # consumers of this library.
6
6
  module Debug
7
- class ISeq
7
+ # A wrapper around a RubyVM::InstructionSequence that provides a more
8
+ # convenient interface for accessing parts of the iseq.
9
+ class ISeq # :nodoc:
8
10
  attr_reader :parts
9
11
 
10
12
  def initialize(parts)
@@ -42,6 +44,11 @@ module Prism
42
44
  end
43
45
  end
44
46
 
47
+ private_constant :ISeq
48
+
49
+ # :call-seq:
50
+ # Debug::cruby_locals(source) -> Array
51
+ #
45
52
  # For the given source, compiles with CRuby and returns a list of all of the
46
53
  # sets of local variables that were encountered.
47
54
  def self.cruby_locals(source)
@@ -52,9 +59,21 @@ module Prism
52
59
  stack = [ISeq.new(RubyVM::InstructionSequence.compile(source).to_a)]
53
60
 
54
61
  while (iseq = stack.pop)
55
- # For some reason, CRuby occasionally pushes this special local
56
- # variable when there are splat arguments. We get rid of that here.
57
- locals << (iseq.local_table - [:"#arg_rest"])
62
+ names = [*iseq.local_table]
63
+ names.map!.with_index do |name, index|
64
+ # When an anonymous local variable is present in the iseq's local
65
+ # table, it is represented as the stack offset from the top.
66
+ # However, when these are dumped to binary and read back in, they
67
+ # are replaced with the symbol :#arg_rest. To consistently handle
68
+ # this, we replace them here with their index.
69
+ if name == :"#arg_rest"
70
+ names.length - index + 1
71
+ else
72
+ name
73
+ end
74
+ end
75
+
76
+ locals << names
58
77
  iseq.each_child { |child| stack << child }
59
78
  end
60
79
 
@@ -64,8 +83,16 @@ module Prism
64
83
  end
65
84
  end
66
85
 
86
+ # Used to hold the place of a local that will be in the local table but
87
+ # cannot be accessed directly from the source code. For example, the
88
+ # iteration variable in a for loop or the positional parameter on a method
89
+ # definition that is destructured.
67
90
  AnonymousLocal = Object.new
91
+ private_constant :AnonymousLocal
68
92
 
93
+ # :call-seq:
94
+ # Debug::prism_locals(source) -> Array
95
+ #
69
96
  # For the given source, parses with prism and returns a list of all of the
70
97
  # sets of local variables that were encountered.
71
98
  def self.prism_locals(source)
@@ -102,19 +129,21 @@ module Prism
102
129
  AnonymousLocal
103
130
  end
104
131
  end,
105
- *params.keywords.reject(&:value).map(&:name),
106
- *params.keywords.select(&:value).map(&:name)
132
+ *params.keywords.grep(RequiredKeywordParameterNode).map(&:name),
133
+ *params.keywords.grep(OptionalKeywordParameterNode).map(&:name),
107
134
  ]
108
135
 
109
136
  sorted << AnonymousLocal if params.keywords.any?
110
137
 
111
138
  # Recurse down the parameter tree to find any destructured
112
139
  # parameters and add them after the other parameters.
113
- param_stack = params.requireds.concat(params.posts).grep(RequiredDestructuredParameterNode).reverse
140
+ param_stack = params.requireds.concat(params.posts).grep(MultiTargetNode).reverse
114
141
  while (param = param_stack.pop)
115
142
  case param
116
- when RequiredDestructuredParameterNode
117
- param_stack.concat(param.parameters.reverse)
143
+ when MultiTargetNode
144
+ param_stack.concat(param.rights.reverse)
145
+ param_stack << param.rest
146
+ param_stack.concat(param.lefts.reverse)
118
147
  when RequiredParameterNode
119
148
  sorted << param.name
120
149
  when SplatNode
@@ -150,12 +179,13 @@ module Prism
150
179
  locals
151
180
  end
152
181
 
182
+ # :call-seq:
183
+ # Debug::newlines(source) -> Array
184
+ #
185
+ # For the given source string, return the byte offsets of every newline in
186
+ # the source.
153
187
  def self.newlines(source)
154
188
  Prism.parse(source).source.offsets
155
189
  end
156
-
157
- def self.parse_serialize_file(filepath)
158
- parse_serialize_file_metadata(filepath, [filepath.bytesize, filepath.b, 0].pack("LA*L"))
159
- end
160
190
  end
161
191
  end
@@ -161,7 +161,7 @@ module Prism
161
161
  nil,
162
162
  node.operator_loc.copy(length: node.operator_loc.length - 1),
163
163
  nil,
164
- ArgumentsNode.new([node.value], node.value.location),
164
+ ArgumentsNode.new([node.value], 0, node.value.location),
165
165
  nil,
166
166
  nil,
167
167
  0,
@@ -41,23 +41,24 @@ module Prism
41
41
  # attr_reader listeners: Hash[Symbol, Array[Listener]]
42
42
  attr_reader :listeners
43
43
 
44
+ # Initialize a new dispatcher.
44
45
  def initialize
45
46
  @listeners = {}
46
47
  end
47
48
 
48
- # Register a listener for one or more events
49
+ # Register a listener for one or more events.
49
50
  #
50
51
  # def register: (Listener, *Symbol) -> void
51
52
  def register(listener, *events)
52
53
  events.each { |event| (listeners[event] ||= []) << listener }
53
54
  end
54
55
 
55
- # Walks `root` dispatching events to all registered listeners
56
+ # Walks `root` dispatching events to all registered listeners.
56
57
  #
57
58
  # def dispatch: (Node) -> void
58
59
  alias dispatch visit
59
60
 
60
- # Dispatches a single event for `node` to all registered listeners
61
+ # Dispatches a single event for `node` to all registered listeners.
61
62
  #
62
63
  # def dispatch_once: (Node) -> void
63
64
  def dispatch_once(node)
@@ -736,14 +737,6 @@ module Prism
736
737
  listeners[:on_keyword_hash_node_leave]&.each { |listener| listener.on_keyword_hash_node_leave(node) }
737
738
  end
738
739
 
739
- # Dispatch enter and leave events for KeywordParameterNode nodes and continue
740
- # walking the tree.
741
- def visit_keyword_parameter_node(node)
742
- listeners[:on_keyword_parameter_node_enter]&.each { |listener| listener.on_keyword_parameter_node_enter(node) }
743
- super
744
- listeners[:on_keyword_parameter_node_leave]&.each { |listener| listener.on_keyword_parameter_node_leave(node) }
745
- end
746
-
747
740
  # Dispatch enter and leave events for KeywordRestParameterNode nodes and continue
748
741
  # walking the tree.
749
742
  def visit_keyword_rest_parameter_node(node)
@@ -904,6 +897,14 @@ module Prism
904
897
  listeners[:on_numbered_reference_read_node_leave]&.each { |listener| listener.on_numbered_reference_read_node_leave(node) }
905
898
  end
906
899
 
900
+ # Dispatch enter and leave events for OptionalKeywordParameterNode nodes and continue
901
+ # walking the tree.
902
+ def visit_optional_keyword_parameter_node(node)
903
+ listeners[:on_optional_keyword_parameter_node_enter]&.each { |listener| listener.on_optional_keyword_parameter_node_enter(node) }
904
+ super
905
+ listeners[:on_optional_keyword_parameter_node_leave]&.each { |listener| listener.on_optional_keyword_parameter_node_leave(node) }
906
+ end
907
+
907
908
  # Dispatch enter and leave events for OptionalParameterNode nodes and continue
908
909
  # walking the tree.
909
910
  def visit_optional_parameter_node(node)
@@ -1008,12 +1009,12 @@ module Prism
1008
1009
  listeners[:on_regular_expression_node_leave]&.each { |listener| listener.on_regular_expression_node_leave(node) }
1009
1010
  end
1010
1011
 
1011
- # Dispatch enter and leave events for RequiredDestructuredParameterNode nodes and continue
1012
+ # Dispatch enter and leave events for RequiredKeywordParameterNode nodes and continue
1012
1013
  # walking the tree.
1013
- def visit_required_destructured_parameter_node(node)
1014
- listeners[:on_required_destructured_parameter_node_enter]&.each { |listener| listener.on_required_destructured_parameter_node_enter(node) }
1014
+ def visit_required_keyword_parameter_node(node)
1015
+ listeners[:on_required_keyword_parameter_node_enter]&.each { |listener| listener.on_required_keyword_parameter_node_enter(node) }
1015
1016
  super
1016
- listeners[:on_required_destructured_parameter_node_leave]&.each { |listener| listener.on_required_destructured_parameter_node_leave(node) }
1017
+ listeners[:on_required_keyword_parameter_node_leave]&.each { |listener| listener.on_required_keyword_parameter_node_leave(node) }
1017
1018
  end
1018
1019
 
1019
1020
  # Dispatch enter and leave events for RequiredParameterNode nodes and continue
@@ -1216,7 +1217,7 @@ module Prism
1216
1217
  listeners[:on_yield_node_leave]&.each { |listener| listener.on_yield_node_leave(node) }
1217
1218
  end
1218
1219
 
1219
- class DispatchOnce < Visitor
1220
+ class DispatchOnce < Visitor # :nodoc:
1220
1221
  attr_reader :listeners
1221
1222
 
1222
1223
  def initialize(listeners)
@@ -1727,12 +1728,6 @@ module Prism
1727
1728
  listeners[:on_keyword_hash_node_leave]&.each { |listener| listener.on_keyword_hash_node_leave(node) }
1728
1729
  end
1729
1730
 
1730
- # Dispatch enter and leave events for KeywordParameterNode nodes.
1731
- def visit_keyword_parameter_node(node)
1732
- listeners[:on_keyword_parameter_node_enter]&.each { |listener| listener.on_keyword_parameter_node_enter(node) }
1733
- listeners[:on_keyword_parameter_node_leave]&.each { |listener| listener.on_keyword_parameter_node_leave(node) }
1734
- end
1735
-
1736
1731
  # Dispatch enter and leave events for KeywordRestParameterNode nodes.
1737
1732
  def visit_keyword_rest_parameter_node(node)
1738
1733
  listeners[:on_keyword_rest_parameter_node_enter]&.each { |listener| listener.on_keyword_rest_parameter_node_enter(node) }
@@ -1853,6 +1848,12 @@ module Prism
1853
1848
  listeners[:on_numbered_reference_read_node_leave]&.each { |listener| listener.on_numbered_reference_read_node_leave(node) }
1854
1849
  end
1855
1850
 
1851
+ # Dispatch enter and leave events for OptionalKeywordParameterNode nodes.
1852
+ def visit_optional_keyword_parameter_node(node)
1853
+ listeners[:on_optional_keyword_parameter_node_enter]&.each { |listener| listener.on_optional_keyword_parameter_node_enter(node) }
1854
+ listeners[:on_optional_keyword_parameter_node_leave]&.each { |listener| listener.on_optional_keyword_parameter_node_leave(node) }
1855
+ end
1856
+
1856
1857
  # Dispatch enter and leave events for OptionalParameterNode nodes.
1857
1858
  def visit_optional_parameter_node(node)
1858
1859
  listeners[:on_optional_parameter_node_enter]&.each { |listener| listener.on_optional_parameter_node_enter(node) }
@@ -1931,10 +1932,10 @@ module Prism
1931
1932
  listeners[:on_regular_expression_node_leave]&.each { |listener| listener.on_regular_expression_node_leave(node) }
1932
1933
  end
1933
1934
 
1934
- # Dispatch enter and leave events for RequiredDestructuredParameterNode nodes.
1935
- def visit_required_destructured_parameter_node(node)
1936
- listeners[:on_required_destructured_parameter_node_enter]&.each { |listener| listener.on_required_destructured_parameter_node_enter(node) }
1937
- listeners[:on_required_destructured_parameter_node_leave]&.each { |listener| listener.on_required_destructured_parameter_node_leave(node) }
1935
+ # Dispatch enter and leave events for RequiredKeywordParameterNode nodes.
1936
+ def visit_required_keyword_parameter_node(node)
1937
+ listeners[:on_required_keyword_parameter_node_enter]&.each { |listener| listener.on_required_keyword_parameter_node_enter(node) }
1938
+ listeners[:on_required_keyword_parameter_node_leave]&.each { |listener| listener.on_required_keyword_parameter_node_leave(node) }
1938
1939
  end
1939
1940
 
1940
1941
  # Dispatch enter and leave events for RequiredParameterNode nodes.
data/lib/prism/dsl.rb CHANGED
@@ -63,8 +63,8 @@ module Prism
63
63
  end
64
64
 
65
65
  # Create a new ArgumentsNode node
66
- def ArgumentsNode(arguments, location = Location())
67
- ArgumentsNode.new(arguments, location)
66
+ def ArgumentsNode(arguments, flags, location = Location())
67
+ ArgumentsNode.new(arguments, flags, location)
68
68
  end
69
69
 
70
70
  # Create a new ArrayNode node
@@ -358,8 +358,8 @@ module Prism
358
358
  end
359
359
 
360
360
  # Create a new HashPatternNode node
361
- def HashPatternNode(constant, assocs, kwrest, opening_loc, closing_loc, location = Location())
362
- HashPatternNode.new(constant, assocs, kwrest, opening_loc, closing_loc, location)
361
+ def HashPatternNode(constant, elements, rest, opening_loc, closing_loc, location = Location())
362
+ HashPatternNode.new(constant, elements, rest, opening_loc, closing_loc, location)
363
363
  end
364
364
 
365
365
  # Create a new IfNode node
@@ -462,11 +462,6 @@ module Prism
462
462
  KeywordHashNode.new(elements, location)
463
463
  end
464
464
 
465
- # Create a new KeywordParameterNode node
466
- def KeywordParameterNode(name, name_loc, value, location = Location())
467
- KeywordParameterNode.new(name, name_loc, value, location)
468
- end
469
-
470
465
  # Create a new KeywordRestParameterNode node
471
466
  def KeywordRestParameterNode(name, name_loc, operator_loc, location = Location())
472
467
  KeywordRestParameterNode.new(name, name_loc, operator_loc, location)
@@ -538,13 +533,13 @@ module Prism
538
533
  end
539
534
 
540
535
  # Create a new MultiTargetNode node
541
- def MultiTargetNode(targets, lparen_loc, rparen_loc, location = Location())
542
- MultiTargetNode.new(targets, lparen_loc, rparen_loc, location)
536
+ def MultiTargetNode(lefts, rest, rights, lparen_loc, rparen_loc, location = Location())
537
+ MultiTargetNode.new(lefts, rest, rights, lparen_loc, rparen_loc, location)
543
538
  end
544
539
 
545
540
  # Create a new MultiWriteNode node
546
- def MultiWriteNode(targets, lparen_loc, rparen_loc, operator_loc, value, location = Location())
547
- MultiWriteNode.new(targets, lparen_loc, rparen_loc, operator_loc, value, location)
541
+ def MultiWriteNode(lefts, rest, rights, lparen_loc, rparen_loc, operator_loc, value, location = Location())
542
+ MultiWriteNode.new(lefts, rest, rights, lparen_loc, rparen_loc, operator_loc, value, location)
548
543
  end
549
544
 
550
545
  # Create a new NextNode node
@@ -567,6 +562,11 @@ module Prism
567
562
  NumberedReferenceReadNode.new(number, location)
568
563
  end
569
564
 
565
+ # Create a new OptionalKeywordParameterNode node
566
+ def OptionalKeywordParameterNode(name, name_loc, value, location = Location())
567
+ OptionalKeywordParameterNode.new(name, name_loc, value, location)
568
+ end
569
+
570
570
  # Create a new OptionalParameterNode node
571
571
  def OptionalParameterNode(name, name_loc, operator_loc, value, location = Location())
572
572
  OptionalParameterNode.new(name, name_loc, operator_loc, value, location)
@@ -632,9 +632,9 @@ module Prism
632
632
  RegularExpressionNode.new(opening_loc, content_loc, closing_loc, unescaped, flags, location)
633
633
  end
634
634
 
635
- # Create a new RequiredDestructuredParameterNode node
636
- def RequiredDestructuredParameterNode(parameters, opening_loc, closing_loc, location = Location())
637
- RequiredDestructuredParameterNode.new(parameters, opening_loc, closing_loc, location)
635
+ # Create a new RequiredKeywordParameterNode node
636
+ def RequiredKeywordParameterNode(name, name_loc, location = Location())
637
+ RequiredKeywordParameterNode.new(name, name_loc, location)
638
638
  end
639
639
 
640
640
  # Create a new RequiredParameterNode node
data/lib/prism/ffi.rb CHANGED
@@ -9,7 +9,7 @@ require "ffi"
9
9
  module Prism
10
10
  BACKEND = :FFI
11
11
 
12
- module LibRubyParser
12
+ module LibRubyParser # :nodoc:
13
13
  extend FFI::Library
14
14
 
15
15
  # Define the library that we will be pulling functions from. Note that this
@@ -69,9 +69,10 @@ module Prism
69
69
  load_exported_functions_from(
70
70
  "prism.h",
71
71
  "pm_version",
72
- "pm_parse_serialize",
73
- "pm_lex_serialize",
74
- "pm_parse_lex_serialize"
72
+ "pm_serialize_parse",
73
+ "pm_serialize_parse_comments",
74
+ "pm_serialize_lex",
75
+ "pm_serialize_parse_lex"
75
76
  )
76
77
 
77
78
  load_exported_functions_from(
@@ -94,7 +95,7 @@ module Prism
94
95
 
95
96
  # This object represents a pm_buffer_t. We only use it as an opaque pointer,
96
97
  # so it doesn't need to know the fields of pm_buffer_t.
97
- class PrismBuffer
98
+ class PrismBuffer # :nodoc:
98
99
  SIZEOF = LibRubyParser.pm_buffer_sizeof
99
100
 
100
101
  attr_reader :pointer
@@ -132,7 +133,7 @@ module Prism
132
133
 
133
134
  # This object represents a pm_string_t. We only use it as an opaque pointer,
134
135
  # so it doesn't have to be an FFI::Struct.
135
- class PrismString
136
+ class PrismString # :nodoc:
136
137
  SIZEOF = LibRubyParser.pm_string_sizeof
137
138
 
138
139
  attr_reader :pointer
@@ -166,14 +167,6 @@ module Prism
166
167
  end
167
168
  end
168
169
  end
169
-
170
- def self.dump_internal(source, source_size, filepath)
171
- PrismBuffer.with do |buffer|
172
- metadata = [filepath.bytesize, filepath.b, 0].pack("LA*L") if filepath
173
- pm_parse_serialize(source, source_size, buffer.pointer, metadata)
174
- buffer.read
175
- end
176
- end
177
170
  end
178
171
 
179
172
  # Mark the LibRubyParser module as private as it should only be called through
@@ -183,69 +176,153 @@ module Prism
183
176
  # The version constant is set by reading the result of calling pm_version.
184
177
  VERSION = LibRubyParser.pm_version.read_string
185
178
 
186
- # Mirror the Prism.dump API by using the serialization API.
187
- def self.dump(code, filepath = nil)
188
- LibRubyParser.dump_internal(code, code.bytesize, filepath)
189
- end
179
+ class << self
180
+ # Mirror the Prism.dump API by using the serialization API.
181
+ def dump(code, **options)
182
+ LibRubyParser::PrismBuffer.with do |buffer|
183
+ LibRubyParser.pm_serialize_parse(buffer.pointer, code, code.bytesize, dump_options(options))
184
+ buffer.read
185
+ end
186
+ end
190
187
 
191
- # Mirror the Prism.dump_file API by using the serialization API.
192
- def self.dump_file(filepath)
193
- LibRubyParser::PrismString.with(filepath) do |string|
194
- LibRubyParser.dump_internal(string.source, string.length, filepath)
188
+ # Mirror the Prism.dump_file API by using the serialization API.
189
+ def dump_file(filepath, **options)
190
+ LibRubyParser::PrismString.with(filepath) do |string|
191
+ dump(string.read, **options, filepath: filepath)
192
+ end
195
193
  end
196
- end
197
194
 
198
- # Mirror the Prism.lex API by using the serialization API.
199
- def self.lex(code, filepath = nil)
200
- LibRubyParser::PrismBuffer.with do |buffer|
201
- LibRubyParser.pm_lex_serialize(code, code.bytesize, filepath, buffer.pointer)
202
- Serialize.load_tokens(Source.new(code), buffer.read)
195
+ # Mirror the Prism.lex API by using the serialization API.
196
+ def lex(code, **options)
197
+ LibRubyParser::PrismBuffer.with do |buffer|
198
+ LibRubyParser.pm_serialize_lex(buffer.pointer, code, code.bytesize, dump_options(options))
199
+ Serialize.load_tokens(Source.new(code), buffer.read)
200
+ end
203
201
  end
204
- end
205
202
 
206
- # Mirror the Prism.lex_file API by using the serialization API.
207
- def self.lex_file(filepath)
208
- LibRubyParser::PrismString.with(filepath) do |string|
209
- lex(string.read, filepath)
203
+ # Mirror the Prism.lex_file API by using the serialization API.
204
+ def lex_file(filepath, **options)
205
+ LibRubyParser::PrismString.with(filepath) do |string|
206
+ lex(string.read, **options, filepath: filepath)
207
+ end
210
208
  end
211
- end
212
209
 
213
- # Mirror the Prism.parse API by using the serialization API.
214
- def self.parse(code, filepath = nil)
215
- Prism.load(code, dump(code, filepath))
216
- end
210
+ # Mirror the Prism.parse API by using the serialization API.
211
+ def parse(code, **options)
212
+ Prism.load(code, dump(code, **options))
213
+ end
217
214
 
218
- # Mirror the Prism.parse_file API by using the serialization API. This uses
219
- # native strings instead of Ruby strings because it allows us to use mmap when
220
- # it is available.
221
- def self.parse_file(filepath)
222
- LibRubyParser::PrismString.with(filepath) do |string|
223
- parse(string.read, filepath)
215
+ # Mirror the Prism.parse_file API by using the serialization API. This uses
216
+ # native strings instead of Ruby strings because it allows us to use mmap when
217
+ # it is available.
218
+ def parse_file(filepath, **options)
219
+ LibRubyParser::PrismString.with(filepath) do |string|
220
+ parse(string.read, **options, filepath: filepath)
221
+ end
224
222
  end
225
- end
226
223
 
227
- # Mirror the Prism.parse_lex API by using the serialization API.
228
- def self.parse_lex(code, filepath = nil)
229
- LibRubyParser::PrismBuffer.with do |buffer|
230
- metadata = [filepath.bytesize, filepath.b, 0].pack("LA*L") if filepath
231
- LibRubyParser.pm_parse_lex_serialize(code, code.bytesize, buffer.pointer, metadata)
224
+ # Mirror the Prism.parse_comments API by using the serialization API.
225
+ def parse_comments(code, **options)
226
+ LibRubyParser::PrismBuffer.with do |buffer|
227
+ LibRubyParser.pm_serialize_parse_comments(buffer.pointer, code, code.bytesize, dump_options(options))
232
228
 
233
- source = Source.new(code)
234
- loader = Serialize::Loader.new(source, buffer.read)
229
+ source = Source.new(code)
230
+ loader = Serialize::Loader.new(source, buffer.read)
235
231
 
236
- tokens = loader.load_tokens
237
- node, comments, magic_comments, errors, warnings = loader.load_nodes
232
+ loader.load_header
233
+ loader.load_force_encoding
234
+ loader.load_start_line
235
+ loader.load_comments
236
+ end
237
+ end
238
+
239
+ # Mirror the Prism.parse_file_comments API by using the serialization
240
+ # API. This uses native strings instead of Ruby strings because it allows us
241
+ # to use mmap when it is available.
242
+ def parse_file_comments(filepath, **options)
243
+ LibRubyParser::PrismString.with(filepath) do |string|
244
+ parse_comments(string.read, **options, filepath: filepath)
245
+ end
246
+ end
238
247
 
239
- tokens.each { |token,| token.value.force_encoding(loader.encoding) }
248
+ # Mirror the Prism.parse_lex API by using the serialization API.
249
+ def parse_lex(code, **options)
250
+ LibRubyParser::PrismBuffer.with do |buffer|
251
+ LibRubyParser.pm_serialize_parse_lex(buffer.pointer, code, code.bytesize, dump_options(options))
240
252
 
241
- ParseResult.new([node, tokens], comments, magic_comments, errors, warnings, source)
253
+ source = Source.new(code)
254
+ loader = Serialize::Loader.new(source, buffer.read)
255
+
256
+ tokens = loader.load_tokens
257
+ node, comments, magic_comments, errors, warnings = loader.load_nodes
258
+ tokens.each { |token,| token.value.force_encoding(loader.encoding) }
259
+
260
+ ParseResult.new([node, tokens], comments, magic_comments, errors, warnings, source)
261
+ end
242
262
  end
243
- end
244
263
 
245
- # Mirror the Prism.parse_lex_file API by using the serialization API.
246
- def self.parse_lex_file(filepath)
247
- LibRubyParser::PrismString.with(filepath) do |string|
248
- parse_lex(string.read, filepath)
264
+ # Mirror the Prism.parse_lex_file API by using the serialization API.
265
+ def parse_lex_file(filepath, **options)
266
+ LibRubyParser::PrismString.with(filepath) do |string|
267
+ parse_lex(string.read, **options, filepath: filepath)
268
+ end
269
+ end
270
+
271
+ private
272
+
273
+ # Convert the given options into a serialized options string.
274
+ def dump_options(options)
275
+ template = +""
276
+ values = []
277
+
278
+ template << "L"
279
+ if (filepath = options[:filepath])
280
+ values.push(filepath.bytesize, filepath.b)
281
+ template << "A*"
282
+ else
283
+ values << 0
284
+ end
285
+
286
+ template << "L"
287
+ values << options.fetch(:line, 1)
288
+
289
+ template << "L"
290
+ if (encoding = options[:encoding])
291
+ name = encoding.name
292
+ values.push(name.bytesize, name.b)
293
+ template << "A*"
294
+ else
295
+ values << 0
296
+ end
297
+
298
+ template << "C"
299
+ values << (options.fetch(:frozen_string_literal, false) ? 1 : 0)
300
+
301
+ template << "C"
302
+ values << (options[:verbose] ? 0 : 1)
303
+
304
+ template << "L"
305
+ if (scopes = options[:scopes])
306
+ values << scopes.length
307
+
308
+ scopes.each do |scope|
309
+ template << "L"
310
+ values << scope.length
311
+
312
+ scope.each do |local|
313
+ name = local.name
314
+ template << "L"
315
+ values << name.bytesize
316
+
317
+ template << "A*"
318
+ values << name.b
319
+ end
320
+ end
321
+ else
322
+ values << 0
323
+ end
324
+
325
+ values.pack(template)
249
326
  end
250
327
  end
251
328
  end