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 def _integer_literal_expression_parser(index, tokens):
78 failure = (False, index, None)
80 if tokens[index].type != 'integer_literal':
82 value = int(tokens[index].match)
85 return True, index, FurIntegerLiteralExpression(value=value)
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]))
91 return (False, index, None)
93 def _symbol_expression_parser(index, tokens):
94 if tokens[index].type == 'symbol':
95 return (True, index + 1, FurSymbolExpression(value=tokens[index].match))
97 return (False, index, None)
99 def _parenthese_wrapped_parser(internal_parser):
100 def result_parser(index, tokens):
101 failure = (False, index, None)
103 if tokens[index].type == 'open_parenthese':
108 success, index, internal = internal_parser(index, tokens)
112 if tokens[index].type == 'close_parenthese':
115 raise Exception('Expected ")" on line {}, found "{}"'.format(
120 return True, index, internal
124 def _parenthesized_expression_parser(index, tokens):
125 return _parenthese_wrapped_parser(_expression_parser)(index, tokens)
127 def _negation_expression_parser(index, tokens):
128 failure = (False, index, None)
130 if tokens[index].match != '-':
133 success, index, value = _literal_level_expression_parser(index + 1, tokens)
138 return (True, index, FurNegationExpression(value=value))
140 def _literal_level_expression_parser(index, tokens):
142 _negation_expression_parser,
143 _function_call_expression_parser,
144 _parenthesized_expression_parser,
145 _integer_literal_expression_parser,
146 _string_literal_expression_parser,
147 _symbol_expression_parser,
150 def _left_recursive_infix_operator_parser(operator_token_matcher, operand_parser, order):
151 def result_parser(index, tokens):
152 failure = (False, index, None)
154 success, index, result = operand_parser(index, tokens)
159 while success and index < len(tokens) and operator_token_matcher(tokens[index]):
162 if index + 1 < len(tokens):
163 success, try_index, value = operand_parser(index + 1, tokens)
166 result = FurInfixExpression(
168 operator=tokens[index].match,
174 return True, index, result
178 def _multiplication_level_expression_parser(index, tokens):
179 return _left_recursive_infix_operator_parser(
180 lambda token: token.type == 'multiplication_level_operator',
181 _literal_level_expression_parser,
182 'multiplication_level',
185 def _addition_level_expression_parser(index, tokens):
186 return _left_recursive_infix_operator_parser(
187 lambda token: token.type == 'addition_level_operator',
188 _multiplication_level_expression_parser,
192 def _comparison_level_expression_parser(index, tokens):
193 return _left_recursive_infix_operator_parser(
194 lambda token: token.type == 'comparison_level_operator',
195 _addition_level_expression_parser,
199 def _and_level_expression_parser(index, tokens):
200 return _left_recursive_infix_operator_parser(
201 lambda token: token.type == 'symbol' and token.match == 'and',
202 _comparison_level_expression_parser,
206 def _or_level_expression_parser(index, tokens):
207 return _left_recursive_infix_operator_parser(
208 lambda token: token.type == 'symbol' and token.match == 'or',
209 _and_level_expression_parser,
213 def _comma_separated_list_parser(subparser):
214 def result_parser(index, tokens):
219 success, index, item = subparser(index, tokens)
224 return (True, start_index, ())
226 while success and index < len(tokens) and tokens[index].type == 'comma':
229 if index + 1 < len(tokens):
230 success, try_index, item = subparser(index + 1, tokens)
236 return True, index, tuple(items)
240 def _comma_separated_expression_list_parser(index, tokens):
241 return _comma_separated_list_parser(_expression_parser)(index, tokens)
243 FurFunctionCallExpression = collections.namedtuple(
244 'FurFunctionCallExpression',
251 FurExpressionStatement = collections.namedtuple(
252 'FurExpressionStatement',
258 FurAssignmentStatement = collections.namedtuple(
259 'FurAssignmentStatement',
266 FurFunctionDefinitionStatement = collections.namedtuple(
267 'FurFunctionDefinitionStatement',
270 'argument_name_list',
275 FurProgram = collections.namedtuple(
282 def _function_call_expression_parser(index, tokens):
283 failure = (False, index, None)
285 # We have to be careful what expressions we add here. Otherwise expressions
286 # like "a + b()" become ambiguous to the parser.
287 success, index, function = _or_parser(
288 _symbol_expression_parser,
289 _parenthesized_expression_parser,
295 success, index, arguments = _parenthese_wrapped_parser(_comma_separated_expression_list_parser)(
303 while success and index < len(tokens):
304 # "function" is actually the full function call if the next parse attempt doesn't succeed
305 # We can't give this a better name without a bunch of checks, however.
306 function = FurFunctionCallExpression(
311 success, index, arguments = _parenthese_wrapped_parser(_comma_separated_expression_list_parser)(
316 return True, index, function
318 _expression_parser = _or_level_expression_parser
320 def _expression_statement_parser(index, tokens):
321 failure = (False, index, None)
323 success, index, expression = _expression_parser(index, tokens)
328 return (True, index, FurExpressionStatement(expression=expression))
330 BUILTINS = {'print', 'pow'}
332 def _assignment_statement_parser(index, tokens):
333 failure = (False, index, None)
335 if tokens[index].type == 'symbol':
336 target = tokens[index].match
337 target_assignment_line = tokens[index].line
344 if tokens[index].type == 'assignment_operator':
345 if target in BUILTINS:
347 'Trying to assign to builtin "{}" on line {}'.format(target, target_assignment_line),
349 assignment_operator_index = index
353 success, index, expression = _expression_parser(index + 1, tokens)
357 'Expected expression after assignment operator on line {}'.format(
358 tokens[assignment_operator_index].line
362 return True, index, FurAssignmentStatement(target=target, expression=expression)
364 def _function_definition_statement_parser(index, tokens):
365 failure = (False, index, None)
367 if tokens[index].type == 'keyword' and tokens[index].match == 'def':
372 if tokens[index].type == 'symbol':
373 name = tokens[index].match
376 raise Exception('Expected function name, found "{}" on line {}'.format(
381 if tokens[index].type == 'open_parenthese':
384 raise Exception('Expected "(", found "{}" on line {}'.format(
389 success, index, argument_name_list = _comma_separated_list_parser(_symbol_expression_parser)(
394 if tokens[index].type == 'close_parenthese':
397 raise Exception('Expected ")", found "{}" on line {}'.format(
402 if tokens[index].type == 'symbol' and tokens[index].match == 'do':
407 success, index, statement_list = _zero_or_more_parser(tuple, _statement_parser)(index, tokens)
409 _, index, _ = consume_newlines(index, tokens)
411 if tokens[index].type == 'keyword' and tokens[index].match == 'end':
416 return True, index, FurFunctionDefinitionStatement(
418 argument_name_list=tuple(an.value for an in argument_name_list),
419 statement_list=statement_list,
422 def _statement_parser(index, tokens):
423 _, index, _ = consume_newlines(index, tokens)
425 if index == len(tokens):
426 return (False, index, None)
429 _assignment_statement_parser,
430 _expression_statement_parser,
431 _function_definition_statement_parser,
434 def _program_formatter(statement_list):
435 return FurProgram(statement_list=statement_list)
437 _program_parser = _zero_or_more_parser(_program_formatter, _statement_parser)
439 def _parse(parser, tokens):
440 success, index, result = parser(0, tokens)
442 if index < len(tokens):
443 raise Exception('Unable to parse token {}'.format(tokens[index]))
448 raise Exception('Unable to parse')
451 return _parse(_program_parser, tokens)
453 if __name__ == '__main__':
458 class FurStringLiteralExpressionParserTests(unittest.TestCase):
459 def test_parses_single_quoted_string_literal(self):
461 _string_literal_expression_parser(0, tokenization.tokenize("'Hello, world'")),
465 FurStringLiteralExpression(value='Hello, world'),
469 class FurFunctionCallExpressionParserTests(unittest.TestCase):
470 def test_parses_function_with_string_literal_argument(self):
472 _function_call_expression_parser(0, tokenization.tokenize("print('Hello, world')")),
476 FurFunctionCallExpression(
478 arguments=(FurStringLiteralExpression(value='Hello, world'),),