1 // This file defines how to compile files
7 #include "../environment.h"
9 #include "../packages.h"
10 #include "../stdlib/datatypes.h"
11 #include "../stdlib/paths.h"
12 #include "../stdlib/text.h"
13 #include "../typecheck.h"
15 #include "compilation.h"
18 Text_t compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t *ast) {
23 DeclareMatch(def, ast, LangDef);
24 ns_env = namespace_env(env, def->name);
25 block = def->namespace;
29 DeclareMatch(def, ast, StructDef);
30 ns_env = namespace_env(env, def->name);
31 block = def->namespace;
35 DeclareMatch(def, ast, EnumDef);
36 ns_env = namespace_env(env, def->name);
37 block = def->namespace;
41 DeclareMatch(decl, ast, Declare);
42 const char *decl_name = Match(decl->var, Var)->name;
43 bool is_private = (decl_name[0] == '_');
44 if (is_private) return EMPTY_TEXT;
46 type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value);
47 if (t->tag == FunctionType) t = Type(ClosureType, t);
48 assert(t->tag != ModuleType);
49 if (t->tag == AbortType || t->tag == VoidType || t->tag == ReturnType)
50 code_err(ast, "You can't declare a variable with a ", type_to_text(t), " value");
52 return Texts(decl->value ? compile_statement_type_header(env, header_path, decl->value) : EMPTY_TEXT, "extern ",
53 compile_declaration(t, namespace_name(env, env->namespace, Text$from_str(decl_name))), ";\n");
55 case FunctionDef: return compile_function_declaration(env, ast);
56 case ConvertDef: return compile_convert_declaration(env, ast);
57 default: return EMPTY_TEXT;
60 Text_t header = EMPTY_TEXT;
61 for (ast_list_t *stmt = block ? Match(block, Block)->statements : NULL; stmt; stmt = stmt->next) {
62 header = Texts(header, compile_statement_namespace_header(ns_env, header_path, stmt->ast));
71 } compile_typedef_info_t;
73 static void _make_typedefs(compile_typedef_info_t *info, ast_t *ast) {
74 if (ast->tag == StructDef) {
75 DeclareMatch(def, ast, StructDef);
76 if (def->external) return;
77 Text_t struct_name = namespace_name(info->env, info->env->namespace, Texts(def->name, "$$struct"));
78 Text_t type_name = namespace_name(info->env, info->env->namespace, Texts(def->name, "$$type"));
79 *info->header = Texts(*info->header, "typedef struct ", struct_name, " ", type_name, ";\n");
80 } else if (ast->tag == EnumDef) {
81 DeclareMatch(def, ast, EnumDef);
82 Text_t struct_name = namespace_name(info->env, info->env->namespace, Texts(def->name, "$$struct"));
83 Text_t type_name = namespace_name(info->env, info->env->namespace, Texts(def->name, "$$type"));
84 *info->header = Texts(*info->header, "typedef struct ", struct_name, " ", type_name, ";\n");
86 for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
88 namespace_name(info->env, info->env->namespace, Texts(def->name, "$", tag->name, "$$struct"));
90 namespace_name(info->env, info->env->namespace, Texts(def->name, "$", tag->name, "$$type"));
91 *info->header = Texts(*info->header, "typedef struct ", tag_struct, " ", tag_type, ";\n");
93 } else if (ast->tag == LangDef) {
94 DeclareMatch(def, ast, LangDef);
95 *info->header = Texts(*info->header, "typedef Text_t ",
96 namespace_name(info->env, info->env->namespace, Texts(def->name, "$$type")), ";\n");
97 *info->header = Texts(*info->header, "typedef Text_t ", "Optional",
98 namespace_name(info->env, info->env->namespace, Texts(def->name, "$$type")), ";\n");
102 static void _define_types_and_funcs(compile_typedef_info_t *info, ast_t *ast) {
103 *info->header = Texts(*info->header, compile_statement_type_header(info->env, info->header_path, ast),
104 compile_statement_namespace_header(info->env, info->header_path, ast));
107 static visit_behavior_t add_type_headers(type_ast_t *type_ast, void *userdata) {
108 if (!type_ast) return VISIT_STOP;
110 if (type_ast->tag == EnumTypeAST) {
111 compile_typedef_info_t *info = (compile_typedef_info_t *)userdata;
112 // Force the type to get defined:
113 (void)parse_type_ast(info->env, type_ast);
114 DeclareMatch(enum_, type_ast, EnumTypeAST);
115 const char *name = String("enum$", (int64_t)(type_ast->start - type_ast->file->text));
116 Text_t struct_name = namespace_name(info->env, info->env->namespace, Texts(name, "$$struct"));
117 Text_t type_name = namespace_name(info->env, info->env->namespace, Texts(name, "$$type"));
118 *info->header = Texts(*info->header, "typedef struct ", struct_name, " ", type_name, ";\n");
120 for (tag_ast_t *tag = enum_->tags; tag; tag = tag->next) {
122 namespace_name(info->env, info->env->namespace, Texts(name, "$", tag->name, "$$struct"));
123 Text_t tag_type = namespace_name(info->env, info->env->namespace, Texts(name, "$", tag->name, "$$type"));
124 *info->header = Texts(*info->header, "typedef struct ", tag_struct, " ", tag_type, ";\n");
127 *info->header = Texts(*info->header, compile_enum_header(info->env, name, enum_->tags));
130 return VISIT_PROCEED;
134 Text_t compile_file_header(env_t *env, Path_t header_path, ast_t *ast) {
136 Texts("#pragma once\n",
137 env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT,
138 "#include <tomo@", TOMO_VERSION, "/tomo.h>\n");
140 compile_typedef_info_t info = {.env = env, .header = &header, .header_path = header_path};
141 visit_topologically(Match(ast, Block)->statements, (Closure_t){.fn = (void *)_make_typedefs, &info});
143 type_ast_visit(ast, add_type_headers, &info);
145 visit_topologically(Match(ast, Block)->statements, (Closure_t){.fn = (void *)_define_types_and_funcs, &info});
147 header = Texts(header, "void ", namespace_name(env, env->namespace, Text("$initialize")), "(void);\n");
152 Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast) {
155 DeclareMatch(use, ast, Use);
156 Path_t source_path = Path$from_str(ast->file->filename);
157 Path_t source_dir = Path$parent(source_path);
158 Path_t build_dir = Path$resolved(Path$parent(header_path), Path$current_dir());
161 OptionalPath_t installed = find_installed_package(ast);
163 List_t children = Path$glob(Path$child(installed, Text("/[!._0-9]*.tm")));
164 Text_t includes = EMPTY_TEXT;
165 for (int64_t i = 0; i < (int64_t)children.length; i++) {
166 Path_t tm_file = *(Path_t *)(children.data + i * children.stride);
167 Path_t lib_build_dir = Path$sibling(tm_file, Text(".build"));
168 Path_t header = Path$child(lib_build_dir, Texts(Path$base_name(tm_file), Text(".h")));
169 includes = Texts(includes, "#include \"", Path$as_c_string(header), "\"\n");
171 return with_source_info(env, ast, includes);
174 Path_t used_path = Path$resolved(Path$from_str(use->path), source_dir);
175 Path_t used_build_dir = Path$sibling(used_path, Text(".build"));
176 Path_t used_header_path = Path$child(used_build_dir, Texts(Path$base_name(used_path), Text(".h")));
177 return Texts("#include \"", Path$as_c_string(Path$relative_to(used_header_path, build_dir)), "\"\n");
180 if (use->path[0] == '<') {
181 return Texts("#include ", use->path, "\n");
183 Path_t used_path = Path$resolved(Path$from_str(use->path), source_dir);
184 return Texts("#include \"", Path$as_c_string(Path$relative_to(used_path, build_dir)), "\"\n");
186 default: return EMPTY_TEXT;
190 return compile_struct_header(env, ast);
193 DeclareMatch(def, ast, EnumDef);
194 return compile_enum_header(env, def->name, def->tags);
197 DeclareMatch(def, ast, LangDef);
199 // Constructor macro:
200 "#define ", namespace_name(env, env->namespace, Text$from_str(def->name)), "(text) ((",
201 namespace_name(env, env->namespace, Texts(def->name, "$$type")),
202 "){.length=sizeof(text)-1, .tag=TEXT_ASCII, .ascii=\"\" "
205 namespace_name(env, env->namespace, Text$from_str(def->name)), "s(...) ((",
206 namespace_name(env, env->namespace, Texts(def->name, "$$type")),
207 ")Texts(__VA_ARGS__))\n"
208 "extern const TypeInfo_t ",
209 namespace_name(env, env->namespace, Texts(def->name, Text("$$info"))), ";\n");
211 default: return EMPTY_TEXT;