ripper_ruby_parser 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc 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
- * Only tested with MRI 1.9.3. Definitely does not work with the ripper gem for
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. It may work with lower 1.9 versions. Let me know if it does.
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
- exp = Sexp.from_array(parser.parse)
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
- ASSIGNMENT_SUB_TYPE_MAP[type] || type
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
- s(:iter, process(call), args, stmt)
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 = s(*map_body(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
- s(:if,
7
- process(cond),
8
- handle_statement_list(truepart),
9
- process(falsepart))
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
- process_if exp
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
- args && s(:lasgn, args[1][1])
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
- if statements.length == 1
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 = extract_string_parts exp
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 = extract_inner_string content[0]
39
+ string, rest = extract_string_parts content
30
40
 
31
- s(:lit, Regexp.new(string[1]))
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 = extract_string_parts list
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 = extract_inner_string(inner)
90
+ string = process(inner)
70
91
  rest = []
71
92
 
72
- if string.sexp_type == :str
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(/(\\[n\\"])/) do
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
- s(mapped, process(left), process(right))
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.to_i }
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) {|val| eval(val) }
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
@@ -0,0 +1,5 @@
1
+ module RipperRubyParser
2
+ class SyntaxError < RuntimeError
3
+
4
+ end
5
+ end
@@ -1,3 +1,3 @@
1
1
  module RipperRubyParser
2
- VERSION = '0.0.3'
2
+ VERSION = '0.0.4'
3
3
  end
@@ -86,7 +86,7 @@ describe "Using RipperRubyParser and RubyParser" do
86
86
  end
87
87
  end
88
88
 
89
- describe "for a example with operators and explicit block parameter from Reek" do
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
@@ -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 reciever" do
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 reciever" do
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 reciever" do
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 reciever" do
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 reciever and arguments" do
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 all nested sexps" do
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
- nil, :foo, s(:arglist)), nil, s(:block))
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.3
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-21 00:00:00.000000000 Z
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: &16124900 !ruby/object:Gem::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: *16124900
24
+ version_requirements: *22929360
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: minitest
27
- requirement: &16124380 !ruby/object:Gem::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: *16124380
35
+ version_requirements: *22928840
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rake
38
- requirement: &16123900 !ruby/object:Gem::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: *16123900
46
+ version_requirements: *22928360
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: ruby_parser
49
- requirement: &16123420 !ruby/object:Gem::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: *16123420
57
+ version_requirements: *22927880
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: simplecov
60
- requirement: &16123040 !ruby/object:Gem::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: *16123040
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