yarp 0.6.0 → 0.8.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/CONTRIBUTING.md +4 -0
  4. data/{Makefile.in → Makefile} +5 -4
  5. data/README.md +6 -3
  6. data/config.yml +83 -274
  7. data/docs/build_system.md +4 -15
  8. data/docs/building.md +1 -5
  9. data/docs/encoding.md +1 -0
  10. data/docs/{extension.md → ruby_api.md} +6 -3
  11. data/docs/serialization.md +71 -24
  12. data/ext/yarp/api_node.c +173 -585
  13. data/ext/yarp/extconf.rb +15 -10
  14. data/ext/yarp/extension.c +4 -2
  15. data/ext/yarp/extension.h +1 -1
  16. data/include/yarp/ast.h +167 -306
  17. data/include/yarp/defines.h +5 -15
  18. data/include/yarp/enc/yp_encoding.h +1 -0
  19. data/include/yarp/unescape.h +1 -1
  20. data/include/yarp/util/yp_buffer.h +9 -0
  21. data/include/yarp/util/yp_constant_pool.h +3 -0
  22. data/include/yarp/util/yp_list.h +7 -7
  23. data/include/yarp/util/yp_newline_list.h +4 -0
  24. data/include/yarp/util/yp_state_stack.h +1 -1
  25. data/include/yarp/util/yp_string.h +5 -1
  26. data/include/yarp/version.h +2 -3
  27. data/include/yarp.h +4 -2
  28. data/lib/yarp/ffi.rb +226 -0
  29. data/lib/yarp/lex_compat.rb +16 -2
  30. data/lib/yarp/node.rb +594 -1437
  31. data/lib/yarp/ripper_compat.rb +3 -3
  32. data/lib/yarp/serialize.rb +312 -149
  33. data/lib/yarp.rb +167 -2
  34. data/src/enc/yp_unicode.c +9 -0
  35. data/src/node.c +92 -250
  36. data/src/prettyprint.c +81 -206
  37. data/src/serialize.c +124 -149
  38. data/src/unescape.c +29 -35
  39. data/src/util/yp_buffer.c +18 -0
  40. data/src/util/yp_list.c +7 -16
  41. data/src/util/yp_state_stack.c +0 -6
  42. data/src/util/yp_string.c +8 -17
  43. data/src/yarp.c +444 -717
  44. data/yarp.gemspec +5 -5
  45. metadata +6 -6
  46. data/config.h.in +0 -25
  47. data/configure +0 -4487
data/lib/yarp.rb CHANGED
@@ -7,7 +7,7 @@ module YARP
7
7
  class Source
8
8
  attr_reader :source, :offsets
9
9
 
10
- def initialize(source, offsets)
10
+ def initialize(source, offsets = compute_offsets(source))
11
11
  @source = source
12
12
  @offsets = offsets
13
13
  end
@@ -23,6 +23,14 @@ module YARP
23
23
  def column(value)
24
24
  value - offsets[line(value) - 1]
25
25
  end
26
+
27
+ private
28
+
29
+ def compute_offsets(code)
30
+ offsets = [0]
31
+ code.b.scan("\n") { offsets << $~.end(0) }
32
+ offsets
33
+ end
26
34
  end
27
35
 
28
36
  # This represents a location in the source.
@@ -101,6 +109,8 @@ module YARP
101
109
 
102
110
  # This represents a comment that was encountered during parsing.
103
111
  class Comment
112
+ TYPES = [:inline, :embdoc, :__END__]
113
+
104
114
  attr_reader :type, :location
105
115
 
106
116
  def initialize(type, location)
@@ -279,6 +289,11 @@ module YARP
279
289
  end
280
290
  end
281
291
 
292
+ # Slice the location of the node from the source.
293
+ def slice
294
+ location.slice
295
+ end
296
+
282
297
  def pretty_print(q)
283
298
  q.group do
284
299
  q.text(self.class.name.split("::").last)
@@ -306,6 +321,152 @@ module YARP
306
321
  # This module is used for testing and debugging and is not meant to be used by
307
322
  # consumers of this library.
308
323
  module Debug
324
+ class ISeq
325
+ attr_reader :parts
326
+
327
+ def initialize(parts)
328
+ @parts = parts
329
+ end
330
+
331
+ def type
332
+ parts[0]
333
+ end
334
+
335
+ def local_table
336
+ parts[10]
337
+ end
338
+
339
+ def instructions
340
+ parts[13]
341
+ end
342
+
343
+ def each_child
344
+ instructions.each do |instruction|
345
+ # Only look at arrays. Other instructions are line numbers or
346
+ # tracepoint events.
347
+ next unless instruction.is_a?(Array)
348
+
349
+ instruction.each do |opnd|
350
+ # Only look at arrays. Other operands are literals.
351
+ next unless opnd.is_a?(Array)
352
+
353
+ # Only look at instruction sequences. Other operands are literals.
354
+ next unless opnd[0] == "YARVInstructionSequence/SimpleDataFormat"
355
+
356
+ yield ISeq.new(opnd)
357
+ end
358
+ end
359
+ end
360
+ end
361
+
362
+ # For the given source, compiles with CRuby and returns a list of all of the
363
+ # sets of local variables that were encountered.
364
+ def self.cruby_locals(source)
365
+ verbose = $VERBOSE
366
+ $VERBOSE = nil
367
+
368
+ begin
369
+ locals = []
370
+ stack = [ISeq.new(RubyVM::InstructionSequence.compile(source).to_a)]
371
+
372
+ while (iseq = stack.pop)
373
+ if iseq.type != :once
374
+ names = iseq.local_table
375
+
376
+ # CRuby will push on a special local variable when there are keyword
377
+ # arguments. We get rid of that here.
378
+ names = names.grep_v(Integer)
379
+
380
+ # TODO: We don't support numbered local variables yet, so we get rid
381
+ # of those here.
382
+ names = names.grep_v(/^_\d$/)
383
+
384
+ # For some reason, CRuby occasionally pushes this special local
385
+ # variable when there are splat arguments. We get rid of that here.
386
+ names = names.grep_v(:"#arg_rest")
387
+
388
+ # Now push them onto the list of locals.
389
+ locals << names
390
+ end
391
+
392
+ iseq.each_child { |child| stack << child }
393
+ end
394
+
395
+ locals
396
+ ensure
397
+ $VERBOSE = verbose
398
+ end
399
+ end
400
+
401
+ # For the given source, parses with YARP and returns a list of all of the
402
+ # sets of local variables that were encountered.
403
+ def self.yarp_locals(source)
404
+ locals = []
405
+ stack = [YARP.parse(source).value]
406
+
407
+ while (node = stack.pop)
408
+ case node
409
+ when BlockNode, DefNode, LambdaNode
410
+ names = node.locals
411
+
412
+ params = node.parameters
413
+ params = params&.parameters unless node.is_a?(DefNode)
414
+
415
+ # YARP places parameters in the same order that they appear in the
416
+ # source. CRuby places them in the order that they need to appear
417
+ # according to their own internal calling convention. We mimic that
418
+ # order here so that we can compare properly.
419
+ if params
420
+ sorted = [
421
+ *params.requireds.grep(RequiredParameterNode).map(&:constant_id),
422
+ *params.optionals.map(&:constant_id),
423
+ *((params.rest.name ? params.rest.name.to_sym : :*) if params.rest && params.rest.operator != ","),
424
+ *params.posts.grep(RequiredParameterNode).map(&:constant_id),
425
+ *params.keywords.reject(&:value).map { |param| param.name.chomp(":").to_sym },
426
+ *params.keywords.select(&:value).map { |param| param.name.chomp(":").to_sym }
427
+ ]
428
+
429
+ # TODO: When we get a ... parameter, we should be pushing * and &
430
+ # onto the local list. We don't do that yet, so we need to add them
431
+ # in here.
432
+ if params.keyword_rest.is_a?(ForwardingParameterNode)
433
+ sorted.push(:*, :&, :"...")
434
+ end
435
+
436
+ # Recurse down the parameter tree to find any destructured
437
+ # parameters and add them after the other parameters.
438
+ param_stack = params.requireds.concat(params.posts).grep(RequiredDestructuredParameterNode).reverse
439
+ while (param = param_stack.pop)
440
+ case param
441
+ when RequiredDestructuredParameterNode
442
+ param_stack.concat(param.parameters.reverse)
443
+ when RequiredParameterNode
444
+ sorted << param.constant_id
445
+ when SplatNode
446
+ sorted << param.expression.constant_id if param.expression
447
+ end
448
+ end
449
+
450
+ names = sorted.concat(names - sorted)
451
+ end
452
+
453
+ locals << names
454
+ when ClassNode, ModuleNode, ProgramNode, SingletonClassNode
455
+ locals << node.locals
456
+ when ForNode
457
+ locals << []
458
+ when PostExecutionNode
459
+ locals.push([], [])
460
+ when InterpolatedRegularExpressionNode
461
+ locals << [] if node.once?
462
+ end
463
+
464
+ stack.concat(node.child_nodes.compact)
465
+ end
466
+
467
+ locals
468
+ end
469
+
309
470
  def self.newlines(source)
310
471
  YARP.parse(source).source.offsets
311
472
  end
@@ -327,4 +488,8 @@ require_relative "yarp/ripper_compat"
327
488
  require_relative "yarp/serialize"
328
489
  require_relative "yarp/pack"
329
490
 
330
- require "yarp/yarp"
491
+ if RUBY_ENGINE == "ruby" and !ENV["YARP_FFI_BACKEND"]
492
+ require "yarp/yarp"
493
+ else
494
+ require "yarp/ffi"
495
+ end
data/src/enc/yp_unicode.c CHANGED
@@ -2318,3 +2318,12 @@ yp_encoding_t yp_encoding_utf_8 = {
2318
2318
  .isupper_char = yp_encoding_utf_8_isupper_char,
2319
2319
  .multibyte = true
2320
2320
  };
2321
+
2322
+ yp_encoding_t yp_encoding_utf8_mac = {
2323
+ .name = "utf8-mac",
2324
+ .char_width = yp_encoding_utf_8_char_width,
2325
+ .alnum_char = yp_encoding_utf_8_alnum_char,
2326
+ .alpha_char = yp_encoding_utf_8_alpha_char,
2327
+ .isupper_char = yp_encoding_utf_8_isupper_char,
2328
+ .multibyte = true
2329
+ };