Remove TODO with unclear reasoning
[fur] / normalization.py
1 import collections
2
3 import parsing
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 NormalNegationExpression = collections.namedtuple(
35     'NormalNegationExpression',
36     [
37         'internal_expression',
38     ],
39 )
40
41 NormalInfixExpression = collections.namedtuple(
42     'NormalInfixExpression',
43     [
44         'order',
45         'operator',
46         'left',
47         'right',
48     ],
49 )
50
51 NormalFunctionCallExpression = collections.namedtuple(
52     'NormalFunctionCallExpression',
53     [
54         'function_expression',
55         'argument_count',
56         'argument_items',
57     ],
58 )
59
60 NormalArrayVariableInitializationStatement = collections.namedtuple(
61     'NormalArrayVariableInitializationStatement',
62     [
63         'variable',
64         'items',
65     ],
66 )
67
68 NormalVariableInitializationStatement = collections.namedtuple(
69     'NormalVariableInitializationStatement',
70     [
71         'variable',
72         'expression',
73     ],
74 )
75
76 NormalVariableReassignmentStatement = collections.namedtuple(
77     'NormalVariableReassignmentStatement',
78     [
79         'variable',
80         'expression',
81     ],
82 )
83
84 NormalExpressionStatement = collections.namedtuple(
85     'NormalExpressionStatement',
86     [
87         'expression',
88     ],
89 )
90
91 NormalAssignmentStatement = collections.namedtuple(
92     'NormalAssignmentStatement',
93     [
94         'target',
95         'expression',
96     ],
97 )
98
99 NormalIfElseStatement = collections.namedtuple(
100     'NormalIfElseStatement',
101     [
102         'condition_expression',
103         'if_statements',
104         'else_statements',
105     ],
106 )
107
108 NormalFunctionDefinitionStatement = collections.namedtuple(
109     'NormalFunctionDefinitionStatement',
110     [
111         'name',
112         'argument_name_list',
113         'statement_list',
114     ],
115 )
116
117 NormalProgram = collections.namedtuple(
118     'NormalProgram',
119     [
120         'statement_list',
121     ],
122 )
123
124 def fake_normalization(counter, thing):
125     return (counter, (), thing)
126
127 def normalize_integer_literal_expression(counter, expression):
128     return (
129         counter,
130         (),
131         NormalIntegerLiteralExpression(integer=expression.integer),
132     )
133
134 def normalize_string_literal_expression(counter, expression):
135     return (
136         counter,
137         (),
138         NormalStringLiteralExpression(string=expression.string),
139     )
140
141 def normalize_symbol_expression(counter, expression):
142     variable = '${}'.format(counter)
143     return (
144         counter + 1,
145         (NormalVariableInitializationStatement(
146             variable=variable,
147             expression=NormalSymbolExpression(symbol=expression.symbol),
148         ),),
149         NormalVariableExpression(variable=variable),
150     )
151
152 def normalize_function_call_expression(counter, expression):
153     assert isinstance(expression, parsing.FurFunctionCallExpression)
154
155     prestatements = []
156     arguments = []
157
158     for argument in expression.arguments:
159         counter, argument_prestatements, normalized_argument = normalize_expression(counter, argument)
160
161         for s in argument_prestatements:
162             prestatements.append(s)
163
164         variable = '${}'.format(counter)
165         prestatements.append(NormalVariableInitializationStatement(
166             variable=variable,
167             expression=normalized_argument,
168         ))
169         arguments.append(NormalVariableExpression(
170             variable=variable,
171         ))
172         counter += 1
173
174     arguments_variable = '${}'.format(counter)
175     counter += 1
176
177     prestatements.append(NormalArrayVariableInitializationStatement(
178         variable=arguments_variable,
179         items=tuple(arguments),
180     ))
181
182     counter, function_prestatements, function_expression = normalize_expression(
183         counter,
184         expression.function,
185     )
186
187     for ps in function_prestatements:
188         prestatements.append(ps)
189
190     if not isinstance(function_expression, NormalVariableExpression):
191         function_variable = '${}'.format(counter)
192
193         prestatements.append(NormalVariableInitializationStatement(
194             variable=function_variable,
195             expression=function_expression,
196         ))
197
198         function_expression = NormalVariableExpression(variable=function_variable)
199         counter += 1
200
201     return (
202         counter,
203         tuple(prestatements),
204         NormalFunctionCallExpression(
205             function_expression=function_expression,
206             argument_count=len(arguments),
207             argument_items=NormalVariableExpression(variable=arguments_variable),
208         ),
209     )
210
211 def normalize_basic_infix_operation(counter, expression):
212     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
213     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
214
215     left_variable = '${}'.format(counter)
216     counter += 1
217     right_variable = '${}'.format(counter)
218     counter += 1
219
220     root_prestatements = (
221         NormalVariableInitializationStatement(
222             variable=left_variable,
223             expression=left_expression,
224         ),
225         NormalVariableInitializationStatement(
226             variable=right_variable,
227             expression=right_expression,
228         ),
229     )
230
231     return (
232         counter,
233         left_prestatements + right_prestatements + root_prestatements,
234         NormalInfixExpression(
235             order=expression.order,
236             operator=expression.operator,
237             left=NormalVariableExpression(variable=left_variable),
238             right=NormalVariableExpression(variable=right_variable),
239         ),
240     )
241
242 def normalize_comparison_expression(counter, expression):
243     stack = []
244
245     while isinstance(expression.left, parsing.FurInfixExpression) and expression.order == 'comparison_level':
246         stack.append((expression.operator, expression.order, expression.right))
247         expression = expression.left
248
249     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
250     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
251
252     left_variable = '${}'.format(counter)
253     counter += 1
254     right_variable = '${}'.format(counter)
255     counter += 1
256
257     root_prestatements = (
258         NormalVariableInitializationStatement(
259             variable=left_variable,
260             expression=left_expression,
261         ),
262         NormalVariableInitializationStatement(
263             variable=right_variable,
264             expression=right_expression,
265         ),
266     )
267
268     counter, result_prestatements, result_expression = (
269         counter,
270         left_prestatements + right_prestatements + root_prestatements,
271         NormalInfixExpression(
272             order=expression.order,
273             operator=expression.operator,
274             left=NormalVariableExpression(variable=left_variable),
275             right=NormalVariableExpression(variable=right_variable),
276         ),
277     )
278
279     while len(stack) > 0:
280         right_operator, right_order, right_expression = stack.pop()
281         and_right_expression = parsing.FurInfixExpression(
282             operator=right_operator,
283             order=right_order,
284             left=NormalVariableExpression(variable=right_variable),
285             right=right_expression,
286         )
287
288         and_expression = parsing.FurInfixExpression(
289             operator='and',
290             order='and_level',
291             left=result_expression,
292             right=and_right_expression,
293         )
294
295         counter, and_prestatements, result_expression = normalize_boolean_expression(
296             counter,
297             and_expression,
298         )
299
300         result_prestatements = result_prestatements + and_prestatements
301
302     return (counter, result_prestatements, result_expression)
303
304 def normalize_boolean_expression(counter, expression):
305     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
306     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
307
308     result_variable = '${}'.format(counter)
309     if_else_prestatment = NormalVariableInitializationStatement(variable=result_variable, expression=left_expression)
310     counter += 1
311
312     condition_expression=NormalVariableExpression(variable=result_variable)
313     short_circuited_statements = right_prestatements + (NormalVariableReassignmentStatement(variable=result_variable, expression=right_expression),)
314
315     if expression.operator == 'and':
316         if_else_statement = NormalIfElseStatement(
317             condition_expression=condition_expression,
318             if_statements=short_circuited_statements,
319             else_statements=(),
320         )
321
322     elif expression.operator == 'or':
323         if_else_statement = NormalIfElseStatement(
324             condition_expression=condition_expression,
325             if_statements=(),
326             else_statements=short_circuited_statements,
327         )
328
329     else:
330         raise Exception('Unable to handle operator "{}"'.format(expression.operator))
331
332     return (
333         counter,
334         left_prestatements + (if_else_prestatment, if_else_statement),
335         NormalVariableExpression(variable=result_variable),
336     )
337
338
339 def normalize_infix_expression(counter, expression):
340     return {
341         'multiplication_level': normalize_basic_infix_operation,
342         'addition_level': normalize_basic_infix_operation,
343         'comparison_level': normalize_comparison_expression,
344         'and_level': normalize_boolean_expression,
345         'or_level': normalize_boolean_expression,
346     }[expression.order](counter, expression)
347
348 def normalize_negation_expression(counter, expression):
349     counter, prestatements, internal_expression = normalize_expression(counter, expression.value)
350
351     internal_variable = '${}'.format(counter)
352     counter += 1
353
354     return (
355         counter,
356         prestatements + (NormalVariableInitializationStatement(variable=internal_variable, expression=internal_expression),),
357         NormalNegationExpression(internal_expression=NormalVariableExpression(variable=internal_variable)),
358     )
359
360 def normalize_expression(counter, expression):
361     return {
362         NormalInfixExpression: fake_normalization,
363         NormalVariableExpression: fake_normalization,
364         parsing.FurFunctionCallExpression: normalize_function_call_expression,
365         parsing.FurInfixExpression: normalize_infix_expression,
366         parsing.FurIntegerLiteralExpression: normalize_integer_literal_expression,
367         parsing.FurNegationExpression: normalize_negation_expression,
368         parsing.FurStringLiteralExpression: normalize_string_literal_expression,
369         parsing.FurSymbolExpression: normalize_symbol_expression,
370     }[type(expression)](counter, expression)
371
372 def normalize_expression_statement(counter, statement):
373     # TODO Normalized will be a NormalVariableExpression, which will go unused
374     # for expression statements in every case except when it's a return
375     # statement. This cases warnings on C compilation. We should only generate
376     # this variable when it will be used on return.
377     counter, prestatements, normalized = normalize_expression(counter, statement.expression)
378
379     return (
380         counter,
381         prestatements,
382         NormalExpressionStatement(expression=normalized),
383     )
384
385 def normalize_function_definition_statement(counter, statement):
386     return (
387         counter,
388         (),
389         NormalFunctionDefinitionStatement(
390             name=statement.name,
391             argument_name_list=statement.argument_name_list,
392             statement_list=normalize_statement_list(statement.statement_list),
393         ),
394     )
395
396 def normalize_assignment_statement(counter, statement):
397     counter, prestatements, normalized_expression = normalize_expression(counter, statement.expression)
398     return (
399         counter,
400         prestatements,
401         NormalAssignmentStatement(
402             target=statement.target,
403             expression=normalized_expression,
404         ),
405     )
406
407 def normalize_statement(counter, statement):
408     return {
409         parsing.FurAssignmentStatement: normalize_assignment_statement,
410         parsing.FurExpressionStatement: normalize_expression_statement,
411         parsing.FurFunctionDefinitionStatement: normalize_function_definition_statement,
412     }[type(statement)](counter, statement)
413
414 @util.force_generator(tuple)
415 def normalize_statement_list(statement_list):
416     counter = 0
417
418     for statement in statement_list:
419         counter, prestatements, normalized = normalize_statement(counter, statement)
420         for s in prestatements:
421             yield s
422         yield normalized
423
424 def normalize(program):
425     return NormalProgram(
426         statement_list=normalize_statement_list(program.statement_list),
427     )