summaryrefslogtreecommitdiff
path: root/compiler/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/main.c')
-rw-r--r--compiler/main.c310
1 files changed, 310 insertions, 0 deletions
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 <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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;
+}