Closures (and garbage collection for environments, which is required by closures...
[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     return (
138         counter,
139         tuple(prestatements),
140         NormalFunctionCallExpression(
141             function=expression.function, # TODO Normalize the function
142             argument_count=len(arguments),
143             argument_items=NormalVariableExpression(variable=arguments_variable),
144         ),
145     )
146
147 def normalize_basic_infix_operation(counter, expression):
148     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
149     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
150
151     left_variable = '${}'.format(counter)
152     counter += 1
153     right_variable = '${}'.format(counter)
154     counter += 1
155
156     root_prestatements = (
157         NormalVariableInitializationStatement(
158             variable=left_variable,
159             expression=left_expression,
160         ),
161         NormalVariableInitializationStatement(
162             variable=right_variable,
163             expression=right_expression,
164         ),
165     )
166
167     return (
168         counter,
169         left_prestatements + right_prestatements + root_prestatements,
170         NormalInfixExpression(
171             order=expression.order, # TODO Do we need this?
172             operator=expression.operator,
173             left=NormalVariableExpression(variable=left_variable),
174             right=NormalVariableExpression(variable=right_variable),
175         ),
176     )
177
178 def normalize_comparison_expression(counter, expression):
179     stack = []
180
181     while isinstance(expression.left, parsing.FurInfixExpression) and expression.order == 'comparison_level':
182         stack.append((expression.operator, expression.order, expression.right))
183         expression = expression.left
184
185     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
186     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
187
188     left_variable = '${}'.format(counter)
189     counter += 1
190     right_variable = '${}'.format(counter)
191     counter += 1
192
193     root_prestatements = (
194         NormalVariableInitializationStatement(
195             variable=left_variable,
196             expression=left_expression,
197         ),
198         NormalVariableInitializationStatement(
199             variable=right_variable,
200             expression=right_expression,
201         ),
202     )
203
204     counter, result_prestatements, result_expression = (
205         counter,
206         left_prestatements + right_prestatements + root_prestatements,
207         # TODO Implement short-circuiting
208         NormalInfixExpression(
209             order=expression.order, # TODO Do we need this?
210             operator=expression.operator,
211             left=NormalVariableExpression(variable=left_variable),
212             right=NormalVariableExpression(variable=right_variable),
213         ),
214     )
215
216     while len(stack) > 0:
217         right_operator, right_order, right_expression = stack.pop()
218         and_right_expression = parsing.FurInfixExpression(
219             operator=right_operator,
220             order=right_order,
221             left=NormalVariableExpression(variable=right_variable),
222             right=right_expression,
223         )
224
225         and_expression = parsing.FurInfixExpression(
226             operator='and',
227             order='and_level',
228             left=result_expression,
229             right=and_right_expression,
230         )
231
232         counter, and_prestatements, result_expression = normalize_boolean_expression(
233             counter,
234             and_expression,
235         )
236
237         result_prestatements = result_prestatements + and_prestatements
238
239     return (counter, result_prestatements, result_expression)
240
241 def normalize_boolean_expression(counter, expression):
242     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
243     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
244
245     result_variable = '${}'.format(counter)
246     if_else_prestatment = NormalVariableInitializationStatement(variable=result_variable, expression=left_expression)
247     counter += 1
248
249     condition_expression=NormalVariableExpression(variable=result_variable)
250     short_circuited_statements = right_prestatements + (NormalVariableReassignmentStatement(variable=result_variable, expression=right_expression),)
251
252     if expression.operator == 'and':
253         if_else_statement = NormalIfElseStatement(
254             condition_expression=condition_expression,
255             if_statements=short_circuited_statements,
256             else_statements=(),
257         )
258
259     elif expression.operator == 'or':
260         if_else_statement = NormalIfElseStatement(
261             condition_expression=condition_expression,
262             if_statements=(),
263             else_statements=short_circuited_statements,
264         )
265
266     else:
267         raise Exception('Unable to handle operator "{}"'.format(expression.operator))
268
269     return (
270         counter,
271         left_prestatements + (if_else_prestatment, if_else_statement),
272         NormalVariableExpression(variable=result_variable),
273     )
274
275
276 def normalize_infix_expression(counter, expression):
277     return {
278         'multiplication_level': normalize_basic_infix_operation,
279         'addition_level': normalize_basic_infix_operation,
280         'comparison_level': normalize_comparison_expression,
281         'and_level': normalize_boolean_expression,
282         'or_level': normalize_boolean_expression,
283     }[expression.order](counter, expression)
284
285 def normalize_negation_expression(counter, expression):
286     counter, prestatements, internal_expression = normalize_expression(counter, expression.value)
287
288     internal_variable = '${}'.format(counter)
289     counter += 1
290
291     return (
292         counter,
293         prestatements + (NormalVariableInitializationStatement(variable=internal_variable, expression=internal_expression),),
294         NormalNegationExpression(internal_expression=NormalVariableExpression(variable=internal_variable)),
295     )
296
297 def normalize_parenthesized_expression(counter, expression):
298     return normalize_expression(counter, expression.internal)
299
300 def normalize_expression(counter, expression):
301     return {
302         NormalInfixExpression: fake_normalization,
303         NormalVariableExpression: fake_normalization,
304         parsing.FurFunctionCallExpression: normalize_function_call_expression,
305         parsing.FurInfixExpression: normalize_infix_expression,
306         parsing.FurIntegerLiteralExpression: fake_normalization,
307         parsing.FurNegationExpression: normalize_negation_expression,
308         parsing.FurParenthesizedExpression: normalize_parenthesized_expression,
309         parsing.FurStringLiteralExpression: fake_normalization,
310         parsing.FurSymbolExpression: fake_normalization,
311     }[type(expression)](counter, expression)
312
313 def normalize_expression_statement(counter, statement):
314     # TODO Verify all expression types are supported and just call normalize_expression
315     counter, prestatements, normalized = {
316         parsing.FurFunctionCallExpression: normalize_function_call_expression,
317         parsing.FurSymbolExpression: normalize_expression,
318         parsing.FurInfixExpression: normalize_expression,
319         parsing.FurIntegerLiteralExpression: normalize_expression,
320     }[type(statement.expression)](counter, statement.expression)
321
322     return (
323         counter,
324         prestatements,
325         NormalExpressionStatement(expression=normalized),
326     )
327
328 def normalize_function_definition_statement(counter, statement):
329     return (
330         counter,
331         (),
332         NormalFunctionDefinitionStatement(
333             name=statement.name,
334             argument_name_list=statement.argument_name_list,
335             statement_list=normalize_statement_list(statement.statement_list),
336         ),
337     )
338
339 def normalize_assignment_statement(counter, statement):
340     counter, prestatements, normalized_expression = normalize_expression(counter, statement.expression)
341     return (
342         counter,
343         prestatements,
344         NormalAssignmentStatement(
345             target=statement.target,
346             expression=normalized_expression,
347         ),
348     )
349
350 def normalize_statement(counter, statement):
351     return {
352         parsing.FurAssignmentStatement: normalize_assignment_statement,
353         parsing.FurExpressionStatement: normalize_expression_statement,
354         parsing.FurFunctionDefinitionStatement: normalize_function_definition_statement,
355     }[type(statement)](counter, statement)
356
357 @util.force_generator(tuple)
358 def normalize_statement_list(statement_list):
359     counter = 0
360
361     for statement in statement_list:
362         counter, prestatements, normalized = normalize_statement(counter, statement)
363         for s in prestatements:
364             yield s
365         yield normalized
366
367 def normalize(program):
368
369     return NormalProgram(
370         statement_list=normalize_statement_list(program.statement_list),
371     )