/* * Compiler command-line interface. * Copyright (c) 2023-2024, Quinn Stephens and the OSMORA team. * Provided under the BSD 3-Clause license. */ #include #include #include #include #include #include "debug.h" #include "hashmap.h" #include "parser/proc.h" #include "parser/type.h" #include "parser.h" #define HASHMAP_ROWS 16 #define OID_O 0x00 struct option { char *name; char *value; }; static char *argv0; static char *in_fname; static struct option options[] = { [OID_O] = { "o", NULL } }; __attribute__((noreturn)) static void cmd_fatal(const char *fmt, ...) { va_list ap; fprintf(stderr, "\033[1m%s: \033[31mfatal error:\033[0m ", argv0); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "compilation terminated.\n"); exit(EXIT_FAILURE); } static void cmd_error(const char *fmt, ...) { va_list ap; fprintf(stderr, "\033[1m%s: \033[31merror:\033[0m ", argv0); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } static char * load_text_file(char *filename) { FILE *fp; size_t size; char *buf; fp = fopen(filename, "rb"); if (fp == NULL) { cmd_error("%s: %s\n", filename, strerror(errno)); return NULL; } /* Get file size */ fseek(fp, 0, SEEK_END); size = (size_t)ftell(fp); fseek(fp, 0, SEEK_SET); if (size < sizeof(char)) { cmd_error("%s: empty file\n", filename); fclose(fp); return NULL; } /* Read entire file */ buf = malloc(size + sizeof(char)); if (fread(buf, 1, size, fp) != size) { cmd_error("%s: %s\n", filename, strerror(errno)); free(buf); fclose(fp); return NULL; } fclose(fp); /* Terminate string */ buf[size] = '\0'; return buf; } static struct option * find_option(char *name) { for (int o = 0; o < (int)(sizeof(options) / sizeof(struct option)); o++) { if (strcmp(options[o].name, name) == 0) { return &options[0]; } } return NULL; } static void set_out_fname(void) { size_t len; char *buf; char *dot; /* Skip if set already */ if (options[OID_O].value != NULL) { return; } len = strlen(in_fname); buf = malloc(len + 4); strcpy(buf, in_fname); /* Append extension after last '.' or at end */ dot = strrchr(buf, '.'); if (dot == NULL) { strcpy(buf + len, ".txt"); } else { strcpy(buf + (dot - buf), ".txt"); } options[OID_O].value = buf; } static void parse_args(int argc, char **argv) { struct option *opt; in_fname = NULL; for (int a = 1; a < argc; a++) { /* Set input filename */ if (argv[a][0] != '-') { if (in_fname != NULL) { cmd_error("multiple input files specified\n"); } else { in_fname = argv[a]; } continue; } /* Identify option */ opt = find_option(argv[a] + 1); if (opt == NULL) { cmd_error("unrecognized command-line option '%s'\n", argv[a]); continue; } /* Set option value */ a++; if (a >= argc) { cmd_error("missing value after '%s'\n", argv[a - 1]); } opt->value = argv[a]; } if (in_fname == NULL) { cmd_fatal("no input files\n"); } set_out_fname(); } static void print_ptrs(int n_ptrs) { for (int n = 0; n < n_ptrs; n++) { putchar('*'); } } static void print_alias(struct type *typ) { printf("alias (%lu bytes, %d pointer(s));\n", typ->size, typ->n_ptrs); } static void print_enum(struct type *typ) { struct enum_member *mem; printf("enum {\n"); for (size_t r = 0; r < typ->members.n_rows; r++) { mem = (struct enum_member*)typ->members.rows[r].head; 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; } } printf("}\n"); } static void print_struct(struct type *typ) { struct struct_member *mem; printf("struct (%lu bytes) {\n", typ->size); for (size_t r = 0; r < typ->members.n_rows; r++) { mem = (struct struct_member*)typ->members.rows[r].head; 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); mem = (struct struct_member*)mem->hashmap_entry.list_entry.next; } } printf("}\n"); } static void print_type(struct type *typ) { printf("type %.*s: ", (int)typ->name_len, typ->name); switch (typ->kind) { case TYK_ALIAS: print_alias(typ); break; case TYK_ENUM: print_enum(typ); break; case TYK_STRUCT: print_struct(typ); break; default: 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) { char *input; struct parser parser; struct list *types_rows, *procs_rows; struct hashmap types, procs; argv0 = argv[0]; if (argc < 2) { cmd_fatal("no input files\n"); exit(EXIT_FAILURE); } /* Parse command-line arguments */ parse_args(argc, argv); /* Load input file */ input = load_text_file(in_fname); if (input == NULL) { exit(EXIT_FAILURE); } /* Initialize hashmaps */ types_rows = malloc(HASHMAP_ROWS * sizeof(struct list)); types.rows = types_rows; types.n_rows = HASHMAP_ROWS; hashmap_init(&types); procs_rows = malloc(HASHMAP_ROWS * sizeof(struct list)); procs.rows = procs_rows; procs.n_rows = HASHMAP_ROWS; hashmap_init(&procs); /* Parse input */ parser.tok.fname = in_fname; parser.types = &types; parser.procs = &procs; parser_init(&parser, input); parser_parse(&parser); dump_types(parser.types); dump_procs(parser.procs); free(input); return 0; }