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