aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-04-24 13:53:37 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-04-24 13:53:37 -0400
commit061ec4fd8f6679f9484d6a7f11d66edd7bc3ef3d (patch)
treede78992147bba540dd97700bab75e2e8139237f6
parent7aa6b2a4961b5a68b3e1206e4d1e2b042b246236 (diff)
Support naked 'use' statements
-rw-r--r--compile.c29
-rw-r--r--typecheck.c88
2 files changed, 76 insertions, 41 deletions
diff --git a/compile.c b/compile.c
index fa836414..1e46be1c 100644
--- a/compile.c
+++ b/compile.c
@@ -300,17 +300,7 @@ CORD compile_statement(env_t *env, ast_t *ast)
case Declare: {
auto decl = Match(ast, Declare);
if (decl->value->tag == Use) {
- auto use = Match(decl->value, Use);
- const char *path = use->raw_path;
- const char *name = file_base_name(path);
- if (strncmp(path, "./", 2) == 0 || strncmp(path, "../", 3) == 0) {
- env->code->imports = CORD_all(env->code->imports, "#include \"", path, ".h\"\n");
- env->code->object_files = CORD_all(env->code->object_files, "'", path, ".o' ");
- } else {
- env->code->imports = CORD_all(env->code->imports, "#include <", path, ".h>\n");
- env->code->object_files = CORD_all(env->code->object_files, "-l", name, " ");
- }
- return CORD_EMPTY;
+ return compile_statement(env, decl->value);
} else {
type_t *t = get_type(env, decl->value);
if (t->tag == AbortType || t->tag == VoidType)
@@ -790,6 +780,19 @@ CORD compile_statement(env_t *env, ast_t *ast)
return CORD_EMPTY;
}
case InlineCCode: return Match(ast, InlineCCode)->code;
+ case Use: {
+ auto use = Match(ast, Use);
+ const char *path = use->raw_path;
+ const char *name = file_base_name(path);
+ if (strncmp(path, "./", 2) == 0 || strncmp(path, "../", 3) == 0) {
+ env->code->imports = CORD_all(env->code->imports, "#include \"", path, ".h\"\n");
+ env->code->object_files = CORD_all(env->code->object_files, "'", path, ".o' ");
+ } else {
+ env->code->imports = CORD_all(env->code->imports, "#include <", path, ".h>\n");
+ env->code->object_files = CORD_all(env->code->object_files, "-l", name, " ");
+ }
+ return CORD_EMPTY;
+ }
default:
return CORD_asprintf("(void)%r;", compile(env, ast));
}
@@ -1854,7 +1857,7 @@ CORD compile(env_t *env, ast_t *ast)
}
}
case InlineCCode: return Match(ast, InlineCCode)->code;
- case Use: code_err(ast, "Uses are not supported yet");
+ case Use: return CORD_EMPTY;
case LinkerDirective: code_err(ast, "Linker directives are not supported yet");
case Extern: code_err(ast, "Externs are not supported yet");
case TableEntry: code_err(ast, "Table entries should not be compiled directly");
@@ -1864,7 +1867,7 @@ CORD compile(env_t *env, ast_t *ast)
case Unknown: code_err(ast, "Unknown AST");
}
code_err(ast, "Unknown AST: %W", ast);
- return NULL;
+ return CORD_EMPTY;
}
void compile_namespace(env_t *env, const char *ns_name, ast_t *block)
diff --git a/typecheck.c b/typecheck.c
index 4fcbcf20..cba6b41c 100644
--- a/typecheck.c
+++ b/typecheck.c
@@ -102,6 +102,37 @@ type_t *get_math_type(env_t *env, ast_t *ast, type_t *lhs_t, type_t *rhs_t)
code_err(ast, "Math operations between %T and %T are not supported", lhs_t, rhs_t);
}
+static env_t *load_module(env_t *env, ast_t *use_ast)
+{
+ auto use = Match(use_ast, Use);
+ const char *name = file_base_name(use->raw_path);
+ env_t *module_env = Table$str_get(*env->imports, name);
+ if (module_env)
+ return module_env;
+
+ module_env = new_compilation_unit();
+ module_env->file_prefix = heap_strf("%s$", name);
+ Table$str_set(module_env->imports, name, module_env);
+ const char *my_name = heap_strn(CORD_to_const_char_star(env->file_prefix), CORD_len(env->file_prefix)-1);
+ Table$str_set(module_env->imports, my_name, env);
+
+ const char *resolved_path = resolve_path(use->raw_path, use_ast->file->filename, getenv("USE_PATH"));
+ if (!resolved_path)
+ code_err(use_ast, "No such file exists: \"%s\"", use->raw_path);
+
+ file_t *f = load_file(resolved_path);
+ if (!f) errx(1, "No such file: %s", resolved_path);
+
+ ast_t *ast = parse_file(f, NULL);
+ if (!ast) errx(1, "Could not compile!");
+
+ for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
+ bind_statement(module_env, stmt->ast);
+ }
+ Table$str_set(env->imports, name, module_env);
+ return module_env;
+}
+
void bind_statement(env_t *env, ast_t *statement)
{
switch (statement->tag) {
@@ -114,7 +145,10 @@ void bind_statement(env_t *env, ast_t *statement)
const char *name = Match(decl->var, Var)->name;
if (get_binding(env, name))
code_err(decl->var, "A %T called '%s' has already been defined", get_binding(env, name)->type, name);
- bind_statement(env, decl->value);
+ if (decl->value->tag == Use)
+ (void)load_module(env, decl->value);
+ else
+ bind_statement(env, decl->value);
type_t *type = get_type(env, decl->value);
CORD code = CORD_cat(env->scope_prefix ? env->scope_prefix : "$", name);
set_binding(env, name, new(binding_t, .type=type, .code=code));
@@ -153,7 +187,7 @@ void bind_statement(env_t *env, ast_t *statement)
type->__data.StructType.opaque = false;
type_t *typeinfo_type = Type(TypeInfoType, .name=def->name, .type=type, .env=ns_env);
- Table$str_set(env->globals, def->name, new(binding_t, .type=typeinfo_type, .code=CORD_all(env->file_prefix, def->name)));
+ Table$str_set(env->locals, def->name, new(binding_t, .type=typeinfo_type, .code=CORD_all(env->file_prefix, def->name)));
for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) {
bind_statement(ns_env, stmt->ast);
@@ -199,7 +233,7 @@ void bind_statement(env_t *env, ast_t *statement)
}
type_t *typeinfo_type = Type(TypeInfoType, .name=def->name, .type=type, .env=ns_env);
- Table$str_set(env->globals, def->name, new(binding_t, .type=typeinfo_type));
+ Table$str_set(env->locals, def->name, new(binding_t, .type=typeinfo_type));
for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) {
bind_statement(ns_env, stmt->ast);
@@ -223,38 +257,36 @@ void bind_statement(env_t *env, ast_t *statement)
.code="(Text_t)"));
type_t *typeinfo_type = Type(TypeInfoType, .name=def->name, .type=type, .env=ns_env);
- Table$str_set(env->globals, def->name, new(binding_t, .type=typeinfo_type));
+ Table$str_set(env->locals, def->name, new(binding_t, .type=typeinfo_type));
for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next)
bind_statement(ns_env, stmt->ast);
break;
}
case Use: {
- auto use = Match(statement, Use);
- const char *name = file_base_name(use->raw_path);
- if (Table$str_get(*env->imports, name))
- break;
-
- env_t *module_env = new_compilation_unit();
- module_env->file_prefix = heap_strf("%s$", name);
- Table$str_set(module_env->imports, name, module_env);
- const char *my_name = heap_strn(CORD_to_const_char_star(env->file_prefix), CORD_len(env->file_prefix)-1);
- Table$str_set(module_env->imports, my_name, env);
-
- const char *resolved_path = resolve_path(use->raw_path, statement->file->filename, getenv("USE_PATH"));
- if (!resolved_path)
- code_err(statement, "No such file exists: \"%s\"", use->raw_path);
-
- file_t *f = load_file(resolved_path);
- if (!f) errx(1, "No such file: %s", resolved_path);
-
- ast_t *ast = parse_file(f, NULL);
- if (!ast) errx(1, "Could not compile!");
-
- for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
- bind_statement(module_env, stmt->ast);
+ env_t *module_env = load_module(env, statement);
+ for (table_t *bindings = module_env->locals; bindings != module_env->globals; bindings = bindings->fallback) {
+ for (int64_t i = 1; i <= Table$length(*bindings); i++) {
+ struct {const char *name; binding_t *binding; } *entry = Table$entry(*bindings, i);
+ if (entry->name[0] == '_')
+ continue;
+ if (Table$str_get(*env->locals, entry->name))
+ code_err(statement, "This module imports a symbol called '%s', which would clobber another variable", entry->name);
+ printf("Imported binding: %s\n", entry->name);
+ Table$str_set(env->locals, entry->name, entry->binding);
+ }
+ }
+ for (int64_t i = 1; i <= Table$length(*module_env->types); i++) {
+ struct {const char *name; type_t *type; } *entry = Table$entry(*module_env->types, i);
+ if (entry->name[0] == '_')
+ continue;
+ if (Table$str_get(*env->types, entry->name))
+ continue;
+
+//code_err(statement, "This module imports a type called '%s', which would clobber another type", entry->name);
+ printf("Imported type: %s\n", entry->name);
+ Table$str_set(env->types, entry->name, entry->type);
}
- Table$str_set(env->imports, name, module_env);
break;
}
case Extern: {