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