ripper_ruby_parser 0.0.5 → 0.0.6

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.
@@ -23,7 +23,13 @@ module RipperRubyParser
23
23
 
24
24
  @processor.filename = filename
25
25
  @processor.extra_compatible = extra_compatible
26
- @processor.process exp
26
+ result = @processor.process exp
27
+
28
+ if result == s(:void_stmt)
29
+ nil
30
+ else
31
+ result
32
+ end
27
33
  end
28
34
 
29
35
  private
@@ -7,8 +7,13 @@ module RipperRubyParser
7
7
  end
8
8
 
9
9
  def process_aref exp
10
- _, item, idx = exp.shift 3
11
- s(:call, process(item), :[], process(idx))
10
+ _, coll, idx = exp.shift 3
11
+
12
+ coll = process(coll)
13
+ coll = nil if coll == s(:self)
14
+
15
+ idx = process(idx) || s(:arglist)
16
+ s(:call, coll, :[], idx)
12
17
  end
13
18
  end
14
19
  end
@@ -98,6 +98,11 @@ module RipperRubyParser
98
98
  return item
99
99
  end
100
100
 
101
+ OPERATOR_ASSIGNMENT_MAP = {
102
+ :"||" => :op_asgn_or,
103
+ :"&&" => :op_asgn_and
104
+ }
105
+
101
106
  def create_operator_assignment_sub_type lvalue, value, operator
102
107
  case lvalue.sexp_type
103
108
  when :aref_field
@@ -107,8 +112,8 @@ module RipperRubyParser
107
112
  _, obj, _, (_, field) = lvalue
108
113
  s(:op_asgn2, obj, :"#{field}=", operator, value)
109
114
  else
110
- if operator == :"||"
111
- s(:op_asgn_or, lvalue, create_assignment_sub_type(lvalue, value))
115
+ if (mapped = OPERATOR_ASSIGNMENT_MAP[operator])
116
+ s(mapped, lvalue, create_assignment_sub_type(lvalue, value))
112
117
  else
113
118
  operator_call = s(:call, lvalue, operator, s(:arglist, value))
114
119
  create_assignment_sub_type lvalue, operator_call
@@ -81,7 +81,7 @@ module RipperRubyParser
81
81
 
82
82
  if evar
83
83
  evar = process(evar)[1]
84
- easgn = s(:lasgn, :e, s(:gvar, :$!))
84
+ easgn = s(:lasgn, evar, s(:gvar, :$!))
85
85
  arr << easgn
86
86
  end
87
87
 
@@ -20,7 +20,35 @@ module RipperRubyParser
20
20
 
21
21
  def process_string_embexpr exp
22
22
  _, list = exp.shift 2
23
- s(:evstr, process(list.first))
23
+ val = process(list.first)
24
+ if val.sexp_type == :str
25
+ val
26
+ else
27
+ s(:evstr, val)
28
+ end
29
+ end
30
+
31
+ def process_string_dvar exp
32
+ _, list = exp.shift 2
33
+ val = process(list)
34
+ s(:evstr, val)
35
+ end
36
+
37
+ def process_string_concat exp
38
+ _, left, right = exp.shift 3
39
+
40
+ left = process(left)
41
+ right = process(right)
42
+
43
+ if left.sexp_type == :str
44
+ right[1] = left[1] + right[1]
45
+ right
46
+ else # Expecting left.sexp_type == :dstr
47
+ _, first, *rest = right
48
+ left.push s(:str, first)
49
+ left.push(*rest)
50
+ left
51
+ end
24
52
  end
25
53
 
26
54
  def process_xstring_literal exp
@@ -37,11 +65,7 @@ module RipperRubyParser
37
65
  _, content, (_, flags, _) = exp.shift 3
38
66
 
39
67
  string, rest = extract_string_parts content
40
-
41
- numflags = 0
42
- flags =~ /m/ and numflags |= Regexp::MULTILINE
43
- flags =~ /x/ and numflags |= Regexp::EXTENDED
44
- flags =~ /i/ and numflags |= Regexp::IGNORECASE
68
+ numflags = character_flags_to_numerical flags
45
69
 
46
70
  if rest.empty?
47
71
  s(:lit, Regexp.new(string, numflags))
@@ -104,6 +128,11 @@ module RipperRubyParser
104
128
  rest << result
105
129
  end
106
130
 
131
+ while not(rest.empty?) and rest.first.sexp_type == :str
132
+ str = rest.shift
133
+ string += str[1]
134
+ end
135
+
107
136
  return string, rest
108
137
  end
109
138
 
@@ -121,11 +150,61 @@ module RipperRubyParser
121
150
  return string, rest
122
151
  end
123
152
 
153
+ SINGLE_LETTER_ESCAPES = {
154
+ "a" => "\a",
155
+ "b" => "\b",
156
+ "e" => "\e",
157
+ "f" => "\f",
158
+ "n" => "\n",
159
+ "r" => "\r",
160
+ "s" => "\s",
161
+ "t" => "\t",
162
+ "v" => "\v",
163
+ }
164
+
165
+ SINGLE_LETTER_ESCAPES_REGEXP =
166
+ Regexp.new("^[#{SINGLE_LETTER_ESCAPES.keys.join}]$")
167
+
124
168
  def unescape string
125
- string.gsub(/(\\[^)])/) do
126
- eval "\"#{$1}\""
169
+ string.gsub(/\\(
170
+ [0-7]{1,3} | # octal character
171
+ x[0-9a-fA-F]{1,2} | # hex byte
172
+ u[0-9a-fA-F]{4} | # unicode character
173
+ C-. | # control (regular)
174
+ c. | # control (shorthand)
175
+ . # single-character
176
+ )/x) do
177
+ bare = $1
178
+ case bare
179
+ when SINGLE_LETTER_ESCAPES_REGEXP
180
+ SINGLE_LETTER_ESCAPES[bare]
181
+ when /^x/
182
+ bare[1..-1].to_i(16).chr
183
+ when /^u/
184
+ bare[1..-1].to_i(16).chr(Encoding::UTF_8)
185
+ when /^(c|C-)(.)$/
186
+ ($2.ord & 0b1001_1111).chr
187
+ when /^[0-7]+/
188
+ bare.to_i(8).chr
189
+ else
190
+ bare
191
+ end
127
192
  end
128
193
  end
194
+
195
+ def character_flags_to_numerical flags
196
+ numflags = 0
197
+
198
+ flags =~ /m/ and numflags |= Regexp::MULTILINE
199
+ flags =~ /x/ and numflags |= Regexp::EXTENDED
200
+ flags =~ /i/ and numflags |= Regexp::IGNORECASE
201
+
202
+ flags =~ /n/ and numflags |= Regexp::NOENCODING
203
+ flags =~ /[ues]/ and numflags |= Regexp::FIXEDENCODING
204
+
205
+ numflags
206
+ end
207
+
129
208
  end
130
209
  end
131
210
  end
@@ -3,20 +3,30 @@ module RipperRubyParser
3
3
  module Loops
4
4
  def process_until exp
5
5
  _, cond, block = exp.shift 3
6
- s(:until, process(cond), handle_statement_list(block), true)
6
+
7
+ make_until(process(cond), handle_statement_list(block), true)
7
8
  end
8
9
 
9
10
  def process_until_mod exp
10
11
  _, cond, block = exp.shift 3
11
12
 
12
- check_at_start = block.sexp_type != :begin
13
+ check_at_start = check_at_start?(block)
13
14
 
14
- s(:until, process(cond), process(block), check_at_start)
15
+ make_until(process(cond), process(block), check_at_start)
15
16
  end
16
17
 
17
18
  def process_while exp
18
19
  _, cond, block = exp.shift 3
19
- s(:while, process(cond), handle_statement_list(block), true)
20
+
21
+ make_while(process(cond), handle_statement_list(block), true)
22
+ end
23
+
24
+ def process_while_mod exp
25
+ _, cond, block = exp.shift 3
26
+
27
+ check_at_start = check_at_start?(block)
28
+
29
+ make_while(process(cond), process(block), check_at_start)
20
30
  end
21
31
 
22
32
  def process_for exp
@@ -25,6 +35,28 @@ module RipperRubyParser
25
35
  s(:lasgn, process(var)[1]),
26
36
  handle_statement_list(block))
27
37
  end
38
+
39
+ private
40
+
41
+ def make_guarded_block(type, inverse, cond, block, check_at_start)
42
+ if cond.sexp_type == :not
43
+ type = inverse
44
+ cond = cond[1]
45
+ end
46
+ s(type, cond, block, check_at_start)
47
+ end
48
+
49
+ def make_until(cond, block, check_at_start)
50
+ make_guarded_block(:until, :while, cond, block, check_at_start)
51
+ end
52
+
53
+ def make_while(cond, block, check_at_start)
54
+ make_guarded_block(:while, :until, cond, block, check_at_start)
55
+ end
56
+
57
+ def check_at_start?(block)
58
+ block.sexp_type != :begin
59
+ end
28
60
  end
29
61
  end
30
62
  end
@@ -20,6 +20,7 @@ module RipperRubyParser
20
20
 
21
21
  def process_binary exp
22
22
  _, left, op, right = exp.shift 4
23
+
23
24
  if op == :=~
24
25
  if left.sexp_type == :regexp_literal
25
26
  s(:match2, process(left), process(right))
@@ -31,13 +32,7 @@ module RipperRubyParser
31
32
  elsif (mapped = NEGATED_BINARY_OPERATOR_MAP[op])
32
33
  s(:not, process(s(:binary, left, mapped, right)))
33
34
  elsif (mapped = BINARY_OPERATOR_MAP[op])
34
- left = process(left)
35
- right = process(right)
36
- if mapped == left.sexp_type
37
- s(left.sexp_type, left[1], s(mapped, left[2], right))
38
- else
39
- s(mapped, left, right)
40
- end
35
+ rebalance_binary(s(mapped, process(left), process(right)))
41
36
  else
42
37
  s(:call, process(left), op, s(:arglist, process(right)))
43
38
  end
@@ -73,6 +68,18 @@ module RipperRubyParser
73
68
  _, cond, truepart, falsepart = exp.shift 4
74
69
  s(:if, process(cond), process(truepart), process(falsepart))
75
70
  end
71
+
72
+ private
73
+
74
+ def rebalance_binary exp
75
+ op, left, right = exp
76
+
77
+ if op == left.sexp_type
78
+ s(op, left[1], rebalance_binary(s(op, left[2], right)))
79
+ else
80
+ s(op, left, right)
81
+ end
82
+ end
76
83
  end
77
84
  end
78
85
  end
@@ -48,12 +48,8 @@ module RipperRubyParser
48
48
  def process_program exp
49
49
  _, content = exp.shift 2
50
50
 
51
- if content.length == 1
52
- process(content.first)
53
- else
54
- statements = content.map { |sub_exp| process(sub_exp) }
55
- s(:block, *statements)
56
- end
51
+ statements = content.map { |sub_exp| process(sub_exp) }
52
+ wrap_in_block statements
57
53
  end
58
54
 
59
55
  def process_module exp
@@ -91,6 +87,10 @@ module RipperRubyParser
91
87
  s(:colon2, process(left), extract_node_symbol(right))
92
88
  end
93
89
 
90
+ def process_const_path_field exp
91
+ s(:const, process_const_path_ref(exp))
92
+ end
93
+
94
94
  def process_const_ref exp
95
95
  _, ref = exp.shift 3
96
96
  process(ref)
@@ -101,6 +101,10 @@ module RipperRubyParser
101
101
  s(:colon3, extract_node_symbol(ref))
102
102
  end
103
103
 
104
+ def process_top_const_field exp
105
+ s(:const, process_top_const_ref(exp))
106
+ end
107
+
104
108
  def process_paren exp
105
109
  _, body = exp.shift 2
106
110
  if body.size == 0
@@ -1,3 +1,3 @@
1
1
  module RipperRubyParser
2
- VERSION = '0.0.5'
2
+ VERSION = '0.0.6'
3
3
  end
@@ -125,5 +125,25 @@ describe "Using RipperRubyParser and RubyParser" do
125
125
  formatted(imitation).must_equal formatted(original)
126
126
  end
127
127
  end
128
+
129
+ describe "for an example with regular expressions with different encoding flags" do
130
+ it "gives the same result" do
131
+ program = <<-END
132
+ regular = /foo/
133
+ noenc = /foo/n
134
+ utf8 = /foo/u
135
+ euc = /foo/e
136
+ sjis = /foo/s
137
+
138
+ regular = /foo\#{bar}/
139
+ noenc = /foo\#{bar}/n
140
+ utf8 = /foo\#{bar}/u
141
+ euc = /foo\#{bar}/e
142
+ sjis = /foo\#{bar}/s
143
+ END
144
+
145
+ program.must_be_parsed_as_before
146
+ end
147
+ end
128
148
  end
129
149
 
data/test/test_helper.rb CHANGED
@@ -36,9 +36,18 @@ class MiniTest::Spec
36
36
  result = parser.parse code
37
37
  assert_equal sexp, result
38
38
  end
39
+
40
+ def assert_parsed_as_before code
41
+ oldparser = RubyParser.new
42
+ newparser = RipperRubyParser::Parser.new
43
+ expected = oldparser.parse code.dup
44
+ result = newparser.parse code
45
+ assert_equal formatted(expected), formatted(result)
46
+ end
39
47
  end
40
48
 
41
49
  module MiniTest::Expectations
42
50
  infect_an_assertion :assert_parsed_as, :must_be_parsed_as
51
+ infect_an_assertion :assert_parsed_as_before, :must_be_parsed_as_before, :unary
43
52
  end
44
53
 
@@ -2,6 +2,22 @@ require File.expand_path('../test_helper.rb', File.dirname(__FILE__))
2
2
 
3
3
  describe RipperRubyParser::Parser do
4
4
  describe "#parse" do
5
+ describe "for single assignment" do
6
+ it "works when assigning to a namespaced constant" do
7
+ "Foo::Bar = baz".
8
+ must_be_parsed_as s(:cdecl,
9
+ s(:colon2, s(:const, :Foo), :Bar),
10
+ s(:call, nil, :baz, s(:arglist)))
11
+ end
12
+
13
+ it "works when assigning to constant in the root namespace" do
14
+ "::Foo = bar".
15
+ must_be_parsed_as s(:cdecl,
16
+ s(:colon3, :Foo),
17
+ s(:call, nil, :bar, s(:arglist)))
18
+ end
19
+ end
20
+
5
21
  describe "for multiple assignment" do
6
22
  specify do
7
23
  "foo, * = bar".
@@ -45,6 +61,12 @@ describe RipperRubyParser::Parser do
45
61
  :+,
46
62
  s(:call, nil, :qux, s(:arglist)))
47
63
  end
64
+
65
+ it "works with &&=" do
66
+ "foo &&= bar".
67
+ must_be_parsed_as s(:op_asgn_and,
68
+ s(:lvar, :foo), s(:lasgn, :foo, s(:call, nil, :bar, s(:arglist))))
69
+ end
48
70
  end
49
71
  end
50
72
 
@@ -68,5 +68,17 @@ describe RipperRubyParser::Parser do
68
68
  s(:call, nil, :bar, s(:arglist)))
69
69
  end
70
70
  end
71
+
72
+ describe "for rescue" do
73
+ it "works with assignment to an error variable" do
74
+ "begin; foo; rescue => bar; baz; end".
75
+ must_be_parsed_as s(:rescue,
76
+ s(:call, nil, :foo, s(:arglist)),
77
+ s(:resbody,
78
+ s(:array,
79
+ s(:lasgn, :bar, s(:gvar, :$!))),
80
+ s(:call, nil, :baz, s(:arglist))))
81
+ end
82
+ end
71
83
  end
72
84
  end
@@ -0,0 +1,179 @@
1
+ # coding: utf-8
2
+ require File.expand_path('../test_helper.rb', File.dirname(__FILE__))
3
+
4
+ describe RipperRubyParser::Parser do
5
+ describe "#parse" do
6
+ describe "for regexp literals" do
7
+ it "works with the no-encoding flag" do
8
+ parser = RipperRubyParser::Parser.new
9
+ result = parser.parse "/foo/n"
10
+ # Use inspect since regular == finds no difference between /foo/n
11
+ # and /foo/
12
+ result.inspect.must_equal s(:lit, /foo/n).inspect
13
+ end
14
+
15
+ describe "with interpolations" do
16
+ it "works with the no-encoding flag" do
17
+ '/foo#{bar}/n'.
18
+ must_be_parsed_as s(:dregx,
19
+ "foo",
20
+ s(:evstr,
21
+ s(:call, nil, :bar, s(:arglist))), 32)
22
+ end
23
+
24
+ it "works with the unicode-encoding flag" do
25
+ '/foo#{bar}/u'.
26
+ must_be_parsed_as s(:dregx,
27
+ "foo",
28
+ s(:evstr,
29
+ s(:call, nil, :bar, s(:arglist))), 16)
30
+ end
31
+
32
+ it "works with the euc-encoding flag" do
33
+ '/foo#{bar}/e'.
34
+ must_be_parsed_as s(:dregx,
35
+ "foo",
36
+ s(:evstr,
37
+ s(:call, nil, :bar, s(:arglist))), 16)
38
+ end
39
+
40
+ it "works with the sjis-encoding flag" do
41
+ '/foo#{bar}/s'.
42
+ must_be_parsed_as s(:dregx,
43
+ "foo",
44
+ s(:evstr,
45
+ s(:call, nil, :bar, s(:arglist))), 16)
46
+ end
47
+
48
+ describe "containing just a literal string" do
49
+ it "performs the interpolation when it is at the end" do
50
+ '/foo#{"bar"}/'.must_be_parsed_as s(:lit, /foobar/)
51
+ end
52
+
53
+ it "performs the interpolation when it is in the middle" do
54
+ '/foo#{"bar"}baz/'.must_be_parsed_as s(:lit, /foobarbaz/)
55
+ end
56
+
57
+ it "performs the interpolation when it is at the start" do
58
+ '/#{"foo"}bar/'.must_be_parsed_as s(:lit, /foobar/)
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ describe "for string literals" do
65
+ describe "with escape sequences" do
66
+ it "works with hex escapes" do
67
+ "\"\\x36\"".must_be_parsed_as s(:str, "6")
68
+ "\"\\x4a\"".must_be_parsed_as s(:str, "J")
69
+ "\"\\x4A\"".must_be_parsed_as s(:str, "J")
70
+ "\"\\x3Z\"".must_be_parsed_as s(:str, "\x03Z")
71
+ end
72
+
73
+ it "works with single-letter escapes" do
74
+ "\"foo\\abar\"".must_be_parsed_as s(:str, "foo\abar")
75
+ "\"foo\\bbar\"".must_be_parsed_as s(:str, "foo\bbar")
76
+ "\"foo\\ebar\"".must_be_parsed_as s(:str, "foo\ebar")
77
+ "\"foo\\fbar\"".must_be_parsed_as s(:str, "foo\fbar")
78
+ "\"foo\\nbar\"".must_be_parsed_as s(:str, "foo\nbar")
79
+ "\"foo\\rbar\"".must_be_parsed_as s(:str, "foo\rbar")
80
+ "\"foo\\sbar\"".must_be_parsed_as s(:str, "foo\sbar")
81
+ "\"foo\\tbar\"".must_be_parsed_as s(:str, "foo\tbar")
82
+ "\"foo\\vbar\"".must_be_parsed_as s(:str, "foo\vbar")
83
+ end
84
+
85
+ it "works with octal number escapes" do
86
+ "\"foo\\123bar\"".must_be_parsed_as s(:str, "foo\123bar")
87
+ "\"foo\\23bar\"".must_be_parsed_as s(:str, "foo\023bar")
88
+ "\"foo\\3bar\"".must_be_parsed_as s(:str, "foo\003bar")
89
+
90
+ "\"foo\\118bar\"".must_be_parsed_as s(:str, "foo\0118bar")
91
+ "\"foo\\18bar\"".must_be_parsed_as s(:str, "foo\0018bar")
92
+ end
93
+
94
+ it "works with simple short hand control sequence escapes" do
95
+ "\"foo\\cabar\"".must_be_parsed_as s(:str, "foo\cabar")
96
+ "\"foo\\cZbar\"".must_be_parsed_as s(:str, "foo\cZbar")
97
+ end
98
+
99
+ it "works with simple regular control sequence escapes" do
100
+ "\"foo\\C-abar\"".must_be_parsed_as s(:str, "foo\C-abar")
101
+ "\"foo\\C-Zbar\"".must_be_parsed_as s(:str, "foo\C-Zbar")
102
+ end
103
+
104
+ # TODO: Implement remaining escape sequence cases.
105
+
106
+ # TODO: Behave differently in extra_compatible mode.
107
+ it "works with unicode escapes (unlike RubyParser)" do
108
+ "\"foo\\u273bbar\"".must_be_parsed_as s(:str, "foo✻bar")
109
+ end
110
+ end
111
+
112
+ describe "with interpolations" do
113
+ describe "containing just a literal string" do
114
+ it "performs the interpolation when it is at the end" do
115
+ '"foo#{"bar"}"'.must_be_parsed_as s(:str, "foobar")
116
+ end
117
+
118
+ it "performs the interpolation when it is in the middle" do
119
+ '"foo#{"bar"}baz"'.must_be_parsed_as s(:str, "foobarbaz")
120
+ end
121
+
122
+ it "performs the interpolation when it is at the start" do
123
+ '"#{"foo"}bar"'.must_be_parsed_as s(:str, "foobar")
124
+ end
125
+ end
126
+
127
+ describe "without braces" do
128
+ it "works for ivars" do
129
+ "\"foo\#@bar\"".must_be_parsed_as s(:dstr,
130
+ "foo",
131
+ s(:evstr, s(:ivar, :@bar)))
132
+ end
133
+
134
+ it "works for gvars" do
135
+ "\"foo\#$bar\"".must_be_parsed_as s(:dstr,
136
+ "foo",
137
+ s(:evstr, s(:gvar, :$bar)))
138
+ end
139
+
140
+ it "works for cvars" do
141
+ "\"foo\#@@bar\"".must_be_parsed_as s(:dstr,
142
+ "foo",
143
+ s(:evstr, s(:cvar, :@@bar)))
144
+ end
145
+ end
146
+ end
147
+
148
+ describe "with string concatenation" do
149
+ it "performs the concatenation in the case of two simple literal strings" do
150
+ "\"foo\" \"bar\"".must_be_parsed_as s(:str, "foobar")
151
+ end
152
+
153
+ it "performs the concatenation when the right string has interpolations" do
154
+ "\"foo\" \"bar\#{baz}\"".
155
+ must_be_parsed_as s(:dstr,
156
+ "foobar",
157
+ s(:evstr, s(:call, nil, :baz, s(:arglist))))
158
+ end
159
+
160
+ it "performs the concatenation when the left string has interpolations" do
161
+ "\"foo\#{bar}\" \"baz\"".
162
+ must_be_parsed_as s(:dstr,
163
+ "foo",
164
+ s(:evstr, s(:call, nil, :bar, s(:arglist))),
165
+ s(:str, "baz"))
166
+ end
167
+
168
+ it "performs the concatenation when both strings have interpolations" do
169
+ "\"foo\#{bar}\" \"baz\#{qux}\"".
170
+ must_be_parsed_as s(:dstr,
171
+ "foo",
172
+ s(:evstr, s(:call, nil, :bar, s(:arglist))),
173
+ s(:str, "baz"),
174
+ s(:evstr, s(:call, nil, :qux, s(:arglist))))
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,52 @@
1
+ require File.expand_path('../test_helper.rb', File.dirname(__FILE__))
2
+
3
+ describe RipperRubyParser::Parser do
4
+ describe "#parse" do
5
+ describe "for the while statement" do
6
+ it "works in the single-line postfix case" do
7
+ "foo while bar".
8
+ must_be_parsed_as s(:while,
9
+ s(:call, nil, :bar, s(:arglist)),
10
+ s(:call, nil, :foo, s(:arglist)), true)
11
+ end
12
+
13
+ it "works in the block postfix case" do
14
+ "begin; foo; end while bar".
15
+ must_be_parsed_as s(:while,
16
+ s(:call, nil, :bar, s(:arglist)),
17
+ s(:call, nil, :foo, s(:arglist)), false)
18
+ end
19
+
20
+ it "normalizes a negative condition" do
21
+ "while not foo; bar; end".
22
+ must_be_parsed_as s(:until,
23
+ s(:call, nil, :foo, s(:arglist)),
24
+ s(:call, nil, :bar, s(:arglist)), true)
25
+ end
26
+
27
+ it "normalizes a negative condition in the postfix case" do
28
+ "foo while not bar".
29
+ must_be_parsed_as s(:until,
30
+ s(:call, nil, :bar, s(:arglist)),
31
+ s(:call, nil, :foo, s(:arglist)), true)
32
+ end
33
+ end
34
+
35
+ describe "for the until statement" do
36
+ it "normalizes a negative condition" do
37
+ "until not foo; bar; end".
38
+ must_be_parsed_as s(:while,
39
+ s(:call, nil, :foo, s(:arglist)),
40
+ s(:call, nil, :bar, s(:arglist)), true)
41
+
42
+ end
43
+
44
+ it "normalizes a negative condition in the postfix case" do
45
+ "foo until not bar".
46
+ must_be_parsed_as s(:while,
47
+ s(:call, nil, :bar, s(:arglist)),
48
+ s(:call, nil, :foo, s(:arglist)), true)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -12,5 +12,18 @@ describe RipperRubyParser::Parser do
12
12
  s(:call, nil, :bar, s(:arglist)))))
13
13
  end
14
14
  end
15
+
16
+ describe "for boolean operators" do
17
+ it "handles triple :and" do
18
+ "foo and bar and baz and qux".
19
+ must_be_parsed_as s(:and,
20
+ s(:call, nil, :foo, s(:arglist)),
21
+ s(:and,
22
+ s(:call, nil, :bar, s(:arglist)),
23
+ s(:and,
24
+ s(:call, nil, :baz, s(:arglist)),
25
+ s(:call, nil, :qux, s(:arglist)))))
26
+ end
27
+ end
15
28
  end
16
29
  end
@@ -21,6 +21,12 @@ describe RipperRubyParser::Parser do
21
21
  sexp_p.verify
22
22
  end
23
23
 
24
+ describe "for an empty program" do
25
+ it "returns nil" do
26
+ "".must_be_parsed_as nil
27
+ end
28
+ end
29
+
24
30
  describe "for a class declaration" do
25
31
  it "works with a namespaced class name" do
26
32
  result = parser.parse "class Foo::Bar; end"
@@ -576,6 +582,17 @@ describe RipperRubyParser::Parser do
576
582
  :[],
577
583
  s(:arglist, s(:call, nil, :bar, s(:arglist))))
578
584
  end
585
+
586
+ it "works without any indexes" do
587
+ "foo[]".must_be_parsed_as s(:call, s(:call, nil, :foo, s(:arglist)),
588
+ :[], s(:arglist))
589
+ end
590
+
591
+ it "drops self from self[]" do
592
+ "self[foo]".must_be_parsed_as s(:call, nil, :[],
593
+ s(:arglist,
594
+ s(:call, nil, :foo, s(:arglist))))
595
+ end
579
596
  end
580
597
 
581
598
  describe "for method definitions" do
@@ -981,13 +998,13 @@ describe RipperRubyParser::Parser do
981
998
  end
982
999
 
983
1000
  it "works for a double-quoted string representing a regex literal with escaped right bracket" do
984
- result = parser.parse "\"/\\)/\""
1001
+ result = parser.parse "\"/\\\\)/\""
985
1002
  result.must_equal s(:str, "/\\)/")
986
1003
  end
987
1004
 
988
- it "works for a single-quoted string representing a regex literal with escaped right bracket" do
989
- result = parser.parse "'/\\)/'"
990
- result.must_equal s(:str, "/\\)/")
1005
+ it "works for a double-quoted string containing a uselessly escaped right bracket" do
1006
+ result = parser.parse "\"/\\)/\""
1007
+ result.must_equal s(:str, "/)/")
991
1008
  end
992
1009
 
993
1010
  it "works for a string containing escaped quotes" do
@@ -1153,7 +1170,7 @@ describe RipperRubyParser::Parser do
1153
1170
  end
1154
1171
  end
1155
1172
 
1156
- describe "for constant references" do
1173
+ describe "for constant lookups" do
1157
1174
  it "works when explicitely starting from the root namespace" do
1158
1175
  result = parser.parse "::Foo"
1159
1176
  result.must_equal s(:colon3, :Foo)
@@ -1165,6 +1182,12 @@ describe RipperRubyParser::Parser do
1165
1182
  s(:colon2, s(:const, :Foo), :Bar),
1166
1183
  :Baz)
1167
1184
  end
1185
+
1186
+ it "works looking up a constant in a non-constant" do
1187
+ "foo::Bar".must_be_parsed_as s(:colon2,
1188
+ s(:call, nil, :foo, s(:arglist)),
1189
+ :Bar)
1190
+ end
1168
1191
  end
1169
1192
 
1170
1193
  describe "for variable references" do
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.5
4
+ version: 0.0.6
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-04-02 00:00:00.000000000 Z
12
+ date: 2012-04-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sexp_processor
16
- requirement: &18762580 !ruby/object:Gem::Requirement
16
+ requirement: &21062800 !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: *18762580
24
+ version_requirements: *21062800
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: minitest
27
- requirement: &18762060 !ruby/object:Gem::Requirement
27
+ requirement: &21062280 !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: *18762060
35
+ version_requirements: *21062280
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rake
38
- requirement: &18761580 !ruby/object:Gem::Requirement
38
+ requirement: &21061800 !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: *18761580
46
+ version_requirements: *21061800
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: ruby_parser
49
- requirement: &18761100 !ruby/object:Gem::Requirement
49
+ requirement: &21061320 !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: *18761100
57
+ version_requirements: *21061320
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: simplecov
60
- requirement: &18760720 !ruby/object:Gem::Requirement
60
+ requirement: &21060940 !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: *18760720
68
+ version_requirements: *21060940
69
69
  description:
70
70
  email:
71
71
  - matijs@matijs.net
@@ -102,6 +102,7 @@ files:
102
102
  - test/end_to_end/test_comparison_test.rb
103
103
  - test/end_to_end/line_numbering_test.rb
104
104
  - test/end_to_end/error_conditions_test.rb
105
+ - test/unit/parser_literals_test.rb
105
106
  - test/unit/parser_test.rb
106
107
  - test/unit/parser_blocks_test.rb
107
108
  - test/unit/sexp_processor_test.rb
@@ -110,6 +111,7 @@ files:
110
111
  - test/unit/version_test.rb
111
112
  - test/unit/parser_assignment_test.rb
112
113
  - test/unit/parser_method_calls_test.rb
114
+ - test/unit/parser_loops_test.rb
113
115
  - test/unit/commenting_sexp_builder_test.rb
114
116
  - README.rdoc
115
117
  - Rakefile
@@ -152,6 +154,8 @@ test_files:
152
154
  - test/unit/parser_assignment_test.rb
153
155
  - test/unit/parser_blocks_test.rb
154
156
  - test/unit/parser_conditionals_test.rb
157
+ - test/unit/parser_literals_test.rb
158
+ - test/unit/parser_loops_test.rb
155
159
  - test/unit/parser_method_calls_test.rb
156
160
  - test/unit/parser_operators_test.rb
157
161
  - test/unit/parser_test.rb