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 FurAdditionExpression = collections.namedtuple(
69 'FurAdditionExpression',
76 FurSubtractionExpression = collections.namedtuple(
77 'FurSubtractionExpression',
84 FurMultiplicationExpression = collections.namedtuple(
85 'FurMultiplicationExpression',
92 FurIntegerDivisionExpression = collections.namedtuple(
93 'FurIntegerDivisionExpression',
100 FurModularDivisionExpression = collections.namedtuple(
101 'FurModularDivisionExpression',
108 FurEqualityExpression = collections.namedtuple(
109 'FurEqualityExpression',
116 FurInequalityExpression = collections.namedtuple(
117 'FurInequalityExpression',
124 FurLessThanOrEqualExpression = collections.namedtuple(
125 'FurLessThanOrEqualExpression',
132 FurGreaterThanOrEqualExpression = collections.namedtuple(
133 'FurGreaterThanOrEqualExpression',
140 FurLessThanExpression = collections.namedtuple(
141 'FurLessThanExpression',
148 FurGreaterThanExpression = collections.namedtuple(
149 'FurGreaterThanExpression',
156 def _integer_literal_expression_parser(index, tokens):
157 failure = (False, index, None)
159 if tokens[index].type != 'integer_literal':
161 value = int(tokens[index].match)
164 return True, index, FurIntegerLiteralExpression(value=value)
166 def _string_literal_expression_parser(index, tokens):
167 if tokens[index].type == 'single_quoted_string_literal':
168 return (True, index + 1, FurStringLiteralExpression(value=tokens[index].match[1:-1]))
170 return (False, index, None)
172 def _symbol_expression_parser(index, tokens):
173 if tokens[index].type == 'symbol':
174 return (True, index + 1, FurSymbolExpression(value=tokens[index].match))
176 return (False, index, None)
178 def _parenthesized_expression_parser(index, tokens):
179 failure = (False, index, None)
181 if tokens[index].type == 'open_parenthese':
186 success, index, internal = _expression_parser(index, tokens)
190 if tokens[index].type == 'close_parenthese':
193 raise Exception('Expected ")" on line {}, found "{}"'.format(
198 return True, index, FurParenthesizedExpression(internal=internal)
200 def _negation_expression_parser(index, tokens):
201 failure = (False, index, None)
203 if tokens[index].match != '-':
206 success, index, value = _literal_level_expression_parser(index + 1, tokens)
211 return (True, index, FurNegationExpression(value=value))
213 def _literal_level_expression_parser(index, tokens):
215 _negation_expression_parser,
216 _function_call_expression_parser,
217 _parenthesized_expression_parser,
218 _integer_literal_expression_parser,
219 _string_literal_expression_parser,
220 _symbol_expression_parser,
223 def _multiplication_level_expression_parser(index, tokens):
224 failure = (False, index, None)
226 success, index, result = _literal_level_expression_parser(index, tokens)
231 while success and index < len(tokens) and tokens[index].type == 'multiplication_level_operator':
234 if index + 1 < len(tokens):
235 success, try_index, value = _literal_level_expression_parser(index + 1, tokens)
239 '*': FurMultiplicationExpression,
240 '//': FurIntegerDivisionExpression,
241 '%': FurModularDivisionExpression,
242 }[tokens[index].match](left=result, right=value)
245 return True, index, result
247 def _addition_level_expression_parser(index, tokens):
248 failure = (False, index, None)
250 success, index, result = _multiplication_level_expression_parser(index, tokens)
255 while success and index < len(tokens) and tokens[index].type == 'addition_level_operator':
258 if index + 1 < len(tokens):
259 success, try_index, value = _multiplication_level_expression_parser(index + 1, tokens)
263 '+': FurAdditionExpression,
264 '-': FurSubtractionExpression,
265 }[tokens[index].match](left=result, right=value)
268 return True, index, result
270 def _equality_level_expression_parser(index, tokens):
271 failure = (False, index, None)
273 success, index, result = _addition_level_expression_parser(index, tokens)
278 while success and index < len(tokens) and tokens[index].type == 'equality_level_operator':
281 if index + 1 < len(tokens):
282 success, try_index, value = _addition_level_expression_parser(index + 1, tokens)
286 '==': FurEqualityExpression,
287 '!=': FurInequalityExpression,
288 '>=': FurGreaterThanOrEqualExpression,
289 '<=': FurLessThanOrEqualExpression,
290 '>': FurGreaterThanExpression,
291 '<': FurLessThanExpression,
292 }[tokens[index].match](left=result, right=value)
295 return True, index, result
298 def _comma_separated_list_parser(index, tokens):
299 failure = (False, index, None)
303 success, index, expression = _expression_parser(index, tokens)
306 expressions.append(expression)
310 while success and index < len(tokens) and tokens[index].type == 'comma':
313 if index + 1 < len(tokens):
314 success, try_index, expression = _expression_parser(index + 1, tokens)
317 expressions.append(expression)
320 return True, index, tuple(expressions)
323 FurFunctionCallExpression = collections.namedtuple(
324 'FurFunctionCallExpression',
331 FurAssignmentStatement = collections.namedtuple(
332 'FurAssignmentStatement',
339 FurProgram = collections.namedtuple(
346 def _function_call_expression_parser(index, tokens):
347 # TODO Use a FurSymbolExpression for the name
348 failure = (False, index, None)
350 success, index, function = _symbol_expression_parser(index, tokens)
355 if tokens[index].type != 'open_parenthese':
359 success, index, arguments = _comma_separated_list_parser(index, tokens)
364 if tokens[index].type != 'close_parenthese':
365 raise Exception('Expected ")", found "{}" on line {}'.format(
371 return True, index, FurFunctionCallExpression(function=function, arguments=arguments)
373 _expression_parser = _equality_level_expression_parser
375 def _assignment_statement_parser(index, tokens):
376 # TODO Use a FurSymbolExpression for the target? Maybe this is actually not a good idea
377 failure = (False, index, None)
379 if tokens[index].type != 'symbol':
381 target = tokens[index].match
384 if tokens[index].type != 'assignment_operator':
386 assignment_operator_index = index
388 success, index, expression = _expression_parser(index + 1, tokens)
392 'Expected expression after assignment operator on line {}'.format(
393 tokens[assignment_operator_index].line
397 return True, index, FurAssignmentStatement(target=target, expression=expression)
399 def _statement_parser(index, tokens):
400 # 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)
402 _assignment_statement_parser,
406 def _program_formatter(statement_list):
407 return FurProgram(statement_list=statement_list)
409 _program_parser = _zero_or_more_parser(_program_formatter, _statement_parser)
411 def _parse(parser, tokens):
412 success, index, result = parser(0, tokens)
414 if index < len(tokens):
415 raise Exception('Unable to parse token {}'.format(tokens[index]))
420 raise Exception('Unable to parse')
423 return _parse(_program_parser, tokens)
425 if __name__ == '__main__':
430 class FurStringLiteralExpressionParserTests(unittest.TestCase):
431 def test_parses_single_quoted_string_literal(self):
433 _string_literal_expression_parser(0, tokenization.tokenize("'Hello, world'")),
437 FurStringLiteralExpression(value='Hello, world'),
441 class FurFunctionCallExpressionParserTests(unittest.TestCase):
442 def test_parses_function_with_string_literal_argument(self):
444 _function_call_expression_parser(0, tokenization.tokenize("print('Hello, world')")),
448 FurFunctionCallExpression(
450 arguments=(FurStringLiteralExpression(value='Hello, world'),),