ripper_ruby_parser 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.rdoc 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