Make it easier to run without memory leak tests
[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 NormalVariableReassignmentStatement = collections.namedtuple(
84     'NormalVariableReassignmentStatement',
85     [
86         'variable',
87         'expression',
88     ],
89 )
90
91 NormalExpressionStatement = collections.namedtuple(
92     'NormalExpressionStatement',
93     [
94         'expression',
95     ],
96 )
97
98 NormalAssignmentStatement = collections.namedtuple(
99     'NormalAssignmentStatement',
100     [
101         'target',
102         'expression',
103     ],
104 )
105
106 NormalIfElseStatement = collections.namedtuple(
107     'NormalIfElseStatement',
108     [
109         'condition_expression',
110         'if_statement_list',
111         'else_statement_list',
112     ],
113 )
114
115 NormalProgram = collections.namedtuple(
116     'NormalProgram',
117     [
118         'statement_list',
119     ],
120 )
121
122 def normalize_integer_literal_expression(counter, expression):
123     variable = '${}'.format(counter)
124     return (
125         counter + 1,
126         (
127             NormalVariableInitializationStatement(
128                 variable=variable,
129                 expression=NormalIntegerLiteralExpression(integer=expression.integer),
130             ),
131         ),
132         NormalVariableExpression(variable=variable),
133     )
134
135 def normalize_lambda_expression(counter, expression):
136     variable = '${}'.format(counter)
137
138     _, statement_list = normalize_statement_list(
139         0,
140         expression.statement_list,
141         assign_result_to='result',
142     )
143
144     return (
145         counter + 1,
146         (
147             NormalVariableInitializationStatement(
148                 variable=variable,
149                 expression=NormalLambdaExpression(
150                     name=expression.name,
151                     argument_name_list=expression.argument_name_list,
152                     statement_list=statement_list,
153                 ),
154             ),
155         ),
156         NormalVariableExpression(variable=variable),
157     )
158
159 NormalListConstructExpression = collections.namedtuple(
160     'NormalListConstructExpression',
161     [
162         'allocate',
163     ],
164 )
165
166 NormalListAppendStatement = collections.namedtuple(
167     'NormalListAppendStatement',
168     [
169         'list_expression',
170         'item_expression',
171     ],
172 )
173
174 def normalize_list_literal_expression(counter, expression):
175     list_variable = '${}'.format(counter)
176     counter += 1
177
178     prestatements = [
179         NormalVariableInitializationStatement(
180             variable=list_variable,
181             expression=NormalListConstructExpression(allocate=len(expression.item_expression_list)),
182         ),
183     ]
184
185     list_expression = NormalVariableExpression(variable=list_variable)
186
187     for item_expression in expression.item_expression_list:
188         counter, item_expression_prestatements, normalized = normalize_expression(
189             counter,
190             item_expression,
191         )
192
193         for p in item_expression_prestatements:
194             prestatements.append(p)
195
196         prestatements.append(
197             NormalListAppendStatement(
198                 list_expression=list_expression,
199                 item_expression=normalized,
200             )
201         )
202
203     return (
204         counter,
205         tuple(prestatements),
206         list_expression,
207     )
208
209 def normalize_string_literal_expression(counter, expression):
210     variable = '${}'.format(counter)
211     return (
212         counter + 1,
213         (
214             NormalVariableInitializationStatement(
215                 variable=variable,
216                 expression=NormalStringLiteralExpression(string=expression.string),
217             ),
218         ),
219         NormalVariableExpression(variable=variable),
220     )
221
222 NormalStructureLiteralExpression = collections.namedtuple(
223     'NormalStructureLiteralExpression',
224     [
225         'field_count',
226         'symbol_list_variable',
227         'value_list_variable',
228     ],
229 )
230
231 def normalize_structure_literal_expression(counter, expression):
232     prestatements = []
233     field_symbol_array = []
234     field_value_array = []
235
236     for symbol_expression_pair in expression.fields:
237         counter, field_prestatements, field_expression = normalize_expression(
238             counter,
239             symbol_expression_pair.expression,
240         )
241
242         for p in field_prestatements:
243             prestatements.append(p)
244
245         field_symbol_array.append(symbol_expression_pair.symbol)
246         field_value_array.append(field_expression)
247
248     symbol_array_variable = '${}'.format(counter)
249     counter += 1
250
251     prestatements.append(
252         NormalSymbolArrayVariableInitializationStatement(
253             variable=symbol_array_variable,
254             symbol_list=tuple(field_symbol_array),
255         )
256     )
257
258     value_array_variable = '${}'.format(counter)
259     counter += 1
260
261     prestatements.append(
262         NormalArrayVariableInitializationStatement(
263             variable=value_array_variable,
264             items=tuple(field_value_array),
265         )
266     )
267
268     variable = '${}'.format(counter)
269
270     prestatements.append(
271         NormalVariableInitializationStatement(
272             variable=variable,
273             expression=NormalStructureLiteralExpression(
274                 field_count=len(expression.fields),
275                 symbol_list_variable=symbol_array_variable,
276                 value_list_variable=value_array_variable,
277             ),
278         )
279     )
280
281     return (
282         counter + 1,
283         tuple(prestatements),
284         NormalVariableExpression(variable=variable),
285     )
286
287
288 def normalize_symbol_expression(counter, expression):
289     variable = '${}'.format(counter)
290     return (
291         counter + 1,
292         (
293             NormalVariableInitializationStatement(
294                 variable=variable,
295                 expression=NormalSymbolExpression(symbol=expression.symbol),
296             ),
297         ),
298         NormalVariableExpression(variable=variable),
299     )
300
301 def normalize_function_call_expression(counter, expression):
302     prestatements = []
303
304     for argument in expression.argument_list:
305         counter, argument_prestatements, normalized_argument = normalize_expression(counter, argument)
306
307         for s in argument_prestatements:
308             prestatements.append(s)
309
310         variable = '${}'.format(counter)
311         prestatements.append(
312             NormalVariableInitializationStatement(
313                 variable=variable,
314                 expression=normalized_argument,
315             )
316         )
317         prestatements.append(
318             NormalPushStatement(
319                 expression=NormalVariableExpression(
320                     variable=variable,
321                 ),
322             ),
323         )
324         counter += 1
325
326     counter, function_prestatements, function_expression = normalize_expression(
327         counter,
328         expression.function,
329     )
330
331     for ps in function_prestatements:
332         prestatements.append(ps)
333
334     if not isinstance(function_expression, NormalVariableExpression):
335         function_variable = '${}'.format(counter)
336
337         prestatements.append(
338             NormalVariableInitializationStatement(
339                 variable=function_variable,
340                 expression=function_expression,
341             )
342         )
343
344         function_expression = NormalVariableExpression(variable=function_variable)
345         counter += 1
346
347     result_variable = '${}'.format(counter)
348
349     prestatements.append(
350         NormalVariableInitializationStatement(
351             variable=result_variable,
352             expression=NormalFunctionCallExpression(
353                 metadata=expression.metadata,
354                 function_expression=function_expression,
355                 argument_count=len(expression.argument_list),
356             ),
357         )
358     )
359
360     return (
361         counter + 1,
362         tuple(prestatements),
363         NormalVariableExpression(variable=result_variable),
364     )
365
366 def normalize_if_expression(counter, expression):
367     counter, condition_prestatements, condition_expression = normalize_expression(
368         counter,
369         expression.condition_expression,
370     )
371
372     result_variable = '${}'.format(counter)
373     counter += 1
374
375     counter, if_statement_list = normalize_statement_list(
376         counter,
377         expression.if_statement_list,
378         assign_result_to=result_variable,
379     )
380     counter, else_statement_list = normalize_statement_list(
381         counter,
382         expression.else_statement_list,
383         assign_result_to=result_variable,
384     )
385
386     return (
387         counter,
388         condition_prestatements + (
389             NormalVariableInitializationStatement(
390                 variable=result_variable,
391                 expression=NormalVariableExpression(variable='builtin$nil'),
392             ),
393             NormalIfElseStatement(
394                 condition_expression=condition_expression,
395                 if_statement_list=if_statement_list,
396                 else_statement_list=else_statement_list,
397             ),
398         ),
399         NormalVariableExpression(variable=result_variable),
400     )
401
402 def normalize_expression(counter, expression):
403     return {
404         desugaring.DesugaredFunctionCallExpression: normalize_function_call_expression,
405         desugaring.DesugaredIfExpression: normalize_if_expression,
406         desugaring.DesugaredIntegerLiteralExpression: normalize_integer_literal_expression,
407         desugaring.DesugaredLambdaExpression: normalize_lambda_expression,
408         desugaring.DesugaredListLiteralExpression: normalize_list_literal_expression,
409         desugaring.DesugaredStringLiteralExpression: normalize_string_literal_expression,
410         desugaring.DesugaredStructureLiteralExpression: normalize_structure_literal_expression,
411         desugaring.DesugaredSymbolExpression: normalize_symbol_expression,
412     }[type(expression)](counter, expression)
413
414 def normalize_expression_statement(counter, statement):
415     # TODO Normalized will be a NormalVariableExpression, which will go unused
416     # for expression statements in every case except when it's a return
417     # statement. This cases warnings on C compilation. We should only generate
418     # this variable when it will be used on return.
419     counter, prestatements, normalized = normalize_expression(counter, statement.expression)
420
421     return (
422         counter,
423         prestatements,
424         NormalExpressionStatement(expression=normalized),
425     )
426
427 def normalize_assignment_statement(counter, statement):
428     counter, prestatements, normalized_expression = normalize_expression(counter, statement.expression)
429     return (
430         counter,
431         prestatements,
432         NormalAssignmentStatement(
433             target=statement.target,
434             expression=normalized_expression,
435         ),
436     )
437
438 def normalize_statement(counter, statement):
439     return {
440         desugaring.DesugaredAssignmentStatement: normalize_assignment_statement,
441         desugaring.DesugaredExpressionStatement: normalize_expression_statement,
442     }[type(statement)](counter, statement)
443
444 @util.force_generator(tuple)
445 def normalize_statement_list(counter, statement_list, **kwargs):
446     assign_result_to = kwargs.pop('assign_result_to', None)
447
448     assert len(kwargs) == 0
449
450     result_statement_list = []
451
452     for statement in statement_list:
453         counter, prestatements, normalized = normalize_statement(counter, statement)
454         for s in prestatements:
455             result_statement_list.append(s)
456         result_statement_list.append(normalized)
457
458     # TODO The way we fix the last statement is really confusing
459     last_statement = result_statement_list[-1]
460
461     if isinstance(last_statement, NormalExpressionStatement) and isinstance(last_statement.expression, NormalVariableExpression):
462         if assign_result_to is not None:
463             result_expression = result_statement_list.pop().expression
464             result_statement_list.append(
465                 NormalVariableReassignmentStatement(
466                     variable=assign_result_to,
467                     expression=result_expression,
468                 )
469             )
470
471     return (
472         counter,
473         result_statement_list,
474     )
475
476 def normalize(program):
477     _, statement_list = normalize_statement_list(0, program.statement_list)
478
479     return NormalProgram(
480         statement_list=statement_list,
481     )