redparse 0.8.3 → 0.8.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/History.txt +63 -4
- data/Makefile +43 -0
- data/README.txt +101 -166
- data/Rakefile +1 -1
- data/bin/redparse +49 -21
- data/lib/redparse.rb +88 -1654
- data/lib/redparse/cache.rb +172 -0
- data/lib/redparse/compile.rb +1648 -0
- data/lib/redparse/float_accurate_to_s.rb +162 -0
- data/lib/redparse/generate.rb +6 -2
- data/lib/redparse/node.rb +677 -397
- data/lib/redparse/parse_tree_server.rb +129 -0
- data/lib/redparse/pthelper.rb +43 -0
- data/lib/redparse/reg_more_sugar.rb +5 -5
- data/lib/redparse/version.rb +1 -1
- data/redparse.gemspec +43 -0
- data/test/data/skkdictools.rb +3 -0
- data/test/generate_parse_tree_server_rc.rb +43 -0
- data/test/rp-locatetest.rb +41 -1
- data/test/test_1.9.rb +114 -0
- data/test/test_all.rb +3 -0
- data/test/test_redparse.rb +283 -124
- data/test/test_xform_tree.rb +66 -0
- metadata +57 -56
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
#require 'yaml'
|
3
|
+
#require 'marshal'
|
4
|
+
|
5
|
+
module ParseTreeComm
|
6
|
+
SERIALIZE=Marshal
|
7
|
+
def put o
|
8
|
+
o=SERIALIZE.dump o
|
9
|
+
msg= o.size.to_s+"\n"+o+"\n"
|
10
|
+
begin
|
11
|
+
@out.write msg
|
12
|
+
@out.flush
|
13
|
+
rescue Exception
|
14
|
+
@out=@in=nil
|
15
|
+
raise
|
16
|
+
end
|
17
|
+
end
|
18
|
+
def get
|
19
|
+
begin
|
20
|
+
len=@in.gets.to_i
|
21
|
+
msg=@in.read(len)
|
22
|
+
@in.getc #read trailing \n
|
23
|
+
rescue Exception
|
24
|
+
@in=@out=nil
|
25
|
+
raise
|
26
|
+
end
|
27
|
+
result=SERIALIZE.load msg
|
28
|
+
return result
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class ParseTreeServer
|
33
|
+
include ParseTreeComm
|
34
|
+
|
35
|
+
def self.path_to_server_command
|
36
|
+
File.expand_path __FILE__
|
37
|
+
end
|
38
|
+
|
39
|
+
def ensure_parse_tree_and_1_8
|
40
|
+
if ::RUBY_VERSION[/^\d+\.\d+/].to_f>1.8
|
41
|
+
ruby18=ENV['RUBY1_8']||fail("you must use ruby <= 1.8 (with parse_tree) or set RUBY1_8 env to a 1.8 interpreter")
|
42
|
+
exec ruby18, $0
|
43
|
+
else
|
44
|
+
begin require 'rubygems'; rescue LoadError; end
|
45
|
+
|
46
|
+
if File.exist? find_home+"/.redparse/parse_tree_server.rc"
|
47
|
+
$:.concat File.readlines(find_home+"/.redparse/parse_tree_server.rc").map{|l| l.chop }
|
48
|
+
end
|
49
|
+
|
50
|
+
require 'parse_tree'
|
51
|
+
end
|
52
|
+
rescue Exception=>e
|
53
|
+
put e
|
54
|
+
put nil
|
55
|
+
put nil
|
56
|
+
raise
|
57
|
+
end
|
58
|
+
|
59
|
+
def main
|
60
|
+
si=STDIN
|
61
|
+
so=STDOUT
|
62
|
+
@out=so; @in=si
|
63
|
+
ensure_parse_tree_and_1_8
|
64
|
+
begin
|
65
|
+
warnstash=Tempfile.new "warnstash"
|
66
|
+
STDERR.reopen warnstash
|
67
|
+
instance=ParseTree.new
|
68
|
+
while true
|
69
|
+
str=get
|
70
|
+
exit! if str==:exit!
|
71
|
+
if str==:version
|
72
|
+
put ::RUBY_VERSION
|
73
|
+
next
|
74
|
+
end
|
75
|
+
|
76
|
+
pos=STDERR.pos
|
77
|
+
|
78
|
+
tree=
|
79
|
+
begin
|
80
|
+
instance.parse_tree_for_string(str) #tree
|
81
|
+
rescue Exception=>e;
|
82
|
+
tree=e
|
83
|
+
end
|
84
|
+
put tree
|
85
|
+
|
86
|
+
open(STDERR.path){|f|
|
87
|
+
f.pos=pos
|
88
|
+
put warnings=f.read.split #warnings
|
89
|
+
}
|
90
|
+
end
|
91
|
+
rescue Exception=>e; put e; raise
|
92
|
+
ensure exit!
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Finds the user's home directory.
|
98
|
+
#--
|
99
|
+
# Some comments from the ruby-talk list regarding finding the home
|
100
|
+
# directory:
|
101
|
+
#
|
102
|
+
# I have HOME, USERPROFILE and HOMEDRIVE + HOMEPATH. Ruby seems
|
103
|
+
# to be depending on HOME in those code samples. I propose that
|
104
|
+
# it should fallback to USERPROFILE and HOMEDRIVE + HOMEPATH (at
|
105
|
+
# least on Win32).
|
106
|
+
#(originally stolen from rubygems)
|
107
|
+
def find_home
|
108
|
+
['HOME', 'USERPROFILE'].each do |homekey|
|
109
|
+
return ENV[homekey] if ENV[homekey]
|
110
|
+
end
|
111
|
+
|
112
|
+
if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
|
113
|
+
return "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}"
|
114
|
+
end
|
115
|
+
|
116
|
+
begin
|
117
|
+
File.expand_path("~")
|
118
|
+
rescue
|
119
|
+
if File::ALT_SEPARATOR then
|
120
|
+
"C:/"
|
121
|
+
else
|
122
|
+
"/"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
ParseTreeServer.new.main if $0==__FILE__
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class RedParse
|
2
|
+
def self.remove_silly_begins(pt)
|
3
|
+
pt.each_with_index{|x,i|
|
4
|
+
if Array===x
|
5
|
+
remove_silly_begins(x)
|
6
|
+
if x.size==2 and x.first==:begin
|
7
|
+
pt[i]=x=x.last
|
8
|
+
end
|
9
|
+
end
|
10
|
+
}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
__END__
|
15
|
+
begin require 'rubygems'
|
16
|
+
rescue LoadError; #do nothing
|
17
|
+
end
|
18
|
+
|
19
|
+
have_graphwalk=true
|
20
|
+
begin require 'ron/graphedge'
|
21
|
+
rescue LoadError;
|
22
|
+
warn 'Ron::GraphWalk not found; some tests will be too strict'
|
23
|
+
have_graphwalk=false
|
24
|
+
end
|
25
|
+
|
26
|
+
unless have_graphwalk
|
27
|
+
class RedParse
|
28
|
+
def self.remove_silly_begins(pt) pt end
|
29
|
+
end
|
30
|
+
else
|
31
|
+
class RedParse
|
32
|
+
def self.remove_silly_begins(pt)
|
33
|
+
munger=proc{|cntr,o,i,ty,useit|
|
34
|
+
if Array===o and o.size==2 and o.first==:begin
|
35
|
+
useit[0]=true
|
36
|
+
Ron::GraphWalk.graphcopy(o.last,&munger)
|
37
|
+
end
|
38
|
+
}
|
39
|
+
Ron::GraphWalk.graphcopy(pt,&munger)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
@@ -20,7 +20,7 @@
|
|
20
20
|
unless Array===(Class*5).subregs
|
21
21
|
module ::Reg
|
22
22
|
class Repeat
|
23
|
-
undef subregs if
|
23
|
+
undef subregs if allocate.respond_to? :subregs
|
24
24
|
def subregs
|
25
25
|
[@reg]
|
26
26
|
end
|
@@ -29,7 +29,7 @@
|
|
29
29
|
end
|
30
30
|
|
31
31
|
|
32
|
-
unless defined? ::Reg::Transform and ::Reg::Transform.ancestors.include? ::Reg::
|
32
|
+
unless defined? ::Reg::Transform #and ::Reg::Transform.ancestors.include? ::Reg::HasBmatch
|
33
33
|
#hack, until support for this syntax makes it into the release of reg
|
34
34
|
module ::Reg
|
35
35
|
class Transform;
|
@@ -46,7 +46,7 @@
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
unless ::Reg::Reg.
|
49
|
+
unless Object.allocate.extend(::Reg::Reg).respond_to? :lb
|
50
50
|
module ::Reg
|
51
51
|
module Reg
|
52
52
|
def lb
|
@@ -73,7 +73,7 @@
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
unless ::Reg::Reg.
|
76
|
+
unless Object.allocate.extend(::Reg::Reg).respond_to? :la
|
77
77
|
module ::Reg
|
78
78
|
module Reg
|
79
79
|
def la
|
@@ -100,7 +100,7 @@
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
-
unless ::Reg::Reg.
|
103
|
+
unless Object.allocate.extend(::Reg::Reg).respond_to? :watch
|
104
104
|
module ::Reg
|
105
105
|
module Reg
|
106
106
|
def watch
|
data/lib/redparse/version.rb
CHANGED
data/redparse.gemspec
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "#{File.dirname(__FILE__)}/lib/redparse/version"
|
4
|
+
RedParse::Description=open("README.txt"){|f| f.read[/^==+ ?description[^\n]*?\n *\n?(.*?\n *\n.*?)\n *\n/im,1] }
|
5
|
+
RedParse::Latest_changes="###"+open("History.txt"){|f| f.read[/\A===(.*?)(?====)/m,1] }
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "redparse"
|
9
|
+
s.version = RedParse::VERSION
|
10
|
+
s.date = Time.now.strftime("%Y-%m-%d")
|
11
|
+
s.authors = ["Caleb Clausen"]
|
12
|
+
s.email = %q{caleb (at) inforadical (dot) net}
|
13
|
+
s.summary = "RedParse is a ruby parser written in pure ruby."
|
14
|
+
s.description = RedParse::Description
|
15
|
+
s.homepage = %{http://github.com/coatl/redparse}
|
16
|
+
s.rubyforge_project = %q{redparse}
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split
|
19
|
+
s.test_files = %w[test/test_all.rb]
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.extra_rdoc_files = ["README.txt", "COPYING.LGPL"]
|
22
|
+
s.has_rdoc = true
|
23
|
+
s.rdoc_options = %w[--main README.txt]
|
24
|
+
|
25
|
+
s.rubygems_version = %q{1.3.0}
|
26
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
27
|
+
|
28
|
+
if s.respond_to? :specification_version then
|
29
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
30
|
+
s.specification_version = 2
|
31
|
+
|
32
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
33
|
+
s.add_runtime_dependency("rubylexer", '0.7.7')
|
34
|
+
s.add_runtime_dependency("reg", ['>= 0.4.8'])
|
35
|
+
else
|
36
|
+
s.add_dependency("rubylexer", '0.7.7')
|
37
|
+
s.add_dependency("reg", ['>= 0.4.8'])
|
38
|
+
end
|
39
|
+
else
|
40
|
+
s.add_dependency("rubylexer", '0.7.7')
|
41
|
+
s.add_dependency("reg", ['>= 0.4.8'])
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'parse_tree'
|
4
|
+
|
5
|
+
##
|
6
|
+
# Finds the user's home directory.
|
7
|
+
#--
|
8
|
+
# Some comments from the ruby-talk list regarding finding the home
|
9
|
+
# directory:
|
10
|
+
#
|
11
|
+
# I have HOME, USERPROFILE and HOMEDRIVE + HOMEPATH. Ruby seems
|
12
|
+
# to be depending on HOME in those code samples. I propose that
|
13
|
+
# it should fallback to USERPROFILE and HOMEDRIVE + HOMEPATH (at
|
14
|
+
# least on Win32).
|
15
|
+
#(originally stolen from rubygems)
|
16
|
+
def find_home
|
17
|
+
['HOME', 'USERPROFILE'].each do |homekey|
|
18
|
+
return ENV[homekey] if ENV[homekey]
|
19
|
+
end
|
20
|
+
|
21
|
+
if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
|
22
|
+
return "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}"
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
File.expand_path("~")
|
27
|
+
rescue
|
28
|
+
if File::ALT_SEPARATOR then
|
29
|
+
"C:/"
|
30
|
+
else
|
31
|
+
"/"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
PARSETREE_AND_DEPENDANCIES=
|
37
|
+
%r{/(?:ZenTest|RubyInline|sexp_processor|ParseTree)-}
|
38
|
+
|
39
|
+
pt_needs_dirs=$:.grep PARSETREE_AND_DEPENDANCIES
|
40
|
+
|
41
|
+
open find_home+"/.redparse/parse_tree_server.rc","w" do |rcfile|
|
42
|
+
rcfile.write pt_needs_dirs.join("\n")+"\n"
|
43
|
+
end
|
data/test/rp-locatetest.rb
CHANGED
@@ -17,6 +17,43 @@
|
|
17
17
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
18
18
|
=end
|
19
19
|
|
20
|
+
|
21
|
+
=begin
|
22
|
+
rp-locatetest passes all the ruby files it can find on the system (using
|
23
|
+
the unix command locate) to bin/redparse --vsparsetree. In other words,
|
24
|
+
it's comparing the output of RedParse (when cvt'd to a ParseTree-style
|
25
|
+
tree) to the actual output of ParseTree.
|
26
|
+
|
27
|
+
I usually run it like this:
|
28
|
+
|
29
|
+
nice ruby test/rp-locatetest.rb >test.log 2>&1 &
|
30
|
+
|
31
|
+
That runs rp-locatetest at low priority, in the background, saving
|
32
|
+
stdout and stderr to test.log.
|
33
|
+
|
34
|
+
You can then trawl through test.log, looking for lines that don't start
|
35
|
+
with 'no differences in '. (Other than the page of warnings in the
|
36
|
+
beginning, which is repeated every so often.) Or, you can look for
|
37
|
+
non-commented lines in the file 'problemfiles'. (Which is just a list of
|
38
|
+
files that caused failures in rp-locatetest.)
|
39
|
+
|
40
|
+
Ok, this is more complicated than it ought to be. Sorry.
|
41
|
+
|
42
|
+
rp-locatetest takes a LONG time to run, and hogs the cpu at 100%. That's
|
43
|
+
why I was using nice. So, make sure it's ok to do that on whatever
|
44
|
+
server you run it on. I always get impatient and kill it after some
|
45
|
+
hours or days. On a subsequent run it'll pick up again where it left
|
46
|
+
off.
|
47
|
+
|
48
|
+
Right now, about 0.5% of my ruby files cause problems for rp-locatetest.
|
49
|
+
Most of those failures correspond to known problems (the file is listed
|
50
|
+
at the end of the README and/or there's a case in test_redparse.rb for
|
51
|
+
that failure).
|
52
|
+
=end
|
53
|
+
|
54
|
+
$VERBOSE=1
|
55
|
+
$Debug=1
|
56
|
+
|
20
57
|
#require 'test/code/rubylexervsruby'
|
21
58
|
#require 'test/code/strgen'
|
22
59
|
require "redparse/problemfiles"
|
@@ -152,7 +189,8 @@ until testdata.empty?
|
|
152
189
|
|
153
190
|
# puts "testing: "+chunk.join(" ")
|
154
191
|
chunk.empty? and next
|
155
|
-
system $RUBY, "-Ilib", "bin/redparse", "--update-problemfiles", "-
|
192
|
+
system $RUBY, "-Ilib", "bin/redparse", "--update-problemfiles", "--ignore-silly-begins", "--unparse",
|
193
|
+
"-q", "--vsparsetree", *chunk
|
156
194
|
exit 2 if $?>>8 == 2 #exit if child got ^c
|
157
195
|
end
|
158
196
|
end
|
@@ -343,3 +381,5 @@ __END__
|
|
343
381
|
/usr/lib/ruby/1.8/hpricot/traverse.rb
|
344
382
|
/home/caleb/sandbox/rubylexer/jewels/net-netrc-0.2.1/lib/net/netrc.rb
|
345
383
|
/home/caleb/sandbox/rubylexer/jewels/etc-0.2.0/lib/etc.rb
|
384
|
+
|
385
|
+
|
data/test/test_1.9.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubylexer/test/oneliners_1.9'
|
3
|
+
require "redparse"
|
4
|
+
|
5
|
+
class TestsFor1_9 < Test::Unit::TestCase
|
6
|
+
RUBY_1_9_TO_1_8_EQUIVALENCES=[
|
7
|
+
'{a: b}'...'{:a=>b}',
|
8
|
+
'{a: b, c: d}'...'{:a=>b, :c=>d}',
|
9
|
+
"a ? b\n : c"..."a ? b : \n c",
|
10
|
+
|
11
|
+
'not(true)'...'not true',
|
12
|
+
'not(+1)'...'not +1',
|
13
|
+
'not (true).to_s'...'not (true).to_s', #equivalent, but parser gets there different ways
|
14
|
+
]
|
15
|
+
|
16
|
+
RUBY_1_9_TO_1_8_EQUIVALENCES_BUT_FOR_STRESC=[
|
17
|
+
'"foo"'...'"foo"',
|
18
|
+
'"foo#{bar}"'...'"foo#{bar}"',
|
19
|
+
|
20
|
+
'%W[foo]'...'%W[foo]',
|
21
|
+
'%W[foo#{bar}]'...'%W[foo#{bar}]',
|
22
|
+
|
23
|
+
'`foo`'...'`foo`',
|
24
|
+
'`foo#{bar}`'...'`foo#{bar}`',
|
25
|
+
|
26
|
+
'//'...'//',
|
27
|
+
'/aa/i'...'/aa/i',
|
28
|
+
'/a#{a}/u'...'/a#{a}/u',
|
29
|
+
'/a#{"a"}/e'...'/a#{"a"}/e',
|
30
|
+
'/b#{__FILE__}/s'...'/b#{__FILE__}/s',
|
31
|
+
'/bb/m'...'/bb/m',
|
32
|
+
'/b b/x'...'/b b/x',
|
33
|
+
'/b#{b}/o'...'/b#{b}/o',
|
34
|
+
]
|
35
|
+
|
36
|
+
|
37
|
+
RUBY_1_9_VALID=[
|
38
|
+
'not (true).to_s',
|
39
|
+
]
|
40
|
+
|
41
|
+
include RedParse::Nodes
|
42
|
+
RUBY_1_9_PATTERNS={
|
43
|
+
'not(true).to_s'=>+CallNode[+UnOpNode["not", +VarLikeNode["true"]], "to_s"],
|
44
|
+
}
|
45
|
+
|
46
|
+
def test_ruby19_equivs
|
47
|
+
RUBY_1_9_TO_1_8_EQUIVALENCES.each{|pair|
|
48
|
+
new,old=pair.first,pair.last
|
49
|
+
pt19=RedParse.new(new,'(eval)',1,[],:rubyversion=>1.9,:cache_mode=>:none).parse
|
50
|
+
pt18=RedParse.new(old,'(eval)',1,[],:cache_mode=>:none).parse
|
51
|
+
assert_equal pt18,pt19
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_ruby19_equivs_but_for_stresc
|
56
|
+
RUBY_1_9_TO_1_8_EQUIVALENCES_BUT_FOR_STRESC.each{|pair|
|
57
|
+
new,old=pair.first,pair.last
|
58
|
+
pt19=RedParse.new(new,'(eval)',1,[],:rubyversion=>1.9,:cache_mode=>:none).parse
|
59
|
+
pt18=RedParse.new(old,'(eval)',1,[],:cache_mode=>:none).parse
|
60
|
+
if pt18.instance_variable_get(:@bs_handler)==:dquote_esc_seq
|
61
|
+
pt18.instance_variable_set :@bs_handler,:dquote19_esc_seq
|
62
|
+
else
|
63
|
+
pt18.instance_variable_set :@bs_handler,:Wquote19_esc_seq
|
64
|
+
pt18.instance_variable_get(:@parses_like).each{|x|
|
65
|
+
x.instance_variable_set :@bs_handler,:Wquote19_esc_seq if x.instance_variable_get :@bs_handler
|
66
|
+
}
|
67
|
+
end
|
68
|
+
assert_equal pt18,pt19
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_ruby19_valid
|
73
|
+
RUBY_1_9_VALID.each{|xmpl|
|
74
|
+
pt19=RedParse.new(xmpl,'(eval)',1,[],:rubyversion=>1.9,:cache_mode=>:none).parse
|
75
|
+
assert_nil pt19.errors
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_ruby19_patterns
|
80
|
+
RUBY_1_9_PATTERNS.each_pair{|code,pattern|
|
81
|
+
pt=RedParse.new(code,'(eval)',1,[],:rubyversion=>1.9,:cache_mode=>:none).parse
|
82
|
+
assert_match pattern, pt
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
include Ruby1_9OneLiners
|
90
|
+
|
91
|
+
def count_methods(tree)
|
92
|
+
count=0
|
93
|
+
tree.walk{|node|
|
94
|
+
case node
|
95
|
+
when CallSiteNode, MethodNode; count+=1
|
96
|
+
end
|
97
|
+
}
|
98
|
+
return count
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_1_9
|
102
|
+
EXPECT_NO_METHODS.each{|snippet|
|
103
|
+
tree=RedParse.new(snippet,"-e").parse
|
104
|
+
count=count_methods(tree)
|
105
|
+
assert_equal count,0
|
106
|
+
}
|
107
|
+
|
108
|
+
EXPECT_1_METHOD.each{|snippet|
|
109
|
+
tree=RedParse.new(snippet,"-e").parse
|
110
|
+
count=count_methods(tree)
|
111
|
+
assert_equal count,1
|
112
|
+
}
|
113
|
+
end
|
114
|
+
end
|