prism 1.5.1 → 1.9.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +74 -1
  3. data/Makefile +12 -5
  4. data/README.md +2 -1
  5. data/config.yml +34 -8
  6. data/docs/build_system.md +2 -2
  7. data/docs/cruby_compilation.md +1 -1
  8. data/docs/design.md +2 -2
  9. data/docs/parser_translation.md +1 -1
  10. data/docs/releasing.md +4 -25
  11. data/docs/ripper_translation.md +8 -17
  12. data/docs/ruby_api.md +1 -0
  13. data/ext/prism/api_node.c +7 -3
  14. data/ext/prism/extconf.rb +1 -1
  15. data/ext/prism/extension.c +10 -2
  16. data/ext/prism/extension.h +1 -1
  17. data/include/prism/ast.h +89 -25
  18. data/include/prism/diagnostic.h +3 -0
  19. data/include/prism/options.h +8 -2
  20. data/include/prism/parser.h +3 -0
  21. data/include/prism/version.h +3 -3
  22. data/include/prism.h +1 -1
  23. data/lib/prism/compiler.rb +152 -152
  24. data/lib/prism/dot_visitor.rb +5 -0
  25. data/lib/prism/dsl.rb +2 -2
  26. data/lib/prism/ffi.rb +11 -3
  27. data/lib/prism/inspect_visitor.rb +1 -0
  28. data/lib/prism/lex_compat.rb +133 -150
  29. data/lib/prism/node.rb +1184 -34
  30. data/lib/prism/parse_result.rb +11 -15
  31. data/lib/prism/polyfill/scan_byte.rb +1 -1
  32. data/lib/prism/polyfill/warn.rb +16 -22
  33. data/lib/prism/reflection.rb +1 -1
  34. data/lib/prism/serialize.rb +8 -5
  35. data/lib/prism/translation/parser/compiler.rb +16 -16
  36. data/lib/prism/translation/parser.rb +12 -3
  37. data/lib/prism/translation/parser_current.rb +5 -3
  38. data/lib/prism/translation/parser_versions.rb +36 -0
  39. data/lib/prism/translation/ripper/filter.rb +53 -0
  40. data/lib/prism/translation/ripper/lexer.rb +135 -0
  41. data/lib/prism/translation/ripper.rb +86 -40
  42. data/lib/prism/translation/ruby_parser.rb +55 -20
  43. data/lib/prism/translation.rb +5 -3
  44. data/lib/prism/visitor.rb +152 -152
  45. data/lib/prism.rb +21 -14
  46. data/prism.gemspec +5 -7
  47. data/rbi/prism/dsl.rbi +3 -3
  48. data/rbi/prism/node.rbi +24 -8
  49. data/rbi/prism/translation/parser_versions.rbi +23 -0
  50. data/rbi/prism.rbi +0 -3
  51. data/sig/prism/dsl.rbs +2 -2
  52. data/sig/prism/node.rbs +22 -8
  53. data/sig/prism/parse_result.rbs +1 -0
  54. data/sig/prism.rbs +58 -40
  55. data/src/diagnostic.c +7 -1
  56. data/src/encoding.c +172 -67
  57. data/src/node.c +9 -0
  58. data/src/options.c +17 -7
  59. data/src/prettyprint.c +16 -0
  60. data/src/prism.c +1335 -1958
  61. data/src/serialize.c +7 -1
  62. data/src/token_type.c +2 -2
  63. data/src/util/pm_constant_pool.c +1 -1
  64. data/src/util/pm_string.c +6 -8
  65. metadata +7 -9
  66. data/lib/prism/translation/parser33.rb +0 -13
  67. data/lib/prism/translation/parser34.rb +0 -13
  68. data/lib/prism/translation/parser35.rb +0 -13
  69. data/rbi/prism/translation/parser33.rbi +0 -6
  70. data/rbi/prism/translation/parser34.rbi +0 -6
  71. data/rbi/prism/translation/parser35.rbi +0 -6
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  # :markup: markdown
3
3
 
4
- require "ripper"
5
-
6
4
  module Prism
7
5
  module Translation
8
6
  # This class provides a compatibility layer between prism and Ripper. It
@@ -71,7 +69,7 @@ module Prism
71
69
  # [[1, 13], :on_kw, "end", END ]]
72
70
  #
73
71
  def self.lex(src, filename = "-", lineno = 1, raise_errors: false)
74
- result = Prism.lex_compat(src, filepath: filename, line: lineno)
72
+ result = Prism.lex_compat(src, filepath: filename, line: lineno, version: "current")
75
73
 
76
74
  if result.failure? && raise_errors
77
75
  raise SyntaxError, result.errors.first.message
@@ -80,6 +78,19 @@ module Prism
80
78
  end
81
79
  end
82
80
 
81
+ # Tokenizes the Ruby program and returns an array of strings.
82
+ # The +filename+ and +lineno+ arguments are mostly ignored, since the
83
+ # return value is just the tokenized input.
84
+ # By default, this method does not handle syntax errors in +src+,
85
+ # use the +raise_errors+ keyword to raise a SyntaxError for an error in +src+.
86
+ #
87
+ # p Ripper.tokenize("def m(a) nil end")
88
+ # # => ["def", " ", "m", "(", "a", ")", " ", "nil", " ", "end"]
89
+ #
90
+ def self.tokenize(...)
91
+ lex(...).map(&:value)
92
+ end
93
+
83
94
  # This contains a table of all of the parser events and their
84
95
  # corresponding arity.
85
96
  PARSER_EVENT_TABLE = {
@@ -426,9 +437,35 @@ module Prism
426
437
  end
427
438
  end
428
439
 
440
+ autoload :Filter, "prism/translation/ripper/filter"
441
+ autoload :Lexer, "prism/translation/ripper/lexer"
429
442
  autoload :SexpBuilder, "prism/translation/ripper/sexp"
430
443
  autoload :SexpBuilderPP, "prism/translation/ripper/sexp"
431
444
 
445
+ # :stopdoc:
446
+ # This is not part of the public API but used by some gems.
447
+
448
+ # Ripper-internal bitflags.
449
+ LEX_STATE_NAMES = %i[
450
+ BEG END ENDARG ENDFN ARG CMDARG MID FNAME DOT CLASS LABEL LABELED FITEM
451
+ ].map.with_index.to_h { |name, i| [2 ** i, name] }.freeze
452
+ private_constant :LEX_STATE_NAMES
453
+
454
+ LEX_STATE_NAMES.each do |value, key|
455
+ const_set("EXPR_#{key}", value)
456
+ end
457
+ EXPR_NONE = 0
458
+ EXPR_VALUE = EXPR_BEG
459
+ EXPR_BEG_ANY = EXPR_BEG | EXPR_MID | EXPR_CLASS
460
+ EXPR_ARG_ANY = EXPR_ARG | EXPR_CMDARG
461
+ EXPR_END_ANY = EXPR_END | EXPR_ENDARG | EXPR_ENDFN
462
+
463
+ def self.lex_state_name(state)
464
+ LEX_STATE_NAMES.filter_map { |flag, name| name if state & flag != 0 }.join("|")
465
+ end
466
+
467
+ # :startdoc:
468
+
432
469
  # The source that is being parsed.
433
470
  attr_reader :source
434
471
 
@@ -795,7 +832,7 @@ module Prism
795
832
  # foo(bar)
796
833
  # ^^^
797
834
  def visit_arguments_node(node)
798
- arguments, _ = visit_call_node_arguments(node, nil, false)
835
+ arguments, _, _ = visit_call_node_arguments(node, nil, false)
799
836
  arguments
800
837
  end
801
838
 
@@ -1005,16 +1042,16 @@ module Prism
1005
1042
  case node.name
1006
1043
  when :[]
1007
1044
  receiver = visit(node.receiver)
1008
- arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
1045
+ arguments, block, has_ripper_block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
1009
1046
 
1010
1047
  bounds(node.location)
1011
1048
  call = on_aref(receiver, arguments)
1012
1049
 
1013
- if block.nil?
1014
- call
1015
- else
1050
+ if has_ripper_block
1016
1051
  bounds(node.location)
1017
1052
  on_method_add_block(call, block)
1053
+ else
1054
+ call
1018
1055
  end
1019
1056
  when :[]=
1020
1057
  receiver = visit(node.receiver)
@@ -1073,9 +1110,9 @@ module Prism
1073
1110
  if node.variable_call?
1074
1111
  on_vcall(message)
1075
1112
  else
1076
- arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1113
+ arguments, block, has_ripper_block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1077
1114
  call =
1078
- if node.opening_loc.nil? && arguments&.any?
1115
+ if node.opening_loc.nil? && get_arguments_and_block(node.arguments, node.block).first.any?
1079
1116
  bounds(node.location)
1080
1117
  on_command(message, arguments)
1081
1118
  elsif !node.opening_loc.nil?
@@ -1086,11 +1123,11 @@ module Prism
1086
1123
  on_method_add_arg(on_fcall(message), on_args_new)
1087
1124
  end
1088
1125
 
1089
- if block.nil?
1090
- call
1091
- else
1126
+ if has_ripper_block
1092
1127
  bounds(node.block.location)
1093
1128
  on_method_add_block(call, block)
1129
+ else
1130
+ call
1094
1131
  end
1095
1132
  end
1096
1133
  end
@@ -1114,7 +1151,7 @@ module Prism
1114
1151
  bounds(node.location)
1115
1152
  on_assign(on_field(receiver, call_operator, message), value)
1116
1153
  else
1117
- arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1154
+ arguments, block, has_ripper_block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1118
1155
  call =
1119
1156
  if node.opening_loc.nil?
1120
1157
  bounds(node.location)
@@ -1132,27 +1169,35 @@ module Prism
1132
1169
  on_method_add_arg(on_call(receiver, call_operator, message), arguments)
1133
1170
  end
1134
1171
 
1135
- if block.nil?
1136
- call
1137
- else
1172
+ if has_ripper_block
1138
1173
  bounds(node.block.location)
1139
1174
  on_method_add_block(call, block)
1175
+ else
1176
+ call
1140
1177
  end
1141
1178
  end
1142
1179
  end
1143
1180
  end
1144
1181
 
1145
- # Visit the arguments and block of a call node and return the arguments
1146
- # and block as they should be used.
1147
- private def visit_call_node_arguments(arguments_node, block_node, trailing_comma)
1182
+ # Extract the arguments and block Ripper-style, which means if the block
1183
+ # is like `&b` then it's moved to arguments.
1184
+ private def get_arguments_and_block(arguments_node, block_node)
1148
1185
  arguments = arguments_node&.arguments || []
1149
1186
  block = block_node
1150
1187
 
1151
1188
  if block.is_a?(BlockArgumentNode)
1152
- arguments << block
1189
+ arguments += [block]
1153
1190
  block = nil
1154
1191
  end
1155
1192
 
1193
+ [arguments, block]
1194
+ end
1195
+
1196
+ # Visit the arguments and block of a call node and return the arguments
1197
+ # and block as they should be used.
1198
+ private def visit_call_node_arguments(arguments_node, block_node, trailing_comma)
1199
+ arguments, block = get_arguments_and_block(arguments_node, block_node)
1200
+
1156
1201
  [
1157
1202
  if arguments.length == 1 && arguments.first.is_a?(ForwardingArgumentsNode)
1158
1203
  visit(arguments.first)
@@ -1166,7 +1211,8 @@ module Prism
1166
1211
  on_args_add_block(args, false)
1167
1212
  end
1168
1213
  end,
1169
- visit(block)
1214
+ visit(block),
1215
+ block != nil,
1170
1216
  ]
1171
1217
  end
1172
1218
 
@@ -1603,10 +1649,10 @@ module Prism
1603
1649
  end
1604
1650
 
1605
1651
  bounds(node.location)
1606
- if receiver.nil?
1607
- on_def(name, parameters, bodystmt)
1608
- else
1652
+ if receiver
1609
1653
  on_defs(receiver, operator, name, parameters, bodystmt)
1654
+ else
1655
+ on_def(name, parameters, bodystmt)
1610
1656
  end
1611
1657
  end
1612
1658
 
@@ -2004,7 +2050,7 @@ module Prism
2004
2050
  # ^^^^^^^^^^^^^^^
2005
2051
  def visit_index_operator_write_node(node)
2006
2052
  receiver = visit(node.receiver)
2007
- arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2053
+ arguments, _, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2008
2054
 
2009
2055
  bounds(node.location)
2010
2056
  target = on_aref_field(receiver, arguments)
@@ -2021,7 +2067,7 @@ module Prism
2021
2067
  # ^^^^^^^^^^^^^^^^
2022
2068
  def visit_index_and_write_node(node)
2023
2069
  receiver = visit(node.receiver)
2024
- arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2070
+ arguments, _, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2025
2071
 
2026
2072
  bounds(node.location)
2027
2073
  target = on_aref_field(receiver, arguments)
@@ -2038,7 +2084,7 @@ module Prism
2038
2084
  # ^^^^^^^^^^^^^^^^
2039
2085
  def visit_index_or_write_node(node)
2040
2086
  receiver = visit(node.receiver)
2041
- arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2087
+ arguments, _, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2042
2088
 
2043
2089
  bounds(node.location)
2044
2090
  target = on_aref_field(receiver, arguments)
@@ -2055,7 +2101,7 @@ module Prism
2055
2101
  # ^^^^^^^^
2056
2102
  def visit_index_target_node(node)
2057
2103
  receiver = visit(node.receiver)
2058
- arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2104
+ arguments, _, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2059
2105
 
2060
2106
  bounds(node.location)
2061
2107
  on_aref_field(receiver, arguments)
@@ -3085,7 +3131,7 @@ module Prism
3085
3131
  # super(foo)
3086
3132
  # ^^^^^^^^^^
3087
3133
  def visit_super_node(node)
3088
- arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.rparen_loc || node.location))
3134
+ arguments, block, has_ripper_block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.rparen_loc || node.location))
3089
3135
 
3090
3136
  if !node.lparen_loc.nil?
3091
3137
  bounds(node.lparen_loc)
@@ -3095,11 +3141,11 @@ module Prism
3095
3141
  bounds(node.location)
3096
3142
  call = on_super(arguments)
3097
3143
 
3098
- if block.nil?
3099
- call
3100
- else
3144
+ if has_ripper_block
3101
3145
  bounds(node.block.location)
3102
3146
  on_method_add_block(call, block)
3147
+ else
3148
+ call
3103
3149
  end
3104
3150
  end
3105
3151
 
@@ -3295,7 +3341,7 @@ module Prism
3295
3341
 
3296
3342
  # Lazily initialize the parse result.
3297
3343
  def result
3298
- @result ||= Prism.parse(source, partial_script: true)
3344
+ @result ||= Prism.parse(source, partial_script: true, version: "current")
3299
3345
  end
3300
3346
 
3301
3347
  ##########################################################################
@@ -3409,12 +3455,12 @@ module Prism
3409
3455
 
3410
3456
  # :stopdoc:
3411
3457
  def _dispatch_0; end
3412
- def _dispatch_1(_); end
3413
- def _dispatch_2(_, _); end
3414
- def _dispatch_3(_, _, _); end
3415
- def _dispatch_4(_, _, _, _); end
3416
- def _dispatch_5(_, _, _, _, _); end
3417
- def _dispatch_7(_, _, _, _, _, _, _); end
3458
+ def _dispatch_1(arg); arg end
3459
+ def _dispatch_2(arg, _); arg end
3460
+ def _dispatch_3(arg, _, _); arg end
3461
+ def _dispatch_4(arg, _, _, _); arg end
3462
+ def _dispatch_5(arg, _, _, _, _); arg end
3463
+ def _dispatch_7(arg, _, _, _, _, _, _); arg end
3418
3464
  # :startdoc:
3419
3465
 
3420
3466
  #
@@ -2,12 +2,17 @@
2
2
  # :markup: markdown
3
3
 
4
4
  begin
5
- require "ruby_parser"
5
+ require "sexp"
6
6
  rescue LoadError
7
- warn(%q{Error: Unable to load ruby_parser. Add `gem "ruby_parser"` to your Gemfile.})
7
+ warn(%q{Error: Unable to load sexp. Add `gem "sexp_processor"` to your Gemfile.})
8
8
  exit(1)
9
9
  end
10
10
 
11
+ class RubyParser # :nodoc:
12
+ class SyntaxError < RuntimeError # :nodoc:
13
+ end
14
+ end
15
+
11
16
  module Prism
12
17
  module Translation
13
18
  # This module is the entry-point for converting a prism syntax tree into the
@@ -152,7 +157,7 @@ module Prism
152
157
  # ^^
153
158
  # ```
154
159
  def visit_back_reference_read_node(node)
155
- s(node, :back_ref, node.name.name.delete_prefix("$").to_sym)
160
+ s(node, :back_ref, node.name.to_s.delete_prefix("$").to_sym)
156
161
  end
157
162
 
158
163
  # ```
@@ -415,14 +420,18 @@ module Prism
415
420
  visit(node.constant_path)
416
421
  end
417
422
 
418
- if node.body.nil?
419
- s(node, :class, name, visit(node.superclass))
420
- elsif node.body.is_a?(StatementsNode)
421
- compiler = copy_compiler(in_def: false)
422
- s(node, :class, name, visit(node.superclass)).concat(node.body.body.map { |child| child.accept(compiler) })
423
- else
424
- s(node, :class, name, visit(node.superclass), node.body.accept(copy_compiler(in_def: false)))
425
- end
423
+ result =
424
+ if node.body.nil?
425
+ s(node, :class, name, visit(node.superclass))
426
+ elsif node.body.is_a?(StatementsNode)
427
+ compiler = copy_compiler(in_def: false)
428
+ s(node, :class, name, visit(node.superclass)).concat(node.body.body.map { |child| child.accept(compiler) })
429
+ else
430
+ s(node, :class, name, visit(node.superclass), node.body.accept(copy_compiler(in_def: false)))
431
+ end
432
+
433
+ attach_comments(result, node)
434
+ result
426
435
  end
427
436
 
428
437
  # ```
@@ -611,7 +620,9 @@ module Prism
611
620
  s(node, :defs, visit(node.receiver), name)
612
621
  end
613
622
 
623
+ attach_comments(result, node)
614
624
  result.line(node.name_loc.start_line)
625
+
615
626
  if node.parameters.nil?
616
627
  result << s(node, :args).line(node.name_loc.start_line)
617
628
  else
@@ -1270,14 +1281,18 @@ module Prism
1270
1281
  visit(node.constant_path)
1271
1282
  end
1272
1283
 
1273
- if node.body.nil?
1274
- s(node, :module, name)
1275
- elsif node.body.is_a?(StatementsNode)
1276
- compiler = copy_compiler(in_def: false)
1277
- s(node, :module, name).concat(node.body.body.map { |child| child.accept(compiler) })
1278
- else
1279
- s(node, :module, name, node.body.accept(copy_compiler(in_def: false)))
1280
- end
1284
+ result =
1285
+ if node.body.nil?
1286
+ s(node, :module, name)
1287
+ elsif node.body.is_a?(StatementsNode)
1288
+ compiler = copy_compiler(in_def: false)
1289
+ s(node, :module, name).concat(node.body.body.map { |child| child.accept(compiler) })
1290
+ else
1291
+ s(node, :module, name, node.body.accept(copy_compiler(in_def: false)))
1292
+ end
1293
+
1294
+ attach_comments(result, node)
1295
+ result
1281
1296
  end
1282
1297
 
1283
1298
  # ```
@@ -1407,7 +1422,7 @@ module Prism
1407
1422
  # ```
1408
1423
  def visit_parameters_node(node)
1409
1424
  children =
1410
- node.compact_child_nodes.map do |element|
1425
+ node.each_child_node.map do |element|
1411
1426
  if element.is_a?(MultiTargetNode)
1412
1427
  visit_destructured_parameter(element)
1413
1428
  else
@@ -1820,6 +1835,17 @@ module Prism
1820
1835
 
1821
1836
  private
1822
1837
 
1838
+ # Attach prism comments to the given sexp.
1839
+ def attach_comments(sexp, node)
1840
+ return unless node.comments
1841
+ return if node.comments.empty?
1842
+
1843
+ extra = node.location.start_line - node.comments.last.location.start_line
1844
+ comments = node.comments.map(&:slice)
1845
+ comments.concat([nil] * [0, extra].max)
1846
+ sexp.comments = comments.join("\n")
1847
+ end
1848
+
1823
1849
  # Create a new compiler with the given options.
1824
1850
  def copy_compiler(in_def: self.in_def, in_pattern: self.in_pattern)
1825
1851
  Compiler.new(file, in_def: in_def, in_pattern: in_pattern)
@@ -1898,6 +1924,14 @@ module Prism
1898
1924
  translate(Prism.parse_file(filepath, partial_script: true), filepath)
1899
1925
  end
1900
1926
 
1927
+ # Parse the give file and translate it into the
1928
+ # seattlerb/ruby_parser gem's Sexp format. This method is
1929
+ # provided for API compatibility to RubyParser and takes an
1930
+ # optional +timeout+ argument.
1931
+ def process(ruby, file = "(string)", timeout = nil)
1932
+ Timeout.timeout(timeout) { parse(ruby, file) }
1933
+ end
1934
+
1901
1935
  class << self
1902
1936
  # Parse the given source and translate it into the seattlerb/ruby_parser
1903
1937
  # gem's Sexp format.
@@ -1922,6 +1956,7 @@ module Prism
1922
1956
  raise ::RubyParser::SyntaxError, "#{filepath}:#{error.location.start_line} :: #{error.message}"
1923
1957
  end
1924
1958
 
1959
+ result.attach_comments!
1925
1960
  result.value.accept(Compiler.new(filepath))
1926
1961
  end
1927
1962
  end
@@ -7,9 +7,11 @@ module Prism
7
7
  module Translation # steep:ignore
8
8
  autoload :Parser, "prism/translation/parser"
9
9
  autoload :ParserCurrent, "prism/translation/parser_current"
10
- autoload :Parser33, "prism/translation/parser33"
11
- autoload :Parser34, "prism/translation/parser34"
12
- autoload :Parser35, "prism/translation/parser35"
10
+ autoload :Parser33, "prism/translation/parser_versions"
11
+ autoload :Parser34, "prism/translation/parser_versions"
12
+ autoload :Parser35, "prism/translation/parser_versions"
13
+ autoload :Parser40, "prism/translation/parser_versions"
14
+ autoload :Parser41, "prism/translation/parser_versions"
13
15
  autoload :Ripper, "prism/translation/ripper"
14
16
  autoload :RubyParser, "prism/translation/ruby_parser"
15
17
  end