RubyToC 1.0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +69 -0
- data/Makefile +49 -0
- data/Manifest.txt +98 -0
- data/README.txt +75 -0
- data/demo/char.rb +13 -0
- data/demo/factorial.rb +11 -0
- data/demo/hello.rb +11 -0
- data/demo/misc.rb +25 -0
- data/demo/newarray.rb +11 -0
- data/demo/strcat.rb +12 -0
- data/rewrite.rb +32 -0
- data/rewriter.rb +356 -0
- data/ruby_to_c.rb +680 -0
- data/support.rb +317 -0
- data/test_all.rb +9 -0
- data/test_extras.rb +143 -0
- data/test_rewriter.rb +292 -0
- data/test_ruby_to_c.rb +533 -0
- data/test_support.rb +525 -0
- data/test_type_checker.rb +838 -0
- data/test_typed_sexp_processor.rb +134 -0
- data/translate.rb +31 -0
- data/type.rb +33 -0
- data/type_checker.rb +922 -0
- data/typed_sexp_processor.rb +88 -0
- data/validate.sh +49 -0
- data/zcomparable.rb +300 -0
- metadata +74 -0
data/History.txt
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
*** 1.0.0-beta-4 / 2005-07-13
|
2
|
+
|
3
|
+
+ 1 minor enhancements
|
4
|
+
+ Added gemspec (hastily).
|
5
|
+
+ 2 bug fixes
|
6
|
+
+ Translates bool type to VALUE since we were using Qtrue/Qfalse.
|
7
|
+
+ Fixed rubygems for non-gem systems.
|
8
|
+
|
9
|
+
*** 1.0.0-beta-3 / 2005-06-08
|
10
|
+
|
11
|
+
+ 16 minor enhancements
|
12
|
+
+ Added ivar and iasgn support. Needs more work.
|
13
|
+
+ Added limited support for self.
|
14
|
+
+ Added pipeline tests for bools, call_arglist, call_attrasgn, fbody.
|
15
|
+
+ Added process_not to RubyToC.
|
16
|
+
+ Added support for float and symbol literals.
|
17
|
+
+ Added support for gasgn, cvasgn, const (class consts, not classes).
|
18
|
+
+ Improved error handling/reporting, esp in RubyToC.
|
19
|
+
+ In TypeChecker.boostrap, pre-registered all base classes.
|
20
|
+
+ Modified process_class test to include a class const.
|
21
|
+
+ Processing :class now adds class constants to the local var scope.
|
22
|
+
+ Processing :const checks both genv and env now.
|
23
|
+
+ Rearchitected all tests into a pipeline test class.
|
24
|
+
+ Rewrite attrasgn into regular calls.
|
25
|
+
+ Rewrite fbody into a regular defn.
|
26
|
+
+ Rewrote :array inside call to :arglist.
|
27
|
+
+ Rewrote :or nodes in process_case to correctly be binary.
|
28
|
+
+ 1 bug fix:
|
29
|
+
+ Fixed a bug where single line while bodies were missing a semicolon.
|
30
|
+
|
31
|
+
*** 1.0.0-beta-2 / 2005-02-15
|
32
|
+
|
33
|
+
+ 1 minor enhancement
|
34
|
+
+ Added post-condition while/until support and tests.
|
35
|
+
+ 4 bug fixes
|
36
|
+
+ Fixed bug:1422: Escape newlines to prevent multi-line strings in C.
|
37
|
+
+ Fixed bug:1429: Arrays of strings are not being properly.
|
38
|
+
+ Fixed bug:1447/1448: Readme file's website and added ParseTree dependency.
|
39
|
+
|
40
|
+
*** 1.0.0-beta-1 / 2005-02-01
|
41
|
+
|
42
|
+
+ 1 major enhancements
|
43
|
+
+ Hit 80% non-error conversion threshold for public beta release.
|
44
|
+
+ 3 minor enhancements
|
45
|
+
+ (Mostly) Filled out functionality in Rewriter and TypeChecker.
|
46
|
+
+ Flushed out what we don't do in RubyToC.
|
47
|
+
+ Wrote a ton of rdoc
|
48
|
+
|
49
|
+
*** 1.0.0-a2 / 2004-12-31
|
50
|
+
|
51
|
+
+ 7 major enhancements
|
52
|
+
+ Alpha 2 released to private group for critique.
|
53
|
+
+ Refactored and split out ParseTree package.
|
54
|
+
+ Gemified dependency on ParseTree.
|
55
|
+
+ Added iter rewriting
|
56
|
+
+ Added post type inference rewriting specific to C library.
|
57
|
+
+ Massive increase to the base we can translate.
|
58
|
+
+ We have stabilized the architecture but still have a ways to go.
|
59
|
+
+ 2 minor enhancements
|
60
|
+
+ Added propaganda (presentations).
|
61
|
+
+ Much better test coverage, now with ZenTest compliant naming.
|
62
|
+
+ 2+ bug fixes
|
63
|
+
+ Gem-proofed makefile.
|
64
|
+
+ Tons of little fixes we didn't bother to track.
|
65
|
+
|
66
|
+
*** 1.0.0-a1 / 2004-09-24
|
67
|
+
|
68
|
+
+ 1 major enhancement
|
69
|
+
+ Birthday! Alpha 1 released to private group for critique.
|
data/Makefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
RUBY?=ruby
|
2
|
+
RUBY_FLAGS?=-w -I.:../../ParseTree/dev/lib:../../ParseTree/dev/test:../../RubyInline/dev
|
3
|
+
TEST?=
|
4
|
+
|
5
|
+
all test:
|
6
|
+
$(RUBY) $(RUBY_FLAGS) test_all.rb $(TEST)
|
7
|
+
|
8
|
+
docs:
|
9
|
+
rdoc -d -I png --main RubyToC -x test_\* -x something.rb
|
10
|
+
|
11
|
+
VAL=./validate.sh
|
12
|
+
FILE?=../../ZenTest/dev/TestOrderedHash.rb
|
13
|
+
|
14
|
+
valx:
|
15
|
+
$(VAL) $(FILE) > x
|
16
|
+
|
17
|
+
val:
|
18
|
+
$(VAL) -q $(FILE) | head -20
|
19
|
+
|
20
|
+
valno:
|
21
|
+
$(VAL) $(FILE) | grep no:
|
22
|
+
|
23
|
+
valstat:
|
24
|
+
BAD=$$(wc -l rb.bad.txt | perl -pe 's/\s*(\d+)\s+.*/$$1/'); GOOD=$$(wc -l rb.good.txt | perl -pe 's/\s*(\d+)\s+.*/$$1/'); echo "1 - $$BAD / ($$GOOD + $$BAD)" | bc -l
|
25
|
+
|
26
|
+
valoccur:
|
27
|
+
egrep "ERROR|no:" rb.err.txt | perl -pe 's/ in .*//; s/(translating \S+):/$$1/; s/(is not an Array \w+):.*/$$1/; s/.* (is not a supported node type)/blah $$1/; s/(Unable to unify).*/$$1/; s/(Unknown literal) \S+/$$1/;' | occur
|
28
|
+
|
29
|
+
trouble: trouble.o
|
30
|
+
@exit 0
|
31
|
+
|
32
|
+
trouble.o: trouble.c
|
33
|
+
gcc -I/usr/local/lib/ruby/1.8/powerpc-darwin/ -c trouble.c -o trouble.o
|
34
|
+
|
35
|
+
trouble.c: zcomparable.rb translate.rb ruby_to_c.rb type_checker.rb
|
36
|
+
$(RUBY) $(RUBY_FLAGS) translate.rb zcomparable.rb > trouble.c
|
37
|
+
|
38
|
+
FORCE:
|
39
|
+
demos: FORCE
|
40
|
+
for rf in demo/*.rb; do f=$$(basename $$rf .rb); echo $$f; ./translate.rb demo/$$f > demo/$$f.c && gcc -Iinc -o demo/$$f demo/$$f.c; done
|
41
|
+
|
42
|
+
interp: FORCE
|
43
|
+
for rf in demo/*.rb; do f=$$(basename $$rf .rb); echo $$f; ./interp.rb demo/$$f; done
|
44
|
+
|
45
|
+
clean:
|
46
|
+
rm -f *~ trouble.* diff.txt demo/*~
|
47
|
+
rm -rf ~/.ruby_inline
|
48
|
+
for rf in demo/*.rb; do f=$$(basename $$rf .rb); rm -f demo/$$f.c demo/$$f; done
|
49
|
+
|
data/Manifest.txt
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
History.txt
|
2
|
+
Makefile
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
demo/char.rb
|
6
|
+
demo/factorial.rb
|
7
|
+
demo/hello.rb
|
8
|
+
demo/misc.rb
|
9
|
+
demo/newarray.rb
|
10
|
+
demo/strcat.rb
|
11
|
+
propaganda/Ruby2C.key/.typeAttributes.dict
|
12
|
+
propaganda/Ruby2C.key/Contents/PkgInfo
|
13
|
+
propaganda/Ruby2C.key/pasted.pdf
|
14
|
+
propaganda/Ruby2C.key/pasted1.pdf
|
15
|
+
propaganda/Ruby2C.key/pasted10.pdf
|
16
|
+
propaganda/Ruby2C.key/pasted2.pdf
|
17
|
+
propaganda/Ruby2C.key/pasted3.pdf
|
18
|
+
propaganda/Ruby2C.key/pasted4.pdf
|
19
|
+
propaganda/Ruby2C.key/pasted5.pdf
|
20
|
+
propaganda/Ruby2C.key/pasted6.pdf
|
21
|
+
propaganda/Ruby2C.key/pasted7.pdf
|
22
|
+
propaganda/Ruby2C.key/pasted8.pdf
|
23
|
+
propaganda/Ruby2C.key/pasted9.pdf
|
24
|
+
propaganda/Ruby2C.key/presentation.apxl
|
25
|
+
propaganda/Ruby2C.key/thumbs/.rjsf
|
26
|
+
propaganda/Ruby2C.key/thumbs/mt0.tiff
|
27
|
+
propaganda/Ruby2C.key/thumbs/mt1.tiff
|
28
|
+
propaganda/Ruby2C.key/thumbs/mt10.tiff
|
29
|
+
propaganda/Ruby2C.key/thumbs/mt11.tiff
|
30
|
+
propaganda/Ruby2C.key/thumbs/mt12.tiff
|
31
|
+
propaganda/Ruby2C.key/thumbs/mt13.tiff
|
32
|
+
propaganda/Ruby2C.key/thumbs/mt14.tiff
|
33
|
+
propaganda/Ruby2C.key/thumbs/mt15.tiff
|
34
|
+
propaganda/Ruby2C.key/thumbs/mt2.tiff
|
35
|
+
propaganda/Ruby2C.key/thumbs/mt3.tiff
|
36
|
+
propaganda/Ruby2C.key/thumbs/mt4.tiff
|
37
|
+
propaganda/Ruby2C.key/thumbs/mt5.tiff
|
38
|
+
propaganda/Ruby2C.key/thumbs/mt6.tiff
|
39
|
+
propaganda/Ruby2C.key/thumbs/mt7.tiff
|
40
|
+
propaganda/Ruby2C.key/thumbs/mt8.tiff
|
41
|
+
propaganda/Ruby2C.key/thumbs/mt9.tiff
|
42
|
+
propaganda/Ruby2C.key/thumbs/st0.tiff
|
43
|
+
propaganda/Ruby2C.key/thumbs/st1.tiff
|
44
|
+
propaganda/Ruby2C.key/thumbs/st10.tiff
|
45
|
+
propaganda/Ruby2C.key/thumbs/st11.tiff
|
46
|
+
propaganda/Ruby2C.key/thumbs/st13.tiff
|
47
|
+
propaganda/Ruby2C.key/thumbs/st131.tiff
|
48
|
+
propaganda/Ruby2C.key/thumbs/st15.tiff
|
49
|
+
propaganda/Ruby2C.key/thumbs/st16.tiff
|
50
|
+
propaganda/Ruby2C.key/thumbs/st161.tiff
|
51
|
+
propaganda/Ruby2C.key/thumbs/st17.tiff
|
52
|
+
propaganda/Ruby2C.key/thumbs/st18.tiff
|
53
|
+
propaganda/Ruby2C.key/thumbs/st19.tiff
|
54
|
+
propaganda/Ruby2C.key/thumbs/st2.tiff
|
55
|
+
propaganda/Ruby2C.key/thumbs/st20.tiff
|
56
|
+
propaganda/Ruby2C.key/thumbs/st201.tiff
|
57
|
+
propaganda/Ruby2C.key/thumbs/st21.tiff
|
58
|
+
propaganda/Ruby2C.key/thumbs/st22.tiff
|
59
|
+
propaganda/Ruby2C.key/thumbs/st23.tiff
|
60
|
+
propaganda/Ruby2C.key/thumbs/st231.tiff
|
61
|
+
propaganda/Ruby2C.key/thumbs/st24.tiff
|
62
|
+
propaganda/Ruby2C.key/thumbs/st25.tiff
|
63
|
+
propaganda/Ruby2C.key/thumbs/st3.tiff
|
64
|
+
propaganda/Ruby2C.key/thumbs/st31.tiff
|
65
|
+
propaganda/Ruby2C.key/thumbs/st4.tiff
|
66
|
+
propaganda/Ruby2C.key/thumbs/st5.tiff
|
67
|
+
propaganda/Ruby2C.key/thumbs/st7.tiff
|
68
|
+
propaganda/Ruby2C.key/thumbs/st8.tiff
|
69
|
+
propaganda/Ruby2C.key/thumbs/st9.tiff
|
70
|
+
propaganda/Ruby2C.key/tile_paper_blue.jpg
|
71
|
+
propaganda/Ruby2C.key/tile_paper_gray.jpg
|
72
|
+
propaganda/Ruby2C.key/tile_paper_green.jpg
|
73
|
+
propaganda/Ruby2C.key/tile_paper_purple.jpg
|
74
|
+
propaganda/Ruby2C.key/tile_paper_red.jpg
|
75
|
+
propaganda/Ruby2C.key/tile_paper_yellow.jpg
|
76
|
+
propaganda/Ruby2C.key/tiny/.jrsf
|
77
|
+
propaganda/Ruby2C.key/tiny/tile_paper_blue.jpg
|
78
|
+
propaganda/Ruby2C.pdf
|
79
|
+
propaganda/class diagram.graffle
|
80
|
+
propaganda/processors.graffle
|
81
|
+
propaganda/ruby2c architecture.graffle
|
82
|
+
rewrite.rb
|
83
|
+
rewriter.rb
|
84
|
+
ruby_to_c.rb
|
85
|
+
support.rb
|
86
|
+
test_all.rb
|
87
|
+
test_extras.rb
|
88
|
+
test_rewriter.rb
|
89
|
+
test_ruby_to_c.rb
|
90
|
+
test_support.rb
|
91
|
+
test_type_checker.rb
|
92
|
+
test_typed_sexp_processor.rb
|
93
|
+
translate.rb
|
94
|
+
type.rb
|
95
|
+
type_checker.rb
|
96
|
+
typed_sexp_processor.rb
|
97
|
+
validate.sh
|
98
|
+
zcomparable.rb
|
data/README.txt
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
ruby_to_c
|
2
|
+
http://rubyforge.org/projects/ruby2c/
|
3
|
+
ryand-ruby@zenspider.com
|
4
|
+
ruby2c@zenspider.com - mailing list
|
5
|
+
|
6
|
+
DESCRIPTION:
|
7
|
+
|
8
|
+
ruby_to_c translates a static ruby subset to C. Hopefully it works.
|
9
|
+
|
10
|
+
NOTE ! NOTE ! NOTE ! NOTE ! NOTE ! NOTE ! NOTE ! NOTE ! NOTE
|
11
|
+
|
12
|
+
THIS IS BETA SOFTWARE!
|
13
|
+
|
14
|
+
NOTE ! NOTE ! NOTE ! NOTE ! NOTE ! NOTE ! NOTE ! NOTE ! NOTE
|
15
|
+
|
16
|
+
RubyToC has the following modules:
|
17
|
+
|
18
|
+
Rewriter - massages the sexp into a more consistent form.
|
19
|
+
TypeChecker - type inferencer for the above sexps.
|
20
|
+
RubyToC - converts a ruby (subset) sexp to C.
|
21
|
+
|
22
|
+
and the following tools:
|
23
|
+
|
24
|
+
translate.rb - Translates a given file to C.
|
25
|
+
|
26
|
+
FEATURES/PROBLEMS:
|
27
|
+
|
28
|
+
+ This is a preview release! BETA BETA BETA! Do NOT use this!
|
29
|
+
+ Please contact me or Eric (drbrain of segment7 dot net) if you:
|
30
|
+
+ have any feedback!
|
31
|
+
+ have any changes!
|
32
|
+
+ want to work on this!
|
33
|
+
|
34
|
+
SYNOPSYS:
|
35
|
+
|
36
|
+
./translate.rb blah.rb > blah.c; gcc -c -I /rubylib/1.8/platform blah.c
|
37
|
+
|
38
|
+
TODO:
|
39
|
+
|
40
|
+
+ Numerous, but we are trying to get them in here... sorry...
|
41
|
+
+ Want to move to a gem directory structure (lib/ test/ bin/ etc)
|
42
|
+
|
43
|
+
REQUIREMENTS:
|
44
|
+
|
45
|
+
+ ParseTree - http://rubyforge.org/projects/parsetree/
|
46
|
+
+ RubyInline - http://rubyforge.org/projects/rubyinline/
|
47
|
+
|
48
|
+
INSTALL:
|
49
|
+
|
50
|
+
+ Um. Please don't install this crap yet...
|
51
|
+
|
52
|
+
LICENSE:
|
53
|
+
|
54
|
+
(The MIT License)
|
55
|
+
|
56
|
+
Copyright (c) 2001-2002 Ryan Davis, Zen Spider Software
|
57
|
+
|
58
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
59
|
+
a copy of this software and associated documentation files (the
|
60
|
+
"Software"), to deal in the Software without restriction, including
|
61
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
62
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
63
|
+
permit persons to whom the Software is furnished to do so, subject to
|
64
|
+
the following conditions:
|
65
|
+
|
66
|
+
The above copyright notice and this permission notice shall be
|
67
|
+
included in all copies or substantial portions of the Software.
|
68
|
+
|
69
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
70
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
71
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
72
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
73
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
74
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
75
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/demo/char.rb
ADDED
data/demo/factorial.rb
ADDED
data/demo/hello.rb
ADDED
data/demo/misc.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
class Misc
|
3
|
+
def assert(cond, n)
|
4
|
+
if (!cond) then
|
5
|
+
puts("-- SymTab fatal error ")
|
6
|
+
case (n)
|
7
|
+
when 3
|
8
|
+
puts("-- too many nodes in graph")
|
9
|
+
when 4
|
10
|
+
puts("-- too many sets")
|
11
|
+
when 6
|
12
|
+
puts("-- too many symbols")
|
13
|
+
when 7
|
14
|
+
puts("-- too many character classes")
|
15
|
+
end
|
16
|
+
# puts("Stack Trace = #{caller.join "\n"}")
|
17
|
+
exit(n)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def main
|
22
|
+
assert(1 == 1, 3)
|
23
|
+
return 0
|
24
|
+
end
|
25
|
+
end
|
data/demo/newarray.rb
ADDED
data/demo/strcat.rb
ADDED
data/rewrite.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
require 'rewriter'
|
5
|
+
|
6
|
+
old_classes = []
|
7
|
+
ObjectSpace.each_object(Class) do |klass|
|
8
|
+
old_classes << klass
|
9
|
+
end
|
10
|
+
|
11
|
+
ARGV.each do |name|
|
12
|
+
require name
|
13
|
+
end
|
14
|
+
|
15
|
+
new_classes = []
|
16
|
+
ObjectSpace.each_object(Class) do |klass|
|
17
|
+
new_classes << klass
|
18
|
+
end
|
19
|
+
|
20
|
+
new_classes -= old_classes
|
21
|
+
|
22
|
+
parser = ParseTree.new
|
23
|
+
rewriter = Rewriter.new
|
24
|
+
|
25
|
+
new_classes.each do |klass|
|
26
|
+
sexp = parser.parse_tree klass
|
27
|
+
sexp.each do |exp|
|
28
|
+
exp = rewriter.process(exp)
|
29
|
+
pp exp
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
data/rewriter.rb
ADDED
@@ -0,0 +1,356 @@
|
|
1
|
+
|
2
|
+
begin require 'rubygems' rescue LoadError end
|
3
|
+
require 'parse_tree'
|
4
|
+
require 'typed_sexp_processor'
|
5
|
+
|
6
|
+
class Sexp
|
7
|
+
# add arglist because we introduce the new array type in this file
|
8
|
+
@@array_types << :arglist
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# Rewriter (probably should be renamed) is a first-pass filter that
|
13
|
+
# normalizes some of ruby's ASTs to make them more processable later
|
14
|
+
# in the pipeline. It only has processors for what it is interested
|
15
|
+
# in, so real the individual methods for a better understanding of
|
16
|
+
# what it does.
|
17
|
+
|
18
|
+
class Rewriter < SexpProcessor
|
19
|
+
|
20
|
+
def initialize # :nodoc:
|
21
|
+
super
|
22
|
+
self.auto_shift_type = true
|
23
|
+
self.unsupported = [ :cfunc, ]
|
24
|
+
# self.debug[:defn] = /method/ # coolest debugging feature ever
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Rewrites :attrasgn nodes to the unified :call format:
|
29
|
+
#
|
30
|
+
# [:attrasgn, lhs, :name=, args],
|
31
|
+
#
|
32
|
+
# becomes:
|
33
|
+
#
|
34
|
+
# [:call, lhs, :name=, args]
|
35
|
+
|
36
|
+
def process_attrasgn(exp)
|
37
|
+
lhs = process exp.shift
|
38
|
+
name = exp.shift
|
39
|
+
args = process exp.shift
|
40
|
+
args[0] = :arglist unless args.nil?
|
41
|
+
|
42
|
+
s(:call, lhs, name, args)
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Rewrites :call nodes to the unified :call format:
|
47
|
+
# [:call, lhs, :name, args]
|
48
|
+
|
49
|
+
def process_call(exp)
|
50
|
+
lhs = process exp.shift
|
51
|
+
name = exp.shift
|
52
|
+
args = process exp.shift
|
53
|
+
args[0] = :arglist unless args.nil?
|
54
|
+
|
55
|
+
s(:call, lhs, name, args)
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Rewrites :case/:when nodes as nested :if nodes.
|
60
|
+
|
61
|
+
def process_case(exp)
|
62
|
+
result = s()
|
63
|
+
var = process exp.shift
|
64
|
+
else_stmt = process exp.pop
|
65
|
+
|
66
|
+
new_exp = result
|
67
|
+
|
68
|
+
until exp.empty? do
|
69
|
+
c = exp.shift
|
70
|
+
# start a new scope and move to it
|
71
|
+
new_exp << s(:if)
|
72
|
+
new_exp = new_exp.last
|
73
|
+
|
74
|
+
assert_type c, :when
|
75
|
+
ignored_type, vars, stmts = process(c)
|
76
|
+
|
77
|
+
vars = vars.map { |v| s(:call,
|
78
|
+
var.deep_clone,
|
79
|
+
:===,
|
80
|
+
s(:arglist, process(v)))}
|
81
|
+
if vars.size > 1 then
|
82
|
+
|
83
|
+
# building from the bottom up, so everything is bizarro-sexp
|
84
|
+
# BIZARRO-SEXP NO LIKE OR!
|
85
|
+
or_sexp = vars.inject(s(:or, *vars.slice!(-2,2))) do |lhs, rhs|
|
86
|
+
s(:or, rhs, lhs)
|
87
|
+
end
|
88
|
+
|
89
|
+
new_exp << or_sexp
|
90
|
+
else
|
91
|
+
new_exp << vars.first
|
92
|
+
end
|
93
|
+
new_exp << stmts
|
94
|
+
end
|
95
|
+
new_exp << else_stmt
|
96
|
+
|
97
|
+
result.first
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# Rewrites :defn nodes to pull the functions arguments to the top:
|
102
|
+
#
|
103
|
+
# Input:
|
104
|
+
#
|
105
|
+
# [:defn, name, [:scope, [:block, [:args, ...]]]]
|
106
|
+
# [:defn, name, [:fbody, [:scope, [:block, [:args, ...]]]]]
|
107
|
+
# [:defn, name, [:ivar, name]]
|
108
|
+
# [:defn, name, [:attrset, name]]
|
109
|
+
#
|
110
|
+
# Output:
|
111
|
+
#
|
112
|
+
# [:defn, name, args, body]
|
113
|
+
|
114
|
+
def process_defn(exp)
|
115
|
+
name = exp.shift
|
116
|
+
args = s(:args)
|
117
|
+
body = process exp.shift
|
118
|
+
|
119
|
+
case body.first
|
120
|
+
when :scope, :fbody then
|
121
|
+
body = body[1] if body.first == :fbody
|
122
|
+
args = body.last[1]
|
123
|
+
assert_type args, :args
|
124
|
+
assert_type body, :scope
|
125
|
+
assert_type body[1], :block
|
126
|
+
body.last.delete_at 1
|
127
|
+
when :bmethod then
|
128
|
+
# BEFORE: [:defn, :bmethod_added, [:bmethod, [:dasgn_curr, :x], ...]]
|
129
|
+
# AFTER: [:defn, :bmethod_added, [:args, :x], [:scope, [:block, ...]]]
|
130
|
+
body.shift # :bmethod
|
131
|
+
# [:dasgn_curr, :x],
|
132
|
+
# [:call, [:dvar, :x], :+, [:arglist, [:lit, 1]]]]]
|
133
|
+
dasgn = body.shift
|
134
|
+
assert_type dasgn, :dasgn_curr
|
135
|
+
dasgn.shift # type
|
136
|
+
args.push(*dasgn)
|
137
|
+
body.find_and_replace_all(:dvar, :lvar)
|
138
|
+
if body.first.first == :block then
|
139
|
+
body = s(:scope, body.shift)
|
140
|
+
else
|
141
|
+
body = s(:scope, s(:block, body.shift)) # single statement
|
142
|
+
end
|
143
|
+
when :dmethod
|
144
|
+
# BEFORE: [:defn, :dmethod_added, [:dmethod, :bmethod_maker, ...]]
|
145
|
+
# AFTER: [:defn, :dmethod_added, ...]
|
146
|
+
body = body[2][1][2] # UGH! FIX
|
147
|
+
args = body[1]
|
148
|
+
body.delete_at 1
|
149
|
+
body = s(:scope, body)
|
150
|
+
when :ivar then
|
151
|
+
body = s(:scope, s(:block, s(:return, body)))
|
152
|
+
when :attrset then
|
153
|
+
argname = body.last
|
154
|
+
args << :arg
|
155
|
+
body = s(:scope, s(:block, s(:return, s(:iasgn, argname, s(:lvar, :arg)))))
|
156
|
+
else
|
157
|
+
raise "Unknown :defn format: #{name.inspect} #{args.inspect} #{body.inspect}"
|
158
|
+
end
|
159
|
+
|
160
|
+
return s(:defn, name, args, body)
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# Rewrites :fcall nodes to the unified :call format:
|
165
|
+
# [:call, lhs, :name, args]
|
166
|
+
|
167
|
+
def process_fcall(exp)
|
168
|
+
name = exp.shift
|
169
|
+
args = process exp.shift
|
170
|
+
args[0] = :arglist
|
171
|
+
|
172
|
+
return s(:call, nil, name, args)
|
173
|
+
end
|
174
|
+
|
175
|
+
##
|
176
|
+
# I'm not really sure what this is for, other than to guarantee that
|
177
|
+
# there are 4 elements in the sexp.
|
178
|
+
|
179
|
+
def process_if(exp)
|
180
|
+
cond = process exp.shift
|
181
|
+
t = process(exp.shift) || nil # FIX: nil is bad, we need to switch to dummy
|
182
|
+
f = process(exp.shift) || nil
|
183
|
+
return s(:if, cond, t, f)
|
184
|
+
end
|
185
|
+
|
186
|
+
##
|
187
|
+
# Rewrites specific :iter nodes into while loops:
|
188
|
+
# [DOC]
|
189
|
+
|
190
|
+
def process_iter(exp)
|
191
|
+
call = process exp.shift
|
192
|
+
var = process exp.shift
|
193
|
+
body = process exp.shift
|
194
|
+
|
195
|
+
if var.nil? then
|
196
|
+
var = s(:lvar, :temp_var1) # HACK Use Unique
|
197
|
+
end
|
198
|
+
|
199
|
+
assert_type call, :call
|
200
|
+
|
201
|
+
if call[2] != :each then # TODO: fix call[n] (api)
|
202
|
+
call.shift # :call
|
203
|
+
lhs = call.shift
|
204
|
+
method_name = call.shift
|
205
|
+
|
206
|
+
case method_name
|
207
|
+
when :downto then
|
208
|
+
var.shift #
|
209
|
+
start_value = lhs
|
210
|
+
finish_value = call.pop.pop # not sure about this
|
211
|
+
var_name = var.shift
|
212
|
+
body.find_and_replace_all(:dvar, :lvar)
|
213
|
+
result = s(:dummy,
|
214
|
+
s(:lasgn, var_name, start_value),
|
215
|
+
s(:while,
|
216
|
+
s(:call, s(:lvar, var_name), :>=,
|
217
|
+
s(:arglist, finish_value)),
|
218
|
+
s(:block,
|
219
|
+
body,
|
220
|
+
s(:lasgn, var_name,
|
221
|
+
s(:call, s(:lvar, var_name), :-,
|
222
|
+
s(:arglist, s(:lit, 1))))), true))
|
223
|
+
when :upto then
|
224
|
+
# REFACTOR: completely duped from above and direction changed
|
225
|
+
var.shift #
|
226
|
+
start_value = lhs
|
227
|
+
finish_value = call.pop.pop # not sure about this
|
228
|
+
var_name = var.shift
|
229
|
+
body.find_and_replace_all(:dvar, :lvar)
|
230
|
+
result = s(:dummy,
|
231
|
+
s(:lasgn, var_name, start_value),
|
232
|
+
s(:while,
|
233
|
+
s(:call, s(:lvar, var_name), :<=,
|
234
|
+
s(:arglist, finish_value)),
|
235
|
+
s(:block,
|
236
|
+
body,
|
237
|
+
s(:lasgn, var_name,
|
238
|
+
s(:call, s(:lvar, var_name), :+,
|
239
|
+
s(:arglist, s(:lit, 1))))), true))
|
240
|
+
when :define_method then
|
241
|
+
# BEFORE: [:iter, [:call, nil, :define_method, [:array, [:lit, :bmethod_added]]], [:dasgn_curr, :x], [:call, [:dvar, :x], :+, [:array, [:lit, 1]]]]
|
242
|
+
# we want to get it rewritten for the scope/block context, so:
|
243
|
+
# - throw call away
|
244
|
+
# - rewrite to args
|
245
|
+
# - plop body into a scope
|
246
|
+
# AFTER: [:block, [:args, :x], [:call, [:lvar, :x], :+, [:arglist, [:lit, 1]]]]
|
247
|
+
var.find_and_replace_all(:dasgn_curr, :args)
|
248
|
+
body.find_and_replace_all(:dvar, :lvar)
|
249
|
+
result = s(:block, var, body)
|
250
|
+
else
|
251
|
+
raise "unknown iter method #{method_name}"
|
252
|
+
end
|
253
|
+
else
|
254
|
+
s(:iter, call, var, body)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
##
|
259
|
+
# Rewrites self into lvars
|
260
|
+
|
261
|
+
def process_self(exp)
|
262
|
+
s(:lvar, :self)
|
263
|
+
end
|
264
|
+
|
265
|
+
##
|
266
|
+
# Rewrites until nodes into while nodes.
|
267
|
+
|
268
|
+
def process_until(exp)
|
269
|
+
cond = process s(:not, exp.shift)
|
270
|
+
body = process exp.shift
|
271
|
+
raise "boo" if exp.empty?
|
272
|
+
is_precondition = exp.shift
|
273
|
+
s(:while, cond, body, is_precondition)
|
274
|
+
end
|
275
|
+
|
276
|
+
##
|
277
|
+
# Rewrites :vcall nodes to the unified :call format:
|
278
|
+
# [:call, lhs, :name, args]
|
279
|
+
|
280
|
+
def process_vcall(exp)
|
281
|
+
name = exp.shift
|
282
|
+
|
283
|
+
s(:call, nil, name, nil) # TODO: never has any args?
|
284
|
+
end
|
285
|
+
|
286
|
+
##
|
287
|
+
# Rewrites :when nodes so :case can digest it into if/else structure
|
288
|
+
# [:when, [args], body]
|
289
|
+
|
290
|
+
def process_when(exp)
|
291
|
+
vars = exp.shift
|
292
|
+
assert_type vars, :array
|
293
|
+
vars.shift # nuke vars type
|
294
|
+
stmts = process(exp)
|
295
|
+
return s(:when, vars, stmts.first)
|
296
|
+
end
|
297
|
+
|
298
|
+
##
|
299
|
+
# Rewrites :zarray nodes to :array with no args.
|
300
|
+
|
301
|
+
def process_zarray(exp)
|
302
|
+
return s(:array)
|
303
|
+
end
|
304
|
+
|
305
|
+
end
|
306
|
+
|
307
|
+
##
|
308
|
+
# R2CRewriter (should probably move this out to its own file) does
|
309
|
+
# rewritings that are language specific to C.
|
310
|
+
|
311
|
+
class R2CRewriter < SexpProcessor
|
312
|
+
|
313
|
+
##
|
314
|
+
# REWRITES maps a function signature to a proc responsible for
|
315
|
+
# generating the appropriate sexp for that rewriting.
|
316
|
+
|
317
|
+
REWRITES = {
|
318
|
+
[Type.str, :+, Type.str] => proc { |l,n,r|
|
319
|
+
t(:call, nil, :strcat, r.unshift(r.shift, l), Type.str)
|
320
|
+
},
|
321
|
+
[Type.file, :puts, Type.str] => proc { |l,n,r|
|
322
|
+
t(:call, nil, :fputs, r.push(l))
|
323
|
+
},
|
324
|
+
}
|
325
|
+
|
326
|
+
def initialize # :nodoc:
|
327
|
+
super
|
328
|
+
self.auto_shift_type = true
|
329
|
+
self.expected = TypedSexp
|
330
|
+
end
|
331
|
+
|
332
|
+
##
|
333
|
+
# Rewrites function calls by looking them up in the REWRITES map. If
|
334
|
+
# a match exists, it invokes the block passing in the lhs, rhs, and
|
335
|
+
# function name. If one does not exist, it simply repacks the sexp
|
336
|
+
# and sends it along.
|
337
|
+
|
338
|
+
def process_call(exp)
|
339
|
+
lhs = process exp.shift
|
340
|
+
name = exp.shift
|
341
|
+
rhs = process exp.shift
|
342
|
+
|
343
|
+
lhs_type = lhs.sexp_type rescue nil
|
344
|
+
type_signature = [lhs_type, name]
|
345
|
+
type_signature += rhs[1..-1].map { |sexp| sexp.sexp_type } unless rhs.nil?
|
346
|
+
|
347
|
+
result = if REWRITES.has_key? type_signature then
|
348
|
+
REWRITES[type_signature].call(lhs, name, rhs)
|
349
|
+
else
|
350
|
+
t(:call, lhs, name, rhs, exp.sexp_type)
|
351
|
+
end
|
352
|
+
|
353
|
+
return result
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|