From 8c7d53008072dfda8b9d60be92fae1a8046fae5d Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Thu, 6 Jun 2024 16:28:53 -0400 Subject: [PATCH] Split header compilation into a separate function --- ast.h | 1 + compile.c | 294 +++++++++++++++++++++++++++++++++----------------- compile.h | 10 +- enums.c | 52 ++++++--- enums.h | 3 + environment.c | 23 ++++ environment.h | 6 +- parse.c | 4 +- structs.c | 34 +++--- structs.h | 3 + tomo.c | 36 +++---- 11 files changed, 307 insertions(+), 159 deletions(-) diff --git a/ast.h b/ast.h index e9e4dc3..9d549e8 100644 --- a/ast.h +++ b/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; diff --git a/compile.c b/compile.c index a696ac8..1cf6e5e 100644 --- a/compile.c +++ b/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 \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 \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 \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 diff --git a/compile.h b/compile.h index a868b12..04715e5 100644 --- a/compile.h +++ b/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); diff --git a/enums.c b/enums.c index e9256b1..ee63157 100644 --- a/enums.c +++ b/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 diff --git a/enums.h b/enums.h index f69ae63..68b03ec 100644 --- a/enums.h +++ b/enums.h @@ -2,9 +2,12 @@ // Compilation of tagged unions (enums) +#include + #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 diff --git a/environment.c b/environment.c index d5f6a15..0c37c52 100644 --- a/environment.c +++ b/environment.c @@ -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); diff --git a/environment.h b/environment.h index 301ffbb..0582dfc 100644 --- a/environment.h +++ b/environment.h @@ -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); diff --git a/parse.c b/parse.c index 539c295..c6d8cec 100644 --- a/parse.c +++ b/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; diff --git a/structs.c b/structs.c index 57b5355..bd1aa7d 100644 --- a/structs.c +++ b/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 diff --git a/structs.h b/structs.h index 9a17f31..166a382 100644 --- a/structs.h +++ b/structs.h @@ -2,9 +2,12 @@ // Compilation of user-defined structs +#include + #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 diff --git a/tomo.c b/tomo.c index c81f50b..1d73cb3 100644 --- a/tomo.c +++ b/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 \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" );