Basic empty program returning 0
[sandbox] / stutter.py
1 #!/usr/bin/env python
2
3 '''
4 To run this file:
5
6     python stutter.py stutter_code.stt > c_code.c
7 '''
8
9 import re
10 import string
11
12 # Utility functions
13
14 def is_integer(s_expression):
15     return isinstance(s_expression, int) \
16         and not s_expression is True \
17         and not s_expression is False
18
19 # String to s-expressions
20
21 TOKEN = re.compile(r'\s*({})'.format('|'.join('(?P<{}>{})'.format(*token) for token in [
22     ('open_parenthese',         r'\('),
23     ('close_parenthese',        r'\)'),
24     ('integer_literal',         r'\d+'),
25     ('unexpected_character',    r'.'),
26 ])))
27
28 def parse_all(source):
29     stack = []
30     items = []
31
32     for token in TOKEN.finditer(source):
33         if token.group('open_parenthese'):
34             stack.append(items)
35             items = []
36
37         elif token.group('close_parenthese'):
38             if len(stack) == 0:
39                 raise Exception('Parenthese closed but not opened')
40
41             stack[-1].append(tuple(items))
42             items = stack.pop()
43
44         elif token.group('integer_literal'):
45             items.append(int(token.group('integer_literal')))
46
47         elif token.group('unexpected_character'):
48             raise Exception('Unexpected character {}'.format(
49                 token.group('unexpected_character'),
50             ))
51
52         else:
53             raise Exception()
54
55
56     if len(stack) > 0:
57         raise Exception('Parenthese opened but not closed')
58
59     return items
60
61 # C AST Objects
62
63 class CType(object):
64     def __init__(self, name):
65         self.name = name
66
67 class CPointerType(CType):
68     def __init__(self, pointer_to):
69         self.pointer_to = pointer_to
70
71 class CArgumentDeclaration(object):
72     def __init__(self, type, name):
73         assert isinstance(type, CType)
74         self.type = type
75         self.name = name
76
77 class CExpression(object):
78     pass
79
80 class CIntegerLiteralExpression(CExpression):
81     def __init__(self, integer):
82         assert isinstance(integer, int)
83
84         # Booleans in Python are integers but we don't want them
85         assert not integer is True
86         assert not integer is False
87
88         self.integer = integer
89
90 class CFunctionCallExpression(CExpression):
91     def __init__(self, name, arguments):
92         assert all(isinstance(argument, CExpression) for argument in arguments)
93         self.name = name
94         self.arguments = arguments
95
96 class CStatement(object):
97     pass
98
99 class CExpressionStatement(CStatement):
100     def __init__(self, expression):
101         self.expression = expression
102
103 class CReturnStatement(CStatement):
104     def __init__(self, expression):
105         self.expression = expression
106
107 class CFunctionDeclaration(object):
108     def __init__(self, return_type, name, argument_declaration_list, body):
109         assert isinstance(return_type, CType)
110         assert isinstance(argument_declaration_list, list)
111         assert all(isinstance(ad, CArgumentDeclaration) for ad in argument_declaration_list)
112         assert isinstance(body, list)
113         assert all(isinstance(s, CStatement) for s in body)
114
115         self.return_type = return_type
116         self.name = name
117         self.argument_declaration_list = argument_declaration_list
118         self.body = body
119
120 # BEGIN S-expression to C AST layer
121
122 def evaluate_to_c(s_expression):
123     if is_integer(s_expression):
124         return CIntegerLiteralExpression(s_expression)
125
126     raise Exception('Unable to evaluate expression {} to C'.format(s_expression))
127
128 def evaluate_all_to_c(s_expressions):
129     c_expressions = list(map(evaluate_to_c, s_expressions))
130     body = list(map(CExpressionStatement, c_expressions[:-1])) + [CReturnStatement(c_expressions[-1])]
131     
132     return CFunctionDeclaration(
133             CType('int'),
134             'main',
135             [
136                 CArgumentDeclaration(CType('int'), 'argc'),
137                 CArgumentDeclaration(CPointerType(CPointerType(CType('char'))), 'argv'),
138             ],
139             body,
140         )
141
142 # BEGIN C AST to C source layer
143
144 TAB_WIDTH = 2
145
146 def indent(string):
147     assert isinstance(string, str)
148
149     def indent_line(line):
150         line = line.rstrip()
151
152         if line == '':
153             return line
154
155         return ' ' * TAB_WIDTH + line
156
157     return '\n'.join(indent_line(line) for line in string.splitlines())
158
159 def generate_pointer_type(pointer_type):
160     assert isinstance(pointer_type, CPointerType)
161     return '{}*'.format(generate_type(pointer_type.pointer_to))
162
163 def generate_type(
164         type,
165         generate_pointer_type = generate_pointer_type):
166     assert isinstance(type, CType)
167
168     if isinstance(type, CPointerType):
169         return generate_pointer_type(type)
170
171     return type.name
172
173 def generate_argument_declaration(argument_declaration):
174     assert isinstance(argument_declaration, CArgumentDeclaration)
175     return '{} {}'.format(generate_type(argument_declaration.type), argument_declaration.name)
176
177 def generate_argument_declaration_list(argument_declarations):
178     return ', '.join(generate_argument_declaration(ad) for ad in argument_declarations)
179
180 def generate_integer_literal_expression(expression):
181     assert isinstance(expression, CIntegerLiteralExpression)
182     return str(expression.integer)
183
184 def generate_function_call_expression(expression):
185     assert isinstance(expression, CFunctionCallExpression)
186     return '{}({})'.format(
187         expression.name,
188         ', '.join(generate_expression(e) for e in expression.arguments),
189     )
190
191 def generate_expression(
192         expression,
193         generate_integer_literal_expression = generate_integer_literal_expression,
194         generate_function_call_expression = generate_function_call_expression,
195         ):
196
197     if isinstance(expression, CIntegerLiteralExpression):
198         return generate_integer_literal_expression(expression)
199
200     if isinstance(expression, CFunctionCallExpression):
201         return generate_function_call_expression(expression)
202
203     raise Exception('Expression type {} not implemented'.format(type(expression)))
204
205 def generate_expression_statement(statement):
206     return '{};'.format(generate_expression(statement.expression))
207
208 def generate_return_statement(statement):
209     return 'return {};'.format(generate_expression(statement.expression))
210
211 def generate_statement(
212         statement,
213         generate_expression_statement = generate_expression_statement,
214         generate_return_statement = generate_return_statement):
215
216     if isinstance(statement, CExpressionStatement):
217         return generate_expression_statement(statement)
218
219     if isinstance(statement, CReturnStatement):
220         return generate_return_statement(statement)
221
222     raise Exception('Handling for statements of type {} not implemented'.format(type(statement.type)))
223
224 def generate_statement_list(statements):
225     assert all(isinstance(s, CStatement) for s in statements)
226     return '\n'.join(generate_statement(s) for s in statements)
227
228 FUNCTION_DEFINITION_TEMPLATE = string.Template(
229 '''
230 $return_type $name($argument_declaration_list)
231 {
232 $body
233 }
234 '''.strip())
235
236 def generate_function_declaration(function_declaration):
237     assert isinstance(function_declaration, CFunctionDeclaration)
238     return FUNCTION_DEFINITION_TEMPLATE.substitute(
239         return_type = generate_type(function_declaration.return_type),
240         name = function_declaration.name,
241         argument_declaration_list = generate_argument_declaration_list(function_declaration.argument_declaration_list),
242         body = indent(generate_statement_list(function_declaration.body)),
243     )
244
245 if __name__ == '__main__':
246     import sys
247     source_file_name = sys.argv[1]
248
249     with open(source_file_name, 'r') as source_file:
250         source = source_file.read()
251
252     result = generate_function_declaration(evaluate_all_to_c(parse_all(source)))
253     print(result)