From 6e7ba75736ca8b463a263e7d58aa8f6bff62faaf Mon Sep 17 00:00:00 2001 From: David Kerkeslager Date: Wed, 9 Aug 2017 11:34:44 -0400 Subject: [PATCH] Allow users to define new zero-argument functions --- examples/16_functions.fur | 5 +++ examples/16_functions.fur.output.txt | 1 + generation.py | 10 ++++- normalization.py | 38 +++++++++++++---- parsing.py | 62 +++++++++++++++++++++++++++- templates/program.c | 19 ++++++++- tokenization.py | 4 +- transformation.py | 49 ++++++++++++++++++---- 8 files changed, 165 insertions(+), 23 deletions(-) create mode 100644 examples/16_functions.fur create mode 100644 examples/16_functions.fur.output.txt diff --git a/examples/16_functions.fur b/examples/16_functions.fur new file mode 100644 index 0000000..5786384 --- /dev/null +++ b/examples/16_functions.fur @@ -0,0 +1,5 @@ +def get_answer() do + 42 +end + +print(get_answer()) diff --git a/examples/16_functions.fur.output.txt b/examples/16_functions.fur.output.txt new file mode 100644 index 0000000..f70d7bb --- /dev/null +++ b/examples/16_functions.fur.output.txt @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/generation.py b/generation.py index 6bc2384..c058f91 100644 --- a/generation.py +++ b/generation.py @@ -113,9 +113,13 @@ def generate_if_else_statement(statement): indent('\n'.join(generate_statement(s) for s in statement.else_statements)), ) +def generate_function_declaration(statement): + return 'Environment_set(environment, "{}", user${});'.format(statement.name, statement.name) + def generate_statement(statement): return { transformation.CExpressionStatement: generate_expression_statement, + transformation.CFunctionDeclaration: generate_function_declaration, transformation.CIfElseStatement: generate_if_else_statement, transformation.CSymbolAssignmentStatement: generate_symbol_assignment_statement, transformation.CArrayVariableInitializationStatement: generate_array_variable_initialization_statement, @@ -126,8 +130,10 @@ def generate_statement(statement): def generate(program): template = ENV.get_template('program.c') return template.render( - builtins=list(sorted(program.builtin_set)), - statements=[generate_statement(statement) for statement in program.statements], + builtins=tuple(sorted(program.builtin_set)), + function_definition_list=program.function_definition_list, + generate_statement=generate_statement, + statements=program.statements, standard_libraries=list(sorted(program.standard_libraries)), string_literal_list=program.string_literal_list, symbol_list=program.symbol_list, diff --git a/normalization.py b/normalization.py index a41eb8d..6f27b55 100644 --- a/normalization.py +++ b/normalization.py @@ -1,6 +1,7 @@ import collections import parsing +import util NormalVariableExpression = collections.namedtuple( 'NormalVariableExpression', @@ -75,6 +76,14 @@ NormalIfElseStatement = collections.namedtuple( ], ) +NormalFunctionDefinitionStatement = collections.namedtuple( + 'NormalFunctionDefinitionStatement', + [ + 'name', + 'statement_list', + ], +) + NormalProgram = collections.namedtuple( 'NormalProgram', [ @@ -82,6 +91,7 @@ NormalProgram = collections.namedtuple( ], ) +# TODO Get rid of this def fake_normalization(counter, thing): return (counter, (), thing) @@ -294,6 +304,7 @@ def normalize_expression(counter, expression): def normalize_expression_statement(counter, statement): counter, prestatements, normalized = { parsing.FurFunctionCallExpression: normalize_function_call_expression, + parsing.FurIntegerLiteralExpression: normalize_expression, }[type(statement.expression)](counter, statement.expression) return ( @@ -302,22 +313,35 @@ def normalize_expression_statement(counter, statement): NormalExpressionStatement(expression=normalized), ) +def normalize_function_definition_statement(counter, statement): + return ( + counter, + (), + NormalFunctionDefinitionStatement( + name=statement.name, + statement_list=normalize_statement_list(statement.statement_list), + ), + ) + def normalize_statement(counter, statement): return { + parsing.FurAssignmentStatement: fake_normalization, # TODO unfake this parsing.FurExpressionStatement: normalize_expression_statement, - parsing.FurAssignmentStatement: fake_normalization, + parsing.FurFunctionDefinitionStatement: normalize_function_definition_statement, }[type(statement)](counter, statement) -def normalize(program): +@util.force_generator(tuple) +def normalize_statement_list(statement_list): counter = 0 - statement_list = [] - for statement in program.statement_list: + for statement in statement_list: counter, prestatements, normalized = normalize_statement(counter, statement) for s in prestatements: - statement_list.append(s) - statement_list.append(normalized) + yield s + yield normalized + +def normalize(program): return NormalProgram( - statement_list=statement_list, + statement_list=normalize_statement_list(program.statement_list), ) diff --git a/parsing.py b/parsing.py index adc96e9..041f3aa 100644 --- a/parsing.py +++ b/parsing.py @@ -213,7 +213,7 @@ def _or_level_expression_parser(index, tokens): )(index, tokens) def _comma_separated_list_parser(index, tokens): - failure = (False, index, None) + start_index = index expressions = [] @@ -222,7 +222,7 @@ def _comma_separated_list_parser(index, tokens): if success: expressions.append(expression) else: - return failure + return (True, start_index, ()) while success and index < len(tokens) and tokens[index].type == 'comma': success = False @@ -260,6 +260,14 @@ FurAssignmentStatement = collections.namedtuple( ], ) +FurFunctionDefinitionStatement = collections.namedtuple( + 'FurFunctionDefinitionStatement', + [ + 'name', + 'statement_list', + ], +) + FurProgram = collections.namedtuple( 'FurProgram', [ @@ -330,6 +338,55 @@ 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, + )) + + 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, statement_list=statement_list) + def _statement_parser(index, tokens): _, index, _ = consume_newlines(index, tokens) @@ -339,6 +396,7 @@ def _statement_parser(index, tokens): return _or_parser( _assignment_statement_parser, _expression_statement_parser, + _function_definition_statement_parser, )(index, tokens) def _program_formatter(statement_list): diff --git a/templates/program.c b/templates/program.c index 0d6d947..73d9334 100644 --- a/templates/program.c +++ b/templates/program.c @@ -326,6 +326,23 @@ Object builtin$print$implementation(size_t argc, Object* args) Object builtin$print = { CLOSURE, (Instance)builtin$print$implementation }; {% endif %} +{% for function_definition in function_definition_list %} +Object user${{function_definition.name}}$implementation(size_t argc, Object* args) +{ + Environment* environment = Environment_construct(); + + {% for statement in function_definition.statement_list %} + {{ generate_statement(statement) }} + {% endfor %} + + Object result = {{ generate_statement(function_definition.statement_list[-1]) }} + Environment_destruct(environment); + return result; +} + +Object user${{function_definition.name}} = { CLOSURE, (Instance)user${{function_definition.name}}$implementation }; +{% endfor %} + int main(int argc, char** argv) { Environment* environment = Environment_construct(); @@ -336,7 +353,7 @@ int main(int argc, char** argv) {% endfor %} {% for statement in statements %} - {{ statement }} + {{ generate_statement(statement) }} {% endfor %} Environment_destruct(environment); diff --git a/tokenization.py b/tokenization.py index a736912..1a2653e 100644 --- a/tokenization.py +++ b/tokenization.py @@ -31,13 +31,13 @@ def _make_token_matcher(definition): return token_matcher - _TOKEN_MATCHERS = [ + ('keyword', r'(def|end)(?![a-z_])'), ('open_parenthese', r'\('), ('close_parenthese', r'\)'), ('comma', r','), ('integer_literal', r'\d+'), - ('symbol', r'[a-z]+'), + ('symbol', r'[a-z_]+'), ('single_quoted_string_literal', r"'.*?'"), ('comparison_level_operator', r'(<=|>=|==|!=|<|>)'), ('assignment_operator', r'='), diff --git a/transformation.py b/transformation.py index b636286..2825257 100644 --- a/transformation.py +++ b/transformation.py @@ -114,10 +114,26 @@ CIfElseStatement = collections.namedtuple( ], ) +CFunctionDeclaration = collections.namedtuple( + 'CFunctionDeclaration', + [ + 'name', + ], +) + +CFunctionDefinition = collections.namedtuple( + 'CFunctionDefinition', + [ + 'name', + 'statement_list', + ], +) + CProgram = collections.namedtuple( 'CProgram', [ 'builtin_set', + 'function_definition_list', 'statements', 'standard_libraries', 'string_literal_list', @@ -270,18 +286,17 @@ def transform_function_call_expression(accumulators, function_call): # TODO Check that the builtin is actually callable accumulators.builtin_set.add(function_call.function.value) - # TODO Use the symbol from SYMBOL LIST - return CFunctionCallExpression( - name=function_call.function.value, - argument_count=function_call.argument_count, - argument_items=transform_expression(accumulators, function_call.argument_items), - ) - - raise Exception() + # TODO Use the symbol from SYMBOL LIST + return CFunctionCallExpression( + name=function_call.function.value, + argument_count=function_call.argument_count, + argument_items=transform_expression(accumulators, function_call.argument_items), + ) def transform_expression_statement(accumulators, statement): expression = { parsing.FurFunctionCallExpression: transform_function_call_expression, + parsing.FurIntegerLiteralExpression: transform_expression, normalization.NormalFunctionCallExpression: transform_function_call_expression, }[type(statement.expression)](accumulators, statement.expression) @@ -314,13 +329,26 @@ def transform_variable_reassignment_statement(accumulators, statement): expression=transform_expression(accumulators, statement.expression), ) +def transform_function_definition_statement(accumulators, statement): + # TODO Allow defining the same function in different contexts + if any(fd.name == statement.name for fd in accumulators.function_definition_list): + raise Exception('A function with name "{}" already exists'.format(statement.name)) + + accumulators.function_definition_list.append(CFunctionDefinition( + name=statement.name, + statement_list=tuple(transform_statement(accumulators, s) for s in statement.statement_list) + )) + + return CFunctionDeclaration(name=statement.name) + def transform_statement(accumulators, statement): return { parsing.FurAssignmentStatement: transform_symbol_assignment_statement, parsing.FurExpressionStatement: transform_expression_statement, + normalization.NormalArrayVariableInitializationStatement: transform_array_variable_initialization_statement, normalization.NormalExpressionStatement: transform_expression_statement, + normalization.NormalFunctionDefinitionStatement: transform_function_definition_statement, normalization.NormalIfElseStatement: transform_if_else_statement, - normalization.NormalArrayVariableInitializationStatement: transform_array_variable_initialization_statement, normalization.NormalVariableInitializationStatement: transform_variable_initialization_statement, normalization.NormalVariableReassignmentStatement: transform_variable_reassignment_statement, }[type(statement)](accumulators, statement) @@ -330,6 +358,7 @@ Accumulators = collections.namedtuple( 'Accumulators', [ 'builtin_set', + 'function_definition_list', 'symbol_list', 'string_literal_list', ], @@ -338,6 +367,7 @@ Accumulators = collections.namedtuple( def transform(program): accumulators = Accumulators( builtin_set=set(), + function_definition_list=[], symbol_list=[], string_literal_list=[], ) @@ -353,6 +383,7 @@ def transform(program): return CProgram( builtin_set=accumulators.builtin_set, + function_definition_list=accumulators.function_definition_list, statements=statement_list, standard_libraries=standard_library_set, string_literal_list=accumulators.string_literal_list, -- 2.20.1