Add crossplatform IR generation for if/else statements
[fur] / crossplatform_ir_generation.py
1 import collections
2
3 import conversion
4
5 def flatten(xses):
6     return tuple(x for xs in xses for x in xs)
7
8 CIRProgram = collections.namedtuple(
9     'CIRProgram',
10     (
11         'entry_list',
12     ),
13 )
14
15 CIRLabel = collections.namedtuple(
16     'CIRLabel',
17     (
18         'label',
19     ),
20 )
21
22 CIRInstruction = collections.namedtuple(
23     'CIRInstruction',
24     (
25         'instruction',
26         'argument',
27     ),
28 )
29
30 def generate_integer_literal(integer):
31     return integer
32
33 def generate_string_literal(string):
34     return '"{}"'.format(string)
35
36 def generate_symbol_literal(symbol):
37     return 'sym({})'.format(symbol)
38
39 def generate_function_call_expression(expression):
40     return generate_expression(expression.function_expression) + (
41         CIRInstruction(
42             instruction='call',
43             argument=expression.argument_count,
44         ),
45     )
46
47 def generate_integer_literal_expression(expression):
48     return (CIRInstruction(
49         instruction='push_value',
50         argument=generate_integer_literal(expression.integer),
51     ),)
52
53 def generate_string_literal_expression(expression):
54     return (CIRInstruction(
55         instruction='push_value',
56         argument=generate_string_literal(expression.string),
57     ),)
58
59 def generate_symbol_expression(expression):
60     return (CIRInstruction(
61         instruction='push',
62         argument=generate_symbol_literal(expression.symbol),
63     ),)
64
65 def generate_variable_expression(expression):
66     return (CIRInstruction(
67         instruction='push',
68         argument=generate_symbol_literal(expression.variable),
69     ),)
70
71 def generate_expression(expression):
72     return {
73         conversion.CPSFunctionCallExpression: generate_function_call_expression,
74         conversion.CPSIntegerLiteralExpression: generate_integer_literal_expression,
75         conversion.CPSStringLiteralExpression: generate_string_literal_expression,
76         conversion.CPSSymbolExpression: generate_symbol_expression,
77         conversion.CPSVariableExpression: generate_variable_expression,
78     }[type(expression)](expression)
79
80 def generate_expression_statement(counters, statement):
81     return (
82         (),
83         generate_expression(statement.expression) + (
84             CIRInstruction(
85                 instruction='drop',
86                 argument=None,
87             ),
88         ),
89     )
90
91 def generate_if_else_statement(counters, statement):
92     if_counter = counters['if']
93     counters['if'] += 1
94
95     referenced_entry_list_list = []
96
97     if_instruction_list_list = []
98     for if_statement in statement.if_statement_list:
99         referenced_entry_list, instruction_list = generate_statement(counters, if_statement)
100         referenced_entry_list_list.append(referenced_entry_list)
101         if_instruction_list_list.append(instruction_list)
102
103     else_instruction_list_list = []
104
105     for else_statement in statement.else_statement_list:
106         referenced_entry_list, instruction_list = generate_statement(counters, else_statement)
107         referenced_entry_list_list.append(referenced_entry_list)
108         else_instruction_list_list.append(instruction_list)
109
110     if_label = '__if${}__'.format(if_counter)
111     else_label = '__else${}__'.format(if_counter)
112     endif_label = '__endif${}__'.format(if_counter)
113
114     return (
115         referenced_entry_list_list,
116         generate_expression(statement.condition_expression) + (
117             CIRInstruction(
118                 instruction='jump_if_false',
119                 argument=else_label,
120             ),
121             CIRLabel(label=if_label),
122         ) + flatten(if_instruction_list_list) + (
123             CIRInstruction(
124                 instruction='jump',
125                 argument=endif_label,
126             ),
127             CIRLabel(label=else_label),
128         ) + flatten(else_instruction_list_list) + (
129             CIRLabel(label=endif_label),
130         ),
131     )
132
133 def generate_assignment_statement(counters, statement):
134     return (
135         (),
136         generate_expression(statement.expression) + (
137             CIRInstruction(
138                 instruction='pop',
139                 argument=generate_symbol_literal(statement.target),
140             ),
141         ),
142     )
143
144 def generate_push_statement(counters, statement):
145     return (
146         (),
147         generate_expression(statement.expression),
148     )
149
150 def generate_variable_initialization_statement(counters, statement):
151     return (
152         (),
153         generate_expression(statement.expression) + (
154             CIRInstruction(
155                 instruction='pop',
156                 argument=generate_symbol_literal(statement.variable),
157             ),
158         ),
159     )
160
161 def generate_variable_reassignment_statement(counter, statement):
162     return (
163         (),
164         generate_expression(statement.expression) + (
165             CIRInstruction(
166                 instruction='pop',
167                 argument=generate_symbol_literal(statement.variable),
168             ),
169         ),
170     )
171
172 def generate_statement(counters, statement):
173     return {
174         conversion.CPSAssignmentStatement: generate_assignment_statement,
175         conversion.CPSExpressionStatement: generate_expression_statement,
176         conversion.CPSIfElseStatement: generate_if_else_statement,
177         conversion.CPSPushStatement: generate_push_statement,
178         conversion.CPSVariableInitializationStatement: generate_variable_initialization_statement,
179         conversion.CPSVariableReassignmentStatement: generate_variable_reassignment_statement,
180     }[type(statement)](counters, statement)
181
182 def generate(converted):
183     referenced_entry_list_list = []
184     instruction_list_list = []
185     counters = {
186         'if': 0,
187     }
188
189     for statement in converted.statement_list:
190         referenced_entry_list, instruction_list = generate_statement(counters, statement)
191         referenced_entry_list_list.append(referenced_entry_list)
192         instruction_list_list.append(instruction_list)
193
194     return CIRProgram(
195         entry_list=(
196             CIRLabel(label='__main__'),
197         ) + tuple(
198             referenced_entry
199             for referenced_entry_list in referenced_entry_list_list
200             for referenced_entry in referenced_entry_list
201         ) + tuple(
202             instruction
203             for instruction_list in instruction_list_list
204             for instruction in instruction_list
205         ),
206     )
207
208 def output(program):
209     lines = []
210
211     for entry in program.entry_list:
212         if isinstance(entry, CIRInstruction):
213             lines.append('    {} {}'.format(entry.instruction, entry.argument))
214
215         if isinstance(entry, CIRLabel):
216             lines.append('\n{}:'.format(entry.label))
217
218     return '\n'.join(lines).lstrip()