From 2ca7824bedade35a08da5c037b8dc999a54e84f1 Mon Sep 17 00:00:00 2001 From: Mustafa Quraish Date: Sat, 5 Feb 2022 05:47:52 -0500 Subject: Allow function declarations without a definition --- src/ast.h | 2 ++ src/generator.c | 3 +++ src/parser.c | 50 ++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 41 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/ast.h b/src/ast.h index 074228c..70896ac 100644 --- a/src/ast.h +++ b/src/ast.h @@ -95,6 +95,8 @@ typedef struct ast_node { // TODO: Arguments / etc? Variable *args; int num_args; + + bool is_defined; } func; // Block of statements diff --git a/src/generator.c b/src/generator.c index 82c492b..d380f36 100644 --- a/src/generator.c +++ b/src/generator.c @@ -444,6 +444,9 @@ void generate_function_header(Node *func, FILE *out) void generate_function(Node *func, FILE *out) { assert(func->type == AST_FUNC); + // This will happen for declarations. + if (func->func.body == NULL) + return; current_function = func; generate_function_header(func, out); generate_block(func->func.body, out); diff --git a/src/parser.c b/src/parser.c index 9f66060..0294dc2 100644 --- a/src/parser.c +++ b/src/parser.c @@ -896,18 +896,34 @@ void parse_func_args(Lexer *lexer, Node *func) Node *parse_func(Lexer *lexer) { - Token token; - token = assert_token(Lexer_next(lexer), TOKEN_FN); - - token = assert_token(Lexer_next(lexer), TOKEN_IDENTIFIER); - if (identifier_exists(&token)) - die_location(token.loc, "Function name already exists as an identifier"); + assert_token(Lexer_next(lexer), TOKEN_FN); + Token token = assert_token(Lexer_next(lexer), TOKEN_IDENTIFIER); Node *func = Node_new(AST_FUNC); - push_new_function(func); + Node *dfunc = func; // The pointer to a declaration node + func->func.name = token.value.as_string; + // If the identifier exists, there's 3 possible cases: + // 1. It's another variable / struct, which is an error. + // 2. It's a function that's been defined, which is an error. + // 3. It's a function that's been declared (but not defined), which is OK + if (identifier_exists(&token)) { + dfunc = find_function_definition(&token); + // Case 1 + if (dfunc == NULL) + die_location(token.loc, "Function name already exists as an identifier"); + // Case 2 + if (dfunc->func.is_defined) + die_location(token.loc, "Function already defined earlier"); + + // Case 3 (No error, just set the current function correctly) + current_function = func; + } else { + // We don't have a declaration yet, push this. + push_new_function(func); + } - func->func.name = token.value.as_string; + // TODO: IMPORTANT: Make sure that args match the declaration, if any. parse_func_args(lexer, func); token = Lexer_peek(lexer); @@ -920,12 +936,18 @@ Node *parse_func(Lexer *lexer) func->func.return_type = type_new(TYPE_VOID); } - // Make sure there's no funny business with the stack offset - assert(cur_stack_offset == 0); - assert(block_stack_count == 0); - func->func.body = parse_block(lexer); - assert(block_stack_count == 0); - assert(cur_stack_offset == 0); + token = Lexer_peek(lexer); + if (token.type != TOKEN_SEMICOLON) { + // Make sure there's no funny business with the stack offset + assert(cur_stack_offset == 0); + assert(block_stack_count == 0); + func->func.body = parse_block(lexer); + assert(block_stack_count == 0); + assert(cur_stack_offset == 0); + + // Mark the declaration as defined. + dfunc->func.is_defined = true; + } // Reset current function current_function = NULL; -- cgit v1.2.3