Break compilation phases into functions
This commit is contained in:
parent
a47bd2d569
commit
9c9eff753f
302
tomo.c
302
tomo.c
@ -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";
|
||||
|
||||
const char *cc = getenv("CC");
|
||||
if (!cc) cc = "tcc";
|
||||
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
}
|
||||
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);
|
||||
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 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("Skipping recompilation of %s\n", filename);
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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");
|
||||
|
||||
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 *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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
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
|
||||
|
10
tomo.h
Normal file
10
tomo.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user