X-Git-Url: https://code.kerkeslager.com/?a=blobdiff_plain;f=parsing.py;h=6734af7c4ddb226e729162a06a6378910f83ff07;hb=d6af7d074bf65e782e42055623a197863b5f8000;hp=5a88fdcc472c5c77265d617342fec8b5a925ddea;hpb=eab88b322191c40168553b8671b968d1b1558084;p=fur diff --git a/parsing.py b/parsing.py index 5a88fdc..6734af7 100644 --- a/parsing.py +++ b/parsing.py @@ -1,5 +1,11 @@ import collections +def consume_newlines(index, tokens): + while index < len(tokens) and tokens[index].type == 'newline': + index += 1 + + return True, index, None + def _or_parser(*parsers): def result_parser(index, tokens): failure = (False, index, None) @@ -58,6 +64,7 @@ FurNegationExpression = collections.namedtuple( ], ) +# TODO We don't need to wrap this type FurParenthesizedExpression = collections.namedtuple( 'FurParenthesizedExpression', [ @@ -184,17 +191,17 @@ def _addition_level_expression_parser(index, tokens): 'addition_level', )(index, tokens) -def _equality_level_expression_parser(index, tokens): +def _comparison_level_expression_parser(index, tokens): return _left_recursive_infix_operator_parser( - lambda token: token.type == 'equality_level_operator', + lambda token: token.type == 'comparison_level_operator', _addition_level_expression_parser, - 'equality_level', + 'comparison_level', )(index, tokens) def _and_level_expression_parser(index, tokens): return _left_recursive_infix_operator_parser( lambda token: token.type == 'symbol' and token.match == 'and', - _equality_level_expression_parser, + _comparison_level_expression_parser, 'and_level', )(index, tokens) @@ -205,30 +212,35 @@ def _or_level_expression_parser(index, tokens): 'or_level', )(index, tokens) -def _comma_separated_list_parser(index, tokens): - failure = (False, index, None) +def _comma_separated_list_parser(subparser): + def result_parser(index, tokens): + start_index = index - expressions = [] + items = [] - success, index, expression = _expression_parser(index, tokens) + success, index, item = subparser(index, tokens) - if success: - expressions.append(expression) - else: - return failure + if success: + items.append(item) + else: + return (True, start_index, ()) - while success and index < len(tokens) and tokens[index].type == 'comma': - success = False + while success and index < len(tokens) and tokens[index].type == 'comma': + success = False - if index + 1 < len(tokens): - success, try_index, expression = _expression_parser(index + 1, tokens) + if index + 1 < len(tokens): + success, try_index, item = subparser(index + 1, tokens) - if success: - expressions.append(expression) - index = try_index + if success: + items.append(item) + index = try_index - return True, index, tuple(expressions) + return True, index, tuple(items) + return result_parser + +def _comma_separated_expression_list_parser(index, tokens): + return _comma_separated_list_parser(_expression_parser)(index, tokens) FurFunctionCallExpression = collections.namedtuple( 'FurFunctionCallExpression', @@ -238,6 +250,13 @@ FurFunctionCallExpression = collections.namedtuple( ], ) +FurExpressionStatement = collections.namedtuple( + 'FurExpressionStatement', + [ + 'expression', + ], +) + FurAssignmentStatement = collections.namedtuple( 'FurAssignmentStatement', [ @@ -246,6 +265,15 @@ FurAssignmentStatement = collections.namedtuple( ], ) +FurFunctionDefinitionStatement = collections.namedtuple( + 'FurFunctionDefinitionStatement', + [ + 'name', + 'argument_name_list', + 'statement_list', + ], +) + FurProgram = collections.namedtuple( 'FurProgram', [ @@ -266,7 +294,7 @@ def _function_call_expression_parser(index, tokens): return failure index += 1 - success, index, arguments = _comma_separated_list_parser(index, tokens) + success, index, arguments = _comma_separated_expression_list_parser(index, tokens) if not success: return failure @@ -282,6 +310,16 @@ def _function_call_expression_parser(index, tokens): _expression_parser = _or_level_expression_parser +def _expression_statement_parser(index, tokens): + failure = (False, index, None) + + success, index, expression = _expression_parser(index, tokens) + + if not success: + return failure + + return (True, index, FurExpressionStatement(expression=expression)) + def _assignment_statement_parser(index, tokens): # TODO Use a FurSymbolExpression for the target? Maybe this is actually not a good idea failure = (False, index, None) @@ -306,11 +344,74 @@ def _assignment_statement_parser(index, tokens): return True, index, FurAssignmentStatement(target=target, expression=expression) +def _function_definition_statement_parser(index, tokens): + failure = (False, index, None) + + if tokens[index].type == 'keyword' and tokens[index].match == 'def': + index += 1 + else: + return failure + + if tokens[index].type == 'symbol': + name = tokens[index].match + index += 1 + else: + raise Exception('Expected function name, found "{}" on line {}'.format( + tokens[index].match, + tokens[index].line, + )) + + if tokens[index].type == 'open_parenthese': + index += 1 + else: + raise Exception('Expected "(", found "{}" on line {}'.format( + tokens[index].match, + tokens[index].line, + )) + + success, index, argument_name_list = _comma_separated_list_parser(_symbol_expression_parser)( + index, + tokens, + ) + + if tokens[index].type == 'close_parenthese': + index += 1 + else: + raise Exception('Expected ")", found "{}" on line {}'.format( + tokens[index].match, + tokens[index].line, + )) + + if tokens[index].type == 'symbol' and tokens[index].match == 'do': + index += 1 + else: + return failure + + success, index, statement_list = _zero_or_more_parser(tuple, _statement_parser)(index, tokens) + + _, index, _ = consume_newlines(index, tokens) + + if tokens[index].type == 'keyword' and tokens[index].match == 'end': + index += 1 + else: + return failure + + return True, index, FurFunctionDefinitionStatement( + name=name, + argument_name_list=tuple(an.value for an in argument_name_list), + statement_list=statement_list, + ) + def _statement_parser(index, tokens): - # 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) + _, index, _ = consume_newlines(index, tokens) + + if index == len(tokens): + return (False, index, None) + return _or_parser( _assignment_statement_parser, - _expression_parser, + _expression_statement_parser, + _function_definition_statement_parser, )(index, tokens) def _program_formatter(statement_list):