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