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