Split header compilation into a separate function
This commit is contained in:
parent
31c8d0af15
commit
8c7d530080
1
ast.h
1
ast.h
@ -193,6 +193,7 @@ struct ast_s {
|
||||
struct {
|
||||
arg_ast_t *args;
|
||||
ast_t *body;
|
||||
int64_t id;
|
||||
} Lambda;
|
||||
struct {
|
||||
ast_t *fn;
|
||||
|
294
compile.c
294
compile.c
@ -16,6 +16,7 @@
|
||||
|
||||
static CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool allow_optional);
|
||||
static env_t *with_enum_scope(env_t *env, type_t *t);
|
||||
static CORD compile_statement_header(env_t *env, ast_t *ast);
|
||||
|
||||
CORD compile_type_ast(env_t *env, type_ast_t *t)
|
||||
{
|
||||
@ -25,7 +26,7 @@ CORD compile_type_ast(env_t *env, type_ast_t *t)
|
||||
case TableTypeAST: return "table_t";
|
||||
case ArrayTypeAST: return "array_t";
|
||||
case FunctionTypeAST: return "const void*";
|
||||
default: code_err(t, "Not implemented");
|
||||
default: code_err(t, "Compiling type AST is not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,6 +68,36 @@ static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed)
|
||||
return false;
|
||||
}
|
||||
|
||||
static table_t *get_closed_vars(env_t *env, ast_t *lambda_ast)
|
||||
{
|
||||
auto lambda = Match(lambda_ast, Lambda);
|
||||
env_t *body_scope = fresh_scope(env);
|
||||
for (arg_ast_t *arg = lambda->args; arg; arg = arg->next) {
|
||||
type_t *arg_type = get_arg_ast_type(env, arg);
|
||||
set_binding(body_scope, arg->name, new(binding_t, .type=arg_type, .code=CORD_cat("$", arg->name)));
|
||||
}
|
||||
|
||||
fn_ctx_t fn_ctx = (fn_ctx_t){
|
||||
.closure_scope=body_scope->locals->fallback,
|
||||
.closed_vars=new(table_t),
|
||||
};
|
||||
body_scope->fn_ctx = &fn_ctx;
|
||||
body_scope->locals->fallback = env->globals;
|
||||
type_t *ret_t = get_type(body_scope, lambda->body);
|
||||
fn_ctx.return_type = ret_t;
|
||||
|
||||
// Find which variables are captured in the closure:
|
||||
env_t *tmp_scope = fresh_scope(body_scope);
|
||||
for (ast_list_t *stmt = Match(lambda->body, Block)->statements; stmt; stmt = stmt->next) {
|
||||
type_t *stmt_type = get_type(tmp_scope, stmt->ast);
|
||||
if (stmt->next || (stmt_type->tag == VoidType || stmt_type->tag == AbortType))
|
||||
(void)compile_statement(tmp_scope, stmt->ast);
|
||||
else
|
||||
(void)compile(tmp_scope, stmt->ast);
|
||||
}
|
||||
return fn_ctx.closed_vars;
|
||||
}
|
||||
|
||||
CORD compile_declaration(env_t *env, type_t *t, CORD name)
|
||||
{
|
||||
if (t->tag == FunctionType) {
|
||||
@ -77,8 +108,10 @@ CORD compile_declaration(env_t *env, type_t *t, CORD name)
|
||||
if (arg->next) code = CORD_cat(code, ", ");
|
||||
}
|
||||
return CORD_all(code, ")");
|
||||
} else {
|
||||
} else if (t->tag != ModuleType) {
|
||||
return CORD_all(compile_type(env, t), " ", name);
|
||||
} else {
|
||||
return CORD_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,14 +144,14 @@ CORD compile_type(env_t *env, type_t *t)
|
||||
case PointerType: return CORD_cat(compile_type(env, Match(t, PointerType)->pointed), "*");
|
||||
case StructType: {
|
||||
auto s = Match(t, StructType);
|
||||
return CORD_all(s->env->file_prefix, s->name, "_t");
|
||||
return CORD_all("struct ", s->env->file_prefix, s->name, "_s");
|
||||
}
|
||||
case EnumType: {
|
||||
auto e = Match(t, EnumType);
|
||||
return CORD_all(e->env->file_prefix, e->name, "_t");
|
||||
}
|
||||
case TypeInfoType: return "TypeInfo";
|
||||
default: compiler_err(NULL, NULL, NULL, "Not implemented");
|
||||
default: compiler_err(NULL, NULL, NULL, "Compiling type is not implemented for type with tag %d", t->tag);
|
||||
}
|
||||
}
|
||||
|
||||
@ -482,8 +515,6 @@ CORD compile_statement(env_t *env, ast_t *ast)
|
||||
}
|
||||
case LangDef: {
|
||||
auto def = Match(ast, LangDef);
|
||||
CORD_appendf(&env->code->typedefs, "typedef CORD %r%s_t;\n", env->file_prefix, def->name);
|
||||
CORD_appendf(&env->code->typedefs, "extern const TypeInfo %r%s;\n", env->file_prefix, def->name);
|
||||
CORD_appendf(&env->code->typeinfos, "public const TypeInfo %r%s = {%zu, %zu, {.tag=TextInfo, .TextInfo={%r}}};\n",
|
||||
env->file_prefix, def->name, sizeof(CORD), __alignof__(CORD),
|
||||
Text$quoted(def->name, false));
|
||||
@ -508,8 +539,6 @@ CORD compile_statement(env_t *env, ast_t *ast)
|
||||
|
||||
if (is_private)
|
||||
env->code->staticdefs = CORD_all(env->code->staticdefs, "static ", ret_type_code, " ", name, arg_signature, ";\n");
|
||||
else
|
||||
env->code->fndefs = CORD_all(env->code->fndefs, ret_type_code, " ", name, arg_signature, ";\n");
|
||||
|
||||
CORD code;
|
||||
if (fndef->cache) {
|
||||
@ -560,11 +589,13 @@ CORD compile_statement(env_t *env, ast_t *ast)
|
||||
") Table$remove(&cache, NULL, table_type);\n");
|
||||
}
|
||||
|
||||
CORD arg_typedef = compile_struct_header(env, args_def);
|
||||
env->code->local_typedefs = CORD_all(env->code->local_typedefs, arg_typedef);
|
||||
CORD wrapper = CORD_all(
|
||||
is_private ? CORD_EMPTY : "public ", ret_type_code, " ", name, arg_signature, "{\n"
|
||||
"static table_t cache = {};\n",
|
||||
compile_type(env, args_t), " args = {", all_args, "};\n"
|
||||
"static const TypeInfo *table_type = $TableInfo(", compile_type_info(env, args_t), ", ", compile_type_info(env, ret_t), ");\n",
|
||||
"const TypeInfo *table_type = $TableInfo(", compile_type_info(env, args_t), ", ", compile_type_info(env, ret_t), ");\n",
|
||||
ret_type_code, "*cached = Table$get_raw(cache, &args, table_type);\n"
|
||||
"if (cached) return *cached;\n",
|
||||
ret_type_code, " ret = ", name, "$uncached(", all_args, ");\n",
|
||||
@ -810,36 +841,9 @@ CORD compile_statement(env_t *env, ast_t *ast)
|
||||
return compile_statement(env, loop);
|
||||
}
|
||||
}
|
||||
case Extern: {
|
||||
auto ext = Match(ast, Extern);
|
||||
type_t *t = parse_type_ast(env, ext->type);
|
||||
CORD decl;
|
||||
if (t->tag == ClosureType) {
|
||||
t = Match(t, ClosureType)->fn;
|
||||
auto fn = Match(t, FunctionType);
|
||||
decl = CORD_all(compile_type(env, fn->ret), " ", ext->name, "(");
|
||||
for (arg_t *arg = fn->args; arg; arg = arg->next) {
|
||||
decl = CORD_all(decl, compile_type(env, arg->type));
|
||||
if (arg->next) decl = CORD_cat(decl, ", ");
|
||||
}
|
||||
decl = CORD_cat(decl, ")");
|
||||
} else {
|
||||
decl = compile_declaration(env, t, ext->name);
|
||||
}
|
||||
env->code->fndefs = CORD_all(env->code->fndefs, "extern ", decl, ";\n");
|
||||
return CORD_EMPTY;
|
||||
}
|
||||
case Extern: return CORD_EMPTY;
|
||||
case InlineCCode: return Match(ast, InlineCCode)->code;
|
||||
case Use: {
|
||||
auto use = Match(ast, Use);
|
||||
const char *path = use->raw_path;
|
||||
if (strncmp(path, "./", 2) == 0 || strncmp(path, "../", 3) == 0) {
|
||||
env->code->imports = CORD_all(env->code->imports, "#include \"", path, ".h\"\n");
|
||||
} else {
|
||||
env->code->imports = CORD_all(env->code->imports, "#include <", path, ".h>\n");
|
||||
}
|
||||
return CORD_EMPTY;
|
||||
}
|
||||
case Use: return CORD_EMPTY;
|
||||
default:
|
||||
if (!is_discardable(env, ast))
|
||||
code_err(ast, "The result of this statement cannot be discarded");
|
||||
@ -1494,8 +1498,7 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
}
|
||||
case Lambda: {
|
||||
auto lambda = Match(ast, Lambda);
|
||||
static int64_t lambda_number = 1;
|
||||
CORD name = CORD_asprintf("%slambda$%ld", env->file_prefix, lambda_number++);
|
||||
CORD name = CORD_asprintf("%slambda$%ld", env->file_prefix, lambda->id);
|
||||
|
||||
env_t *body_scope = fresh_scope(env);
|
||||
for (arg_ast_t *arg = lambda->args; arg; arg = arg->next) {
|
||||
@ -1503,15 +1506,14 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
set_binding(body_scope, arg->name, new(binding_t, .type=arg_type, .code=CORD_cat("$", arg->name)));
|
||||
}
|
||||
|
||||
type_t *ret_t = get_type(body_scope, lambda->body);
|
||||
|
||||
fn_ctx_t fn_ctx = (fn_ctx_t){
|
||||
.return_type=ret_t,
|
||||
.closure_scope=body_scope->locals->fallback,
|
||||
.closed_vars=new(table_t),
|
||||
};
|
||||
body_scope->fn_ctx = &fn_ctx;
|
||||
body_scope->locals->fallback = env->globals;
|
||||
type_t *ret_t = get_type(body_scope, lambda->body);
|
||||
fn_ctx.return_type = ret_t;
|
||||
|
||||
CORD code = CORD_all("static ", compile_type(env, ret_t), " ", name, "(");
|
||||
for (arg_ast_t *arg = lambda->args; arg; arg = arg->next) {
|
||||
@ -1519,33 +1521,23 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
code = CORD_all(code, compile_type(env, arg_type), " $", arg->name, ", ");
|
||||
}
|
||||
|
||||
// Find which variables are captured in the closure:
|
||||
env_t *tmp_scope = fresh_scope(body_scope);
|
||||
for (ast_list_t *stmt = Match(lambda->body, Block)->statements; stmt; stmt = stmt->next) {
|
||||
type_t *stmt_type = get_type(tmp_scope, stmt->ast);
|
||||
if (stmt->next || (stmt_type->tag == VoidType || stmt_type->tag == AbortType))
|
||||
(void)compile_statement(tmp_scope, stmt->ast);
|
||||
else
|
||||
(void)compile(tmp_scope, stmt->ast);
|
||||
}
|
||||
CORD args_typedef = compile_statement_header(env, ast);
|
||||
env->code->local_typedefs = CORD_all(env->code->local_typedefs, args_typedef);
|
||||
|
||||
table_t *closed_vars = get_closed_vars(env, ast);
|
||||
CORD userdata;
|
||||
if (Table$length(*fn_ctx.closed_vars) == 0) {
|
||||
if (Table$length(*closed_vars) == 0) {
|
||||
code = CORD_cat(code, "void *userdata)");
|
||||
userdata = "NULL";
|
||||
} else {
|
||||
CORD def = "typedef struct {";
|
||||
userdata = CORD_all("new(", name, "$userdata_t");
|
||||
for (int64_t i = 1; i <= Table$length(*fn_ctx.closed_vars); i++) {
|
||||
struct { const char *name; binding_t *b; } *entry = Table$entry(*fn_ctx.closed_vars, i);
|
||||
for (int64_t i = 1; i <= Table$length(*closed_vars); i++) {
|
||||
struct { const char *name; binding_t *b; } *entry = Table$entry(*closed_vars, i);
|
||||
if (entry->b->type->tag == ModuleType)
|
||||
continue;
|
||||
def = CORD_all(def, compile_declaration(env, entry->b->type, entry->name), "; ");
|
||||
userdata = CORD_all(userdata, ", ", entry->b->code);
|
||||
}
|
||||
userdata = CORD_all(userdata, ")");
|
||||
def = CORD_all(def, "} ", name, "$userdata_t;");
|
||||
env->code->typedefs = CORD_cat(env->code->typedefs, def);
|
||||
code = CORD_all(code, name, "$userdata_t *userdata)");
|
||||
}
|
||||
|
||||
@ -1956,8 +1948,6 @@ void compile_namespace(env_t *env, const char *ns_name, ast_t *block)
|
||||
code_err(decl->value, "This value is supposed to be a compile-time constant, but I can't figure out how to make it one");
|
||||
CORD var_decl = CORD_all(compile_type(env, t), " ", compile(ns_env, decl->var), " = ", compile(ns_env, decl->value), ";\n");
|
||||
env->code->staticdefs = CORD_all(env->code->staticdefs, var_decl);
|
||||
|
||||
env->code->fndefs = CORD_all(env->code->fndefs, "extern ", compile_type(env, t), " ", compile(ns_env, decl->var), ";\n");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@ -1965,8 +1955,18 @@ void compile_namespace(env_t *env, const char *ns_name, ast_t *block)
|
||||
assert(!code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CORD compile_namespace_headers(env_t *env, const char *ns_name, ast_t *block)
|
||||
{
|
||||
env_t *ns_env = namespace_env(env, ns_name);
|
||||
CORD header = CORD_EMPTY;
|
||||
for (ast_list_t *stmt = block ? Match(block, Block)->statements : NULL; stmt; stmt = stmt->next) {
|
||||
header = CORD_all(header, compile_statement_header(ns_env, stmt->ast));
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
CORD compile_type_info(env_t *env, type_t *t)
|
||||
@ -2183,24 +2183,8 @@ CORD compile_cli_arg_call(env_t *env, CORD fn_name, type_t *fn_type)
|
||||
return code;
|
||||
}
|
||||
|
||||
module_code_t compile_file(ast_t *ast)
|
||||
CORD compile_file(env_t *env, ast_t *ast)
|
||||
{
|
||||
env_t *env = new_compilation_unit();
|
||||
|
||||
const char *name = file_base_name(ast->file->filename);
|
||||
env->file_prefix = heap_strf("%s$", name);
|
||||
Table$str_set(env->imports, name, env);
|
||||
|
||||
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
|
||||
prebind_statement(env, stmt->ast);
|
||||
}
|
||||
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
|
||||
// Hack: make sure global is bound as foo$var:
|
||||
if (stmt->ast->tag == Declare && Match(Match(stmt->ast, Declare)->var, Var)->name[0] != '_')
|
||||
env->scope_prefix = heap_strf("%s$", name);
|
||||
bind_statement(env, stmt->ast);
|
||||
env->scope_prefix = NULL;
|
||||
}
|
||||
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
|
||||
if (stmt->ast->tag == Declare) {
|
||||
auto decl = Match(stmt->ast, Declare);
|
||||
@ -2220,9 +2204,6 @@ module_code_t compile_file(ast_t *ast)
|
||||
"static ", compile_type(env, t), " $", decl_name, " = ",
|
||||
compile(env, decl->value), ";\n");
|
||||
} else {
|
||||
env->code->fndefs = CORD_all(
|
||||
env->code->fndefs,
|
||||
"extern ", compile_declaration(env, t, CORD_cat(env->file_prefix, decl_name)), ";\n");
|
||||
env->code->staticdefs = CORD_all(
|
||||
env->code->staticdefs,
|
||||
compile_type(env, t), " ", env->file_prefix, decl_name, " = ",
|
||||
@ -2237,24 +2218,139 @@ module_code_t compile_file(ast_t *ast)
|
||||
}
|
||||
}
|
||||
|
||||
return (module_code_t){
|
||||
.module_name=name,
|
||||
.env=env,
|
||||
.header=CORD_all(
|
||||
// "#line 1 ", Text$quoted(ast->file->filename, false), "\n",
|
||||
"#include <tomo/tomo.h>\n",
|
||||
env->code->imports, "\n",
|
||||
env->code->typedefs, "\n",
|
||||
env->code->typecode, "\n",
|
||||
env->code->fndefs, "\n"
|
||||
),
|
||||
.c_file=CORD_all(
|
||||
// "#line 1 ", Text$quoted(ast->file->filename, false), "\n",
|
||||
env->code->staticdefs, "\n",
|
||||
env->code->funcs, "\n",
|
||||
env->code->typeinfos, "\n"
|
||||
),
|
||||
};
|
||||
const char *name = file_base_name(ast->file->filename);
|
||||
return CORD_all(
|
||||
// "#line 1 ", Text$quoted(ast->file->filename, false), "\n",
|
||||
"#include <tomo/tomo.h>\n"
|
||||
"#include \"", name, ".tm.h\"\n\n",
|
||||
env->code->local_typedefs, "\n",
|
||||
env->code->staticdefs, "\n",
|
||||
env->code->funcs, "\n",
|
||||
env->code->typeinfos, "\n"
|
||||
);
|
||||
}
|
||||
|
||||
CORD compile_statement_header(env_t *env, ast_t *ast)
|
||||
{
|
||||
switch (ast->tag) {
|
||||
case DocTest: {
|
||||
auto test = Match(ast, DocTest);
|
||||
return compile_statement_header(env, test->expr);
|
||||
}
|
||||
case Declare: {
|
||||
auto decl = Match(ast, Declare);
|
||||
if (decl->value->tag == Use) {
|
||||
return compile_statement_header(env, decl->value);
|
||||
}
|
||||
type_t *t = get_type(env, decl->value);
|
||||
assert(t->tag != ModuleType);
|
||||
if (t->tag == AbortType || t->tag == VoidType)
|
||||
code_err(ast, "You can't declare a variable with a %T value", t);
|
||||
const char *decl_name = Match(decl->var, Var)->name;
|
||||
bool is_private = (decl_name[0] == '_');
|
||||
if (!is_constant(env, decl->value))
|
||||
code_err(decl->value, "This value is not a valid constant initializer.");
|
||||
|
||||
CORD code = (decl->value->tag == Use) ? compile_statement_header(env, decl->value) : CORD_EMPTY;
|
||||
if (is_private) {
|
||||
return code;
|
||||
} else {
|
||||
return CORD_all(
|
||||
code, "\n" "extern ", compile_declaration(env, t, CORD_cat(env->file_prefix, decl_name)), ";\n");
|
||||
}
|
||||
}
|
||||
case StructDef: {
|
||||
return compile_struct_header(env, ast);
|
||||
}
|
||||
case EnumDef: {
|
||||
return compile_enum_header(env, ast);
|
||||
}
|
||||
case LangDef: {
|
||||
auto def = Match(ast, LangDef);
|
||||
return CORD_all(
|
||||
"typedef CORD ", env->file_prefix, def->name, "_t;\n",
|
||||
"extern const TypeInfo ", env->file_prefix, def->name, ";\n");
|
||||
compile_namespace(env, def->name, def->namespace);
|
||||
return CORD_EMPTY;
|
||||
}
|
||||
case FunctionDef: {
|
||||
auto fndef = Match(ast, FunctionDef);
|
||||
bool is_private = Match(fndef->name, Var)->name[0] == '_';
|
||||
if (is_private) return CORD_EMPTY;
|
||||
CORD name = compile(env, fndef->name);
|
||||
|
||||
CORD arg_signature = "(";
|
||||
for (arg_ast_t *arg = fndef->args; arg; arg = arg->next) {
|
||||
type_t *arg_type = get_arg_ast_type(env, arg);
|
||||
arg_signature = CORD_cat(arg_signature, compile_declaration(env, arg_type, CORD_cat("$", arg->name)));
|
||||
if (arg->next) arg_signature = CORD_cat(arg_signature, ", ");
|
||||
}
|
||||
arg_signature = CORD_cat(arg_signature, ")");
|
||||
|
||||
type_t *ret_t = fndef->ret_type ? parse_type_ast(env, fndef->ret_type) : Type(VoidType);
|
||||
CORD ret_type_code = compile_type(env, ret_t);
|
||||
CORD header = CORD_all(ret_type_code, " ", name, arg_signature, ";\n");
|
||||
return header;
|
||||
}
|
||||
case Lambda: {
|
||||
auto lambda = Match(ast, Lambda);
|
||||
CORD name = CORD_asprintf("%slambda$%ld", env->file_prefix, lambda->id);
|
||||
table_t *closed_vars = get_closed_vars(env, ast);
|
||||
if (Table$length(*closed_vars) == 0) {
|
||||
return CORD_EMPTY;
|
||||
} else {
|
||||
CORD def = "typedef struct {";
|
||||
for (int64_t i = 1; i <= Table$length(*closed_vars); i++) {
|
||||
struct { const char *name; binding_t *b; } *entry = Table$entry(*closed_vars, i);
|
||||
if (entry->b->type->tag == ModuleType)
|
||||
continue;
|
||||
def = CORD_all(def, compile_declaration(env, entry->b->type, entry->name), "; ");
|
||||
}
|
||||
return CORD_all(def, "} ", name, "$userdata_t;");
|
||||
}
|
||||
}
|
||||
case Extern: {
|
||||
auto ext = Match(ast, Extern);
|
||||
type_t *t = parse_type_ast(env, ext->type);
|
||||
CORD decl;
|
||||
if (t->tag == ClosureType) {
|
||||
t = Match(t, ClosureType)->fn;
|
||||
auto fn = Match(t, FunctionType);
|
||||
decl = CORD_all(compile_type(env, fn->ret), " ", ext->name, "(");
|
||||
for (arg_t *arg = fn->args; arg; arg = arg->next) {
|
||||
decl = CORD_all(decl, compile_type(env, arg->type));
|
||||
if (arg->next) decl = CORD_cat(decl, ", ");
|
||||
}
|
||||
decl = CORD_cat(decl, ")");
|
||||
} else {
|
||||
decl = compile_declaration(env, t, ext->name);
|
||||
}
|
||||
return CORD_all("extern ", decl, ";\n");
|
||||
}
|
||||
case Use: {
|
||||
auto use = Match(ast, Use);
|
||||
const char *path = use->raw_path;
|
||||
if (strncmp(path, "./", 2) == 0 || strncmp(path, "../", 3) == 0) {
|
||||
return CORD_all("#include \"", path, ".h\"\n");
|
||||
} else {
|
||||
return CORD_all("#include <", path, ".h>\n");
|
||||
}
|
||||
}
|
||||
default:
|
||||
return CORD_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
CORD compile_header(env_t *env, ast_t *ast)
|
||||
{
|
||||
// "#line 1 ", Text$quoted(ast->file->filename, false), "\n",
|
||||
CORD header = "#pragma once\n"
|
||||
"#include <tomo/tomo.h>\n";
|
||||
|
||||
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next)
|
||||
header = CORD_all(header, compile_statement_header(env, stmt->ast));
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
||||
|
10
compile.h
10
compile.h
@ -9,19 +9,15 @@
|
||||
#include "builtins/util.h"
|
||||
#include "environment.h"
|
||||
|
||||
typedef struct {
|
||||
const char *module_name;
|
||||
env_t *env;
|
||||
CORD header, c_file;
|
||||
} module_code_t;
|
||||
|
||||
CORD expr_as_text(env_t *env, CORD expr, type_t *t, CORD color);
|
||||
module_code_t compile_file(ast_t *ast);
|
||||
CORD compile_header(env_t *env, ast_t *ast);
|
||||
CORD compile_file(env_t *env, ast_t *ast);
|
||||
CORD compile_type_ast(env_t *env, type_ast_t *t);
|
||||
CORD compile_declaration(env_t *env, type_t *t, const char *name);
|
||||
CORD compile_type(env_t *env, type_t *t);
|
||||
CORD compile(env_t *env, ast_t *ast);
|
||||
void compile_namespace(env_t *env, const char *ns_name, ast_t *block);
|
||||
CORD compile_namespace_headers(env_t *env, const char *ns_name, ast_t *block);
|
||||
CORD compile_statement(env_t *env, ast_t *ast);
|
||||
CORD compile_type_info(env_t *env, type_t *t);
|
||||
CORD compile_cli_arg_call(env_t *env, CORD fn_name, type_t *fn_type);
|
||||
|
52
enums.c
52
enums.c
@ -153,19 +153,8 @@ void compile_enum_def(env_t *env, ast_t *ast)
|
||||
{
|
||||
auto def = Match(ast, EnumDef);
|
||||
CORD full_name = CORD_cat(env->file_prefix, def->name);
|
||||
CORD_appendf(&env->code->typedefs, "typedef struct %r_s %r_t;\n", full_name, full_name);
|
||||
CORD_appendf(&env->code->typedefs, "extern const TypeInfo %r;\n", full_name);
|
||||
CORD enum_def = CORD_all("struct ", full_name, "_s {\n"
|
||||
"\tenum {");
|
||||
for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
|
||||
CORD_appendf(&enum_def, "%r$tag$%s = %ld", full_name, tag->name, tag->value);
|
||||
if (tag->next) enum_def = CORD_cat(enum_def, ", ");
|
||||
}
|
||||
enum_def = CORD_cat(enum_def, "} $tag;\n"
|
||||
"union {\n");
|
||||
for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
|
||||
compile_struct_def(env, WrapAST(ast, StructDef, .name=CORD_to_const_char_star(CORD_all(def->name, "$", tag->name)), .fields=tag->fields));
|
||||
enum_def = CORD_all(enum_def, full_name, "$", tag->name, "_t ", tag->name, ";\n");
|
||||
if (tag->fields) { // Constructor macros:
|
||||
CORD arg_sig = CORD_EMPTY;
|
||||
for (arg_ast_t *field = tag->fields; field; field = field->next) {
|
||||
@ -174,9 +163,6 @@ void compile_enum_def(env_t *env, ast_t *ast)
|
||||
if (field->next) arg_sig = CORD_cat(arg_sig, ", ");
|
||||
}
|
||||
if (arg_sig == CORD_EMPTY) arg_sig = "void";
|
||||
CORD constructor_def = CORD_all(full_name, "_t ", full_name, "$tagged$", tag->name, "(", arg_sig, ");\n");
|
||||
env->code->fndefs = CORD_cat(env->code->fndefs, constructor_def);
|
||||
|
||||
CORD constructor_impl = CORD_all("public inline ", full_name, "_t ", full_name, "$tagged$", tag->name, "(", arg_sig, ") { return (",
|
||||
full_name, "_t){.$tag=", full_name, "$tag$", tag->name, ", .", tag->name, "={");
|
||||
for (arg_ast_t *field = tag->fields; field; field = field->next) {
|
||||
@ -187,8 +173,6 @@ void compile_enum_def(env_t *env, ast_t *ast)
|
||||
env->code->funcs = CORD_cat(env->code->funcs, constructor_impl);
|
||||
}
|
||||
}
|
||||
enum_def = CORD_cat(enum_def, "};\n};\n");
|
||||
env->code->typecode = CORD_cat(env->code->typecode, enum_def);
|
||||
|
||||
type_t *t = Table$str_get(*env->types, def->name);
|
||||
CORD typeinfo = CORD_asprintf("public const TypeInfo %s = {%zu, %zu, {.tag=CustomInfo, .CustomInfo={",
|
||||
@ -216,4 +200,40 @@ void compile_enum_def(env_t *env, ast_t *ast)
|
||||
compile_namespace(env, def->name, def->namespace);
|
||||
}
|
||||
|
||||
CORD compile_enum_header(env_t *env, ast_t *ast)
|
||||
{
|
||||
auto def = Match(ast, EnumDef);
|
||||
CORD full_name = CORD_cat(env->file_prefix, def->name);
|
||||
CORD header = CORD_all("typedef struct ", full_name, "_s ", full_name, "_t;\n",
|
||||
"extern const TypeInfo ", full_name, ";\n");
|
||||
CORD enum_def = CORD_all("struct ", full_name, "_s {\n"
|
||||
"\tenum {");
|
||||
for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
|
||||
CORD_appendf(&enum_def, "%r$tag$%s = %ld", full_name, tag->name, tag->value);
|
||||
if (tag->next) enum_def = CORD_cat(enum_def, ", ");
|
||||
}
|
||||
enum_def = CORD_cat(enum_def, "} $tag;\n"
|
||||
"union {\n");
|
||||
CORD constructors = CORD_EMPTY;
|
||||
for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
|
||||
CORD field_header = compile_struct_header(env, WrapAST(ast, StructDef, .name=CORD_to_const_char_star(CORD_all(def->name, "$", tag->name)), .fields=tag->fields));
|
||||
header = CORD_all(header, field_header);
|
||||
enum_def = CORD_all(enum_def, full_name, "$", tag->name, "_t ", tag->name, ";\n");
|
||||
if (tag->fields) { // Constructor macros:
|
||||
CORD arg_sig = CORD_EMPTY;
|
||||
for (arg_ast_t *field = tag->fields; field; field = field->next) {
|
||||
type_t *field_t = get_arg_ast_type(env, field);
|
||||
arg_sig = CORD_all(arg_sig, compile_declaration(env, field_t, field->name));
|
||||
if (field->next) arg_sig = CORD_cat(arg_sig, ", ");
|
||||
}
|
||||
if (arg_sig == CORD_EMPTY) arg_sig = "void";
|
||||
CORD constructor_def = CORD_all(full_name, "_t ", full_name, "$tagged$", tag->name, "(", arg_sig, ");\n");
|
||||
constructors = CORD_cat(constructors, constructor_def);
|
||||
}
|
||||
}
|
||||
enum_def = CORD_cat(enum_def, "};\n};\n");
|
||||
header = CORD_all(header, enum_def, constructors);
|
||||
return CORD_all(header, compile_namespace_headers(env, def->name, def->namespace));
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
||||
|
3
enums.h
3
enums.h
@ -2,9 +2,12 @@
|
||||
|
||||
// Compilation of tagged unions (enums)
|
||||
|
||||
#include <gc/cord.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "environment.h"
|
||||
|
||||
void compile_enum_def(env_t *env, ast_t *ast);
|
||||
CORD compile_enum_header(env_t *env, ast_t *ast);
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
||||
|
@ -235,6 +235,29 @@ env_t *new_compilation_unit(void)
|
||||
return env;
|
||||
}
|
||||
|
||||
env_t *load_module_env(env_t *env, ast_t *ast)
|
||||
{
|
||||
const char *name = file_base_name(ast->file->filename);
|
||||
env_t *cached = Table$str_get(*env->imports, name);
|
||||
if (cached) return cached;
|
||||
env = fresh_scope(env);
|
||||
env->file_prefix = heap_strf("%s$", name);
|
||||
Table$str_set(env->imports, name, env);
|
||||
|
||||
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next)
|
||||
prebind_statement(env, stmt->ast);
|
||||
|
||||
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
|
||||
// Hack: make sure global variables are bound as foo$var:
|
||||
if (stmt->ast->tag == Declare && Match(Match(stmt->ast, Declare)->var, Var)->name[0] != '_')
|
||||
env->scope_prefix = heap_strf("%s$", name);
|
||||
bind_statement(env, stmt->ast);
|
||||
env->scope_prefix = NULL;
|
||||
}
|
||||
Table$str_set(env->imports, name, env);
|
||||
return env;
|
||||
}
|
||||
|
||||
env_t *global_scope(env_t *env)
|
||||
{
|
||||
env_t *scope = new(env_t);
|
||||
|
@ -8,10 +8,7 @@
|
||||
#include "builtins/table.h"
|
||||
|
||||
typedef struct {
|
||||
CORD imports;
|
||||
CORD typedefs;
|
||||
CORD typecode;
|
||||
CORD fndefs;
|
||||
CORD local_typedefs;
|
||||
CORD staticdefs;
|
||||
CORD funcs;
|
||||
CORD typeinfos;
|
||||
@ -49,6 +46,7 @@ typedef struct {
|
||||
} binding_t;
|
||||
|
||||
env_t *new_compilation_unit(void);
|
||||
env_t *load_module_env(env_t *env, ast_t *ast);
|
||||
env_t *global_scope(env_t *env);
|
||||
env_t *fresh_scope(env_t *env);
|
||||
env_t *for_scope(env_t *env, ast_t *ast);
|
||||
|
4
parse.c
4
parse.c
@ -19,6 +19,7 @@ static const char closing[128] = {['(']=')', ['[']=']', ['<']='>', ['{']='}'};
|
||||
typedef struct {
|
||||
file_t *file;
|
||||
jmp_buf *on_err;
|
||||
int64_t next_lambda_id;
|
||||
} parse_ctx_t;
|
||||
|
||||
typedef ast_t* (parser_t)(parse_ctx_t*,const char*);
|
||||
@ -1224,7 +1225,7 @@ PARSER(parse_lambda) {
|
||||
spaces(&pos);
|
||||
expect_closing(ctx, &pos, ")", "I was expecting a ')' to finish this anonymous function's arguments");
|
||||
ast_t *body = optional(ctx, &pos, parse_block);
|
||||
return NewAST(ctx->file, start, pos, Lambda, .args=args, .body=body);
|
||||
return NewAST(ctx->file, start, pos, Lambda, .id=ctx->next_lambda_id++, .args=args, .body=body);
|
||||
}
|
||||
|
||||
PARSER(parse_nil) {
|
||||
@ -2076,6 +2077,7 @@ ast_t *parse_expression_str(const char *str) {
|
||||
parse_ctx_t ctx = {
|
||||
.file=file,
|
||||
.on_err=NULL,
|
||||
.next_lambda_id=0,
|
||||
};
|
||||
|
||||
const char *pos = file->text;
|
||||
|
34
structs.c
34
structs.c
@ -115,20 +115,6 @@ void compile_struct_def(env_t *env, ast_t *ast)
|
||||
{
|
||||
auto def = Match(ast, StructDef);
|
||||
CORD full_name = CORD_cat(env->file_prefix, def->name);
|
||||
CORD_appendf(&env->code->typedefs, "typedef struct %r_s %r_t;\n", full_name, full_name);
|
||||
|
||||
CORD struct_code = CORD_all("struct ", full_name, "_s {\n");
|
||||
for (arg_ast_t *field = def->fields; field; field = field->next) {
|
||||
type_t *field_t = get_arg_ast_type(env, field);
|
||||
CORD type_code = compile_type(env, field_t);
|
||||
CORD_appendf(&struct_code, "%r %s%s;\n", type_code, field->name,
|
||||
CORD_cmp(type_code, "Bool_t") ? "" : ":1");
|
||||
}
|
||||
struct_code = CORD_all(struct_code, "};\n");
|
||||
env->code->typecode = CORD_all(env->code->typecode, struct_code);
|
||||
|
||||
// Typeinfo:
|
||||
CORD_appendf(&env->code->typedefs, "extern const TypeInfo %r;\n", full_name);
|
||||
|
||||
type_t *t = Table$str_get(*env->types, def->name);
|
||||
assert(t && t->tag == StructType);
|
||||
@ -176,4 +162,24 @@ void compile_struct_def(env_t *env, ast_t *ast)
|
||||
compile_namespace(env, def->name, def->namespace);
|
||||
}
|
||||
|
||||
CORD compile_struct_header(env_t *env, ast_t *ast)
|
||||
{
|
||||
auto def = Match(ast, StructDef);
|
||||
CORD full_name = CORD_cat(env->file_prefix, def->name);
|
||||
CORD header = CORD_all("typedef struct ", full_name, "_s ", full_name, "_t;\n");
|
||||
|
||||
CORD struct_code = CORD_all("struct ", full_name, "_s {\n");
|
||||
for (arg_ast_t *field = def->fields; field; field = field->next) {
|
||||
type_t *field_t = get_arg_ast_type(env, field);
|
||||
CORD type_code = compile_type(env, field_t);
|
||||
CORD_appendf(&struct_code, "%r %s%s;\n", type_code, field->name,
|
||||
CORD_cmp(type_code, "Bool_t") ? "" : ":1");
|
||||
}
|
||||
struct_code = CORD_all(struct_code, "};\n");
|
||||
header = CORD_all(header, struct_code);
|
||||
header = CORD_all(header, "extern const TypeInfo ", full_name, ";\n");
|
||||
header = CORD_all(header, compile_namespace_headers(env, def->name, def->namespace));
|
||||
return header;
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
||||
|
@ -2,9 +2,12 @@
|
||||
|
||||
// Compilation of user-defined structs
|
||||
|
||||
#include <gc/cord.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "environment.h"
|
||||
|
||||
void compile_struct_def(env_t *env, ast_t *ast);
|
||||
CORD compile_struct_header(env_t *env, ast_t *ast);
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
||||
|
36
tomo.c
36
tomo.c
@ -26,9 +26,9 @@ static bool cleanup_files = false;
|
||||
static const char *autofmt, *cconfig, *cflags, *objfiles, *ldlibs, *ldflags, *cc;
|
||||
|
||||
static array_t get_file_dependencies(const char *filename, array_t *object_files);
|
||||
static int transpile(const char *filename, bool force_retranspile, module_code_t *module_code);
|
||||
static int transpile(env_t *base_env, const char *filename, bool force_retranspile);
|
||||
static int compile_object_file(const char *filename, bool force_recompile);
|
||||
static int compile_executable(const char *filename, array_t object_files, module_code_t *module_code);
|
||||
static int compile_executable(env_t *base_env, const char *filename, array_t object_files);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@ -120,14 +120,13 @@ int main(int argc, char *argv[])
|
||||
Array$insert(&object_files, &my_obj, 0, $ArrayInfo(&$Text));
|
||||
array_t file_deps = get_file_dependencies(filename, &object_files);
|
||||
|
||||
module_code_t module_code = {};
|
||||
int transpile_status = transpile(filename, true, &module_code);
|
||||
env_t *env = new_compilation_unit();
|
||||
int transpile_status = transpile(env, filename, true);
|
||||
if (transpile_status != 0) return transpile_status;
|
||||
|
||||
for (int64_t i = 0; i < file_deps.length; i++) {
|
||||
const char *dep = *(char**)(file_deps.data + i*file_deps.stride);
|
||||
module_code_t _ = {};
|
||||
transpile_status = transpile(dep, false, &_);
|
||||
transpile_status = transpile(env, dep, false);
|
||||
if (transpile_status != 0) return transpile_status;
|
||||
}
|
||||
|
||||
@ -174,7 +173,7 @@ int main(int argc, char *argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
int executable_status = compile_executable(filename, object_files, &module_code);
|
||||
int executable_status = compile_executable(env, filename, object_files);
|
||||
if (mode == MODE_COMPILE_EXE || executable_status != 0)
|
||||
return executable_status;
|
||||
|
||||
@ -282,7 +281,7 @@ static bool is_stale(const char *filename, const char *relative_to)
|
||||
return target_stat.st_mtime < relative_to_stat.st_mtime;
|
||||
}
|
||||
|
||||
int transpile(const char *filename, bool force_retranspile, module_code_t *module_code)
|
||||
int transpile(env_t *base_env, const char *filename, bool force_retranspile)
|
||||
{
|
||||
const char *tm_file = filename;
|
||||
const char *c_filename = heap_strf("%s.c", tm_file);
|
||||
@ -311,18 +310,19 @@ int transpile(const char *filename, bool force_retranspile, module_code_t *modul
|
||||
pclose(out);
|
||||
}
|
||||
|
||||
*module_code = compile_file(ast);
|
||||
env_t *module_env = load_module_env(base_env, ast);
|
||||
|
||||
CORD h_code = compile_header(module_env, ast);
|
||||
FILE *h_file = fopen(h_filename, "w");
|
||||
if (!h_file)
|
||||
errx(1, "Couldn't open file: %s", h_filename);
|
||||
CORD_put("#pragma once\n", h_file);
|
||||
CORD_put(module_code->header, h_file);
|
||||
CORD_put(h_code, h_file);
|
||||
if (fclose(h_file))
|
||||
errx(1, "Failed to close file: %s", h_filename);
|
||||
if (verbose)
|
||||
printf("Transpiled to %s\n", h_filename);
|
||||
|
||||
CORD c_code = compile_file(module_env, ast);
|
||||
if (autofmt && autofmt[0]) {
|
||||
FILE *prog = popen(heap_strf("%s %s -o %s >/dev/null 2>/dev/null", autofmt, h_filename, h_filename), "w");
|
||||
pclose(prog);
|
||||
@ -331,10 +331,7 @@ int transpile(const char *filename, bool force_retranspile, module_code_t *modul
|
||||
FILE *c_file = fopen(c_filename, "w");
|
||||
if (!c_file)
|
||||
errx(1, "Couldn't open file: %s", c_filename);
|
||||
CORD_put(CORD_all(
|
||||
"#include <tomo/tomo.h>\n"
|
||||
"#include \"", module_code->module_name, ".tm.h\"\n\n",
|
||||
module_code->c_file), c_file);
|
||||
CORD_put(c_code, c_file);
|
||||
if (fclose(c_file))
|
||||
errx(1, "Failed to close file: %s", c_filename);
|
||||
if (verbose)
|
||||
@ -374,9 +371,12 @@ int compile_object_file(const char *filename, bool force_recompile)
|
||||
return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int compile_executable(const char *filename, array_t object_files, module_code_t *module_code)
|
||||
int compile_executable(env_t *base_env, const char *filename, array_t object_files)
|
||||
{
|
||||
binding_t *main_binding = get_binding(module_code->env, "main");
|
||||
const char *name = file_base_name(filename);
|
||||
env_t *env = Table$str_get(*base_env->imports, name);
|
||||
assert(env);
|
||||
binding_t *main_binding = get_binding(env, "main");
|
||||
if (!main_binding || main_binding->type->tag != FunctionType) {
|
||||
errx(1, "No main() function has been defined for %s, so it can't be run!", filename);
|
||||
}
|
||||
@ -395,7 +395,7 @@ int compile_executable(const char *filename, array_t object_files, module_code_t
|
||||
"int main(int argc, char *argv[]) {\n"
|
||||
"tomo_init();\n"
|
||||
"\n",
|
||||
CORD_all(compile_cli_arg_call(module_code->env, main_binding->code, main_binding->type), "return 0;\n"),
|
||||
CORD_all(compile_cli_arg_call(env, main_binding->code, main_binding->type), "return 0;\n"),
|
||||
"}\n"
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user