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(operator_token_matcher, 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 operator_token_matcher(tokens[index]):
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 lambda token: token.type == '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 lambda token: token.type == 'addition_level_operator',
183 _multiplication_level_expression_parser,
187 def _equality_level_expression_parser(index, tokens):
188 return _left_recursive_infix_operator_parser(
189 lambda token: token.type == 'equality_level_operator',
190 _addition_level_expression_parser,
194 def _and_level_expression_parser(index, tokens):
195 return _left_recursive_infix_operator_parser(
196 lambda token: token.type == 'symbol' and token.match == 'and',
197 _equality_level_expression_parser,
201 def _or_level_expression_parser(index, tokens):
202 return _left_recursive_infix_operator_parser(
203 lambda token: token.type == 'symbol' and token.match == 'or',
204 _and_level_expression_parser,
208 def _comma_separated_list_parser(index, tokens):
209 failure = (False, index, None)
213 success, index, expression = _expression_parser(index, tokens)
216 expressions.append(expression)
220 while success and index < len(tokens) and tokens[index].type == 'comma':
223 if index + 1 < len(tokens):
224 success, try_index, expression = _expression_parser(index + 1, tokens)
227 expressions.append(expression)
230 return True, index, tuple(expressions)
233 FurFunctionCallExpression = collections.namedtuple(
234 'FurFunctionCallExpression',
241 FurAssignmentStatement = collections.namedtuple(
242 'FurAssignmentStatement',
249 FurProgram = collections.namedtuple(
256 def _function_call_expression_parser(index, tokens):
257 # TODO Use a FurSymbolExpression for the name
258 failure = (False, index, None)
260 success, index, function = _symbol_expression_parser(index, tokens)
265 if tokens[index].type != 'open_parenthese':
269 success, index, arguments = _comma_separated_list_parser(index, tokens)
274 if tokens[index].type != 'close_parenthese':
275 raise Exception('Expected ")", found "{}" on line {}'.format(
281 return True, index, FurFunctionCallExpression(function=function, arguments=arguments)
283 _expression_parser = _or_level_expression_parser
285 def _assignment_statement_parser(index, tokens):
286 # TODO Use a FurSymbolExpression for the target? Maybe this is actually not a good idea
287 failure = (False, index, None)
289 if tokens[index].type != 'symbol':
291 target = tokens[index].match
294 if tokens[index].type != 'assignment_operator':
296 assignment_operator_index = index
298 success, index, expression = _expression_parser(index + 1, tokens)
302 'Expected expression after assignment operator on line {}'.format(
303 tokens[assignment_operator_index].line
307 return True, index, FurAssignmentStatement(target=target, expression=expression)
309 def _statement_parser(index, tokens):
310 # 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)
312 _assignment_statement_parser,
316 def _program_formatter(statement_list):
317 return FurProgram(statement_list=statement_list)
319 _program_parser = _zero_or_more_parser(_program_formatter, _statement_parser)
321 def _parse(parser, tokens):
322 success, index, result = parser(0, tokens)
324 if index < len(tokens):
325 raise Exception('Unable to parse token {}'.format(tokens[index]))
330 raise Exception('Unable to parse')
333 return _parse(_program_parser, tokens)
335 if __name__ == '__main__':
340 class FurStringLiteralExpressionParserTests(unittest.TestCase):
341 def test_parses_single_quoted_string_literal(self):
343 _string_literal_expression_parser(0, tokenization.tokenize("'Hello, world'")),
347 FurStringLiteralExpression(value='Hello, world'),
351 class FurFunctionCallExpressionParserTests(unittest.TestCase):
352 def test_parses_function_with_string_literal_argument(self):
354 _function_call_expression_parser(0, tokenization.tokenize("print('Hello, world')")),
358 FurFunctionCallExpression(
360 arguments=(FurStringLiteralExpression(value='Hello, world'),),