Add constant symbol list, which solves all the symbol allocation problems
[fur] / parsing.py
1 import collections
2
3 def _or_parser(*parsers):
4     def result_parser(index, tokens):
5         failure = (False, index, None)
6
7         for parser in parsers:
8             success, index, value = parser(index, tokens)
9
10             if success:
11                 return (success, index, value)
12
13         return failure
14
15     return result_parser
16
17 def _zero_or_more_parser(formatter, parser):
18     def result_parser(index, tokens):
19         values = []
20
21         while index < len(tokens):
22             success, index, value = parser(index, tokens)
23
24             if success:
25                 values.append(value)
26             else:
27                 break
28
29         return (True, index, formatter(values))
30
31     return result_parser
32
33 FurIntegerLiteralExpression = collections.namedtuple(
34     'FurIntegerLiteralExpression',
35     [
36         'value',
37     ],
38 )
39
40 FurStringLiteralExpression = collections.namedtuple(
41     'FurStringLiteralExpression',
42     [
43         'value',
44     ],
45 )
46
47 FurSymbolExpression = collections.namedtuple(
48     'FurSymbolExpression',
49     [
50         'value',
51     ],
52 )
53
54 FurNegationExpression = collections.namedtuple(
55     'FurNegationExpression',
56     [
57         'value',
58     ],
59 )
60
61 FurAdditionExpression = collections.namedtuple(
62     'FurAdditionExpression',
63     [
64         'left',
65         'right',
66     ],
67 )
68
69 FurSubtractionExpression = collections.namedtuple(
70     'FurSubtractionExpression',
71     [
72         'left',
73         'right',
74     ],
75 )
76
77 FurMultiplicationExpression = collections.namedtuple(
78     'FurMultiplicationExpression',
79     [
80         'left',
81         'right',
82     ],
83 )
84
85 FurIntegerDivisionExpression = collections.namedtuple(
86     'FurIntegerDivisionExpression',
87     [
88         'left',
89         'right',
90     ],
91 )
92
93 FurModularDivisionExpression = collections.namedtuple(
94     'FurModularDivisionExpression',
95     [
96         'left',
97         'right',
98     ],
99 )
100
101 def _integer_literal_expression_parser(index, tokens):
102     failure = (False, index, None)
103
104     if tokens[index].type != 'integer_literal':
105         return failure
106     value = int(tokens[index].match)
107     index += 1
108
109     return True, index, FurIntegerLiteralExpression(value=value)
110
111 def _string_literal_expression_parser(index, tokens):
112     if tokens[index].type == 'single_quoted_string_literal':
113         return (True, index + 1, FurStringLiteralExpression(value=tokens[index].match[1:-1]))
114
115     return (False, index, None)
116
117 def _symbol_expression_parser(index, tokens):
118     if tokens[index].type == 'symbol':
119         return (True, index + 1, FurSymbolExpression(value=tokens[index].match))
120
121     return (False, index, None)
122
123 def _negation_expression_parser(index, tokens):
124     failure = (False, index, None)
125
126     if tokens[index].match != '-':
127         return failure
128
129     success, index, value = _literal_level_expression_parser(index + 1, tokens)
130
131     if not success:
132         return failure
133
134     return (True, index, FurNegationExpression(value=value))
135
136 def _literal_level_expression_parser(index, tokens):
137     return _or_parser(
138         _negation_expression_parser,
139         _function_call_expression_parser,
140         _integer_literal_expression_parser,
141         _string_literal_expression_parser,
142         _symbol_expression_parser,
143     )(index, tokens)
144
145 def _multiplication_level_expression_parser(index, tokens):
146     failure = (False, index, None)
147
148     success, index, result = _literal_level_expression_parser(index, tokens)
149
150     if not success:
151         return failure
152
153     while success and index < len(tokens) and tokens[index].type == 'multiplication_level_operator':
154         success = False
155
156         if index + 1 < len(tokens):
157             success, try_index, value = _literal_level_expression_parser(index + 1, tokens)
158
159         if success:
160             result = {
161                 '*': FurMultiplicationExpression,
162                 '//': FurIntegerDivisionExpression,
163                 '%': FurModularDivisionExpression,
164             }[tokens[index].match](left=result, right=value)
165             index = try_index
166
167     return True, index, result
168
169 def _addition_level_expression_parser(index, tokens):
170     failure = (False, index, None)
171
172     success, index, result = _multiplication_level_expression_parser(index, tokens)
173
174     if not success:
175         return failure
176
177     while success and index < len(tokens) and tokens[index].type == 'addition_level_operator':
178         success = False
179
180         if index + 1 < len(tokens):
181             success, try_index, value = _multiplication_level_expression_parser(index + 1, tokens)
182
183         if success:
184             result = {
185                 '+': FurAdditionExpression,
186                 '-': FurSubtractionExpression,
187             }[tokens[index].match](left=result, right=value)
188             index = try_index
189
190     return True, index, result
191
192 def _comma_separated_list_parser(index, tokens):
193     failure = (False, index, None)
194
195     expressions = []
196
197     success, index, expression = _addition_level_expression_parser(index, tokens)
198
199     if success:
200         expressions.append(expression)
201     else:
202         return failure
203
204     while success and index < len(tokens) and tokens[index].type == 'comma':
205         success = False
206
207         if index + 1 < len(tokens):
208             success, try_index, expression = _addition_level_expression_parser(index + 1, tokens)
209
210         if success:
211             expressions.append(expression)
212             index = try_index
213
214     return True, index, tuple(expressions)
215
216
217 FurFunctionCallExpression = collections.namedtuple(
218     'FurFunctionCallExpression',
219     [
220         'function',
221         'arguments',
222     ],
223 )
224
225 FurAssignmentStatement = collections.namedtuple(
226     'FurAssignmentStatement',
227     [
228         'target',
229         'expression',
230     ],
231 )
232
233 FurProgram = collections.namedtuple(
234     'FurProgram',
235     [
236         'statement_list',
237     ],
238 )
239
240 def _function_call_expression_parser(index, tokens):
241     # TODO Use a FurSymbolExpression for the name
242     failure = (False, index, None)
243
244     success, index, function = _symbol_expression_parser(index, tokens)
245
246     if not success:
247         return failure
248
249     if tokens[index].type != 'open_parenthese':
250         return failure
251     index += 1
252
253     success, index, arguments = _comma_separated_list_parser(index, tokens)
254
255     if not success:
256         return failure
257
258     if tokens[index].type != 'close_parenthese':
259         raise Exception('Expected ")", found "{}" on line {}'.format(
260             tokens[index].match,
261             tokens[index].line,
262         ))
263     index += 1
264
265     return True, index, FurFunctionCallExpression(function=function, arguments=arguments)
266
267 _expression_parser = _multiplication_level_expression_parser
268
269 def _assignment_statement_parser(index, tokens):
270     # TODO Use a FurSymbolExpression for the target
271     failure = (False, index, None)
272
273     if tokens[index].type != 'symbol':
274         return failure
275     target = tokens[index].match
276     index += 1
277
278     if tokens[index].type != 'assignment_operator':
279         return failure
280     assignment_operator_index = index
281
282     success, index, expression = _expression_parser(index + 1, tokens)
283
284     if not success:
285         raise Exception(
286             'Expected expression after assignment operator on line {}'.format(
287                 tokens[assignment_operator_index].line
288             )
289         )
290
291     return True, index, FurAssignmentStatement(target=target, expression=expression)
292
293 def _statement_parser(index, tokens):
294     return _or_parser(
295         _assignment_statement_parser,
296         _expression_parser,
297     )(index, tokens)
298
299 def _program_formatter(statement_list):
300     return FurProgram(statement_list=statement_list)
301
302 _program_parser = _zero_or_more_parser(_program_formatter, _statement_parser)
303
304 def _parse(parser, tokens):
305     success, index, result = parser(0, tokens)
306
307     if index < len(tokens):
308         raise Exception('Unable to parse token {}'.format(tokens[index]))
309
310     if success:
311         return result
312
313     raise Exception('Unable to parse')
314
315 def parse(tokens):
316     return _parse(_program_parser, tokens)
317
318 if __name__ == '__main__':
319     import unittest
320
321     import tokenization
322
323     class FurStringLiteralExpressionParserTests(unittest.TestCase):
324         def test_parses_single_quoted_string_literal(self):
325             self.assertEqual(
326                 _string_literal_expression_parser(0, tokenization.tokenize("'Hello, world'")),
327                 (
328                     True,
329                     1,
330                     FurStringLiteralExpression(value='Hello, world'),
331                 ),
332             )
333
334     class FurFunctionCallExpressionParserTests(unittest.TestCase):
335         def test_parses_function_with_string_literal_argument(self):
336             self.assertEqual(
337                 _function_call_expression_parser(0, tokenization.tokenize("print('Hello, world')")),
338                 (
339                     True,
340                     4,
341                     FurFunctionCallExpression(
342                         name='print',
343                         arguments=(FurStringLiteralExpression(value='Hello, world'),),
344                     ),
345                 ),
346             )
347
348     unittest.main()