Changed parser to return top-level elements instead of single lines.
[voodoo-lang.git] / lib / ruby / voodoo / generators / common_code_generator.rb
blob7e8fba217f159a322d8000770bebcf928920cf20
1 module Voodoo
2   # Common base class for code generators
3   class CommonCodeGenerator
4     def initialize params
5       @architecture = params[:architecture]
6       @format = params[:format]
7       @sections = { :code => '' }
8       @section = :code
9       @top_level = Environment.initial_environment
10       @environment = @top_level
11       #@if_labels = []
12     end
14     # Add code.
15     # Example:
16     #   add_code [:call, :exit, 0]
17     def add_code *actions
18       in_section :code do
19         actions.each do |action|
20           keyword, args = action[0], action[1..-1]
21           case keyword
22           when :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne
23             truebody = action[2]
24             falsebody = action[3]
25             send keyword, action[1][0], action[1][1]
26             add_code *truebody
27             if falsebody && !falsebody.empty?
28               ifelse
29               add_code *falsebody
30             end
31             end_if
32           when :return
33             send :ret, *args
34           when :'tail-call'
35             send :tail_call, *args
36           else
37             send *action
38           end
39         end
40       end
41     end
43     # Add a label at the current address in the code section.
44     def add_code_label name
45       in_section(:code) { label name }
46     end
48     # Add data.
49     # Example:
50     #   add_data [:word, 42]
51     def add_data *defs
52       in_section :data do
53         defs.each do |defn|
54           keyword, args = defn[0], defn[1..-1]
55           case keyword
56           when :align
57             align_data *args
58           when :byte, :string, :word
59             send *defn
60           else
61             raise "Invalid directive in data section: #{defn[0]}"
62           end
63         end
64       end
65     end
67     # Add a label at the current address in the data section.
68     def add_data_label name
69       in_section(:data) { label name }
70     end
72     # Add function.
73     # Example:
74     #   add_data [:word, 42]
75     def add_function formals, *code
76       in_section :functions do
77         begin_function *formals
78         add_code *code
79         end_function
80       end
81     end
83     # Add a label at the address where the next function will be generated.
84     def add_function_label name
85       in_section(:functions) { label name }
86     end
88     # Set the current section
89     def section= name
90       @section = name
91       unless @sections.has_key? name
92         @sections[name] = ''
93       end
94     end
96     def section name = nil
97       self.section = name if name
98       @section
99     end
101     def in_section name, &block
102       oldsection = @section
103       section = name
104       yield
105       section = oldsection
106     end
108     # Given an input file name, returns the canonical output file name
109     # for this code generator.
110     def output_file_name input_name
111       input_name.sub(/\.voo$/, '') + '.o'
112     end
114     class Environment
115       @@gensym_counter = 0
117       attr_reader :args, :locals, :symbols
119       def initialize parent = nil
120         ## Parent environment
121         @parent = parent
122         ## Symbol lookup table
123         @symbols = parent ? parent.symbols.dup : {}
124         ## Number of arguments
125         @args = parent ? parent.args : 0
126         ## Number of local variables
127         @locals = parent ? parent.locals : 0
128       end
130       def add_arg symbol
131         @symbols[symbol] = [:arg, @args]
132         @args = @args + 1
133       end
135       def add_args symbols
136         symbols.each { |sym| add_arg sym }
137       end
139       def add_local symbol
140         @symbols[symbol] = [:local, @locals]
141         @locals = @locals + 1
142       end
144       def add_locals symbols
145         symbols.each { |sym| add_local sym }
146       end
148       def gensym
149         @@gensym_counter = @@gensym_counter + 1
150         ".G#{@@gensym_counter}"
151       end
153       def [] symbol
154         @symbols[symbol]
155       end
157       def self.initial_environment
158         Environment.new
159       end
160     end
162   end