jruby-prism-parser 0.24.0-java → 1.4.0-java

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 (148) hide show
  1. checksums.yaml +4 -4
  2. data/BSDmakefile +58 -0
  3. data/CHANGELOG.md +269 -1
  4. data/CONTRIBUTING.md +0 -4
  5. data/Makefile +25 -18
  6. data/README.md +57 -6
  7. data/config.yml +1724 -140
  8. data/docs/build_system.md +39 -11
  9. data/docs/configuration.md +4 -0
  10. data/docs/cruby_compilation.md +1 -1
  11. data/docs/fuzzing.md +1 -1
  12. data/docs/parser_translation.md +14 -9
  13. data/docs/parsing_rules.md +4 -1
  14. data/docs/releasing.md +8 -10
  15. data/docs/relocation.md +34 -0
  16. data/docs/ripper_translation.md +72 -0
  17. data/docs/ruby_api.md +2 -1
  18. data/docs/serialization.md +29 -5
  19. data/ext/prism/api_node.c +3395 -1999
  20. data/ext/prism/api_pack.c +9 -0
  21. data/ext/prism/extconf.rb +55 -34
  22. data/ext/prism/extension.c +597 -346
  23. data/ext/prism/extension.h +6 -5
  24. data/include/prism/ast.h +2612 -455
  25. data/include/prism/defines.h +160 -2
  26. data/include/prism/diagnostic.h +188 -76
  27. data/include/prism/encoding.h +22 -4
  28. data/include/prism/node.h +89 -17
  29. data/include/prism/options.h +224 -12
  30. data/include/prism/pack.h +11 -0
  31. data/include/prism/parser.h +267 -66
  32. data/include/prism/prettyprint.h +8 -0
  33. data/include/prism/regexp.h +18 -8
  34. data/include/prism/static_literals.h +121 -0
  35. data/include/prism/util/pm_buffer.h +75 -2
  36. data/include/prism/util/pm_char.h +1 -2
  37. data/include/prism/util/pm_constant_pool.h +18 -9
  38. data/include/prism/util/pm_integer.h +126 -0
  39. data/include/prism/util/pm_list.h +1 -1
  40. data/include/prism/util/pm_newline_list.h +19 -0
  41. data/include/prism/util/pm_string.h +48 -8
  42. data/include/prism/version.h +3 -3
  43. data/include/prism.h +99 -5
  44. data/jruby-prism.jar +0 -0
  45. data/lib/prism/compiler.rb +11 -1
  46. data/lib/prism/desugar_compiler.rb +113 -74
  47. data/lib/prism/dispatcher.rb +45 -1
  48. data/lib/prism/dot_visitor.rb +201 -77
  49. data/lib/prism/dsl.rb +673 -461
  50. data/lib/prism/ffi.rb +233 -45
  51. data/lib/prism/inspect_visitor.rb +2389 -0
  52. data/lib/prism/lex_compat.rb +35 -16
  53. data/lib/prism/mutation_compiler.rb +24 -8
  54. data/lib/prism/node.rb +7731 -8460
  55. data/lib/prism/node_ext.rb +328 -32
  56. data/lib/prism/pack.rb +4 -0
  57. data/lib/prism/parse_result/comments.rb +34 -24
  58. data/lib/prism/parse_result/errors.rb +65 -0
  59. data/lib/prism/parse_result/newlines.rb +102 -12
  60. data/lib/prism/parse_result.rb +448 -44
  61. data/lib/prism/pattern.rb +28 -10
  62. data/lib/prism/polyfill/append_as_bytes.rb +15 -0
  63. data/lib/prism/polyfill/byteindex.rb +13 -0
  64. data/lib/prism/polyfill/unpack1.rb +14 -0
  65. data/lib/prism/reflection.rb +413 -0
  66. data/lib/prism/relocation.rb +504 -0
  67. data/lib/prism/serialize.rb +1940 -1198
  68. data/lib/prism/string_query.rb +30 -0
  69. data/lib/prism/translation/parser/builder.rb +61 -0
  70. data/lib/prism/translation/parser/compiler.rb +569 -195
  71. data/lib/prism/translation/parser/lexer.rb +516 -39
  72. data/lib/prism/translation/parser.rb +177 -12
  73. data/lib/prism/translation/parser33.rb +1 -1
  74. data/lib/prism/translation/parser34.rb +1 -1
  75. data/lib/prism/translation/parser35.rb +12 -0
  76. data/lib/prism/translation/ripper/sexp.rb +125 -0
  77. data/lib/prism/translation/ripper/shim.rb +5 -0
  78. data/lib/prism/translation/ripper.rb +3224 -462
  79. data/lib/prism/translation/ruby_parser.rb +194 -69
  80. data/lib/prism/translation.rb +4 -1
  81. data/lib/prism/version.rb +1 -1
  82. data/lib/prism/visitor.rb +13 -0
  83. data/lib/prism.rb +17 -27
  84. data/prism.gemspec +57 -17
  85. data/rbi/prism/compiler.rbi +12 -0
  86. data/rbi/prism/dsl.rbi +524 -0
  87. data/rbi/prism/inspect_visitor.rbi +12 -0
  88. data/rbi/prism/node.rbi +8722 -0
  89. data/rbi/prism/node_ext.rbi +107 -0
  90. data/rbi/prism/parse_result.rbi +404 -0
  91. data/rbi/prism/reflection.rbi +58 -0
  92. data/rbi/prism/string_query.rbi +12 -0
  93. data/rbi/prism/translation/parser.rbi +11 -0
  94. data/rbi/prism/translation/parser33.rbi +6 -0
  95. data/rbi/prism/translation/parser34.rbi +6 -0
  96. data/rbi/prism/translation/parser35.rbi +6 -0
  97. data/rbi/prism/translation/ripper.rbi +15 -0
  98. data/rbi/prism/visitor.rbi +473 -0
  99. data/rbi/prism.rbi +44 -7745
  100. data/sig/prism/compiler.rbs +9 -0
  101. data/sig/prism/dispatcher.rbs +16 -0
  102. data/sig/prism/dot_visitor.rbs +6 -0
  103. data/sig/prism/dsl.rbs +351 -0
  104. data/sig/prism/inspect_visitor.rbs +22 -0
  105. data/sig/prism/lex_compat.rbs +10 -0
  106. data/sig/prism/mutation_compiler.rbs +159 -0
  107. data/sig/prism/node.rbs +3614 -0
  108. data/sig/prism/node_ext.rbs +82 -0
  109. data/sig/prism/pack.rbs +43 -0
  110. data/sig/prism/parse_result.rbs +192 -0
  111. data/sig/prism/pattern.rbs +13 -0
  112. data/sig/prism/reflection.rbs +50 -0
  113. data/sig/prism/relocation.rbs +185 -0
  114. data/sig/prism/serialize.rbs +8 -0
  115. data/sig/prism/string_query.rbs +11 -0
  116. data/sig/prism/visitor.rbs +169 -0
  117. data/sig/prism.rbs +248 -4767
  118. data/src/diagnostic.c +672 -230
  119. data/src/encoding.c +211 -108
  120. data/src/node.c +7541 -1653
  121. data/src/options.c +135 -20
  122. data/src/pack.c +33 -17
  123. data/src/prettyprint.c +1543 -1485
  124. data/src/prism.c +7813 -3050
  125. data/src/regexp.c +225 -73
  126. data/src/serialize.c +101 -77
  127. data/src/static_literals.c +617 -0
  128. data/src/token_type.c +14 -13
  129. data/src/util/pm_buffer.c +187 -20
  130. data/src/util/pm_char.c +5 -5
  131. data/src/util/pm_constant_pool.c +39 -19
  132. data/src/util/pm_integer.c +670 -0
  133. data/src/util/pm_list.c +1 -1
  134. data/src/util/pm_newline_list.c +43 -5
  135. data/src/util/pm_string.c +213 -33
  136. data/src/util/pm_strncasecmp.c +13 -1
  137. data/src/util/pm_strpbrk.c +32 -6
  138. metadata +55 -19
  139. data/docs/ripper.md +0 -36
  140. data/include/prism/util/pm_state_stack.h +0 -42
  141. data/include/prism/util/pm_string_list.h +0 -44
  142. data/lib/prism/debug.rb +0 -206
  143. data/lib/prism/node_inspector.rb +0 -68
  144. data/lib/prism/translation/parser/rubocop.rb +0 -45
  145. data/rbi/prism_static.rbi +0 -207
  146. data/sig/prism_static.rbs +0 -201
  147. data/src/util/pm_state_stack.c +0 -25
  148. data/src/util/pm_string_list.c +0 -28
data/lib/prism/ffi.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # typed: ignore
2
3
 
3
4
  # This file is responsible for mirroring the API provided by the C extension by
4
5
  # using FFI to call into the shared library.
@@ -7,14 +8,21 @@ require "rbconfig"
7
8
  require "ffi"
8
9
 
9
10
  module Prism
10
- BACKEND = :FFI
11
-
12
11
  module LibRubyParser # :nodoc:
13
12
  extend FFI::Library
14
13
 
15
14
  # Define the library that we will be pulling functions from. Note that this
16
15
  # must align with the build shared library from make/rake.
17
- ffi_lib File.expand_path("../../build/libprism.#{RbConfig::CONFIG["SOEXT"]}", __dir__)
16
+ libprism_in_build = File.expand_path("../../build/libprism.#{RbConfig::CONFIG["SOEXT"]}", __dir__)
17
+ libprism_in_libdir = "#{RbConfig::CONFIG["libdir"]}/prism/libprism.#{RbConfig::CONFIG["SOEXT"]}"
18
+
19
+ if File.exist?(libprism_in_build)
20
+ INCLUDE_DIR = File.expand_path("../../include", __dir__)
21
+ ffi_lib libprism_in_build
22
+ else
23
+ INCLUDE_DIR = "#{RbConfig::CONFIG["libdir"]}/prism/include"
24
+ ffi_lib libprism_in_libdir
25
+ end
18
26
 
19
27
  # Convert a native C type declaration into a symbol that FFI understands.
20
28
  # For example:
@@ -24,16 +32,22 @@ module Prism
24
32
  # size_t -> :size_t
25
33
  # void -> :void
26
34
  #
27
- def self.resolve_type(type)
35
+ def self.resolve_type(type, callbacks)
28
36
  type = type.strip
29
- type.end_with?("*") ? :pointer : type.delete_prefix("const ").to_sym
37
+
38
+ if !type.end_with?("*")
39
+ type.delete_prefix("const ").to_sym
40
+ else
41
+ type = type.delete_suffix("*").rstrip
42
+ callbacks.include?(type.to_sym) ? type.to_sym : :pointer
43
+ end
30
44
  end
31
45
 
32
46
  # Read through the given header file and find the declaration of each of the
33
47
  # given functions. For each one, define a function with the same name and
34
48
  # signature as the C function.
35
- def self.load_exported_functions_from(header, *functions)
36
- File.foreach(File.expand_path("../../include/#{header}", __dir__)) do |line|
49
+ def self.load_exported_functions_from(header, *functions, callbacks)
50
+ File.foreach("#{INCLUDE_DIR}/#{header}") do |line|
37
51
  # We only want to attempt to load exported functions.
38
52
  next unless line.start_with?("PRISM_EXPORTED_FUNCTION ")
39
53
 
@@ -56,24 +70,33 @@ module Prism
56
70
 
57
71
  # Resolve the type of the argument by dropping the name of the argument
58
72
  # first if it is present.
59
- arg_types.map! { |type| resolve_type(type.sub(/\w+$/, "")) }
73
+ arg_types.map! { |type| resolve_type(type.sub(/\w+$/, ""), callbacks) }
60
74
 
61
75
  # Attach the function using the FFI library.
62
- attach_function name, arg_types, resolve_type(return_type)
76
+ attach_function name, arg_types, resolve_type(return_type, [])
63
77
  end
64
78
 
65
79
  # If we didn't find all of the functions, raise an error.
66
80
  raise "Could not find functions #{functions.inspect}" unless functions.empty?
67
81
  end
68
82
 
83
+ callback :pm_parse_stream_fgets_t, [:pointer, :int, :pointer], :pointer
84
+ enum :pm_string_init_result_t, %i[PM_STRING_INIT_SUCCESS PM_STRING_INIT_ERROR_GENERIC PM_STRING_INIT_ERROR_DIRECTORY]
85
+ enum :pm_string_query_t, [:PM_STRING_QUERY_ERROR, -1, :PM_STRING_QUERY_FALSE, :PM_STRING_QUERY_TRUE]
86
+
69
87
  load_exported_functions_from(
70
88
  "prism.h",
71
89
  "pm_version",
72
90
  "pm_serialize_parse",
91
+ "pm_serialize_parse_stream",
73
92
  "pm_serialize_parse_comments",
74
93
  "pm_serialize_lex",
75
94
  "pm_serialize_parse_lex",
76
- "pm_parse_success_p"
95
+ "pm_parse_success_p",
96
+ "pm_string_query_local",
97
+ "pm_string_query_constant",
98
+ "pm_string_query_method_name",
99
+ [:pm_parse_stream_fgets_t]
77
100
  )
78
101
 
79
102
  load_exported_functions_from(
@@ -82,7 +105,8 @@ module Prism
82
105
  "pm_buffer_init",
83
106
  "pm_buffer_value",
84
107
  "pm_buffer_length",
85
- "pm_buffer_free"
108
+ "pm_buffer_free",
109
+ []
86
110
  )
87
111
 
88
112
  load_exported_functions_from(
@@ -91,7 +115,8 @@ module Prism
91
115
  "pm_string_free",
92
116
  "pm_string_source",
93
117
  "pm_string_length",
94
- "pm_string_sizeof"
118
+ "pm_string_sizeof",
119
+ []
95
120
  )
96
121
 
97
122
  # This object represents a pm_buffer_t. We only use it as an opaque pointer,
@@ -165,13 +190,26 @@ module Prism
165
190
  def self.with_file(filepath)
166
191
  raise TypeError unless filepath.is_a?(String)
167
192
 
193
+ # On Windows and Mac, it's expected that filepaths will be encoded in
194
+ # UTF-8. If they are not, we need to convert them to UTF-8 before
195
+ # passing them into pm_string_mapped_init.
196
+ if RbConfig::CONFIG["host_os"].match?(/bccwin|cygwin|djgpp|mingw|mswin|wince|darwin/i) &&
197
+ (encoding = filepath.encoding) != Encoding::ASCII_8BIT && encoding != Encoding::UTF_8
198
+ filepath = filepath.encode(Encoding::UTF_8)
199
+ end
200
+
168
201
  FFI::MemoryPointer.new(SIZEOF) do |pm_string|
169
- if LibRubyParser.pm_string_mapped_init(pm_string, filepath)
202
+ case (result = LibRubyParser.pm_string_mapped_init(pm_string, filepath))
203
+ when :PM_STRING_INIT_SUCCESS
170
204
  pointer = LibRubyParser.pm_string_source(pm_string)
171
205
  length = LibRubyParser.pm_string_length(pm_string)
172
206
  return yield new(pointer, length, false)
173
- else
207
+ when :PM_STRING_INIT_ERROR_GENERIC
174
208
  raise SystemCallError.new(filepath, FFI.errno)
209
+ when :PM_STRING_INIT_ERROR_DIRECTORY
210
+ raise Errno::EISDIR.new(filepath)
211
+ else
212
+ raise "Unknown error initializing pm_string_t: #{result.inspect}"
175
213
  end
176
214
  ensure
177
215
  LibRubyParser.pm_string_free(pm_string)
@@ -189,8 +227,8 @@ module Prism
189
227
 
190
228
  class << self
191
229
  # Mirror the Prism.dump API by using the serialization API.
192
- def dump(code, **options)
193
- LibRubyParser::PrismString.with_string(code) { |string| dump_common(string, options) }
230
+ def dump(source, **options)
231
+ LibRubyParser::PrismString.with_string(source) { |string| dump_common(string, options) }
194
232
  end
195
233
 
196
234
  # Mirror the Prism.dump_file API by using the serialization API.
@@ -216,13 +254,36 @@ module Prism
216
254
  end
217
255
 
218
256
  # 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.
257
+ # native strings instead of Ruby strings because it allows us to use mmap
258
+ # when it is available.
221
259
  def parse_file(filepath, **options)
222
260
  options[:filepath] = filepath
223
261
  LibRubyParser::PrismString.with_file(filepath) { |string| parse_common(string, string.read, options) }
224
262
  end
225
263
 
264
+ # Mirror the Prism.parse_stream API by using the serialization API.
265
+ def parse_stream(stream, **options)
266
+ LibRubyParser::PrismBuffer.with do |buffer|
267
+ source = +""
268
+ callback = -> (string, size, _) {
269
+ raise "Expected size to be >= 0, got: #{size}" if size <= 0
270
+
271
+ if !(line = stream.gets(size - 1)).nil?
272
+ source << line
273
+ string.write_string("#{line}\x00", line.bytesize + 1)
274
+ end
275
+ }
276
+
277
+ # In the pm_serialize_parse_stream function it accepts a pointer to the
278
+ # IO object as a void* and then passes it through to the callback as the
279
+ # third argument, but it never touches it itself. As such, since we have
280
+ # access to the IO object already through the closure of the lambda, we
281
+ # can pass a null pointer here and not worry.
282
+ LibRubyParser.pm_serialize_parse_stream(buffer.pointer, nil, callback, dump_options(options))
283
+ Prism.load(source, buffer.read, options.fetch(:freeze, false))
284
+ end
285
+ end
286
+
226
287
  # Mirror the Prism.parse_comments API by using the serialization API.
227
288
  def parse_comments(code, **options)
228
289
  LibRubyParser::PrismString.with_string(code) { |string| parse_comments_common(string, code, options) }
@@ -252,61 +313,79 @@ module Prism
252
313
  LibRubyParser::PrismString.with_string(code) { |string| parse_file_success_common(string, options) }
253
314
  end
254
315
 
316
+ # Mirror the Prism.parse_failure? API by using the serialization API.
317
+ def parse_failure?(code, **options)
318
+ !parse_success?(code, **options)
319
+ end
320
+
255
321
  # Mirror the Prism.parse_file_success? API by using the serialization API.
256
322
  def parse_file_success?(filepath, **options)
257
323
  options[:filepath] = filepath
258
324
  LibRubyParser::PrismString.with_file(filepath) { |string| parse_file_success_common(string, options) }
259
325
  end
260
326
 
327
+ # Mirror the Prism.parse_file_failure? API by using the serialization API.
328
+ def parse_file_failure?(filepath, **options)
329
+ !parse_file_success?(filepath, **options)
330
+ end
331
+
332
+ # Mirror the Prism.profile API by using the serialization API.
333
+ def profile(source, **options)
334
+ LibRubyParser::PrismString.with_string(source) do |string|
335
+ LibRubyParser::PrismBuffer.with do |buffer|
336
+ LibRubyParser.pm_serialize_parse(buffer.pointer, string.pointer, string.length, dump_options(options))
337
+ nil
338
+ end
339
+ end
340
+ end
341
+
342
+ # Mirror the Prism.profile_file API by using the serialization API.
343
+ def profile_file(filepath, **options)
344
+ LibRubyParser::PrismString.with_file(filepath) do |string|
345
+ LibRubyParser::PrismBuffer.with do |buffer|
346
+ options[:filepath] = filepath
347
+ LibRubyParser.pm_serialize_parse(buffer.pointer, string.pointer, string.length, dump_options(options))
348
+ nil
349
+ end
350
+ end
351
+ end
352
+
261
353
  private
262
354
 
263
355
  def dump_common(string, options) # :nodoc:
264
356
  LibRubyParser::PrismBuffer.with do |buffer|
265
357
  LibRubyParser.pm_serialize_parse(buffer.pointer, string.pointer, string.length, dump_options(options))
266
- buffer.read
358
+
359
+ dumped = buffer.read
360
+ dumped.freeze if options.fetch(:freeze, false)
361
+
362
+ dumped
267
363
  end
268
364
  end
269
365
 
270
366
  def lex_common(string, code, options) # :nodoc:
271
- serialized = LibRubyParser::PrismBuffer.with do |buffer|
367
+ LibRubyParser::PrismBuffer.with do |buffer|
272
368
  LibRubyParser.pm_serialize_lex(buffer.pointer, string.pointer, string.length, dump_options(options))
273
- buffer.read
369
+ Serialize.load_lex(code, buffer.read, options.fetch(:freeze, false))
274
370
  end
275
-
276
- Serialize.load_tokens(Source.new(code), serialized)
277
371
  end
278
372
 
279
373
  def parse_common(string, code, options) # :nodoc:
280
374
  serialized = dump_common(string, options)
281
- Prism.load(code, serialized)
375
+ Serialize.load_parse(code, serialized, options.fetch(:freeze, false))
282
376
  end
283
377
 
284
378
  def parse_comments_common(string, code, options) # :nodoc:
285
379
  LibRubyParser::PrismBuffer.with do |buffer|
286
380
  LibRubyParser.pm_serialize_parse_comments(buffer.pointer, string.pointer, string.length, dump_options(options))
287
-
288
- source = Source.new(code)
289
- loader = Serialize::Loader.new(source, buffer.read)
290
-
291
- loader.load_header
292
- loader.load_encoding
293
- loader.load_start_line
294
- loader.load_comments
381
+ Serialize.load_parse_comments(code, buffer.read, options.fetch(:freeze, false))
295
382
  end
296
383
  end
297
384
 
298
385
  def parse_lex_common(string, code, options) # :nodoc:
299
386
  LibRubyParser::PrismBuffer.with do |buffer|
300
387
  LibRubyParser.pm_serialize_parse_lex(buffer.pointer, string.pointer, string.length, dump_options(options))
301
-
302
- source = Source.new(code)
303
- loader = Serialize::Loader.new(source, buffer.read)
304
-
305
- tokens = loader.load_tokens
306
- node, comments, magic_comments, data_loc, errors, warnings = loader.load_nodes
307
- tokens.each { |token,| token.value.force_encoding(loader.encoding) }
308
-
309
- ParseResult.new([node, tokens], comments, magic_comments, data_loc, errors, warnings, source)
388
+ Serialize.load_parse_lex(code, buffer.read, options.fetch(:freeze, false))
310
389
  end
311
390
  end
312
391
 
@@ -314,6 +393,40 @@ module Prism
314
393
  LibRubyParser.pm_parse_success_p(string.pointer, string.length, dump_options(options))
315
394
  end
316
395
 
396
+ # Return the value that should be dumped for the command_line option.
397
+ def dump_options_command_line(options)
398
+ command_line = options.fetch(:command_line, "")
399
+ raise ArgumentError, "command_line must be a string" unless command_line.is_a?(String)
400
+
401
+ command_line.each_char.inject(0) do |value, char|
402
+ case char
403
+ when "a" then value | 0b000001
404
+ when "e" then value | 0b000010
405
+ when "l" then value | 0b000100
406
+ when "n" then value | 0b001000
407
+ when "p" then value | 0b010000
408
+ when "x" then value | 0b100000
409
+ else raise ArgumentError, "invalid command_line option: #{char}"
410
+ end
411
+ end
412
+ end
413
+
414
+ # Return the value that should be dumped for the version option.
415
+ def dump_options_version(version)
416
+ case version
417
+ when nil, "latest"
418
+ 0
419
+ when /\A3\.3(\.\d+)?\z/
420
+ 1
421
+ when /\A3\.4(\.\d+)?\z/
422
+ 2
423
+ when /\A3\.5(\.\d+)?\z/
424
+ 0
425
+ else
426
+ raise ArgumentError, "invalid version: #{version}"
427
+ end
428
+ end
429
+
317
430
  # Convert the given options into a serialized options string.
318
431
  def dump_options(options)
319
432
  template = +""
@@ -332,7 +445,7 @@ module Prism
332
445
 
333
446
  template << "L"
334
447
  if (encoding = options[:encoding])
335
- name = encoding.name
448
+ name = encoding.is_a?(Encoding) ? encoding.name : encoding
336
449
  values.push(name.bytesize, name.b)
337
450
  template << "A*"
338
451
  else
@@ -343,17 +456,57 @@ module Prism
343
456
  values << (options.fetch(:frozen_string_literal, false) ? 1 : 0)
344
457
 
345
458
  template << "C"
346
- values << { nil => 0, "3.3.0" => 1, "3.4.0" => 0, "latest" => 0 }.fetch(options[:version])
459
+ values << dump_options_command_line(options)
460
+
461
+ template << "C"
462
+ values << dump_options_version(options[:version])
463
+
464
+ template << "C"
465
+ values << (options[:encoding] == false ? 1 : 0)
466
+
467
+ template << "C"
468
+ values << (options.fetch(:main_script, false) ? 1 : 0)
469
+
470
+ template << "C"
471
+ values << (options.fetch(:partial_script, false) ? 1 : 0)
472
+
473
+ template << "C"
474
+ values << (options.fetch(:freeze, false) ? 1 : 0)
347
475
 
348
476
  template << "L"
349
477
  if (scopes = options[:scopes])
350
478
  values << scopes.length
351
479
 
352
480
  scopes.each do |scope|
481
+ locals = nil
482
+ forwarding = 0
483
+
484
+ case scope
485
+ when Array
486
+ locals = scope
487
+ when Scope
488
+ locals = scope.locals
489
+
490
+ scope.forwarding.each do |forward|
491
+ case forward
492
+ when :* then forwarding |= 0x1
493
+ when :** then forwarding |= 0x2
494
+ when :& then forwarding |= 0x4
495
+ when :"..." then forwarding |= 0x8
496
+ else raise ArgumentError, "invalid forwarding value: #{forward}"
497
+ end
498
+ end
499
+ else
500
+ raise TypeError, "wrong argument type #{scope.class.inspect} (expected Array or Prism::Scope)"
501
+ end
502
+
353
503
  template << "L"
354
- values << scope.length
504
+ values << locals.length
355
505
 
356
- scope.each do |local|
506
+ template << "C"
507
+ values << forwarding
508
+
509
+ locals.each do |local|
357
510
  name = local.name
358
511
  template << "L"
359
512
  values << name.bytesize
@@ -369,4 +522,39 @@ module Prism
369
522
  values.pack(template)
370
523
  end
371
524
  end
525
+
526
+ # Here we are going to patch StringQuery to put in the class-level methods so
527
+ # that it can maintain a consistent interface
528
+ class StringQuery
529
+ class << self
530
+ # Mirrors the C extension's StringQuery::local? method.
531
+ def local?(string)
532
+ query(LibRubyParser.pm_string_query_local(string, string.bytesize, string.encoding.name))
533
+ end
534
+
535
+ # Mirrors the C extension's StringQuery::constant? method.
536
+ def constant?(string)
537
+ query(LibRubyParser.pm_string_query_constant(string, string.bytesize, string.encoding.name))
538
+ end
539
+
540
+ # Mirrors the C extension's StringQuery::method_name? method.
541
+ def method_name?(string)
542
+ query(LibRubyParser.pm_string_query_method_name(string, string.bytesize, string.encoding.name))
543
+ end
544
+
545
+ private
546
+
547
+ # Parse the enum result and return an appropriate boolean.
548
+ def query(result)
549
+ case result
550
+ when :PM_STRING_QUERY_ERROR
551
+ raise ArgumentError, "Invalid or non ascii-compatible encoding"
552
+ when :PM_STRING_QUERY_FALSE
553
+ false
554
+ when :PM_STRING_QUERY_TRUE
555
+ true
556
+ end
557
+ end
558
+ end
559
+ end
372
560
  end