Fix/simplify the generation of if/else statements
[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.CPSIfElseExpression: generate_if_else_expression,
127         conversion.CPSIntegerLiteralExpression: generate_integer_literal_expression,
128         conversion.CPSLambdaExpression: generate_lambda_expression,
129         conversion.CPSStringLiteralExpression: generate_string_literal_expression,
130         conversion.CPSSymbolExpression: generate_symbol_expression,
131         conversion.CPSVariableExpression: generate_variable_expression,
132     }[type(expression)](counters, expression)
133
134 def generate_expression_statement(counters, statement):
135     referenced_entry_list, instruction_list = generate_expression(
136         counters,
137         statement.expression,
138     )
139
140     instruction_list += (
141         CIRInstruction(
142             instruction='drop',
143             argument=None,
144         ),
145     )
146
147     return referenced_entry_list, instruction_list
148
149 def generate_if_else_expression(counters, statement):
150     if_counter = counters['if']
151     counters['if'] += 1
152
153     referenced_entry_list_list = []
154
155     condition_referenced_entry_list, condition_instruction_list = generate_expression(
156         counters,
157         statement.condition_expression,
158     )
159
160     if_instruction_list_list = []
161     for if_statement in statement.if_statement_list:
162         referenced_entry_list, instruction_list = generate_statement(counters, if_statement)
163         referenced_entry_list_list.append(referenced_entry_list)
164         if_instruction_list_list.append(instruction_list)
165
166     if_instruction_list = flatten(if_instruction_list_list)
167     assert if_instruction_list[-1].instruction == 'drop'
168     if_instruction_list = if_instruction_list[:-1]
169
170     else_instruction_list_list = []
171
172     for else_statement in statement.else_statement_list:
173         referenced_entry_list, instruction_list = generate_statement(counters, else_statement)
174         referenced_entry_list_list.append(referenced_entry_list)
175         else_instruction_list_list.append(instruction_list)
176
177     else_instruction_list = flatten(else_instruction_list_list)
178     assert else_instruction_list[-1].instruction == 'drop'
179     else_instruction_list = else_instruction_list[:-1]
180
181     if_label = '__if${}__'.format(if_counter)
182     else_label = '__else${}__'.format(if_counter)
183     endif_label = '__endif${}__'.format(if_counter)
184
185     instruction_list = condition_instruction_list + (
186         CIRInstruction(
187             instruction='jump_if_false',
188             argument=else_label,
189         ),
190         CIRInstruction(
191             instruction='jump',
192             argument=if_label,
193         ),
194         CIRLabel(label=if_label),
195     ) + if_instruction_list + (
196         CIRInstruction(
197             instruction='jump',
198             argument=endif_label,
199         ),
200         CIRLabel(label=else_label),
201     ) + else_instruction_list + (
202         CIRLabel(label=endif_label),
203     )
204
205     return (
206         condition_referenced_entry_list + flatten(referenced_entry_list_list),
207         instruction_list,
208     )
209
210 def generate_assignment_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.target),
220         ),
221     )
222
223     return referenced_entry_list, instruction_list
224
225 def generate_push_statement(counters, statement):
226     return generate_expression(counters, statement.expression)
227
228 def generate_variable_initialization_statement(counters, statement):
229     referenced_entry_list, instruction_list = generate_expression(
230         counters,
231         statement.expression,
232     )
233
234     instruction_list += (
235         CIRInstruction(
236             instruction='pop',
237             argument=generate_symbol_literal(statement.variable),
238         ),
239     )
240
241     return referenced_entry_list, instruction_list
242
243 def generate_statement(counters, statement):
244     return {
245         conversion.CPSAssignmentStatement: generate_assignment_statement,
246         conversion.CPSExpressionStatement: generate_expression_statement,
247         conversion.CPSPushStatement: generate_push_statement,
248         conversion.CPSVariableInitializationStatement: generate_variable_initialization_statement,
249     }[type(statement)](counters, statement)
250
251 def generate(converted):
252     referenced_entry_list_list = []
253     instruction_list_list = []
254     counters = {
255         'if': 0,
256     }
257
258     for statement in converted.statement_list:
259         referenced_entry_list, instruction_list = generate_statement(counters, statement)
260         referenced_entry_list_list.append(referenced_entry_list)
261         instruction_list_list.append(instruction_list)
262
263     return CIRProgram(
264         entry_list=flatten(referenced_entry_list_list) + (
265             CIRLabel(label='__main__'),
266         ) + flatten(instruction_list_list),
267     )
268
269 NO_ARGUMENT_INSTRUCTIONS = set([
270     'drop',
271     'return',
272 ])
273
274 def format_argument(arg):
275     if arg is None:
276         return 'nil'
277     return arg
278
279 def output(program):
280     lines = []
281
282     for entry in program.entry_list:
283         if isinstance(entry, CIRInstruction):
284             if entry.instruction in NO_ARGUMENT_INSTRUCTIONS and entry.argument is None:
285                 lines.append('    {}'.format(entry.instruction))
286             else:
287                 lines.append('    {} {}'.format(entry.instruction, format_argument(entry.argument)))
288
289         if isinstance(entry, CIRLabel):
290             lines.append('\n{}:'.format(entry.label))
291
292     return '\n'.join(lines).lstrip()