aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMustafa Quraish <[email protected]>2022-02-05 05:47:52 -0500
committerMustafa Quraish <[email protected]>2022-02-05 08:56:15 -0500
commit2ca7824bedade35a08da5c037b8dc999a54e84f1 (patch)
tree281421602911828b060f43a540862517c19370b3
parentHandle command-line arguments properly on linux (diff)
downloadcup-2ca7824bedade35a08da5c037b8dc999a54e84f1.tar.xz
cup-2ca7824bedade35a08da5c037b8dc999a54e84f1.zip
Allow function declarations without a definition
-rw-r--r--src/ast.h2
-rw-r--r--src/generator.c3
-rw-r--r--src/parser.c50
3 files changed, 41 insertions, 14 deletions
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;