rubymacros 0.1.3 → 0.1.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 +13 -4
- data/Manifest.txt +4 -1
- data/README.txt +6 -5
- data/Rakefile +4 -3
- data/TODO +35 -0
- data/example/andand.rb +7 -0
- data/example/andand_wrap.rb +2 -0
- data/example/assert.rb +2 -2
- data/lib/macro.rb +36 -18
- data/lib/macro/form.rb +3 -3
- data/lib/macro/version.rb +1 -1
- data/lib/weakkeyhash.rb +17 -0
- data/test/test_expand.rb +2 -2
- data/test/test_form.rb +1 -1
- metadata +8 -5
- data/rubymacros.vpj +0 -87
data/History.txt
CHANGED
@@ -1,6 +1,16 @@
|
|
1
|
-
=== 0.1.
|
1
|
+
=== 0.1.4 / 2009-05-21
|
2
|
+
* 1 Major Enhancement:
|
3
|
+
* line numbers are now preserved in preprocessed code;
|
4
|
+
* backtraces should make more sense.
|
5
|
+
* 1 Minor Enhancement:
|
6
|
+
* updated to keep in sync with the latest RedParse api (sigh)
|
7
|
+
|
8
|
+
=== 0.1.3 / 2009-05-02
|
9
|
+
* 1 Minor Enhancement:
|
10
|
+
* depend on redparse>=0.8.1, since 0.8.0 had a stupid permission problem
|
2
11
|
|
3
|
-
|
12
|
+
=== 0.1.2 / 2009-04-26
|
13
|
+
* 7 Minor Enhancements
|
4
14
|
* lots of nice comments added, thanks to Paul Brannan and Tatsuji Kawai
|
5
15
|
* Paul fixed the weird rdoc failure too!
|
6
16
|
* incorrect warning removed
|
@@ -10,7 +20,6 @@
|
|
10
20
|
* all files should be world-readable now
|
11
21
|
|
12
22
|
=== 0.1.0 / 2008-10-24
|
13
|
-
|
14
|
-
* 1 major enhancement
|
23
|
+
* 1 Major Enhancement
|
15
24
|
* Birthday!
|
16
25
|
|
data/Manifest.txt
CHANGED
@@ -2,7 +2,6 @@ COPYING.LGPL
|
|
2
2
|
Manifest.txt
|
3
3
|
README.txt
|
4
4
|
History.txt
|
5
|
-
rubymacros.vpj
|
6
5
|
Rakefile
|
7
6
|
test/test_form.rb
|
8
7
|
test/test_expand.rb
|
@@ -23,3 +22,7 @@ example/with_wrap.rb
|
|
23
22
|
example/linenum.rb
|
24
23
|
example/linenum_user.rb
|
25
24
|
example/linenum_wrap.rb
|
25
|
+
example/andand_wrap.rb
|
26
|
+
lib/weakkeyhash.rb
|
27
|
+
test/test_all.rb
|
28
|
+
TODO
|
data/README.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= RubyMacros
|
2
|
-
* http://rubymacros.rubyforge.org
|
3
2
|
* http://rubyforge.org/projects/rubymacros
|
3
|
+
* http://github.com/coatl/rubymacros
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
RubyMacros is a lisp-like macro pre-processor for Ruby. More than just a
|
@@ -32,7 +32,6 @@ a proof of concept or toy at this point:
|
|
32
32
|
* some ruby syntax is unsupported in files using macros
|
33
33
|
* files using macros must be loaded via Macro.require;
|
34
34
|
* Kernel#require will not recognize macros
|
35
|
-
* RedParse Node tree format will be changing slightly
|
36
35
|
* macros cannot be scoped
|
37
36
|
* no variable (or other) hygiene
|
38
37
|
|
@@ -95,10 +94,12 @@ macro callsite in the parsetree which contained it.
|
|
95
94
|
|
96
95
|
|
97
96
|
== Known Problems
|
98
|
-
* need to insert extra parens around form params
|
97
|
+
* need to insert extra parens around form params
|
99
98
|
* a variety of parsetrees are kept around forever for no good reason
|
100
|
-
* a few
|
101
|
-
*
|
99
|
+
* a few disabled tests in unit tests
|
100
|
+
* =begin...=end in forms causes minor unit test failures
|
101
|
+
* unterminated string in forms is not handled well
|
102
|
+
|
102
103
|
|
103
104
|
== License:
|
104
105
|
Copyright (C) 2008 Caleb Clausen
|
data/Rakefile
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
# Distributed under the terms of Ruby's license.
|
3
3
|
require 'rubygems'
|
4
4
|
require 'hoe'
|
5
|
-
require 'lib/macro/version.rb'
|
6
5
|
|
7
6
|
|
8
7
|
if $*==["test"]
|
@@ -16,6 +15,8 @@ if $*==["test"]
|
|
16
15
|
exit
|
17
16
|
end
|
18
17
|
|
18
|
+
require 'lib/macro/version.rb'
|
19
|
+
|
19
20
|
readme=open("README.txt")
|
20
21
|
readme.readline("\n== DESCRIPTION:")
|
21
22
|
readme.readline("\n\n")
|
@@ -24,8 +25,8 @@ end
|
|
24
25
|
hoe=Hoe.new("rubymacros", Macro::VERSION) do |_|
|
25
26
|
_.author = "Caleb Clausen"
|
26
27
|
_.email = "rubymacros-owner @at@ inforadical .dot. net"
|
27
|
-
_.url = ["http://
|
28
|
-
_.extra_deps << ['redparse', '>= 0.8.
|
28
|
+
_.url = ["http://github.com/coatl/rubymacros/", "http://rubyforge.org/projects/rubymacros/"]
|
29
|
+
_.extra_deps << ['redparse', '>= 0.8.2']
|
29
30
|
_.test_globs=["test/*"]
|
30
31
|
_.description=desc
|
31
32
|
_.summary=desc[/\A[^.]+\./]
|
data/TODO
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
macro options
|
2
|
+
immediate vs delayed
|
3
|
+
'protected' vs unprotected macros -- params and result surrounded by ()
|
4
|
+
precedence for operator macros
|
5
|
+
hygienic vs unhygienic
|
6
|
+
|
7
|
+
static selector namespaces?
|
8
|
+
|
9
|
+
other types of macro:
|
10
|
+
replacement by example -- block style
|
11
|
+
replacement by example -- form with matchers in form escapes
|
12
|
+
straight out Reg::Transform expression as macro
|
13
|
+
operator macros
|
14
|
+
lmacros
|
15
|
+
|
16
|
+
parser extensions to make the language more macro-friendly:
|
17
|
+
dynamically named callsites: rcvr.( a<b ? :foo : :bar )(args)
|
18
|
+
dynamically named constant lookups: Parent::(whatever)
|
19
|
+
arbitrary expression in block slot of callsite:
|
20
|
+
foo.bar(args)block
|
21
|
+
foo.bar(args)bl.ock
|
22
|
+
foo.bar(args)(...whatever)
|
23
|
+
(but don't allow funny characters here (unless parenthesised))
|
24
|
+
block literals. empty block is {||}
|
25
|
+
else block and hash disambiguated by presence of => or :
|
26
|
+
dynamic method names?: def (expr returning name); ... end
|
27
|
+
|
28
|
+
|
29
|
+
cool macros to write:
|
30
|
+
,, -- evaluates left and right sides then returns val of LEFT
|
31
|
+
.? -- call a method if rcvr respond_to? it
|
32
|
+
{<..>} -- ordered hash
|
33
|
+
trace_subexpressions() -- print val of each subexpression
|
34
|
+
better assert
|
35
|
+
iter
|
data/example/andand.rb
CHANGED
data/example/assert.rb
CHANGED
@@ -4,8 +4,8 @@ if $Debug
|
|
4
4
|
macro assert(cond)
|
5
5
|
if RedParse::OpNode===cond and /\A[=!]=\Z/===cond.op
|
6
6
|
left,op,right=*cond
|
7
|
-
:(fail 'expected '+^left.unparse
|
8
|
-
^op+" "+^right.unparse
|
7
|
+
:(fail 'expected '+^left.unparse+"(==#{^left}) to be "+
|
8
|
+
^op+" "+^right.unparse+"(==#{^right})" unless ^cond)
|
9
9
|
else
|
10
10
|
:(fail "expected #{:(^^cond)}, but was not true" unless ^cond)
|
11
11
|
end
|
data/lib/macro.rb
CHANGED
@@ -24,7 +24,7 @@ require 'redparse'
|
|
24
24
|
require "macro/form"
|
25
25
|
require "macro/version"
|
26
26
|
|
27
|
-
warn "need to insert extra parens around form params
|
27
|
+
warn "need to insert extra parens around form params"
|
28
28
|
|
29
29
|
class Object # :nodoc:
|
30
30
|
#as close as I can get to an empty binding (used below in Macro.eval)
|
@@ -168,7 +168,7 @@ class Macro
|
|
168
168
|
result={}
|
169
169
|
seen[obj.__id__]=result
|
170
170
|
obj.each_pair{|k,v|
|
171
|
-
result[copy k]=copy v,seen
|
171
|
+
result[copy( k )]=copy v,seen
|
172
172
|
}
|
173
173
|
result
|
174
174
|
when Module,Proc,IO,Method,
|
@@ -209,9 +209,9 @@ class Macro
|
|
209
209
|
modpath=ConstantNode[nil,*session[:@modpath]]
|
210
210
|
modpath.push "Object" unless modpath.size>1
|
211
211
|
if session[:@namespace_type]==ModuleNode
|
212
|
-
node=ModuleNode[modpath,node] #:(module ^modpath; ^node; end)
|
212
|
+
node=ModuleNode[modpath,node,[],nil,nil] #:(module ^modpath; ^node; end)
|
213
213
|
else
|
214
|
-
node=ClassNode[modpath,nil,node] #:(class ^modpath; ^node; end)
|
214
|
+
node=ClassNode[modpath,nil,node,[],nil,nil] #:(class ^modpath; ^node; end)
|
215
215
|
end
|
216
216
|
end
|
217
217
|
|
@@ -228,7 +228,7 @@ class Macro
|
|
228
228
|
[unexpanded, ConstantNode[nil,"Macro","GLOBALS"],
|
229
229
|
HashLiteralNode[LiteralNode[:@expand_in_defs], VarLikeNode["true"]], Macro.quote(filename)],
|
230
230
|
nil,nil]
|
231
|
-
return CallNode[expanded,evalname,[Macro.quote filename],nil,nil]
|
231
|
+
return CallNode[expanded,evalname,[Macro.quote( filename )],nil,nil]
|
232
232
|
end
|
233
233
|
|
234
234
|
class Node
|
@@ -257,7 +257,7 @@ class Macro
|
|
257
257
|
|
258
258
|
expanded_tree=self #Macro.expand(deep_copy,::Macro::GLOBALS)
|
259
259
|
|
260
|
-
unparsed=expanded_tree.unparse
|
260
|
+
unparsed=expanded_tree.unparse
|
261
261
|
#puts unparsed
|
262
262
|
::Kernel.eval unparsed, binding||::Object.new_binding,file||'(eval)',line||1
|
263
263
|
end
|
@@ -286,7 +286,7 @@ class Macro
|
|
286
286
|
true
|
287
287
|
}
|
288
288
|
|
289
|
-
unparsed=expanded_tree.unparse
|
289
|
+
unparsed=expanded_tree.unparse
|
290
290
|
#p expanded_tree
|
291
291
|
#puts unparsed
|
292
292
|
Tempfile.open("macroexpanded_"+name.gsub("/","__")){|tf|
|
@@ -480,8 +480,8 @@ class Macro
|
|
480
480
|
|
481
481
|
# and keep recursing, no matter what, by all means!!
|
482
482
|
newnode=Macro.expand newnode,macros,session #just do it here
|
483
|
-
|
484
|
-
|
483
|
+
newnode=OneLineParenedNode[newnode] #disable newlines in macro text
|
484
|
+
return newnode,false #and not in caller
|
485
485
|
end
|
486
486
|
end
|
487
487
|
|
@@ -529,7 +529,14 @@ class Macro
|
|
529
529
|
end
|
530
530
|
session[:@modpath].push *name
|
531
531
|
|
532
|
-
|
532
|
+
map!{|n|
|
533
|
+
case n
|
534
|
+
when nil
|
535
|
+
when Node; Macro.expand(n,macros,session)
|
536
|
+
when Array; n.map!{|nn| Macro.expand(nn,macros,session) }
|
537
|
+
else fail
|
538
|
+
end
|
539
|
+
}
|
533
540
|
|
534
541
|
if old_modpath
|
535
542
|
session[:@modpath]=old_modpath
|
@@ -610,7 +617,7 @@ class Macro
|
|
610
617
|
carets+=1
|
611
618
|
end
|
612
619
|
if carets==nest
|
613
|
-
return node.unparse
|
620
|
+
return node.unparse
|
614
621
|
else
|
615
622
|
return super
|
616
623
|
end
|
@@ -646,7 +653,7 @@ class Macro
|
|
646
653
|
#
|
647
654
|
# +o+:: a list of options for unparse
|
648
655
|
#
|
649
|
-
def unparse o
|
656
|
+
def unparse o=default_unparse_options
|
650
657
|
result="macro "
|
651
658
|
result+=receiver.unparse(o)+'.' if receiver
|
652
659
|
result+=name
|
@@ -655,12 +662,23 @@ class Macro
|
|
655
662
|
result+=args.map{|arg| arg.unparse o}.join','
|
656
663
|
result+=")"
|
657
664
|
end
|
658
|
-
result+=
|
659
|
-
result+=body.unparse(o) if body
|
665
|
+
result+=unparse_nl(body,o)+body.unparse(o) if body
|
660
666
|
result+=rescues.map{|resc| resc.unparse o}.to_s
|
661
|
-
result+="else "+else_.unparse( o )+"\n" if else_
|
662
|
-
result+="ensure "+ensure_.unparse( o )+"\n" if ensure_
|
663
|
-
result+="
|
667
|
+
result+=unparse_nl(else_,o)+"else "+else_.unparse( o )+"\n" if else_
|
668
|
+
result+=unparse_nl(ensure_,o)+"ensure "+ensure_.unparse( o )+"\n" if ensure_
|
669
|
+
result+=";end"
|
670
|
+
return result
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
class OneLineParenedNode < ParenedNode
|
675
|
+
#hacky way to get unparser to not emit newlines in most cases
|
676
|
+
def unparse(o=default_parse_options)
|
677
|
+
old_linenum=o[:linenum]
|
678
|
+
o[:linenum]=2**128
|
679
|
+
result=super(o)
|
680
|
+
diff=o[:linenum]-2**128
|
681
|
+
o[:linenum]=old_linenum+diff
|
664
682
|
return result
|
665
683
|
end
|
666
684
|
end
|
@@ -674,7 +692,7 @@ class Macro
|
|
674
692
|
[
|
675
693
|
-[KW('macro'), KW(beginsendsmatcher).~.*, KW('end'), KW(/^(do|\{)$/).~.la]>>MisparsedNode
|
676
694
|
]+super+[
|
677
|
-
-[Op('^@'), Expr,
|
695
|
+
-[Op('^@'), Expr, lower_op()]>>FormParameterNode,
|
678
696
|
-[Op(':@'), (ParenedNode&-{:size=>1})|(VarLikeNode&-{:ident=>"nil"})]>>FormNode,
|
679
697
|
-['macro', CallSiteNode, KW(';'),
|
680
698
|
Expr.-, RescueNode.*, ElseNode.-, EnsureNode.-,
|
data/lib/macro/form.rb
CHANGED
@@ -110,7 +110,7 @@ class Macro
|
|
110
110
|
#
|
111
111
|
# +o+:: a list of options for unparse
|
112
112
|
#
|
113
|
-
def unparse o
|
113
|
+
def unparse o=default_unparse_options
|
114
114
|
":("+text.unparse(o)+")"
|
115
115
|
end
|
116
116
|
|
@@ -193,7 +193,7 @@ class Macro
|
|
193
193
|
#
|
194
194
|
# +o+:: a list of options for unparse
|
195
195
|
#
|
196
|
-
def unparse o
|
196
|
+
def unparse o=default_unparse_options
|
197
197
|
"^"+val.unparse(o)
|
198
198
|
end
|
199
199
|
|
@@ -205,7 +205,7 @@ class Macro
|
|
205
205
|
end
|
206
206
|
|
207
207
|
def inspect
|
208
|
-
val.unparse
|
208
|
+
val.unparse
|
209
209
|
end
|
210
210
|
end
|
211
211
|
end
|
data/lib/macro/version.rb
CHANGED
data/lib/weakkeyhash.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class WeakKeyHash<Hash
|
2
|
+
def initialize(&block)
|
3
|
+
super
|
4
|
+
end
|
5
|
+
|
6
|
+
|
7
|
+
def [](key)
|
8
|
+
super key.__id__
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(key,val)
|
12
|
+
ObjectSpace.define_finalizer(key,&method :delete)
|
13
|
+
super key.__id__,val
|
14
|
+
end
|
15
|
+
|
16
|
+
#dunno how many more methods will actually work...
|
17
|
+
end
|
data/test/test_expand.rb
CHANGED
@@ -30,10 +30,10 @@ class ExpandTest < Test::Unit::TestCase
|
|
30
30
|
RedParse::LiteralNode[2]], nil, nil]], nil, nil]
|
31
31
|
ttt.macro_expand(Macro::GLOBALS,{})
|
32
32
|
|
33
|
-
assert_equal ttt.unparse
|
33
|
+
assert_equal ttt.unparse,'p((1+2))'
|
34
34
|
|
35
35
|
ttt=Macro.parse "p(simple(1,2))"
|
36
36
|
ttt.macro_expand(Macro::GLOBALS,{})
|
37
|
-
assert_equal ttt.unparse
|
37
|
+
assert_equal ttt.unparse,'p((1+2))'
|
38
38
|
end
|
39
39
|
end
|
data/test/test_form.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubymacros
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Caleb Clausen
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-05-
|
12
|
+
date: 2009-05-22 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0.8.
|
23
|
+
version: 0.8.2
|
24
24
|
version:
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: hoe
|
@@ -47,7 +47,6 @@ files:
|
|
47
47
|
- Manifest.txt
|
48
48
|
- README.txt
|
49
49
|
- History.txt
|
50
|
-
- rubymacros.vpj
|
51
50
|
- Rakefile
|
52
51
|
- test/test_form.rb
|
53
52
|
- test/test_expand.rb
|
@@ -68,8 +67,12 @@ files:
|
|
68
67
|
- example/linenum.rb
|
69
68
|
- example/linenum_user.rb
|
70
69
|
- example/linenum_wrap.rb
|
70
|
+
- example/andand_wrap.rb
|
71
|
+
- lib/weakkeyhash.rb
|
72
|
+
- test/test_all.rb
|
73
|
+
- TODO
|
71
74
|
has_rdoc: true
|
72
|
-
homepage: http://
|
75
|
+
homepage: http://github.com/coatl/rubymacros/
|
73
76
|
post_install_message:
|
74
77
|
rdoc_options:
|
75
78
|
- --main
|
data/rubymacros.vpj
DELETED
@@ -1,87 +0,0 @@
|
|
1
|
-
<!DOCTYPE Project SYSTEM "http://www.slickedit.com/dtd/vse/10.0/vpj.dtd">
|
2
|
-
<Project
|
3
|
-
Version="10.0"
|
4
|
-
VendorName="SlickEdit"
|
5
|
-
WorkingDir=".">
|
6
|
-
<Config
|
7
|
-
Name="Release"
|
8
|
-
OutputFile=""
|
9
|
-
CompilerConfigName="Latest Version">
|
10
|
-
<Menu>
|
11
|
-
<Target
|
12
|
-
Name="Compile"
|
13
|
-
MenuCaption="&Compile"
|
14
|
-
CaptureOutputWith="ProcessBuffer"
|
15
|
-
SaveOption="SaveCurrent"
|
16
|
-
RunFromDir="%rw">
|
17
|
-
<Exec/>
|
18
|
-
</Target>
|
19
|
-
<Target
|
20
|
-
Name="Build"
|
21
|
-
MenuCaption="&Build"
|
22
|
-
CaptureOutputWith="ProcessBuffer"
|
23
|
-
SaveOption="SaveWorkspaceFiles"
|
24
|
-
RunFromDir="%rw">
|
25
|
-
<Exec/>
|
26
|
-
</Target>
|
27
|
-
<Target
|
28
|
-
Name="Rebuild"
|
29
|
-
MenuCaption="&Rebuild"
|
30
|
-
CaptureOutputWith="ProcessBuffer"
|
31
|
-
SaveOption="SaveWorkspaceFiles"
|
32
|
-
RunFromDir="%rw">
|
33
|
-
<Exec/>
|
34
|
-
</Target>
|
35
|
-
<Target
|
36
|
-
Name="Debug"
|
37
|
-
MenuCaption="&Debug"
|
38
|
-
SaveOption="SaveNone"
|
39
|
-
RunFromDir="%rw">
|
40
|
-
<Exec/>
|
41
|
-
</Target>
|
42
|
-
<Target
|
43
|
-
Name="Execute"
|
44
|
-
MenuCaption="E&xecute"
|
45
|
-
SaveOption="SaveNone"
|
46
|
-
RunFromDir="%rw">
|
47
|
-
<Exec CmdLine='".exe"'/>
|
48
|
-
</Target>
|
49
|
-
</Menu>
|
50
|
-
</Config>
|
51
|
-
<CustomFolders>
|
52
|
-
<Folder
|
53
|
-
Name="Source Files"
|
54
|
-
Filters="*.c;*.C;*.cc;*.cpp;*.cp;*.cxx;*.prg;*.pas;*.dpr;*.asm;*.s;*.bas;*.java;*.cs;*.sc;*.e;*.cob;*.html;*.rc;*.tcl;*.py;*.pl"/>
|
55
|
-
<Folder
|
56
|
-
Name="Header Files"
|
57
|
-
Filters="*.h;*.H;*.hh;*.hpp;*.hxx;*.inc;*.sh;*.cpy;*.if"/>
|
58
|
-
<Folder
|
59
|
-
Name="Resource Files"
|
60
|
-
Filters="*.ico;*.cur;*.dlg"/>
|
61
|
-
<Folder
|
62
|
-
Name="Bitmaps"
|
63
|
-
Filters="*.bmp"/>
|
64
|
-
<Folder
|
65
|
-
Name="Other Files"
|
66
|
-
Filters="">
|
67
|
-
</Folder>
|
68
|
-
</CustomFolders>
|
69
|
-
<Files AutoFolders="DirectoryView">
|
70
|
-
<Folder Name="example">
|
71
|
-
<F N="example/assert.rb"/>
|
72
|
-
<F N="example/assert_wrap.rb"/>
|
73
|
-
<F N="example/simple.rb"/>
|
74
|
-
<F N="example/simple_wrap.rb"/>
|
75
|
-
</Folder>
|
76
|
-
<Folder Name="lib">
|
77
|
-
<Folder Name="macro">
|
78
|
-
<F N="lib/macro/form.rb"/>
|
79
|
-
</Folder>
|
80
|
-
<F N="lib/macro.rb"/>
|
81
|
-
<F N="lib/weakkeyhash.rb"/>
|
82
|
-
</Folder>
|
83
|
-
<Folder Name="test">
|
84
|
-
<F N="test/test_form.rb"/>
|
85
|
-
</Folder>
|
86
|
-
</Files>
|
87
|
-
</Project>
|