From 063c40584ae78a396b558a5e2a08e3d871450c0b Mon Sep 17 00:00:00 2001 From: Quinn Stephens Date: Sun, 3 Nov 2024 19:12:47 -0500 Subject: [compiler] Parse and print procedure declarations Signed-off-by: Quinn Stephens --- Makefile | 2 +- compiler/hashmap.c | 10 ++-- compiler/lexer/keywords.c | 1 + compiler/main.c | 113 ++++++++++++++++++++++++++---------- compiler/parser/parser.c | 4 ++ compiler/parser/proc.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++ include/lexer/token.h | 1 + include/parser/proc.h | 34 +++++++++++ include/parser/var.h | 24 ++++++++ test.q | 2 + 10 files changed, 299 insertions(+), 36 deletions(-) create mode 100644 compiler/parser/proc.c create mode 100644 include/parser/proc.h create mode 100644 include/parser/var.h diff --git a/Makefile b/Makefile index b758a0a..5dfb0a2 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ CC = clang LD = ld.ldd EXENAME = quarkc -OFILES = $(addprefix compiler/,debug.o hash.o hashmap.o lexer/char_info.o lexer/keywords.o lexer/lexer.o parser/type.o parser/parser.o main.o) +OFILES = $(addprefix compiler/,debug.o hash.o hashmap.o lexer/char_info.o lexer/keywords.o lexer/lexer.o parser/type.o parser/proc.o parser/parser.o main.o) CFLAGS = -Wall -Wextra -Iinclude LDFLAGS = diff --git a/compiler/hashmap.c b/compiler/hashmap.c index e5aed7b..e567224 100644 --- a/compiler/hashmap.c +++ b/compiler/hashmap.c @@ -17,16 +17,16 @@ hashmap_add(struct hashmap *map, struct hashmap_entry *entry) struct hashmap_entry * hashmap_find(struct hashmap *map, hash_t hash) { - struct hashmap_entry *head, *entry; + struct hashmap_entry *entry; - entry = head = (struct hashmap_entry*)(map->rows[hash % map->n_rows].head); - do { + entry = (struct hashmap_entry*)map->rows[hash % map->n_rows].head; + while (entry != (struct hashmap_entry*)&map->rows[hash % map->n_rows]) { if (entry->hash == hash) { return entry; } - entry = (struct hashmap_entry*)(entry->list_entry.next); - } while (entry != head); + entry = (struct hashmap_entry*)entry->list_entry.next; + } return NULL; } diff --git a/compiler/lexer/keywords.c b/compiler/lexer/keywords.c index 0e95048..88c2e67 100644 --- a/compiler/lexer/keywords.c +++ b/compiler/lexer/keywords.c @@ -65,6 +65,7 @@ keywords_init(void) keywords.n_rows = HASHMAP_ROWS; hashmap_init(&keywords); + add_keyword("proc", TK_PROC); add_keyword("type", TK_TYPE); add_keyword("enum", TK_ENUM); add_keyword("struct", TK_STRUCT); diff --git a/compiler/main.c b/compiler/main.c index 3666859..e614be6 100644 --- a/compiler/main.c +++ b/compiler/main.c @@ -11,6 +11,7 @@ #include #include "debug.h" #include "hashmap.h" +#include "parser/proc.h" #include "parser/type.h" #include "parser.h" @@ -196,15 +197,11 @@ print_enum(struct type *typ) for (size_t r = 0; r < typ->members.n_rows; r++) { mem = (struct enum_member*)typ->members.rows[r].head; - if (mem == (struct enum_member*)&typ->members.rows[r]) { - continue; - } - - do { + while (mem != (struct enum_member*)&typ->members.rows[r]) { printf(" %.*s,\n", (int)mem->name_len, mem->name); mem = (struct enum_member*)mem->hashmap_entry.list_entry.next; - } while (mem != (struct enum_member*)&typ->members.rows[r]); + } } printf("}\n"); @@ -219,17 +216,13 @@ print_struct(struct type *typ) for (size_t r = 0; r < typ->members.n_rows; r++) { mem = (struct struct_member*)typ->members.rows[r].head; - if (mem == (struct struct_member*)&typ->members.rows[r]) { - continue; - } - - do { - printf(" %.*s", (int)mem->typ->name_len, mem->typ->name); + while (mem != (struct struct_member*)&typ->members.rows[r]) { + printf(" %.*s ", (int)mem->typ->name_len, mem->typ->name); print_ptrs(mem->n_ptrs); - printf(" %.*s;\n", (int)mem->name_len, mem->name); + printf("%.*s;\n", (int)mem->name_len, mem->name); mem = (struct struct_member*)mem->hashmap_entry.list_entry.next; - } while (mem != (struct struct_member*)&typ->members.rows[r]); + } } printf("}\n"); @@ -251,11 +244,84 @@ print_type(struct type *typ) print_struct(typ); break; default: - printf("(unknown);\n"); + printf("unknown;\n"); break; } } +static void +print_proc(struct procedure *proc) +{ + struct list_entry *list_entry; + struct parameter *param; + int p; + + printf("proc %.*s(", (int)proc->name_len, proc->name); + + list_entry = proc->params.head; + p = 0; + while (list_entry != (struct list_entry*)&proc->params) { + param = (struct parameter*)((uint8_t*)list_entry - __builtin_offsetof(struct parameter, list_entry)); + + if (p) { + printf(", "); + } + printf("%.*s ", (int)param->var.typ->name_len, param->var.typ->name); + print_ptrs(param->var.n_ptrs); + printf("%.*s", (int)param->var.name_len, param->var.name); + + list_entry = list_entry->next; + p++; + } + + printf(")"); + + if (proc->ret_typ != NULL) { + printf(" -> %.*s", (int)proc->ret_typ->name_len, proc->ret_typ->name); + print_ptrs(proc->ret_n_ptrs); + } + + printf(";\n"); +} + +static void +dump_types(struct hashmap *map) +{ + struct type *typ, *next; + + for (size_t r = 0; r < map->n_rows; r++) { + typ = (struct type*)map->rows[r].head; + while (typ != (struct type*)&map->rows[r]) { + print_type(typ); + + next = (struct type*)typ->hashmap_entry.list_entry.next; + free(typ); + typ = next; + } + } + + free(map->rows); +} + +static void +dump_procs(struct hashmap *map) +{ + struct procedure *proc, *next; + + for (size_t r = 0; r < map->n_rows; r++) { + proc = (struct procedure*)map->rows[r].head; + while (proc != (struct procedure*)&map->rows[r]) { + print_proc(proc); + + next = (struct procedure*)proc->hashmap_entry.list_entry.next; + free(proc); + proc = next; + } + } + + free(map->rows); +} + int main(int argc, char **argv) { @@ -263,7 +329,6 @@ main(int argc, char **argv) struct parser parser; struct list *types_rows, *procs_rows; struct hashmap types, procs; - struct type *typ; argv0 = argv[0]; if (argc < 2) { @@ -297,21 +362,9 @@ main(int argc, char **argv) parser_init(&parser, input); parser_parse(&parser); - /* Print parsed types */ - for (size_t r = 0; r < types.n_rows; r++) { - typ = (struct type*)types.rows[r].head; - if (typ == (struct type*)&types.rows[r]) { - continue; - } - - do { - print_type(typ); - typ = (struct type*)typ->hashmap_entry.list_entry.next; - } while (typ != (struct type*)&types.rows[r]); - } + dump_types(parser.types); + dump_procs(parser.procs); - free(procs_rows); - free(types_rows); free(input); return 0; } diff --git a/compiler/parser/parser.c b/compiler/parser/parser.c index a630e37..7ee3270 100644 --- a/compiler/parser/parser.c +++ b/compiler/parser/parser.c @@ -12,6 +12,7 @@ #include "debug.h" #include "hashmap.h" #include "parser/type.h" +#include "parser/proc.h" #include "parser.h" static void @@ -62,6 +63,9 @@ parser_parse(struct parser *ctx) next_token(ctx); while (ctx->tok.kind != TK_EOF) { switch (ctx->tok.kind) { + case TK_PROC: + parse_proc(ctx); + break; case TK_TYPE: parse_type(ctx); break; diff --git a/compiler/parser/proc.c b/compiler/parser/proc.c new file mode 100644 index 0000000..bbbdad1 --- /dev/null +++ b/compiler/parser/proc.c @@ -0,0 +1,144 @@ +/* + * 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/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; + } + } + + /* Add procedure to parser's registry */ + hashmap_add(ctx->procs, &proc->hashmap_entry); + + /* We are finished now if this is just a declaration */ + if (ctx->tok.kind == TK_SEMICOLON) { + next_token(ctx); + return; + } + + if (ctx->tok.kind != TK_LBRACE) { + tok_error(&ctx->tok, "Expected \";\" or \"{\"\n"); + return; + } + + /* TODO: Parse body/code */ + + if (next_token(ctx)->kind != TK_RBRACE) { + tok_error(&ctx->tok, "Expected \"}\" after procedure body\n"); + return; + } + + next_token(ctx); +} diff --git a/include/lexer/token.h b/include/lexer/token.h index e0a9ea3..2ba5408 100644 --- a/include/lexer/token.h +++ b/include/lexer/token.h @@ -21,6 +21,7 @@ typedef enum { TK_CHARACTER, /* Keywords */ + TK_PROC, TK_TYPE, TK_ENUM, TK_STRUCT, diff --git a/include/parser/proc.h b/include/parser/proc.h new file mode 100644 index 0000000..0cc8471 --- /dev/null +++ b/include/parser/proc.h @@ -0,0 +1,34 @@ +/* + * Procedure parser. + * Copyright (c) 2023-2024, Quinn Stephens and the OSMORA team. + * Provided under the BSD 3-Clause license. + */ + +#ifndef _PARSER_PROC_H +#define _PARSER_PROC_H + +#include "list.h" +#include "hashmap.h" +#include "parser/var.h" + +struct parameter { + struct variable var; + struct list_entry list_entry; +}; + +struct procedure { + struct hashmap_entry hashmap_entry; + + char *name; + size_t name_len; + + struct hashmap vars; + struct list params; + + struct type *ret_typ; + int ret_n_ptrs; +}; + +void parse_proc(struct parser *ctx); + +#endif /* !_PARSER_PROC_H */ diff --git a/include/parser/var.h b/include/parser/var.h new file mode 100644 index 0000000..dd981f5 --- /dev/null +++ b/include/parser/var.h @@ -0,0 +1,24 @@ +/* + * Variable definitions. + * Copyright (c) 2023-2024, Quinn Stephens and the OSMORA team. + * Provided under the BSD 3-Clause license. + */ + +#ifndef _PARSER_VAR_H +#define _PARSER_VAR_H + +#include +#include "hashmap.h" +#include "parser/type.h" + +struct variable { + struct hashmap_entry hashmap_entry; + + char *name; + size_t name_len; + + struct type *typ; + int n_ptrs; +}; + +#endif /* !_PARSER_VAR_H */ diff --git a/test.q b/test.q index 4020e5b..0769752 100644 --- a/test.q +++ b/test.q @@ -47,3 +47,5 @@ type EfiSystemTable: struct { uint numberOfTableEntries; EfiConfigurationTable *configurationTable; } + +proc efiEntry(EfiHandle imageHandle, EfiSystemTable *systemTable) -> EfiStatus; -- cgit v1.2.3