Use order instead of operator to normalize infix expressions
[fur] / normalization.py
1 import collections
2
3 import parsing
4
5 NormalVariableExpression = collections.namedtuple(
6     'NormalVariableExpression',
7     [
8         'variable',
9     ],
10 )
11
12 NormalInfixExpression = collections.namedtuple(
13     'NormalInfixExpression',
14     [
15         'order',
16         'operator',
17         'left',
18         'right',
19     ],
20 )
21
22 NormalFunctionCallExpression = collections.namedtuple(
23     'NormalFunctionCallExpression',
24     [
25         'function',
26         'arguments',
27     ],
28 )
29
30 NormalVariableAssignmentStatement = collections.namedtuple(
31     'NormalVariableAssignmentStatement',
32     [
33         'variable',
34         'expression',
35     ],
36 )
37
38 NormalExpressionStatement = collections.namedtuple(
39     'NormalExpressionStatement',
40     [
41         'expression',
42     ],
43 )
44
45 NormalProgram = collections.namedtuple(
46     'NormalProgram',
47     [
48         'statement_list',
49     ],
50 )
51
52 def fake_normalization(counter, thing):
53     return (counter, (), thing)
54
55 def normalize_function_call_expression(counter, expression):
56     assert isinstance(expression, parsing.FurFunctionCallExpression)
57
58     prestatements = []
59     arguments = []
60
61     for argument in expression.arguments:
62         counter, argument_prestatements, normalized_argument = normalize_expression(counter, argument)
63
64         for s in argument_prestatements:
65             prestatements.append(s)
66
67         variable = '${}'.format(counter)
68         prestatements.append(NormalVariableAssignmentStatement(
69             variable=variable,
70             expression=normalized_argument,
71         ))
72         arguments.append(NormalVariableExpression(
73             variable=variable,
74         ))
75         counter += 1
76
77     return (
78         counter,
79         tuple(prestatements),
80         NormalFunctionCallExpression(
81             expression.function, # TODO Normalize the function
82             arguments=tuple(arguments),
83         ),
84     )
85
86 def normalize_basic_infix_operation(counter, expression):
87     counter, left_prestatements, left_expression = normalize_expression(counter, expression.left)
88     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
89
90     left_variable = '${}'.format(counter)
91     counter += 1
92     right_variable = '${}'.format(counter)
93     counter += 1
94
95     root_prestatements = (
96         NormalVariableAssignmentStatement(
97             variable=left_variable,
98             expression=left_expression,
99         ),
100         NormalVariableAssignmentStatement(
101             variable=right_variable,
102             expression=right_expression,
103         ),
104     )
105
106     return (
107         counter,
108         left_prestatements + right_prestatements + root_prestatements,
109         NormalInfixExpression(
110             order=expression.order, # TODO Do we need this?
111             operator=expression.operator,
112             left=NormalVariableExpression(variable=left_variable),
113             right=NormalVariableExpression(variable=right_variable),
114         ),
115     )
116
117 def normalize_infix_expression(counter, expression):
118     # TODO Unfake this normalization
119     return {
120         'multiplication_level': normalize_basic_infix_operation,
121         'addition_level': normalize_basic_infix_operation,
122         'comparison_level': fake_normalization,
123         'and_level': fake_normalization,
124         'or_level': fake_normalization,
125     }[expression.order](counter, expression)
126
127 def normalize_expression(counter, expression):
128     return {
129         parsing.FurFunctionCallExpression: normalize_function_call_expression,
130         parsing.FurInfixExpression: normalize_infix_expression,
131         parsing.FurIntegerLiteralExpression: fake_normalization,
132         parsing.FurNegationExpression: fake_normalization, # TODO Don't fake this
133         parsing.FurParenthesizedExpression: fake_normalization, # TODO Don't fake this
134         parsing.FurStringLiteralExpression: fake_normalization,
135         parsing.FurSymbolExpression: fake_normalization,
136     }[type(expression)](counter, expression)
137
138 def normalize_expression_statement(counter, statement):
139     counter, prestatements, normalized = {
140         parsing.FurFunctionCallExpression: normalize_function_call_expression,
141     }[type(statement.expression)](counter, statement.expression)
142
143     return (
144         counter,
145         prestatements,
146         NormalExpressionStatement(expression=normalized),
147     )
148
149 def normalize_statement(counter, statement):
150     return {
151         parsing.FurExpressionStatement: normalize_expression_statement,
152         parsing.FurAssignmentStatement: fake_normalization,
153     }[type(statement)](counter, statement)
154
155 def normalize(program):
156     counter = 0
157     statement_list = []
158
159     for statement in program.statement_list:
160         counter, prestatements, normalized = normalize_statement(counter, statement)
161         for s in prestatements:
162             statement_list.append(s)
163         statement_list.append(normalized)
164
165     return NormalProgram(
166         statement_list=statement_list,
167     )