Just pass through the internals of parentheses, unwrapped
[fur] / parsing.py
1 import collections
2
3 def consume_newlines(index, tokens):
4     while index < len(tokens) and tokens[index].type == 'newline':
5         index += 1
6
7     return True, index, None
8
9 def _or_parser(*parsers):
10     def result_parser(index, tokens):
11         failure = (False, index, None)
12
13         for parser in parsers:
14             success, index, value = parser(index, tokens)
15
16             if success:
17                 return (success, index, value)
18
19         return failure
20
21     return result_parser
22
23 def _zero_or_more_parser(formatter, parser):
24     def result_parser(index, tokens):
25         values = []
26
27         while index < len(tokens):
28             success, index, value = parser(index, tokens)
29
30             if success:
31                 values.append(value)
32             else:
33                 break
34
35         return (True, index, formatter(values))
36
37     return result_parser
38
39 FurIntegerLiteralExpression = collections.namedtuple(
40     'FurIntegerLiteralExpression',
41     [
42         'value',
43     ],
44 )
45
46 FurStringLiteralExpression = collections.namedtuple(
47     'FurStringLiteralExpression',
48     [
49         'value',
50     ],
51 )
52
53 FurSymbolExpression = collections.namedtuple(
54     'FurSymbolExpression',
55     [
56         'value',
57     ],
58 )
59
60 FurNegationExpression = collections.namedtuple(
61     'FurNegationExpression',
62     [
63         'value',
64     ],
65 )
66
67 FurInfixExpression = collections.namedtuple(
68     'FurInfixExpression',
69     [
70         'order',
71         'operator',
72         'left',
73         'right',
74     ],
75 )
76
77 def _integer_literal_expression_parser(index, tokens):
78     failure = (False, index, None)
79
80     if tokens[index].type != 'integer_literal':
81         return failure
82     value = int(tokens[index].match)
83     index += 1
84
85     return True, index, FurIntegerLiteralExpression(value=value)
86
87 def _string_literal_expression_parser(index, tokens):
88     if tokens[index].type == 'single_quoted_string_literal':
89         return (True, index + 1, FurStringLiteralExpression(value=tokens[index].match[1:-1]))
90
91     return (False, index, None)
92
93 def _symbol_expression_parser(index, tokens):
94     if tokens[index].type == 'symbol':
95         return (True, index + 1, FurSymbolExpression(value=tokens[index].match))
96
97     return (False, index, None)
98
99 def _parenthesized_expression_parser(index, tokens):
100     failure = (False, index, None)
101
102     if tokens[index].type == 'open_parenthese':
103         index += 1
104     else:
105         return failure
106
107     success, index, internal = _expression_parser(index, tokens)
108     if not success:
109         return failure
110
111     if tokens[index].type == 'close_parenthese':
112         index += 1
113     else:
114         raise Exception('Expected ")" on line {}, found "{}"'.format(
115             tokens[index].line,
116             tokens[index].match,
117         ))
118
119     return True, index, internal
120
121 def _negation_expression_parser(index, tokens):
122     failure = (False, index, None)
123
124     if tokens[index].match != '-':
125         return failure
126
127     success, index, value = _literal_level_expression_parser(index + 1, tokens)
128
129     if not success:
130         return failure
131
132     return (True, index, FurNegationExpression(value=value))
133
134 def _literal_level_expression_parser(index, tokens):
135     return _or_parser(
136         _negation_expression_parser,
137         _function_call_expression_parser,
138         _parenthesized_expression_parser,
139         _integer_literal_expression_parser,
140         _string_literal_expression_parser,
141         _symbol_expression_parser,
142     )(index, tokens)
143
144 def _left_recursive_infix_operator_parser(operator_token_matcher, operand_parser, order):
145     def result_parser(index, tokens):
146         failure = (False, index, None)
147
148         success, index, result = operand_parser(index, tokens)
149
150         if not success:
151             return failure
152
153         while success and index < len(tokens) and operator_token_matcher(tokens[index]):
154             success = False
155
156             if index + 1 < len(tokens):
157                 success, try_index, value = operand_parser(index + 1, tokens)
158
159             if success:
160                 result = FurInfixExpression(
161                     order=order,
162                     operator=tokens[index].match,
163                     left=result,
164                     right=value,
165                 )
166                 index = try_index
167
168         return True, index, result
169
170     return result_parser
171
172 def _multiplication_level_expression_parser(index, tokens):
173     return _left_recursive_infix_operator_parser(
174         lambda token: token.type == 'multiplication_level_operator',
175         _literal_level_expression_parser,
176         'multiplication_level',
177     )(index, tokens)
178
179 def _addition_level_expression_parser(index, tokens):
180     return _left_recursive_infix_operator_parser(
181         lambda token: token.type == 'addition_level_operator',
182         _multiplication_level_expression_parser,
183         'addition_level',
184     )(index, tokens)
185
186 def _comparison_level_expression_parser(index, tokens):
187     return _left_recursive_infix_operator_parser(
188         lambda token: token.type == 'comparison_level_operator',
189         _addition_level_expression_parser,
190         'comparison_level',
191     )(index, tokens)
192
193 def _and_level_expression_parser(index, tokens):
194     return _left_recursive_infix_operator_parser(
195         lambda token: token.type == 'symbol' and token.match == 'and',
196         _comparison_level_expression_parser,
197         'and_level',
198     )(index, tokens)
199
200 def _or_level_expression_parser(index, tokens):
201     return _left_recursive_infix_operator_parser(
202         lambda token: token.type == 'symbol' and token.match == 'or',
203         _and_level_expression_parser,
204         'or_level',
205     )(index, tokens)
206
207 def _comma_separated_list_parser(subparser):
208     def result_parser(index, tokens):
209         start_index = index
210
211         items = []
212
213         success, index, item = subparser(index, tokens)
214
215         if success:
216             items.append(item)
217         else:
218             return (True, start_index, ())
219
220         while success and index < len(tokens) and tokens[index].type == 'comma':
221             success = False
222
223             if index + 1 < len(tokens):
224                 success, try_index, item = subparser(index + 1, tokens)
225
226             if success:
227                 items.append(item)
228                 index = try_index
229
230         return True, index, tuple(items)
231
232     return result_parser
233
234 def _comma_separated_expression_list_parser(index, tokens):
235     return _comma_separated_list_parser(_expression_parser)(index, tokens)
236
237 FurFunctionCallExpression = collections.namedtuple(
238     'FurFunctionCallExpression',
239     [
240         'function',
241         'arguments',
242     ],
243 )
244
245 FurExpressionStatement = collections.namedtuple(
246     'FurExpressionStatement',
247     [
248         'expression',
249     ],
250 )
251
252 FurAssignmentStatement = collections.namedtuple(
253     'FurAssignmentStatement',
254     [
255         'target',
256         'expression',
257     ],
258 )
259
260 FurFunctionDefinitionStatement = collections.namedtuple(
261     'FurFunctionDefinitionStatement',
262     [
263         'name',
264         'argument_name_list',
265         'statement_list',
266     ],
267 )
268
269 FurProgram = collections.namedtuple(
270     'FurProgram',
271     [
272         'statement_list',
273     ],
274 )
275
276 def _function_call_expression_parser(index, tokens):
277     # TODO Use a FurSymbolExpression for the name
278     failure = (False, index, None)
279
280     success, index, function = _symbol_expression_parser(index, tokens)
281
282     if not success:
283         return failure
284
285     if tokens[index].type != 'open_parenthese':
286         return failure
287     index += 1
288
289     success, index, arguments = _comma_separated_expression_list_parser(index, tokens)
290
291     if not success:
292         return failure
293
294     if tokens[index].type != 'close_parenthese':
295         raise Exception('Expected ")", found "{}" on line {}'.format(
296             tokens[index].match,
297             tokens[index].line,
298         ))
299     index += 1
300
301     return True, index, FurFunctionCallExpression(function=function, arguments=arguments)
302
303 _expression_parser = _or_level_expression_parser
304
305 def _expression_statement_parser(index, tokens):
306     failure = (False, index, None)
307
308     success, index, expression = _expression_parser(index, tokens)
309
310     if not success:
311         return failure
312
313     return (True, index, FurExpressionStatement(expression=expression))
314
315 def _assignment_statement_parser(index, tokens):
316     # TODO Use a FurSymbolExpression for the target? Maybe this is actually not a good idea
317     failure = (False, index, None)
318
319     if tokens[index].type != 'symbol':
320         return failure
321     target = tokens[index].match
322     index += 1
323
324     if tokens[index].type != 'assignment_operator':
325         return failure
326     assignment_operator_index = index
327
328     success, index, expression = _expression_parser(index + 1, tokens)
329
330     if not success:
331         raise Exception(
332             'Expected expression after assignment operator on line {}'.format(
333                 tokens[assignment_operator_index].line
334             )
335         )
336
337     return True, index, FurAssignmentStatement(target=target, expression=expression)
338
339 def _function_definition_statement_parser(index, tokens):
340     failure = (False, index, None)
341
342     if tokens[index].type == 'keyword' and tokens[index].match == 'def':
343         index += 1
344     else:
345         return failure
346
347     if tokens[index].type == 'symbol':
348         name = tokens[index].match
349         index += 1
350     else:
351         raise Exception('Expected function name, found "{}" on line {}'.format(
352             tokens[index].match,
353             tokens[index].line,
354         ))
355
356     if tokens[index].type == 'open_parenthese':
357         index += 1
358     else:
359         raise Exception('Expected "(", found "{}" on line {}'.format(
360             tokens[index].match,
361             tokens[index].line,
362         ))
363
364     success, index, argument_name_list = _comma_separated_list_parser(_symbol_expression_parser)(
365         index,
366         tokens,
367     )
368
369     if tokens[index].type == 'close_parenthese':
370         index += 1
371     else:
372         raise Exception('Expected ")", found "{}" on line {}'.format(
373             tokens[index].match,
374             tokens[index].line,
375         ))
376
377     if tokens[index].type == 'symbol' and tokens[index].match == 'do':
378         index += 1
379     else:
380         return failure
381
382     success, index, statement_list = _zero_or_more_parser(tuple, _statement_parser)(index, tokens)
383
384     _, index, _ = consume_newlines(index, tokens)
385
386     if tokens[index].type == 'keyword' and tokens[index].match == 'end':
387         index += 1
388     else:
389         return failure
390
391     return True, index, FurFunctionDefinitionStatement(
392         name=name,
393         argument_name_list=tuple(an.value for an in argument_name_list),
394         statement_list=statement_list,
395     )
396
397 def _statement_parser(index, tokens):
398     _, index, _ = consume_newlines(index, tokens)
399
400     if index == len(tokens):
401         return (False, index, None)
402
403     return _or_parser(
404         _assignment_statement_parser,
405         _expression_statement_parser,
406         _function_definition_statement_parser,
407     )(index, tokens)
408
409 def _program_formatter(statement_list):
410     return FurProgram(statement_list=statement_list)
411
412 _program_parser = _zero_or_more_parser(_program_formatter, _statement_parser)
413
414 def _parse(parser, tokens):
415     success, index, result = parser(0, tokens)
416
417     if index < len(tokens):
418         raise Exception('Unable to parse token {}'.format(tokens[index]))
419
420     if success:
421         return result
422
423     raise Exception('Unable to parse')
424
425 def parse(tokens):
426     return _parse(_program_parser, tokens)
427
428 if __name__ == '__main__':
429     import unittest
430
431     import tokenization
432
433     class FurStringLiteralExpressionParserTests(unittest.TestCase):
434         def test_parses_single_quoted_string_literal(self):
435             self.assertEqual(
436                 _string_literal_expression_parser(0, tokenization.tokenize("'Hello, world'")),
437                 (
438                     True,
439                     1,
440                     FurStringLiteralExpression(value='Hello, world'),
441                 ),
442             )
443
444     class FurFunctionCallExpressionParserTests(unittest.TestCase):
445         def test_parses_function_with_string_literal_argument(self):
446             self.assertEqual(
447                 _function_call_expression_parser(0, tokenization.tokenize("print('Hello, world')")),
448                 (
449                     True,
450                     4,
451                     FurFunctionCallExpression(
452                         name='print',
453                         arguments=(FurStringLiteralExpression(value='Hello, world'),),
454                     ),
455                 ),
456             )
457
458     unittest.main()