Get all examples compiling, though output is not correct
[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_instruction_name_from_builtin(builtin):
40     try:
41         return {
42             # Environment operations
43             '__get__': 'get',
44
45             # Integer operations
46             '__add__': 'add',
47             '__integer_divide__': 'idiv',
48             '__modular_divide__': 'mod',
49             '__multiply__': 'mul',
50             '__negate__': 'neg',
51             '__subtract__': 'sub',
52
53             # Boolean operations
54             '__eq__': 'eq',
55             '__neq__': 'neq',
56             '__lt__': 'lt',
57             '__lte__': 'lte',
58             '__gt__': 'gt',
59             '__gte__': 'gte',
60
61             # String operations
62             '__concat__': 'concat',
63         }[builtin]
64
65     except KeyError:
66         import ipdb; ipdb.set_trace()
67
68 def generate_function_call_expression(counters, expression):
69     if isinstance(expression.function_expression, conversion.CPSBuiltinExpression):
70         return (
71             (),
72             (
73                 CIRInstruction(
74                     instruction=generate_instruction_name_from_builtin(
75                         expression.function_expression.symbol,
76                     ),
77                     argument=expression.argument_count,
78                 ),
79             )
80         )
81
82     referenced_entry_list, instruction_list = generate_expression(
83         counters,
84         expression.function_expression,
85     )
86
87     instruction_list += (
88         CIRInstruction(
89             instruction='call',
90             argument=expression.argument_count,
91         ),
92     )
93
94     return referenced_entry_list, instruction_list
95
96 def generate_integer_literal_expression(counters, expression):
97     referenced_entry_list = ()
98     instruction_list = (CIRInstruction(
99         instruction='push_integer',
100         argument=generate_integer_literal(expression.integer),
101     ),)
102
103     return referenced_entry_list, instruction_list
104
105 def escape_name(name):
106     return name.replace('$','$$').replace('_','$')
107
108 def generate_lambda_expression(counters, expression):
109     if expression.name is None:
110         name = '__lambda__'
111     else:
112         name = escape_name(expression.name)
113
114     name_counter = counters.get(name, 0)
115     counters[expression.name] = name_counter + 1
116     label = '{}${}'.format(name, name_counter)
117
118     referenced_entry_list_list = []
119     instruction_list_list = []
120
121     for statement in expression.statement_list:
122         referenced_entry_list, instruction_list = generate_statement(counters, statement)
123         referenced_entry_list_list.append(referenced_entry_list)
124         instruction_list_list.append(instruction_list)
125
126     # Pop from the stack in reversed order, because arguments were pushed onto
127     # the stack in order
128     argument_bindings = tuple(
129         CIRInstruction(instruction='pop', argument='sym({})'.format(arg))
130         for arg in reversed(expression.argument_name_list)
131     )
132
133     lambda_body = flatten(instruction_list_list)
134     assert lambda_body[-1].instruction == 'drop'
135     lambda_body = argument_bindings + lambda_body[:-1] + (CIRInstruction(instruction='return', argument=None),)
136
137     referenced_entry_list_list.append(
138         (CIRLabel(label=label),) + lambda_body,
139     )
140
141     instruction_list = (
142         CIRInstruction(instruction='close', argument=label),
143     )
144
145     return flatten(referenced_entry_list_list), instruction_list
146
147 def generate_list_construct_expression(counters, expression):
148     referenced_entry_list = ()
149     instruction_list = (CIRInstruction(
150         instruction='list',
151         argument=2,
152     ),)
153     return referenced_entry_list, instruction_list
154
155 def generate_string_literal_expression(counters, expression):
156     referenced_entry_list = ()
157     instruction_list = (CIRInstruction(
158         instruction='push_string',
159         argument=generate_string_literal(expression.string),
160     ),)
161
162     return referenced_entry_list, instruction_list
163
164 def generate_structure_literal_expression(counters, expression):
165     referenced_entry_list = ()
166     instruction_list = (CIRInstruction(
167         instruction='structure',
168         argument=expression.field_count,
169     ),)
170
171     return referenced_entry_list, instruction_list
172
173 def generate_symbol_expression(counters, expression):
174     referenced_entry_list = ()
175     instruction_list = (CIRInstruction(
176         instruction='push',
177         argument=generate_symbol_literal(expression.symbol),
178     ),)
179
180     return referenced_entry_list, instruction_list
181
182 def generate_symbol_literal_expression(counters, expression):
183     referenced_entry_list = ()
184     instruction_list = (CIRInstruction(
185         instruction='push_symbol',
186         argument=generate_symbol_literal(expression.symbol),
187     ),)
188
189     return referenced_entry_list, instruction_list
190
191 def generate_variable_expression(counters, expression):
192     referenced_entry_list = ()
193     instruction_list = (CIRInstruction(
194         instruction='push',
195         argument=generate_symbol_literal(expression.variable),
196     ),)
197
198     return referenced_entry_list, instruction_list
199
200 def generate_expression(counters, expression):
201     return {
202         conversion.CPSFunctionCallExpression: generate_function_call_expression,
203         conversion.CPSIfElseExpression: generate_if_else_expression,
204         conversion.CPSIntegerLiteralExpression: generate_integer_literal_expression,
205         conversion.CPSLambdaExpression: generate_lambda_expression,
206         conversion.CPSListConstructExpression: generate_list_construct_expression,
207         conversion.CPSStringLiteralExpression: generate_string_literal_expression,
208         conversion.CPSStructureLiteralExpression: generate_structure_literal_expression,
209         conversion.CPSSymbolExpression: generate_symbol_expression,
210         conversion.CPSSymbolLiteralExpression: generate_symbol_literal_expression,
211         conversion.CPSVariableExpression: generate_variable_expression,
212     }[type(expression)](counters, expression)
213
214 def generate_expression_statement(counters, statement):
215     referenced_entry_list, instruction_list = generate_expression(
216         counters,
217         statement.expression,
218     )
219
220     instruction_list += (
221         CIRInstruction(
222             instruction='drop',
223             argument=None,
224         ),
225     )
226
227     return referenced_entry_list, instruction_list
228
229 def generate_if_else_expression(counters, statement):
230     if_counter = counters['if']
231     counters['if'] += 1
232
233     referenced_entry_list_list = []
234
235     condition_referenced_entry_list, condition_instruction_list = generate_expression(
236         counters,
237         statement.condition_expression,
238     )
239
240     if_instruction_list_list = []
241     for if_statement in statement.if_statement_list:
242         referenced_entry_list, instruction_list = generate_statement(counters, if_statement)
243         referenced_entry_list_list.append(referenced_entry_list)
244         if_instruction_list_list.append(instruction_list)
245
246     if_instruction_list = flatten(if_instruction_list_list)
247     assert if_instruction_list[-1].instruction == 'drop'
248     if_instruction_list = if_instruction_list[:-1]
249
250     else_instruction_list_list = []
251
252     for else_statement in statement.else_statement_list:
253         referenced_entry_list, instruction_list = generate_statement(counters, else_statement)
254         referenced_entry_list_list.append(referenced_entry_list)
255         else_instruction_list_list.append(instruction_list)
256
257     else_instruction_list = flatten(else_instruction_list_list)
258     assert else_instruction_list[-1].instruction == 'drop'
259     else_instruction_list = else_instruction_list[:-1]
260
261     if_label = '__if${}__'.format(if_counter)
262     else_label = '__else${}__'.format(if_counter)
263     endif_label = '__endif${}__'.format(if_counter)
264
265     instruction_list = condition_instruction_list + (
266         CIRInstruction(
267             instruction='jump_if_false',
268             argument=else_label,
269         ),
270         CIRInstruction(
271             instruction='jump',
272             argument=if_label,
273         ),
274         CIRLabel(label=if_label),
275     ) + if_instruction_list + (
276         CIRInstruction(
277             instruction='jump',
278             argument=endif_label,
279         ),
280         CIRLabel(label=else_label),
281     ) + else_instruction_list + (
282         CIRLabel(label=endif_label),
283     )
284
285     return (
286         condition_referenced_entry_list + flatten(referenced_entry_list_list),
287         instruction_list,
288     )
289
290 def generate_assignment_statement(counters, statement):
291     referenced_entry_list, instruction_list = generate_expression(
292         counters,
293         statement.expression,
294     )
295
296     instruction_list += (
297         CIRInstruction(
298             instruction='pop',
299             argument=generate_symbol_literal(statement.target),
300         ),
301     )
302
303     return referenced_entry_list, instruction_list
304
305 def generate_push_statement(counters, statement):
306     return generate_expression(counters, statement.expression)
307
308 def generate_variable_initialization_statement(counters, statement):
309     referenced_entry_list, instruction_list = generate_expression(
310         counters,
311         statement.expression,
312     )
313
314     instruction_list += (
315         CIRInstruction(
316             instruction='pop',
317             argument=generate_symbol_literal(statement.variable),
318         ),
319     )
320
321     return referenced_entry_list, instruction_list
322
323 def generate_statement(counters, statement):
324     return {
325         conversion.CPSAssignmentStatement: generate_assignment_statement,
326         conversion.CPSExpressionStatement: generate_expression_statement,
327         conversion.CPSPushStatement: generate_push_statement,
328         conversion.CPSVariableInitializationStatement: generate_variable_initialization_statement,
329     }[type(statement)](counters, statement)
330
331 def generate(converted):
332     referenced_entry_list_list = []
333     instruction_list_list = []
334     counters = {
335         'if': 0,
336     }
337
338     for statement in converted.statement_list:
339         referenced_entry_list, instruction_list = generate_statement(counters, statement)
340         referenced_entry_list_list.append(referenced_entry_list)
341         instruction_list_list.append(instruction_list)
342
343     return CIRProgram(
344         entry_list=flatten(referenced_entry_list_list) + (
345             CIRLabel(label='__main__'),
346         ) + flatten(instruction_list_list) + (
347             CIRInstruction(instruction='end', argument=None),
348         )
349     )
350
351 NO_ARGUMENT_INSTRUCTIONS = set([
352     'drop',
353     'return',
354 ])
355
356 def format_argument(arg):
357     if arg is None:
358         return 'nil'
359     return arg
360
361 def output(program):
362     lines = []
363
364     for entry in program.entry_list:
365         if isinstance(entry, CIRInstruction):
366             if entry.instruction in NO_ARGUMENT_INSTRUCTIONS and entry.argument is None:
367                 lines.append('    {}'.format(entry.instruction))
368             else:
369                 lines.append('    {} {}'.format(entry.instruction, format_argument(entry.argument)))
370
371         if isinstance(entry, CIRLabel):
372             lines.append('\n{}:'.format(entry.label))
373
374     return '\n'.join(lines).lstrip()