Generate a C program (which has a memory error after completion, but still)
[fur] / crossplatform_ir_generation.py
1 import collections
2
3 import conversion
4
5 def flatten(xses):
6     return tuple(x for xs in xses for x in xs)
7
8 CIRProgram = collections.namedtuple(
9     'CIRProgram',
10     (
11         'entry_list',
12     ),
13 )
14
15 CIRLabel = collections.namedtuple(
16     'CIRLabel',
17     (
18         'label',
19     ),
20 )
21
22 CIRInstruction = collections.namedtuple(
23     'CIRInstruction',
24     (
25         'instruction',
26         'argument',
27     ),
28 )
29
30 def generate_integer_literal(integer):
31     return integer
32
33 def generate_string_literal(string):
34     return '"{}"'.format(string)
35
36 def generate_symbol_literal(symbol):
37     return 'sym({})'.format(symbol)
38
39 def generate_function_call_expression(counters, expression):
40     referenced_entry_list, instruction_list = generate_expression(
41         counters,
42         expression.function_expression,
43     )
44
45     instruction_list += (
46         CIRInstruction(
47             instruction='call',
48             argument=expression.argument_count,
49         ),
50     )
51
52     return referenced_entry_list, instruction_list
53
54 def generate_integer_literal_expression(counters, expression):
55     referenced_entry_list = ()
56     instruction_list = (CIRInstruction(
57         instruction='push_integer',
58         argument=generate_integer_literal(expression.integer),
59     ),)
60
61     return referenced_entry_list, instruction_list
62
63 def escape_name(name):
64     return name.replace('$','$$').replace('_','$')
65
66 def generate_lambda_expression(counters, expression):
67     if expression.name is None:
68         name = '__lambda__'
69     else:
70         name = escape_name(expression.name)
71
72     name_counter = counters.get(name, 0)
73     counters[expression.name] = name_counter + 1
74     label = '{}${}'.format(name, name_counter)
75
76     referenced_entry_list_list = []
77     instruction_list_list = []
78
79     for statement in expression.statement_list:
80         referenced_entry_list, instruction_list = generate_statement(counters, statement)
81         referenced_entry_list_list.append(referenced_entry_list)
82         instruction_list_list.append(instruction_list)
83
84     # Pop from the stack in reversed order, because arguments were pushed onto
85     # the stack in order
86     argument_bindings = tuple(
87         CIRInstruction(instruction='pop', argument='sym({})'.format(arg))
88         for arg in reversed(expression.argument_name_list)
89     )
90
91     lambda_body = flatten(instruction_list_list)
92     assert lambda_body[-1].instruction == 'drop'
93     lambda_body = argument_bindings + lambda_body[:-1] + (CIRInstruction(instruction='return', argument=None),)
94
95     referenced_entry_list_list.append(
96         (CIRLabel(label=label),) + lambda_body,
97     )
98
99     instruction_list = (
100         CIRInstruction(instruction='close', argument=label),
101     )
102
103     return flatten(referenced_entry_list_list), instruction_list
104
105 def generate_list_construct_expression(counters, expression):
106     referenced_entry_list = ()
107     instruction_list = (CIRInstruction(
108         instruction='list',
109         argument=2,
110     ),)
111     return referenced_entry_list, instruction_list
112
113 def generate_string_literal_expression(counters, expression):
114     referenced_entry_list = ()
115     instruction_list = (CIRInstruction(
116         instruction='push_string',
117         argument=generate_string_literal(expression.string),
118     ),)
119
120     return referenced_entry_list, instruction_list
121
122 def generate_structure_literal_expression(counters, expression):
123     referenced_entry_list = ()
124     instruction_list = (CIRInstruction(
125         instruction='structure',
126         argument=expression.field_count,
127     ),)
128
129     return referenced_entry_list, instruction_list
130
131 def generate_symbol_expression(counters, expression):
132     referenced_entry_list = ()
133     instruction_list = (CIRInstruction(
134         instruction='push',
135         argument=generate_symbol_literal(expression.symbol),
136     ),)
137
138     return referenced_entry_list, instruction_list
139
140 def generate_symbol_literal_expression(counters, expression):
141     referenced_entry_list = ()
142     instruction_list = (CIRInstruction(
143         instruction='push_symbol',
144         argument=generate_symbol_literal(expression.symbol),
145     ),)
146
147     return referenced_entry_list, instruction_list
148
149 def generate_variable_expression(counters, expression):
150     referenced_entry_list = ()
151     instruction_list = (CIRInstruction(
152         instruction='push',
153         argument=generate_symbol_literal(expression.variable),
154     ),)
155
156     return referenced_entry_list, instruction_list
157
158 def generate_expression(counters, expression):
159     return {
160         conversion.CPSFunctionCallExpression: generate_function_call_expression,
161         conversion.CPSIfElseExpression: generate_if_else_expression,
162         conversion.CPSIntegerLiteralExpression: generate_integer_literal_expression,
163         conversion.CPSLambdaExpression: generate_lambda_expression,
164         conversion.CPSListConstructExpression: generate_list_construct_expression,
165         conversion.CPSStringLiteralExpression: generate_string_literal_expression,
166         conversion.CPSStructureLiteralExpression: generate_structure_literal_expression,
167         conversion.CPSSymbolExpression: generate_symbol_expression,
168         conversion.CPSSymbolLiteralExpression: generate_symbol_literal_expression,
169         conversion.CPSVariableExpression: generate_variable_expression,
170     }[type(expression)](counters, expression)
171
172 def generate_expression_statement(counters, statement):
173     referenced_entry_list, instruction_list = generate_expression(
174         counters,
175         statement.expression,
176     )
177
178     instruction_list += (
179         CIRInstruction(
180             instruction='drop',
181             argument=None,
182         ),
183     )
184
185     return referenced_entry_list, instruction_list
186
187 def generate_if_else_expression(counters, statement):
188     if_counter = counters['if']
189     counters['if'] += 1
190
191     referenced_entry_list_list = []
192
193     condition_referenced_entry_list, condition_instruction_list = generate_expression(
194         counters,
195         statement.condition_expression,
196     )
197
198     if_instruction_list_list = []
199     for if_statement in statement.if_statement_list:
200         referenced_entry_list, instruction_list = generate_statement(counters, if_statement)
201         referenced_entry_list_list.append(referenced_entry_list)
202         if_instruction_list_list.append(instruction_list)
203
204     if_instruction_list = flatten(if_instruction_list_list)
205     assert if_instruction_list[-1].instruction == 'drop'
206     if_instruction_list = if_instruction_list[:-1]
207
208     else_instruction_list_list = []
209
210     for else_statement in statement.else_statement_list:
211         referenced_entry_list, instruction_list = generate_statement(counters, else_statement)
212         referenced_entry_list_list.append(referenced_entry_list)
213         else_instruction_list_list.append(instruction_list)
214
215     else_instruction_list = flatten(else_instruction_list_list)
216     assert else_instruction_list[-1].instruction == 'drop'
217     else_instruction_list = else_instruction_list[:-1]
218
219     if_label = '__if${}__'.format(if_counter)
220     else_label = '__else${}__'.format(if_counter)
221     endif_label = '__endif${}__'.format(if_counter)
222
223     instruction_list = condition_instruction_list + (
224         CIRInstruction(
225             instruction='jump_if_false',
226             argument=else_label,
227         ),
228         CIRInstruction(
229             instruction='jump',
230             argument=if_label,
231         ),
232         CIRLabel(label=if_label),
233     ) + if_instruction_list + (
234         CIRInstruction(
235             instruction='jump',
236             argument=endif_label,
237         ),
238         CIRLabel(label=else_label),
239     ) + else_instruction_list + (
240         CIRLabel(label=endif_label),
241     )
242
243     return (
244         condition_referenced_entry_list + flatten(referenced_entry_list_list),
245         instruction_list,
246     )
247
248 def generate_assignment_statement(counters, statement):
249     referenced_entry_list, instruction_list = generate_expression(
250         counters,
251         statement.expression,
252     )
253
254     instruction_list += (
255         CIRInstruction(
256             instruction='pop',
257             argument=generate_symbol_literal(statement.target),
258         ),
259     )
260
261     return referenced_entry_list, instruction_list
262
263 def generate_push_statement(counters, statement):
264     return generate_expression(counters, statement.expression)
265
266 def generate_variable_initialization_statement(counters, statement):
267     referenced_entry_list, instruction_list = generate_expression(
268         counters,
269         statement.expression,
270     )
271
272     instruction_list += (
273         CIRInstruction(
274             instruction='pop',
275             argument=generate_symbol_literal(statement.variable),
276         ),
277     )
278
279     return referenced_entry_list, instruction_list
280
281 def generate_statement(counters, statement):
282     return {
283         conversion.CPSAssignmentStatement: generate_assignment_statement,
284         conversion.CPSExpressionStatement: generate_expression_statement,
285         conversion.CPSPushStatement: generate_push_statement,
286         conversion.CPSVariableInitializationStatement: generate_variable_initialization_statement,
287     }[type(statement)](counters, statement)
288
289 def generate(converted):
290     referenced_entry_list_list = []
291     instruction_list_list = []
292     counters = {
293         'if': 0,
294     }
295
296     for statement in converted.statement_list:
297         referenced_entry_list, instruction_list = generate_statement(counters, statement)
298         referenced_entry_list_list.append(referenced_entry_list)
299         instruction_list_list.append(instruction_list)
300
301     return CIRProgram(
302         entry_list=flatten(referenced_entry_list_list) + (
303             CIRLabel(label='__main__'),
304         ) + flatten(instruction_list_list) + (
305             CIRInstruction(instruction='end', argument=None),
306         )
307     )
308
309 NO_ARGUMENT_INSTRUCTIONS = set([
310     'drop',
311     'return',
312 ])
313
314 def format_argument(arg):
315     if arg is None:
316         return 'nil'
317     return arg
318
319 def output(program):
320     lines = []
321
322     for entry in program.entry_list:
323         if isinstance(entry, CIRInstruction):
324             if entry.instruction in NO_ARGUMENT_INSTRUCTIONS and entry.argument is None:
325                 lines.append('    {}'.format(entry.instruction))
326             else:
327                 lines.append('    {} {}'.format(entry.instruction, format_argument(entry.argument)))
328
329         if isinstance(entry, CIRLabel):
330             lines.append('\n{}:'.format(entry.label))
331
332     return '\n'.join(lines).lstrip()