From a515dfb3b8f8e999362db7a6b52b3104c03b750a Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Fri, 1 Nov 2024 23:46:08 -0400 Subject: Import quark sources Signed-off-by: Ian Moffett --- compiler/main.c | 310 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 compiler/main.c (limited to 'compiler/main.c') diff --git a/compiler/main.c b/compiler/main.c new file mode 100644 index 0000000..da261bc --- /dev/null +++ b/compiler/main.c @@ -0,0 +1,310 @@ +/* + * 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/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_alias(struct type *typ) +{ + printf("alias (%lu bytes, %d pointer(s))", 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; + if (mem == (struct enum_member*)&typ->members.rows[r]) { + continue; + } + + do { + 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("}"); +} + +static void +print_struct(struct type *typ) +{ + struct struct_member *mem; + + printf("struct {\n"); + + 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 %.*s; (%d pointer(s))\n", (int)mem->typ->name_len, mem->typ->name, (int)mem->name_len, mem->name, mem->n_ptrs); + + mem = (struct struct_member*)mem->hashmap_entry.list_entry.next; + } while (mem != (struct struct_member*)&typ->members.rows[r]); + } + + printf("}"); +} + +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)"); + break; + } + + printf(";\n"); + +} + +int +main(int argc, char **argv) +{ + char *input; + struct parser parser; + struct list *types_rows, *procs_rows; + struct hashmap types, procs; + struct type *typ; + + 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); + + /* 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]); + } + + free(procs_rows); + free(types_rows); + free(input); + return 0; +} -- cgit v1.2.3