Normalize comparison expressions, even ternary comparison expressions
[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 NormalVariableAssignmentStatement = collections.namedtuple(
31     'NormalVariableAssignmentStatement',
32     [
33         'variable',
34         'expression',
35     ],
36 )
37
38 NormalExpressionStatement = collections.namedtuple(
39     'NormalExpressionStatement',
40     [
41         'expression',
42     ],
43 )
44
45 NormalProgram = collections.namedtuple(
46     'NormalProgram',
47     [
48         'statement_list',
49     ],
50 )
51
52 def fake_normalization(counter, thing):
53     return (counter, (), thing)
54
55 def normalize_function_call_expression(counter, expression):
56     assert isinstance(expression, parsing.FurFunctionCallExpression)
57
58     prestatements = []
59     arguments = []
60
61     for argument in expression.arguments:
62         counter, argument_prestatements, normalized_argument = normalize_expression(counter, argument)
63
64         for s in argument_prestatements:
65             prestatements.append(s)
66
67         variable = '${}'.format(counter)
68         prestatements.append(NormalVariableAssignmentStatement(
69             variable=variable,
70             expression=normalized_argument,
71         ))
72         arguments.append(NormalVariableExpression(
73             variable=variable,
74         ))
75         counter += 1
76
77     return (
78         counter,
79         tuple(prestatements),
80         NormalFunctionCallExpression(
81             expression.function, # TODO Normalize the function
82             arguments=tuple(arguments),
83         ),
84     )
85
86 def normalize_basic_infix_operation(counter, expression):
87     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
88     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
89
90     left_variable = '${}'.format(counter)
91     counter += 1
92     right_variable = '${}'.format(counter)
93     counter += 1
94
95     root_prestatements = (
96         NormalVariableAssignmentStatement(
97             variable=left_variable,
98             expression=left_expression,
99         ),
100         NormalVariableAssignmentStatement(
101             variable=right_variable,
102             expression=right_expression,
103         ),
104     )
105
106     return (
107         counter,
108         left_prestatements + right_prestatements + root_prestatements,
109         NormalInfixExpression(
110             order=expression.order, # TODO Do we need this?
111             operator=expression.operator,
112             left=NormalVariableExpression(variable=left_variable),
113             right=NormalVariableExpression(variable=right_variable),
114         ),
115     )
116
117 def normalize_comparison_expression(counter, expression):
118     stack = []
119
120     while isinstance(expression.left, parsing.FurInfixExpression) and expression.order == 'comparison_level':
121         stack.append((expression.operator, expression.order, expression.right))
122         expression = expression.left
123
124     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
125     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
126
127     left_variable = '${}'.format(counter)
128     counter += 1
129     right_variable = '${}'.format(counter)
130     counter += 1
131
132     root_prestatements = (
133         NormalVariableAssignmentStatement(
134             variable=left_variable,
135             expression=left_expression,
136         ),
137         NormalVariableAssignmentStatement(
138             variable=right_variable,
139             expression=right_expression,
140         ),
141     )
142
143     counter, result_prestatements, result_expression = (
144         counter,
145         left_prestatements + right_prestatements + root_prestatements,
146         # TODO Implement short-circuiting
147         NormalInfixExpression(
148             order=expression.order, # TODO Do we need this?
149             operator=expression.operator,
150             left=NormalVariableExpression(variable=left_variable),
151             right=NormalVariableExpression(variable=right_variable),
152         ),
153     )
154
155     while len(stack) > 0:
156         right_operator, right_order, right_expression = stack.pop()
157         and_right_expression = parsing.FurInfixExpression(
158             operator=right_operator,
159             order=right_order,
160             left=NormalVariableExpression(variable=right_variable),
161             right=right_expression,
162         )
163
164         and_expression = parsing.FurInfixExpression(
165             operator='and',
166             order='and_level',
167             left=result_expression,
168             right=and_right_expression,
169         )
170
171         counter, and_prestatements, result_expression = normalize_boolean_expression(
172             counter,
173             and_expression,
174         )
175
176         result_prestatements = result_prestatements + and_prestatements
177
178     return (counter, result_prestatements, result_expression)
179
180 def normalize_boolean_expression(counter, expression):
181     # TODO Unfake this
182     return fake_normalization(counter, expression)
183
184 def normalize_infix_expression(counter, expression):
185     return {
186         'multiplication_level': normalize_basic_infix_operation,
187         'addition_level': normalize_basic_infix_operation,
188         'comparison_level': normalize_comparison_expression,
189         'and_level': normalize_boolean_expression,
190         'or_level': normalize_boolean_expression,
191     }[expression.order](counter, expression)
192
193 def normalize_expression(counter, expression):
194     return {
195         parsing.FurFunctionCallExpression: normalize_function_call_expression,
196         parsing.FurInfixExpression: normalize_infix_expression,
197         parsing.FurIntegerLiteralExpression: fake_normalization,
198         parsing.FurNegationExpression: fake_normalization, # TODO Don't fake this
199         parsing.FurParenthesizedExpression: fake_normalization, # TODO Don't fake this
200         parsing.FurStringLiteralExpression: fake_normalization,
201         parsing.FurSymbolExpression: fake_normalization,
202     }[type(expression)](counter, expression)
203
204 def normalize_expression_statement(counter, statement):
205     counter, prestatements, normalized = {
206         parsing.FurFunctionCallExpression: normalize_function_call_expression,
207     }[type(statement.expression)](counter, statement.expression)
208
209     return (
210         counter,
211         prestatements,
212         NormalExpressionStatement(expression=normalized),
213     )
214
215 def normalize_statement(counter, statement):
216     return {
217         parsing.FurExpressionStatement: normalize_expression_statement,
218         parsing.FurAssignmentStatement: fake_normalization,
219     }[type(statement)](counter, statement)
220
221 def normalize(program):
222     counter = 0
223     statement_list = []
224
225     for statement in program.statement_list:
226         counter, prestatements, normalized = normalize_statement(counter, statement)
227         for s in prestatements:
228             statement_list.append(s)
229         statement_list.append(normalized)
230
231     return NormalProgram(
232         statement_list=statement_list,
233     )