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