ripper_ruby_parser 0.0.1 → 0.0.2

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.
@@ -0,0 +1,73 @@
1
+ require File.expand_path('../test_helper.rb', File.dirname(__FILE__))
2
+ require 'ruby_parser'
3
+
4
+ describe "Using RipperRubyParser and RubyParser" do
5
+ def to_comments exp
6
+ inner = exp.map do |sub_exp|
7
+ if sub_exp.is_a? Sexp
8
+ to_comments sub_exp
9
+ else
10
+ sub_exp
11
+ end
12
+ end
13
+
14
+ if exp.comments.nil?
15
+ s(*inner)
16
+ else
17
+ s(:comment, exp.comments, s(*inner))
18
+ end
19
+ end
20
+
21
+ let :newparser do
22
+ RipperRubyParser::Parser.new
23
+ end
24
+
25
+ let :oldparser do
26
+ RubyParser.new
27
+ end
28
+
29
+ describe "for a program with quite some comments" do
30
+ let :program do
31
+ <<-END
32
+ # Foo
33
+ class Foo
34
+ # The foo
35
+ # method
36
+ def foo
37
+ bar # bar
38
+ # internal comment
39
+ end
40
+
41
+ def bar
42
+ baz
43
+ end
44
+ end
45
+ # Quux
46
+ module Qux
47
+ class Quux
48
+ def bar
49
+ end
50
+ def baz
51
+ end
52
+ end
53
+ end
54
+ END
55
+ end
56
+
57
+ let :original do
58
+ oldparser.parse program
59
+ end
60
+
61
+ let :imitation do
62
+ newparser.parse program
63
+ end
64
+
65
+ it "gives the same result" do
66
+ imitation.must_equal original
67
+ end
68
+
69
+ it "gives the same result with comments" do
70
+ to_comments(imitation).must_equal to_comments(original)
71
+ end
72
+ end
73
+ end
@@ -52,5 +52,65 @@ describe "Using RipperRubyParser and RubyParser" do
52
52
  end
53
53
  end
54
54
 
55
+ describe "for an example with yield from Reek" do
56
+ let :program do
57
+ 'def fred() yield(3) if block_given?; end'
58
+ end
59
+
60
+ it "gives the same result" do
61
+ original = oldparser.parse program
62
+ imitation = newparser.parse program
63
+
64
+ imitation.must_equal original
65
+ end
66
+ end
67
+
68
+ describe "for an example with floats from Reek" do
69
+ let :program do
70
+ <<-END
71
+ def total_envy
72
+ fred = @item
73
+ total = 0
74
+ total += fred.price
75
+ total += fred.tax
76
+ total *= 1.15
77
+ end
78
+ END
79
+ end
80
+
81
+ it "gives the same result" do
82
+ original = oldparser.parse program
83
+ imitation = newparser.parse program
84
+
85
+ formatted(imitation).must_equal formatted(original)
86
+ end
87
+ end
88
+
89
+ describe "for a example with operators and explicit block parameter from Reek" do
90
+ let :program do
91
+ <<-END
92
+ def parse(arg, argv, &error)
93
+ if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))
94
+ return nil, block, nil
95
+ end
96
+ opt = (val = parse_arg(val, &error))[1]
97
+ val = conv_arg(*val)
98
+ if opt and !arg
99
+ argv.shift
100
+ else
101
+ val[0] = nil
102
+ end
103
+ val
104
+ end
105
+ END
106
+ end
107
+
108
+ it "gives the same result" do
109
+ original = oldparser.parse program
110
+ imitation = newparser.parse program
111
+
112
+ formatted(imitation).must_equal formatted(original)
113
+ end
114
+ end
55
115
  end
56
116
 
@@ -0,0 +1,63 @@
1
+ require File.expand_path('../test_helper.rb', File.dirname(__FILE__))
2
+ require 'ruby_parser'
3
+
4
+ describe "Using RipperRubyParser and RubyParser" do
5
+ def to_line_numbers exp
6
+ exp.map! do |sub_exp|
7
+ if sub_exp.is_a? Sexp
8
+ to_line_numbers sub_exp
9
+ else
10
+ sub_exp
11
+ end
12
+ end
13
+
14
+ if exp.sexp_type == :scope
15
+ exp
16
+ else
17
+ s(:line_number, exp.line, exp)
18
+ end
19
+ end
20
+
21
+ let :newparser do
22
+ RipperRubyParser::Parser.new
23
+ end
24
+
25
+ let :oldparser do
26
+ RubyParser.new
27
+ end
28
+
29
+ describe "for a multi-line program" do
30
+ let :program do
31
+ <<-END
32
+ class Foo
33
+ def foo()
34
+ bar()
35
+ baz(qux)
36
+ end
37
+ end
38
+
39
+ module Bar
40
+ @@baz = {}
41
+ end
42
+ END
43
+ end
44
+
45
+ let :original do
46
+ oldparser.parse program
47
+ end
48
+
49
+ let :imitation do
50
+ newparser.parse program
51
+ end
52
+
53
+ it "gives the same result" do
54
+ imitation.must_equal original
55
+ end
56
+
57
+ it "gives the same result with line numbers" do
58
+ formatted(to_line_numbers(imitation)).
59
+ must_equal formatted(to_line_numbers(original))
60
+ end
61
+ end
62
+ end
63
+
data/test/test_helper.rb CHANGED
@@ -1,5 +1,7 @@
1
- require 'simplecov'
2
- SimpleCov.start
1
+ if ENV["SIMPLECOV"]
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+ end
3
5
  require 'minitest/spec'
4
6
  require 'minitest/autorun'
5
7
 
@@ -7,6 +9,16 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
9
 
8
10
  require 'ripper_ruby_parser'
9
11
 
10
- def formatted exp
11
- exp.to_s.gsub /\), /, "),\n"
12
+ class MiniTest::Unit::TestCase
13
+ def formatted exp
14
+ exp.to_s.gsub(/\), /, "),\n")
15
+ end
16
+
17
+ def suppress_warnings
18
+ old_verbose = $VERBOSE
19
+ $VERBOSE = nil
20
+ result = yield
21
+ $VERBOSE = old_verbose
22
+ result
23
+ end
12
24
  end
@@ -0,0 +1,75 @@
1
+ require File.expand_path('../test_helper.rb', File.dirname(__FILE__))
2
+
3
+ describe RipperRubyParser::CommentingSexpBuilder do
4
+ def parse_with_builder str
5
+ builder = RipperRubyParser::CommentingSexpBuilder.new str
6
+ builder.parse
7
+ end
8
+
9
+ describe "handling comments" do
10
+ it "produces a comment node surrounding a commented def" do
11
+ result = parse_with_builder "# Foo\ndef foo; end"
12
+ result.must_equal [:program,
13
+ [[:comment,
14
+ "# Foo\n",
15
+ [:def,
16
+ [:@ident, "foo", [2, 4]],
17
+ [:params, nil, nil, nil, nil, nil],
18
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]]
19
+ end
20
+
21
+ it "produces a blank comment node surrounding a def that has no comment" do
22
+ result = parse_with_builder "def foo; end"
23
+ result.must_equal [:program,
24
+ [[:comment,
25
+ "",
26
+ [:def,
27
+ [:@ident, "foo", [1, 4]],
28
+ [:params, nil, nil, nil, nil, nil],
29
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]]
30
+ end
31
+
32
+ it "produces a comment node surrounding a commented class" do
33
+ result = parse_with_builder "# Foo\nclass Foo; end"
34
+ result.must_equal [:program,
35
+ [[:comment,
36
+ "# Foo\n",
37
+ [:class,
38
+ [:const_ref, [:@const, "Foo", [2, 6]]],
39
+ nil,
40
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]]
41
+ end
42
+
43
+ it "produce a blank comment node surrounding a class that has no comment" do
44
+ result = parse_with_builder "class Foo; end"
45
+ result.must_equal [:program,
46
+ [[:comment,
47
+ "",
48
+ [:class,
49
+ [:const_ref, [:@const, "Foo", [1, 6]]],
50
+ nil,
51
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]]
52
+ end
53
+
54
+ it "produces a comment node surrounding a commented module" do
55
+ result = parse_with_builder "# Foo\nmodule Foo; end"
56
+ result.must_equal [:program,
57
+ [[:comment,
58
+ "# Foo\n",
59
+ [:module,
60
+ [:const_ref, [:@const, "Foo", [2, 7]]],
61
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]]
62
+ end
63
+
64
+ it "produces a blank comment node surrounding a module that has no comment" do
65
+ result = parse_with_builder "module Foo; end"
66
+ result.must_equal [:program,
67
+ [[:comment,
68
+ "",
69
+ [:module,
70
+ [:const_ref, [:@const, "Foo", [1, 7]]],
71
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]]
72
+ end
73
+ end
74
+ end
75
+
@@ -19,6 +19,25 @@ describe RipperRubyParser::Parser do
19
19
  sexp_p.verify
20
20
  end
21
21
 
22
+ describe "for a class declaration" do
23
+ it "works with a namespaced class name" do
24
+ result = parser.parse "class Foo::Bar; end"
25
+ result.must_equal s(:class,
26
+ s(:colon2, s(:const, :Foo), :Bar),
27
+ nil,
28
+ s(:scope))
29
+ end
30
+ end
31
+
32
+ describe "for a module declaration" do
33
+ it "works with a namespaced module name" do
34
+ result = parser.parse "module Foo::Bar; end"
35
+ result.must_equal s(:module,
36
+ s(:colon2, s(:const, :Foo), :Bar),
37
+ s(:scope))
38
+ end
39
+ end
40
+
22
41
  describe "for if" do
23
42
  it "works in the postfix case" do
24
43
  result = parser.parse "foo if bar"
@@ -28,6 +47,14 @@ describe RipperRubyParser::Parser do
28
47
  nil)
29
48
  end
30
49
 
50
+ it "works in the block case" do
51
+ result = parser.parse "if foo; bar; end"
52
+ result.must_equal s(:if,
53
+ s(:call, nil, :foo, s(:arglist)),
54
+ s(:call, nil, :bar, s(:arglist)),
55
+ nil)
56
+ end
57
+
31
58
  it "works with an else clause" do
32
59
  result = parser.parse "if foo; bar; else; baz; end"
33
60
  result.must_equal s(:if,
@@ -132,33 +159,219 @@ describe RipperRubyParser::Parser do
132
159
  result.must_equal s(:return,
133
160
  s(:call, nil, :foo, s(:arglist)))
134
161
  end
162
+
163
+ it "works with a splat argument" do
164
+ result = parser.parse "return *foo"
165
+ result.must_equal s(:return,
166
+ s(:svalue,
167
+ s(:splat,
168
+ s(:call, nil, :foo, s(:arglist)))))
169
+ end
170
+
171
+ it "works with multiple arguments" do
172
+ result = parser.parse "return foo, bar"
173
+ result.must_equal s(:return,
174
+ s(:array,
175
+ s(:call, nil, :foo, s(:arglist)),
176
+ s(:call, nil, :bar, s(:arglist))))
177
+ end
178
+
179
+ it "works with a regular argument and a splat argument" do
180
+ result = parser.parse "return foo, *bar"
181
+ result.must_equal s(:return,
182
+ s(:array,
183
+ s(:call, nil, :foo, s(:arglist)),
184
+ s(:splat,
185
+ s(:call, nil, :bar, s(:arglist)))))
186
+ end
135
187
  end
136
188
 
137
189
  describe "for the until statement" do
138
- it "works with do" do
190
+ it "works in the prefix block case with do" do
139
191
  result = parser.parse "until foo do; bar; end"
140
192
  result.must_equal s(:until,
141
193
  s(:call, nil, :foo, s(:arglist)),
142
194
  s(:call, nil, :bar, s(:arglist)), true)
143
195
  end
144
196
 
145
- it "works without do" do
197
+ it "works in the prefix block case without do" do
146
198
  result = parser.parse "until foo; bar; end"
147
199
  result.must_equal s(:until,
148
200
  s(:call, nil, :foo, s(:arglist)),
149
201
  s(:call, nil, :bar, s(:arglist)), true)
150
202
  end
203
+
204
+ it "works in the single-line postfix case" do
205
+ result = parser.parse "foo until bar"
206
+ result.must_equal s(:until,
207
+ s(:call, nil, :bar, s(:arglist)),
208
+ s(:call, nil, :foo, s(:arglist)), true)
209
+ end
210
+
211
+ it "works in the block postfix case" do
212
+ result = parser.parse "begin; foo; end until bar"
213
+ result.must_equal s(:until,
214
+ s(:call, nil, :bar, s(:arglist)),
215
+ s(:call, nil, :foo, s(:arglist)), false)
216
+ end
151
217
  end
152
218
 
153
- describe "for identifiers" do
154
- it "works for an ivar" do
155
- result = parser.parse "@foo"
156
- result.must_equal s(:ivar, :@foo)
219
+ describe "for the while statement" do
220
+ it "works with do" do
221
+ result = parser.parse "while foo do; bar; end"
222
+ result.must_equal s(:while,
223
+ s(:call, nil, :foo, s(:arglist)),
224
+ s(:call, nil, :bar, s(:arglist)), true)
157
225
  end
158
226
 
159
- it "works for self" do
160
- result = parser.parse "self"
161
- result.must_equal s(:self)
227
+ it "works without do" do
228
+ result = parser.parse "while foo; bar; end"
229
+ result.must_equal s(:while,
230
+ s(:call, nil, :foo, s(:arglist)),
231
+ s(:call, nil, :bar, s(:arglist)), true)
232
+ end
233
+ end
234
+
235
+ describe "for the for statement" do
236
+ it "works with do" do
237
+ result = suppress_warnings {
238
+ parser.parse "for foo in bar do; baz; end" }
239
+ result.must_equal s(:for,
240
+ s(:call, nil, :bar, s(:arglist)),
241
+ s(:lasgn, :foo),
242
+ s(:call, nil, :baz, s(:arglist)))
243
+ end
244
+
245
+ it "works without do" do
246
+ result = suppress_warnings {
247
+ parser.parse "for foo in bar; baz; end" }
248
+ result.must_equal s(:for,
249
+ s(:call, nil, :bar, s(:arglist)),
250
+ s(:lasgn, :foo),
251
+ s(:call, nil, :baz, s(:arglist)))
252
+ end
253
+ end
254
+
255
+ describe "for a begin..end block" do
256
+ it "works with no statements" do
257
+ result = parser.parse "begin; end"
258
+ result.must_equal s(:nil)
259
+ end
260
+
261
+ it "works with one statement" do
262
+ result = parser.parse "begin; foo; end"
263
+ result.must_equal s(:call, nil, :foo, s(:arglist))
264
+ end
265
+
266
+ it "works with multiple statements" do
267
+ result = parser.parse "begin; foo; bar; end"
268
+ result.must_equal s(:block,
269
+ s(:call, nil, :foo, s(:arglist)),
270
+ s(:call, nil, :bar, s(:arglist)))
271
+ end
272
+ end
273
+
274
+ describe "for the rescue statement" do
275
+ it "works with single statement main and rescue bodies" do
276
+ result = parser.parse "begin; foo; rescue; bar; end"
277
+ result.must_equal s(:rescue,
278
+ s(:call, nil, :foo, s(:arglist)),
279
+ s(:resbody,
280
+ s(:array),
281
+ s(:call, nil, :bar, s(:arglist))))
282
+ end
283
+
284
+ it "works with multi-statement main and rescue bodies" do
285
+ result = parser.parse "begin; foo; bar; rescue; baz; qux; end"
286
+ result.must_equal s(:rescue,
287
+ s(:block,
288
+ s(:call, nil, :foo, s(:arglist)),
289
+ s(:call, nil, :bar, s(:arglist))),
290
+ s(:resbody,
291
+ s(:array),
292
+ s(:block,
293
+ s(:call, nil, :baz, s(:arglist)),
294
+ s(:call, nil, :qux, s(:arglist)))))
295
+ end
296
+
297
+ it "works with assignment to an error variable" do
298
+ result = suppress_warnings {
299
+ parser.parse "begin; foo; rescue => e; bar; end" }
300
+ result.must_equal s(:rescue,
301
+ s(:call, nil, :foo, s(:arglist)),
302
+ s(:resbody,
303
+ s(:array, s(:lasgn, :e, s(:gvar, :$!))),
304
+ s(:call, nil, :bar, s(:arglist))))
305
+ end
306
+
307
+ it "works with filtering of the exception type" do
308
+ result = parser.parse "begin; foo; rescue Bar; baz; end"
309
+ result.must_equal s(:rescue,
310
+ s(:call, nil, :foo, s(:arglist)),
311
+ s(:resbody,
312
+ s(:array, s(:const, :Bar)),
313
+ s(:call, nil, :baz, s(:arglist))))
314
+ end
315
+
316
+ it "works with filtering of the exception type and assignment to an error variable" do
317
+ result = suppress_warnings {
318
+ parser.parse "begin; foo; rescue Bar => e; baz; end" }
319
+ result.must_equal s(:rescue,
320
+ s(:call, nil, :foo, s(:arglist)),
321
+ s(:resbody,
322
+ s(:array,
323
+ s(:const, :Bar),
324
+ s(:lasgn, :e, s(:gvar, :$!))),
325
+ s(:call, nil, :baz, s(:arglist))))
326
+ end
327
+
328
+ it "works rescuing multiple exception types" do
329
+ result = parser.parse "begin; foo; rescue Bar, Baz; qux; end"
330
+ result.must_equal s(:rescue,
331
+ s(:call, nil, :foo, s(:arglist)),
332
+ s(:resbody,
333
+ s(:array, s(:const, :Bar), s(:const, :Baz)),
334
+ s(:call, nil, :qux, s(:arglist))))
335
+ end
336
+
337
+ it "works in the postfix case" do
338
+ result = parser.parse "foo rescue bar"
339
+ result.must_equal s(:rescue,
340
+ s(:call, nil, :foo, s(:arglist)),
341
+ s(:resbody,
342
+ s(:array),
343
+ s(:call, nil, :bar, s(:arglist))))
344
+ end
345
+ end
346
+
347
+ describe "for the ensure statement" do
348
+ it "works with single statement main and ensure bodies" do
349
+ result = parser.parse "begin; foo; ensure; bar; end"
350
+ result.must_equal s(:ensure,
351
+ s(:call, nil, :foo, s(:arglist)),
352
+ s(:call, nil, :bar, s(:arglist)))
353
+ end
354
+
355
+ it "works with multi-statement main and ensure bodies" do
356
+ result = parser.parse "begin; foo; bar; ensure; baz; qux; end"
357
+ result.must_equal s(:ensure,
358
+ s(:block,
359
+ s(:call, nil, :foo, s(:arglist)),
360
+ s(:call, nil, :bar, s(:arglist))),
361
+ s(:block,
362
+ s(:call, nil, :baz, s(:arglist)),
363
+ s(:call, nil, :qux, s(:arglist))))
364
+ end
365
+
366
+ it "works together with rescue" do
367
+ result = parser.parse "begin; foo; rescue; bar; ensure; baz; end"
368
+ result.must_equal s(:ensure,
369
+ s(:rescue,
370
+ s(:call, nil, :foo, s(:arglist)),
371
+ s(:resbody,
372
+ s(:array),
373
+ s(:call, nil, :bar, s(:arglist)))),
374
+ s(:call, nil, :baz, s(:arglist)))
162
375
  end
163
376
  end
164
377
 
@@ -181,6 +394,14 @@ describe RipperRubyParser::Parser do
181
394
  s(:call, nil, :bar, s(:arglist)),
182
395
  s(:splat, s(:call, nil, :baz, s(:arglist)))))
183
396
  end
397
+
398
+ it "works for a simple case passing a block" do
399
+ result = parser.parse "foo &bar"
400
+ result.must_equal s(:call, nil, :foo,
401
+ s(:arglist,
402
+ s(:block_pass,
403
+ s(:call, nil, :bar, s(:arglist)))))
404
+ end
184
405
  end
185
406
 
186
407
  describe "for array literals" do
@@ -226,6 +447,13 @@ describe RipperRubyParser::Parser do
226
447
  end
227
448
  end
228
449
 
450
+ describe "for number literals" do
451
+ it "works for floats" do
452
+ result = parser.parse "3.14"
453
+ result.must_equal s(:lit, 3.14)
454
+ end
455
+ end
456
+
229
457
  describe "for collection indexing" do
230
458
  it "works in the simple case" do
231
459
  result = parser.parse "foo[bar]"
@@ -273,6 +501,81 @@ describe RipperRubyParser::Parser do
273
501
  s(:args, :bar),
274
502
  s(:scope, s(:block, s(:nil))))
275
503
  end
504
+
505
+ it "works with a simple splat" do
506
+ result = parser.parse "def foo *bar; end"
507
+ result.must_equal s(:defn,
508
+ :foo,
509
+ s(:args, :"*bar"),
510
+ s(:scope, s(:block, s(:nil))))
511
+ end
512
+
513
+ it "works with a regular argument plus splat" do
514
+ result = parser.parse "def foo bar, *baz; end"
515
+ result.must_equal s(:defn,
516
+ :foo,
517
+ s(:args, :bar, :"*baz"),
518
+ s(:scope, s(:block, s(:nil))))
519
+ end
520
+
521
+ it "works with a nameless splat" do
522
+ result = parser.parse "def foo *; end"
523
+ result.must_equal s(:defn,
524
+ :foo,
525
+ s(:args, :"*"),
526
+ s(:scope, s(:block, s(:nil))))
527
+ end
528
+
529
+ it "works for a simple case with explicit block parameter" do
530
+ result = parser.parse "def foo &bar; end"
531
+ result.must_equal s(:defn,
532
+ :foo,
533
+ s(:args, :"&bar"),
534
+ s(:scope, s(:block, s(:nil))))
535
+ end
536
+
537
+ it "works with a regular argument plus explicit block parameter" do
538
+ result = parser.parse "def foo bar, &baz; end"
539
+ result.must_equal s(:defn,
540
+ :foo,
541
+ s(:args, :bar, :"&baz"),
542
+ s(:scope, s(:block, s(:nil))))
543
+ end
544
+
545
+ it "works with a argument with default value plus explicit block parameter" do
546
+ result = parser.parse "def foo bar=1, &baz; end"
547
+ result.must_equal s(:defn,
548
+ :foo,
549
+ s(:args,
550
+ :bar, :"&baz",
551
+ s(:block,
552
+ s(:lasgn, :bar, s(:lit, 1)))),
553
+ s(:scope, s(:block, s(:nil))))
554
+ end
555
+
556
+ it "works with a splat plus explicit block parameter" do
557
+ result = parser.parse "def foo *bar, &baz; end"
558
+ result.must_equal s(:defn,
559
+ :foo,
560
+ s(:args, :"*bar", :"&baz"),
561
+ s(:scope, s(:block, s(:nil))))
562
+ end
563
+
564
+ it "works with an argument with default value plus splat" do
565
+ result = parser.parse "def foo bar=1, *baz; end"
566
+ result.must_equal s(:defn,
567
+ :foo,
568
+ s(:args, :bar, :"*baz",
569
+ s(:block,
570
+ s(:lasgn, :bar, s(:lit, 1)))),
571
+ s(:scope, s(:block, s(:nil))))
572
+ end
573
+
574
+ it "works when the method name is an operator" do
575
+ result = parser.parse "def +; end"
576
+ result.must_equal s(:defn, :+, s(:args),
577
+ s(:scope, s(:block, s(:nil))))
578
+ end
276
579
  end
277
580
 
278
581
  describe "for method calls" do
@@ -289,10 +592,20 @@ describe RipperRubyParser::Parser do
289
592
  s(:arglist, s(:call, nil, :bar, s(:arglist))))
290
593
  end
291
594
 
595
+ it "works with an empty parameter list and no brackets" do
596
+ result = parser.parse "foo"
597
+ result.must_equal s(:call, nil, :foo, s(:arglist))
598
+ end
599
+
292
600
  it "works with brackets around an empty parameter list" do
293
601
  result = parser.parse "foo()"
294
602
  result.must_equal s(:call, nil, :foo, s(:arglist))
295
603
  end
604
+
605
+ it "works for methods ending in a question mark" do
606
+ result = parser.parse "foo?"
607
+ result.must_equal s(:call, nil, :foo?, s(:arglist))
608
+ end
296
609
  end
297
610
 
298
611
  describe "with a reciever" do
@@ -351,6 +664,49 @@ describe RipperRubyParser::Parser do
351
664
  end
352
665
  end
353
666
 
667
+ describe "for yield" do
668
+ it "works with no arguments and no brackets" do
669
+ result = parser.parse "yield"
670
+ result.must_equal s(:yield)
671
+ end
672
+
673
+ it "works with brackets but no arguments" do
674
+ result = parser.parse "yield()"
675
+ result.must_equal s(:yield)
676
+ end
677
+
678
+ it "works with one argument and no brackets" do
679
+ result = parser.parse "yield foo"
680
+ result.must_equal s(:yield, s(:call, nil, :foo, s(:arglist)))
681
+ end
682
+
683
+ it "works with one argument and brackets" do
684
+ result = parser.parse "yield(foo)"
685
+ result.must_equal s(:yield, s(:call, nil, :foo, s(:arglist)))
686
+ end
687
+
688
+ it "works with multiple arguments and no brackets" do
689
+ result = parser.parse "yield foo, bar"
690
+ result.must_equal s(:yield,
691
+ s(:call, nil, :foo, s(:arglist)),
692
+ s(:call, nil, :bar, s(:arglist)))
693
+ end
694
+
695
+ it "works with multiple arguments and brackets" do
696
+ result = parser.parse "yield(foo, bar)"
697
+ result.must_equal s(:yield,
698
+ s(:call, nil, :foo, s(:arglist)),
699
+ s(:call, nil, :bar, s(:arglist)))
700
+ end
701
+
702
+ it "works with splat" do
703
+ result = parser.parse "yield foo, *bar"
704
+ result.must_equal s(:yield,
705
+ s(:call, nil, :foo, s(:arglist)),
706
+ s(:splat, s(:call, nil, :bar, s(:arglist))))
707
+ end
708
+ end
709
+
354
710
  describe "for literals" do
355
711
  it "works for symbols" do
356
712
  result = parser.parse ":foo"
@@ -440,10 +796,17 @@ describe RipperRubyParser::Parser do
440
796
  result.must_equal s(:lit, /\)\n\\/)
441
797
  end
442
798
 
443
- it "works for symbols created by prefixing a simple string with :" do
799
+ it "works for simple dsyms" do
444
800
  result = parser.parse ':"foo"'
445
801
  result.must_equal s(:lit, :foo)
446
802
  end
803
+
804
+ it "works for dsyms with interpolations" do
805
+ result = parser.parse ':"foo#{bar}"'
806
+ result.must_equal s(:dsym,
807
+ "foo",
808
+ s(:evstr, s(:call, nil, :bar, s(:arglist))))
809
+ end
447
810
  end
448
811
 
449
812
  describe "for the __FILE__ keyword" do
@@ -458,9 +821,26 @@ describe RipperRubyParser::Parser do
458
821
  result = parser.parse "::Foo"
459
822
  result.must_equal s(:colon3, :Foo)
460
823
  end
824
+
825
+ it "works with a three-level constant lookup" do
826
+ result = parser.parse "Foo::Bar::Baz"
827
+ result.must_equal s(:colon2,
828
+ s(:colon2, s(:const, :Foo), :Bar),
829
+ :Baz)
830
+ end
461
831
  end
462
832
 
463
833
  describe "for variable references" do
834
+ it "works for self" do
835
+ result = parser.parse "self"
836
+ result.must_equal s(:self)
837
+ end
838
+
839
+ it "works for instance variables" do
840
+ result = parser.parse "@foo"
841
+ result.must_equal s(:ivar, :@foo)
842
+ end
843
+
464
844
  it "works for global variables" do
465
845
  result = parser.parse "$foo"
466
846
  result.must_equal s(:gvar, :$foo)
@@ -470,6 +850,11 @@ describe RipperRubyParser::Parser do
470
850
  result = parser.parse "$1"
471
851
  result.must_equal s(:nth_ref, 1)
472
852
  end
853
+
854
+ it "works for class variables" do
855
+ result = parser.parse "@@foo"
856
+ result.must_equal s(:cvar, :@@foo)
857
+ end
473
858
  end
474
859
 
475
860
  describe "for single assignment" do
@@ -496,11 +881,33 @@ describe RipperRubyParser::Parser do
496
881
  s(:call, nil, :bar, s(:arglist)),
497
882
  s(:call, nil, :baz, s(:arglist))))
498
883
  end
884
+
885
+ it "works when assigning to an attribute" do
886
+ result = parser.parse "foo.bar = baz"
887
+ result.must_equal s(:attrasgn,
888
+ s(:call, nil, :foo, s(:arglist)),
889
+ :bar=,
890
+ s(:arglist, s(:call, nil, :baz, s(:arglist))))
891
+ end
892
+
893
+ it "works when assigning to a class variable" do
894
+ result = parser.parse "@@foo = bar"
895
+ result.must_equal s(:cvdecl,
896
+ :@@foo,
897
+ s(:call, nil, :bar, s(:arglist)))
898
+ end
899
+
900
+ it "works when assigning to a global variable" do
901
+ result = parser.parse "$foo = bar"
902
+ result.must_equal s(:gasgn,
903
+ :$foo,
904
+ s(:call, nil, :bar, s(:arglist)))
905
+ end
499
906
  end
500
907
 
501
908
  describe "for operator assignment" do
502
909
  it "works with +=" do
503
- result = parser.parse "foo += bar"
910
+ result = suppress_warnings { parser.parse "foo += bar" }
504
911
  result.must_equal s(:lasgn,
505
912
  :foo,
506
913
  s(:call,
@@ -510,7 +917,7 @@ describe RipperRubyParser::Parser do
510
917
  end
511
918
 
512
919
  it "works with -=" do
513
- result = parser.parse "foo -= bar"
920
+ result = suppress_warnings { parser.parse "foo -= bar" }
514
921
  result.must_equal s(:lasgn,
515
922
  :foo,
516
923
  s(:call,
@@ -519,6 +926,14 @@ describe RipperRubyParser::Parser do
519
926
  s(:arglist, s(:call, nil, :bar, s(:arglist)))))
520
927
  end
521
928
 
929
+ it "works with ||=" do
930
+ result = suppress_warnings { parser.parse "foo ||= bar" }
931
+ result.must_equal s(:op_asgn_or,
932
+ s(:lvar, :foo),
933
+ s(:lasgn, :foo,
934
+ s(:call, nil, :bar, s(:arglist))))
935
+ end
936
+
522
937
  it "works when assigning to an instance variable" do
523
938
  result = parser.parse "@foo += bar"
524
939
  result.must_equal s(:iasgn,
@@ -537,11 +952,38 @@ describe RipperRubyParser::Parser do
537
952
  :+,
538
953
  s(:call, nil, :baz, s(:arglist)))
539
954
  end
955
+
956
+ it "works with ||= when assigning to a collection element" do
957
+ result = parser.parse "foo[bar] ||= baz"
958
+ result.must_equal s(:op_asgn1,
959
+ s(:call, nil, :foo, s(:arglist)),
960
+ s(:arglist, s(:call, nil, :bar, s(:arglist))),
961
+ :"||",
962
+ s(:call, nil, :baz, s(:arglist)))
963
+ end
964
+
965
+ it "works when assigning to an attribute" do
966
+ result = parser.parse "foo.bar += baz"
967
+ result.must_equal s(:op_asgn2,
968
+ s(:call, nil, :foo, s(:arglist)),
969
+ :bar=,
970
+ :+,
971
+ s(:call, nil, :baz, s(:arglist)))
972
+ end
973
+
974
+ it "works with ||= when assigning to an attribute" do
975
+ result = parser.parse "foo.bar ||= baz"
976
+ result.must_equal s(:op_asgn2,
977
+ s(:call, nil, :foo, s(:arglist)),
978
+ :bar=,
979
+ :"||",
980
+ s(:call, nil, :baz, s(:arglist)))
981
+ end
540
982
  end
541
983
 
542
984
  describe "for multiple assignment" do
543
985
  it "works the same number of items on each side" do
544
- result = parser.parse "foo, bar = baz, qux"
986
+ result = suppress_warnings { parser.parse "foo, bar = baz, qux" }
545
987
  result.must_equal s(:masgn,
546
988
  s(:array, s(:lasgn, :foo), s(:lasgn, :bar)),
547
989
  s(:array,
@@ -550,7 +992,7 @@ describe RipperRubyParser::Parser do
550
992
  end
551
993
 
552
994
  it "works with a single item on the right-hand side" do
553
- result = parser.parse "foo, bar = baz"
995
+ result = suppress_warnings { parser.parse "foo, bar = baz" }
554
996
  result.must_equal s(:masgn,
555
997
  s(:array, s(:lasgn, :foo), s(:lasgn, :bar)),
556
998
  s(:to_ary,
@@ -558,16 +1000,117 @@ describe RipperRubyParser::Parser do
558
1000
  end
559
1001
 
560
1002
  it "works with left-hand splat" do
561
- result = parser.parse "foo, *bar = baz, qux"
1003
+ result = suppress_warnings { parser.parse "foo, *bar = baz, qux" }
562
1004
  result.must_equal s(:masgn,
563
1005
  s(:array, s(:lasgn, :foo), s(:splat, s(:lasgn, :bar))),
564
1006
  s(:array,
565
1007
  s(:call, nil, :baz, s(:arglist)),
566
1008
  s(:call, nil, :qux, s(:arglist))))
567
1009
  end
1010
+
1011
+ it "works with brackets around the left-hand side" do
1012
+ result = suppress_warnings { parser.parse "(foo, bar) = baz" }
1013
+ result.must_equal s(:masgn,
1014
+ s(:array, s(:lasgn, :foo), s(:lasgn, :bar)),
1015
+ s(:to_ary,
1016
+ s(:call, nil, :baz, s(:arglist))))
1017
+ end
1018
+
1019
+ it "works with complex destructuring" do
1020
+ result = suppress_warnings { parser.parse "foo, (bar, baz) = qux" }
1021
+ result.must_equal s(:masgn,
1022
+ s(:array,
1023
+ s(:lasgn, :foo),
1024
+ s(:masgn,
1025
+ s(:array,
1026
+ s(:lasgn, :bar),
1027
+ s(:lasgn, :baz)))),
1028
+ s(:to_ary,
1029
+ s(:call, nil, :qux, s(:arglist))))
1030
+ end
1031
+
1032
+ it "works with instance variables" do
1033
+ result = parser.parse "@foo, @bar = baz"
1034
+ result.must_equal s(:masgn,
1035
+ s(:array, s(:iasgn, :@foo), s(:iasgn, :@bar)),
1036
+ s(:to_ary,
1037
+ s(:call, nil, :baz, s(:arglist))))
1038
+ end
1039
+
1040
+ it "works with class variables" do
1041
+ result = parser.parse "@@foo, @@bar = baz"
1042
+ result.must_equal s(:masgn,
1043
+ s(:array, s(:cvdecl, :@@foo), s(:cvdecl, :@@bar)),
1044
+ s(:to_ary,
1045
+ s(:call, nil, :baz, s(:arglist))))
1046
+ end
1047
+
1048
+ it "works with attributes" do
1049
+ result = parser.parse "foo.bar, foo.baz = qux"
1050
+ result.must_equal s(:masgn,
1051
+ s(:array,
1052
+ s(:attrasgn,
1053
+ s(:call, nil, :foo, s(:arglist)),
1054
+ :bar=,
1055
+ s(:arglist)),
1056
+ s(:attrasgn,
1057
+ s(:call, nil, :foo, s(:arglist)),
1058
+ :baz=,
1059
+ s(:arglist))),
1060
+ s(:to_ary,
1061
+ s(:call, nil, :qux, s(:arglist))))
1062
+ end
1063
+
1064
+ it "works with collection elements" do
1065
+ result = parser.parse "foo[1], bar[2] = baz"
1066
+ result.must_equal s(:masgn,
1067
+ s(:array,
1068
+ s(:attrasgn,
1069
+ s(:call, nil, :foo, s(:arglist)),
1070
+ :[]=,
1071
+ s(:arglist, s(:lit, 1))),
1072
+ s(:attrasgn,
1073
+ s(:call, nil, :bar, s(:arglist)),
1074
+ :[]=,
1075
+ s(:arglist, s(:lit, 2)))),
1076
+ s(:to_ary, s(:call, nil, :baz, s(:arglist))))
1077
+ end
1078
+
1079
+ it "works with constants" do
1080
+ result = parser.parse "Foo, Bar = baz"
1081
+ result.must_equal s(:masgn,
1082
+ s(:array, s(:cdecl, :Foo), s(:cdecl, :Bar)),
1083
+ s(:to_ary,
1084
+ s(:call, nil, :baz, s(:arglist))))
1085
+ end
1086
+
1087
+ it "works with instance variables and splat" do
1088
+ result = parser.parse "@foo, *@bar = baz"
1089
+ result.must_equal s(:masgn,
1090
+ s(:array,
1091
+ s(:iasgn, :@foo),
1092
+ s(:splat, s(:iasgn, :@bar))),
1093
+ s(:to_ary,
1094
+ s(:call, nil, :baz, s(:arglist))))
1095
+ end
1096
+
568
1097
  end
569
1098
 
570
1099
  describe "for operators" do
1100
+ it "handles :and" do
1101
+ result = parser.parse "foo and bar"
1102
+ result.must_equal s(:and,
1103
+ s(:call, nil, :foo, s(:arglist)),
1104
+ s(:call, nil, :bar, s(:arglist)))
1105
+ end
1106
+
1107
+ it "handles :or" do
1108
+ result = parser.parse "foo or bar"
1109
+ result.must_equal s(:or,
1110
+ s(:call, nil, :foo, s(:arglist)),
1111
+ s(:call, nil, :bar, s(:arglist)))
1112
+ end
1113
+
571
1114
  it "converts :&& to :and" do
572
1115
  result = parser.parse "foo && bar"
573
1116
  result.must_equal s(:and,
@@ -582,6 +1125,38 @@ describe RipperRubyParser::Parser do
582
1125
  s(:call, nil, :bar, s(:arglist)))
583
1126
  end
584
1127
 
1128
+ it "handles :!=" do
1129
+ result = parser.parse "foo != bar"
1130
+ result.must_equal s(:not,
1131
+ s(:call,
1132
+ s(:call, nil, :foo, s(:arglist)),
1133
+ :==,
1134
+ s(:arglist,
1135
+ s(:call, nil, :bar, s(:arglist)))))
1136
+ end
1137
+
1138
+ it "handles :=~ with two non-literals" do
1139
+ result = parser.parse "foo =~ bar"
1140
+ result.must_equal s(:call,
1141
+ s(:call, nil, :foo, s(:arglist)),
1142
+ :=~,
1143
+ s(:arglist, s(:call, nil, :bar, s(:arglist))))
1144
+ end
1145
+
1146
+ it "handles :=~ with literal regexp on the left hand side" do
1147
+ result = parser.parse "/foo/ =~ bar"
1148
+ result.must_equal s(:match2,
1149
+ s(:lit, /foo/),
1150
+ s(:call, nil, :bar, s(:arglist)))
1151
+ end
1152
+
1153
+ it "handles :=~ with literal regexp on the right hand side" do
1154
+ result = parser.parse "foo =~ /bar/"
1155
+ result.must_equal s(:match3,
1156
+ s(:lit, /bar/),
1157
+ s(:call, nil, :foo, s(:arglist)))
1158
+ end
1159
+
585
1160
  it "handles unary minus with a number literal" do
586
1161
  result = parser.parse "-1"
587
1162
  result.must_equal s(:lit, -1)
@@ -595,6 +1170,29 @@ describe RipperRubyParser::Parser do
595
1170
  s(:arglist))
596
1171
  end
597
1172
 
1173
+ it "handles unary plus with a number literal" do
1174
+ result = parser.parse "+ 1"
1175
+ result.must_equal s(:lit, 1)
1176
+ end
1177
+
1178
+ it "handles unary plus with a non-literal" do
1179
+ result = parser.parse "+ foo"
1180
+ result.must_equal s(:call,
1181
+ s(:call, nil, :foo, s(:arglist)),
1182
+ :+@,
1183
+ s(:arglist))
1184
+ end
1185
+
1186
+ it "handles unary not" do
1187
+ result = parser.parse "not foo"
1188
+ result.must_equal s(:not, s(:call, nil, :foo, s(:arglist)))
1189
+ end
1190
+
1191
+ it "converts :! to :not" do
1192
+ result = parser.parse "!foo"
1193
+ result.must_equal s(:not, s(:call, nil, :foo, s(:arglist)))
1194
+ end
1195
+
598
1196
  it "handles the range operator with positive number literals" do
599
1197
  result = parser.parse "1..2"
600
1198
  result.must_equal s(:lit, 1..2)
@@ -618,6 +1216,203 @@ describe RipperRubyParser::Parser do
618
1216
  s(:call, nil, :foo, s(:arglist)),
619
1217
  s(:call, nil, :bar, s(:arglist)))
620
1218
  end
1219
+
1220
+ it "handles the ternary operator" do
1221
+ result = parser.parse "foo ? bar : baz"
1222
+ result.must_equal s(:if,
1223
+ s(:call, nil, :foo, s(:arglist)),
1224
+ s(:call, nil, :bar, s(:arglist)),
1225
+ s(:call, nil, :baz, s(:arglist)))
1226
+ end
1227
+ end
1228
+
1229
+ describe "for expressions" do
1230
+ it "handles assignment inside binary operator expressions" do
1231
+ result = suppress_warnings { parser.parse "foo + (bar = baz)" }
1232
+ result.must_equal s(:call,
1233
+ s(:call, nil, :foo, s(:arglist)),
1234
+ :+,
1235
+ s(:arglist,
1236
+ s(:lasgn,
1237
+ :bar,
1238
+ s(:call, nil, :baz, s(:arglist)))))
1239
+ end
1240
+
1241
+ it "handles assignment inside unary operator expressions" do
1242
+ result = suppress_warnings { parser.parse "+(foo = bar)" }
1243
+ result.must_equal s(:call,
1244
+ s(:lasgn, :foo, s(:call, nil, :bar, s(:arglist))),
1245
+ :+@,
1246
+ s(:arglist))
1247
+ end
1248
+ end
1249
+
1250
+ # Note: differences in the handling of comments are not caught by Sexp's
1251
+ # implementation of equality.
1252
+ describe "for comments" do
1253
+ it "handles method comments" do
1254
+ result = parser.parse "# Foo\ndef foo; end"
1255
+ result.must_equal s(:defn,
1256
+ :foo,
1257
+ s(:args), s(:scope, s(:block, s(:nil))))
1258
+ result.comments.must_equal "# Foo\n"
1259
+ end
1260
+
1261
+ it "matches comments to the correct entity" do
1262
+ result = parser.parse "# Foo\nclass Foo\n# Bar\ndef bar\nend\nend"
1263
+ result.must_equal s(:class, :Foo, nil,
1264
+ s(:scope,
1265
+ s(:defn, :bar,
1266
+ s(:args), s(:scope, s(:block, s(:nil))))))
1267
+ result.comments.must_equal "# Foo\n"
1268
+ defn = result[3][1]
1269
+ defn.sexp_type.must_equal :defn
1270
+ defn.comments.must_equal "# Bar\n"
1271
+ end
1272
+
1273
+ it "combines multi-line comments" do
1274
+ result = parser.parse "# Foo\n# Bar\ndef foo; end"
1275
+ result.must_equal s(:defn,
1276
+ :foo,
1277
+ s(:args), s(:scope, s(:block, s(:nil))))
1278
+ result.comments.must_equal "# Foo\n# Bar\n"
1279
+ end
1280
+ end
1281
+
1282
+ # Note: differences in the handling of line numbers are not caught by
1283
+ # Sexp's implementation of equality.
1284
+ describe "assigning line numbers" do
1285
+ it "works for a plain method call" do
1286
+ result = parser.parse "foo"
1287
+ result.line.must_equal 1
1288
+ end
1289
+
1290
+ it "works for a method call with brackets" do
1291
+ result = parser.parse "foo()"
1292
+ result.line.must_equal 1
1293
+ end
1294
+
1295
+ it "works for a method call with reciever" do
1296
+ result = parser.parse "foo.bar"
1297
+ result.line.must_equal 1
1298
+ end
1299
+
1300
+ it "works for a method call with reciever and arguments" do
1301
+ result = parser.parse "foo.bar baz"
1302
+ result.line.must_equal 1
1303
+ end
1304
+
1305
+ it "works for a method call with arguments" do
1306
+ result = parser.parse "foo bar"
1307
+ result.line.must_equal 1
1308
+ end
1309
+
1310
+ it "works for a block with two lines" do
1311
+ result = parser.parse "foo\nbar\n"
1312
+ result.sexp_type.must_equal :block
1313
+ result[1].line.must_equal 1
1314
+ result[2].line.must_equal 2
1315
+ result.line.must_equal 1
1316
+ end
1317
+
1318
+ it "works for a constant reference" do
1319
+ result = parser.parse "Foo"
1320
+ result.line.must_equal 1
1321
+ end
1322
+
1323
+ it "works for an instance variable" do
1324
+ result = parser.parse "@foo"
1325
+ result.line.must_equal 1
1326
+ end
1327
+
1328
+ it "works for a global variable" do
1329
+ result = parser.parse "$foo"
1330
+ result.line.must_equal 1
1331
+ end
1332
+
1333
+ it "works for a class variable" do
1334
+ result = parser.parse "@@foo"
1335
+ result.line.must_equal 1
1336
+ end
1337
+
1338
+ it "works for a local variable" do
1339
+ result = parser.parse "foo = bar\nfoo\n"
1340
+ result.sexp_type.must_equal :block
1341
+ result[1].line.must_equal 1
1342
+ result[2].line.must_equal 2
1343
+ result.line.must_equal 1
1344
+ end
1345
+
1346
+ it "works for an integer literal" do
1347
+ result = parser.parse "42"
1348
+ result.line.must_equal 1
1349
+ end
1350
+
1351
+ it "works for a float literal" do
1352
+ result = parser.parse "3.14"
1353
+ result.line.must_equal 1
1354
+ end
1355
+
1356
+ it "works for a regular expression back reference" do
1357
+ result = parser.parse "$1"
1358
+ result.line.must_equal 1
1359
+ end
1360
+
1361
+ it "works for self" do
1362
+ result = parser.parse "self"
1363
+ result.line.must_equal 1
1364
+ end
1365
+
1366
+ it "works for __FILE__" do
1367
+ result = parser.parse "__FILE__"
1368
+ result.line.must_equal 1
1369
+ end
1370
+
1371
+ it "works for nil" do
1372
+ result = parser.parse "nil"
1373
+ result.line.must_equal 1
1374
+ end
1375
+
1376
+ it "works for a symbol literal" do
1377
+ result = parser.parse ":foo"
1378
+ result.line.must_equal 1
1379
+ end
1380
+
1381
+ it "works for a class definition" do
1382
+ result = parser.parse "class Foo; end"
1383
+ result.line.must_equal 1
1384
+ end
1385
+
1386
+ it "works for a module definition" do
1387
+ result = parser.parse "module Foo; end"
1388
+ result.line.must_equal 1
1389
+ end
1390
+
1391
+ it "works for a method definition" do
1392
+ result = parser.parse "def foo; end"
1393
+ result.line.must_equal 1
1394
+ end
1395
+
1396
+ it "works for assignment of the empty hash" do
1397
+ result = suppress_warnings { parser.parse "foo = {}" }
1398
+ result.line.must_equal 1
1399
+ end
1400
+
1401
+ it "works for multiple assignment of empty hashes" do
1402
+ result = suppress_warnings { parser.parse "foo, bar = {}, {}" }
1403
+ result.line.must_equal 1
1404
+ end
1405
+
1406
+ it "assigns line numbers to all nested sexps" do
1407
+ result = parser.parse "foo() do\nend\n"
1408
+ result.must_equal s(:iter,
1409
+ s(:call,
1410
+ nil, :foo, s(:arglist)), nil, s(:block))
1411
+ arglist = result[1][3]
1412
+ block = result[3]
1413
+ nums = [ arglist.line, block.line ]
1414
+ nums.must_equal [1, 1]
1415
+ end
621
1416
  end
622
1417
  end
623
1418
  end