Added spec:commit task to commit changes to spec/ruby sources.
[rbx.git] / shotgun / lib / cuby_primitives.rb
blob7463e0649f16e9d79d3874835dd30c4f47f1addb
1 require 'rubygems'
2 require 'sexp/simple_processor'
3 require 'cuby'
4 require 'sydparse'
6 class CubyPrimitives
7   def initialize(header_path=".")
8     @headers = header_path
9     @funcs = Cuby::HeaderParser.find_in_directory(@headers).values.flatten
10     @output = {}
11     detect_mappings
12   end
13   
14   attr_accessor :output
15   
16   def detect_mappings
17     maps = Hash.new { |h,k| h[k] = {}}
18     @funcs.each do |func|
19       m = /(.*?)_(.*)/.match(func.name)
20       next unless m
21       next if m[1].empty?
22       map = m[1].downcase.to_sym
23       name = m[2]
24       arg = func.arguments.first
25       if arg and arg[:type] == "STATE"
26         map_to = "#{func.name}(state, %s)"
27       else
28         map_to = name
29       end
30       
31       maps[map][name.to_sym] = map_to
32     end
33     
34     @maps = maps
35   end
36     
37   def new_cuby
38     c = Cuby.new
39     %w!_int j k m!.each do |var|
40       c.declare_var "int", var
41     end
42     
43     %w!self _lit t1 t2 t3!.each do |var|
44       c.declare_var "OBJECT", var
45     end
46     
47     c.declare_var "rstate", "state"
48     
49     c.map_operator = :as
50     c.declare_method = :declare
51     
52     add_header_maps(c)
53     add_builtin_methods(c)
54     
55     c.declare_function 'int', 'sizeof', ['void']
56     
57     c.add_method(:fail) do
58       "_ret = FALSE; break"
59     end
60     
61     c.on_return = proc { |val|
62       "stack_push(#{val}); break"
63     }
64     
65     c.true_value = "Qtrue"
66     c.false_value = "Qfalse"
67     return c
68   end
69   
70   def declare_function(cuby, func)
71     args = func.arguments.map { |a| a[:type] }
72     cuby.declare_function func.return_type, func.name, args
73   end
74   
75   ObjectMap = {
76     :fixnum? => "FIXNUM_P(%s)",
77     :to_int => "N2I(%s)"
78   }
79   
80   NumericMap = {
81     :to_fix => "I2N(%s)"
82   }
83   
84   CharStarMap = {
85     :set_value => ["char", proc { |s, v|
86       "*#{s} = #{v}"
87     }],
88     
89     :[] => ["char", proc { |s,a|
90       "#{s}[#{a}]"
91     }]
92   }
93   
94   StringMap = {
95     :as_char => ["char", proc { |s| "'#{s[1..-2]}'"}]
96   }
97   
98   Operators = [:+, :==, :-, :*, :<, :>, :-@, :%, :/]
99     
100   def add_header_maps(cuby)
101     @funcs.each do |func|
102       declare_function(cuby, func)
103     end
104     
105     @maps.each do |name, map|
106       cuby.add_map name, map
107     end
108     
109     cuby.add_map :object, ObjectMap
110     cuby.add_map :numeric, NumericMap
111     cuby.add_map :charstar, CharStarMap
112     cuby.add_map :string, StringMap
113     cuby.add_type_map "OBJECT", :object
114     cuby.add_type_map "int", :numeric
115     cuby.add_type_map "char*", :charstar
116     
117     Operators.each do |op|
118       cuby.operators << op
119     end
120   end
121   
122   def add_builtin_methods(c)
123     
124   end
125   
126   def fixup_body(args, c_code)
127     preamble = "self = stack_pop();\n"
128     args.each do |a|
129       preamble << "#{a} = stack_pop();\n"
130     end
131     
132     return preamble + c_code
133   end
134   
135   class Processor < SimpleSexpProcessor
136     def initialize(cont, output)
137       super()
138       self.require_expected = false
139       self.strict = true
140       self.auto_shift_type = true
141       
142       @cont = cont
143       @output = output
144     end
145     
146     def process_class(x)
147       x.shift
148       x.shift
149       process x.shift
150     end
151     
152     def process_scope(x)
153       process x.shift
154     end
155     
156     def process_block(x)
157       while e = x.shift
158         process e
159       end
160     end
161     
162     def process_defn(x)
163       name = x.shift
164       body = x.shift
165       
166       an = body[1].delete_at(1)
167       args = an[1]
168             
169       c = @cont.new_cuby
170       c.generate_from body
171       
172       @output[name] = @cont.fixup_body(args, c.code)
173     end
174   end
175   
176   def parse_file(path)
177     io = File.open(path)
178     syd = SydneyParser.load_file io
179     io.close
180     
181     pro = Processor.new(self, @output)
182     pro.process syd.sexp
183   end
186 cp = CubyPrimitives.new("shotgun/lib")
187 cp.parse_file ARGV.shift
188 cp.output.each do |meth, code|
189   p meth
190   puts code