a9fa3ec9f29a46c0c19cd5e4a60d7a66ee28fa2a
[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 NormalIntegerLiteralExpression = collections.namedtuple(
14     'NormalIntegerLiteralExpression',
15     [
16         'integer',
17     ],
18 )
19
20 NormalStringLiteralExpression = collections.namedtuple(
21     'NormalStringLiteralExpression',
22     [
23         'string',
24     ],
25 )
26
27 NormalNegationExpression = collections.namedtuple(
28     'NormalNegationExpression',
29     [
30         'internal_expression',
31     ],
32 )
33
34 NormalInfixExpression = collections.namedtuple(
35     'NormalInfixExpression',
36     [
37         'order',
38         'operator',
39         'left',
40         'right',
41     ],
42 )
43
44 NormalFunctionCallExpression = collections.namedtuple(
45     'NormalFunctionCallExpression',
46     [
47         'function',
48         'argument_count',
49         'argument_items',
50     ],
51 )
52
53 NormalArrayVariableInitializationStatement = collections.namedtuple(
54     'NormalArrayVariableInitializationStatement',
55     [
56         'variable',
57         'items',
58     ],
59 )
60
61 NormalVariableInitializationStatement = collections.namedtuple(
62     'NormalVariableInitializationStatement',
63     [
64         'variable',
65         'expression',
66     ],
67 )
68
69 NormalVariableReassignmentStatement = collections.namedtuple(
70     'NormalVariableReassignmentStatement',
71     [
72         'variable',
73         'expression',
74     ],
75 )
76
77 NormalExpressionStatement = collections.namedtuple(
78     'NormalExpressionStatement',
79     [
80         'expression',
81     ],
82 )
83
84 NormalAssignmentStatement = collections.namedtuple(
85     'NormalAssignmentStatement',
86     [
87         'target',
88         'expression',
89     ],
90 )
91
92 NormalIfElseStatement = collections.namedtuple(
93     'NormalIfElseStatement',
94     [
95         'condition_expression',
96         'if_statements',
97         'else_statements',
98     ],
99 )
100
101 NormalFunctionDefinitionStatement = collections.namedtuple(
102     'NormalFunctionDefinitionStatement',
103     [
104         'name',
105         'argument_name_list',
106         'statement_list',
107     ],
108 )
109
110 NormalProgram = collections.namedtuple(
111     'NormalProgram',
112     [
113         'statement_list',
114     ],
115 )
116
117 # TODO Get rid of this
118 def fake_normalization(counter, thing):
119     return (counter, (), thing)
120
121 def normalize_integer_literal_expression(counter, expression):
122     return (counter, (), NormalIntegerLiteralExpression(integer=expression.integer))
123
124 def normalize_string_literal_expression(counter, expression):
125     return (counter, (), NormalStringLiteralExpression(string=expression.string))
126
127 def normalize_function_call_expression(counter, expression):
128     assert isinstance(expression, parsing.FurFunctionCallExpression)
129
130     prestatements = []
131     arguments = []
132
133     for argument in expression.arguments:
134         counter, argument_prestatements, normalized_argument = normalize_expression(counter, argument)
135
136         for s in argument_prestatements:
137             prestatements.append(s)
138
139         variable = '${}'.format(counter)
140         prestatements.append(NormalVariableInitializationStatement(
141             variable=variable,
142             expression=normalized_argument,
143         ))
144         arguments.append(NormalVariableExpression(
145             variable=variable,
146         ))
147         counter += 1
148
149     arguments_variable = '${}'.format(counter)
150     counter += 1
151
152     prestatements.append(NormalArrayVariableInitializationStatement(
153         variable=arguments_variable,
154         items=tuple(arguments),
155     ))
156
157     counter, function_prestatements, function_expression = normalize_expression(
158         counter,
159         expression.function,
160     )
161
162     for ps in function_prestatements:
163         prestatements.append(ps)
164
165     return (
166         counter,
167         tuple(prestatements),
168         NormalFunctionCallExpression(
169             function=function_expression,
170             argument_count=len(arguments),
171             argument_items=NormalVariableExpression(variable=arguments_variable),
172         ),
173     )
174
175 def normalize_basic_infix_operation(counter, expression):
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     return (
196         counter,
197         left_prestatements + right_prestatements + root_prestatements,
198         NormalInfixExpression(
199             order=expression.order, # TODO Do we need this?
200             operator=expression.operator,
201             left=NormalVariableExpression(variable=left_variable),
202             right=NormalVariableExpression(variable=right_variable),
203         ),
204     )
205
206 def normalize_comparison_expression(counter, expression):
207     stack = []
208
209     while isinstance(expression.left, parsing.FurInfixExpression) and expression.order == 'comparison_level':
210         stack.append((expression.operator, expression.order, expression.right))
211         expression = expression.left
212
213     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
214     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
215
216     left_variable = '${}'.format(counter)
217     counter += 1
218     right_variable = '${}'.format(counter)
219     counter += 1
220
221     root_prestatements = (
222         NormalVariableInitializationStatement(
223             variable=left_variable,
224             expression=left_expression,
225         ),
226         NormalVariableInitializationStatement(
227             variable=right_variable,
228             expression=right_expression,
229         ),
230     )
231
232     counter, result_prestatements, result_expression = (
233         counter,
234         left_prestatements + right_prestatements + root_prestatements,
235         # TODO Implement short-circuiting
236         NormalInfixExpression(
237             order=expression.order, # TODO Do we need this?
238             operator=expression.operator,
239             left=NormalVariableExpression(variable=left_variable),
240             right=NormalVariableExpression(variable=right_variable),
241         ),
242     )
243
244     while len(stack) > 0:
245         right_operator, right_order, right_expression = stack.pop()
246         and_right_expression = parsing.FurInfixExpression(
247             operator=right_operator,
248             order=right_order,
249             left=NormalVariableExpression(variable=right_variable),
250             right=right_expression,
251         )
252
253         and_expression = parsing.FurInfixExpression(
254             operator='and',
255             order='and_level',
256             left=result_expression,
257             right=and_right_expression,
258         )
259
260         counter, and_prestatements, result_expression = normalize_boolean_expression(
261             counter,
262             and_expression,
263         )
264
265         result_prestatements = result_prestatements + and_prestatements
266
267     return (counter, result_prestatements, result_expression)
268
269 def normalize_boolean_expression(counter, expression):
270     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
271     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
272
273     result_variable = '${}'.format(counter)
274     if_else_prestatment = NormalVariableInitializationStatement(variable=result_variable, expression=left_expression)
275     counter += 1
276
277     condition_expression=NormalVariableExpression(variable=result_variable)
278     short_circuited_statements = right_prestatements + (NormalVariableReassignmentStatement(variable=result_variable, expression=right_expression),)
279
280     if expression.operator == 'and':
281         if_else_statement = NormalIfElseStatement(
282             condition_expression=condition_expression,
283             if_statements=short_circuited_statements,
284             else_statements=(),
285         )
286
287     elif expression.operator == 'or':
288         if_else_statement = NormalIfElseStatement(
289             condition_expression=condition_expression,
290             if_statements=(),
291             else_statements=short_circuited_statements,
292         )
293
294     else:
295         raise Exception('Unable to handle operator "{}"'.format(expression.operator))
296
297     return (
298         counter,
299         left_prestatements + (if_else_prestatment, if_else_statement),
300         NormalVariableExpression(variable=result_variable),
301     )
302
303
304 def normalize_infix_expression(counter, expression):
305     return {
306         'multiplication_level': normalize_basic_infix_operation,
307         'addition_level': normalize_basic_infix_operation,
308         'comparison_level': normalize_comparison_expression,
309         'and_level': normalize_boolean_expression,
310         'or_level': normalize_boolean_expression,
311     }[expression.order](counter, expression)
312
313 def normalize_negation_expression(counter, expression):
314     counter, prestatements, internal_expression = normalize_expression(counter, expression.value)
315
316     internal_variable = '${}'.format(counter)
317     counter += 1
318
319     return (
320         counter,
321         prestatements + (NormalVariableInitializationStatement(variable=internal_variable, expression=internal_expression),),
322         NormalNegationExpression(internal_expression=NormalVariableExpression(variable=internal_variable)),
323     )
324
325 def normalize_expression(counter, expression):
326     return {
327         NormalInfixExpression: fake_normalization,
328         NormalVariableExpression: fake_normalization,
329         parsing.FurFunctionCallExpression: normalize_function_call_expression,
330         parsing.FurInfixExpression: normalize_infix_expression,
331         parsing.FurIntegerLiteralExpression: normalize_integer_literal_expression,
332         parsing.FurNegationExpression: normalize_negation_expression,
333         parsing.FurStringLiteralExpression: normalize_string_literal_expression,
334         parsing.FurSymbolExpression: fake_normalization,
335     }[type(expression)](counter, expression)
336
337 def normalize_expression_statement(counter, statement):
338     # TODO Verify all expression types are supported and just call normalize_expression
339     counter, prestatements, normalized = {
340         parsing.FurFunctionCallExpression: normalize_function_call_expression,
341         parsing.FurSymbolExpression: normalize_expression,
342         parsing.FurInfixExpression: normalize_expression,
343         parsing.FurIntegerLiteralExpression: normalize_expression,
344     }[type(statement.expression)](counter, statement.expression)
345
346     return (
347         counter,
348         prestatements,
349         NormalExpressionStatement(expression=normalized),
350     )
351
352 def normalize_function_definition_statement(counter, statement):
353     return (
354         counter,
355         (),
356         NormalFunctionDefinitionStatement(
357             name=statement.name,
358             argument_name_list=statement.argument_name_list,
359             statement_list=normalize_statement_list(statement.statement_list),
360         ),
361     )
362
363 def normalize_assignment_statement(counter, statement):
364     counter, prestatements, normalized_expression = normalize_expression(counter, statement.expression)
365     return (
366         counter,
367         prestatements,
368         NormalAssignmentStatement(
369             target=statement.target,
370             expression=normalized_expression,
371         ),
372     )
373
374 def normalize_statement(counter, statement):
375     return {
376         parsing.FurAssignmentStatement: normalize_assignment_statement,
377         parsing.FurExpressionStatement: normalize_expression_statement,
378         parsing.FurFunctionDefinitionStatement: normalize_function_definition_statement,
379     }[type(statement)](counter, statement)
380
381 @util.force_generator(tuple)
382 def normalize_statement_list(statement_list):
383     counter = 0
384
385     for statement in statement_list:
386         counter, prestatements, normalized = normalize_statement(counter, statement)
387         for s in prestatements:
388             yield s
389         yield normalized
390
391 def normalize(program):
392
393     return NormalProgram(
394         statement_list=normalize_statement_list(program.statement_list),
395     )