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