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 # TODO We don't need to wrap this type
68 FurParenthesizedExpression = collections.namedtuple(
69 'FurParenthesizedExpression',
75 FurInfixExpression = collections.namedtuple(
85 def _integer_literal_expression_parser(index, tokens):
86 failure = (False, index, None)
88 if tokens[index].type != 'integer_literal':
90 value = int(tokens[index].match)
93 return True, index, FurIntegerLiteralExpression(value=value)
95 def _string_literal_expression_parser(index, tokens):
96 if tokens[index].type == 'single_quoted_string_literal':
97 return (True, index + 1, FurStringLiteralExpression(value=tokens[index].match[1:-1]))
99 return (False, index, None)
101 def _symbol_expression_parser(index, tokens):
102 if tokens[index].type == 'symbol':
103 return (True, index + 1, FurSymbolExpression(value=tokens[index].match))
105 return (False, index, None)
107 def _parenthesized_expression_parser(index, tokens):
108 failure = (False, index, None)
110 if tokens[index].type == 'open_parenthese':
115 success, index, internal = _expression_parser(index, tokens)
119 if tokens[index].type == 'close_parenthese':
122 raise Exception('Expected ")" on line {}, found "{}"'.format(
127 return True, index, FurParenthesizedExpression(internal=internal)
129 def _negation_expression_parser(index, tokens):
130 failure = (False, index, None)
132 if tokens[index].match != '-':
135 success, index, value = _literal_level_expression_parser(index + 1, tokens)
140 return (True, index, FurNegationExpression(value=value))
142 def _literal_level_expression_parser(index, tokens):
144 _negation_expression_parser,
145 _function_call_expression_parser,
146 _parenthesized_expression_parser,
147 _integer_literal_expression_parser,
148 _string_literal_expression_parser,
149 _symbol_expression_parser,
152 def _left_recursive_infix_operator_parser(operator_token_matcher, operand_parser, order):
153 def result_parser(index, tokens):
154 failure = (False, index, None)
156 success, index, result = operand_parser(index, tokens)
161 while success and index < len(tokens) and operator_token_matcher(tokens[index]):
164 if index + 1 < len(tokens):
165 success, try_index, value = operand_parser(index + 1, tokens)
168 result = FurInfixExpression(
170 operator=tokens[index].match,
176 return True, index, result
180 def _multiplication_level_expression_parser(index, tokens):
181 return _left_recursive_infix_operator_parser(
182 lambda token: token.type == 'multiplication_level_operator',
183 _literal_level_expression_parser,
184 'multiplication_level',
187 def _addition_level_expression_parser(index, tokens):
188 return _left_recursive_infix_operator_parser(
189 lambda token: token.type == 'addition_level_operator',
190 _multiplication_level_expression_parser,
194 def _comparison_level_expression_parser(index, tokens):
195 return _left_recursive_infix_operator_parser(
196 lambda token: token.type == 'comparison_level_operator',
197 _addition_level_expression_parser,
201 def _and_level_expression_parser(index, tokens):
202 return _left_recursive_infix_operator_parser(
203 lambda token: token.type == 'symbol' and token.match == 'and',
204 _comparison_level_expression_parser,
208 def _or_level_expression_parser(index, tokens):
209 return _left_recursive_infix_operator_parser(
210 lambda token: token.type == 'symbol' and token.match == 'or',
211 _and_level_expression_parser,
215 def _comma_separated_list_parser(subparser):
216 def result_parser(index, tokens):
221 success, index, item = subparser(index, tokens)
226 return (True, start_index, ())
228 while success and index < len(tokens) and tokens[index].type == 'comma':
231 if index + 1 < len(tokens):
232 success, try_index, item = subparser(index + 1, tokens)
238 return True, index, tuple(items)
242 def _comma_separated_expression_list_parser(index, tokens):
243 return _comma_separated_list_parser(_expression_parser)(index, tokens)
245 FurFunctionCallExpression = collections.namedtuple(
246 'FurFunctionCallExpression',
253 FurExpressionStatement = collections.namedtuple(
254 'FurExpressionStatement',
260 FurAssignmentStatement = collections.namedtuple(
261 'FurAssignmentStatement',
268 FurFunctionDefinitionStatement = collections.namedtuple(
269 'FurFunctionDefinitionStatement',
272 'argument_name_list',
277 FurProgram = collections.namedtuple(
284 def _function_call_expression_parser(index, tokens):
285 # TODO Use a FurSymbolExpression for the name
286 failure = (False, index, None)
288 success, index, function = _symbol_expression_parser(index, tokens)
293 if tokens[index].type != 'open_parenthese':
297 success, index, arguments = _comma_separated_expression_list_parser(index, tokens)
302 if tokens[index].type != 'close_parenthese':
303 raise Exception('Expected ")", found "{}" on line {}'.format(
309 return True, index, FurFunctionCallExpression(function=function, arguments=arguments)
311 _expression_parser = _or_level_expression_parser
313 def _expression_statement_parser(index, tokens):
314 failure = (False, index, None)
316 success, index, expression = _expression_parser(index, tokens)
321 return (True, index, FurExpressionStatement(expression=expression))
323 def _assignment_statement_parser(index, tokens):
324 # TODO Use a FurSymbolExpression for the target? Maybe this is actually not a good idea
325 failure = (False, index, None)
327 if tokens[index].type != 'symbol':
329 target = tokens[index].match
332 if tokens[index].type != 'assignment_operator':
334 assignment_operator_index = index
336 success, index, expression = _expression_parser(index + 1, tokens)
340 'Expected expression after assignment operator on line {}'.format(
341 tokens[assignment_operator_index].line
345 return True, index, FurAssignmentStatement(target=target, expression=expression)
347 def _function_definition_statement_parser(index, tokens):
348 failure = (False, index, None)
350 if tokens[index].type == 'keyword' and tokens[index].match == 'def':
355 if tokens[index].type == 'symbol':
356 name = tokens[index].match
359 raise Exception('Expected function name, found "{}" on line {}'.format(
364 if tokens[index].type == 'open_parenthese':
367 raise Exception('Expected "(", found "{}" on line {}'.format(
372 success, index, argument_name_list = _comma_separated_list_parser(_symbol_expression_parser)(
377 if tokens[index].type == 'close_parenthese':
380 raise Exception('Expected ")", found "{}" on line {}'.format(
385 if tokens[index].type == 'symbol' and tokens[index].match == 'do':
390 success, index, statement_list = _zero_or_more_parser(tuple, _statement_parser)(index, tokens)
392 _, index, _ = consume_newlines(index, tokens)
394 if tokens[index].type == 'keyword' and tokens[index].match == 'end':
399 return True, index, FurFunctionDefinitionStatement(
401 argument_name_list=tuple(an.value for an in argument_name_list),
402 statement_list=statement_list,
405 def _statement_parser(index, tokens):
406 _, index, _ = consume_newlines(index, tokens)
408 if index == len(tokens):
409 return (False, index, None)
412 _assignment_statement_parser,
413 _expression_statement_parser,
414 _function_definition_statement_parser,
417 def _program_formatter(statement_list):
418 return FurProgram(statement_list=statement_list)
420 _program_parser = _zero_or_more_parser(_program_formatter, _statement_parser)
422 def _parse(parser, tokens):
423 success, index, result = parser(0, tokens)
425 if index < len(tokens):
426 raise Exception('Unable to parse token {}'.format(tokens[index]))
431 raise Exception('Unable to parse')
434 return _parse(_program_parser, tokens)
436 if __name__ == '__main__':
441 class FurStringLiteralExpressionParserTests(unittest.TestCase):
442 def test_parses_single_quoted_string_literal(self):
444 _string_literal_expression_parser(0, tokenization.tokenize("'Hello, world'")),
448 FurStringLiteralExpression(value='Hello, world'),
452 class FurFunctionCallExpressionParserTests(unittest.TestCase):
453 def test_parses_function_with_string_literal_argument(self):
455 _function_call_expression_parser(0, tokenization.tokenize("print('Hello, world')")),
459 FurFunctionCallExpression(
461 arguments=(FurStringLiteralExpression(value='Hello, world'),),