Add support for parenthesized functions
[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 NormalNegationExpression = collections.namedtuple(
14     'NormalNegationExpression',
15     [
16         'internal_expression',
17     ],
18 )
19
20 NormalInfixExpression = collections.namedtuple(
21     'NormalInfixExpression',
22     [
23         'order',
24         'operator',
25         'left',
26         'right',
27     ],
28 )
29
30 NormalFunctionCallExpression = collections.namedtuple(
31     'NormalFunctionCallExpression',
32     [
33         'function',
34         'argument_count',
35         'argument_items',
36     ],
37 )
38
39 NormalArrayVariableInitializationStatement = collections.namedtuple(
40     'NormalArrayVariableInitializationStatement',
41     [
42         'variable',
43         'items',
44     ],
45 )
46
47 NormalVariableInitializationStatement = collections.namedtuple(
48     'NormalVariableInitializationStatement',
49     [
50         'variable',
51         'expression',
52     ],
53 )
54
55 NormalVariableReassignmentStatement = collections.namedtuple(
56     'NormalVariableReassignmentStatement',
57     [
58         'variable',
59         'expression',
60     ],
61 )
62
63 NormalExpressionStatement = collections.namedtuple(
64     'NormalExpressionStatement',
65     [
66         'expression',
67     ],
68 )
69
70 NormalAssignmentStatement = collections.namedtuple(
71     'NormalAssignmentStatement',
72     [
73         'target',
74         'expression',
75     ],
76 )
77
78 NormalIfElseStatement = collections.namedtuple(
79     'NormalIfElseStatement',
80     [
81         'condition_expression',
82         'if_statements',
83         'else_statements',
84     ],
85 )
86
87 NormalFunctionDefinitionStatement = collections.namedtuple(
88     'NormalFunctionDefinitionStatement',
89     [
90         'name',
91         'argument_name_list',
92         'statement_list',
93     ],
94 )
95
96 NormalProgram = collections.namedtuple(
97     'NormalProgram',
98     [
99         'statement_list',
100     ],
101 )
102
103 # TODO Get rid of this
104 def fake_normalization(counter, thing):
105     return (counter, (), thing)
106
107 def normalize_function_call_expression(counter, expression):
108     assert isinstance(expression, parsing.FurFunctionCallExpression)
109
110     prestatements = []
111     arguments = []
112
113     for argument in expression.arguments:
114         counter, argument_prestatements, normalized_argument = normalize_expression(counter, argument)
115
116         for s in argument_prestatements:
117             prestatements.append(s)
118
119         variable = '${}'.format(counter)
120         prestatements.append(NormalVariableInitializationStatement(
121             variable=variable,
122             expression=normalized_argument,
123         ))
124         arguments.append(NormalVariableExpression(
125             variable=variable,
126         ))
127         counter += 1
128
129     arguments_variable = '${}'.format(counter)
130     counter += 1
131
132     prestatements.append(NormalArrayVariableInitializationStatement(
133         variable=arguments_variable,
134         items=tuple(arguments),
135     ))
136
137     counter, function_prestatements, function_expression = normalize_expression(
138         counter,
139         expression.function,
140     )
141
142     for ps in function_prestatements:
143         prestatements.append(ps)
144
145     return (
146         counter,
147         tuple(prestatements),
148         NormalFunctionCallExpression(
149             function=function_expression,
150             argument_count=len(arguments),
151             argument_items=NormalVariableExpression(variable=arguments_variable),
152         ),
153     )
154
155 def normalize_basic_infix_operation(counter, expression):
156     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
157     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
158
159     left_variable = '${}'.format(counter)
160     counter += 1
161     right_variable = '${}'.format(counter)
162     counter += 1
163
164     root_prestatements = (
165         NormalVariableInitializationStatement(
166             variable=left_variable,
167             expression=left_expression,
168         ),
169         NormalVariableInitializationStatement(
170             variable=right_variable,
171             expression=right_expression,
172         ),
173     )
174
175     return (
176         counter,
177         left_prestatements + right_prestatements + root_prestatements,
178         NormalInfixExpression(
179             order=expression.order, # TODO Do we need this?
180             operator=expression.operator,
181             left=NormalVariableExpression(variable=left_variable),
182             right=NormalVariableExpression(variable=right_variable),
183         ),
184     )
185
186 def normalize_comparison_expression(counter, expression):
187     stack = []
188
189     while isinstance(expression.left, parsing.FurInfixExpression) and expression.order == 'comparison_level':
190         stack.append((expression.operator, expression.order, expression.right))
191         expression = expression.left
192
193     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
194     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
195
196     left_variable = '${}'.format(counter)
197     counter += 1
198     right_variable = '${}'.format(counter)
199     counter += 1
200
201     root_prestatements = (
202         NormalVariableInitializationStatement(
203             variable=left_variable,
204             expression=left_expression,
205         ),
206         NormalVariableInitializationStatement(
207             variable=right_variable,
208             expression=right_expression,
209         ),
210     )
211
212     counter, result_prestatements, result_expression = (
213         counter,
214         left_prestatements + right_prestatements + root_prestatements,
215         # TODO Implement short-circuiting
216         NormalInfixExpression(
217             order=expression.order, # TODO Do we need this?
218             operator=expression.operator,
219             left=NormalVariableExpression(variable=left_variable),
220             right=NormalVariableExpression(variable=right_variable),
221         ),
222     )
223
224     while len(stack) > 0:
225         right_operator, right_order, right_expression = stack.pop()
226         and_right_expression = parsing.FurInfixExpression(
227             operator=right_operator,
228             order=right_order,
229             left=NormalVariableExpression(variable=right_variable),
230             right=right_expression,
231         )
232
233         and_expression = parsing.FurInfixExpression(
234             operator='and',
235             order='and_level',
236             left=result_expression,
237             right=and_right_expression,
238         )
239
240         counter, and_prestatements, result_expression = normalize_boolean_expression(
241             counter,
242             and_expression,
243         )
244
245         result_prestatements = result_prestatements + and_prestatements
246
247     return (counter, result_prestatements, result_expression)
248
249 def normalize_boolean_expression(counter, expression):
250     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
251     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
252
253     result_variable = '${}'.format(counter)
254     if_else_prestatment = NormalVariableInitializationStatement(variable=result_variable, expression=left_expression)
255     counter += 1
256
257     condition_expression=NormalVariableExpression(variable=result_variable)
258     short_circuited_statements = right_prestatements + (NormalVariableReassignmentStatement(variable=result_variable, expression=right_expression),)
259
260     if expression.operator == 'and':
261         if_else_statement = NormalIfElseStatement(
262             condition_expression=condition_expression,
263             if_statements=short_circuited_statements,
264             else_statements=(),
265         )
266
267     elif expression.operator == 'or':
268         if_else_statement = NormalIfElseStatement(
269             condition_expression=condition_expression,
270             if_statements=(),
271             else_statements=short_circuited_statements,
272         )
273
274     else:
275         raise Exception('Unable to handle operator "{}"'.format(expression.operator))
276
277     return (
278         counter,
279         left_prestatements + (if_else_prestatment, if_else_statement),
280         NormalVariableExpression(variable=result_variable),
281     )
282
283
284 def normalize_infix_expression(counter, expression):
285     return {
286         'multiplication_level': normalize_basic_infix_operation,
287         'addition_level': normalize_basic_infix_operation,
288         'comparison_level': normalize_comparison_expression,
289         'and_level': normalize_boolean_expression,
290         'or_level': normalize_boolean_expression,
291     }[expression.order](counter, expression)
292
293 def normalize_negation_expression(counter, expression):
294     counter, prestatements, internal_expression = normalize_expression(counter, expression.value)
295
296     internal_variable = '${}'.format(counter)
297     counter += 1
298
299     return (
300         counter,
301         prestatements + (NormalVariableInitializationStatement(variable=internal_variable, expression=internal_expression),),
302         NormalNegationExpression(internal_expression=NormalVariableExpression(variable=internal_variable)),
303     )
304
305 def normalize_expression(counter, expression):
306     return {
307         NormalInfixExpression: fake_normalization,
308         NormalVariableExpression: fake_normalization,
309         parsing.FurFunctionCallExpression: normalize_function_call_expression,
310         parsing.FurInfixExpression: normalize_infix_expression,
311         parsing.FurIntegerLiteralExpression: fake_normalization,
312         parsing.FurNegationExpression: normalize_negation_expression,
313         parsing.FurStringLiteralExpression: fake_normalization,
314         parsing.FurSymbolExpression: fake_normalization,
315     }[type(expression)](counter, expression)
316
317 def normalize_expression_statement(counter, statement):
318     # TODO Verify all expression types are supported and just call normalize_expression
319     counter, prestatements, normalized = {
320         parsing.FurFunctionCallExpression: normalize_function_call_expression,
321         parsing.FurSymbolExpression: normalize_expression,
322         parsing.FurInfixExpression: normalize_expression,
323         parsing.FurIntegerLiteralExpression: normalize_expression,
324     }[type(statement.expression)](counter, statement.expression)
325
326     return (
327         counter,
328         prestatements,
329         NormalExpressionStatement(expression=normalized),
330     )
331
332 def normalize_function_definition_statement(counter, statement):
333     return (
334         counter,
335         (),
336         NormalFunctionDefinitionStatement(
337             name=statement.name,
338             argument_name_list=statement.argument_name_list,
339             statement_list=normalize_statement_list(statement.statement_list),
340         ),
341     )
342
343 def normalize_assignment_statement(counter, statement):
344     counter, prestatements, normalized_expression = normalize_expression(counter, statement.expression)
345     return (
346         counter,
347         prestatements,
348         NormalAssignmentStatement(
349             target=statement.target,
350             expression=normalized_expression,
351         ),
352     )
353
354 def normalize_statement(counter, statement):
355     return {
356         parsing.FurAssignmentStatement: normalize_assignment_statement,
357         parsing.FurExpressionStatement: normalize_expression_statement,
358         parsing.FurFunctionDefinitionStatement: normalize_function_definition_statement,
359     }[type(statement)](counter, statement)
360
361 @util.force_generator(tuple)
362 def normalize_statement_list(statement_list):
363     counter = 0
364
365     for statement in statement_list:
366         counter, prestatements, normalized = normalize_statement(counter, statement)
367         for s in prestatements:
368             yield s
369         yield normalized
370
371 def normalize(program):
372
373     return NormalProgram(
374         statement_list=normalize_statement_list(program.statement_list),
375     )