ripper_ruby_parser 0.0.3 → 0.0.4
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.
- 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
|