ripper_ruby_parser 1.6.0 → 1.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +86 -0
  3. data/README.md +8 -25
  4. data/lib/ripper_ruby_parser.rb +2 -2
  5. data/lib/ripper_ruby_parser/commenting_ripper_parser.rb +54 -23
  6. data/lib/ripper_ruby_parser/parser.rb +3 -3
  7. data/lib/ripper_ruby_parser/sexp_handlers.rb +11 -9
  8. data/lib/ripper_ruby_parser/sexp_handlers/assignment.rb +10 -11
  9. data/lib/ripper_ruby_parser/sexp_handlers/blocks.rb +48 -63
  10. data/lib/ripper_ruby_parser/sexp_handlers/conditionals.rb +17 -19
  11. data/lib/ripper_ruby_parser/sexp_handlers/helper_methods.rb +35 -2
  12. data/lib/ripper_ruby_parser/sexp_handlers/literals.rb +15 -242
  13. data/lib/ripper_ruby_parser/sexp_handlers/loops.rb +4 -2
  14. data/lib/ripper_ruby_parser/sexp_handlers/method_calls.rb +1 -1
  15. data/lib/ripper_ruby_parser/sexp_handlers/methods.rb +24 -24
  16. data/lib/ripper_ruby_parser/sexp_handlers/string_literals.rb +266 -0
  17. data/lib/ripper_ruby_parser/sexp_processor.rb +47 -78
  18. data/lib/ripper_ruby_parser/unescape.rb +79 -50
  19. data/lib/ripper_ruby_parser/version.rb +1 -1
  20. metadata +115 -78
  21. data/Rakefile +0 -33
  22. data/test/end_to_end/comments_test.rb +0 -59
  23. data/test/end_to_end/comparison_test.rb +0 -104
  24. data/test/end_to_end/lib_comparison_test.rb +0 -29
  25. data/test/end_to_end/line_numbering_test.rb +0 -64
  26. data/test/end_to_end/samples_comparison_test.rb +0 -13
  27. data/test/end_to_end/test_comparison_test.rb +0 -32
  28. data/test/pt_testcase/pt_test.rb +0 -44
  29. data/test/ripper_ruby_parser/commenting_ripper_parser_test.rb +0 -190
  30. data/test/ripper_ruby_parser/parser_test.rb +0 -469
  31. data/test/ripper_ruby_parser/sexp_handlers/assignment_test.rb +0 -649
  32. data/test/ripper_ruby_parser/sexp_handlers/blocks_test.rb +0 -661
  33. data/test/ripper_ruby_parser/sexp_handlers/conditionals_test.rb +0 -536
  34. data/test/ripper_ruby_parser/sexp_handlers/literals_test.rb +0 -1117
  35. data/test/ripper_ruby_parser/sexp_handlers/loops_test.rb +0 -209
  36. data/test/ripper_ruby_parser/sexp_handlers/method_calls_test.rb +0 -267
  37. data/test/ripper_ruby_parser/sexp_handlers/methods_test.rb +0 -427
  38. data/test/ripper_ruby_parser/sexp_handlers/operators_test.rb +0 -399
  39. data/test/ripper_ruby_parser/sexp_processor_test.rb +0 -303
  40. data/test/ripper_ruby_parser/version_test.rb +0 -7
  41. data/test/samples/assignment.rb +0 -17
  42. data/test/samples/comments.rb +0 -13
  43. data/test/samples/conditionals.rb +0 -23
  44. data/test/samples/loops.rb +0 -36
  45. data/test/samples/misc.rb +0 -278
  46. data/test/samples/number.rb +0 -7
  47. data/test/samples/operators.rb +0 -18
  48. data/test/samples/strings.rb +0 -140
  49. data/test/test_helper.rb +0 -79
@@ -0,0 +1,266 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RipperRubyParser
4
+ module SexpHandlers
5
+ # Sexp handlers for string and stringlike literals
6
+ module StringLiterals
7
+ def process_string_literal(exp)
8
+ _, content = exp.shift 2
9
+ process(content)
10
+ end
11
+
12
+ def process_string_content(exp)
13
+ _, *rest = shift_all exp
14
+ line, string, rest = extract_string_parts(rest)
15
+
16
+ if rest.empty?
17
+ with_line_number(line, s(:str, string))
18
+ else
19
+ s(:dstr, string, *rest)
20
+ end
21
+ end
22
+
23
+ alias process_word process_string_content
24
+
25
+ def process_string_embexpr(exp)
26
+ _, list = exp.shift 2
27
+
28
+ val = process(list.sexp_body.first)
29
+
30
+ case val.sexp_type
31
+ when :str, :dstr
32
+ val
33
+ when :void_stmt
34
+ s(:dstr, "", s(:evstr))
35
+ else
36
+ s(:dstr, "", s(:evstr, val))
37
+ end
38
+ end
39
+
40
+ def process_string_dvar(exp)
41
+ _, list = exp.shift 2
42
+ val = process(list)
43
+ s(:dstr, "", s(:evstr, val))
44
+ end
45
+
46
+ def process_string_concat(exp)
47
+ _, left, right = exp.shift 3
48
+
49
+ left = process(left)
50
+ right = process(right)
51
+
52
+ if left.sexp_type == :str
53
+ merge_left_into_right(left, right)
54
+ else
55
+ merge_right_into_left(left, right)
56
+ end
57
+ end
58
+
59
+ def process_xstring_literal(exp)
60
+ _, content = exp.shift 2
61
+ process(content)
62
+ end
63
+
64
+ def process_xstring(exp)
65
+ _, *rest = shift_all exp
66
+ line, string, rest = extract_string_parts(rest)
67
+ result = if rest.empty?
68
+ s(:xstr, string)
69
+ else
70
+ s(:dxstr, string, *rest)
71
+ end
72
+ result.line = line
73
+ result
74
+ end
75
+
76
+ def process_regexp_literal(exp)
77
+ _, content, (_, flags,) = exp.shift 3
78
+
79
+ content = process(content)
80
+ numflags = character_flags_to_numerical flags
81
+
82
+ if content.length == 2
83
+ return with_line_number(content.line, s(:lit, Regexp.new(content.last, numflags)))
84
+ end
85
+
86
+ content.sexp_type = :dregx_once if /o/.match?(flags)
87
+ content << numflags unless numflags == 0
88
+ content
89
+ end
90
+
91
+ def process_regexp(exp)
92
+ _, *rest = shift_all exp
93
+ line, string, rest = extract_string_parts(rest)
94
+ with_line_number(line, s(:dregx, string, *rest))
95
+ end
96
+
97
+ def process_symbol_literal(exp)
98
+ _, symbol = exp.shift 2
99
+ handle_symbol_content(symbol)
100
+ end
101
+
102
+ def process_symbol(exp)
103
+ _, node = exp.shift 2
104
+ handle_symbol_content(node)
105
+ end
106
+
107
+ def process_dyna_symbol(exp)
108
+ _, node = exp.shift 2
109
+ handle_dyna_symbol_content(node)
110
+ end
111
+
112
+ def process_qsymbols(exp)
113
+ _, *items = shift_all(exp)
114
+ items = items.map { |item| handle_symbol_content(item) }
115
+ s(:qsymbols, *items)
116
+ end
117
+
118
+ def process_symbols(exp)
119
+ _, *items = shift_all(exp)
120
+ items = items.map { |item| handle_dyna_symbol_content(item) }
121
+ s(:symbols, *items)
122
+ end
123
+
124
+ def process_at_tstring_content(exp)
125
+ _, content, pos, delim = exp.shift 4
126
+ string = handle_string_unescaping(content, delim)
127
+ string = handle_string_encoding(string, delim)
128
+ with_position(pos, s(:str, string))
129
+ end
130
+
131
+ private
132
+
133
+ def extract_string_parts(list)
134
+ return nil, "", [] if list.empty?
135
+
136
+ list = merge_raw_string_literals list
137
+ list = map_process_list list
138
+ parts = unpack_dstr list
139
+ merge_initial_string_literals(parts)
140
+ end
141
+
142
+ def merge_raw_string_literals(list)
143
+ chunks = list.chunk { |it| it.sexp_type == :@tstring_content }
144
+ chunks.flat_map do |is_simple, items|
145
+ if is_simple && items.count > 1
146
+ head = items.first
147
+ contents = items.map { |it| it[1] }.join
148
+ [s(:@tstring_content, contents, head[2], head[3])]
149
+ else
150
+ items
151
+ end
152
+ end
153
+ end
154
+
155
+ def unpack_dstr(list)
156
+ list.flat_map do |item|
157
+ type, val, *rest = item
158
+ if type == :dstr
159
+ if val.empty?
160
+ rest
161
+ else
162
+ [s(:str, val), *rest]
163
+ end
164
+ else
165
+ [item]
166
+ end
167
+ end
168
+ end
169
+
170
+ def merge_initial_string_literals(parts)
171
+ string = ""
172
+ while parts.first&.sexp_type == :str
173
+ str = parts.shift
174
+ line ||= str.line
175
+ string += str.last
176
+ end
177
+
178
+ return line, string, parts
179
+ end
180
+
181
+ def character_flags_to_numerical(flags)
182
+ numflags = 0
183
+
184
+ numflags = Regexp::MULTILINE if /m/.match?(flags)
185
+ numflags |= Regexp::EXTENDED if /x/.match?(flags)
186
+ numflags |= Regexp::IGNORECASE if /i/.match?(flags)
187
+
188
+ numflags |= Regexp::NOENCODING if /n/.match?(flags)
189
+ numflags |= Regexp::FIXEDENCODING if /[ues]/.match?(flags)
190
+
191
+ numflags
192
+ end
193
+
194
+ def handle_dyna_symbol_content(node)
195
+ type, *body = *process(node)
196
+ case type
197
+ when :str, :xstr
198
+ s(:lit, body.first.to_sym)
199
+ when :dstr, :dxstr
200
+ s(:dsym, *body)
201
+ end
202
+ end
203
+
204
+ def handle_symbol_content(node)
205
+ if node.sexp_type == :@kw
206
+ symbol, position = extract_node_symbol_with_position(node)
207
+ with_position(position, s(:lit, symbol))
208
+ else
209
+ processed = process(node)
210
+ symbol = processed.last.to_sym
211
+ line = processed.line
212
+ with_line_number(line, s(:lit, symbol))
213
+ end
214
+ end
215
+
216
+ def merge_left_into_right(left, right)
217
+ right[1] = left.last + right[1]
218
+ right
219
+ end
220
+
221
+ def merge_right_into_left(left, right)
222
+ if right.sexp_type == :str
223
+ left.push right
224
+ else
225
+ _, first, *rest = right
226
+ left.push s(:str, first) unless first.empty?
227
+ left.push(*rest)
228
+ end
229
+ end
230
+
231
+ INTERPOLATING_HEREDOC = /^<<[-~]?[^-~']/.freeze
232
+ NON_INTERPOLATING_HEREDOC = /^<<[-~]?'/.freeze
233
+ INTERPOLATING_STRINGS = ['"', "`", ':"', /^%Q.$/, /^%.$/].freeze
234
+ NON_INTERPOLATING_STRINGS = ["'", ":'", /^%q.$/].freeze
235
+ INTERPOLATING_WORD_LIST = /^%[WI].$/.freeze
236
+ NON_INTERPOLATING_WORD_LIST = /^%[wi].$/.freeze
237
+ REGEXP_LITERALS = ["/", /^%r.$/].freeze
238
+
239
+ def handle_string_unescaping(content, delim)
240
+ case delim
241
+ when INTERPOLATING_HEREDOC, *INTERPOLATING_STRINGS
242
+ unescape(content)
243
+ when INTERPOLATING_WORD_LIST
244
+ unescape_wordlist_word(content)
245
+ when *NON_INTERPOLATING_STRINGS
246
+ simple_unescape(content)
247
+ when *REGEXP_LITERALS
248
+ unescape_regexp(content)
249
+ when NON_INTERPOLATING_WORD_LIST
250
+ simple_unescape_wordlist_word(content)
251
+ else
252
+ content
253
+ end
254
+ end
255
+
256
+ def handle_string_encoding(string, delim)
257
+ case delim
258
+ when INTERPOLATING_HEREDOC, INTERPOLATING_WORD_LIST, *INTERPOLATING_STRINGS
259
+ fix_encoding string
260
+ else
261
+ string
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'sexp_processor'
4
- require 'ripper_ruby_parser/sexp_handlers'
5
- require 'ripper_ruby_parser/unescape'
3
+ require "sexp_processor"
4
+ require "ripper_ruby_parser/sexp_handlers"
5
+ require "ripper_ruby_parser/unescape"
6
6
 
7
7
  module RipperRubyParser
8
8
  # Processes the sexp created by Ripper to what RubyParser would produce.
@@ -11,31 +11,16 @@ module RipperRubyParser
11
11
  class SexpProcessor < ::SexpProcessor
12
12
  include Unescape
13
13
 
14
- attr_reader :filename
15
- attr_reader :extra_compatible
14
+ attr_reader :filename, :extra_compatible
16
15
 
17
16
  def initialize(filename: nil, extra_compatible: nil)
18
17
  super()
19
18
 
20
- @processors[:@int] = :process_at_int
21
- @processors[:@float] = :process_at_float
22
- @processors[:@rational] = :process_at_rational
23
- @processors[:@CHAR] = :process_at_CHAR
24
- @processors[:@label] = :process_at_label
25
-
26
- @processors[:@const] = :process_at_const
27
- @processors[:@ident] = :process_at_ident
28
- @processors[:@cvar] = :process_at_cvar
29
- @processors[:@gvar] = :process_at_gvar
30
- @processors[:@ivar] = :process_at_ivar
31
- @processors[:@kw] = :process_at_kw
32
- @processors[:@op] = :process_at_op
33
- @processors[:@backref] = :process_at_backref
34
-
35
- @processors[:@backtick] = :process_at_backtick
36
- @processors[:@period] = :process_at_period
37
-
38
- @processors[:@tstring_content] = :process_at_tstring_content
19
+ public_methods.each do |name|
20
+ if name =~ /^process_at_(.*)/
21
+ @processors["@#{Regexp.last_match(1)}".to_sym] = name.to_sym
22
+ end
23
+ end
39
24
 
40
25
  @filename = filename
41
26
  @extra_compatible = extra_compatible
@@ -45,6 +30,8 @@ module RipperRubyParser
45
30
  @in_method_body = false
46
31
  @kwrest = []
47
32
  @block_kwrest = []
33
+
34
+ @kept_comment = nil
48
35
  end
49
36
 
50
37
  include SexpHandlers
@@ -56,37 +43,31 @@ module RipperRubyParser
56
43
  end
57
44
 
58
45
  def process_module(exp)
59
- _, const_ref, body = exp.shift 3
60
- const, line = const_ref_to_const_with_line_number const_ref
61
- with_line_number(line,
62
- s(:module, const, *class_or_module_body(body)))
46
+ _, const_ref, body, pos = exp.shift 4
47
+ const = const_ref_to_const const_ref
48
+ with_position(pos,
49
+ s(:module, const, *class_or_module_body(body)))
63
50
  end
64
51
 
65
52
  def process_class(exp)
66
- _, const_ref, parent, body = exp.shift 4
67
- const, line = const_ref_to_const_with_line_number const_ref
53
+ _, const_ref, parent, body, pos = exp.shift 5
54
+ const = const_ref_to_const const_ref
68
55
  parent = process(parent)
69
- with_line_number(line,
70
- s(:class, const, parent, *class_or_module_body(body)))
56
+ with_position(pos,
57
+ s(:class, const, parent, *class_or_module_body(body)))
71
58
  end
72
59
 
73
60
  def process_sclass(exp)
74
- _, klass, block = exp.shift 3
75
- s(:sclass, process(klass), *class_or_module_body(block))
61
+ _, klass, block, pos = exp.shift 4
62
+ with_position pos, s(:sclass, process(klass), *class_or_module_body(block))
76
63
  end
77
64
 
78
65
  def process_stmts(exp)
79
66
  _, *statements = shift_all(exp)
80
- statements = map_process_list_compact statements
81
- case statements.count
82
- when 0
83
- s(:void_stmt)
84
- when 1
85
- statements.first
86
- else
87
- first = statements.shift
88
- s(:block, *unwrap_block(first), *statements)
89
- end
67
+ statements = map_unwrap_begin_list map_process_list statements
68
+ line = statements.first.line
69
+ statements = reject_void_stmt statements
70
+ wrap_in_block(statements, line)
90
71
  end
91
72
 
92
73
  def process_var_ref(exp)
@@ -104,6 +85,11 @@ module RipperRubyParser
104
85
  s(:valias, left[1].to_sym, right[1].to_sym)
105
86
  end
106
87
 
88
+ def process_void_stmt(exp)
89
+ _, pos = exp.shift 2
90
+ with_position pos, s(:void_stmt)
91
+ end
92
+
107
93
  def process_const_path_ref(exp)
108
94
  _, left, right = exp.shift 3
109
95
  s(:colon2, process(left), extract_node_symbol(process(right)))
@@ -130,49 +116,33 @@ module RipperRubyParser
130
116
  def process_paren(exp)
131
117
  _, body = exp.shift 2
132
118
  result = process body
133
- if result.sexp_type == :void_stmt
134
- s(:nil)
135
- else
136
- result
137
- end
119
+ convert_void_stmt_to_nil_symbol result
138
120
  end
139
121
 
140
122
  def process_comment(exp)
141
123
  _, comment, inner = exp.shift 3
124
+ comment = @kept_comment + comment if @kept_comment
125
+ @kept_comment = nil
142
126
  sexp = process(inner)
143
- sexp.comments = comment
127
+ case sexp.sexp_type
128
+ when :defs, :defn, :module, :class, :sclass
129
+ sexp.comments = comment
130
+ else
131
+ @kept_comment = comment
132
+ end
144
133
  sexp
145
134
  end
146
135
 
147
136
  def process_BEGIN(exp)
148
- _, body = exp.shift 2
137
+ _, body, pos = exp.shift 3
149
138
  body = reject_void_stmt map_process_list body.sexp_body
150
- s(:iter, s(:preexe), 0, *body)
139
+ with_position pos, s(:iter, s(:preexe), 0, *body)
151
140
  end
152
141
 
153
142
  def process_END(exp)
154
- _, body = exp.shift 2
143
+ _, body, pos = exp.shift 3
155
144
  body = map_process_list_compact body.sexp_body
156
- s(:iter, s(:postexe), 0, *body)
157
- end
158
-
159
- # number literals
160
- def process_at_int(exp)
161
- make_literal(exp) { |val| Integer(val) }
162
- end
163
-
164
- def process_at_float(exp)
165
- make_literal(exp, &:to_f)
166
- end
167
-
168
- def process_at_rational(exp)
169
- make_literal(exp, &:to_r)
170
- end
171
-
172
- # character literals
173
- def process_at_CHAR(exp)
174
- _, val, pos = exp.shift 3
175
- with_position(pos, s(:str, unescape(val[1..-1])))
145
+ with_position pos, s(:iter, s(:postexe), 0, *body)
176
146
  end
177
147
 
178
148
  def process_at_label(exp)
@@ -227,7 +197,7 @@ module RipperRubyParser
227
197
  _, str, pos = exp.shift 3
228
198
  name = str[1..-1]
229
199
  with_position pos do
230
- if name =~ /[0-9]/
200
+ if /[0-9]/.match?(name)
231
201
  s(:nth_ref, name.to_i)
232
202
  else
233
203
  s(:back_ref, name.to_sym)
@@ -242,17 +212,16 @@ module RipperRubyParser
242
212
 
243
213
  private
244
214
 
245
- def const_ref_to_const_with_line_number(const_ref)
215
+ def const_ref_to_const(const_ref)
246
216
  const = process(const_ref)
247
- line = const.line
248
217
  const = const[1] if const.sexp_type == :const
249
- return const, line
218
+ const
250
219
  end
251
220
 
252
221
  def class_or_module_body(exp)
253
222
  body = process(exp)
254
223
 
255
- return body if body.empty?
224
+ return [] if body.sexp_type == :void_stmt
256
225
 
257
226
  unwrap_block body
258
227
  end