Added variable-length args to function calls
[fur] / normalization.py
1 import collections
2
3 import parsing
4
5 NormalVariableExpression = collections.namedtuple(
6     'NormalVariableExpression',
7     [
8         'variable',
9     ],
10 )
11
12 NormalNegationExpression = collections.namedtuple(
13     'NormalNegationExpression',
14     [
15         'internal_expression',
16     ],
17 )
18
19 NormalInfixExpression = collections.namedtuple(
20     'NormalInfixExpression',
21     [
22         'order',
23         'operator',
24         'left',
25         'right',
26     ],
27 )
28
29 NormalFunctionCallExpression = collections.namedtuple(
30     'NormalFunctionCallExpression',
31     [
32         'function',
33         'argument_count',
34         'argument_items',
35     ],
36 )
37
38 NormalArrayVariableInitializationStatement = collections.namedtuple(
39     'NormalArrayVariableInitializationStatement',
40     [
41         'variable',
42         'items',
43     ],
44 )
45
46 NormalVariableInitializationStatement = collections.namedtuple(
47     'NormalVariableInitializationStatement',
48     [
49         'variable',
50         'expression',
51     ],
52 )
53
54 NormalVariableReassignmentStatement = collections.namedtuple(
55     'NormalVariableReassignmentStatement',
56     [
57         'variable',
58         'expression',
59     ],
60 )
61
62 NormalExpressionStatement = collections.namedtuple(
63     'NormalExpressionStatement',
64     [
65         'expression',
66     ],
67 )
68
69 NormalIfElseStatement = collections.namedtuple(
70     'NormalIfElseStatement',
71     [
72         'condition_expression',
73         'if_statements',
74         'else_statements',
75     ],
76 )
77
78 NormalProgram = collections.namedtuple(
79     'NormalProgram',
80     [
81         'statement_list',
82     ],
83 )
84
85 def fake_normalization(counter, thing):
86     return (counter, (), thing)
87
88 def normalize_function_call_expression(counter, expression):
89     assert isinstance(expression, parsing.FurFunctionCallExpression)
90
91     prestatements = []
92     arguments = []
93
94     for argument in expression.arguments:
95         counter, argument_prestatements, normalized_argument = normalize_expression(counter, argument)
96
97         for s in argument_prestatements:
98             prestatements.append(s)
99
100         variable = '${}'.format(counter)
101         prestatements.append(NormalVariableInitializationStatement(
102             variable=variable,
103             expression=normalized_argument,
104         ))
105         arguments.append(NormalVariableExpression(
106             variable=variable,
107         ))
108         counter += 1
109
110     arguments_variable = '${}'.format(counter)
111     counter += 1
112
113     prestatements.append(NormalArrayVariableInitializationStatement(
114         variable=arguments_variable,
115         items=tuple(arguments),
116     ))
117
118     return (
119         counter,
120         tuple(prestatements),
121         NormalFunctionCallExpression(
122             function=expression.function, # TODO Normalize the function
123             argument_count=len(arguments),
124             argument_items=NormalVariableExpression(variable=arguments_variable),
125         ),
126     )
127
128 def normalize_basic_infix_operation(counter, expression):
129     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
130     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
131
132     left_variable = '${}'.format(counter)
133     counter += 1
134     right_variable = '${}'.format(counter)
135     counter += 1
136
137     root_prestatements = (
138         NormalVariableInitializationStatement(
139             variable=left_variable,
140             expression=left_expression,
141         ),
142         NormalVariableInitializationStatement(
143             variable=right_variable,
144             expression=right_expression,
145         ),
146     )
147
148     return (
149         counter,
150         left_prestatements + right_prestatements + root_prestatements,
151         NormalInfixExpression(
152             order=expression.order, # TODO Do we need this?
153             operator=expression.operator,
154             left=NormalVariableExpression(variable=left_variable),
155             right=NormalVariableExpression(variable=right_variable),
156         ),
157     )
158
159 def normalize_comparison_expression(counter, expression):
160     stack = []
161
162     while isinstance(expression.left, parsing.FurInfixExpression) and expression.order == 'comparison_level':
163         stack.append((expression.operator, expression.order, expression.right))
164         expression = expression.left
165
166     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
167     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
168
169     left_variable = '${}'.format(counter)
170     counter += 1
171     right_variable = '${}'.format(counter)
172     counter += 1
173
174     root_prestatements = (
175         NormalVariableInitializationStatement(
176             variable=left_variable,
177             expression=left_expression,
178         ),
179         NormalVariableInitializationStatement(
180             variable=right_variable,
181             expression=right_expression,
182         ),
183     )
184
185     counter, result_prestatements, result_expression = (
186         counter,
187         left_prestatements + right_prestatements + root_prestatements,
188         # TODO Implement short-circuiting
189         NormalInfixExpression(
190             order=expression.order, # TODO Do we need this?
191             operator=expression.operator,
192             left=NormalVariableExpression(variable=left_variable),
193             right=NormalVariableExpression(variable=right_variable),
194         ),
195     )
196
197     while len(stack) > 0:
198         right_operator, right_order, right_expression = stack.pop()
199         and_right_expression = parsing.FurInfixExpression(
200             operator=right_operator,
201             order=right_order,
202             left=NormalVariableExpression(variable=right_variable),
203             right=right_expression,
204         )
205
206         and_expression = parsing.FurInfixExpression(
207             operator='and',
208             order='and_level',
209             left=result_expression,
210             right=and_right_expression,
211         )
212
213         counter, and_prestatements, result_expression = normalize_boolean_expression(
214             counter,
215             and_expression,
216         )
217
218         result_prestatements = result_prestatements + and_prestatements
219
220     return (counter, result_prestatements, result_expression)
221
222 def normalize_boolean_expression(counter, expression):
223     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
224     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
225
226     result_variable = '${}'.format(counter)
227     if_else_prestatment = NormalVariableInitializationStatement(variable=result_variable, expression=left_expression)
228     counter += 1
229
230     condition_expression=NormalVariableExpression(variable=result_variable)
231     short_circuited_statements = right_prestatements + (NormalVariableReassignmentStatement(variable=result_variable, expression=right_expression),)
232
233     if expression.operator == 'and':
234         if_else_statement = NormalIfElseStatement(
235             condition_expression=condition_expression,
236             if_statements=short_circuited_statements,
237             else_statements=(),
238         )
239
240     elif expression.operator == 'or':
241         if_else_statement = NormalIfElseStatement(
242             condition_expression=condition_expression,
243             if_statements=(),
244             else_statements=short_circuited_statements,
245         )
246
247     else:
248         raise Exception('Unable to handle operator "{}"'.format(expression.operator))
249
250     return (
251         counter,
252         left_prestatements + (if_else_prestatment, if_else_statement),
253         NormalVariableExpression(variable=result_variable),
254     )
255
256
257 def normalize_infix_expression(counter, expression):
258     return {
259         'multiplication_level': normalize_basic_infix_operation,
260         'addition_level': normalize_basic_infix_operation,
261         'comparison_level': normalize_comparison_expression,
262         'and_level': normalize_boolean_expression,
263         'or_level': normalize_boolean_expression,
264     }[expression.order](counter, expression)
265
266 def normalize_negation_expression(counter, expression):
267     counter, prestatements, internal_expression = normalize_expression(counter, expression.value)
268
269     internal_variable = '${}'.format(counter)
270     counter += 1
271
272     return (
273         counter,
274         prestatements + (NormalVariableInitializationStatement(variable=internal_variable, expression=internal_expression),),
275         NormalNegationExpression(internal_expression=NormalVariableExpression(variable=internal_variable)),
276     )
277
278 def normalize_parenthesized_expression(counter, expression):
279     return normalize_expression(counter, expression.internal)
280
281 def normalize_expression(counter, expression):
282     return {
283         NormalInfixExpression: fake_normalization,
284         NormalVariableExpression: fake_normalization,
285         parsing.FurFunctionCallExpression: normalize_function_call_expression,
286         parsing.FurInfixExpression: normalize_infix_expression,
287         parsing.FurIntegerLiteralExpression: fake_normalization,
288         parsing.FurNegationExpression: normalize_negation_expression,
289         parsing.FurParenthesizedExpression: normalize_parenthesized_expression,
290         parsing.FurStringLiteralExpression: fake_normalization,
291         parsing.FurSymbolExpression: fake_normalization,
292     }[type(expression)](counter, expression)
293
294 def normalize_expression_statement(counter, statement):
295     counter, prestatements, normalized = {
296         parsing.FurFunctionCallExpression: normalize_function_call_expression,
297     }[type(statement.expression)](counter, statement.expression)
298
299     return (
300         counter,
301         prestatements,
302         NormalExpressionStatement(expression=normalized),
303     )
304
305 def normalize_statement(counter, statement):
306     return {
307         parsing.FurExpressionStatement: normalize_expression_statement,
308         parsing.FurAssignmentStatement: fake_normalization,
309     }[type(statement)](counter, statement)
310
311 def normalize(program):
312     counter = 0
313     statement_list = []
314
315     for statement in program.statement_list:
316         counter, prestatements, normalized = normalize_statement(counter, statement)
317         for s in prestatements:
318             statement_list.append(s)
319         statement_list.append(normalized)
320
321     return NormalProgram(
322         statement_list=statement_list,
323     )