3 def _or_parser(*parsers):
4 def result_parser(index, tokens):
5 failure = (False, index, None)
8 success, index, value = parser(index, tokens)
11 return (success, index, value)
17 def _zero_or_more_parser(formatter, parser):
18 def result_parser(index, tokens):
21 while index < len(tokens):
22 success, index, value = parser(index, tokens)
29 return (True, index, formatter(values))
33 FurIntegerLiteralExpression = collections.namedtuple(
34 'FurIntegerLiteralExpression',
40 FurStringLiteralExpression = collections.namedtuple(
41 'FurStringLiteralExpression',
47 FurSymbolExpression = collections.namedtuple(
48 'FurSymbolExpression',
54 FurNegationExpression = collections.namedtuple(
55 'FurNegationExpression',
61 FurParenthesizedExpression = collections.namedtuple(
62 'FurParenthesizedExpression',
68 FurAdditionLevelExpression = collections.namedtuple(
69 'FurAdditionLevelExpression',
77 FurMultiplicationLevelExpression = collections.namedtuple(
78 'FurMultiplicationLevelExpression',
86 FurEqualityLevelExpression = collections.namedtuple(
87 'FurEqualityLevelExpression',
95 def _integer_literal_expression_parser(index, tokens):
96 failure = (False, index, None)
98 if tokens[index].type != 'integer_literal':
100 value = int(tokens[index].match)
103 return True, index, FurIntegerLiteralExpression(value=value)
105 def _string_literal_expression_parser(index, tokens):
106 if tokens[index].type == 'single_quoted_string_literal':
107 return (True, index + 1, FurStringLiteralExpression(value=tokens[index].match[1:-1]))
109 return (False, index, None)
111 def _symbol_expression_parser(index, tokens):
112 if tokens[index].type == 'symbol':
113 return (True, index + 1, FurSymbolExpression(value=tokens[index].match))
115 return (False, index, None)
117 def _parenthesized_expression_parser(index, tokens):
118 failure = (False, index, None)
120 if tokens[index].type == 'open_parenthese':
125 success, index, internal = _expression_parser(index, tokens)
129 if tokens[index].type == 'close_parenthese':
132 raise Exception('Expected ")" on line {}, found "{}"'.format(
137 return True, index, FurParenthesizedExpression(internal=internal)
139 def _negation_expression_parser(index, tokens):
140 failure = (False, index, None)
142 if tokens[index].match != '-':
145 success, index, value = _literal_level_expression_parser(index + 1, tokens)
150 return (True, index, FurNegationExpression(value=value))
152 def _literal_level_expression_parser(index, tokens):
154 _negation_expression_parser,
155 _function_call_expression_parser,
156 _parenthesized_expression_parser,
157 _integer_literal_expression_parser,
158 _string_literal_expression_parser,
159 _symbol_expression_parser,
162 def _left_recursive_infix_operator_parser(token_type, operand_parser, result_expression_type):
163 def result_parser(index, tokens):
164 failure = (False, index, None)
166 success, index, result = operand_parser(index, tokens)
171 while success and index < len(tokens) and tokens[index].type == token_type:
174 if index + 1 < len(tokens):
175 success, try_index, value = operand_parser(index + 1, tokens)
178 result = result_expression_type(
179 operator=tokens[index].match,
185 return True, index, result
189 def _multiplication_level_expression_parser(index, tokens):
190 return _left_recursive_infix_operator_parser(
191 'multiplication_level_operator',
192 _literal_level_expression_parser,
193 FurMultiplicationLevelExpression,
196 def _addition_level_expression_parser(index, tokens):
197 return _left_recursive_infix_operator_parser(
198 'addition_level_operator',
199 _multiplication_level_expression_parser,
200 FurAdditionLevelExpression,
203 def _equality_level_expression_parser(index, tokens):
204 return _left_recursive_infix_operator_parser(
205 'equality_level_operator',
206 _addition_level_expression_parser,
207 FurEqualityLevelExpression,
210 def _comma_separated_list_parser(index, tokens):
211 failure = (False, index, None)
215 success, index, expression = _expression_parser(index, tokens)
218 expressions.append(expression)
222 while success and index < len(tokens) and tokens[index].type == 'comma':
225 if index + 1 < len(tokens):
226 success, try_index, expression = _expression_parser(index + 1, tokens)
229 expressions.append(expression)
232 return True, index, tuple(expressions)
235 FurFunctionCallExpression = collections.namedtuple(
236 'FurFunctionCallExpression',
243 FurAssignmentStatement = collections.namedtuple(
244 'FurAssignmentStatement',
251 FurProgram = collections.namedtuple(
258 def _function_call_expression_parser(index, tokens):
259 # TODO Use a FurSymbolExpression for the name
260 failure = (False, index, None)
262 success, index, function = _symbol_expression_parser(index, tokens)
267 if tokens[index].type != 'open_parenthese':
271 success, index, arguments = _comma_separated_list_parser(index, tokens)
276 if tokens[index].type != 'close_parenthese':
277 raise Exception('Expected ")", found "{}" on line {}'.format(
283 return True, index, FurFunctionCallExpression(function=function, arguments=arguments)
285 _expression_parser = _equality_level_expression_parser
287 def _assignment_statement_parser(index, tokens):
288 # TODO Use a FurSymbolExpression for the target? Maybe this is actually not a good idea
289 failure = (False, index, None)
291 if tokens[index].type != 'symbol':
293 target = tokens[index].match
296 if tokens[index].type != 'assignment_operator':
298 assignment_operator_index = index
300 success, index, expression = _expression_parser(index + 1, tokens)
304 'Expected expression after assignment operator on line {}'.format(
305 tokens[assignment_operator_index].line
309 return True, index, FurAssignmentStatement(target=target, expression=expression)
311 def _statement_parser(index, tokens):
312 # TODO It would be good to include newlines in the parsing of this because it removes the ambiguity between "function(argument)" (one statement) and "function\n(argument)" (two statements)
314 _assignment_statement_parser,
318 def _program_formatter(statement_list):
319 return FurProgram(statement_list=statement_list)
321 _program_parser = _zero_or_more_parser(_program_formatter, _statement_parser)
323 def _parse(parser, tokens):
324 success, index, result = parser(0, tokens)
326 if index < len(tokens):
327 raise Exception('Unable to parse token {}'.format(tokens[index]))
332 raise Exception('Unable to parse')
335 return _parse(_program_parser, tokens)
337 if __name__ == '__main__':
342 class FurStringLiteralExpressionParserTests(unittest.TestCase):
343 def test_parses_single_quoted_string_literal(self):
345 _string_literal_expression_parser(0, tokenization.tokenize("'Hello, world'")),
349 FurStringLiteralExpression(value='Hello, world'),
353 class FurFunctionCallExpressionParserTests(unittest.TestCase):
354 def test_parses_function_with_string_literal_argument(self):
356 _function_call_expression_parser(0, tokenization.tokenize("print('Hello, world')")),
360 FurFunctionCallExpression(
362 arguments=(FurStringLiteralExpression(value='Hello, world'),),