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 _parenthesized_expression_parser(index, tokens):
100 failure = (False, index, None)
102 if tokens[index].type == 'open_parenthese':
107 success, index, internal = _expression_parser(index, tokens)
111 if tokens[index].type == 'close_parenthese':
114 raise Exception('Expected ")" on line {}, found "{}"'.format(
119 return True, index, internal
121 def _negation_expression_parser(index, tokens):
122 failure = (False, index, None)
124 if tokens[index].match != '-':
127 success, index, value = _literal_level_expression_parser(index + 1, tokens)
132 return (True, index, FurNegationExpression(value=value))
134 def _literal_level_expression_parser(index, tokens):
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,
144 def _left_recursive_infix_operator_parser(operator_token_matcher, operand_parser, order):
145 def result_parser(index, tokens):
146 failure = (False, index, None)
148 success, index, result = operand_parser(index, tokens)
153 while success and index < len(tokens) and operator_token_matcher(tokens[index]):
156 if index + 1 < len(tokens):
157 success, try_index, value = operand_parser(index + 1, tokens)
160 result = FurInfixExpression(
162 operator=tokens[index].match,
168 return True, index, result
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',
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,
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,
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,
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,
207 def _comma_separated_list_parser(subparser):
208 def result_parser(index, tokens):
213 success, index, item = subparser(index, tokens)
218 return (True, start_index, ())
220 while success and index < len(tokens) and tokens[index].type == 'comma':
223 if index + 1 < len(tokens):
224 success, try_index, item = subparser(index + 1, tokens)
230 return True, index, tuple(items)
234 def _comma_separated_expression_list_parser(index, tokens):
235 return _comma_separated_list_parser(_expression_parser)(index, tokens)
237 FurFunctionCallExpression = collections.namedtuple(
238 'FurFunctionCallExpression',
245 FurExpressionStatement = collections.namedtuple(
246 'FurExpressionStatement',
252 FurAssignmentStatement = collections.namedtuple(
253 'FurAssignmentStatement',
260 FurFunctionDefinitionStatement = collections.namedtuple(
261 'FurFunctionDefinitionStatement',
264 'argument_name_list',
269 FurProgram = collections.namedtuple(
276 def _function_call_expression_parser(index, tokens):
277 # TODO Allow function calls as the source of the function. This requires a
278 # left-recursive parser, however.
279 failure = (False, index, None)
281 # We have to be careful what expressions we add here. Otherwise expressions
282 # like "a + b()" become ambiguous to the parser.
283 success, index, function = _or_parser(
284 _symbol_expression_parser,
285 _parenthesized_expression_parser,
291 if tokens[index].type != 'open_parenthese':
295 success, index, arguments = _comma_separated_expression_list_parser(index, tokens)
300 if tokens[index].type != 'close_parenthese':
301 raise Exception('Expected ")", found "{}" on line {}'.format(
307 return True, index, FurFunctionCallExpression(function=function, arguments=arguments)
309 _expression_parser = _or_level_expression_parser
311 def _expression_statement_parser(index, tokens):
312 failure = (False, index, None)
314 success, index, expression = _expression_parser(index, tokens)
319 return (True, index, FurExpressionStatement(expression=expression))
321 def _assignment_statement_parser(index, tokens):
322 # TODO Use a FurSymbolExpression for the target? Maybe this is actually not a good idea
323 failure = (False, index, None)
325 if tokens[index].type != 'symbol':
327 target = tokens[index].match
330 if tokens[index].type != 'assignment_operator':
332 assignment_operator_index = index
334 success, index, expression = _expression_parser(index + 1, tokens)
338 'Expected expression after assignment operator on line {}'.format(
339 tokens[assignment_operator_index].line
343 return True, index, FurAssignmentStatement(target=target, expression=expression)
345 def _function_definition_statement_parser(index, tokens):
346 failure = (False, index, None)
348 if tokens[index].type == 'keyword' and tokens[index].match == 'def':
353 if tokens[index].type == 'symbol':
354 name = tokens[index].match
357 raise Exception('Expected function name, found "{}" on line {}'.format(
362 if tokens[index].type == 'open_parenthese':
365 raise Exception('Expected "(", found "{}" on line {}'.format(
370 success, index, argument_name_list = _comma_separated_list_parser(_symbol_expression_parser)(
375 if tokens[index].type == 'close_parenthese':
378 raise Exception('Expected ")", found "{}" on line {}'.format(
383 if tokens[index].type == 'symbol' and tokens[index].match == 'do':
388 success, index, statement_list = _zero_or_more_parser(tuple, _statement_parser)(index, tokens)
390 _, index, _ = consume_newlines(index, tokens)
392 if tokens[index].type == 'keyword' and tokens[index].match == 'end':
397 return True, index, FurFunctionDefinitionStatement(
399 argument_name_list=tuple(an.value for an in argument_name_list),
400 statement_list=statement_list,
403 def _statement_parser(index, tokens):
404 _, index, _ = consume_newlines(index, tokens)
406 if index == len(tokens):
407 return (False, index, None)
410 _assignment_statement_parser,
411 _expression_statement_parser,
412 _function_definition_statement_parser,
415 def _program_formatter(statement_list):
416 return FurProgram(statement_list=statement_list)
418 _program_parser = _zero_or_more_parser(_program_formatter, _statement_parser)
420 def _parse(parser, tokens):
421 success, index, result = parser(0, tokens)
423 if index < len(tokens):
424 raise Exception('Unable to parse token {}'.format(tokens[index]))
429 raise Exception('Unable to parse')
432 return _parse(_program_parser, tokens)
434 if __name__ == '__main__':
439 class FurStringLiteralExpressionParserTests(unittest.TestCase):
440 def test_parses_single_quoted_string_literal(self):
442 _string_literal_expression_parser(0, tokenization.tokenize("'Hello, world'")),
446 FurStringLiteralExpression(value='Hello, world'),
450 class FurFunctionCallExpressionParserTests(unittest.TestCase):
451 def test_parses_function_with_string_literal_argument(self):
453 _function_call_expression_parser(0, tokenization.tokenize("print('Hello, world')")),
457 FurFunctionCallExpression(
459 arguments=(FurStringLiteralExpression(value='Hello, world'),),