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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +86 -0
- data/README.md +8 -25
- data/lib/ripper_ruby_parser.rb +2 -2
- data/lib/ripper_ruby_parser/commenting_ripper_parser.rb +54 -23
- data/lib/ripper_ruby_parser/parser.rb +3 -3
- data/lib/ripper_ruby_parser/sexp_handlers.rb +11 -9
- data/lib/ripper_ruby_parser/sexp_handlers/assignment.rb +10 -11
- data/lib/ripper_ruby_parser/sexp_handlers/blocks.rb +48 -63
- data/lib/ripper_ruby_parser/sexp_handlers/conditionals.rb +17 -19
- data/lib/ripper_ruby_parser/sexp_handlers/helper_methods.rb +35 -2
- data/lib/ripper_ruby_parser/sexp_handlers/literals.rb +15 -242
- data/lib/ripper_ruby_parser/sexp_handlers/loops.rb +4 -2
- data/lib/ripper_ruby_parser/sexp_handlers/method_calls.rb +1 -1
- data/lib/ripper_ruby_parser/sexp_handlers/methods.rb +24 -24
- data/lib/ripper_ruby_parser/sexp_handlers/string_literals.rb +266 -0
- data/lib/ripper_ruby_parser/sexp_processor.rb +47 -78
- data/lib/ripper_ruby_parser/unescape.rb +79 -50
- data/lib/ripper_ruby_parser/version.rb +1 -1
- metadata +115 -78
- data/Rakefile +0 -33
- data/test/end_to_end/comments_test.rb +0 -59
- data/test/end_to_end/comparison_test.rb +0 -104
- data/test/end_to_end/lib_comparison_test.rb +0 -29
- data/test/end_to_end/line_numbering_test.rb +0 -64
- data/test/end_to_end/samples_comparison_test.rb +0 -13
- data/test/end_to_end/test_comparison_test.rb +0 -32
- data/test/pt_testcase/pt_test.rb +0 -44
- data/test/ripper_ruby_parser/commenting_ripper_parser_test.rb +0 -190
- data/test/ripper_ruby_parser/parser_test.rb +0 -469
- data/test/ripper_ruby_parser/sexp_handlers/assignment_test.rb +0 -649
- data/test/ripper_ruby_parser/sexp_handlers/blocks_test.rb +0 -661
- data/test/ripper_ruby_parser/sexp_handlers/conditionals_test.rb +0 -536
- data/test/ripper_ruby_parser/sexp_handlers/literals_test.rb +0 -1117
- data/test/ripper_ruby_parser/sexp_handlers/loops_test.rb +0 -209
- data/test/ripper_ruby_parser/sexp_handlers/method_calls_test.rb +0 -267
- data/test/ripper_ruby_parser/sexp_handlers/methods_test.rb +0 -427
- data/test/ripper_ruby_parser/sexp_handlers/operators_test.rb +0 -399
- data/test/ripper_ruby_parser/sexp_processor_test.rb +0 -303
- data/test/ripper_ruby_parser/version_test.rb +0 -7
- data/test/samples/assignment.rb +0 -17
- data/test/samples/comments.rb +0 -13
- data/test/samples/conditionals.rb +0 -23
- data/test/samples/loops.rb +0 -36
- data/test/samples/misc.rb +0 -278
- data/test/samples/number.rb +0 -7
- data/test/samples/operators.rb +0 -18
- data/test/samples/strings.rb +0 -140
- 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
|
4
|
-
require
|
5
|
-
require
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
60
|
-
const
|
61
|
-
|
62
|
-
|
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
|
67
|
-
const
|
53
|
+
_, const_ref, parent, body, pos = exp.shift 5
|
54
|
+
const = const_ref_to_const const_ref
|
68
55
|
parent = process(parent)
|
69
|
-
|
70
|
-
|
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
|
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 =
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
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.
|
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
|
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
|
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
|
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
|
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
|
-
|
218
|
+
const
|
250
219
|
end
|
251
220
|
|
252
221
|
def class_or_module_body(exp)
|
253
222
|
body = process(exp)
|
254
223
|
|
255
|
-
return
|
224
|
+
return [] if body.sexp_type == :void_stmt
|
256
225
|
|
257
226
|
unwrap_block body
|
258
227
|
end
|