e8ee51c9bab31edc1a60c0d4dbb841d95baefa67
[fur] / transformation.py
1 import collections
2
3 import conversion
4
5 CIntegerLiteral = collections.namedtuple(
6     'CIntegerLiteral',
7     [
8         'value',
9     ],
10 )
11
12 CStringLiteral = collections.namedtuple(
13     'CStringLiteral',
14     [
15         'index',
16         'value',
17     ],
18 )
19
20 CVariableExpression = collections.namedtuple(
21     'CVariableExpression',
22     [
23         'variable',
24     ],
25 )
26
27 CSymbolExpression = collections.namedtuple(
28     'CSymbolExpression',
29     [
30         'symbol',
31         'symbol_list_index',
32     ],
33 )
34
35 CStructureLiteralExpression = collections.namedtuple(
36     'CStructureLiteralExpression',
37     [
38         'field_count',
39         'symbol_list_variable',
40         'value_list_variable',
41     ],
42 )
43
44 CPushStatement = collections.namedtuple(
45     'CPushStatement',
46     (
47         'expression',
48     ),
49 )
50
51 CFunctionCallExpression = collections.namedtuple(
52     'CFunctionCallExpression',
53     (
54         'metadata',
55         'function_expression',
56         'argument_count',
57     ),
58 )
59
60 # TODO We are currently not changing variables, just preventing them from being accessed.
61 CSymbolAssignmentStatement = collections.namedtuple(
62     'CSymbolAssignmentStatement',
63     [
64         'target',
65         'target_symbol_list_index',
66         'expression',
67     ],
68 )
69
70 CArrayVariableInitializationStatement = collections.namedtuple(
71     'CArrayVariableInitializationStatement',
72     [
73         'variable',
74         'items',
75     ],
76 )
77
78 CSymbolArrayVariableInitializationStatement = collections.namedtuple(
79     'CSymbolArrayVariableInitializationStatement',
80     [
81         'variable',
82         'symbol_list',
83         'symbol_list_indices',
84     ],
85 )
86
87 CVariableInitializationStatement = collections.namedtuple(
88     'CVariableInitializationStatement',
89     [
90         'variable',
91         'expression',
92     ],
93 )
94
95 CVariableReassignmentStatement = collections.namedtuple(
96     'CVariableReassignmentStatement',
97     [
98         'variable',
99         'expression',
100     ],
101 )
102
103 CExpressionStatement = collections.namedtuple(
104     'CExpressionStatement',
105     [
106         'expression',
107     ],
108 )
109
110 CIfElseStatement = collections.namedtuple(
111     'CIfElseStatement',
112     [
113         'condition_expression',
114         'if_statement_list',
115         'else_statement_list',
116     ],
117 )
118
119 CFunctionDeclaration = collections.namedtuple(
120     'CFunctionDeclaration',
121     [
122         'name',
123     ],
124 )
125
126 # TODO If a function definition doesn't end with an expression, we have issues currently because we try to return statement.
127 # TODO Closures currently wrap entire defining environment, even symbols that are not used, which makes garbage collection ineffective.
128 CFunctionDefinition = collections.namedtuple(
129     'CFunctionDefinition',
130     [
131         'name',
132         'argument_name_list',
133         'statement_list',
134     ],
135 )
136
137 CProgram = collections.namedtuple(
138     'CProgram',
139     [
140         'builtin_set',
141         'function_definition_list',
142         'operator_declarations',
143         'statements',
144         'standard_libraries',
145         'string_literal_list',
146         'symbol_list',
147     ],
148 )
149
150 BUILTINS = {
151     'concatenate':      [],
152     'false':            [],
153     'pow':              ['math.h'],
154     'print':            ['stdio.h'],
155     'true':             [],
156 }
157
158 def transform_variable_expression(accumulators, expression):
159     assert isinstance(expression, conversion.CPSVariableExpression)
160     return CVariableExpression(variable=expression.variable)
161
162 def transform_string_literal_expression(accumulators, expression):
163     value = expression.string
164
165     try:
166         index = accumulators.string_literal_list.index(value)
167     except ValueError:
168         index = len(accumulators.string_literal_list)
169         accumulators.string_literal_list.append(value)
170
171     return CStringLiteral(index=index, value=value)
172
173 def transform_symbol_expression(accumulators, expression):
174     if expression.symbol in BUILTINS:
175         accumulators.builtin_set.add(expression.symbol)
176
177     try:
178         symbol_list_index = accumulators.symbol_list.index(expression.symbol)
179     except ValueError:
180         symbol_list_index = len(accumulators.symbol_list)
181         accumulators.symbol_list.append(expression.symbol)
182
183     return CSymbolExpression(
184         symbol=expression.symbol,
185         symbol_list_index=symbol_list_index,
186     )
187
188 def transform_integer_literal_expression(accumulators, expression):
189     return CIntegerLiteral(value=expression.integer)
190
191 CListConstructExpression = collections.namedtuple(
192     'CListConstructExpression',
193     (
194         'allocate',
195     ),
196 )
197
198 CLambdaExpression = collections.namedtuple(
199     'CLambdaExpression',
200     (
201         'name',
202     ),
203 )
204
205 CListAppendStatement = collections.namedtuple(
206     'CListAppendStatement',
207     (
208         'list_expression',
209         'item_expression',
210     ),
211 )
212
213 def transform_structure_literal_expression(accumulators, expression):
214     return CStructureLiteralExpression(
215         field_count=expression.field_count,
216         symbol_list_variable=expression.symbol_list_variable,
217         value_list_variable=expression.value_list_variable,
218     )
219
220 def transform_lambda_expression(accumulators, expression):
221     # TODO This function feels hacky
222     if len(accumulators.lambda_number_list) == 0:
223         accumulators.lambda_number_list.append(0)
224     else:
225         accumulators.lambda_number_list.append(accumulators.lambda_number_list[-1] + 1)
226
227     name = '__lambda_{}'.format(accumulators.lambda_number_list[-1])
228
229     accumulators.function_definition_list.append(CFunctionDefinition(
230         name=name,
231         argument_name_list=expression.argument_name_list,
232         statement_list=tuple(transform_statement(accumulators, s) for s in expression.statement_list),
233     ))
234
235     return CLambdaExpression(name=name)
236
237
238 def transform_list_construct_expression(accumulators, expression):
239     return CListConstructExpression(allocate=expression.allocate)
240
241 def transform_list_append_statement(accumulators, expression):
242     return CListAppendStatement(
243         list_expression=transform_expression(accumulators, expression.list_expression),
244         item_expression=transform_expression(accumulators, expression.item_expression),
245     )
246
247 def transform_expression(accumulators, expression):
248     return {
249         conversion.CPSFunctionCallExpression: transform_function_call_expression,
250         conversion.CPSIntegerLiteralExpression: transform_integer_literal_expression,
251         conversion.CPSLambdaExpression: transform_lambda_expression,
252         conversion.CPSListConstructExpression: transform_list_construct_expression,
253         conversion.CPSStructureLiteralExpression: transform_structure_literal_expression,
254         conversion.CPSStringLiteralExpression: transform_string_literal_expression,
255         conversion.CPSSymbolExpression: transform_symbol_expression,
256         conversion.CPSVariableExpression: transform_variable_expression,
257     }[type(expression)](accumulators, expression)
258
259 def transform_symbol_assignment_statement(accumulators, assignment_statement):
260     # TODO Check that target is not a builtin
261     try:
262         symbol_list_index = accumulators.symbol_list.index(assignment_statement.target)
263     except ValueError:
264         symbol_list_index = len(accumulators.symbol_list)
265         accumulators.symbol_list.append(assignment_statement.target)
266
267     return CSymbolAssignmentStatement(
268         target=assignment_statement.target,
269         target_symbol_list_index=symbol_list_index,
270         expression=transform_expression(
271             accumulators,
272             assignment_statement.expression,
273         ),
274     )
275
276 def transform_function_call_expression(accumulators, function_call):
277     # TODO Use the symbol from SYMBOL LIST
278     return CFunctionCallExpression(
279         metadata=function_call.metadata,
280         function_expression=transform_expression(accumulators, function_call.function_expression),
281         argument_count=function_call.argument_count,
282     )
283
284 def transform_expression_statement(accumulators, statement):
285     return CExpressionStatement(
286         expression=transform_expression(accumulators, statement.expression),
287     )
288
289 def transform_if_else_statement(accumulators, statement):
290     return CIfElseStatement(
291         condition_expression=transform_expression(accumulators, statement.condition_expression),
292         if_statement_list=tuple(transform_statement(accumulators, s) for s in statement.if_statement_list),
293         else_statement_list=tuple(transform_statement(accumulators, s) for s in statement.else_statement_list),
294     )
295
296 def transform_array_variable_initialization_statement(accumulators, statement):
297     return CArrayVariableInitializationStatement(
298         variable=statement.variable,
299         items=tuple(transform_expression(accumulators, i) for i in statement.items),
300     )
301
302 def transform_symbol_array_variable_initialization_statement(accumulators, statement):
303     symbol_list_indices = []
304
305     for symbol in statement.symbol_list:
306         try:
307             symbol_list_index = accumulators.symbol_list.index(symbol)
308         except ValueError:
309             symbol_list_index = len(accumulators.symbol_list)
310             accumulators.symbol_list.append(symbol)
311
312         symbol_list_indices.append(symbol_list_index)
313
314     return CSymbolArrayVariableInitializationStatement(
315         variable=statement.variable,
316         symbol_list=statement.symbol_list,
317         symbol_list_indices=tuple(symbol_list_indices),
318     )
319
320 def transform_variable_initialization_statement(accumulators, statement):
321     return CVariableInitializationStatement(
322         variable=statement.variable,
323         expression=transform_expression(accumulators, statement.expression),
324     )
325
326 def transform_variable_reassignment_statement(accumulators, statement):
327     return CVariableReassignmentStatement(
328         variable=statement.variable,
329         expression=transform_expression(accumulators, statement.expression),
330     )
331
332 def transform_function_definition_statement(accumulators, statement):
333     # TODO Allow defining the same function in different contexts
334     if any(fd.name == statement.name for fd in accumulators.function_definition_list):
335         raise Exception('A function with name "{}" already exists'.format(statement.name))
336
337     # TODO Add argument names to the symbol table
338     accumulators.function_definition_list.append(CFunctionDefinition(
339         name=statement.name,
340         argument_name_list=statement.argument_name_list,
341         statement_list=tuple(transform_statement(accumulators, s) for s in statement.statement_list)
342     ))
343
344     return CFunctionDeclaration(name=statement.name)
345
346 def transform_push_statement(accumulators, statement):
347     return CPushStatement(expression=transform_expression(accumulators, statement.expression))
348
349 def transform_statement(accumulators, statement):
350     return {
351         conversion.CPSArrayVariableInitializationStatement: transform_array_variable_initialization_statement,
352         conversion.CPSAssignmentStatement: transform_symbol_assignment_statement,
353         conversion.CPSExpressionStatement: transform_expression_statement,
354         conversion.CPSFunctionDefinitionStatement: transform_function_definition_statement,
355         conversion.CPSIfElseStatement: transform_if_else_statement,
356         conversion.CPSListAppendStatement: transform_list_append_statement,
357         conversion.CPSPushStatement: transform_push_statement,
358         conversion.CPSSymbolArrayVariableInitializationStatement: transform_symbol_array_variable_initialization_statement,
359         conversion.CPSVariableInitializationStatement: transform_variable_initialization_statement,
360         conversion.CPSVariableReassignmentStatement: transform_variable_reassignment_statement,
361     }[type(statement)](accumulators, statement)
362
363
364 Accumulators = collections.namedtuple(
365     'Accumulators',
366     [
367         'builtin_set',
368         'function_definition_list',
369         'lambda_number_list',
370         'operator_set',
371         'symbol_list',
372         'string_literal_list',
373     ],
374 )
375
376 def transform(program):
377     accumulators = Accumulators(
378         builtin_set=set(),
379         function_definition_list=[],
380         lambda_number_list=[],
381         operator_set=set(),
382         symbol_list=[],
383         string_literal_list=[],
384     )
385
386     statement_list = [
387         transform_statement(accumulators, statement) for statement in program.statement_list
388     ]
389
390     standard_library_set = set()
391     for builtin in accumulators.builtin_set:
392         for standard_library in BUILTINS[builtin]:
393             standard_library_set.add(standard_library)
394
395     return CProgram(
396         builtin_set=accumulators.builtin_set,
397         function_definition_list=accumulators.function_definition_list,
398         operator_declarations=tuple(sorted(accumulators.operator_set)),
399         statements=statement_list,
400         standard_libraries=standard_library_set,
401         string_literal_list=accumulators.string_literal_list,
402         symbol_list=accumulators.symbol_list,
403     )
404
405
406 if __name__ == '__main__':
407     import unittest
408
409     unittest.main()