diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2025-08-24 17:07:41 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2025-08-24 17:07:41 -0400 |
| commit | 476bacc27671de92e8fdbc5b2a904bbf8bc80377 (patch) | |
| tree | 035183cccc5aa42cb5885dbbccdb6f9cf4f2aa23 /src/compile/files.c | |
| parent | e4d080f598b8528d5200deccde279ff02ac5750e (diff) | |
More splitting out into separate files.
Diffstat (limited to 'src/compile/files.c')
| -rw-r--r-- | src/compile/files.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/src/compile/files.c b/src/compile/files.c new file mode 100644 index 00000000..d9f4052a --- /dev/null +++ b/src/compile/files.c @@ -0,0 +1,195 @@ +#include "../ast.h" +#include "../compile.h" +#include "../environment.h" +#include "../naming.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/paths.h" +#include "../stdlib/tables.h" +#include "../stdlib/text.h" +#include "../typecheck.h" +#include "../types.h" +#include "assignments.h" +#include "enums.h" +#include "functions.h" +#include "statements.h" +#include "structs.h" +#include "types.h" + +static Text_t quoted_str(const char *str) { return Text$quoted(Text$from_str(str), false, Text("\"")); } + +static inline Text_t quoted_text(Text_t text) { return Text$quoted(text, false, Text("\"")); } + +static void initialize_vars_and_statics(env_t *env, ast_t *ast) { + if (!ast) return; + + for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) { + if (stmt->ast->tag == InlineCCode) { + Text_t code = compile_statement(env, stmt->ast); + env->code->staticdefs = Texts(env->code->staticdefs, code, "\n"); + } else if (stmt->ast->tag == Declare) { + DeclareMatch(decl, stmt->ast, Declare); + const char *decl_name = Match(decl->var, Var)->name; + Text_t full_name = namespace_name(env, env->namespace, Text$from_str(decl_name)); + type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value); + if (t->tag == FunctionType) t = Type(ClosureType, t); + Text_t val_code = compile_declared_value(env, stmt->ast); + if ((decl->value && !is_constant(env, decl->value)) || (!decl->value && has_heap_memory(t))) { + Text_t initialized_name = namespace_name(env, env->namespace, Texts(decl_name, "$$initialized")); + env->code->variable_initializers = + Texts(env->code->variable_initializers, + with_source_info(env, stmt->ast, + Texts(full_name, " = ", val_code, ",\n", initialized_name, " = true;\n"))); + } + } else if (stmt->ast->tag == StructDef) { + initialize_vars_and_statics(namespace_env(env, Match(stmt->ast, StructDef)->name), + Match(stmt->ast, StructDef)->namespace); + } else if (stmt->ast->tag == EnumDef) { + initialize_vars_and_statics(namespace_env(env, Match(stmt->ast, EnumDef)->name), + Match(stmt->ast, EnumDef)->namespace); + } else if (stmt->ast->tag == LangDef) { + initialize_vars_and_statics(namespace_env(env, Match(stmt->ast, LangDef)->name), + Match(stmt->ast, LangDef)->namespace); + } else if (stmt->ast->tag == Extend) { + initialize_vars_and_statics(namespace_env(env, Match(stmt->ast, Extend)->name), + Match(stmt->ast, Extend)->body); + } else if (stmt->ast->tag == Use) { + continue; + } else { + Text_t code = compile_statement(env, stmt->ast); + if (code.length > 0) code_err(stmt->ast, "I did not expect this to generate code"); + } + } +} + +static Text_t compile_top_level_code(env_t *env, ast_t *ast) { + if (!ast) return EMPTY_TEXT; + + switch (ast->tag) { + case Use: { + // DeclareMatch(use, ast, Use); + // if (use->what == USE_C_CODE) { + // Path_t path = Path$relative_to(Path$from_str(use->path), + // Path(".build")); return Texts("#include \"", + // Path$as_c_string(path), + // "\"\n"); + // } + return EMPTY_TEXT; + } + case Declare: { + DeclareMatch(decl, ast, Declare); + const char *decl_name = Match(decl->var, Var)->name; + Text_t full_name = namespace_name(env, env->namespace, Text$from_str(decl_name)); + type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value); + if (t->tag == FunctionType) t = Type(ClosureType, t); + Text_t val_code = compile_declared_value(env, ast); + bool is_private = decl_name[0] == '_'; + if ((decl->value && is_constant(env, decl->value)) || (!decl->value && !has_heap_memory(t))) { + set_binding(env, decl_name, t, full_name); + return Texts(is_private ? "static " : "public ", compile_declaration(t, full_name), " = ", val_code, ";\n"); + } else { + Text_t init_var = namespace_name(env, env->namespace, Texts(decl_name, "$$initialized")); + Text_t checked_access = Texts("check_initialized(", full_name, ", ", init_var, ", \"", decl_name, "\")"); + set_binding(env, decl_name, t, checked_access); + + Text_t initialized_name = namespace_name(env, env->namespace, Texts(decl_name, "$$initialized")); + return Texts("static bool ", initialized_name, " = false;\n", is_private ? "static " : "public ", + compile_declaration(t, full_name), ";\n"); + } + } + case FunctionDef: { + Text_t name_code = + namespace_name(env, env->namespace, Text$from_str(Match(Match(ast, FunctionDef)->name, Var)->name)); + return compile_function(env, name_code, ast, &env->code->staticdefs); + } + case ConvertDef: { + type_t *type = get_function_def_type(env, ast); + const char *name = get_type_name(Match(type, FunctionType)->ret); + if (!name) + code_err(ast, + "Conversions are only supported for text, struct, and enum " + "types, not ", + type_to_str(Match(type, FunctionType)->ret)); + Text_t name_code = + namespace_name(env, env->namespace, Texts(name, "$", String(get_line_number(ast->file, ast->start)))); + return compile_function(env, name_code, ast, &env->code->staticdefs); + } + case StructDef: { + DeclareMatch(def, ast, StructDef); + type_t *t = Table$str_get(*env->types, def->name); + assert(t && t->tag == StructType); + Text_t code = compile_struct_typeinfo(env, t, def->name, def->fields, def->secret, def->opaque); + env_t *ns_env = namespace_env(env, def->name); + return Texts(code, def->namespace ? compile_top_level_code(ns_env, def->namespace) : EMPTY_TEXT); + } + case EnumDef: { + DeclareMatch(def, ast, EnumDef); + Text_t code = compile_enum_typeinfo(env, ast); + code = Texts(code, compile_enum_constructors(env, ast)); + env_t *ns_env = namespace_env(env, def->name); + return Texts(code, def->namespace ? compile_top_level_code(ns_env, def->namespace) : EMPTY_TEXT); + } + case LangDef: { + DeclareMatch(def, ast, LangDef); + Text_t code = + Texts("public const TypeInfo_t ", namespace_name(env, env->namespace, Texts(def->name, "$$info")), " = {", + String((int64_t)sizeof(Text_t)), ", ", String((int64_t)__alignof__(Text_t)), + ", .metamethods=Text$metamethods, .tag=TextInfo, .TextInfo={", quoted_str(def->name), "}};\n"); + env_t *ns_env = namespace_env(env, def->name); + return Texts(code, def->namespace ? compile_top_level_code(ns_env, def->namespace) : EMPTY_TEXT); + } + case Extend: { + DeclareMatch(extend, ast, Extend); + binding_t *b = get_binding(env, extend->name); + if (!b || b->type->tag != TypeInfoType) + code_err(ast, "'", extend->name, "' is not the name of any type I recognize."); + env_t *ns_env = Match(b->type, TypeInfoType)->env; + env_t *extended = new (env_t); + *extended = *ns_env; + extended->locals = new (Table_t, .fallback = env->locals); + extended->namespace_bindings = new (Table_t, .fallback = env->namespace_bindings); + extended->id_suffix = env->id_suffix; + return compile_top_level_code(extended, extend->body); + } + case Extern: return EMPTY_TEXT; + case Block: { + Text_t code = EMPTY_TEXT; + for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) { + code = Texts(code, compile_top_level_code(env, stmt->ast)); + } + return code; + } + default: return EMPTY_TEXT; + } +} + +Text_t compile_file(env_t *env, ast_t *ast) { + Text_t top_level_code = compile_top_level_code(env, ast); + Text_t includes = EMPTY_TEXT; + Text_t use_imports = EMPTY_TEXT; + + // First prepare variable initializers to prevent unitialized access: + for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) { + if (stmt->ast->tag == Use) { + use_imports = Texts(use_imports, compile_statement(env, stmt->ast)); + + DeclareMatch(use, stmt->ast, Use); + if (use->what == USE_C_CODE) { + Path_t path = Path$relative_to(Path$from_str(use->path), Path(".build")); + includes = Texts(includes, "#include \"", Path$as_c_string(path), "\"\n"); + } + } + } + + initialize_vars_and_statics(env, ast); + + const char *name = file_base_name(ast->file->filename); + return Texts(env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT, + "#define __SOURCE_FILE__ ", quoted_str(ast->file->filename), "\n", + "#include <tomo_" TOMO_VERSION "/tomo.h>\n" + "#include \"", + name, ".tm.h\"\n\n", includes, env->code->local_typedefs, "\n", env->code->lambdas, "\n", + env->code->staticdefs, "\n", top_level_code, "public void ", + namespace_name(env, env->namespace, Text("$initialize")), "(void) {\n", + "static bool initialized = false;\n", "if (initialized) return;\n", "initialized = true;\n", + use_imports, env->code->variable_initializers, "}\n"); +} |
