3 def consume_newlines(index, tokens):
4 while index < len(tokens) and tokens[index].type == 'newline':
7 return True, index, None
9 def _or_parser(*parsers):
10 def result_parser(index, tokens):
11 failure = (False, index, None)
13 for parser in parsers:
14 success, index, value = parser(index, tokens)
17 return (success, index, value)
23 def _zero_or_more_parser(formatter, parser):
24 def result_parser(index, tokens):
27 while index < len(tokens):
28 success, index, value = parser(index, tokens)
35 return (True, index, formatter(values))
39 FurIntegerLiteralExpression = collections.namedtuple(
40 'FurIntegerLiteralExpression',
46 FurStringLiteralExpression = collections.namedtuple(
47 'FurStringLiteralExpression',
53 FurSymbolExpression = collections.namedtuple(
54 'FurSymbolExpression',
60 FurNegationExpression = collections.namedtuple(
61 'FurNegationExpression',
67 FurInfixExpression = collections.namedtuple(
77 FurListLiteralExpression = collections.namedtuple(
78 'FurListLiteralExpression',
80 'item_expression_list',
84 FurIfExpression = collections.namedtuple(
87 'condition_expression',
89 'else_statement_list',
93 def _integer_literal_expression_parser(index, tokens):
94 failure = (False, index, None)
96 if tokens[index].type != 'integer_literal':
98 value = int(tokens[index].match)
101 return True, index, FurIntegerLiteralExpression(integer=value)
103 def _string_literal_expression_parser(index, tokens):
104 if tokens[index].type == 'double_quoted_string_literal':
105 return (True, index + 1, FurStringLiteralExpression(string=tokens[index].match[1:-1]))
107 if tokens[index].type == 'single_quoted_string_literal':
108 return (True, index + 1, FurStringLiteralExpression(string=tokens[index].match[1:-1]))
110 return (False, index, None)
112 def _symbol_expression_parser(index, tokens):
113 if tokens[index].type == 'symbol':
114 return (True, index + 1, FurSymbolExpression(symbol=tokens[index].match))
116 return (False, index, None)
118 def _wrapped_parser(open_token, close_token, internal_parser):
119 def result_parser(index, tokens):
120 failure = (False, index, None)
122 if tokens[index].type == open_token:
127 success, index, internal = internal_parser(index, tokens)
131 if tokens[index].type == close_token:
134 # TODO Put the actual expected character in the error message
135 raise Exception('Expected closing token on line {}, found "{}"'.format(
140 return True, index, internal
144 def _bracket_wrapped_parser(internal_parser):
145 return _wrapped_parser('open_bracket', 'close_bracket', internal_parser)
147 def _parenthese_wrapped_parser(internal_parser):
148 return _wrapped_parser('open_parenthese', 'close_parenthese', internal_parser)
150 def _parenthesized_expression_parser(index, tokens):
151 return _parenthese_wrapped_parser(_expression_parser)(index, tokens)
153 def _list_literal_expression_parser(index, tokens):
154 failure = (False, index, None)
156 success, index, item_expression_list = _bracket_wrapped_parser(_comma_separated_expression_list_parser)(index, tokens)
159 return success, index, FurListLiteralExpression(
160 item_expression_list=item_expression_list,
165 def _negation_expression_parser(index, tokens):
166 failure = (False, index, None)
168 if tokens[index].match != '-':
171 success, index, value = _literal_level_expression_parser(index + 1, tokens)
176 return (True, index, FurNegationExpression(value=value))
178 def _literal_level_expression_parser(index, tokens):
180 _negation_expression_parser,
181 _list_item_expression_parser,
182 _function_call_expression_parser,
183 _parenthesized_expression_parser,
184 _integer_literal_expression_parser,
185 _string_literal_expression_parser,
186 _list_literal_expression_parser,
187 _symbol_expression_parser,
190 def _left_recursive_infix_operator_parser(operator_token_matcher, operand_parser, order):
191 def result_parser(index, tokens):
192 failure = (False, index, None)
194 success, index, result = operand_parser(index, tokens)
199 while success and index < len(tokens) and operator_token_matcher(tokens[index]):
202 if index + 1 < len(tokens):
203 success, try_index, value = operand_parser(index + 1, tokens)
206 result = FurInfixExpression(
208 operator=tokens[index].match,
214 return True, index, result
218 def _multiplication_level_expression_parser(index, tokens):
219 return _left_recursive_infix_operator_parser(
220 lambda token: token.type == 'multiplication_level_operator',
221 _literal_level_expression_parser,
222 'multiplication_level',
225 def _addition_level_expression_parser(index, tokens):
226 return _left_recursive_infix_operator_parser(
227 lambda token: token.type == 'addition_level_operator',
228 _multiplication_level_expression_parser,
232 def _comparison_level_expression_parser(index, tokens):
233 return _left_recursive_infix_operator_parser(
234 lambda token: token.type == 'comparison_level_operator',
235 _addition_level_expression_parser,
239 def _and_level_expression_parser(index, tokens):
240 return _left_recursive_infix_operator_parser(
241 lambda token: token.type == 'symbol' and token.match == 'and',
242 _comparison_level_expression_parser,
246 def _or_level_expression_parser(index, tokens):
247 return _left_recursive_infix_operator_parser(
248 lambda token: token.type == 'symbol' and token.match == 'or',
249 _and_level_expression_parser,
253 def _comma_separated_list_parser(subparser):
254 def result_parser(index, tokens):
259 _, index, _ = consume_newlines(index, tokens)
261 success, index, item = subparser(index, tokens)
266 return (True, start_index, ())
268 while success and index < len(tokens) and tokens[index].type == 'comma':
272 _, index, _ = consume_newlines(index, tokens)
274 if index < len(tokens):
275 success, try_index, item = subparser(index, tokens)
281 return True, index, tuple(items)
285 def _comma_separated_expression_list_parser(index, tokens):
286 return _comma_separated_list_parser(_expression_parser)(index, tokens)
288 FurListItemExpression = collections.namedtuple(
289 'FurListItemExpression',
296 FurFunctionCallExpression = collections.namedtuple(
297 'FurFunctionCallExpression',
304 FurExpressionStatement = collections.namedtuple(
305 'FurExpressionStatement',
311 FurAssignmentStatement = collections.namedtuple(
312 'FurAssignmentStatement',
319 FurFunctionDefinitionStatement = collections.namedtuple(
320 'FurFunctionDefinitionStatement',
323 'argument_name_list',
328 FurProgram = collections.namedtuple(
335 def _list_item_expression_parser(index, tokens):
336 failure = (False, index, None)
338 # We have to be careful what expressions we add here. Otherwise expressions
339 # like "a + b[0]" become ambiguous to the parser.
340 success, index, list_expression = _or_parser(
341 _symbol_expression_parser,
342 _parenthesized_expression_parser,
348 success, index, index_expression = _bracket_wrapped_parser(_expression_parser)(
356 while success and index < len(tokens):
357 # "list_expression" is actually the full list item expression if the next parse attempt doesn't succeed
358 # We can't give this a better name without a bunch of checks, however.
359 list_expression = FurListItemExpression(
360 list_expression=list_expression,
361 index_expression=index_expression,
364 success, index, index_expression = _bracket_wrapped_parser(_expression_parser)(
369 return True, index, list_expression
371 def _function_call_expression_parser(index, tokens):
372 failure = (False, index, None)
374 # We have to be careful what expressions we add here. Otherwise expressions
375 # like "a + b()" become ambiguous to the parser.
376 success, index, function = _or_parser(
377 _symbol_expression_parser,
378 _parenthesized_expression_parser,
384 success, index, arguments = _parenthese_wrapped_parser(_comma_separated_expression_list_parser)(
392 while success and index < len(tokens):
393 # "function" is actually the full function call if the next parse attempt doesn't succeed
394 # We can't give this a better name without a bunch of checks, however.
395 function = FurFunctionCallExpression(
400 success, index, arguments = _parenthese_wrapped_parser(_comma_separated_expression_list_parser)(
405 return True, index, function
407 def _if_expression_parser(index, tokens):
408 failure = (False, index, None)
410 if tokens[index].match == 'if':
415 success, index, condition_expression = _or_level_expression_parser(index, tokens)
418 raise Exception('Expected condition after "if" on line {}'.format(tokens[index].line))
420 if tokens[index].match == 'do':
423 raise Exception('Expected "do" after "if" on line {}'.format(tokens[index].line))
426 success, index, if_statement_list = _zero_or_more_parser(tuple, _statement_parser)(index, tokens)
427 _, index, _ = consume_newlines(index, tokens)
429 if tokens[index].match == 'else':
431 success, index, else_statement_list = _zero_or_more_parser(tuple, _statement_parser)(index, tokens)
432 _, index, _ = consume_newlines(index, tokens)
434 else_statement_list = ()
436 if tokens[index].match == 'end':
439 raise Exception('Expected "end" after "if" on line {}'.format(tokens[index].line))
445 condition_expression=condition_expression,
446 if_statement_list=if_statement_list,
447 else_statement_list=else_statement_list,
454 _expression_parser = _or_parser(
455 _or_level_expression_parser,
456 _if_expression_parser, # This should always be at the top level
459 def _expression_statement_parser(index, tokens):
460 failure = (False, index, None)
462 success, index, expression = _expression_parser(index, tokens)
467 return (True, index, FurExpressionStatement(expression=expression))
469 BUILTINS = {'print', 'pow'}
471 def _assignment_statement_parser(index, tokens):
472 failure = (False, index, None)
474 if tokens[index].type == 'symbol':
475 target = tokens[index].match
476 target_assignment_line = tokens[index].line
483 if tokens[index].type == 'assignment_operator':
484 if target in BUILTINS:
486 'Trying to assign to builtin "{}" on line {}'.format(target, target_assignment_line),
488 assignment_operator_index = index
492 success, index, expression = _expression_parser(index + 1, tokens)
496 'Expected expression after assignment operator on line {}'.format(
497 tokens[assignment_operator_index].line
501 return True, index, FurAssignmentStatement(target=target, expression=expression)
503 def _function_definition_statement_parser(index, tokens):
504 failure = (False, index, None)
506 if tokens[index].type == 'keyword' and tokens[index].match == 'def':
511 if tokens[index].type == 'symbol':
512 name = tokens[index].match
515 raise Exception('Expected function name, found "{}" on line {}'.format(
520 if tokens[index].type == 'open_parenthese':
523 raise Exception('Expected "(", found "{}" on line {}'.format(
528 success, index, argument_name_list = _comma_separated_list_parser(_symbol_expression_parser)(
533 if tokens[index].type == 'close_parenthese':
536 raise Exception('Expected ")", found "{}" on line {}'.format(
541 if tokens[index].match == 'do':
546 success, index, statement_list = _zero_or_more_parser(tuple, _statement_parser)(index, tokens)
548 _, index, _ = consume_newlines(index, tokens)
550 if tokens[index].type == 'keyword' and tokens[index].match == 'end':
555 return True, index, FurFunctionDefinitionStatement(
557 argument_name_list=tuple(an.symbol for an in argument_name_list),
558 statement_list=statement_list,
561 def _statement_parser(index, tokens):
562 _, index, _ = consume_newlines(index, tokens)
564 if index == len(tokens):
565 return (False, index, None)
568 _assignment_statement_parser,
569 _expression_statement_parser,
570 _function_definition_statement_parser,
573 def _program_formatter(statement_list):
574 return FurProgram(statement_list=statement_list)
576 _program_parser = _zero_or_more_parser(_program_formatter, _statement_parser)
578 def _parse(parser, tokens):
579 success, index, result = parser(0, tokens)
581 if index < len(tokens):
582 raise Exception('Unable to parse token {}'.format(tokens[index]))
587 raise Exception('Unable to parse')
590 return _parse(_program_parser, tokens)
592 if __name__ == '__main__':
597 class FurStringLiteralExpressionParserTests(unittest.TestCase):
598 def test_parses_single_quoted_string_literal(self):
600 _string_literal_expression_parser(0, tokenization.tokenize("'Hello, world'")),
604 FurStringLiteralExpression(string='Hello, world'),
608 class FurFunctionCallExpressionParserTests(unittest.TestCase):
609 def test_parses_function_with_string_literal_argument(self):
611 _function_call_expression_parser(0, tokenization.tokenize("print('Hello, world')")),
615 FurFunctionCallExpression(
617 arguments=(FurStringLiteralExpression(string='Hello, world'),),