ripper_ruby_parser 1.10.0 → 1.12.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a110a987032924880be79cfbb79948e14f90868e8105055a0833284a6f939731
4
- data.tar.gz: f38c7fa39cb0f1f6fc78cbe72956c77fe15fe74fe99507a3bd59d057618aed0a
3
+ metadata.gz: e017d6c1dff36a5f33a74676c9ff3d9d069ad2210bc8e8049b26ddff56d8961b
4
+ data.tar.gz: 76ba1cc8e225478f4679ed0b801c69387f5c350b57fda365a6316f42c0800bba
5
5
  SHA512:
6
- metadata.gz: 7bf9631eb8941e2ed07ec265519ce8860f9d417cb224b2bbd1bbb8b32277374d76936be126c7893f73bcbcaa1414a25d4ca2280c9e0e701462cdbf5d34c3c77f
7
- data.tar.gz: e517b02e7854fc644763932d8935dd5b145e2c7c4546874b419932a1d0f0d53b523cba356949f5b941e1fe6405c5b308cade8c42ba3d02078a8e7abe5df176a6
6
+ metadata.gz: c22fb29374df4cde3de294f1fd7f7e7b529b7a67d69522f5779ff2cb1ec83f8ea8acfdc6eb0750bf9246094989df59b586a1bb96ff3c7c6227aa51be49d8e121
7
+ data.tar.gz: 2d3988eebde86cc80dbcd8079941e00cd3edc4263a105590d608cb37622284cb96bf9cff923b8514d31fd8b4cddaaafdbab270f077d49f0326960c6645577f3b
data/CHANGELOG.md CHANGED
@@ -6,6 +6,68 @@ This project adheres to [Semantic Versioning 2.0.0][1].
6
6
 
7
7
  This document is formatted based on [Keep A CHANGELOG][2].
8
8
 
9
+ ## 1.12.0 / 2025-10-25
10
+
11
+ * Target compatibility with `ruby_parser` 3.21 ([#256] by [mvz])
12
+ * Support Ruby 3.2 through 3.4, dropping support for Ruby 3.0 and 3.1
13
+ ([#255] and [#260] by [mvz])
14
+
15
+ [#255]: https://github.com/mvz/ripper_ruby_parser/pull/255
16
+ [#256]: https://github.com/mvz/ripper_ruby_parser/pull/256
17
+ [#260]: https://github.com/mvz/ripper_ruby_parser/pull/260
18
+
19
+ ## 1.11.0 / 2024-01-05
20
+
21
+ * Support Ruby 3.0 through 3.3, dropping support for Ruby 2.6 and 2.7
22
+ ([#218], [#219], [#233] and [#246] by [mvz])
23
+ * Target compatibility with `ruby_parser` version 3.20.2
24
+ ([#199], [#216] and [#230] by [mvz])
25
+ * Support single-line pattern matching ([#188] by [mvz])
26
+ * Support rightward assignment ([#189] by [mvz])
27
+ * Support endless methods with Ruby 3.0 syntax ([#191] by [mvz])
28
+ * Support arguments without parentheses in endless method body ([#193] by [mvz])
29
+ * Add support for pattern matching ([#172] by [mvz])
30
+ * Add tentative support for numbered parameters ([#163] by [mvz])
31
+ * Support several new Ruby 3.1 syntax features ([#196] by [mvz])
32
+ * Support negative rational and imaginary literals ([#197] by [mvz])
33
+ * Correctly handle match operator with regexp literals in parentheses
34
+ ([#200] by [mvz])
35
+ * Improve operator assignment handling ([#201] by [mvz])
36
+ * Handle literal Ctrl-? (DEL) character correctly ([#202] by [mvz])
37
+ * Handle method names that are keywords in method definitions ([#204] by [mvz])
38
+ * Improve dsym handling compatibility ([#203], [#210] by [mvz])
39
+ * Add support for Ruby 3.2's splat and kwsplat argument forwarding ([#231] by [mvz])
40
+ * Improve handling of the case .. in construction ([#234] by [mvz])
41
+
42
+ [mvz]: https://github.com/mvz
43
+
44
+ [#163]: https://github.com/mvz/ripper_ruby_parser/pull/163
45
+ [#172]: https://github.com/mvz/ripper_ruby_parser/pull/172
46
+ [#188]: https://github.com/mvz/ripper_ruby_parser/pull/188
47
+ [#189]: https://github.com/mvz/ripper_ruby_parser/pull/189
48
+ [#191]: https://github.com/mvz/ripper_ruby_parser/pull/191
49
+ [#193]: https://github.com/mvz/ripper_ruby_parser/pull/193
50
+ [#196]: https://github.com/mvz/ripper_ruby_parser/pull/196
51
+ [#197]: https://github.com/mvz/ripper_ruby_parser/pull/197
52
+ [#199]: https://github.com/mvz/ripper_ruby_parser/pull/199
53
+ [#200]: https://github.com/mvz/ripper_ruby_parser/pull/200
54
+ [#201]: https://github.com/mvz/ripper_ruby_parser/pull/201
55
+ [#202]: https://github.com/mvz/ripper_ruby_parser/pull/202
56
+ [#203]: https://github.com/mvz/ripper_ruby_parser/pull/203
57
+ [#204]: https://github.com/mvz/ripper_ruby_parser/pull/204
58
+ [#205]: https://github.com/mvz/ripper_ruby_parser/pull/205
59
+ [#210]: https://github.com/mvz/ripper_ruby_parser/pull/210
60
+ [#216]: https://github.com/mvz/ripper_ruby_parser/pull/216
61
+ [#218]: https://github.com/mvz/ripper_ruby_parser/pull/218
62
+ [#219]: https://github.com/mvz/ripper_ruby_parser/pull/219
63
+ [#230]: https://github.com/mvz/ripper_ruby_parser/pull/230
64
+ [#231]: https://github.com/mvz/ripper_ruby_parser/pull/231
65
+ [#232]: https://github.com/mvz/ripper_ruby_parser/pull/232
66
+ [#233]: https://github.com/mvz/ripper_ruby_parser/pull/233
67
+ [#234]: https://github.com/mvz/ripper_ruby_parser/pull/234
68
+ [#235]: https://github.com/mvz/ripper_ruby_parser/pull/235
69
+ [#246]: https://github.com/mvz/ripper_ruby_parser/pull/246
70
+
9
71
  ## 1.10.0 / 2022-03-13
10
72
 
11
73
  * Handle shadow arguments ([#161])
@@ -233,6 +295,8 @@ This document is formatted based on [Keep A CHANGELOG][2].
233
295
 
234
296
  <!-- Pull request links -->
235
297
  [#180]: https://github.com/mvz/ripper_ruby_parser/pull/180
298
+ [#172]: https://github.com/mvz/ripper_ruby_parser/pull/172
299
+ [#163]: https://github.com/mvz/ripper_ruby_parser/pull/163
236
300
  [#165]: https://github.com/mvz/ripper_ruby_parser/pull/165
237
301
  [#161]: https://github.com/mvz/ripper_ruby_parser/pull/161
238
302
  [#155]: https://github.com/mvz/ripper_ruby_parser/pull/155
data/README.md CHANGED
@@ -10,8 +10,8 @@ Parse with Ripper, produce sexps that are compatible with RubyParser.
10
10
 
11
11
  * Drop-in replacement for RubyParser
12
12
  * Should handle 1.9 and later syntax gracefully
13
- * Requires Ruby 2.6 or higher
14
- * Compatible with RubyParser 3.18.0
13
+ * Requires Ruby 3.2 or higher
14
+ * Compatible with RubyParser 3.21.0
15
15
 
16
16
  ## Known incompatibilities
17
17
 
@@ -22,10 +22,14 @@ RipperRubyParser has a few incompatibilities with RubyParser.
22
22
  * RipperRubyParser does not always match RubyParser's line numbering
23
23
  * RipperRubyParser dedents auto-dedenting heredocs
24
24
  * RipperRubyParser does not include postfix comments
25
+ * With Ruby 3.4, RipperRubyParser parses variables assigned by regular
26
+ expressions as local variables
25
27
 
26
28
  ## Install
27
29
 
28
- gem install ripper_ruby_parser
30
+ ```bash
31
+ gem install ripper_ruby_parser
32
+ ```
29
33
 
30
34
  ## Synopsis
31
35
 
@@ -44,7 +48,7 @@ parser.parse "foo[bar] += baz qux"
44
48
 
45
49
  ## Requirements
46
50
 
47
- * Ruby 2.5 or higher
51
+ * Ruby 3.2 or higher
48
52
  * `sexp_processor`
49
53
 
50
54
  ## Hacking and contributing
@@ -64,7 +68,7 @@ If you want to send pull requests or patches, please:
64
68
 
65
69
  (The MIT License)
66
70
 
67
- Copyright (c) 2012, 2014-2022 Matijs van Zuijlen
71
+ Copyright (c) 2012, 2014-2025 Matijs van Zuijlen
68
72
 
69
73
  Permission is hereby granted, free of charge, to any person obtaining
70
74
  a copy of this software and associated documentation files (the
@@ -8,6 +8,7 @@ module RipperRubyParser
8
8
  # Variant of Ripper's SexpBuilder parser class that inserts comments as
9
9
  # Sexps into the built parse tree.
10
10
  #
11
+ # rubocop: disable Metrics/ClassLength
11
12
  # @api private
12
13
  class CommentingRipperParser < Ripper::SexpBuilder
13
14
  def initialize(*args)
@@ -35,7 +36,16 @@ module RipperRubyParser
35
36
  end
36
37
 
37
38
  def on_begin(*args)
38
- commentize(:begin, super)
39
+ result = super
40
+
41
+ # Some begin blocks are not created by the 'begin' keyword. Skip
42
+ # commenting for those kinds of blocks.
43
+ (_, kw,), = @comment_stack.last
44
+ if kw == "begin"
45
+ commentize("begin", result)
46
+ else
47
+ result
48
+ end
39
49
  end
40
50
 
41
51
  def on_void_stmt
@@ -60,35 +70,39 @@ module RipperRubyParser
60
70
  end
61
71
 
62
72
  def on_kw(tok)
63
- result = super
64
- case tok
65
- when "class", "def", "module", "BEGIN", "begin", "END"
66
- unless @in_symbol
73
+ super.tap do |result|
74
+ next if @in_symbol
75
+
76
+ case tok
77
+ when "class", "def", "module", "BEGIN", "begin", "END"
67
78
  @comment_stack.push [result, @comment]
68
79
  @comment = ""
80
+ when "end"
81
+ @comment = "" if @comment_stack.any?
69
82
  end
70
83
  end
71
- result
72
84
  end
73
85
 
74
86
  def on_module(*args)
75
- commentize(:module, super)
87
+ commentize("module", super)
76
88
  end
77
89
 
78
90
  def on_class(*args)
79
- commentize(:class, super)
91
+ commentize("class", super)
80
92
  end
81
93
 
82
94
  def on_sclass(*args)
83
- commentize(:class, super)
95
+ commentize("class", super)
84
96
  end
85
97
 
86
- def on_def(*args)
87
- commentize(:def, super)
98
+ def on_def(name, *args)
99
+ (_, _, loc) = name
100
+ commentize("def", super, loc)
88
101
  end
89
102
 
90
- def on_defs(*args)
91
- commentize(:def, super)
103
+ def on_defs(receiver, period, name, *rest)
104
+ (_, _, loc) = name
105
+ commentize("def", super, loc)
92
106
  end
93
107
 
94
108
  def on_args_new
@@ -209,7 +223,7 @@ module RipperRubyParser
209
223
  end
210
224
 
211
225
  def on_tstring_content(content)
212
- super(content) << @delimiter_stack.last
226
+ super << @delimiter_stack.last
213
227
  end
214
228
 
215
229
  def on_tstring_end(delimiter)
@@ -254,6 +268,11 @@ module RipperRubyParser
254
268
  @seen_space = true
255
269
  end
256
270
 
271
+ def on_imaginary(_token)
272
+ @space_before = @seen_space
273
+ super
274
+ end
275
+
257
276
  def on_int(_token)
258
277
  @space_before = @seen_space
259
278
  super
@@ -264,7 +283,13 @@ module RipperRubyParser
264
283
  super
265
284
  end
266
285
 
267
- NUMBER_LITERAL_TYPES = [:@int, :@float].freeze
286
+ def on_rational(_token)
287
+ @space_before = @seen_space
288
+ super
289
+ end
290
+
291
+ NUMBER_LITERAL_TYPES = [:@imaginary, :@int, :@float, :@rational].freeze
292
+ private_constant :NUMBER_LITERAL_TYPES
268
293
 
269
294
  def on_unary(operator, value)
270
295
  if !@space_before && operator == :-@ && NUMBER_LITERAL_TYPES.include?(value.first)
@@ -299,16 +324,32 @@ module RipperRubyParser
299
324
  super
300
325
  end
301
326
 
327
+ def on_case(*args)
328
+ @comment = ""
329
+ super
330
+ end
331
+
332
+ def on_ident(*args)
333
+ @comment = ""
334
+ super
335
+ end
336
+
337
+ def on_string_content(*args)
338
+ @comment = ""
339
+ super
340
+ end
341
+
302
342
  def on_BEGIN(*args)
303
- commentize(:BEGIN, super)
343
+ commentize("BEGIN", super)
304
344
  end
305
345
 
306
346
  def on_END(*args)
307
- commentize(:END, super)
347
+ commentize("END", super)
308
348
  end
309
349
 
310
350
  def on_parse_error(message)
311
- raise SyntaxError, message
351
+ super
352
+ raise SyntaxError, message if message.start_with?("syntax error,")
312
353
  end
313
354
 
314
355
  def on_class_name_error(message, *)
@@ -327,11 +368,21 @@ module RipperRubyParser
327
368
  raise SyntaxError, message
328
369
  end
329
370
 
330
- def commentize(_name, exp)
331
- (_, _kw, loc), comment = @comment_stack.pop
371
+ def commentize(name, exp, target_loc = nil)
372
+ raise "Non-empty comment in progress: #{@comment}" unless @comment.empty?
373
+
374
+ if target_loc
375
+ (_, kw, loc), comment = @comment_stack.pop until (loc <=> target_loc) == -1
376
+ else
377
+ (_, kw, loc), comment = @comment_stack.pop
378
+ end
379
+
380
+ raise "Comment stack mismatch: expected #{kw} to equal #{name}" unless kw == name
381
+
332
382
  @comment = ""
333
383
  exp.push loc
334
384
  [:comment, comment, exp]
335
385
  end
336
386
  end
387
+ # rubocop: enable Metrics/ClassLength
337
388
  end
@@ -95,50 +95,75 @@ module RipperRubyParser
95
95
  def process_opassign(exp)
96
96
  _, lvalue, (_, operator,), value = exp.shift 4
97
97
 
98
+ value_type = value.sexp_type
99
+
98
100
  lvalue = process(lvalue)
99
101
  value = process(value)
100
102
  operator = operator.chop.to_sym
101
103
 
102
- create_operator_assignment_sub_type lvalue, value, operator
104
+ case lvalue.sexp_type
105
+ when :aref_field
106
+ create_aref_operator_assignment_sub_type(lvalue, value, operator)
107
+ when :field
108
+ create_field_operator_assignment_sub_type(lvalue, value, operator, value_type)
109
+ else
110
+ create_regular_operator_assignment_sub_type(lvalue, value, operator)
111
+ end
103
112
  end
104
113
 
105
114
  private
106
115
 
107
116
  def create_multiple_assignment_sub_types(sexp_list)
108
117
  sexp_list.map! do |item|
109
- create_valueless_assignment_sub_type item
118
+ create_valueless_regular_assignment_sub_type item
110
119
  end
111
120
  end
112
121
 
113
- def create_valueless_assignment_sub_type(item)
122
+ def create_valueless_regular_assignment_sub_type(item)
114
123
  item = with_line_number(item.line,
115
124
  create_regular_assignment_sub_type(item, nil))
116
125
  item.pop
117
126
  item
118
127
  end
119
128
 
129
+ def create_valueless_assignment_sub_type(item)
130
+ item = with_line_number(item.line,
131
+ create_assignment_sub_type(item, nil))
132
+ item.pop
133
+ item
134
+ end
135
+
136
+ def create_aref_operator_assignment_sub_type(lvalue, value, operator)
137
+ _, arr, arglist = lvalue
138
+ arglist.sexp_type = :arglist
139
+ s(:op_asgn1, arr, arglist, operator, value)
140
+ end
141
+
142
+ def create_field_operator_assignment_sub_type(lvalue, value, operator, value_type)
143
+ # Structure of lvalue will be something like this:
144
+ # s(:field, receiver, s(:period, "."), s(:lvar, field))
145
+ _, receiver, _, (_, field) = lvalue
146
+ case value_type
147
+ when :command, :command_call
148
+ s(:op_asgn, receiver, value, field, operator)
149
+ else
150
+ s(:op_asgn2, receiver, :"#{field}=", operator, value)
151
+ end
152
+ end
153
+
120
154
  OPERATOR_ASSIGNMENT_MAP = {
121
155
  "||": :op_asgn_or,
122
156
  "&&": :op_asgn_and
123
157
  }.freeze
158
+ private_constant :OPERATOR_ASSIGNMENT_MAP
124
159
 
125
- def create_operator_assignment_sub_type(lvalue, value, operator)
126
- case lvalue.sexp_type
127
- when :aref_field
128
- _, arr, arglist = lvalue
129
- arglist.sexp_type = :arglist
130
- s(:op_asgn1, arr, arglist, operator, value)
131
- when :field
132
- _, obj, _, (_, field) = lvalue
133
- s(:op_asgn2, obj, :"#{field}=", operator, value)
160
+ def create_regular_operator_assignment_sub_type(lvalue, value, operator)
161
+ value = unwrap_begin(value)
162
+ if (mapped = OPERATOR_ASSIGNMENT_MAP[operator])
163
+ s(mapped, lvalue, create_assignment_sub_type(lvalue, value))
134
164
  else
135
- value = unwrap_begin(value)
136
- if (mapped = OPERATOR_ASSIGNMENT_MAP[operator])
137
- s(mapped, lvalue, create_assignment_sub_type(lvalue, value))
138
- else
139
- operator_call = s(:call, lvalue, operator, value)
140
- create_assignment_sub_type lvalue, operator_call
141
- end
165
+ operator_call = s(:call, lvalue, operator, value)
166
+ create_assignment_sub_type lvalue, operator_call
142
167
  end
143
168
  end
144
169
 
@@ -174,6 +199,8 @@ module RipperRubyParser
174
199
  cvar: :cvasgn
175
200
  }.freeze
176
201
 
202
+ private_constant :ASSIGNMENT_SUB_TYPE_MAP, :ASSIGNMENT_IN_METHOD_SUB_TYPE_MAP
203
+
177
204
  def create_assignment_sub_type(lvalue, value)
178
205
  lvalue_type, lvalue_value = lvalue
179
206
  s(map_assignment_lvalue_type(lvalue_type), lvalue_value, value)
@@ -10,7 +10,7 @@ module RipperRubyParser
10
10
  call = process(call)
11
11
  args = process(args)
12
12
  kwrest = kwrest_param(args) if args
13
- stmt = with_kwrest(kwrest) { process(stmt) }
13
+ stmt = with_new_lvar_scope(kwrest) { process(stmt) }
14
14
  make_iter call, args, safe_unwrap_void_stmt(stmt)
15
15
  end
16
16
 
@@ -153,16 +153,13 @@ module RipperRubyParser
153
153
 
154
154
  defaults.map do |sym, val|
155
155
  s(:lasgn,
156
- extract_node_symbol(process(sym)),
156
+ make_symbol(process(sym)),
157
157
  process(val))
158
158
  end
159
159
  end
160
160
 
161
161
  def handle_splat(splat)
162
- if splat == 0
163
- # Only relevant for Ruby < 2.6
164
- [s(:excessed_comma)]
165
- elsif splat
162
+ if splat
166
163
  [process(splat)]
167
164
  else
168
165
  []
@@ -173,7 +170,7 @@ module RipperRubyParser
173
170
  return [] unless kwargs
174
171
 
175
172
  kwargs.map do |sym, val|
176
- symbol = extract_node_symbol process(sym)
173
+ symbol = make_symbol process(sym)
177
174
  if val
178
175
  s(:kwarg, symbol, process(val))
179
176
  else
@@ -217,15 +214,26 @@ module RipperRubyParser
217
214
  end
218
215
  end
219
216
 
217
+ LVAR_MATCHER = Sexp::Matcher.new(:lvar, Sexp._)
218
+ NUMBERED_PARAMS = (1..9).map { |num| :"_#{num}" }.freeze
219
+ private_constant :LVAR_MATCHER, :NUMBERED_PARAMS
220
+
220
221
  def make_iter(call, args, stmt)
221
222
  args[-1] = nil if args && args.last == s(:excessed_comma)
222
- args ||= 0
223
+
224
+ args ||= count_numbered_lvars(stmt)
225
+
223
226
  if stmt.empty?
224
227
  s(:iter, call, args)
225
228
  else
226
229
  s(:iter, call, args, stmt)
227
230
  end
228
231
  end
232
+
233
+ def count_numbered_lvars(stmt)
234
+ lvar_names = (LVAR_MATCHER / stmt).map { |match| match[1] }
235
+ (NUMBERED_PARAMS & lvar_names).length
236
+ end
229
237
  end
230
238
  end
231
239
  end
@@ -64,11 +64,78 @@ module RipperRubyParser
64
64
  *falsepart)
65
65
  end
66
66
 
67
+ def process_in(exp)
68
+ _, pattern, truepart, falsepart = exp.shift 4
69
+
70
+ falsepart = process(falsepart)
71
+ falsepart = if falsepart.nil?
72
+ [nil]
73
+ else
74
+ falsepart.sexp_body
75
+ end
76
+ pattern = process(pattern)
77
+ adjust_rightward_assignment_pattern(pattern)
78
+
79
+ truepart = process(truepart)
80
+ truepart = unwrap_nil(truepart) if truepart
81
+
82
+ s(:case_body,
83
+ s(:in, pattern, truepart),
84
+ *falsepart)
85
+ end
86
+
67
87
  def process_else(exp)
68
88
  _, body = exp.shift 2
69
89
  process(body)
70
90
  end
71
91
 
92
+ def process_aryptn(exp)
93
+ _, _, body, rest, = exp.shift 5
94
+
95
+ elements = body.map do |elem|
96
+ if elem.sexp_type == :var_field
97
+ create_valueless_assignment_sub_type process(elem)
98
+ else
99
+ unwrap_begin process(elem)
100
+ end
101
+ end
102
+ if rest
103
+ rest_var = handle_pattern(rest)
104
+ elements << convert_marked_argument(s(:splat, rest_var))
105
+ end
106
+ s(:array_pat, nil, *elements)
107
+ end
108
+
109
+ def process_hshptn(exp)
110
+ _, _, body, = exp.shift 4
111
+
112
+ elements = body.flat_map do |key, value|
113
+ if value
114
+ [process(key), process(value)]
115
+ else
116
+ [handle_pattern(key), nil]
117
+ end
118
+ end
119
+ s(:hash_pat, nil, *elements)
120
+ end
121
+
122
+ def process_fndptn(exp)
123
+ _, wrapper, before, patterns, after = exp.shift 5
124
+
125
+ wrapper = process(wrapper)
126
+ before = make_splat process(before)
127
+ after = make_splat process(after)
128
+ patterns = patterns.map do |elem|
129
+ if elem.sexp_type == :var_field
130
+ create_valueless_assignment_sub_type process(elem)
131
+ else
132
+ unwrap_begin process(elem)
133
+ end
134
+ end
135
+
136
+ s(:find_pat, wrapper, before, *patterns, after)
137
+ end
138
+
72
139
  private
73
140
 
74
141
  def handle_condition(cond)
@@ -88,6 +155,24 @@ module RipperRubyParser
88
155
  unwrap_nil process(exp) if exp
89
156
  end
90
157
 
158
+ def handle_pattern(exp)
159
+ pattern = process(exp)
160
+ case pattern.sexp_type
161
+ when :lvar, :lit
162
+ @local_variables << pattern[1]
163
+ end
164
+ pattern
165
+ end
166
+
167
+ def adjust_rightward_assignment_pattern(exp)
168
+ case exp.sexp_type
169
+ when :lvar
170
+ exp.sexp_type = :lasgn
171
+ when :lasgn
172
+ adjust_rightward_assignment_pattern(exp.sexp_body.last)
173
+ end
174
+ end
175
+
91
176
  def construct_conditional(cond, truepart, falsepart)
92
177
  if cond.sexp_type == :not
93
178
  _, inner = cond
@@ -107,6 +192,10 @@ module RipperRubyParser
107
192
  [exp]
108
193
  end
109
194
  end
195
+
196
+ def make_splat(exp)
197
+ :"*#{exp[1]}"
198
+ end
110
199
  end
111
200
  end
112
201
  end
@@ -4,12 +4,17 @@ module RipperRubyParser
4
4
  module SexpHandlers
5
5
  # Utility methods used in several of the sexp handler modules
6
6
  module HelperMethods
7
+ def extract_node_symbol(exp)
8
+ ident, = extract_node_symbol_with_position(exp)
9
+ ident
10
+ end
11
+
7
12
  def extract_node_symbol_with_position(exp)
8
13
  _, ident, pos = exp.shift 3
9
14
  return ident.to_sym, pos
10
15
  end
11
16
 
12
- def extract_node_symbol(exp)
17
+ def make_symbol(exp)
13
18
  return nil if exp.nil?
14
19
  raise "Unexpected number of children: #{exp.length}" if exp.length != 2
15
20
 
@@ -36,7 +41,11 @@ module RipperRubyParser
36
41
  def generic_add_star(exp)
37
42
  _, args, splatarg, *rest = shift_all exp
38
43
  items = process args
39
- items.push s(:splat, unwrap_begin(process(splatarg)))
44
+ if splatarg
45
+ items.push s(:splat, unwrap_begin(process(splatarg)))
46
+ else
47
+ items.push s(:splat)
48
+ end
40
49
  items.push(*map_process_list(rest))
41
50
  end
42
51
 
@@ -36,7 +36,11 @@ module RipperRubyParser
36
36
  # s(:assoc_splat, s(:vcall, s(:@ident, "bar")))
37
37
  def process_assoc_splat(exp)
38
38
  _, param = exp.shift 2
39
- s(:kwsplat, process(param))
39
+ if param
40
+ s(:kwsplat, process(param))
41
+ else
42
+ s(:kwsplat)
43
+ end
40
44
  end
41
45
 
42
46
  # number literals
@@ -7,12 +7,12 @@ module RipperRubyParser
7
7
  def process_def(exp)
8
8
  _, ident, params, body, pos = exp.shift 5
9
9
 
10
- ident, = extract_node_symbol_with_position ident
10
+ ident = extract_node_symbol ident
11
11
 
12
12
  in_method do
13
13
  params = convert_arguments(process(params))
14
14
  kwrest = kwrest_param(params)
15
- body = with_kwrest(kwrest) { method_body(body) }
15
+ body = with_new_lvar_scope(kwrest) { method_body(body) }
16
16
  end
17
17
 
18
18
  with_position(pos, s(:defn, ident, params, *body))
@@ -21,12 +21,12 @@ module RipperRubyParser
21
21
  def process_defs(exp)
22
22
  _, receiver, _, ident, params, body, = exp.shift 7
23
23
 
24
- ident, = extract_node_symbol_with_position ident
24
+ ident = extract_node_symbol ident
25
25
 
26
26
  in_method do
27
27
  params = convert_arguments(process(params))
28
28
  kwrest = kwrest_param(params)
29
- body = with_kwrest(kwrest) { method_body(body) }
29
+ body = with_new_lvar_scope(kwrest) { method_body(body) }
30
30
  end
31
31
 
32
32
  s(:defs, process(receiver), ident, params, *body)
@@ -101,8 +101,11 @@ module RipperRubyParser
101
101
  dsplat: "**",
102
102
  blockarg: "&"
103
103
  }.freeze
104
+ private_constant :SPECIAL_ARG_MARKER
104
105
 
105
106
  def convert_arguments(args)
107
+ return s(:args) if args.nil?
108
+
106
109
  args.line ||= args.sexp_body.first&.line
107
110
  args.sexp_body = args.sexp_body.map { |item| convert_argument item }
108
111
  args
@@ -123,7 +126,7 @@ module RipperRubyParser
123
126
 
124
127
  def convert_marked_argument(item)
125
128
  marker = SPECIAL_ARG_MARKER[item.sexp_type]
126
- name = extract_node_symbol item.last
129
+ name = make_symbol item.last
127
130
  :"#{marker}#{name}"
128
131
  end
129
132
 
@@ -151,15 +154,16 @@ module RipperRubyParser
151
154
  Regexp.last_match[1].to_sym if found
152
155
  end
153
156
 
154
- def with_kwrest(kwrest)
155
- @kwrest.push kwrest
157
+ def with_new_lvar_scope(extra_variable)
158
+ old_lvars = @local_variables.dup
159
+ @local_variables.push extra_variable if extra_variable
156
160
  result = yield
157
- @kwrest.pop
161
+ @local_variables = old_lvars
158
162
  result
159
163
  end
160
164
 
161
165
  def kwrest_arg?(method)
162
- @kwrest.include?(method)
166
+ @local_variables.include?(method)
163
167
  end
164
168
  end
165
169
  end
@@ -11,27 +11,28 @@ module RipperRubyParser
11
11
  or: :or
12
12
  }.freeze
13
13
 
14
+ BINARY_LOGICAL_OPERATORS = BINARY_OPERATOR_MAP.keys.freeze
15
+
14
16
  UNARY_OPERATOR_MAP = {
15
17
  not: :!
16
18
  }.freeze
17
19
 
18
- NEGATED_BINARY_OPERATOR_MAP = {
19
- "!~": :=~
20
- }.freeze
21
-
22
20
  SHIFT_OPERATORS = [:<<, :>>].freeze
23
21
 
24
22
  def process_binary(exp)
25
23
  _, left, op, right = exp.shift 4
26
24
 
27
- if op == :=~
25
+ case op
26
+ when :=~
28
27
  make_regexp_match_operator(left, op, right)
29
- elsif (mapped = NEGATED_BINARY_OPERATOR_MAP[op])
30
- s(:not, make_regexp_match_operator(left, mapped, right))
31
- elsif (mapped = BINARY_OPERATOR_MAP[op])
32
- make_boolean_operator(left, mapped, right)
33
- elsif SHIFT_OPERATORS.include? op
28
+ when :!~
29
+ s(:not, make_regexp_match_operator(left, :=~, right))
30
+ when *BINARY_LOGICAL_OPERATORS
31
+ make_boolean_operator(left, op, right)
32
+ when *SHIFT_OPERATORS
34
33
  s(:call, unwrap_begin(process(left)), op, unwrap_begin(process(right)))
34
+ when :"=>"
35
+ make_rightward_assignment(left, right)
35
36
  else
36
37
  s(:call, process(left), op, process(right))
37
38
  end
@@ -77,20 +78,33 @@ module RipperRubyParser
77
78
  private
78
79
 
79
80
  def make_boolean_operator(left, operator, right)
81
+ operator = BINARY_OPERATOR_MAP[operator]
80
82
  _, left, _, right = rebalance_binary(left, operator, right)
81
83
  s(operator, unwrap_begin(process(left)), process(right))
82
84
  end
83
85
 
84
86
  def make_regexp_match_operator(left, operator, right)
85
- if left.sexp_type == :regexp_literal
86
- s(:match2, process(left), process(right))
87
- elsif right.sexp_type == :regexp_literal
88
- s(:match3, process(right), process(left))
87
+ left = process(left)
88
+ right = process(right)
89
+
90
+ if regexp? left
91
+ s(:match2, left, right)
92
+ elsif regexp? right
93
+ s(:match3, right, left)
89
94
  else
90
- s(:call, process(left), operator, process(right))
95
+ s(:call, left, operator, right)
91
96
  end
92
97
  end
93
98
 
99
+ def regexp?(exp)
100
+ exp.sexp_type == :dregx ||
101
+ exp.sexp_type == :lit && exp.sexp_body.first.is_a?(Regexp)
102
+ end
103
+
104
+ def make_rightward_assignment(left, right)
105
+ s(:lasgn, process(right)[1], process(left))
106
+ end
107
+
94
108
  def rebalance_binary(left, operator, right)
95
109
  if BINARY_OPERATOR_MAP[operator] == BINARY_OPERATOR_MAP[left[2]]
96
110
  _, left, _, middle = rebalance_binary(*left.sexp_body)
@@ -83,7 +83,7 @@ module RipperRubyParser
83
83
  return with_line_number(content.line, s(:lit, Regexp.new(content.last, numflags)))
84
84
  end
85
85
 
86
- content.sexp_type = :dregx_once if /o/.match?(flags)
86
+ content.sexp_type = :dregx_once if flags.include?("o")
87
87
  content << numflags unless numflags == 0
88
88
  content
89
89
  end
@@ -123,7 +123,7 @@ module RipperRubyParser
123
123
 
124
124
  def process_at_tstring_content(exp)
125
125
  _, content, pos, delim = exp.shift 4
126
- string = fix_encoding handle_string_unescaping(content, delim)
126
+ string = handle_string_unescaping(content, delim)
127
127
  with_position(pos, s(:str, string))
128
128
  end
129
129
 
@@ -139,11 +139,11 @@ module RipperRubyParser
139
139
  end
140
140
 
141
141
  def merge_raw_string_literals(list)
142
- chunks = list.chunk { |it| it.sexp_type == :@tstring_content }
142
+ chunks = list.chunk { |lit| lit.sexp_type == :@tstring_content }
143
143
  chunks.flat_map do |is_simple, items|
144
144
  if is_simple && items.count > 1
145
145
  head = items.first
146
- contents = items.map { |it| it[1] }.join
146
+ contents = items.map { |lit| lit[1] }.join
147
147
  [s(:@tstring_content, contents, head[2], head[3])]
148
148
  else
149
149
  items
@@ -180,11 +180,11 @@ module RipperRubyParser
180
180
  def character_flags_to_numerical(flags)
181
181
  numflags = 0
182
182
 
183
- numflags = Regexp::MULTILINE if /m/.match?(flags)
184
- numflags |= Regexp::EXTENDED if /x/.match?(flags)
185
- numflags |= Regexp::IGNORECASE if /i/.match?(flags)
183
+ numflags = Regexp::MULTILINE if flags.include?("m")
184
+ numflags |= Regexp::EXTENDED if flags.include?("x")
185
+ numflags |= Regexp::IGNORECASE if flags.include?("i")
186
186
 
187
- numflags |= Regexp::NOENCODING if /n/.match?(flags)
187
+ numflags |= Regexp::NOENCODING if flags.include?("n")
188
188
  numflags |= Regexp::FIXEDENCODING if /[ues]/.match?(flags)
189
189
 
190
190
  numflags
@@ -227,22 +227,29 @@ module RipperRubyParser
227
227
  end
228
228
  end
229
229
 
230
- INTERPOLATING_HEREDOC = /^<<[-~]?[^-~']/.freeze
231
- NON_INTERPOLATING_HEREDOC = /^<<[-~]?'/.freeze
232
- INTERPOLATING_STRINGS = ['"', "`", ':"', /^%Q.$/, /^%.$/].freeze
230
+ INTERPOLATING_HEREDOC = /^<<[-~]?[^-~']/
231
+ NON_INTERPOLATING_HEREDOC = /^<<[-~]?'/
232
+ INTERPOLATING_STRINGS = ['"', "`", /^%Q.$/, /^%.$/].freeze
233
+ INTERPOLATING_DSYM = ':"'
233
234
  NON_INTERPOLATING_STRINGS = ["'", ":'", /^%q.$/].freeze
234
- INTERPOLATING_WORD_LIST = /^%[WI].$/.freeze
235
- NON_INTERPOLATING_WORD_LIST = /^%[wi].$/.freeze
235
+ INTERPOLATING_WORD_LIST = /^%[WI].$/
236
+ NON_INTERPOLATING_WORD_LIST = /^%[wi].$/
236
237
  REGEXP_LITERALS = ["/", /^%r.$/].freeze
237
238
 
239
+ private_constant :INTERPOLATING_HEREDOC, :NON_INTERPOLATING_HEREDOC,
240
+ :INTERPOLATING_STRINGS, :NON_INTERPOLATING_STRINGS,
241
+ :INTERPOLATING_DSYM,
242
+ :INTERPOLATING_WORD_LIST, :NON_INTERPOLATING_WORD_LIST,
243
+ :REGEXP_LITERALS
244
+
238
245
  def handle_string_unescaping(content, delim)
239
246
  case delim
240
- when INTERPOLATING_HEREDOC, *INTERPOLATING_STRINGS
247
+ when INTERPOLATING_HEREDOC, INTERPOLATING_DSYM, *INTERPOLATING_STRINGS
241
248
  unescape(content)
242
249
  when INTERPOLATING_WORD_LIST
243
- unescape_wordlist_word(content)
250
+ fix_encoding unescape_wordlist_word(content)
244
251
  when *NON_INTERPOLATING_STRINGS
245
- simple_unescape(content, delim)
252
+ fix_encoding simple_unescape(content, delim)
246
253
  when *REGEXP_LITERALS
247
254
  unescape_regexp(content)
248
255
  when NON_INTERPOLATING_WORD_LIST
@@ -17,9 +17,7 @@ module RipperRubyParser
17
17
  super()
18
18
 
19
19
  public_methods.each do |name|
20
- if name =~ /^process_at_(.*)/
21
- @processors["@#{Regexp.last_match(1)}".to_sym] = name.to_sym
22
- end
20
+ @processors[:"@#{Regexp.last_match(1)}"] = name.to_sym if name =~ /^process_at_(.*)/
23
21
  end
24
22
 
25
23
  @filename = filename
@@ -28,10 +26,7 @@ module RipperRubyParser
28
26
  @errors = []
29
27
 
30
28
  @in_method_body = false
31
- @kwrest = []
32
- @block_kwrest = []
33
-
34
- @kept_comment = nil
29
+ @local_variables = []
35
30
  end
36
31
 
37
32
  include SexpHandlers
@@ -77,7 +72,7 @@ module RipperRubyParser
77
72
 
78
73
  def process_var_field(exp)
79
74
  _, contents = exp.shift 2
80
- process(contents)
75
+ process(contents) || s(:lvar, nil)
81
76
  end
82
77
 
83
78
  def process_var_alias(exp)
@@ -92,7 +87,7 @@ module RipperRubyParser
92
87
 
93
88
  def process_const_path_ref(exp)
94
89
  _, left, right = exp.shift 3
95
- s(:colon2, process(left), extract_node_symbol(process(right)))
90
+ s(:colon2, process(left), make_symbol(process(right)))
96
91
  end
97
92
 
98
93
  def process_const_path_field(exp)
@@ -106,7 +101,7 @@ module RipperRubyParser
106
101
 
107
102
  def process_top_const_ref(exp)
108
103
  _, ref = exp.shift 2
109
- s(:colon3, extract_node_symbol(process(ref)))
104
+ s(:colon3, make_symbol(process(ref)))
110
105
  end
111
106
 
112
107
  def process_top_const_field(exp)
@@ -121,14 +116,10 @@ module RipperRubyParser
121
116
 
122
117
  def process_comment(exp)
123
118
  _, comment, inner = exp.shift 3
124
- comment = @kept_comment + comment if @kept_comment
125
- @kept_comment = nil
126
119
  sexp = process(inner)
127
120
  case sexp.sexp_type
128
121
  when :defs, :defn, :module, :class, :sclass
129
122
  sexp.comments = comment
130
- else
131
- @kept_comment = comment
132
123
  end
133
124
  sexp
134
125
  end
@@ -20,7 +20,7 @@ module RipperRubyParser
20
20
  M-. | # meta
21
21
  \n | # line break
22
22
  . # other single character
23
- )/x.freeze
23
+ )/x
24
24
 
25
25
  SINGLE_LETTER_ESCAPES = {
26
26
  "a" => "\a",
@@ -74,7 +74,7 @@ module RipperRubyParser
74
74
  def unescape(string)
75
75
  string = string.dup if string.frozen?
76
76
  string.force_encoding("ASCII-8BIT")
77
- string.gsub(ESCAPE_SEQUENCE_REGEXP) do
77
+ result = string.gsub(ESCAPE_SEQUENCE_REGEXP) do
78
78
  bare = Regexp.last_match[1]
79
79
  if bare == "\n"
80
80
  ""
@@ -82,6 +82,7 @@ module RipperRubyParser
82
82
  unescaped_value(bare).force_encoding("ASCII-8BIT")
83
83
  end
84
84
  end
85
+ fix_encoding result
85
86
  end
86
87
 
87
88
  def unescape_wordlist_word(string)
@@ -169,6 +170,9 @@ module RipperRubyParser
169
170
  end
170
171
 
171
172
  def control(val)
173
+ # Special case the \C-? or DEL sequence
174
+ return 127 if val == 63
175
+
172
176
  val & 0b1001_1111
173
177
  end
174
178
 
@@ -179,7 +183,7 @@ module RipperRubyParser
179
183
  def delimiter_regexp_pattern(delimiter)
180
184
  delimiter = delimiter[-1]
181
185
  delimiters = DELIMITER_PAIRS.fetch(delimiter, delimiter)
182
- delimiters.each_char.map { |it| Regexp.escape it }.join(" | ")
186
+ delimiters.each_char.map { |char| Regexp.escape char }.join(" | ")
183
187
  end
184
188
  end
185
189
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RipperRubyParser
4
- VERSION = "1.10.0"
4
+ VERSION = "1.12.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ripper_ruby_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.0
4
+ version: 1.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matijs van Zuijlen
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-03-13 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: sexp_processor
@@ -58,20 +57,6 @@ dependencies:
58
57
  - - ">="
59
58
  - !ruby/object:Gem::Version
60
59
  version: 1.3.1
61
- - !ruby/object:Gem::Dependency
62
- name: pry
63
- requirement: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: 0.14.0
68
- type: :development
69
- prerelease: false
70
- version_requirements: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: 0.14.0
75
60
  - !ruby/object:Gem::Dependency
76
61
  name: rake
77
62
  requirement: !ruby/object:Gem::Requirement
@@ -106,70 +91,70 @@ dependencies:
106
91
  requirements:
107
92
  - - "~>"
108
93
  - !ruby/object:Gem::Version
109
- version: '1.25'
94
+ version: '1.79'
110
95
  type: :development
111
96
  prerelease: false
112
97
  version_requirements: !ruby/object:Gem::Requirement
113
98
  requirements:
114
99
  - - "~>"
115
100
  - !ruby/object:Gem::Version
116
- version: '1.25'
101
+ version: '1.79'
117
102
  - !ruby/object:Gem::Dependency
118
103
  name: rubocop-minitest
119
104
  requirement: !ruby/object:Gem::Requirement
120
105
  requirements:
121
106
  - - "~>"
122
107
  - !ruby/object:Gem::Version
123
- version: 0.17.0
108
+ version: 0.38.0
124
109
  type: :development
125
110
  prerelease: false
126
111
  version_requirements: !ruby/object:Gem::Requirement
127
112
  requirements:
128
113
  - - "~>"
129
114
  - !ruby/object:Gem::Version
130
- version: 0.17.0
115
+ version: 0.38.0
131
116
  - !ruby/object:Gem::Dependency
132
117
  name: rubocop-packaging
133
118
  requirement: !ruby/object:Gem::Requirement
134
119
  requirements:
135
120
  - - "~>"
136
121
  - !ruby/object:Gem::Version
137
- version: 0.5.1
122
+ version: 0.6.0
138
123
  type: :development
139
124
  prerelease: false
140
125
  version_requirements: !ruby/object:Gem::Requirement
141
126
  requirements:
142
127
  - - "~>"
143
128
  - !ruby/object:Gem::Version
144
- version: 0.5.1
129
+ version: 0.6.0
145
130
  - !ruby/object:Gem::Dependency
146
131
  name: rubocop-performance
147
132
  requirement: !ruby/object:Gem::Requirement
148
133
  requirements:
149
134
  - - "~>"
150
135
  - !ruby/object:Gem::Version
151
- version: '1.13'
136
+ version: '1.25'
152
137
  type: :development
153
138
  prerelease: false
154
139
  version_requirements: !ruby/object:Gem::Requirement
155
140
  requirements:
156
141
  - - "~>"
157
142
  - !ruby/object:Gem::Version
158
- version: '1.13'
143
+ version: '1.25'
159
144
  - !ruby/object:Gem::Dependency
160
145
  name: ruby_parser
161
146
  requirement: !ruby/object:Gem::Requirement
162
147
  requirements:
163
148
  - - "~>"
164
149
  - !ruby/object:Gem::Version
165
- version: '3.18'
150
+ version: 3.21.0
166
151
  type: :development
167
152
  prerelease: false
168
153
  version_requirements: !ruby/object:Gem::Requirement
169
154
  requirements:
170
155
  - - "~>"
171
156
  - !ruby/object:Gem::Version
172
- version: '3.18'
157
+ version: 3.21.0
173
158
  - !ruby/object:Gem::Dependency
174
159
  name: sexp_processor
175
160
  requirement: !ruby/object:Gem::Requirement
@@ -190,14 +175,14 @@ dependencies:
190
175
  requirements:
191
176
  - - "~>"
192
177
  - !ruby/object:Gem::Version
193
- version: 0.21.0
178
+ version: 0.22.0
194
179
  type: :development
195
180
  prerelease: false
196
181
  version_requirements: !ruby/object:Gem::Requirement
197
182
  requirements:
198
183
  - - "~>"
199
184
  - !ruby/object:Gem::Version
200
- version: 0.21.0
185
+ version: 0.22.0
201
186
  description: |
202
187
  RipperRubyParser is a parser for Ruby based on Ripper that aims to be a
203
188
  drop-in replacement for RubyParser.
@@ -236,7 +221,6 @@ metadata:
236
221
  source_code_uri: https://github.com/mvz/ripper_ruby_parser
237
222
  changelog_uri: https://github.com/mvz/ripper_ruby_parser/blob/master/CHANGELOG.md
238
223
  rubygems_mfa_required: 'true'
239
- post_install_message:
240
224
  rdoc_options:
241
225
  - "--main"
242
226
  - README.md
@@ -246,15 +230,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
246
230
  requirements:
247
231
  - - ">="
248
232
  - !ruby/object:Gem::Version
249
- version: 2.6.0
233
+ version: 3.2.0
250
234
  required_rubygems_version: !ruby/object:Gem::Requirement
251
235
  requirements:
252
236
  - - ">="
253
237
  - !ruby/object:Gem::Version
254
238
  version: '0'
255
239
  requirements: []
256
- rubygems_version: 3.2.33
257
- signing_key:
240
+ rubygems_version: 3.7.2
258
241
  specification_version: 4
259
242
  summary: Parse with Ripper, produce sexps that are compatible with RubyParser.
260
243
  test_files: []