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