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