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 FurInfixExpression = collections.namedtuple(
78 def _integer_literal_expression_parser(index, tokens):
79 failure = (False, index, None)
81 if tokens[index].type != 'integer_literal':
83 value = int(tokens[index].match)
86 return True, index, FurIntegerLiteralExpression(value=value)
88 def _string_literal_expression_parser(index, tokens):
89 if tokens[index].type == 'single_quoted_string_literal':
90 return (True, index + 1, FurStringLiteralExpression(value=tokens[index].match[1:-1]))
92 return (False, index, None)
94 def _symbol_expression_parser(index, tokens):
95 if tokens[index].type == 'symbol':
96 return (True, index + 1, FurSymbolExpression(value=tokens[index].match))
98 return (False, index, None)
100 def _parenthesized_expression_parser(index, tokens):
101 failure = (False, index, None)
103 if tokens[index].type == 'open_parenthese':
108 success, index, internal = _expression_parser(index, tokens)
112 if tokens[index].type == 'close_parenthese':
115 raise Exception('Expected ")" on line {}, found "{}"'.format(
120 return True, index, FurParenthesizedExpression(internal=internal)
122 def _negation_expression_parser(index, tokens):
123 failure = (False, index, None)
125 if tokens[index].match != '-':
128 success, index, value = _literal_level_expression_parser(index + 1, tokens)
133 return (True, index, FurNegationExpression(value=value))
135 def _literal_level_expression_parser(index, tokens):
137 _negation_expression_parser,
138 _function_call_expression_parser,
139 _parenthesized_expression_parser,
140 _integer_literal_expression_parser,
141 _string_literal_expression_parser,
142 _symbol_expression_parser,
145 def _left_recursive_infix_operator_parser(token_type, operand_parser, order):
146 def result_parser(index, tokens):
147 failure = (False, index, None)
149 success, index, result = operand_parser(index, tokens)
154 while success and index < len(tokens) and tokens[index].type == token_type:
157 if index + 1 < len(tokens):
158 success, try_index, value = operand_parser(index + 1, tokens)
161 result = FurInfixExpression(
163 operator=tokens[index].match,
169 return True, index, result
173 def _multiplication_level_expression_parser(index, tokens):
174 return _left_recursive_infix_operator_parser(
175 'multiplication_level_operator',
176 _literal_level_expression_parser,
177 'multiplication_level',
180 def _addition_level_expression_parser(index, tokens):
181 return _left_recursive_infix_operator_parser(
182 'addition_level_operator',
183 _multiplication_level_expression_parser,
187 def _equality_level_expression_parser(index, tokens):
188 return _left_recursive_infix_operator_parser(
189 'equality_level_operator',
190 _addition_level_expression_parser,
194 def _comma_separated_list_parser(index, tokens):
195 failure = (False, index, None)
199 success, index, expression = _expression_parser(index, tokens)
202 expressions.append(expression)
206 while success and index < len(tokens) and tokens[index].type == 'comma':
209 if index + 1 < len(tokens):
210 success, try_index, expression = _expression_parser(index + 1, tokens)
213 expressions.append(expression)
216 return True, index, tuple(expressions)
219 FurFunctionCallExpression = collections.namedtuple(
220 'FurFunctionCallExpression',
227 FurAssignmentStatement = collections.namedtuple(
228 'FurAssignmentStatement',
235 FurProgram = collections.namedtuple(
242 def _function_call_expression_parser(index, tokens):
243 # TODO Use a FurSymbolExpression for the name
244 failure = (False, index, None)
246 success, index, function = _symbol_expression_parser(index, tokens)
251 if tokens[index].type != 'open_parenthese':
255 success, index, arguments = _comma_separated_list_parser(index, tokens)
260 if tokens[index].type != 'close_parenthese':
261 raise Exception('Expected ")", found "{}" on line {}'.format(
267 return True, index, FurFunctionCallExpression(function=function, arguments=arguments)
269 _expression_parser = _equality_level_expression_parser
271 def _assignment_statement_parser(index, tokens):
272 # TODO Use a FurSymbolExpression for the target? Maybe this is actually not a good idea
273 failure = (False, index, None)
275 if tokens[index].type != 'symbol':
277 target = tokens[index].match
280 if tokens[index].type != 'assignment_operator':
282 assignment_operator_index = index
284 success, index, expression = _expression_parser(index + 1, tokens)
288 'Expected expression after assignment operator on line {}'.format(
289 tokens[assignment_operator_index].line
293 return True, index, FurAssignmentStatement(target=target, expression=expression)
295 def _statement_parser(index, tokens):
296 # 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)
298 _assignment_statement_parser,
302 def _program_formatter(statement_list):
303 return FurProgram(statement_list=statement_list)
305 _program_parser = _zero_or_more_parser(_program_formatter, _statement_parser)
307 def _parse(parser, tokens):
308 success, index, result = parser(0, tokens)
310 if index < len(tokens):
311 raise Exception('Unable to parse token {}'.format(tokens[index]))
316 raise Exception('Unable to parse')
319 return _parse(_program_parser, tokens)
321 if __name__ == '__main__':
326 class FurStringLiteralExpressionParserTests(unittest.TestCase):
327 def test_parses_single_quoted_string_literal(self):
329 _string_literal_expression_parser(0, tokenization.tokenize("'Hello, world'")),
333 FurStringLiteralExpression(value='Hello, world'),
337 class FurFunctionCallExpressionParserTests(unittest.TestCase):
338 def test_parses_function_with_string_literal_argument(self):
340 _function_call_expression_parser(0, tokenization.tokenize("print('Hello, world')")),
344 FurFunctionCallExpression(
346 arguments=(FurStringLiteralExpression(value='Hello, world'),),