1 // This file defines how to compile files
5 #include "../environment.h"
7 #include "../stdlib/datatypes.h"
8 #include "../stdlib/paths.h"
9 #include "../stdlib/tables.h"
10 #include "../stdlib/text.h"
11 #include "../typecheck.h"
13 #include "compilation.h"
15 static void initialize_vars_and_statics(env_t *env, ast_t *ast);
16 static void initialize_namespace(env_t *env, const char *name, ast_t *namespace);
17 static Text_t compile_top_level_code(env_t *env, ast_t *ast);
18 static Text_t compile_namespace(env_t *env, const char *name, ast_t *namespace);
20 void initialize_namespace(env_t *env, const char *name, ast_t *namespace) {
21 initialize_vars_and_statics(namespace_env(env, name), namespace);
24 void initialize_vars_and_statics(env_t *env, ast_t *ast) {
27 for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
28 if (stmt->ast->tag == InlineCCode) {
29 Text_t code = compile_statement(env, stmt->ast);
30 env->code->staticdefs = Texts(env->code->staticdefs, code, "\n");
31 } else if (stmt->ast->tag == Declare) {
32 DeclareMatch(decl, stmt->ast, Declare);
33 const char *decl_name = Match(decl->var, Var)->name;
34 Text_t full_name = namespace_name(env, env->namespace, Text$from_str(decl_name));
35 type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value);
36 if (t->tag == FunctionType) t = Type(ClosureType, t);
37 Text_t val_code = compile_declared_value(env, stmt->ast);
38 if ((decl->value && !is_constant(env, decl->value)) || (!decl->value && has_heap_memory(t))) {
39 Text_t initialized_name = namespace_name(env, env->namespace, Texts(decl_name, "$$initialized"));
40 env->code->variable_initializers =
41 Texts(env->code->variable_initializers,
42 with_source_info(env, stmt->ast,
43 Texts(full_name, " = ", val_code, ",\n", initialized_name, " = true;\n")));
45 } else if (stmt->ast->tag == StructDef) {
46 initialize_namespace(env, Match(stmt->ast, StructDef)->name, Match(stmt->ast, StructDef)->namespace);
47 } else if (stmt->ast->tag == EnumDef) {
48 initialize_namespace(env, Match(stmt->ast, EnumDef)->name, Match(stmt->ast, EnumDef)->namespace);
49 } else if (stmt->ast->tag == LangDef) {
50 initialize_namespace(env, Match(stmt->ast, LangDef)->name, Match(stmt->ast, LangDef)->namespace);
51 } else if (stmt->ast->tag == Use) {
54 Text_t code = compile_statement(env, stmt->ast);
55 if (code.length > 0) code_err(stmt->ast, "I did not expect this to generate code");
60 Text_t compile_namespace(env_t *env, const char *name, ast_t *namespace) {
61 env_t *ns_env = namespace_env(env, name);
62 return namespace ? compile_top_level_code(ns_env, namespace) : EMPTY_TEXT;
65 Text_t compile_top_level_code(env_t *env, ast_t *ast) {
66 if (!ast) return EMPTY_TEXT;
70 // DeclareMatch(use, ast, Use);
71 // if (use->what == USE_C_CODE) {
72 // Path_t path = Path$relative_to(Path$from_str(use->path),
73 // Path(".build")); return Texts("#include \"",
74 // Path$as_c_string(path),
80 DeclareMatch(decl, ast, Declare);
81 const char *decl_name = Match(decl->var, Var)->name;
82 Text_t full_name = namespace_name(env, env->namespace, Text$from_str(decl_name));
83 type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value);
84 if (t->tag == FunctionType) t = Type(ClosureType, t);
85 Text_t val_code = compile_declared_value(env, ast);
86 bool is_private = decl_name[0] == '_';
87 if ((decl->value && is_constant(env, decl->value)) || (!decl->value && !has_heap_memory(t))) {
88 set_binding(env, decl_name, t, full_name);
89 return Texts(is_private ? "static " : "public ", compile_declaration(t, full_name), " = ", val_code, ";\n");
91 Text_t init_var = namespace_name(env, env->namespace, Texts(decl_name, "$$initialized"));
92 Text_t checked_access = Texts("check_initialized(", full_name, ", ", init_var, ", \"", decl_name, "\")");
93 set_binding(env, decl_name, t, checked_access);
95 Text_t initialized_name = namespace_name(env, env->namespace, Texts(decl_name, "$$initialized"));
96 return Texts("static bool ", initialized_name, " = false;\n", is_private ? "static " : "public ",
97 compile_declaration(t, full_name), ";\n");
102 namespace_name(env, env->namespace, Text$from_str(Match(Match(ast, FunctionDef)->name, Var)->name));
103 return compile_function(env, name_code, ast, &env->code->staticdefs);
106 type_t *type = get_function_return_type(env, ast);
107 const char *name = get_type_name(type);
110 "Conversions are only supported for text, struct, and enum "
114 namespace_name(env, env->namespace, Texts(name, "$", get_line_number(ast->file, ast->start)));
115 return compile_function(env, name_code, ast, &env->code->staticdefs);
118 DeclareMatch(def, ast, StructDef);
119 type_t *t = Table$str_get(*env->types, def->name);
120 assert(t && t->tag == StructType);
121 Text_t code = compile_struct_typeinfo(env, t, def->name, def->fields, def->secret, def->opaque);
122 return Texts(code, compile_namespace(env, def->name, def->namespace));
125 DeclareMatch(def, ast, EnumDef);
126 Text_t code = compile_enum_typeinfo(env, def->name, def->tags);
127 code = Texts(code, compile_enum_constructors(env, def->name, def->tags));
128 return Texts(code, compile_namespace(env, def->name, def->namespace));
131 DeclareMatch(def, ast, LangDef);
133 Texts("public const TypeInfo_t ", namespace_name(env, env->namespace, Texts(def->name, "$$info")), " = {",
134 (int64_t)sizeof(Text_t), ", ", (int64_t)__alignof__(Text_t),
135 ", .metamethods=Text$metamethods, .tag=TextInfo, .TextInfo={", quoted_str(def->name), "}};\n");
136 return Texts(code, compile_namespace(env, def->name, def->namespace));
139 Text_t code = EMPTY_TEXT;
140 for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
141 code = Texts(code, compile_top_level_code(env, stmt->ast));
146 default: return EMPTY_TEXT;
155 static visit_behavior_t add_type_infos(type_ast_t *type_ast, void *userdata) {
156 if (type_ast && type_ast->tag == EnumTypeAST) {
157 compile_info_t *info = (compile_info_t *)userdata;
158 // Force the type to get defined:
159 (void)parse_type_ast(info->env, type_ast);
162 compile_enum_typeinfo(info->env, String("enum$", (int64_t)(type_ast->start - type_ast->file->text)),
163 Match(type_ast, EnumTypeAST)->tags),
164 compile_enum_constructors(info->env, String("enum$", (int64_t)(type_ast->start - type_ast->file->text)),
165 Match(type_ast, EnumTypeAST)->tags));
167 return VISIT_PROCEED;
171 Text_t compile_file(env_t *env, ast_t *ast) {
172 Text_t top_level_code = compile_top_level_code(env, ast);
174 compile_info_t info = {.env = env, .code = &top_level_code};
175 type_ast_visit(ast, add_type_infos, &info);
177 Text_t includes = EMPTY_TEXT;
178 Text_t use_imports = EMPTY_TEXT;
180 // First prepare variable initializers to prevent unitialized access:
181 for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
182 if (stmt->ast->tag == Use) {
183 use_imports = Texts(use_imports, compile_statement(env, stmt->ast));
185 DeclareMatch(use, stmt->ast, Use);
186 if (use->what == USE_C_CODE) {
187 Path_t path = Path$from_str(use->path);
188 if (path[0] != '/') {
189 // If we have `use ./foo.c`, then we need to remap it in source code to
190 // `#include "../foo.c"`, since it will be inside the .build directory.
191 path = Path$relative_to(Path$from_str(use->path), Path(ast->file->filename));
192 path = Path$concat("..", path);
194 includes = Texts(includes, "#include \"", Path$as_c_string(path), "\"\n");
199 initialize_vars_and_statics(env, ast);
201 const char *name = file_base_name(ast->file->filename);
202 return Texts(env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT,
203 "#define __SOURCE_FILE__ ", quoted_str(ast->file->filename), "\n", "#include <tomo@", TOMO_VERSION,
206 name, ".tm.h\"\n\n", includes, env->code->local_typedefs, "\n", env->code->lambdas, "\n",
207 env->code->staticdefs, "\n", top_level_code, "public void ",
208 namespace_name(env, env->namespace, Text("$initialize")), "(void) {\n",
209 "static bool initialized = false;\n", "if (initialized) return;\n", "initialized = true;\n",
210 use_imports, env->code->variable_initializers, "}\n");