/* * Procedure parser. * Copyright (c) 2023-2024, Quinn Stephens and the OSMORA team. * Provided under the BSD 3-Clause license. */ #include #include "debug.h" #include "parser/proc.h" #include "parser/stmt.h" #include "parser/type.h" #include "parser.h" #define HASHMAP_ROWS 8 static bool parse_params(struct parser *ctx, struct procedure *proc) { struct parameter *param; struct variable *var; struct type *typ; int n_ptrs; debug("Parsing procedure parameters...\n"); while (ctx->tok.kind != TK_RPAREN) { if (ctx->tok.kind != TK_IDENTIFIER) { tok_error(&ctx->tok, "expected parameter type name or \")\"\n"); return false; } if (!parse_type_ref(ctx, &typ, &n_ptrs)) { return false; } if (ctx->tok.kind != TK_IDENTIFIER) { tok_error(&ctx->tok, "expected parameter name\n"); return false; } param = malloc(sizeof(struct parameter)); var = ¶m->var; var->hashmap_entry.hash = ctx->tok.hash; var->name = ctx->tok.pos; var->name_len = ctx->tok.len; var->typ = typ; var->n_ptrs = n_ptrs; hashmap_add(&proc->vars, &var->hashmap_entry); list_append(&proc->params, ¶m->list_entry); if (next_token(ctx)->kind == TK_COMMA && next_token(ctx)->kind == TK_RPAREN) { tok_warn(&ctx->tok, "extra \",\" at end of parameter list\n"); } } next_token(ctx); return true; } void parse_proc(struct parser *ctx) { struct procedure *proc; debug("Parsing procedure declaration...\n"); /* Procedure name */ if (next_token(ctx)->kind != TK_IDENTIFIER) { tok_error(&ctx->tok, "expected identifier after \"proc\"\n"); return; } /* Ensure procedure does not already exist */ proc = (struct procedure*)hashmap_find(ctx->procs, ctx->tok.hash); if (proc != NULL) { tok_error(&ctx->tok, "procedure \"%.*s\" already declared\n", (int)ctx->tok.len, ctx->tok.pos); return; } /* Create procedure */ proc = malloc(sizeof(struct procedure)); proc->hashmap_entry.hash = ctx->tok.hash; proc->name = ctx->tok.pos; proc->name_len = ctx->tok.len; if (next_token(ctx)->kind != TK_LPAREN) { tok_error(&ctx->tok, "expected \"(\" after procedure name\n"); free(proc); return; } /* Set up variables hashmap */ proc->vars.rows = malloc(HASHMAP_ROWS * sizeof(struct list)); proc->vars.n_rows = HASHMAP_ROWS; hashmap_init(&proc->vars); /* Set up parameter list */ list_init(&proc->params); /* Parse parameters */ if (next_token(ctx)->kind != TK_RPAREN) { if (!parse_params(ctx, proc)) { hashmap_free_entries(&proc->vars); free(proc->vars.rows); free(proc); return; } } else { next_token(ctx); } /* Parse return type if specified */ if (ctx->tok.kind == TK_ARROW) { next_token(ctx); if (!parse_type_ref(ctx, &proc->ret_typ, &proc->ret_n_ptrs)) { hashmap_free_entries(&proc->vars); free(proc->vars.rows); free(proc); return; } } /* We are finished now if this is just a declaration */ if (ctx->tok.kind == TK_SEMICOLON) { proc->node = NULL; /* Add procedure to parser's registry */ hashmap_add(ctx->procs, &proc->hashmap_entry); next_token(ctx); return; } if (ctx->tok.kind != TK_LBRACE) { proc->node = NULL; tok_error(&ctx->tok, "Expected \";\" or \"{\"\n"); return; } /* Allocate AST root node for body */ proc->node = ast_new_node(NK_PROCEDURE); /* Parse procedure body (code) */ parse_stmt_block(ctx, proc->node, proc); /* Add procedure to parser's registry */ hashmap_add(ctx->procs, &proc->hashmap_entry); }