Add the ability to assign to and retrieve variables
[fur] / generation.py
1 import jinja2
2
3 import parsing
4 import transformation
5
6 ENV = jinja2.Environment(
7     autoescape=jinja2.select_autoescape([]),
8     loader=jinja2.FileSystemLoader('templates'),
9     trim_blocks=True,
10 )
11
12 def generate_integer_literal(c_integer_literal):
13     return 'integerLiteral({})'.format(c_integer_literal.value)
14
15 def generate_string_literal(c_string_literal):
16     def c_escape(ch):
17         return {
18             '\n': r'\n',
19             '"': r'\"',
20             '\\': r'\\',
21         }.get(ch, ch)
22
23     return 'stringLiteral(runtime, "{}")'.format(
24         ''.join(c_escape(ch for ch in c_string_literal.value)),
25     )
26
27 def generate_symbol_expression(c_symbol_expression):
28     return 'Environment_get(environment, Runtime_symbol(runtime, "{}"))'.format(c_symbol_expression.value)
29
30 def generate_expression(c_argument):
31     if isinstance(c_argument, transformation.CNegationExpression):
32         return generate_negation_expression(c_argument)
33
34     if isinstance(c_argument, transformation.CFunctionCallExpression):
35         return generate_function_call(c_argument)
36
37     LITERAL_TYPE_MAPPING = {
38         transformation.CIntegerLiteral: generate_integer_literal,
39         transformation.CStringLiteral: generate_string_literal,
40         transformation.CSymbolExpression: generate_symbol_expression,
41     }
42
43     if type(c_argument) in LITERAL_TYPE_MAPPING:
44         return LITERAL_TYPE_MAPPING[type(c_argument)](c_argument)
45
46     INFIX_TYPE_MAPPING = {
47         transformation.CAdditionExpression: 'add',
48         transformation.CSubtractionExpression: 'subtract',
49         transformation.CMultiplicationExpression: 'multiply',
50         transformation.CIntegerDivisionExpression: 'integerDivide',
51         transformation.CModularDivisionExpression: 'modularDivide',
52     }
53
54     return 'builtin${}({}, {})'.format(
55         INFIX_TYPE_MAPPING[type(c_argument)],
56         generate_expression(c_argument.left),
57         generate_expression(c_argument.right),
58     )
59
60 def generate_negation_expression(c_negation_expression):
61     return 'builtin$negate({})'.format(
62         generate_expression(c_negation_expression.value)
63     )
64
65 def generate_function_call(c_function_call):
66     return '{}({})'.format(
67         c_function_call.name,
68         ', '.join(generate_expression(argument) for argument in c_function_call.arguments),
69     )
70
71 def generate_expression_statement(c_function_call_statement):
72     # TODO Do we need to garbage collect the results of arbitrary statements?
73     return '{};'.format(generate_expression(c_function_call_statement))
74
75 def generate_assignment_statement(c_assignment_statement):
76     return 'Environment_set(environment, Runtime_symbol(runtime, "{}"), {});'.format(
77         c_assignment_statement.target,
78         generate_expression(c_assignment_statement.expression),
79     )
80
81 def generate_statement(statement):
82     if isinstance(statement, transformation.CAssignmentStatement):
83         return generate_assignment_statement(statement)
84
85     return generate_expression_statement(statement)
86
87 def generate(c_program):
88     template = ENV.get_template('program.c')
89     return template.render(
90         MAX_SYMBOL_LENGTH=parsing.MAX_SYMBOL_LENGTH,
91         builtins=list(sorted(c_program.builtins)),
92         statements=[generate_statement(statement) for statement in c_program.statements],
93         standard_libraries=list(sorted(c_program.standard_libraries)),
94     )
95
96 if __name__ == '__main__':
97     import unittest
98
99     unittest.main()