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 (
130         counter,
131         (),
132         NormalIntegerLiteralExpression(integer=expression.integer),
133     )
134
135 def normalize_string_literal_expression(counter, expression):
136     # TODO Store this in a C variable
137     return (
138         counter,
139         (),
140         NormalStringLiteralExpression(string=expression.string),
141     )
142
143 def normalize_symbol_expression(counter, expression):
144     variable = '${}'.format(counter)
145     return (
146         counter + 1,
147         (NormalVariableInitializationStatement(
148             variable=variable,
149             expression=NormalSymbolExpression(symbol=expression.symbol),
150         ),),
151         NormalVariableExpression(variable=variable),
152     )
153
154 def normalize_function_call_expression(counter, expression):
155     assert isinstance(expression, parsing.FurFunctionCallExpression)
156
157     prestatements = []
158     arguments = []
159
160     for argument in expression.arguments:
161         counter, argument_prestatements, normalized_argument = normalize_expression(counter, argument)
162
163         for s in argument_prestatements:
164             prestatements.append(s)
165
166         variable = '${}'.format(counter)
167         prestatements.append(NormalVariableInitializationStatement(
168             variable=variable,
169             expression=normalized_argument,
170         ))
171         arguments.append(NormalVariableExpression(
172             variable=variable,
173         ))
174         counter += 1
175
176     arguments_variable = '${}'.format(counter)
177     counter += 1
178
179     prestatements.append(NormalArrayVariableInitializationStatement(
180         variable=arguments_variable,
181         items=tuple(arguments),
182     ))
183
184     counter, function_prestatements, function_expression = normalize_expression(
185         counter,
186         expression.function,
187     )
188
189     for ps in function_prestatements:
190         prestatements.append(ps)
191
192     return (
193         counter,
194         tuple(prestatements),
195         NormalFunctionCallExpression(
196             function=function_expression,
197             argument_count=len(arguments),
198             argument_items=NormalVariableExpression(variable=arguments_variable),
199         ),
200     )
201
202 def normalize_basic_infix_operation(counter, expression):
203     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
204     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
205
206     left_variable = '${}'.format(counter)
207     counter += 1
208     right_variable = '${}'.format(counter)
209     counter += 1
210
211     root_prestatements = (
212         NormalVariableInitializationStatement(
213             variable=left_variable,
214             expression=left_expression,
215         ),
216         NormalVariableInitializationStatement(
217             variable=right_variable,
218             expression=right_expression,
219         ),
220     )
221
222     return (
223         counter,
224         left_prestatements + right_prestatements + root_prestatements,
225         NormalInfixExpression(
226             order=expression.order,
227             operator=expression.operator,
228             left=NormalVariableExpression(variable=left_variable),
229             right=NormalVariableExpression(variable=right_variable),
230         ),
231     )
232
233 def normalize_comparison_expression(counter, expression):
234     stack = []
235
236     while isinstance(expression.left, parsing.FurInfixExpression) and expression.order == 'comparison_level':
237         stack.append((expression.operator, expression.order, expression.right))
238         expression = expression.left
239
240     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
241     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
242
243     left_variable = '${}'.format(counter)
244     counter += 1
245     right_variable = '${}'.format(counter)
246     counter += 1
247
248     root_prestatements = (
249         NormalVariableInitializationStatement(
250             variable=left_variable,
251             expression=left_expression,
252         ),
253         NormalVariableInitializationStatement(
254             variable=right_variable,
255             expression=right_expression,
256         ),
257     )
258
259     counter, result_prestatements, result_expression = (
260         counter,
261         left_prestatements + right_prestatements + root_prestatements,
262         NormalInfixExpression(
263             order=expression.order,
264             operator=expression.operator,
265             left=NormalVariableExpression(variable=left_variable),
266             right=NormalVariableExpression(variable=right_variable),
267         ),
268     )
269
270     while len(stack) > 0:
271         right_operator, right_order, right_expression = stack.pop()
272         and_right_expression = parsing.FurInfixExpression(
273             operator=right_operator,
274             order=right_order,
275             left=NormalVariableExpression(variable=right_variable),
276             right=right_expression,
277         )
278
279         and_expression = parsing.FurInfixExpression(
280             operator='and',
281             order='and_level',
282             left=result_expression,
283             right=and_right_expression,
284         )
285
286         counter, and_prestatements, result_expression = normalize_boolean_expression(
287             counter,
288             and_expression,
289         )
290
291         result_prestatements = result_prestatements + and_prestatements
292
293     return (counter, result_prestatements, result_expression)
294
295 def normalize_boolean_expression(counter, expression):
296     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
297     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
298
299     result_variable = '${}'.format(counter)
300     if_else_prestatment = NormalVariableInitializationStatement(variable=result_variable, expression=left_expression)
301     counter += 1
302
303     condition_expression=NormalVariableExpression(variable=result_variable)
304     short_circuited_statements = right_prestatements + (NormalVariableReassignmentStatement(variable=result_variable, expression=right_expression),)
305
306     if expression.operator == 'and':
307         if_else_statement = NormalIfElseStatement(
308             condition_expression=condition_expression,
309             if_statements=short_circuited_statements,
310             else_statements=(),
311         )
312
313     elif expression.operator == 'or':
314         if_else_statement = NormalIfElseStatement(
315             condition_expression=condition_expression,
316             if_statements=(),
317             else_statements=short_circuited_statements,
318         )
319
320     else:
321         raise Exception('Unable to handle operator "{}"'.format(expression.operator))
322
323     return (
324         counter,
325         left_prestatements + (if_else_prestatment, if_else_statement),
326         NormalVariableExpression(variable=result_variable),
327     )
328
329
330 def normalize_infix_expression(counter, expression):
331     return {
332         'multiplication_level': normalize_basic_infix_operation,
333         'addition_level': normalize_basic_infix_operation,
334         'comparison_level': normalize_comparison_expression,
335         'and_level': normalize_boolean_expression,
336         'or_level': normalize_boolean_expression,
337     }[expression.order](counter, expression)
338
339 def normalize_negation_expression(counter, expression):
340     counter, prestatements, internal_expression = normalize_expression(counter, expression.value)
341
342     internal_variable = '${}'.format(counter)
343     counter += 1
344
345     return (
346         counter,
347         prestatements + (NormalVariableInitializationStatement(variable=internal_variable, expression=internal_expression),),
348         NormalNegationExpression(internal_expression=NormalVariableExpression(variable=internal_variable)),
349     )
350
351 def normalize_expression(counter, expression):
352     return {
353         NormalInfixExpression: fake_normalization,
354         NormalVariableExpression: fake_normalization,
355         parsing.FurFunctionCallExpression: normalize_function_call_expression,
356         parsing.FurInfixExpression: normalize_infix_expression,
357         parsing.FurIntegerLiteralExpression: normalize_integer_literal_expression,
358         parsing.FurNegationExpression: normalize_negation_expression,
359         parsing.FurStringLiteralExpression: normalize_string_literal_expression,
360         parsing.FurSymbolExpression: normalize_symbol_expression,
361     }[type(expression)](counter, expression)
362
363 def normalize_expression_statement(counter, statement):
364     # TODO Normalized will be a NormalVariableExpression, which will go unused
365     # for expression statements in every case except when it's a return
366     # statement. This cases warnings on C compilation. We should only generate
367     # this variable when it will be used on return.
368     counter, prestatements, normalized = normalize_expression(counter, statement.expression)
369
370     return (
371         counter,
372         prestatements,
373         NormalExpressionStatement(expression=normalized),
374     )
375
376 def normalize_function_definition_statement(counter, statement):
377     return (
378         counter,
379         (),
380         NormalFunctionDefinitionStatement(
381             name=statement.name,
382             argument_name_list=statement.argument_name_list,
383             statement_list=normalize_statement_list(statement.statement_list),
384         ),
385     )
386
387 def normalize_assignment_statement(counter, statement):
388     counter, prestatements, normalized_expression = normalize_expression(counter, statement.expression)
389     return (
390         counter,
391         prestatements,
392         NormalAssignmentStatement(
393             target=statement.target,
394             expression=normalized_expression,
395         ),
396     )
397
398 def normalize_statement(counter, statement):
399     return {
400         parsing.FurAssignmentStatement: normalize_assignment_statement,
401         parsing.FurExpressionStatement: normalize_expression_statement,
402         parsing.FurFunctionDefinitionStatement: normalize_function_definition_statement,
403     }[type(statement)](counter, statement)
404
405 @util.force_generator(tuple)
406 def normalize_statement_list(statement_list):
407     counter = 0
408
409     for statement in statement_list:
410         counter, prestatements, normalized = normalize_statement(counter, statement)
411         for s in prestatements:
412             yield s
413         yield normalized
414
415 def normalize(program):
416     return NormalProgram(
417         statement_list=normalize_statement_list(program.statement_list),
418     )