Added method output_file_suffix to code generator API.
[voodoo-lang.git] / lib / voodoo / generators / common_code_generator.rb
blob4f7bbad619f5d297d52026f12ac185818a11c4e4
1 require 'voodoo/generators/generator_api1'
3 module Voodoo
4   # Common base class for code generators.
5   #
6   # Code generators are expected to implement the following methods:
7   #
8   # - #new
9   # - #add
10   # - #add_function
11   # - #gensym
12   # - #output_file_name
13   # - #output_file_suffix
14   # - #wordsize
15   # - #write
16   #
17   # This class contains base implementations of some of these methods,
18   # which can be used and/or overridden by subclasses.
19   #
20   # An example of how to use the code generators provided by this module
21   # is provided on the main page of the documentation of the Voodoo module.
22   #
23   class CommonCodeGenerator
24     # Provide compatibility with old API
25     include GeneratorApi1
27     # Initializes the code generator.
28     # _params_ shall be a hash containing parameters to the code generator,
29     # and shall at least contain the keys <tt>:architecture</tt> and
30     # <tt>:format</tt>, specifying the target architecture and output
31     # format, respectively.
32     def initialize params = {}
33       @architecture = params[:architecture] || Config.default_architecture
34       @format = params[:format] || Config.default_format
35       @sections = {}
36       @section_aliases = {}
37       # Default section aliases. Subclasses can start from scratch by
38       # doing @section_aliases = {}
39       section_alias :code, ".text"
40       section_alias :functions, :code
41       section_alias :data, ".data"
42       self.section = :code
43       @top_level = Environment.initial_environment
44       @environment = @top_level
45       @output_file_suffix = '.o'
46     end
48     # Adds code to the given section.
49     #
50     # Examples:
51     #   add :code, [:return, 0]
52     #   add :data, [:align], [:label, :xyzzy], [:word, 42]
53     #
54     # This method implements the required functionality in terms
55     # of the following methods, which must be implemented by subclasses:
56     #
57     # - #align
58     # - #byte
59     # - #call
60     # - #end_if
61     # - #export
62     # - #begin_function
63     # - #ifelse
64     # - #ifeq
65     # - #ifge
66     # - #ifgt
67     # - #ifle
68     # - #iflt
69     # - #ifne
70     # - #import
71     # - #label
72     # - #let
73     # - #ret
74     # - #set
75     # - #set_byte
76     # - #set_word
77     # - #string
78     # - #word
79     #
80     def add section, *code
81       in_section section do
82         code.each do |action|
83           keyword, args = action[0], action[1..-1]
84           case keyword
85           when :function
86             function args[0], *args[1..-1]
87           when :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne
88             truebody = action[2]
89             falsebody = action[3]
90             send keyword, action[1][0], action[1][1]
91             add section, *truebody
92             if falsebody && !falsebody.empty?
93               ifelse
94               add section, *falsebody
95             end
96             end_if
97           when :return
98             send :ret, *args
99           when :'set-word'
100             send :set_word, *args
101           when :'set-byte'
102             send :set_byte, *args
103           when :'tail-call'
104             send :tail_call, *args
105           else
106             send *action
107           end
108         end
109       end
110     end
112     # Add function.
113     #
114     # Parameters:
115     # [formals] an Array of formal parameter names
116     # [code] an Array of actions to be used as the function's body
117     #
118     # Example:
119     #   add_function [:n], [:return, :add, :n, 1]
120     def add_function formals, *code
121       add :functions, [:function, formals] + code
122     end
124     # Add a function to the current section
125     def function formals, *code
126       begin_function *formals
127       code.each { |action| add section, action }
128       end_function
129     end
131     # Generate a new, unused symbol
132     def gensym
133       Environment.gensym
134     end
136     # Add code to the current section
137     def emit code
138       @sections[real_section_name(@section)] << code
139     end
141     # Get the real name of a section.
142     # Given a section name which may be an alias, this method returns the
143     # real name of the section.
144     def real_section_name name
145       given_name = name
146       while true
147         x = @section_aliases[name]
148         break if x == nil       # Not an alias, exit loop and return name
149         name = x
150         # If name == given_name, we're back where we started. Continuing
151         # would have us loop forever. Just return what we have now.
152         break if name == given_name
153       end
154       name
155     end
157     # Set the current section
158     def section= name
159       real_name = real_section_name name
160       @section = name
161       unless @sections.has_key? real_name
162         @sections[real_name] = ''
163       end
164     end
166     def section name = nil
167       self.section = name if name
168       @section
169     end
171     # Set up +alias_name+ to refer to the same section as +original_name+.
172     def section_alias alias_name, original_name
173       @section_aliases[alias_name] = original_name
174     end
176     def in_section name, &block
177       oldsection = @section
178       self.section = name
179       begin
180         yield
181       ensure
182         self.section = oldsection
183       end
184     end
186     # Given an input file name, returns the canonical output file name
187     # for this code generator.
188     def output_file_name input_name
189       input_name.sub(/\.voo$/, '') + @output_file_suffix
190     end
192     # Returns the canonical output file suffix for this code generator
193     def output_file_suffix
194       @output_file_suffix
195     end
197     class Environment
198       @@gensym_counter = 0
200       attr_reader :args, :locals, :symbols
202       def initialize parent = nil
203         ## Parent environment
204         @parent = parent
205         ## Symbol lookup table
206         @symbols = parent ? parent.symbols.dup : {}
207         ## Number of arguments
208         @args = parent ? parent.args : 0
209         ## Number of local variables
210         @locals = parent ? parent.locals : 0
211       end
213       def add_arg symbol
214         @symbols[symbol] = [:arg, @args]
215         @args = @args + 1
216       end
218       def add_args symbols
219         symbols.each { |sym| add_arg sym }
220       end
222       def add_local symbol
223         @symbols[symbol] = [:local, @locals]
224         @locals = @locals + 1
225       end
227       def add_locals symbols
228         symbols.each { |sym| add_local sym }
229       end
231       def gensym
232         Environment.gensym
233       end
235       def [] symbol
236         @symbols[symbol]
237       end
239       def self.gensym
240         @@gensym_counter = @@gensym_counter + 1
241         "_G#{@@gensym_counter}".to_sym
242       end
244       def self.initial_environment
245         Environment.new
246       end
247     end
249   end