Combined accumulators into one namedtuple
[fur] / transformation.py
1 import collections
2
3 import parsing
4
5 CIntegerLiteral = collections.namedtuple(
6     'CIntegerLiteral',
7     [
8         'value',
9     ],
10 )
11
12 CStringLiteral = collections.namedtuple(
13     'CStringLiteral',
14     [
15         'value',
16     ],
17 )
18
19 CConstantExpression = collections.namedtuple(
20     'CConstantExpression',
21     [
22         'value'
23     ],
24 )
25
26 CSymbolExpression = collections.namedtuple(
27     'CSymbolExpression',
28     [
29         'symbol',
30         'symbol_list_index',
31     ],
32 )
33
34 CNegationExpression = collections.namedtuple(
35     'CNegationExpression',
36     [
37         'value',
38     ],
39 )
40
41 CFunctionCallForFurInfixOperator = collections.namedtuple(
42     'CFunctionCallForFurInfixOperator',
43     [
44         'name',
45         'left',
46         'right',
47     ],
48 )
49
50 CFunctionCallExpression = collections.namedtuple(
51     'CFunctionCallExpression',
52     [
53         'name',
54         'arguments',
55     ],
56 )
57
58 CAssignmentStatement = collections.namedtuple(
59     'CAssignmentStatement',
60     [
61         'target',
62         'target_symbol_list_index',
63         'expression',
64     ],
65 )
66
67 CProgram = collections.namedtuple(
68     'CProgram',
69     [
70         'builtins',
71         'statements',
72         'standard_libraries',
73         'symbol_list',
74     ],
75 )
76
77 EQUALITY_LEVEL_OPERATOR_TO_FUNCTION_NAME_MAPPING = {
78     '==':   'equals',
79     '!=':   'notEquals',
80     '<=':   'lessThanOrEqual',
81     '>=':   'greaterThanOrEqual',
82     '<':    'lessThan',
83     '>':    'greaterThan',
84 }
85
86 def transform_equality_level_expression(accumulators, expression):
87     # Transform expressions like 1 < 2 < 3 into expressions like 1 < 2 && 2 < 3
88     if isinstance(expression.left, parsing.FurInfixExpression) and expression.left.order == 'equality_level':
89         left = transform_equality_level_expression(
90             accumulators,
91             expression.left
92         )
93
94         middle = left.right
95
96         right = transform_expression(
97             accumulators,
98             expression.right,
99         )
100
101         # TODO Don't evaluate the middle expression twice
102         return CFunctionCallForFurInfixOperator(
103             name='and',
104             left=left,
105             right=CFunctionCallForFurInfixOperator(
106                 name=EQUALITY_LEVEL_OPERATOR_TO_FUNCTION_NAME_MAPPING[expression.operator],
107                 left=middle,
108                 right=right,
109             ),
110         )
111
112     return CFunctionCallForFurInfixOperator(
113         name=EQUALITY_LEVEL_OPERATOR_TO_FUNCTION_NAME_MAPPING[expression.operator],
114         left=transform_expression(accumulators, expression.left),
115         right=transform_expression(accumulators, expression.right),
116     )
117
118 BUILTINS = {
119     'false':    [],
120     'pow':      ['math.h'],
121     'print':    ['stdio.h'],
122     'true':     [],
123 }
124
125 def transform_expression(accumulators, expression):
126     if isinstance(expression, parsing.FurParenthesizedExpression):
127         # Parentheses can be removed because everything in the C output is explicitly parenthesized
128         return transform_expression(accumulators, expression.internal)
129
130     if isinstance(expression, parsing.FurNegationExpression):
131         return transform_negation_expression(accumulators, expression)
132
133     if isinstance(expression, parsing.FurFunctionCallExpression):
134         return transform_function_call_expression(accumulators, expression)
135
136     if isinstance(expression, parsing.FurSymbolExpression):
137         if expression.value in ['true', 'false']:
138             return CConstantExpression(value=expression.value)
139
140         if expression.value not in accumulators.symbol_list:
141             symbol_list.append(expression.value)
142
143         return CSymbolExpression(
144             symbol=expression.value,
145             symbol_list_index=accumulators.symbol_list.index(expression.value),
146         )
147
148     LITERAL_TYPE_MAPPING = {
149         parsing.FurIntegerLiteralExpression: CIntegerLiteral,
150         parsing.FurStringLiteralExpression: CStringLiteral,
151     }
152
153     if type(expression) in LITERAL_TYPE_MAPPING:
154         return LITERAL_TYPE_MAPPING[type(expression)](value=expression.value)
155
156     if isinstance(expression, parsing.FurInfixExpression):
157         if expression.order == 'equality_level':
158             return transform_equality_level_expression(accumulators, expression)
159
160         INFIX_OPERATOR_TO_FUNCTION_NAME = {
161             '+':    'add',
162             '-':    'subtract',
163             '*':    'multiply',
164             '//':   'integerDivide',
165             '%':    'modularDivide',
166             'and':  'and',
167             'or':   'or',
168         }
169
170         return CFunctionCallForFurInfixOperator(
171             name=INFIX_OPERATOR_TO_FUNCTION_NAME[expression.operator],
172             left=transform_expression(accumulators, expression.left),
173             right=transform_expression(accumulators, expression.right),
174         )
175
176     raise Exception('Could not transform expression "{}"'.format(expression))
177
178 def transform_assignment_statement(accumulators, assignment_statement):
179     # TODO Check that target is not a builtin
180     if assignment_statement.target not in accumulators.symbol_list:
181         accumulators.symbol_list.append(assignment_statement.target)
182
183     return CAssignmentStatement(
184         target=assignment_statement.target,
185         target_symbol_list_index=accumulators.symbol_list.index(assignment_statement.target),
186         expression=transform_expression(
187             accumulators,
188             assignment_statement.expression,
189         ),
190     )
191
192 def transform_negation_expression(accumulators, negation_expression):
193     return CNegationExpression(
194         value=transform_expression(accumulators, negation_expression.value),
195     )
196
197 def transform_function_call_expression(accumulators, function_call):
198     if function_call.function.value in BUILTINS.keys():
199         # TODO Check that the builtin is actually callable
200         accumulators.builtins.add(function_call.function.value)
201
202         return CFunctionCallExpression(
203             name='builtin$' + function_call.function.value,
204             arguments=tuple(
205                 transform_expression(accumulators, arg)
206                 for arg in function_call.arguments
207             ),
208         )
209
210     raise Exception()
211
212 def transform_statement(accumulators, statement):
213     return {
214         parsing.FurAssignmentStatement: transform_assignment_statement,
215         parsing.FurFunctionCallExpression: transform_function_call_expression,
216     }[type(statement)](accumulators, statement)
217
218
219 Accumulators = collections.namedtuple(
220     'Accumulators',
221     [
222         'builtins',
223         'symbol_list',
224     ],
225 )
226
227 def transform(program):
228     accumulators = Accumulators(
229         builtins=set(),
230         symbol_list = [],
231     )
232
233     c_statements = [
234         transform_statement(accumulators, statement) for statement in program.statement_list
235     ]
236
237     standard_libraries = set()
238     for builtin in accumulators.builtins:
239         for standard_library in BUILTINS[builtin]:
240             standard_libraries.add(standard_library)
241
242     return CProgram(
243         builtins=accumulators.builtins,
244         statements=c_statements,
245         standard_libraries=standard_libraries,
246         symbol_list=accumulators.symbol_list,
247     )
248
249
250 if __name__ == '__main__':
251     import unittest
252
253     unittest.main()