Fix the generation of function CIR
[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     for argument_name in expression.argument_name_list:
72         import ipdb; ipdb.set_trace()
73
74     referenced_entry_list_list = []
75     instruction_list_list = []
76
77     for statement in expression.statement_list:
78         referenced_entry_list, instruction_list = generate_statement(counters, statement)
79         referenced_entry_list_list.append(referenced_entry_list)
80         instruction_list_list.append(instruction_list)
81
82     lambda_body = flatten(instruction_list_list)
83     assert lambda_body[-1].instruction == 'drop'
84     lambda_body = lambda_body[:-1] + (CIRInstruction(instruction='return', argument=None),)
85
86     referenced_entry_list_list.append(
87         (CIRLabel(label=label),) + lambda_body,
88     )
89
90     instruction_list = (
91         CIRInstruction(instruction='close', argument=label),
92     )
93
94     return flatten(referenced_entry_list_list), instruction_list
95
96 def generate_string_literal_expression(counters, expression):
97     referenced_entry_list = ()
98     instruction_list = (CIRInstruction(
99         instruction='push_value',
100         argument=generate_string_literal(expression.string),
101     ),)
102
103     return referenced_entry_list, instruction_list
104
105 def generate_symbol_expression(counters, expression):
106     referenced_entry_list = ()
107     instruction_list = (CIRInstruction(
108         instruction='push',
109         argument=generate_symbol_literal(expression.symbol),
110     ),)
111
112     return referenced_entry_list, instruction_list
113
114 def generate_variable_expression(counters, expression):
115     referenced_entry_list = ()
116     instruction_list = (CIRInstruction(
117         instruction='push',
118         argument=generate_symbol_literal(expression.variable),
119     ),)
120
121     return referenced_entry_list, instruction_list
122
123 def generate_expression(counters, expression):
124     return {
125         conversion.CPSFunctionCallExpression: generate_function_call_expression,
126         conversion.CPSIntegerLiteralExpression: generate_integer_literal_expression,
127         conversion.CPSLambdaExpression: generate_lambda_expression,
128         conversion.CPSStringLiteralExpression: generate_string_literal_expression,
129         conversion.CPSSymbolExpression: generate_symbol_expression,
130         conversion.CPSVariableExpression: generate_variable_expression,
131     }[type(expression)](counters, expression)
132
133 def generate_expression_statement(counters, statement):
134     referenced_entry_list, instruction_list = generate_expression(
135         counters,
136         statement.expression,
137     )
138
139     instruction_list += (
140         CIRInstruction(
141             instruction='drop',
142             argument=None,
143         ),
144     )
145
146     return referenced_entry_list, instruction_list
147
148 def generate_if_else_statement(counters, statement):
149     if_counter = counters['if']
150     counters['if'] += 1
151
152     referenced_entry_list_list = []
153
154     if_instruction_list_list = []
155     for if_statement in statement.if_statement_list:
156         referenced_entry_list, instruction_list = generate_statement(counters, if_statement)
157         referenced_entry_list_list.append(referenced_entry_list)
158         if_instruction_list_list.append(instruction_list)
159
160     else_instruction_list_list = []
161
162     for else_statement in statement.else_statement_list:
163         referenced_entry_list, instruction_list = generate_statement(counters, else_statement)
164         referenced_entry_list_list.append(referenced_entry_list)
165         else_instruction_list_list.append(instruction_list)
166
167     if_label = '__if${}__'.format(if_counter)
168     else_label = '__else${}__'.format(if_counter)
169     endif_label = '__endif${}__'.format(if_counter)
170
171     instruction_list = (
172         referenced_entry_list_list,
173         generate_expression(counters, statement.condition_expression) + (
174             CIRInstruction(
175                 instruction='jump_if_false',
176                 argument=else_label,
177             ),
178             CIRLabel(label=if_label),
179         ) + flatten(if_instruction_list_list) + (
180             CIRInstruction(
181                 instruction='jump',
182                 argument=endif_label,
183             ),
184             CIRLabel(label=else_label),
185         ) + flatten(else_instruction_list_list) + (
186             CIRLabel(label=endif_label),
187         ),
188     )
189
190     return flatten(referenced_entry_list_list), instruction_list
191
192 def generate_assignment_statement(counters, statement):
193     referenced_entry_list, instruction_list = generate_expression(
194         counters,
195         statement.expression,
196     )
197
198     instruction_list += (
199         CIRInstruction(
200             instruction='pop',
201             argument=generate_symbol_literal(statement.target),
202         ),
203     )
204
205     return referenced_entry_list, instruction_list
206
207 def generate_push_statement(counters, statement):
208     return generate_expression(counters, statement.expression)
209
210 def generate_variable_initialization_statement(counters, statement):
211     referenced_entry_list, instruction_list = generate_expression(
212         counters,
213         statement.expression,
214     )
215
216     instruction_list += (
217         CIRInstruction(
218             instruction='pop',
219             argument=generate_symbol_literal(statement.variable),
220         ),
221     )
222
223     return referenced_entry_list, instruction_list
224
225 def generate_variable_reassignment_statement(counters, statement):
226     referenced_entry_list, instruction_list = generate_expression(
227         counters,
228         statement.expression,
229     )
230
231     instruction_list += (
232         CIRInstruction(
233             instruction='pop',
234             argument=generate_symbol_literal(statement.variable),
235         ),
236     )
237
238     return referenced_entry_list, instruction_list
239
240 def generate_statement(counters, statement):
241     return {
242         conversion.CPSAssignmentStatement: generate_assignment_statement,
243         conversion.CPSExpressionStatement: generate_expression_statement,
244         conversion.CPSIfElseStatement: generate_if_else_statement,
245         conversion.CPSPushStatement: generate_push_statement,
246         conversion.CPSVariableInitializationStatement: generate_variable_initialization_statement,
247         conversion.CPSVariableReassignmentStatement: generate_variable_reassignment_statement,
248     }[type(statement)](counters, statement)
249
250 def generate(converted):
251     referenced_entry_list_list = []
252     instruction_list_list = []
253     counters = {
254         'if': 0,
255     }
256
257     for statement in converted.statement_list:
258         referenced_entry_list, instruction_list = generate_statement(counters, statement)
259         referenced_entry_list_list.append(referenced_entry_list)
260         instruction_list_list.append(instruction_list)
261
262     return CIRProgram(
263         entry_list=flatten(referenced_entry_list_list) + (
264             CIRLabel(label='__main__'),
265         ) + flatten(instruction_list_list),
266     )
267
268 NO_ARGUMENT_INSTRUCTIONS = set([
269     'drop',
270     'return',
271 ])
272
273 def format_argument(arg):
274     if arg is None:
275         return 'nil'
276     return arg
277
278 def output(program):
279     lines = []
280
281     for entry in program.entry_list:
282         if isinstance(entry, CIRInstruction):
283             if entry.instruction in NO_ARGUMENT_INSTRUCTIONS and entry.argument is None:
284                 lines.append('    {}'.format(entry.instruction))
285             else:
286                 lines.append('    {} {}'.format(entry.instruction, format_argument(entry.argument)))
287
288         if isinstance(entry, CIRLabel):
289             lines.append('\n{}:'.format(entry.label))
290
291     return '\n'.join(lines).lstrip()