ripper_ruby_parser 1.7.0 → 1.9.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 +76 -0
- data/README.md +6 -4
- data/lib/ripper_ruby_parser/commenting_ripper_parser.rb +24 -12
- data/lib/ripper_ruby_parser/sexp_handlers.rb +2 -0
- data/lib/ripper_ruby_parser/sexp_handlers/assignment.rb +9 -4
- data/lib/ripper_ruby_parser/sexp_handlers/blocks.rb +40 -52
- 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/method_calls.rb +9 -5
- data/lib/ripper_ruby_parser/sexp_handlers/methods.rb +22 -17
- data/lib/ripper_ruby_parser/sexp_handlers/operators.rb +3 -3
- data/lib/ripper_ruby_parser/sexp_handlers/string_literals.rb +256 -0
- data/lib/ripper_ruby_parser/sexp_processor.rb +12 -56
- data/lib/ripper_ruby_parser/unescape.rb +89 -43
- data/lib/ripper_ruby_parser/version.rb +1 -1
- metadata +125 -76
- 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 -31
- 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 -200
- data/test/ripper_ruby_parser/parser_test.rb +0 -553
- data/test/ripper_ruby_parser/sexp_handlers/assignment_test.rb +0 -613
- data/test/ripper_ruby_parser/sexp_handlers/blocks_test.rb +0 -679
- data/test/ripper_ruby_parser/sexp_handlers/conditionals_test.rb +0 -536
- data/test/ripper_ruby_parser/sexp_handlers/literals_test.rb +0 -1106
- 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 -421
- 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/lambdas.rb +0 -5
- data/test/samples/loops.rb +0 -36
- data/test/samples/misc.rb +0 -281
- data/test/samples/number.rb +0 -7
- data/test/samples/operators.rb +0 -18
- data/test/samples/strings.rb +0 -147
- data/test/test_helper.rb +0 -107
@@ -1,29 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require File.expand_path("../test_helper.rb", File.dirname(__FILE__))
|
4
|
-
require "ruby_parser"
|
5
|
-
|
6
|
-
describe "Using RipperRubyParser and RubyParser" do
|
7
|
-
let :newparser do
|
8
|
-
RipperRubyParser::Parser.new
|
9
|
-
end
|
10
|
-
|
11
|
-
let :oldparser do
|
12
|
-
RubyParser.for_current_ruby
|
13
|
-
end
|
14
|
-
|
15
|
-
Dir.glob("lib/**/*.rb").each do |file|
|
16
|
-
describe "for #{file}" do
|
17
|
-
let :program do
|
18
|
-
File.read file
|
19
|
-
end
|
20
|
-
|
21
|
-
it "gives the same result" do
|
22
|
-
original = oldparser.parse program
|
23
|
-
imitation = newparser.parse program
|
24
|
-
|
25
|
-
_(formatted(imitation)).must_equal formatted(original)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require File.expand_path("../test_helper.rb", File.dirname(__FILE__))
|
4
|
-
require "ruby_parser"
|
5
|
-
|
6
|
-
describe "Using RipperRubyParser and RubyParser" do
|
7
|
-
describe "for a multi-line program" do
|
8
|
-
let :program do
|
9
|
-
<<-RUBY
|
10
|
-
class Foo
|
11
|
-
def foo()
|
12
|
-
bar()
|
13
|
-
baz(qux)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
module Bar
|
18
|
-
@@baz = {}
|
19
|
-
end
|
20
|
-
RUBY
|
21
|
-
end
|
22
|
-
|
23
|
-
it "gives the same result" do
|
24
|
-
_(program).must_be_parsed_as_before
|
25
|
-
end
|
26
|
-
|
27
|
-
it "gives the same result with line numbers" do
|
28
|
-
_(program).must_be_parsed_as_before with_line_numbers: true
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require File.expand_path("../test_helper.rb", File.dirname(__FILE__))
|
4
|
-
require "ruby_parser"
|
5
|
-
|
6
|
-
describe "Using RipperRubyParser and RubyParser" do
|
7
|
-
Dir.glob(File.expand_path("../samples/*.rb", File.dirname(__FILE__))).each do |file|
|
8
|
-
it "gives the same result for #{file}" do
|
9
|
-
program = File.read file
|
10
|
-
_(program).must_be_parsed_as_before
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require File.expand_path("../test_helper.rb", File.dirname(__FILE__))
|
4
|
-
require "ruby_parser"
|
5
|
-
|
6
|
-
describe "Using RipperRubyParser and RubyParser" do
|
7
|
-
let :newparser do
|
8
|
-
RipperRubyParser::Parser.new
|
9
|
-
end
|
10
|
-
|
11
|
-
let :oldparser do
|
12
|
-
RubyParser.for_current_ruby
|
13
|
-
end
|
14
|
-
|
15
|
-
Dir.glob("test/ripper_ruby_parser/**/*.rb").each do |file|
|
16
|
-
describe "for #{file}" do
|
17
|
-
let :program do
|
18
|
-
File.read file
|
19
|
-
end
|
20
|
-
|
21
|
-
it "gives the same result" do
|
22
|
-
# Clone string because ruby_parser destroys it when there's a heredoc
|
23
|
-
# inside.
|
24
|
-
copy = program.clone
|
25
|
-
original = oldparser.parse program
|
26
|
-
imitation = newparser.parse copy
|
27
|
-
|
28
|
-
_(formatted(imitation)).must_equal formatted(original)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
data/test/pt_testcase/pt_test.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require File.expand_path("../test_helper.rb", File.dirname(__FILE__))
|
4
|
-
require "pt_testcase"
|
5
|
-
|
6
|
-
class TestParser < RipperRubyParser::Parser
|
7
|
-
def process(input)
|
8
|
-
parse input
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
SKIPPED_TESTS = ["dstr_heredoc_windoze_sucks"].freeze
|
13
|
-
|
14
|
-
class RubyParserTestCase < ParseTreeTestCase
|
15
|
-
def self.previous(_key)
|
16
|
-
"Ruby"
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.generate_test(klass, node, data, input_name, _output_name)
|
20
|
-
if data["Ruby"].is_a? Array
|
21
|
-
klass.send :define_method, "test_#{node}" do
|
22
|
-
skip "Not a parser test"
|
23
|
-
end
|
24
|
-
return
|
25
|
-
end
|
26
|
-
|
27
|
-
if SKIPPED_TESTS.include? node
|
28
|
-
klass.send :define_method, "test_#{node}" do
|
29
|
-
skip "Can't or won't fix this difference"
|
30
|
-
end
|
31
|
-
return
|
32
|
-
end
|
33
|
-
|
34
|
-
super klass, node, data, input_name, "ParseTree"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
class TestRuby19Parser < RubyParserTestCase
|
39
|
-
def setup
|
40
|
-
super
|
41
|
-
|
42
|
-
self.processor = TestParser.new
|
43
|
-
end
|
44
|
-
end
|
@@ -1,200 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require File.expand_path("../test_helper.rb", File.dirname(__FILE__))
|
4
|
-
|
5
|
-
describe RipperRubyParser::CommentingRipperParser do
|
6
|
-
def parse_with_builder(str)
|
7
|
-
builder = RipperRubyParser::CommentingRipperParser.new str
|
8
|
-
builder.parse
|
9
|
-
end
|
10
|
-
|
11
|
-
def empty_params_list
|
12
|
-
@empty_params_list ||= s(:params, *([nil] * 7))
|
13
|
-
end
|
14
|
-
|
15
|
-
describe "handling comments" do
|
16
|
-
# Handle different results for dynamic symbol strings. This was changed in
|
17
|
-
# Ruby 2.6.3 and up. See https://bugs.ruby-lang.org/issues/15670
|
18
|
-
let(:dsym_string_type) { RUBY_VERSION < "2.6.3" ? :xstring : :string_content }
|
19
|
-
|
20
|
-
it "produces a comment node surrounding a commented def" do
|
21
|
-
result = parse_with_builder "# Foo\ndef foo; end"
|
22
|
-
_(result).must_equal s(:program,
|
23
|
-
s(:stmts,
|
24
|
-
s(:comment,
|
25
|
-
"# Foo\n",
|
26
|
-
s(:def,
|
27
|
-
s(:@ident, "foo", s(2, 4)),
|
28
|
-
empty_params_list,
|
29
|
-
s(:bodystmt,
|
30
|
-
s(:stmts, s(:void_stmt, s(2, 12))), nil, nil, nil),
|
31
|
-
s(2, 0)))))
|
32
|
-
end
|
33
|
-
|
34
|
-
it "produces a blank comment node surrounding a def that has no comment" do
|
35
|
-
result = parse_with_builder "def foo; end"
|
36
|
-
_(result).must_equal s(:program,
|
37
|
-
s(:stmts,
|
38
|
-
s(:comment,
|
39
|
-
"",
|
40
|
-
s(:def,
|
41
|
-
s(:@ident, "foo", s(1, 4)),
|
42
|
-
empty_params_list,
|
43
|
-
s(:bodystmt,
|
44
|
-
s(:stmts, s(:void_stmt, s(1, 12))), nil, nil, nil),
|
45
|
-
s(1, 0)))))
|
46
|
-
end
|
47
|
-
|
48
|
-
it "produces a comment node surrounding a commented class" do
|
49
|
-
result = parse_with_builder "# Foo\nclass Foo; end"
|
50
|
-
_(result).must_equal s(:program,
|
51
|
-
s(:stmts,
|
52
|
-
s(:comment,
|
53
|
-
"# Foo\n",
|
54
|
-
s(:class,
|
55
|
-
s(:const_ref, s(:@const, "Foo", s(2, 6))),
|
56
|
-
nil,
|
57
|
-
s(:bodystmt,
|
58
|
-
s(:stmts, s(:void_stmt, s(2, 10))), nil, nil, nil),
|
59
|
-
s(2, 0)))))
|
60
|
-
end
|
61
|
-
|
62
|
-
it "produce a blank comment node surrounding a class that has no comment" do
|
63
|
-
result = parse_with_builder "class Foo; end"
|
64
|
-
_(result).must_equal s(:program,
|
65
|
-
s(:stmts,
|
66
|
-
s(:comment,
|
67
|
-
"",
|
68
|
-
s(:class,
|
69
|
-
s(:const_ref, s(:@const, "Foo", s(1, 6))),
|
70
|
-
nil,
|
71
|
-
s(:bodystmt,
|
72
|
-
s(:stmts, s(:void_stmt, s(1, 10))), nil, nil, nil),
|
73
|
-
s(1, 0)))))
|
74
|
-
end
|
75
|
-
|
76
|
-
it "produces a comment node surrounding a commented module" do
|
77
|
-
result = parse_with_builder "# Foo\nmodule Foo; end"
|
78
|
-
_(result).must_equal s(:program,
|
79
|
-
s(:stmts,
|
80
|
-
s(:comment,
|
81
|
-
"# Foo\n",
|
82
|
-
s(:module,
|
83
|
-
s(:const_ref, s(:@const, "Foo", s(2, 7))),
|
84
|
-
s(:bodystmt,
|
85
|
-
s(:stmts, s(:void_stmt, s(2, 11))), nil, nil, nil),
|
86
|
-
s(2, 0)))))
|
87
|
-
end
|
88
|
-
|
89
|
-
it "produces a blank comment node surrounding a module that has no comment" do
|
90
|
-
result = parse_with_builder "module Foo; end"
|
91
|
-
_(result).must_equal s(:program,
|
92
|
-
s(:stmts,
|
93
|
-
s(:comment,
|
94
|
-
"",
|
95
|
-
s(:module,
|
96
|
-
s(:const_ref, s(:@const, "Foo", s(1, 7))),
|
97
|
-
s(:bodystmt,
|
98
|
-
s(:stmts, s(:void_stmt, s(1, 11))), nil, nil, nil),
|
99
|
-
s(1, 0)))))
|
100
|
-
end
|
101
|
-
|
102
|
-
it "is not confused by a symbol containing a keyword" do
|
103
|
-
result = parse_with_builder ":class; def foo; end"
|
104
|
-
_(result).must_equal s(:program,
|
105
|
-
s(:stmts,
|
106
|
-
s(:symbol_literal, s(:symbol, s(:@kw, "class", s(1, 1)))),
|
107
|
-
s(:comment,
|
108
|
-
"",
|
109
|
-
s(:def,
|
110
|
-
s(:@ident, "foo", s(1, 12)),
|
111
|
-
empty_params_list,
|
112
|
-
s(:bodystmt,
|
113
|
-
s(:stmts, s(:void_stmt, s(1, 20))), nil, nil, nil),
|
114
|
-
s(1, 8)))))
|
115
|
-
end
|
116
|
-
|
117
|
-
it "is not confused by a dynamic symbol" do
|
118
|
-
result = parse_with_builder ":'foo'; def bar; end"
|
119
|
-
_(result).must_equal s(:program,
|
120
|
-
s(:stmts,
|
121
|
-
s(:dyna_symbol,
|
122
|
-
s(dsym_string_type,
|
123
|
-
s(:@tstring_content, "foo", s(1, 2), ":'"))),
|
124
|
-
s(:comment,
|
125
|
-
"",
|
126
|
-
s(:def,
|
127
|
-
s(:@ident, "bar", s(1, 12)),
|
128
|
-
empty_params_list,
|
129
|
-
s(:bodystmt,
|
130
|
-
s(:stmts, s(:void_stmt, s(1, 20))), nil, nil, nil),
|
131
|
-
s(1, 8)))))
|
132
|
-
end
|
133
|
-
|
134
|
-
it "is not confused by a dynamic symbol containing a class definition" do
|
135
|
-
result = parse_with_builder ":\"foo\#{class Bar;end}\""
|
136
|
-
_(result).must_equal s(:program,
|
137
|
-
s(:stmts,
|
138
|
-
s(:dyna_symbol,
|
139
|
-
s(dsym_string_type,
|
140
|
-
s(:@tstring_content, "foo", s(1, 2), ':"'),
|
141
|
-
s(:string_embexpr,
|
142
|
-
s(:stmts,
|
143
|
-
s(:comment,
|
144
|
-
"",
|
145
|
-
s(:class,
|
146
|
-
s(:const_ref, s(:@const, "Bar", s(1, 13))),
|
147
|
-
nil,
|
148
|
-
s(:bodystmt,
|
149
|
-
s(:stmts, s(:void_stmt, s(1, 17))),
|
150
|
-
nil, nil, nil),
|
151
|
-
s(1, 7)))))))))
|
152
|
-
end
|
153
|
-
|
154
|
-
it "turns an embedded document into a comment node" do
|
155
|
-
result = parse_with_builder "=begin Hello\nthere\n=end\nclass Foo; end"
|
156
|
-
_(result).must_equal s(:program,
|
157
|
-
s(:stmts,
|
158
|
-
s(:comment,
|
159
|
-
"=begin Hello\nthere\n=end\n",
|
160
|
-
s(:class,
|
161
|
-
s(:const_ref, s(:@const, "Foo", s(4, 6))),
|
162
|
-
nil,
|
163
|
-
s(:bodystmt,
|
164
|
-
s(:stmts, s(:void_stmt, s(4, 10))), nil, nil, nil),
|
165
|
-
s(4, 0)))))
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
describe "handling syntax errors" do
|
170
|
-
it "raises an error for an incomplete source" do
|
171
|
-
_(proc { parse_with_builder "def foo" }).must_raise RipperRubyParser::SyntaxError
|
172
|
-
end
|
173
|
-
|
174
|
-
it "raises an error for an invalid class name" do
|
175
|
-
_(proc { parse_with_builder "class foo; end" })
|
176
|
-
.must_raise RipperRubyParser::SyntaxError
|
177
|
-
end
|
178
|
-
|
179
|
-
it "raises an error aliasing $1 as foo" do
|
180
|
-
_(proc { parse_with_builder "alias foo $1" }).must_raise RipperRubyParser::SyntaxError
|
181
|
-
end
|
182
|
-
|
183
|
-
it "raises an error aliasing foo as $1" do
|
184
|
-
_(proc { parse_with_builder "alias $1 foo" }).must_raise RipperRubyParser::SyntaxError
|
185
|
-
end
|
186
|
-
|
187
|
-
it "raises an error aliasing $2 as $1" do
|
188
|
-
_(proc { parse_with_builder "alias $1 $2" }).must_raise RipperRubyParser::SyntaxError
|
189
|
-
end
|
190
|
-
|
191
|
-
it "raises an error assigning to $1" do
|
192
|
-
_(proc { parse_with_builder "$1 = foo" }).must_raise RipperRubyParser::SyntaxError
|
193
|
-
end
|
194
|
-
|
195
|
-
it "raises an error using an invalid parameter name" do
|
196
|
-
_(proc { parse_with_builder "def foo(BAR); end" })
|
197
|
-
.must_raise RipperRubyParser::SyntaxError
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|
@@ -1,553 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require File.expand_path("../test_helper.rb", File.dirname(__FILE__))
|
4
|
-
|
5
|
-
describe RipperRubyParser::Parser do
|
6
|
-
let(:parser) { RipperRubyParser::Parser.new }
|
7
|
-
describe "#parse" do
|
8
|
-
it "returns an s-expression" do
|
9
|
-
result = parser.parse "foo"
|
10
|
-
_(result).must_be_instance_of Sexp
|
11
|
-
end
|
12
|
-
|
13
|
-
describe "for an empty program" do
|
14
|
-
it "returns nil" do
|
15
|
-
_("").must_be_parsed_as nil
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
describe "for a class declaration" do
|
20
|
-
it "works with a namespaced class name" do
|
21
|
-
_("class Foo::Bar; end")
|
22
|
-
.must_be_parsed_as s(:class,
|
23
|
-
s(:colon2, s(:const, :Foo), :Bar),
|
24
|
-
nil)
|
25
|
-
end
|
26
|
-
|
27
|
-
it "works for singleton classes" do
|
28
|
-
_("class << self; end").must_be_parsed_as s(:sclass, s(:self))
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
describe "for a module declaration" do
|
33
|
-
it "works with a simple module name" do
|
34
|
-
_("module Foo; end")
|
35
|
-
.must_be_parsed_as s(:module, :Foo)
|
36
|
-
end
|
37
|
-
|
38
|
-
it "works with a namespaced module name" do
|
39
|
-
_("module Foo::Bar; end")
|
40
|
-
.must_be_parsed_as s(:module,
|
41
|
-
s(:colon2, s(:const, :Foo), :Bar))
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
describe "for empty parentheses" do
|
46
|
-
it "works with lone ()" do
|
47
|
-
_("()").must_be_parsed_as s(:nil)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
describe "for a begin..end block" do
|
52
|
-
it "works with no statements" do
|
53
|
-
_("begin; end")
|
54
|
-
.must_be_parsed_as s(:nil)
|
55
|
-
end
|
56
|
-
|
57
|
-
it "works with one statement" do
|
58
|
-
_("begin; foo; end")
|
59
|
-
.must_be_parsed_as s(:call, nil, :foo)
|
60
|
-
end
|
61
|
-
|
62
|
-
it "works with multiple statements" do
|
63
|
-
_("begin; foo; bar; end")
|
64
|
-
.must_be_parsed_as s(:block,
|
65
|
-
s(:call, nil, :foo),
|
66
|
-
s(:call, nil, :bar))
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
describe "for arguments" do
|
71
|
-
it "works for a simple case with splat" do
|
72
|
-
_("foo *bar")
|
73
|
-
.must_be_parsed_as s(:call,
|
74
|
-
nil,
|
75
|
-
:foo,
|
76
|
-
s(:splat, s(:call, nil, :bar)))
|
77
|
-
end
|
78
|
-
|
79
|
-
it "works for a multi-argument case with splat" do
|
80
|
-
_("foo bar, *baz")
|
81
|
-
.must_be_parsed_as s(:call,
|
82
|
-
nil,
|
83
|
-
:foo,
|
84
|
-
s(:call, nil, :bar),
|
85
|
-
s(:splat, s(:call, nil, :baz)))
|
86
|
-
end
|
87
|
-
|
88
|
-
it "works for a simple case passing a block" do
|
89
|
-
_("foo &bar")
|
90
|
-
.must_be_parsed_as s(:call, nil, :foo,
|
91
|
-
s(:block_pass,
|
92
|
-
s(:call, nil, :bar)))
|
93
|
-
end
|
94
|
-
|
95
|
-
it "works for a bare hash" do
|
96
|
-
_("foo bar => baz")
|
97
|
-
.must_be_parsed_as s(:call, nil, :foo,
|
98
|
-
s(:hash,
|
99
|
-
s(:call, nil, :bar),
|
100
|
-
s(:call, nil, :baz)))
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
describe "for the __ENCODING__ keyword" do
|
105
|
-
it "evaluates to the equivalent of Encoding::UTF_8" do
|
106
|
-
_("__ENCODING__")
|
107
|
-
.must_be_parsed_as s(:colon2, s(:const, :Encoding), :UTF_8)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
describe "for the __FILE__ keyword" do
|
112
|
-
describe "when not passing a file name" do
|
113
|
-
it "creates a string sexp with value '(string)'" do
|
114
|
-
_("__FILE__")
|
115
|
-
.must_be_parsed_as s(:str, "(string)")
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
describe "when passing a file name" do
|
120
|
-
it "creates a string sexp with the file name" do
|
121
|
-
result = parser.parse "__FILE__", "foo"
|
122
|
-
_(result).must_equal s(:str, "foo")
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
describe "for the __LINE__ keyword" do
|
128
|
-
it "creates a literal sexp with value of the line number" do
|
129
|
-
_("__LINE__")
|
130
|
-
.must_be_parsed_as s(:lit, 1)
|
131
|
-
_("\n__LINE__")
|
132
|
-
.must_be_parsed_as s(:lit, 2)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
describe "for the END keyword" do
|
137
|
-
it "converts to a :postexe iterator" do
|
138
|
-
_("END { foo }")
|
139
|
-
.must_be_parsed_as s(:iter, s(:postexe), 0, s(:call, nil, :foo))
|
140
|
-
end
|
141
|
-
|
142
|
-
it "works with an empty block" do
|
143
|
-
_("END { }")
|
144
|
-
.must_be_parsed_as s(:iter, s(:postexe), 0)
|
145
|
-
end
|
146
|
-
|
147
|
-
it "assigns correct line numbers" do
|
148
|
-
_("END {\nfoo\n}")
|
149
|
-
.must_be_parsed_as s(:iter,
|
150
|
-
s(:postexe).line(1), 0,
|
151
|
-
s(:call, nil, :foo).line(2)).line(1),
|
152
|
-
with_line_numbers: true
|
153
|
-
end
|
154
|
-
|
155
|
-
it "assigns correct line numbers to a embedded begin block" do
|
156
|
-
_("END {\nbegin\nfoo\nend\n}")
|
157
|
-
.must_be_parsed_as s(:iter,
|
158
|
-
s(:postexe).line(1), 0,
|
159
|
-
s(:call, nil, :foo).line(3)).line(1),
|
160
|
-
with_line_numbers: true
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
describe "for the BEGIN keyword" do
|
165
|
-
it "converts to a :preexe iterator" do
|
166
|
-
_("BEGIN { foo }")
|
167
|
-
.must_be_parsed_as s(:iter, s(:preexe), 0, s(:call, nil, :foo))
|
168
|
-
end
|
169
|
-
|
170
|
-
it "works with an empty block" do
|
171
|
-
_("BEGIN { }")
|
172
|
-
.must_be_parsed_as s(:iter, s(:preexe), 0)
|
173
|
-
end
|
174
|
-
|
175
|
-
it "assigns correct line numbers" do
|
176
|
-
_("BEGIN {\nfoo\n}")
|
177
|
-
.must_be_parsed_as s(:iter,
|
178
|
-
s(:preexe).line(1), 0,
|
179
|
-
s(:call, nil, :foo).line(2)).line(1),
|
180
|
-
with_line_numbers: true
|
181
|
-
end
|
182
|
-
|
183
|
-
it "assigns correct line numbers to a embedded begin block" do
|
184
|
-
_("BEGIN {\nbegin\nfoo\nend\n}")
|
185
|
-
.must_be_parsed_as s(:iter,
|
186
|
-
s(:preexe).line(1), 0,
|
187
|
-
s(:begin,
|
188
|
-
s(:call, nil, :foo).line(3)).line(2)).line(1),
|
189
|
-
with_line_numbers: true
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
describe "for constant lookups" do
|
194
|
-
it "works when explicitely starting from the root namespace" do
|
195
|
-
_("::Foo")
|
196
|
-
.must_be_parsed_as s(:colon3, :Foo)
|
197
|
-
end
|
198
|
-
|
199
|
-
it "works with a three-level constant lookup" do
|
200
|
-
_("Foo::Bar::Baz")
|
201
|
-
.must_be_parsed_as s(:colon2,
|
202
|
-
s(:colon2, s(:const, :Foo), :Bar),
|
203
|
-
:Baz)
|
204
|
-
end
|
205
|
-
|
206
|
-
it "works looking up a constant in a non-constant" do
|
207
|
-
_("foo::Bar").must_be_parsed_as s(:colon2,
|
208
|
-
s(:call, nil, :foo),
|
209
|
-
:Bar)
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
describe "for variable references" do
|
214
|
-
it "works for self" do
|
215
|
-
_("self")
|
216
|
-
.must_be_parsed_as s(:self)
|
217
|
-
end
|
218
|
-
|
219
|
-
it "works for instance variables" do
|
220
|
-
_("@foo")
|
221
|
-
.must_be_parsed_as s(:ivar, :@foo)
|
222
|
-
end
|
223
|
-
|
224
|
-
it "works for global variables" do
|
225
|
-
_("$foo")
|
226
|
-
.must_be_parsed_as s(:gvar, :$foo)
|
227
|
-
end
|
228
|
-
|
229
|
-
it "works for regexp match references" do
|
230
|
-
_("$1")
|
231
|
-
.must_be_parsed_as s(:nth_ref, 1)
|
232
|
-
end
|
233
|
-
|
234
|
-
specify { _("$'").must_be_parsed_as s(:back_ref, :"'") }
|
235
|
-
specify { _("$&").must_be_parsed_as s(:back_ref, :"&") }
|
236
|
-
|
237
|
-
it "works for class variables" do
|
238
|
-
_("@@foo")
|
239
|
-
.must_be_parsed_as s(:cvar, :@@foo)
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
describe "for expressions" do
|
244
|
-
it "handles assignment inside binary operator expressions" do
|
245
|
-
_("foo + (bar = baz)")
|
246
|
-
.must_be_parsed_as s(:call,
|
247
|
-
s(:call, nil, :foo),
|
248
|
-
:+,
|
249
|
-
s(:lasgn,
|
250
|
-
:bar,
|
251
|
-
s(:call, nil, :baz)))
|
252
|
-
end
|
253
|
-
|
254
|
-
it "handles assignment inside unary operator expressions" do
|
255
|
-
_("+(foo = bar)")
|
256
|
-
.must_be_parsed_as s(:call,
|
257
|
-
s(:lasgn, :foo, s(:call, nil, :bar)),
|
258
|
-
:+@)
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
# Note: differences in the handling of comments are not caught by Sexp's
|
263
|
-
# implementation of equality.
|
264
|
-
describe "for comments" do
|
265
|
-
it "handles method comments" do
|
266
|
-
result = parser.parse "# Foo\ndef foo; end"
|
267
|
-
_(result).must_equal s(:defn,
|
268
|
-
:foo,
|
269
|
-
s(:args), s(:nil))
|
270
|
-
_(result.comments).must_equal "# Foo\n"
|
271
|
-
end
|
272
|
-
|
273
|
-
it "handles comments for methods with explicit receiver" do
|
274
|
-
result = parser.parse "# Foo\ndef foo.bar; end"
|
275
|
-
_(result).must_equal s(:defs,
|
276
|
-
s(:call, nil, :foo),
|
277
|
-
:bar,
|
278
|
-
s(:args),
|
279
|
-
s(:nil))
|
280
|
-
_(result.comments).must_equal "# Foo\n"
|
281
|
-
end
|
282
|
-
|
283
|
-
it "matches comments to the correct entity" do
|
284
|
-
result = parser.parse "# Foo\nclass Foo\n# Bar\ndef bar\nend\nend"
|
285
|
-
_(result).must_equal s(:class, :Foo, nil,
|
286
|
-
s(:defn, :bar,
|
287
|
-
s(:args), s(:nil)))
|
288
|
-
_(result.comments).must_equal "# Foo\n"
|
289
|
-
defn = result[3]
|
290
|
-
_(defn.sexp_type).must_equal :defn
|
291
|
-
_(defn.comments).must_equal "# Bar\n"
|
292
|
-
end
|
293
|
-
|
294
|
-
it "combines multi-line comments" do
|
295
|
-
result = parser.parse "# Foo\n# Bar\ndef foo; end"
|
296
|
-
_(result).must_equal s(:defn,
|
297
|
-
:foo,
|
298
|
-
s(:args), s(:nil))
|
299
|
-
_(result.comments).must_equal "# Foo\n# Bar\n"
|
300
|
-
end
|
301
|
-
|
302
|
-
it "drops comments inside method bodies" do
|
303
|
-
result = parser.parse <<-RUBY
|
304
|
-
# Foo
|
305
|
-
class Foo
|
306
|
-
# foo
|
307
|
-
def foo
|
308
|
-
bar # this is dropped
|
309
|
-
end
|
310
|
-
|
311
|
-
# bar
|
312
|
-
def bar
|
313
|
-
baz
|
314
|
-
end
|
315
|
-
end
|
316
|
-
RUBY
|
317
|
-
_(result).must_equal s(:class,
|
318
|
-
:Foo,
|
319
|
-
nil,
|
320
|
-
s(:defn, :foo, s(:args), s(:call, nil, :bar)),
|
321
|
-
s(:defn, :bar, s(:args), s(:call, nil, :baz)))
|
322
|
-
_(result.comments).must_equal "# Foo\n"
|
323
|
-
_(result[3].comments).must_equal "# foo\n"
|
324
|
-
_(result[4].comments).must_equal "# bar\n"
|
325
|
-
end
|
326
|
-
|
327
|
-
it "handles use of singleton class inside methods" do
|
328
|
-
result = parser.parse "# Foo\ndef bar\nclass << self\nbaz\nend\nend"
|
329
|
-
_(result).must_equal s(:defn,
|
330
|
-
:bar,
|
331
|
-
s(:args),
|
332
|
-
s(:sclass, s(:self),
|
333
|
-
s(:call, nil, :baz)))
|
334
|
-
_(result.comments).must_equal "# Foo\n"
|
335
|
-
end
|
336
|
-
|
337
|
-
# TODO: Prefer assigning comment to the BEGIN instead
|
338
|
-
it "assigns comments on BEGIN blocks to the following item" do
|
339
|
-
result = parser.parse "# Bar\nBEGIN { }\n# Foo\nclass Bar\n# foo\ndef foo; end\nend"
|
340
|
-
_(result).must_equal s(:block,
|
341
|
-
s(:iter, s(:preexe), 0),
|
342
|
-
s(:class, :Bar, nil,
|
343
|
-
s(:defn, :foo, s(:args), s(:nil))))
|
344
|
-
_(result[2].comments).must_equal "# Bar\n# Foo\n"
|
345
|
-
_(result[2][3].comments).must_equal "# foo\n"
|
346
|
-
end
|
347
|
-
|
348
|
-
it "assigns comments on multiple BEGIN blocks to the following item" do
|
349
|
-
result = parser.parse "# Bar\nBEGIN { }\n# Baz\nBEGIN { }\n# Foo\ndef foo; end"
|
350
|
-
_(result).must_equal s(:block,
|
351
|
-
s(:iter, s(:preexe), 0),
|
352
|
-
s(:iter, s(:preexe), 0),
|
353
|
-
s(:defn, :foo, s(:args), s(:nil)))
|
354
|
-
_(result[3].comments).must_equal "# Bar\n# Baz\n# Foo\n"
|
355
|
-
end
|
356
|
-
|
357
|
-
it "assigns comments on BEGIN blocks to the first following item" do
|
358
|
-
result = parser.parse "# Bar\nBEGIN { }\n# Baz\nBEGIN { }\n# Foo\ndef foo; end"
|
359
|
-
_(result).must_equal s(:block,
|
360
|
-
s(:iter, s(:preexe), 0),
|
361
|
-
s(:iter, s(:preexe), 0),
|
362
|
-
s(:defn, :foo, s(:args), s(:nil)))
|
363
|
-
_(result[3].comments).must_equal "# Bar\n# Baz\n# Foo\n"
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
# Note: differences in the handling of line numbers are not caught by
|
368
|
-
# Sexp's implementation of equality.
|
369
|
-
describe "assigning line numbers" do
|
370
|
-
it "works for a plain method call" do
|
371
|
-
result = parser.parse "foo"
|
372
|
-
_(result.line).must_equal 1
|
373
|
-
end
|
374
|
-
|
375
|
-
it "works for a method call with parentheses" do
|
376
|
-
result = parser.parse "foo()"
|
377
|
-
_(result.line).must_equal 1
|
378
|
-
end
|
379
|
-
|
380
|
-
it "works for a method call with receiver" do
|
381
|
-
result = parser.parse "foo.bar"
|
382
|
-
_(result.line).must_equal 1
|
383
|
-
end
|
384
|
-
|
385
|
-
it "works for a method call with receiver and arguments" do
|
386
|
-
result = parser.parse "foo.bar baz"
|
387
|
-
_(result.line).must_equal 1
|
388
|
-
end
|
389
|
-
|
390
|
-
it "works for a method call with arguments" do
|
391
|
-
result = parser.parse "foo bar"
|
392
|
-
_(result.line).must_equal 1
|
393
|
-
end
|
394
|
-
|
395
|
-
it "works for a block with two lines" do
|
396
|
-
result = parser.parse "foo\nbar\n"
|
397
|
-
_(result.sexp_type).must_equal :block
|
398
|
-
_(result[1].line).must_equal 1
|
399
|
-
_(result[2].line).must_equal 2
|
400
|
-
_(result.line).must_equal 1
|
401
|
-
end
|
402
|
-
|
403
|
-
it "works for a constant reference" do
|
404
|
-
result = parser.parse "Foo"
|
405
|
-
_(result.line).must_equal 1
|
406
|
-
end
|
407
|
-
|
408
|
-
it "works for an instance variable" do
|
409
|
-
result = parser.parse "@foo"
|
410
|
-
_(result.line).must_equal 1
|
411
|
-
end
|
412
|
-
|
413
|
-
it "works for a global variable" do
|
414
|
-
result = parser.parse "$foo"
|
415
|
-
_(result.line).must_equal 1
|
416
|
-
end
|
417
|
-
|
418
|
-
it "works for a class variable" do
|
419
|
-
result = parser.parse "@@foo"
|
420
|
-
_(result.line).must_equal 1
|
421
|
-
end
|
422
|
-
|
423
|
-
it "works for a local variable" do
|
424
|
-
_("foo = bar\nfoo\n")
|
425
|
-
.must_be_parsed_as s(:block,
|
426
|
-
s(:lasgn, :foo, s(:call, nil, :bar).line(1)).line(1),
|
427
|
-
s(:lvar, :foo).line(2)).line(1),
|
428
|
-
with_line_numbers: true
|
429
|
-
end
|
430
|
-
|
431
|
-
it "works for an integer literal" do
|
432
|
-
result = parser.parse "42"
|
433
|
-
_(result.line).must_equal 1
|
434
|
-
end
|
435
|
-
|
436
|
-
it "works for a float literal" do
|
437
|
-
result = parser.parse "3.14"
|
438
|
-
_(result.line).must_equal 1
|
439
|
-
end
|
440
|
-
|
441
|
-
it "works for a range literal" do
|
442
|
-
result = parser.parse "0..4"
|
443
|
-
_(result.line).must_equal 1
|
444
|
-
end
|
445
|
-
|
446
|
-
it "works for an exclusive range literal" do
|
447
|
-
result = parser.parse "0...4"
|
448
|
-
_(result.line).must_equal 1
|
449
|
-
end
|
450
|
-
|
451
|
-
it "works for a symbol literal" do
|
452
|
-
result = parser.parse ":foo"
|
453
|
-
_(result.line).must_equal 1
|
454
|
-
end
|
455
|
-
|
456
|
-
it "works for a keyword-like symbol literal" do
|
457
|
-
result = parser.parse ":and"
|
458
|
-
_(result.line).must_equal 1
|
459
|
-
end
|
460
|
-
|
461
|
-
it "works for a string literal" do
|
462
|
-
result = parser.parse '"foo"'
|
463
|
-
_(result.line).must_equal 1
|
464
|
-
end
|
465
|
-
|
466
|
-
it "works for a backtick string literal" do
|
467
|
-
result = parser.parse "`foo`"
|
468
|
-
_(result.line).must_equal 1
|
469
|
-
end
|
470
|
-
|
471
|
-
it "works for a plain regexp literal" do
|
472
|
-
result = parser.parse "/foo/"
|
473
|
-
_(result.line).must_equal 1
|
474
|
-
end
|
475
|
-
|
476
|
-
it "works for a regular expression back reference" do
|
477
|
-
result = parser.parse "$1"
|
478
|
-
_(result.line).must_equal 1
|
479
|
-
end
|
480
|
-
|
481
|
-
it "works for self" do
|
482
|
-
result = parser.parse "self"
|
483
|
-
_(result.line).must_equal 1
|
484
|
-
end
|
485
|
-
|
486
|
-
it "works for __FILE__" do
|
487
|
-
result = parser.parse "__FILE__"
|
488
|
-
_(result.line).must_equal 1
|
489
|
-
end
|
490
|
-
|
491
|
-
it "works for nil" do
|
492
|
-
result = parser.parse "nil"
|
493
|
-
_(result.line).must_equal 1
|
494
|
-
end
|
495
|
-
|
496
|
-
it "works for a class definition" do
|
497
|
-
result = parser.parse "class Foo; end"
|
498
|
-
_(result.line).must_equal 1
|
499
|
-
end
|
500
|
-
|
501
|
-
it "works for a module definition" do
|
502
|
-
result = parser.parse "module Foo; end"
|
503
|
-
_(result.line).must_equal 1
|
504
|
-
end
|
505
|
-
|
506
|
-
it "works for a method definition" do
|
507
|
-
result = parser.parse "def foo; end"
|
508
|
-
_(result.line).must_equal 1
|
509
|
-
end
|
510
|
-
|
511
|
-
it "assigns line numbers to nested sexps without their own line numbers" do
|
512
|
-
_("foo(bar) do\nnext baz\nend\n")
|
513
|
-
.must_be_parsed_as s(:iter,
|
514
|
-
s(:call, nil, :foo, s(:call, nil, :bar).line(1)).line(1),
|
515
|
-
0,
|
516
|
-
s(:next, s(:call, nil, :baz).line(2)).line(2)).line(1),
|
517
|
-
with_line_numbers: true
|
518
|
-
end
|
519
|
-
|
520
|
-
describe "when a line number is passed" do
|
521
|
-
it "shifts all line numbers as appropriate" do
|
522
|
-
result = parser.parse "foo\nbar\n", "(string)", 3
|
523
|
-
_(result).must_equal s(:block,
|
524
|
-
s(:call, nil, :foo),
|
525
|
-
s(:call, nil, :bar))
|
526
|
-
_(result.line).must_equal 3
|
527
|
-
_(result[1].line).must_equal 3
|
528
|
-
_(result[2].line).must_equal 4
|
529
|
-
end
|
530
|
-
end
|
531
|
-
end
|
532
|
-
end
|
533
|
-
|
534
|
-
describe "#trickle_up_line_numbers" do
|
535
|
-
it "works through several nested levels" do
|
536
|
-
inner = s(:foo)
|
537
|
-
outer = s(:bar, s(:baz, s(:qux, inner)))
|
538
|
-
outer.line = 42
|
539
|
-
parser.send :trickle_down_line_numbers, outer
|
540
|
-
_(inner.line).must_equal 42
|
541
|
-
end
|
542
|
-
end
|
543
|
-
|
544
|
-
describe "#trickle_down_line_numbers" do
|
545
|
-
it "works through several nested levels" do
|
546
|
-
inner = s(:foo)
|
547
|
-
inner.line = 42
|
548
|
-
outer = s(:bar, s(:baz, s(:qux, inner)))
|
549
|
-
parser.send :trickle_up_line_numbers, outer
|
550
|
-
_(outer.line).must_equal 42
|
551
|
-
end
|
552
|
-
end
|
553
|
-
end
|