prism 0.15.1 → 0.17.0

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