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 FurParenthesizedExpression = collections.namedtuple(
68 'FurParenthesizedExpression',
74 FurInfixExpression = collections.namedtuple(
84 def _integer_literal_expression_parser(index, tokens):
85 failure = (False, index, None)
87 if tokens[index].type != 'integer_literal':
89 value = int(tokens[index].match)
92 return True, index, FurIntegerLiteralExpression(value=value)
94 def _string_literal_expression_parser(index, tokens):
95 if tokens[index].type == 'single_quoted_string_literal':
96 return (True, index + 1, FurStringLiteralExpression(value=tokens[index].match[1:-1]))
98 return (False, index, None)
100 def _symbol_expression_parser(index, tokens):
101 if tokens[index].type == 'symbol':
102 return (True, index + 1, FurSymbolExpression(value=tokens[index].match))
104 return (False, index, None)
106 def _parenthesized_expression_parser(index, tokens):
107 failure = (False, index, None)
109 if tokens[index].type == 'open_parenthese':
114 success, index, internal = _expression_parser(index, tokens)
118 if tokens[index].type == 'close_parenthese':
121 raise Exception('Expected ")" on line {}, found "{}"'.format(
126 return True, index, FurParenthesizedExpression(internal=internal)
128 def _negation_expression_parser(index, tokens):
129 failure = (False, index, None)
131 if tokens[index].match != '-':
134 success, index, value = _literal_level_expression_parser(index + 1, tokens)
139 return (True, index, FurNegationExpression(value=value))
141 def _literal_level_expression_parser(index, tokens):
143 _negation_expression_parser,
144 _function_call_expression_parser,
145 _parenthesized_expression_parser,
146 _integer_literal_expression_parser,
147 _string_literal_expression_parser,
148 _symbol_expression_parser,
151 def _left_recursive_infix_operator_parser(operator_token_matcher, operand_parser, order):
152 def result_parser(index, tokens):
153 failure = (False, index, None)
155 success, index, result = operand_parser(index, tokens)
160 while success and index < len(tokens) and operator_token_matcher(tokens[index]):
163 if index + 1 < len(tokens):
164 success, try_index, value = operand_parser(index + 1, tokens)
167 result = FurInfixExpression(
169 operator=tokens[index].match,
175 return True, index, result
179 def _multiplication_level_expression_parser(index, tokens):
180 return _left_recursive_infix_operator_parser(
181 lambda token: token.type == 'multiplication_level_operator',
182 _literal_level_expression_parser,
183 'multiplication_level',
186 def _addition_level_expression_parser(index, tokens):
187 return _left_recursive_infix_operator_parser(
188 lambda token: token.type == 'addition_level_operator',
189 _multiplication_level_expression_parser,
193 def _comparison_level_expression_parser(index, tokens):
194 return _left_recursive_infix_operator_parser(
195 lambda token: token.type == 'comparison_level_operator',
196 _addition_level_expression_parser,
200 def _and_level_expression_parser(index, tokens):
201 return _left_recursive_infix_operator_parser(
202 lambda token: token.type == 'symbol' and token.match == 'and',
203 _comparison_level_expression_parser,
207 def _or_level_expression_parser(index, tokens):
208 return _left_recursive_infix_operator_parser(
209 lambda token: token.type == 'symbol' and token.match == 'or',
210 _and_level_expression_parser,
214 def _comma_separated_list_parser(index, tokens):
215 failure = (False, index, None)
219 success, index, expression = _expression_parser(index, tokens)
222 expressions.append(expression)
226 while success and index < len(tokens) and tokens[index].type == 'comma':
229 if index + 1 < len(tokens):
230 success, try_index, expression = _expression_parser(index + 1, tokens)
233 expressions.append(expression)
236 return True, index, tuple(expressions)
239 FurFunctionCallExpression = collections.namedtuple(
240 'FurFunctionCallExpression',
247 FurExpressionStatement = collections.namedtuple(
248 'FurExpressionStatement',
254 FurAssignmentStatement = collections.namedtuple(
255 'FurAssignmentStatement',
262 FurProgram = collections.namedtuple(
269 def _function_call_expression_parser(index, tokens):
270 # TODO Use a FurSymbolExpression for the name
271 failure = (False, index, None)
273 success, index, function = _symbol_expression_parser(index, tokens)
278 if tokens[index].type != 'open_parenthese':
282 success, index, arguments = _comma_separated_list_parser(index, tokens)
287 if tokens[index].type != 'close_parenthese':
288 raise Exception('Expected ")", found "{}" on line {}'.format(
294 return True, index, FurFunctionCallExpression(function=function, arguments=arguments)
296 _expression_parser = _or_level_expression_parser
298 def _expression_statement_parser(index, tokens):
299 failure = (False, index, None)
301 success, index, expression = _expression_parser(index, tokens)
306 return (True, index, FurExpressionStatement(expression=expression))
308 def _assignment_statement_parser(index, tokens):
309 # TODO Use a FurSymbolExpression for the target? Maybe this is actually not a good idea
310 failure = (False, index, None)
312 if tokens[index].type != 'symbol':
314 target = tokens[index].match
317 if tokens[index].type != 'assignment_operator':
319 assignment_operator_index = index
321 success, index, expression = _expression_parser(index + 1, tokens)
325 'Expected expression after assignment operator on line {}'.format(
326 tokens[assignment_operator_index].line
330 return True, index, FurAssignmentStatement(target=target, expression=expression)
332 def _statement_parser(index, tokens):
333 _, index, _ = consume_newlines(index, tokens)
335 if index == len(tokens):
336 return (False, index, None)
339 _assignment_statement_parser,
340 _expression_statement_parser,
343 def _program_formatter(statement_list):
344 return FurProgram(statement_list=statement_list)
346 _program_parser = _zero_or_more_parser(_program_formatter, _statement_parser)
348 def _parse(parser, tokens):
349 success, index, result = parser(0, tokens)
351 if index < len(tokens):
352 raise Exception('Unable to parse token {}'.format(tokens[index]))
357 raise Exception('Unable to parse')
360 return _parse(_program_parser, tokens)
362 if __name__ == '__main__':
367 class FurStringLiteralExpressionParserTests(unittest.TestCase):
368 def test_parses_single_quoted_string_literal(self):
370 _string_literal_expression_parser(0, tokenization.tokenize("'Hello, world'")),
374 FurStringLiteralExpression(value='Hello, world'),
378 class FurFunctionCallExpressionParserTests(unittest.TestCase):
379 def test_parses_function_with_string_literal_argument(self):
381 _function_call_expression_parser(0, tokenization.tokenize("print('Hello, world')")),
385 FurFunctionCallExpression(
387 arguments=(FurStringLiteralExpression(value='Hello, world'),),