ripper_ruby_parser 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +2 -7
- data/lib/ripper_ruby_parser/commenting_sexp_builder.rb +22 -0
- data/lib/ripper_ruby_parser/parser.rb +7 -1
- data/lib/ripper_ruby_parser/sexp_handlers/assignment.rb +7 -1
- data/lib/ripper_ruby_parser/sexp_handlers/blocks.rb +37 -3
- data/lib/ripper_ruby_parser/sexp_handlers/conditionals.rb +16 -5
- data/lib/ripper_ruby_parser/sexp_handlers/helper_methods.rb +20 -2
- data/lib/ripper_ruby_parser/sexp_handlers/literals.rb +49 -17
- data/lib/ripper_ruby_parser/sexp_handlers/methods.rb +9 -2
- data/lib/ripper_ruby_parser/sexp_handlers/operators.rb +7 -1
- data/lib/ripper_ruby_parser/sexp_processor.rb +13 -24
- data/lib/ripper_ruby_parser/syntax_error.rb +5 -0
- data/lib/ripper_ruby_parser/version.rb +1 -1
- data/test/end_to_end/comparison_test.rb +14 -1
- data/test/end_to_end/error_conditions_test.rb +51 -0
- data/test/end_to_end/samples_comparison_test.rb +28 -0
- data/test/unit/parser_test.rb +225 -9
- metadata +17 -12
data/README.rdoc
CHANGED
@@ -12,10 +12,7 @@ Parse with Ripper, produce sexps that are compatible with RubyParser.
|
|
12
12
|
|
13
13
|
* Drop-in replacement for RubyParser.
|
14
14
|
* Should handle 1.9 syntax gracefully.
|
15
|
-
*
|
16
|
-
MRI 1.8.
|
17
|
-
* Many edge cases are surely not yet taken care of (but hey, it's version
|
18
|
-
0.0.1).
|
15
|
+
* Needs MRI 1.9.3.
|
19
16
|
|
20
17
|
== Install
|
21
18
|
|
@@ -23,8 +20,6 @@ Parse with Ripper, produce sexps that are compatible with RubyParser.
|
|
23
20
|
|
24
21
|
== Synopsis
|
25
22
|
|
26
|
-
Basic usage:
|
27
|
-
|
28
23
|
require 'ripper_ruby_parser'
|
29
24
|
|
30
25
|
parser = RipperRubyParser::Parser.new
|
@@ -34,7 +29,7 @@ Basic usage:
|
|
34
29
|
|
35
30
|
== Requirements
|
36
31
|
|
37
|
-
* Ruby 1.9.3.
|
32
|
+
* Ruby 1.9.3.
|
38
33
|
* sexp_processor
|
39
34
|
|
40
35
|
== Hacking and contributing
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'ripper'
|
2
|
+
require 'ripper_ruby_parser/syntax_error'
|
3
|
+
|
2
4
|
module RipperRubyParser
|
3
5
|
class CommentingSexpBuilder < Ripper::SexpBuilderPP
|
4
6
|
def initialize *args
|
@@ -34,6 +36,26 @@ module RipperRubyParser
|
|
34
36
|
commentize(super)
|
35
37
|
end
|
36
38
|
|
39
|
+
def on_parse_error *args
|
40
|
+
raise SyntaxError.new(*args)
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_class_name_error *args
|
44
|
+
raise SyntaxError.new(*args)
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_alias_error *args
|
48
|
+
raise SyntaxError.new(*args)
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_assign_error *args
|
52
|
+
raise SyntaxError.new(*args)
|
53
|
+
end
|
54
|
+
|
55
|
+
def on_param_error *args
|
56
|
+
raise SyntaxError.new(*args)
|
57
|
+
end
|
58
|
+
|
37
59
|
private
|
38
60
|
|
39
61
|
def commentize exp
|
@@ -5,14 +5,20 @@ module RipperRubyParser
|
|
5
5
|
# Main parser class. Brings together Ripper and our
|
6
6
|
# RipperRubyParser::SexpProcessor.
|
7
7
|
class Parser
|
8
|
+
attr_accessor :extra_compatible
|
9
|
+
|
8
10
|
def initialize processor=SexpProcessor.new
|
9
11
|
@processor = processor
|
12
|
+
@extra_compatible = false
|
10
13
|
end
|
11
14
|
|
12
15
|
def parse source, filename='(string)', lineno=1
|
13
16
|
parser = CommentingSexpBuilder.new(source, filename, lineno)
|
14
|
-
|
17
|
+
result = parser.parse
|
18
|
+
raise "Ripper parse failed." if result.nil?
|
19
|
+
exp = Sexp.from_array(result)
|
15
20
|
@processor.filename = filename
|
21
|
+
@processor.extra_compatible = extra_compatible
|
16
22
|
@processor.process exp
|
17
23
|
end
|
18
24
|
end
|
@@ -120,12 +120,18 @@ module RipperRubyParser
|
|
120
120
|
:gvar => :gasgn
|
121
121
|
}
|
122
122
|
|
123
|
+
ASSIGNMENT_IN_METHOD_SUB_TYPE_MAP = {
|
124
|
+
:cvar => :cvasgn
|
125
|
+
}
|
126
|
+
|
123
127
|
def create_assignment_sub_type lvalue, value
|
124
128
|
s(map_assignment_lvalue_type(lvalue.sexp_type), lvalue[1], value)
|
125
129
|
end
|
126
130
|
|
127
131
|
def map_assignment_lvalue_type type
|
128
|
-
|
132
|
+
@in_method_body && ASSIGNMENT_IN_METHOD_SUB_TYPE_MAP[type] ||
|
133
|
+
ASSIGNMENT_SUB_TYPE_MAP[type] ||
|
134
|
+
type
|
129
135
|
end
|
130
136
|
end
|
131
137
|
end
|
@@ -6,7 +6,11 @@ module RipperRubyParser
|
|
6
6
|
block = process(block)
|
7
7
|
args = convert_block_args(block[1])
|
8
8
|
stmt = block[2].first
|
9
|
-
|
9
|
+
if stmt.nil?
|
10
|
+
s(:iter, process(call), args)
|
11
|
+
else
|
12
|
+
s(:iter, process(call), args, stmt)
|
13
|
+
end
|
10
14
|
end
|
11
15
|
|
12
16
|
def process_brace_block exp
|
@@ -31,6 +35,8 @@ module RipperRubyParser
|
|
31
35
|
s(:lasgn, sym, val)
|
32
36
|
end
|
33
37
|
|
38
|
+
# FIXME: Delay conflating all arguments until later, so that
|
39
|
+
# #convert_block_args doesn't need to tease this apart again.
|
34
40
|
add_arg_unless_nil(rest, args) {|name| :"*#{name}" }
|
35
41
|
|
36
42
|
add_arg_unless_nil(block, args) {|name| :"&#{name}" }
|
@@ -47,12 +53,12 @@ module RipperRubyParser
|
|
47
53
|
|
48
54
|
block = process(body)[1]
|
49
55
|
|
50
|
-
strip_wrapping_block(block)
|
56
|
+
strip_wrapping_block(block.compact)
|
51
57
|
end
|
52
58
|
|
53
59
|
def process_rescue exp
|
54
60
|
_, eclass, evar, block, _ = exp.shift 5
|
55
|
-
rescue_block =
|
61
|
+
rescue_block = map_body(block)
|
56
62
|
|
57
63
|
arr = []
|
58
64
|
if eclass
|
@@ -74,6 +80,32 @@ module RipperRubyParser
|
|
74
80
|
wrap_in_block(rescue_block))
|
75
81
|
end
|
76
82
|
|
83
|
+
def process_bodystmt exp
|
84
|
+
_, body, rescue_block, _, ensure_block = exp.shift 5
|
85
|
+
|
86
|
+
body = map_body body
|
87
|
+
|
88
|
+
unless rescue_block or ensure_block
|
89
|
+
return s(:scope, s(:block, *body))
|
90
|
+
end
|
91
|
+
|
92
|
+
body = wrap_in_block(body)
|
93
|
+
|
94
|
+
if rescue_block
|
95
|
+
if body.nil?
|
96
|
+
body = s(:rescue, process(rescue_block))
|
97
|
+
else
|
98
|
+
body = s(:rescue, body, process(rescue_block))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
if ensure_block
|
103
|
+
body = s(:ensure, body, process(ensure_block))
|
104
|
+
end
|
105
|
+
|
106
|
+
s(:scope, s(:block, body))
|
107
|
+
end
|
108
|
+
|
77
109
|
def process_rescue_mod exp
|
78
110
|
_, scary, safe = exp.shift 3
|
79
111
|
s(:rescue, process(scary), s(:resbody, s(:array), process(safe)))
|
@@ -106,10 +138,12 @@ module RipperRubyParser
|
|
106
138
|
|
107
139
|
def handle_generic_block exp
|
108
140
|
_, args, stmts = exp.shift 3
|
141
|
+
# FIXME: Symbol :block is irrelevant.
|
109
142
|
s(:block, process(args), s(handle_statement_list(stmts)))
|
110
143
|
end
|
111
144
|
|
112
145
|
def strip_wrapping_block(block)
|
146
|
+
return block unless block.sexp_type == :block
|
113
147
|
case block.length
|
114
148
|
when 1
|
115
149
|
s(:nil)
|
@@ -3,14 +3,25 @@ module RipperRubyParser
|
|
3
3
|
module Conditionals
|
4
4
|
def process_if exp
|
5
5
|
_, cond, truepart, falsepart = exp.shift 4
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
|
7
|
+
cond = process(cond)
|
8
|
+
truepart = handle_statement_list(truepart)
|
9
|
+
falsepart = process(falsepart)
|
10
|
+
|
11
|
+
if cond.sexp_type == :not
|
12
|
+
cond = cond[1]
|
13
|
+
truepart, falsepart = falsepart, truepart
|
14
|
+
end
|
15
|
+
|
16
|
+
s(:if, cond, truepart, falsepart)
|
10
17
|
end
|
11
18
|
|
12
19
|
def process_elsif exp
|
13
|
-
|
20
|
+
_, cond, truepart, falsepart = exp.shift 4
|
21
|
+
|
22
|
+
s(:if, process(cond),
|
23
|
+
handle_statement_list(truepart),
|
24
|
+
process(falsepart))
|
14
25
|
end
|
15
26
|
|
16
27
|
def process_if_mod exp
|
@@ -24,7 +24,22 @@ module RipperRubyParser
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def convert_block_args(args)
|
27
|
-
|
27
|
+
if args
|
28
|
+
names = args[1][1..-1]
|
29
|
+
if names.length > 1 or names.first =~ /^\*/
|
30
|
+
s(:masgn, s(:array, *names.map { |name| arg_name_to_lasgn(name) }))
|
31
|
+
else
|
32
|
+
s(:lasgn, names.first)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def arg_name_to_lasgn(name)
|
38
|
+
if name =~ /^\*(.*)/
|
39
|
+
s(:splat, s(:lasgn, $1.to_sym))
|
40
|
+
else
|
41
|
+
s(:lasgn, name)
|
42
|
+
end
|
28
43
|
end
|
29
44
|
|
30
45
|
def handle_statement_list exp
|
@@ -78,7 +93,10 @@ module RipperRubyParser
|
|
78
93
|
end
|
79
94
|
|
80
95
|
def wrap_in_block statements
|
81
|
-
|
96
|
+
case statements.length
|
97
|
+
when 0
|
98
|
+
nil
|
99
|
+
when 1
|
82
100
|
statements.first
|
83
101
|
else
|
84
102
|
s(:block, *statements)
|
@@ -9,7 +9,7 @@ module RipperRubyParser
|
|
9
9
|
def process_string_content exp
|
10
10
|
exp.shift
|
11
11
|
|
12
|
-
string, rest =
|
12
|
+
string, rest = extract_unescaped_string_parts exp
|
13
13
|
|
14
14
|
if rest.empty?
|
15
15
|
s(:str, string)
|
@@ -23,12 +23,37 @@ module RipperRubyParser
|
|
23
23
|
s(:evstr, process(list.first))
|
24
24
|
end
|
25
25
|
|
26
|
+
def process_xstring_literal exp
|
27
|
+
_, content = exp.shift 2
|
28
|
+
string, rest = extract_unescaped_string_parts content
|
29
|
+
if rest.empty?
|
30
|
+
s(:xstr, string)
|
31
|
+
else
|
32
|
+
s(:dxstr, string, *rest)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
26
36
|
def process_regexp_literal exp
|
27
|
-
_, content, _ = exp.shift 3
|
37
|
+
_, content, (_, flags, _) = exp.shift 3
|
28
38
|
|
29
|
-
string =
|
39
|
+
string, rest = extract_string_parts content
|
30
40
|
|
31
|
-
|
41
|
+
numflags = 0
|
42
|
+
flags =~ /m/ and numflags |= Regexp::MULTILINE
|
43
|
+
flags =~ /x/ and numflags |= Regexp::EXTENDED
|
44
|
+
flags =~ /i/ and numflags |= Regexp::IGNORECASE
|
45
|
+
|
46
|
+
if rest.empty?
|
47
|
+
s(:lit, Regexp.new(string, numflags))
|
48
|
+
else
|
49
|
+
rest << numflags if numflags > 0
|
50
|
+
sexp_type = if flags =~ /o/
|
51
|
+
:dregx_once
|
52
|
+
else
|
53
|
+
:dregx
|
54
|
+
end
|
55
|
+
s(sexp_type, string, *rest)
|
56
|
+
end
|
32
57
|
end
|
33
58
|
|
34
59
|
def process_symbol_literal exp
|
@@ -44,7 +69,7 @@ module RipperRubyParser
|
|
44
69
|
def process_dyna_symbol exp
|
45
70
|
_, list = exp.shift 2
|
46
71
|
|
47
|
-
string, rest =
|
72
|
+
string, rest = extract_unescaped_string_parts list
|
48
73
|
if rest.empty?
|
49
74
|
s(:lit, string.to_sym)
|
50
75
|
else
|
@@ -59,38 +84,45 @@ module RipperRubyParser
|
|
59
84
|
|
60
85
|
private
|
61
86
|
|
62
|
-
def extract_inner_string exp
|
63
|
-
process(exp) || s(:str, "")
|
64
|
-
end
|
65
|
-
|
66
87
|
def extract_string_parts exp
|
67
88
|
inner = exp.shift
|
68
89
|
|
69
|
-
string =
|
90
|
+
string = process(inner)
|
70
91
|
rest = []
|
71
92
|
|
72
|
-
if string.
|
93
|
+
if string.nil?
|
94
|
+
string = ""
|
95
|
+
elsif string.sexp_type == :str
|
73
96
|
string = string[1]
|
74
97
|
else
|
75
98
|
rest << string
|
76
99
|
string = ""
|
77
100
|
end
|
78
101
|
|
79
|
-
string = unescape(string)
|
80
|
-
|
81
102
|
until exp.empty? do
|
82
103
|
result = process(exp.shift)
|
83
|
-
if result.sexp_type == :str
|
84
|
-
result[1] = unescape(result[1])
|
85
|
-
end
|
86
104
|
rest << result
|
87
105
|
end
|
88
106
|
|
89
107
|
return string, rest
|
90
108
|
end
|
91
109
|
|
110
|
+
def extract_unescaped_string_parts exp
|
111
|
+
string, rest = extract_string_parts exp
|
112
|
+
|
113
|
+
string = unescape(string)
|
114
|
+
|
115
|
+
rest.each do |sub_exp|
|
116
|
+
if sub_exp.sexp_type == :str
|
117
|
+
sub_exp[1] = unescape(sub_exp[1])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
return string, rest
|
122
|
+
end
|
123
|
+
|
92
124
|
def unescape string
|
93
|
-
string.gsub(/(\\[
|
125
|
+
string.gsub(/(\\[^)])/) do
|
94
126
|
eval "\"#{$1}\""
|
95
127
|
end
|
96
128
|
end
|
@@ -12,7 +12,7 @@ module RipperRubyParser
|
|
12
12
|
_, receiver, _, method, args, body = exp.shift 6
|
13
13
|
s(:defs, process(receiver),
|
14
14
|
extract_node_symbol(method),
|
15
|
-
process(args), process(body))
|
15
|
+
process(args), in_method { process(body) })
|
16
16
|
end
|
17
17
|
|
18
18
|
def process_return exp
|
@@ -62,12 +62,19 @@ module RipperRubyParser
|
|
62
62
|
|
63
63
|
private
|
64
64
|
|
65
|
+
def in_method
|
66
|
+
@in_method_body = true
|
67
|
+
result = yield
|
68
|
+
@in_method_body = false
|
69
|
+
result
|
70
|
+
end
|
71
|
+
|
65
72
|
def make_method_name_literal exp
|
66
73
|
process(exp).tap {|it| it[0] = :lit}
|
67
74
|
end
|
68
75
|
|
69
76
|
def method_body exp
|
70
|
-
scope = process exp
|
77
|
+
scope = in_method { process exp }
|
71
78
|
block = scope[1]
|
72
79
|
if block.length == 1
|
73
80
|
block.push s(:nil)
|
@@ -28,7 +28,13 @@ module RipperRubyParser
|
|
28
28
|
else
|
29
29
|
mapped = BINARY_OPERTOR_MAP[op]
|
30
30
|
if mapped
|
31
|
-
|
31
|
+
left = process(left)
|
32
|
+
right = process(right)
|
33
|
+
if mapped == left.sexp_type
|
34
|
+
s(left.sexp_type, left[1], s(mapped, left[2], right))
|
35
|
+
else
|
36
|
+
s(mapped, left, right)
|
37
|
+
end
|
32
38
|
else
|
33
39
|
s(:call, process(left), op, s(:arglist, process(right)))
|
34
40
|
end
|
@@ -6,6 +6,7 @@ module RipperRubyParser
|
|
6
6
|
# Processes the sexp created by Ripper to what RubyParser would produce.
|
7
7
|
class SexpProcessor < ::SexpProcessor
|
8
8
|
attr_accessor :filename
|
9
|
+
attr_accessor :extra_compatible
|
9
10
|
|
10
11
|
def initialize
|
11
12
|
super
|
@@ -27,6 +28,10 @@ module RipperRubyParser
|
|
27
28
|
@processors[:@backref] = :process_at_backref
|
28
29
|
|
29
30
|
@processors[:@tstring_content] = :process_at_tstring_content
|
31
|
+
|
32
|
+
@errors = []
|
33
|
+
|
34
|
+
@in_method_body = false
|
30
35
|
end
|
31
36
|
|
32
37
|
def process exp
|
@@ -66,28 +71,6 @@ module RipperRubyParser
|
|
66
71
|
s(:class, const, parent, class_or_module_body(body)))
|
67
72
|
end
|
68
73
|
|
69
|
-
def process_bodystmt exp
|
70
|
-
_, body, rescue_block, _, ensure_block = exp.shift 5
|
71
|
-
|
72
|
-
body = map_body body
|
73
|
-
|
74
|
-
unless rescue_block or ensure_block
|
75
|
-
return s(:scope, s(:block, *body))
|
76
|
-
end
|
77
|
-
|
78
|
-
body = wrap_in_block(body)
|
79
|
-
|
80
|
-
if rescue_block
|
81
|
-
body = s(:rescue, body, process(rescue_block))
|
82
|
-
end
|
83
|
-
|
84
|
-
if ensure_block
|
85
|
-
body = s(:ensure, body, process(ensure_block))
|
86
|
-
end
|
87
|
-
|
88
|
-
s(:scope, body)
|
89
|
-
end
|
90
|
-
|
91
74
|
def process_var_ref exp
|
92
75
|
_, contents = exp.shift 2
|
93
76
|
process(contents)
|
@@ -133,7 +116,7 @@ module RipperRubyParser
|
|
133
116
|
|
134
117
|
# number literals
|
135
118
|
def process_at_int exp
|
136
|
-
make_literal(exp) {|val| val
|
119
|
+
make_literal(exp) {|val| Integer(val) }
|
137
120
|
end
|
138
121
|
|
139
122
|
def process_at_float exp
|
@@ -142,7 +125,13 @@ module RipperRubyParser
|
|
142
125
|
|
143
126
|
# character literals
|
144
127
|
def process_at_CHAR exp
|
145
|
-
make_literal(exp)
|
128
|
+
make_literal(exp) do |val|
|
129
|
+
if extra_compatible
|
130
|
+
val[1].ord
|
131
|
+
else
|
132
|
+
val[1]
|
133
|
+
end
|
134
|
+
end
|
146
135
|
end
|
147
136
|
|
148
137
|
def process_at_label exp
|
@@ -86,7 +86,7 @@ describe "Using RipperRubyParser and RubyParser" do
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
-
describe "for
|
89
|
+
describe "for an example with operators and explicit block parameter from Reek" do
|
90
90
|
let :program do
|
91
91
|
<<-END
|
92
92
|
def parse(arg, argv, &error)
|
@@ -112,5 +112,18 @@ describe "Using RipperRubyParser and RubyParser" do
|
|
112
112
|
formatted(imitation).must_equal formatted(original)
|
113
113
|
end
|
114
114
|
end
|
115
|
+
|
116
|
+
describe "for an example of a complex regular expression from Reek" do
|
117
|
+
let :program do
|
118
|
+
"/(\#{@types})\\s*(\\w+)\\s*\\(([^)]*)\\)/"
|
119
|
+
end
|
120
|
+
|
121
|
+
it "gives the same result" do
|
122
|
+
original = oldparser.parse program
|
123
|
+
imitation = newparser.parse program
|
124
|
+
|
125
|
+
formatted(imitation).must_equal formatted(original)
|
126
|
+
end
|
127
|
+
end
|
115
128
|
end
|
116
129
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.expand_path('../test_helper.rb', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe "Handling errors" do
|
4
|
+
describe "RipperRubyParser::Parser#parse" do
|
5
|
+
let :newparser do
|
6
|
+
RipperRubyParser::Parser.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "raises an error for an incomplete source" do
|
10
|
+
proc {
|
11
|
+
newparser.parse "def foo"
|
12
|
+
}.must_raise RipperRubyParser::SyntaxError
|
13
|
+
end
|
14
|
+
|
15
|
+
it "raises an error for an invalid class name" do
|
16
|
+
proc {
|
17
|
+
newparser.parse("class foo; end")
|
18
|
+
}.must_raise RipperRubyParser::SyntaxError
|
19
|
+
end
|
20
|
+
|
21
|
+
it "raises an error aliasing $1 as foo" do
|
22
|
+
proc {
|
23
|
+
newparser.parse "alias foo $1"
|
24
|
+
}.must_raise RipperRubyParser::SyntaxError
|
25
|
+
end
|
26
|
+
|
27
|
+
it "raises an error aliasing foo as $1" do
|
28
|
+
proc {
|
29
|
+
newparser.parse "alias $1 foo"
|
30
|
+
}.must_raise RipperRubyParser::SyntaxError
|
31
|
+
end
|
32
|
+
|
33
|
+
it "raises an error aliasing $2 as $1" do
|
34
|
+
proc {
|
35
|
+
newparser.parse "alias $1 $2"
|
36
|
+
}.must_raise RipperRubyParser::SyntaxError
|
37
|
+
end
|
38
|
+
|
39
|
+
it "raises an error assigning to $1" do
|
40
|
+
proc {
|
41
|
+
newparser.parse "$1 = foo"
|
42
|
+
}.must_raise RipperRubyParser::SyntaxError
|
43
|
+
end
|
44
|
+
|
45
|
+
it "raises an error using an invalid parameter name" do
|
46
|
+
proc {
|
47
|
+
newparser.parse "def foo(BAR); end"
|
48
|
+
}.must_raise RipperRubyParser::SyntaxError
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path('../test_helper.rb', File.dirname(__FILE__))
|
2
|
+
require 'ruby_parser'
|
3
|
+
|
4
|
+
describe "Using RipperRubyParser and RubyParser" do
|
5
|
+
let :newparser do
|
6
|
+
RipperRubyParser::Parser.new
|
7
|
+
end
|
8
|
+
|
9
|
+
let :oldparser do
|
10
|
+
RubyParser.new
|
11
|
+
end
|
12
|
+
|
13
|
+
Dir.glob("samples/**/*.rb").each do |file|
|
14
|
+
describe "for #{file}" do
|
15
|
+
let :program do
|
16
|
+
File.read file
|
17
|
+
end
|
18
|
+
|
19
|
+
it "gives the same result" do
|
20
|
+
newparser.extra_compatible = true
|
21
|
+
original = oldparser.parse program
|
22
|
+
imitation = newparser.parse program
|
23
|
+
|
24
|
+
formatted(imitation).must_equal formatted(original)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/test/unit/parser_test.rb
CHANGED
@@ -12,6 +12,7 @@ describe RipperRubyParser::Parser do
|
|
12
12
|
sexp_p = MiniTest::Mock.new
|
13
13
|
sexp_p.expect :process, s(:result), [Sexp]
|
14
14
|
sexp_p.expect :filename=, nil, ['(string)']
|
15
|
+
sexp_p.expect :extra_compatible=, nil, [false]
|
15
16
|
|
16
17
|
parser = RipperRubyParser::Parser.new sexp_p
|
17
18
|
result = parser.parse "any code"
|
@@ -74,6 +75,24 @@ describe RipperRubyParser::Parser do
|
|
74
75
|
s(:call, nil, :qux, s(:arglist)),
|
75
76
|
nil))
|
76
77
|
end
|
78
|
+
|
79
|
+
it "handles a negative condition correctly" do
|
80
|
+
result = parser.parse "if not foo; bar; end"
|
81
|
+
result.must_equal s(:if,
|
82
|
+
s(:call, nil, :foo, s(:arglist)),
|
83
|
+
nil,
|
84
|
+
s(:call, nil, :bar, s(:arglist)))
|
85
|
+
end
|
86
|
+
|
87
|
+
it "handles a negative condition in elsif correctly" do
|
88
|
+
result = parser.parse "if foo; bar; elsif not baz; qux; end"
|
89
|
+
result.must_equal s(:if,
|
90
|
+
s(:call, nil, :foo, s(:arglist)),
|
91
|
+
s(:call, nil, :bar, s(:arglist)),
|
92
|
+
s(:if,
|
93
|
+
s(:not, s(:call, nil, :baz, s(:arglist))),
|
94
|
+
s(:call, nil, :qux, s(:arglist)), nil))
|
95
|
+
end
|
77
96
|
end
|
78
97
|
|
79
98
|
describe "for unless" do
|
@@ -273,6 +292,12 @@ describe RipperRubyParser::Parser do
|
|
273
292
|
end
|
274
293
|
|
275
294
|
describe "for the rescue statement" do
|
295
|
+
it "works with empty main and rescue bodies" do
|
296
|
+
result = parser.parse "begin; rescue; end"
|
297
|
+
result.must_equal s(:rescue,
|
298
|
+
s(:resbody, s(:array), nil))
|
299
|
+
end
|
300
|
+
|
276
301
|
it "works with single statement main and rescue bodies" do
|
277
302
|
result = parser.parse "begin; foo; rescue; bar; end"
|
278
303
|
result.must_equal s(:rescue,
|
@@ -343,6 +368,20 @@ describe RipperRubyParser::Parser do
|
|
343
368
|
s(:array),
|
344
369
|
s(:call, nil, :bar, s(:arglist))))
|
345
370
|
end
|
371
|
+
|
372
|
+
it "works in a plain method body" do
|
373
|
+
result = parser.parse "def foo; bar; rescue; baz; end"
|
374
|
+
result.must_equal s(:defn,
|
375
|
+
:foo,
|
376
|
+
s(:args),
|
377
|
+
s(:scope,
|
378
|
+
s(:block,
|
379
|
+
s(:rescue,
|
380
|
+
s(:call, nil, :bar, s(:arglist)),
|
381
|
+
s(:resbody,
|
382
|
+
s(:array),
|
383
|
+
s(:call, nil, :baz, s(:arglist)))))))
|
384
|
+
end
|
346
385
|
end
|
347
386
|
|
348
387
|
describe "for the ensure statement" do
|
@@ -522,6 +561,11 @@ describe RipperRubyParser::Parser do
|
|
522
561
|
result = parser.parse "3.14"
|
523
562
|
result.must_equal s(:lit, 3.14)
|
524
563
|
end
|
564
|
+
|
565
|
+
it "works for octal integer literals" do
|
566
|
+
result = parser.parse "0700"
|
567
|
+
result.must_equal s(:lit, 448)
|
568
|
+
end
|
525
569
|
end
|
526
570
|
|
527
571
|
describe "for collection indexing" do
|
@@ -535,7 +579,7 @@ describe RipperRubyParser::Parser do
|
|
535
579
|
end
|
536
580
|
|
537
581
|
describe "for method definitions" do
|
538
|
-
it "works with def with
|
582
|
+
it "works with def with receiver" do
|
539
583
|
result = parser.parse "def foo.bar; end"
|
540
584
|
result.must_equal s(:defs,
|
541
585
|
s(:call, nil, :foo, s(:arglist)),
|
@@ -649,7 +693,7 @@ describe RipperRubyParser::Parser do
|
|
649
693
|
end
|
650
694
|
|
651
695
|
describe "for method calls" do
|
652
|
-
describe "without a
|
696
|
+
describe "without a receiver" do
|
653
697
|
it "works without brackets" do
|
654
698
|
result = parser.parse "foo bar"
|
655
699
|
result.must_equal s(:call, nil, :foo,
|
@@ -687,7 +731,7 @@ describe RipperRubyParser::Parser do
|
|
687
731
|
end
|
688
732
|
end
|
689
733
|
|
690
|
-
describe "with a
|
734
|
+
describe "with a receiver" do
|
691
735
|
it "works without brackets" do
|
692
736
|
result = parser.parse "foo.bar baz"
|
693
737
|
result.must_equal s(:call,
|
@@ -755,6 +799,13 @@ describe RipperRubyParser::Parser do
|
|
755
799
|
end
|
756
800
|
|
757
801
|
describe "for blocks" do
|
802
|
+
it "works with no statements in the block body" do
|
803
|
+
result = parser.parse "foo do; end"
|
804
|
+
result.must_equal s(:iter,
|
805
|
+
s(:call, nil, :foo, s(:arglist)),
|
806
|
+
nil)
|
807
|
+
end
|
808
|
+
|
758
809
|
it "works with next with no arguments" do
|
759
810
|
result = parser.parse "foo do; next; end"
|
760
811
|
result.must_equal s(:iter,
|
@@ -816,6 +867,43 @@ describe RipperRubyParser::Parser do
|
|
816
867
|
nil,
|
817
868
|
s(:redo))
|
818
869
|
end
|
870
|
+
|
871
|
+
it "works with one argument" do
|
872
|
+
result = parser.parse "foo do |bar|; end"
|
873
|
+
result.must_equal s(:iter,
|
874
|
+
s(:call, nil, :foo, s(:arglist)),
|
875
|
+
s(:lasgn, :bar))
|
876
|
+
end
|
877
|
+
|
878
|
+
it "works with multiple arguments" do
|
879
|
+
result = parser.parse "foo do |bar, baz|; end"
|
880
|
+
result.must_equal s(:iter,
|
881
|
+
s(:call, nil, :foo, s(:arglist)),
|
882
|
+
s(:masgn,
|
883
|
+
s(:array,
|
884
|
+
s(:lasgn, :bar),
|
885
|
+
s(:lasgn, :baz))))
|
886
|
+
end
|
887
|
+
|
888
|
+
it "works with a single splat argument" do
|
889
|
+
result = parser.parse "foo do |*bar|; end"
|
890
|
+
result.must_equal s(:iter,
|
891
|
+
s(:call, nil, :foo, s(:arglist)),
|
892
|
+
s(:masgn,
|
893
|
+
s(:array,
|
894
|
+
s(:splat, s(:lasgn, :bar)))))
|
895
|
+
end
|
896
|
+
|
897
|
+
it "works with a combination of regular arguments and a splat argument" do
|
898
|
+
result = parser.parse "foo do |bar, *baz|; end"
|
899
|
+
result.must_equal s(:iter,
|
900
|
+
s(:call, nil, :foo, s(:arglist)),
|
901
|
+
s(:masgn,
|
902
|
+
s(:array,
|
903
|
+
s(:lasgn, :bar),
|
904
|
+
s(:splat, s(:lasgn, :baz)))))
|
905
|
+
end
|
906
|
+
|
819
907
|
end
|
820
908
|
|
821
909
|
describe "for yield" do
|
@@ -882,6 +970,11 @@ describe RipperRubyParser::Parser do
|
|
882
970
|
result.must_equal s(:str, "\n")
|
883
971
|
end
|
884
972
|
|
973
|
+
it "works for strings with useless escape sequences" do
|
974
|
+
result = parser.parse "\"F\\OO\""
|
975
|
+
result.must_equal s(:str, "FOO")
|
976
|
+
end
|
977
|
+
|
885
978
|
it "works for strings with escaped backslashes" do
|
886
979
|
result = parser.parse "\"\\\\n\""
|
887
980
|
result.must_equal s(:str, "\\n")
|
@@ -950,6 +1043,49 @@ describe RipperRubyParser::Parser do
|
|
950
1043
|
result.must_equal s(:lit, /\)\n\\/)
|
951
1044
|
end
|
952
1045
|
|
1046
|
+
it "works for regexes with interpolations" do
|
1047
|
+
result = parser.parse '/foo#{bar}baz/'
|
1048
|
+
result.must_equal s(:dregx,
|
1049
|
+
"foo",
|
1050
|
+
s(:evstr, s(:call, nil, :bar, s(:arglist))),
|
1051
|
+
s(:str, "baz"))
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
it "works for a regex literal with the multiline flag" do
|
1055
|
+
result = parser.parse "/foo/m"
|
1056
|
+
result.must_equal s(:lit, /foo/m)
|
1057
|
+
end
|
1058
|
+
|
1059
|
+
it "works for a regex literal with the extended flag" do
|
1060
|
+
result = parser.parse "/foo/x"
|
1061
|
+
result.must_equal s(:lit, /foo/x)
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
it "works for a regex literal with the ignorecase flag" do
|
1065
|
+
result = parser.parse "/foo/i"
|
1066
|
+
result.must_equal s(:lit, /foo/i)
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
it "works for a regex literal with a combination of flags" do
|
1070
|
+
result = parser.parse "/foo/ixm"
|
1071
|
+
result.must_equal s(:lit, /foo/ixm)
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
it "works for a regex literal with flags and interpolation" do
|
1075
|
+
result = parser.parse '/foo#{bar}/ixm'
|
1076
|
+
result.must_equal s(:dregx,
|
1077
|
+
"foo",
|
1078
|
+
s(:evstr, s(:call, nil, :bar, s(:arglist))),
|
1079
|
+
7)
|
1080
|
+
end
|
1081
|
+
|
1082
|
+
it "works for a regex literal with interpolate-once flag" do
|
1083
|
+
result = parser.parse '/foo#{bar}/o'
|
1084
|
+
result.must_equal s(:dregx_once,
|
1085
|
+
"foo",
|
1086
|
+
s(:evstr, s(:call, nil, :bar, s(:arglist))))
|
1087
|
+
end
|
1088
|
+
|
953
1089
|
it "works for simple dsyms" do
|
954
1090
|
result = parser.parse ':"foo"'
|
955
1091
|
result.must_equal s(:lit, :foo)
|
@@ -966,6 +1102,30 @@ describe RipperRubyParser::Parser do
|
|
966
1102
|
result = parser.parse "?a"
|
967
1103
|
result.must_equal s(:lit, "a")
|
968
1104
|
end
|
1105
|
+
|
1106
|
+
it "works for character literals in extra compatible mode" do
|
1107
|
+
parser.extra_compatible = true
|
1108
|
+
result = parser.parse "?a"
|
1109
|
+
result.must_equal s(:lit, 97)
|
1110
|
+
end
|
1111
|
+
|
1112
|
+
it "works for basic backtick strings" do
|
1113
|
+
result = parser.parse '`foo`'
|
1114
|
+
result.must_equal s(:xstr, "foo")
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
it "works for interpolated backtick strings" do
|
1118
|
+
result = parser.parse '`foo#{bar}`'
|
1119
|
+
result.must_equal s(:dxstr,
|
1120
|
+
"foo",
|
1121
|
+
s(:evstr, s(:call, nil, :bar, s(:arglist))))
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
it "works for backtick strings with escape sequences" do
|
1125
|
+
result = parser.parse '`foo\\n`'
|
1126
|
+
result.must_equal s(:xstr, "foo\n")
|
1127
|
+
end
|
1128
|
+
|
969
1129
|
end
|
970
1130
|
|
971
1131
|
describe "for the __FILE__ keyword" do
|
@@ -1074,6 +1234,25 @@ describe RipperRubyParser::Parser do
|
|
1074
1234
|
s(:call, nil, :bar, s(:arglist)))
|
1075
1235
|
end
|
1076
1236
|
|
1237
|
+
it "works when assigning to a class variable inside a method" do
|
1238
|
+
result = parser.parse "def foo; @@bar = baz; end"
|
1239
|
+
result.must_equal s(:defn,
|
1240
|
+
:foo, s(:args),
|
1241
|
+
s(:scope,
|
1242
|
+
s(:block,
|
1243
|
+
s(:cvasgn, :@@bar, s(:call, nil, :baz, s(:arglist))))))
|
1244
|
+
end
|
1245
|
+
|
1246
|
+
it "works when assigning to a class variable inside a method with a receiver" do
|
1247
|
+
result = parser.parse "def self.foo; @@bar = baz; end"
|
1248
|
+
result.must_equal s(:defs,
|
1249
|
+
s(:self),
|
1250
|
+
:foo, s(:args),
|
1251
|
+
s(:scope,
|
1252
|
+
s(:block,
|
1253
|
+
s(:cvasgn, :@@bar, s(:call, nil, :baz, s(:arglist))))))
|
1254
|
+
end
|
1255
|
+
|
1077
1256
|
it "works when assigning to a global variable" do
|
1078
1257
|
result = parser.parse "$foo = bar"
|
1079
1258
|
result.must_equal s(:gasgn,
|
@@ -1281,6 +1460,15 @@ describe RipperRubyParser::Parser do
|
|
1281
1460
|
s(:call, nil, :bar, s(:arglist)))
|
1282
1461
|
end
|
1283
1462
|
|
1463
|
+
it "handles double :and" do
|
1464
|
+
result = parser.parse "foo and bar and baz"
|
1465
|
+
result.must_equal s(:and,
|
1466
|
+
s(:call, nil, :foo, s(:arglist)),
|
1467
|
+
s(:and,
|
1468
|
+
s(:call, nil, :bar, s(:arglist)),
|
1469
|
+
s(:call, nil, :baz, s(:arglist))))
|
1470
|
+
end
|
1471
|
+
|
1284
1472
|
it "handles :or" do
|
1285
1473
|
result = parser.parse "foo or bar"
|
1286
1474
|
result.must_equal s(:or,
|
@@ -1288,6 +1476,33 @@ describe RipperRubyParser::Parser do
|
|
1288
1476
|
s(:call, nil, :bar, s(:arglist)))
|
1289
1477
|
end
|
1290
1478
|
|
1479
|
+
it "handles double :or" do
|
1480
|
+
result = parser.parse "foo or bar or baz"
|
1481
|
+
result.must_equal s(:or,
|
1482
|
+
s(:call, nil, :foo, s(:arglist)),
|
1483
|
+
s(:or,
|
1484
|
+
s(:call, nil, :bar, s(:arglist)),
|
1485
|
+
s(:call, nil, :baz, s(:arglist))))
|
1486
|
+
end
|
1487
|
+
|
1488
|
+
it "handles :or after :and" do
|
1489
|
+
result = parser.parse "foo and bar or baz"
|
1490
|
+
result.must_equal s(:or,
|
1491
|
+
s(:and,
|
1492
|
+
s(:call, nil, :foo, s(:arglist)),
|
1493
|
+
s(:call, nil, :bar, s(:arglist))),
|
1494
|
+
s(:call, nil, :baz, s(:arglist)))
|
1495
|
+
end
|
1496
|
+
|
1497
|
+
it "handles :and after :or" do
|
1498
|
+
result = parser.parse "foo or bar and baz"
|
1499
|
+
result.must_equal s(:and,
|
1500
|
+
s(:or,
|
1501
|
+
s(:call, nil, :foo, s(:arglist)),
|
1502
|
+
s(:call, nil, :bar, s(:arglist))),
|
1503
|
+
s(:call, nil, :baz, s(:arglist)))
|
1504
|
+
end
|
1505
|
+
|
1291
1506
|
it "converts :&& to :and" do
|
1292
1507
|
result = parser.parse "foo && bar"
|
1293
1508
|
result.must_equal s(:and,
|
@@ -1469,12 +1684,12 @@ describe RipperRubyParser::Parser do
|
|
1469
1684
|
result.line.must_equal 1
|
1470
1685
|
end
|
1471
1686
|
|
1472
|
-
it "works for a method call with
|
1687
|
+
it "works for a method call with receiver" do
|
1473
1688
|
result = parser.parse "foo.bar"
|
1474
1689
|
result.line.must_equal 1
|
1475
1690
|
end
|
1476
1691
|
|
1477
|
-
it "works for a method call with
|
1692
|
+
it "works for a method call with receiver and arguments" do
|
1478
1693
|
result = parser.parse "foo.bar baz"
|
1479
1694
|
result.line.must_equal 1
|
1480
1695
|
end
|
@@ -1580,11 +1795,12 @@ describe RipperRubyParser::Parser do
|
|
1580
1795
|
result.line.must_equal 1
|
1581
1796
|
end
|
1582
1797
|
|
1583
|
-
it "assigns line numbers to
|
1584
|
-
result = parser.parse "foo() do\nend\n"
|
1798
|
+
it "assigns line numbers to nested sexps that don't generate their own line numbers" do
|
1799
|
+
result = parser.parse "foo() do\nnext\nend\n"
|
1585
1800
|
result.must_equal s(:iter,
|
1586
|
-
s(:call,
|
1587
|
-
|
1801
|
+
s(:call, nil, :foo, s(:arglist)),
|
1802
|
+
nil,
|
1803
|
+
s(:next))
|
1588
1804
|
arglist = result[1][3]
|
1589
1805
|
block = result[3]
|
1590
1806
|
nums = [ arglist.line, block.line ]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ripper_ruby_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-03-
|
12
|
+
date: 2012-03-31 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sexp_processor
|
16
|
-
requirement: &
|
16
|
+
requirement: &22929360 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '3.0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *22929360
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: minitest
|
27
|
-
requirement: &
|
27
|
+
requirement: &22928840 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 2.11.2
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *22928840
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rake
|
38
|
-
requirement: &
|
38
|
+
requirement: &22928360 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 0.9.2
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *22928360
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: ruby_parser
|
49
|
-
requirement: &
|
49
|
+
requirement: &22927880 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 2.3.1
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *22927880
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: simplecov
|
60
|
-
requirement: &
|
60
|
+
requirement: &22927500 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *22927500
|
69
69
|
description:
|
70
70
|
email:
|
71
71
|
- matijs@matijs.net
|
@@ -92,13 +92,16 @@ files:
|
|
92
92
|
- lib/ripper_ruby_parser/parser.rb
|
93
93
|
- lib/ripper_ruby_parser/version.rb
|
94
94
|
- lib/ripper_ruby_parser/sexp_handlers.rb
|
95
|
+
- lib/ripper_ruby_parser/syntax_error.rb
|
95
96
|
- lib/ripper_ruby_parser/sexp_processor.rb
|
96
97
|
- test/test_helper.rb
|
97
98
|
- test/end_to_end/lib_comparison_test.rb
|
99
|
+
- test/end_to_end/samples_comparison_test.rb
|
98
100
|
- test/end_to_end/comparison_test.rb
|
99
101
|
- test/end_to_end/comments_test.rb
|
100
102
|
- test/end_to_end/test_comparison_test.rb
|
101
103
|
- test/end_to_end/line_numbering_test.rb
|
104
|
+
- test/end_to_end/error_conditions_test.rb
|
102
105
|
- test/unit/parser_test.rb
|
103
106
|
- test/unit/sexp_processor_test.rb
|
104
107
|
- test/unit/version_test.rb
|
@@ -134,8 +137,10 @@ summary: Parse with Ripper, produce sexps that are compatible with RubyParser.
|
|
134
137
|
test_files:
|
135
138
|
- test/end_to_end/comments_test.rb
|
136
139
|
- test/end_to_end/comparison_test.rb
|
140
|
+
- test/end_to_end/error_conditions_test.rb
|
137
141
|
- test/end_to_end/lib_comparison_test.rb
|
138
142
|
- test/end_to_end/line_numbering_test.rb
|
143
|
+
- test/end_to_end/samples_comparison_test.rb
|
139
144
|
- test/end_to_end/test_comparison_test.rb
|
140
145
|
- test/test_helper.rb
|
141
146
|
- test/unit/commenting_sexp_builder_test.rb
|