Add support for lists to IR, clean up
[fur] / normalization.py
1 import collections
2
3 import desugaring
4 import util
5
6 NormalVariableExpression = collections.namedtuple(
7     'NormalVariableExpression',
8     [
9         'variable',
10     ],
11 )
12
13 NormalIntegerLiteralExpression = collections.namedtuple(
14     'NormalIntegerLiteralExpression',
15     [
16         'integer',
17     ],
18 )
19
20 NormalLambdaExpression = collections.namedtuple(
21     'NormalLambdaExpression',
22     (
23         'name',
24         'argument_name_list',
25         'statement_list',
26     ),
27 )
28
29 NormalStringLiteralExpression = collections.namedtuple(
30     'NormalStringLiteralExpression',
31     [
32         'string',
33     ],
34 )
35
36 NormalSymbolExpression = collections.namedtuple(
37     'NormalSymbolExpression',
38     [
39         'symbol',
40     ],
41 )
42
43 NormalPushStatement = collections.namedtuple(
44     'NormalPushStatement',
45     (
46         'expression',
47     ),
48 )
49
50 NormalFunctionCallExpression = collections.namedtuple(
51     'NormalFunctionCallExpression',
52     [
53         'metadata',
54         'function_expression',
55         'argument_count',
56     ],
57 )
58
59 NormalArrayVariableInitializationStatement = collections.namedtuple(
60     'NormalArrayVariableInitializationStatement',
61     [
62         'variable',
63         'items',
64     ],
65 )
66
67 NormalSymbolArrayVariableInitializationStatement = collections.namedtuple(
68     'NormalSymbolArrayVariableInitializationStatement',
69     [
70         'variable',
71         'symbol_list',
72     ],
73 )
74
75 NormalVariableInitializationStatement = collections.namedtuple(
76     'NormalVariableInitializationStatement',
77     [
78         'variable',
79         'expression',
80     ],
81 )
82
83 NormalExpressionStatement = collections.namedtuple(
84     'NormalExpressionStatement',
85     [
86         'expression',
87     ],
88 )
89
90 NormalAssignmentStatement = collections.namedtuple(
91     'NormalAssignmentStatement',
92     [
93         'target',
94         'expression',
95     ],
96 )
97
98 NormalIfElseExpression = collections.namedtuple(
99     'NormalIfElseExpression',
100     [
101         'condition_expression',
102         'if_statement_list',
103         'else_statement_list',
104     ],
105 )
106
107 NormalProgram = collections.namedtuple(
108     'NormalProgram',
109     [
110         'statement_list',
111     ],
112 )
113
114 def normalize_integer_literal_expression(counter, expression):
115     return (
116         counter,
117         (),
118         NormalIntegerLiteralExpression(integer=expression.integer),
119     )
120
121 def normalize_lambda_expression(counter, expression):
122     variable = '${}'.format(counter)
123
124     _, statement_list = normalize_statement_list(
125         0,
126         expression.statement_list,
127     )
128
129     return (
130         counter + 1,
131         (
132             NormalVariableInitializationStatement(
133                 variable=variable,
134                 expression=NormalLambdaExpression(
135                     name=expression.name,
136                     argument_name_list=expression.argument_name_list,
137                     statement_list=statement_list,
138                 ),
139             ),
140         ),
141         NormalVariableExpression(variable=variable),
142     )
143
144 NormalListConstructExpression = collections.namedtuple(
145     'NormalListConstructExpression',
146     [
147         'allocate',
148     ],
149 )
150
151 NormalListAppendStatement = collections.namedtuple(
152     'NormalListAppendStatement',
153     [
154         'list_expression',
155         'item_expression',
156     ],
157 )
158
159 def normalize_list_literal_expression(counter, expression):
160     list_variable = '${}'.format(counter)
161     counter += 1
162
163     prestatements = []
164
165     list_expression = NormalVariableExpression(variable=list_variable)
166
167     for item_expression in expression.item_expression_list:
168         counter, item_expression_prestatements, normalized = normalize_expression(
169             counter,
170             item_expression,
171         )
172
173         for p in item_expression_prestatements:
174             prestatements.append(p)
175
176         prestatements.append(
177             NormalPushStatement(
178                 expression=normalized,
179             )
180         )
181
182     prestatements.append(NormalVariableInitializationStatement(
183         variable=list_variable,
184         expression=NormalListConstructExpression(allocate=len(expression.item_expression_list)),
185     ))
186
187     return (
188         counter,
189         tuple(prestatements),
190         list_expression,
191     )
192
193 def normalize_string_literal_expression(counter, expression):
194     return (
195         counter,
196         (),
197         NormalStringLiteralExpression(string=expression.string),
198     )
199
200 NormalStructureLiteralExpression = collections.namedtuple(
201     'NormalStructureLiteralExpression',
202     [
203         'field_count',
204         'symbol_list_variable',
205         'value_list_variable',
206     ],
207 )
208
209 def normalize_structure_literal_expression(counter, expression):
210     prestatements = []
211     field_symbol_array = []
212     field_value_array = []
213
214     for symbol_expression_pair in expression.fields:
215         counter, field_prestatements, field_expression = normalize_expression(
216             counter,
217             symbol_expression_pair.expression,
218         )
219
220         for p in field_prestatements:
221             prestatements.append(p)
222
223         field_symbol_array.append(symbol_expression_pair.symbol)
224         field_value_array.append(field_expression)
225
226     symbol_array_variable = '${}'.format(counter)
227     counter += 1
228
229     prestatements.append(
230         NormalSymbolArrayVariableInitializationStatement(
231             variable=symbol_array_variable,
232             symbol_list=tuple(field_symbol_array),
233         )
234     )
235
236     value_array_variable = '${}'.format(counter)
237     counter += 1
238
239     prestatements.append(
240         NormalArrayVariableInitializationStatement(
241             variable=value_array_variable,
242             items=tuple(field_value_array),
243         )
244     )
245
246     variable = '${}'.format(counter)
247
248     prestatements.append(
249         NormalVariableInitializationStatement(
250             variable=variable,
251             expression=NormalStructureLiteralExpression(
252                 field_count=len(expression.fields),
253                 symbol_list_variable=symbol_array_variable,
254                 value_list_variable=value_array_variable,
255             ),
256         )
257     )
258
259     return (
260         counter + 1,
261         tuple(prestatements),
262         NormalVariableExpression(variable=variable),
263     )
264
265
266 def normalize_symbol_expression(counter, expression):
267     return (
268         counter,
269         (),
270         NormalSymbolExpression(symbol=expression.symbol),
271     )
272
273 def normalize_function_call_expression(counter, expression):
274     prestatements = []
275
276     for argument in expression.argument_list:
277         counter, argument_prestatements, normalized_argument = normalize_expression(counter, argument)
278
279         for s in argument_prestatements:
280             prestatements.append(s)
281
282         prestatements.append(
283             NormalPushStatement(
284                 expression=normalized_argument,
285             ),
286         )
287
288     counter, function_prestatements, function_expression = normalize_expression(
289         counter,
290         expression.function,
291     )
292
293     for ps in function_prestatements:
294         prestatements.append(ps)
295
296     result_variable = '${}'.format(counter)
297
298     prestatements.append(
299         NormalVariableInitializationStatement(
300             variable=result_variable,
301             expression=NormalFunctionCallExpression(
302                 metadata=expression.metadata,
303                 function_expression=function_expression,
304                 argument_count=len(expression.argument_list),
305             ),
306         )
307     )
308
309     return (
310         counter + 1,
311         tuple(prestatements),
312         NormalVariableExpression(variable=result_variable),
313     )
314
315 def normalize_if_expression(counter, expression):
316     counter, condition_prestatements, condition_expression = normalize_expression(
317         counter,
318         expression.condition_expression,
319     )
320
321     counter, if_statement_list = normalize_statement_list(
322         counter,
323         expression.if_statement_list,
324     )
325     counter, else_statement_list = normalize_statement_list(
326         counter,
327         expression.else_statement_list,
328     )
329
330     return (
331         counter,
332         condition_prestatements,
333         NormalIfElseExpression(
334             condition_expression=condition_expression,
335             if_statement_list=if_statement_list,
336             else_statement_list=else_statement_list,
337         ),
338     )
339
340 def normalize_expression(counter, expression):
341     return {
342         desugaring.DesugaredFunctionCallExpression: normalize_function_call_expression,
343         desugaring.DesugaredIfExpression: normalize_if_expression,
344         desugaring.DesugaredIntegerLiteralExpression: normalize_integer_literal_expression,
345         desugaring.DesugaredLambdaExpression: normalize_lambda_expression,
346         desugaring.DesugaredListLiteralExpression: normalize_list_literal_expression,
347         desugaring.DesugaredStringLiteralExpression: normalize_string_literal_expression,
348         desugaring.DesugaredStructureLiteralExpression: normalize_structure_literal_expression,
349         desugaring.DesugaredSymbolExpression: normalize_symbol_expression,
350     }[type(expression)](counter, expression)
351
352 def normalize_expression_statement(counter, statement):
353     # TODO Normalized will be a NormalVariableExpression, which will go unused
354     # for expression statements in every case except when it's a return
355     # statement. This cases warnings on C compilation. We should only generate
356     # this variable when it will be used on return.
357     counter, prestatements, normalized = normalize_expression(counter, statement.expression)
358
359     return (
360         counter,
361         prestatements,
362         NormalExpressionStatement(expression=normalized),
363     )
364
365 def normalize_assignment_statement(counter, statement):
366     counter, prestatements, normalized_expression = normalize_expression(counter, statement.expression)
367     return (
368         counter,
369         prestatements,
370         NormalAssignmentStatement(
371             target=statement.target,
372             expression=normalized_expression,
373         ),
374     )
375
376 def normalize_statement(counter, statement):
377     return {
378         desugaring.DesugaredAssignmentStatement: normalize_assignment_statement,
379         desugaring.DesugaredExpressionStatement: normalize_expression_statement,
380     }[type(statement)](counter, statement)
381
382 @util.force_generator(tuple)
383 def normalize_statement_list(counter, statement_list):
384     result_statement_list = []
385
386     for statement in statement_list:
387         counter, prestatements, normalized = normalize_statement(counter, statement)
388         for s in prestatements:
389             result_statement_list.append(s)
390         result_statement_list.append(normalized)
391
392     return (
393         counter,
394         result_statement_list,
395     )
396
397 def normalize(program):
398     _, statement_list = normalize_statement_list(0, program.statement_list)
399
400     return NormalProgram(
401         statement_list=statement_list,
402     )