diff options
| -rw-r--r-- | tomo.c | 300 | ||||
| -rw-r--r-- | tomo.h | 10 |
2 files changed, 196 insertions, 114 deletions
@@ -4,15 +4,27 @@ #include <gc.h> #include <gc/cord.h> #include <printf.h> +#include <sys/stat.h> #include "ast.h" +#include "builtins/array.h" +#include "builtins/datatypes.h" #include "builtins/text.h" #include "compile.h" #include "parse.h" +#include "tomo.h" #include "typecheck.h" #include "types.h" -typedef enum { MODE_RUN, MODE_COMPILE, MODE_TRANSPILE, MODE_EXPANDED_TRANSPILE } mode_e; +typedef enum { MODE_TRANSPILE = 0, MODE_COMPILE = 1, MODE_RUN = 2 } mode_e; + +static bool verbose = false; +static const char *autofmt; +static const char *cconfig; +static const char *cflags; +static const char *ldlibs; +static const char *ldflags = "-Wl,-rpath '-Wl,$ORIGIN' -L/usr/local/lib"; +static const char *cc; int main(int argc, char *argv[]) { @@ -23,8 +35,6 @@ int main(int argc, char *argv[]) mode = MODE_TRANSPILE; } else if (streq(argv[i], "-c")) { mode = MODE_COMPILE; - } else if (streq(argv[i], "-E")) { - mode = MODE_EXPANDED_TRANSPILE; } else { filename = argv[i]; break; @@ -40,10 +50,103 @@ int main(int argc, char *argv[]) if (register_printf_specifier('W', printf_ast, printf_pointer_size)) errx(1, "Couldn't set printf specifier"); - const char *autofmt = getenv("AUTOFMT"); + autofmt = getenv("AUTOFMT"); if (!autofmt) autofmt = "indent -kr -l100 -nbbo -nut -sob"; if (!autofmt[0]) autofmt = "cat"; + verbose = (getenv("VERBOSE") && strcmp(getenv("VERBOSE"), "1") == 0); + + cconfig = getenv("CCONFIG"); + if (!cconfig) + cconfig = "-std=c11 -fdollars-in-identifiers -fsanitize=signed-integer-overflow -fno-sanitize-recover -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE"; + + cflags = getenv("CFLAGS"); + if (!cflags) + cflags = heap_strf("%s -I. -D_DEFAULT_SOURCE", cconfig); + + ldlibs = "-lgc -lcord -lm -L. -ltomo"; + if (getenv("LDLIBS")) + ldlibs = heap_strf("%s %s", ldlibs, getenv("LDLIBS")); + + ldflags = "-Wl,-rpath '-Wl,$ORIGIN' -L/usr/local/lib"; + + cc = getenv("CC"); + if (!cc) cc = "tcc"; + + int status = transpile(filename, true); + if (status != 0 || mode < MODE_COMPILE) + return status; + + status = compile_object_file(filename, true); + if (status != 0 || mode < MODE_RUN) + return status; + + return run_program(filename); +} + +static void build_object_dependency_graph(const char *filename, table_t *dependencies) +{ + size_t len = strlen(filename); + const char *base_filename; + if (strcmp(filename + (len-2), ".h") == 0 || strcmp(filename + (len-2), ".c") == 0 || strcmp(filename + (len-2), ".o") == 0) + base_filename = heap_strn(filename, len-2); + else if (strcmp(filename + (len-3), ".tm") == 0) + base_filename = filename; + else + errx(1, "I don't know how to find object files in: %s", filename); + + if (Table_str_get(*dependencies, base_filename)) + return; + + array_t *deps = new(array_t); + const char *obj_file = heap_strf("%s.o", base_filename); + Array__insert(deps, &obj_file, 0, $ArrayInfo(&Text)); + Table_str_set(dependencies, base_filename, deps); + + const char *to_scan[] = { + heap_strf("%s.h", base_filename), + heap_strf("%s.c", base_filename), + }; + for (size_t s = 0; s < sizeof(to_scan)/sizeof(to_scan[0]); s++) { + file_t *f = load_file(to_scan[s]); + if (!f) errx(1, "Couldn't find file: %s", to_scan[s]); + for (int64_t i = 0; i < f->num_lines; i++) { + const char *line = f->text + f->lines[i].offset; + const char *prefix = "#include \""; + if (strncmp(line, prefix, strlen(prefix)) == 0) { + const char *included = heap_strn(line + strlen(prefix), strcspn(line + strlen(prefix), "\"")); + const char *resolved_header = resolve_path(included, to_scan[s], NULL); + const char *resolved_obj = heap_strf("%.*s.o", strlen(resolved_header)-2, resolved_header); + Array__insert(deps, &resolved_obj, 0, $ArrayInfo(&Text)); + build_object_dependency_graph(resolved_header, dependencies); + } + } + } +} + +static bool stale(const char *filename, const char *relative_to) +{ + struct stat target_stat; + if (stat(filename, &target_stat)) + return true; + struct stat relative_to_stat; + if (stat(relative_to, &target_stat)) + return true; + return target_stat.st_mtime < relative_to_stat.st_mtime; +} + +int transpile(const char *filename, bool force_retranspile) +{ + const char *tm_file = filename; + const char *c_file = heap_strf("%s.c", tm_file); + const char *h_file = heap_strf("%s.h", tm_file); + if (!force_retranspile && !stale(c_file, tm_file) && !stale(h_file, tm_file)) { + if (verbose) + printf("Skipping transpilation of %s\n", filename); + return 0; + } + + file_t *f = load_file(filename); if (!f) errx(1, "No such file: %s", filename); @@ -53,7 +156,6 @@ int main(int argc, char *argv[]) if (!ast) errx(1, "Could not compile!"); - bool verbose = (getenv("VERBOSE") && strcmp(getenv("VERBOSE"), "1") == 0); if (verbose) { FILE *out = popen(heap_strf("bat -P --file-name='%s'", filename), "w"); fputs(f->text, out); @@ -68,7 +170,7 @@ int main(int argc, char *argv[]) module_code_t module = compile_file(ast); - if (verbose && mode != MODE_RUN) { + if (verbose) { FILE *out = popen(heap_strf("%s | bat -P --file-name=%s.h", autofmt, f->filename), "w"); CORD_put(module.header, out); pclose(out); @@ -77,122 +179,92 @@ int main(int argc, char *argv[]) pclose(out); } - const char *cconfig = getenv("CCONFIG"); - if (!cconfig) - cconfig = "-std=c11 -fdollars-in-identifiers -fsanitize=signed-integer-overflow -fno-sanitize-recover -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE"; - - const char *cflags = getenv("CFLAGS"); - if (!cflags) - cflags = heap_strf("%s -I. -D_DEFAULT_SOURCE", cconfig); - - const char *ldlibs = "-lgc -lcord -lm -L. -ltomo"; - if (getenv("LDLIBS")) - ldlibs = heap_strf("%s %s", ldlibs, getenv("LDLIBS")); - - const char *ldflags = "-Wl,-rpath '-Wl,$ORIGIN' -L/usr/local/lib"; + FILE *prog = popen(heap_strf("%s > %s.h", autofmt, f->filename), "w"); + CORD_put("#pragma once\n", prog); + CORD_put(module.header, prog); + int status = pclose(prog); + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) + printf("Transpiled to %s.h\n", f->filename); + else + return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; - const char *cc = getenv("CC"); - if (!cc) cc = "tcc"; + prog = popen(heap_strf("%s > %s.c", autofmt, f->filename), "w"); + CORD_put(CORD_all("#include \"", module.module_name, ".tm.h\"\n\n", module.c_file), prog); + status = pclose(prog); + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) + printf("Transpiled to %s.c\n", f->filename); + return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; +} - const char *object_files = CORD_to_const_char_star(module.object_files); - - switch (mode) { - case MODE_COMPILE: { - FILE *prog = popen(heap_strf("%s > %s.h", autofmt, f->filename), "w"); - CORD_put("#pragma once\n", prog); - CORD_put(module.header, prog); - int status = pclose(prog); - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) - printf("Transpiled to %s.h\n", f->filename); - else - return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; - - prog = popen(heap_strf("%s > %s.c", autofmt, f->filename), "w"); - CORD_put(CORD_all("#include \"", module.module_name, ".tm.h\"\n\n", module.c_file), prog); - status = pclose(prog); - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) - printf("Transpiled to %s.c\n", f->filename); - else - return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; - - const char *cmd = heap_strf("%s %s -c %s.c -o %s.o", cc, cflags, f->filename, f->filename); +int compile_object_file(const char *filename, bool force_recompile) +{ + const char *obj_file = heap_strf("%s.o", filename); + if (!force_recompile && !stale(obj_file, filename)) { if (verbose) - printf("Running: %s\n", cmd); - prog = popen(cmd, "w"); - status = pclose(prog); - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) - printf("Compiled to %s.o\n", f->filename); - return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; + printf("Skipping recompilation of %s\n", filename); + return 0; } - case MODE_RUN: { - const char *run = streq(cc, "tcc") ? heap_strf("%s | tcc %s %s %s %s -run -", autofmt, cflags, ldflags, ldlibs, object_files) - : heap_strf("%s | gcc %s %s %s %s -x c - -o program && ./program", autofmt, cflags, ldflags, ldlibs, object_files); - if (verbose) - printf("%s\n", run); - FILE *runner = popen(run, "w"); - - CORD program = CORD_all( - "// File: ", f->filename, ".h\n", - module.header, - "\n", - "// File: ", f->filename, ".c\n", - module.c_file, - "\n", - "int main(int argc, const char *argv[]) {\n" - "(void)argc;\n" - "(void)argv;\n" - "GC_INIT();\n" - "detect_color();\n", - module.module_name, "$use();\n" - "return 0;\n" - "}\n" - ); - if (verbose) { - FILE *out = popen(heap_strf("%s | bat -P --file-name=%s.c", autofmt, f->filename), "w"); - CORD_put(program, out); - pclose(out); - } + const char *cmd = heap_strf("%s %s -c %s.c -o %s.o", cc, cflags, filename, filename); + if (verbose) + printf("Running: %s\n", cmd); + FILE *prog = popen(cmd, "w"); + int status = pclose(prog); + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) + printf("Compiled to %s.o\n", filename); + return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; +} - CORD_put(program, runner); - int status = pclose(runner); - return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; - } - case MODE_TRANSPILE: { - FILE *prog = popen(heap_strf("%s > %s.h", autofmt, f->filename), "w"); - CORD_put("#pragma once\n", prog); - CORD_put(module.header, prog); - int status = pclose(prog); - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) - printf("Transpiled to %s.h\n", f->filename); - else - return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; - - prog = popen(heap_strf("%s > %s.c", autofmt, f->filename), "w"); - CORD_put(CORD_all("#include \"", module.module_name, ".tm.h\"\n\n", module.c_file), prog); - status = pclose(prog); - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) - printf("Transpiled to %s.c\n", f->filename); - return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; +int run_program(const char *filename) +{ + const char *resolved = resolve_path(filename, ".", NULL); + + table_t obj_dependencies = {}; + build_object_dependency_graph(resolved, &obj_dependencies); + table_t obj_files = {}; + for (int64_t i = 1; i <= Table_length(obj_dependencies); i++) { + struct { const char *name; array_t *obj_files; } *entry = Table_entry(obj_dependencies, i); + for (int64_t j = 0; j < entry->obj_files->length; j++) { + const char *obj_name = *(char**)(entry->obj_files->data + j*entry->obj_files->stride); + Table_str_set(&obj_files, obj_name, obj_name); + } } - case MODE_EXPANDED_TRANSPILE: { - FILE *prog = popen(heap_strf("%s -x c %s -E - | %s > %s.h", cc, cflags, autofmt, f->filename), "w"); - CORD_put("#pragma once\n", prog); - CORD_put(module.header, prog); - int status = pclose(prog); - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) - printf("Transpiled to %s.h\n", f->filename); - else - return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; - - prog = popen(heap_strf("%s -x c %s -E - | %s > %s.c", cc, cflags, autofmt, f->filename), "w"); - CORD_put(CORD_all("#include \"", module.module_name, ".tm.h\"\n\n", module.c_file), prog); - status = pclose(prog); - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) - printf("Transpiled to %s.c\n", f->filename); - return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; + CORD object_files_cord = CORD_EMPTY; + for (int64_t i = 1; i <= Table_length(obj_files); i++) { + struct { const char *name, *_; } *entry = Table_entry(obj_files, i); + object_files_cord = object_files_cord ? CORD_all(object_files_cord, " ", entry->name) : entry->name; } + + const char *object_files = CORD_to_const_char_star(object_files_cord); + assert(object_files); + const char *run = streq(cc, "tcc") ? heap_strf("%s | tcc %s %s %s %s -run -", autofmt, cflags, ldflags, ldlibs, object_files) + : heap_strf("%s | gcc %s %s %s %s -x c - -o program && ./program", autofmt, cflags, ldflags, ldlibs, object_files); + if (verbose) + printf("%s\n", run); + FILE *runner = popen(run, "w"); + + const char *module_name = file_base_name(filename); + CORD program = CORD_all( + "#include <tomo.h>\n" + "#include \"", filename, ".h\"\n" + "\n" + "int main(int argc, const char *argv[]) {\n" + "(void)argc;\n" + "(void)argv;\n" + "GC_INIT();\n" + "detect_color();\n", + module_name, "$use();\n" + "return 0;\n" + "}\n" + ); + if (verbose) { + FILE *out = popen(heap_strf("%s | bat -P --file-name=run.c", autofmt), "w"); + CORD_put(program, out); + pclose(out); } - return 0; + + CORD_put(program, runner); + int status = pclose(runner); + return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; } // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 @@ -0,0 +1,10 @@ +#pragma once +// Compiler interface + +#include <stdbool.h> + +int transpile(const char *filename, bool force_retranspile); +int compile_object_file(const char *filename, bool force_recompile); +int run_program(const char *filename); + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 |
