diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-06-13 13:17:51 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-06-13 13:17:51 -0400 |
| commit | dab2c399f1b574b598c908124acebaa06c80938a (patch) | |
| tree | 16cfc4ea17a35155a67d663c2f41f5834cf1d6f0 | |
| parent | 5d682df66c96c4f718a01cb5a738986e3510fcf9 (diff) | |
Split import/use into separate concepts
| -rw-r--r-- | ast.c | 3 | ||||
| -rw-r--r-- | ast.h | 7 | ||||
| -rw-r--r-- | compile.c | 26 | ||||
| -rw-r--r-- | parse.c | 24 | ||||
| -rw-r--r-- | tomo.c | 29 | ||||
| -rw-r--r-- | typecheck.c | 52 |
6 files changed, 82 insertions, 59 deletions
@@ -140,7 +140,8 @@ CORD ast_to_xml(ast_t *ast) T(FieldAccess, "<FieldAccess field=\"%s\">%r</FieldAccess>", data.field, ast_to_xml(data.fielded)) T(Optional, "<Optional>%r</Optional>", ast_to_xml(data.value)) T(DocTest, "<DocTest>%r<output>%r</output></DocTest>", optional_tagged("expression", data.expr), xml_escape(data.output)) - T(Use, "<Use>%r</Use>", xml_escape(data.raw_path)) + T(Import, "<Import>%r</Import>", xml_escape(data.path)) + T(Use, "<Use>%r</Use>", xml_escape(data.name)) T(LinkerDirective, "<LinkerDirective>%r</LinkerDirective>", xml_escape(data.directive)) T(InlineCCode, "<InlineCode>%r</InlineCode>", xml_escape(data.code)) #undef T @@ -114,7 +114,7 @@ typedef enum { StructDef, EnumDef, LangDef, Index, FieldAccess, Optional, DocTest, - Use, + Import, Use, LinkerDirective, InlineCCode, } ast_e; @@ -267,7 +267,10 @@ struct ast_s { bool skip_source:1; } DocTest; struct { - const char *raw_path; + const char *path; + } Import; + struct { + const char *name; } Use; struct { const char *directive; @@ -291,7 +291,7 @@ CORD compile_statement(env_t *env, ast_t *ast) if (test->expr->tag == Declare) { auto decl = Match(test->expr, Declare); - if (decl->value->tag == Use) { + if (decl->value->tag == Use || decl->value->tag == Import) { assert(compile_statement(env, test->expr) == CORD_EMPTY); return CORD_asprintf( "test(NULL, NULL, %r, %r, %ld, %ld);", @@ -387,7 +387,7 @@ CORD compile_statement(env_t *env, ast_t *ast) } case Declare: { auto decl = Match(ast, Declare); - if (decl->value->tag == Use) { + if (decl->value->tag == Use || decl->value->tag == Import) { return compile_statement(env, decl->value); } else { type_t *t = get_type(env, decl->value); @@ -843,7 +843,7 @@ CORD compile_statement(env_t *env, ast_t *ast) } case Extern: return CORD_EMPTY; case InlineCCode: return Match(ast, InlineCCode)->code; - case Use: return CORD_EMPTY; + case Use: case Import: return CORD_EMPTY; default: if (!is_discardable(env, ast)) code_err(ast, "The result of this statement cannot be discarded"); @@ -1917,6 +1917,7 @@ CORD compile(env_t *env, ast_t *ast) return Match(ast, InlineCCode)->code; } case Use: code_err(ast, "Compiling 'use' as expression!"); + case Import: code_err(ast, "Compiling 'import' as expression!"); case LinkerDirective: code_err(ast, "Linker directives are not supported yet"); case Extern: code_err(ast, "Externs are not supported as expressions"); case TableEntry: code_err(ast, "Table entries should not be compiled directly"); @@ -2196,7 +2197,7 @@ CORD compile_file(env_t *env, ast_t *ast) if (!is_constant(env, decl->value)) code_err(decl->value, "This value is not a valid constant initializer."); - if (decl->value->tag == Use) { + if (decl->value->tag == Use || decl->value->tag == Import) { assert(compile_statement(env, stmt->ast) == CORD_EMPTY); } else if (is_private) { env->code->staticdefs = CORD_all( @@ -2239,7 +2240,7 @@ CORD compile_statement_header(env_t *env, ast_t *ast) } case Declare: { auto decl = Match(ast, Declare); - if (decl->value->tag == Use) { + if (decl->value->tag == Use || decl->value->tag == Import) { return compile_statement_header(env, decl->value); } type_t *t = get_type(env, decl->value); @@ -2251,7 +2252,7 @@ CORD compile_statement_header(env_t *env, ast_t *ast) 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; + CORD code = (decl->value->tag == Use || decl->value->tag == Import) ? compile_statement_header(env, decl->value) : CORD_EMPTY; if (is_private) { return code; } else { @@ -2327,14 +2328,13 @@ CORD compile_statement_header(env_t *env, ast_t *ast) } return CORD_all("extern ", decl, ";\n"); } + case Import: { + const char *path = Match(ast, Import)->path; + return CORD_all("#include \"", path, ".tm.h\"\n"); + } case Use: { - auto use = Match(ast, Use); - const char *path = use->raw_path; - if (strncmp(path, "./", 2) == 0 || strncmp(path, "../", 3) == 0 || strncmp(path, "~/", 2) == 0 || strncmp(path, "/", 1) == 0) { - return CORD_all("#include \"", path, ".tm.h\"\n"); - } else { - return CORD_all("#include <tomo/lib", path, ".h>\n"); - } + const char *name = Match(ast, Use)->name; + return CORD_all("#include <tomo/lib", name, ".h>\n"); } default: return CORD_EMPTY; @@ -48,8 +48,8 @@ int op_tightness[] = { static const char *keywords[] = { "yes", "xor", "while", "when", "use", "then", "struct", "stop", "skip", "return", - "or", "not", "no", "mod1", "mod", "pass", "lang", "inline", "in", "if", "func", "for", "extern", - "enum", "else", "do", "and", "_min_", "_max_", + "or", "not", "no", "mod1", "mod", "pass", "lang", "import", "inline", "in", "if", + "func", "for", "extern", "enum", "else", "do", "and", "_min_", "_max_", NULL, }; @@ -98,6 +98,7 @@ static PARSER(parse_inline_c); static PARSER(parse_declaration); static PARSER(parse_doctest); static PARSER(parse_use); +static PARSER(parse_import); static PARSER(parse_linker); static PARSER(parse_namespace); static PARSER(parse_file_body); @@ -1463,6 +1464,7 @@ PARSER(parse_declaration) { if (!match(&pos, ":=")) return NULL; spaces(&pos); ast_t *val = optional(ctx, &pos, parse_use); + if (!val) val = optional(ctx, &pos, parse_import); if (!val) val = optional(ctx, &pos, parse_extended_expr); if (!val) parser_err(ctx, pos, strchrnul(pos, '\n'), "This declaration value didn't parse"); return NewAST(ctx->file, start, pos, Declare, .var=var, .value=val); @@ -1633,6 +1635,7 @@ PARSER(parse_namespace) { ||(stmt=optional(ctx, &pos, parse_lang_def)) ||(stmt=optional(ctx, &pos, parse_func_def)) ||(stmt=optional(ctx, &pos, parse_use)) + ||(stmt=optional(ctx, &pos, parse_import)) ||(stmt=optional(ctx, &pos, parse_linker)) ||(stmt=optional(ctx, &pos, parse_extern)) ||(stmt=optional(ctx, &pos, parse_inline_c)) @@ -2014,13 +2017,26 @@ PARSER(parse_use) { const char *start = pos; if (!match_word(&pos, "use")) return NULL; spaces(&pos); + size_t name_len = strcspn(pos, " \t\r\n;"); + if (name_len < 1) + parser_err(ctx, start, pos, "There is no module name here to use"); + char *name = heap_strn(pos, name_len); + pos += name_len; + while (match(&pos, ";")) continue; + return NewAST(ctx->file, start, pos, Use, .name=name); +} + +PARSER(parse_import) { + const char *start = pos; + if (!match_word(&pos, "import")) return NULL; + spaces(&pos); size_t path_len = strcspn(pos, " \t\r\n;"); if (path_len < 1) - parser_err(ctx, start, pos, "There is no filename here to use"); + parser_err(ctx, start, pos, "There is no path here to import"); char *path = heap_strn(pos, path_len); pos += path_len; while (match(&pos, ";")) continue; - return NewAST(ctx->file, start, pos, Use, .raw_path=path); + return NewAST(ctx->file, start, pos, Import, .path=path); } PARSER(parse_linker) { @@ -258,26 +258,23 @@ void build_file_dependency_graph(const char *filename, table_t *to_compile, tabl char *file_dir = realpath(filename, NULL); dirname(file_dir); for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) { - const char *use_path = NULL; - if (stmt->ast->tag == Use) { - use_path = Match(stmt->ast, Use)->raw_path; - } else if (stmt->ast->tag == Declare) { - auto decl = Match(stmt->ast, Declare); - if (decl->value->tag == Use) - use_path = Match(decl->value, Use)->raw_path; - } - if (!use_path) continue; - if (use_path[0] == '/' || strncmp(use_path, "~/", 2) == 0 || strncmp(use_path, "./", 2) == 0 || strncmp(use_path, "../", 3) == 0) { - const char *path = resolve_path(heap_strf("%s.tm", use_path), filename, ""); - if (!path) errx(1, "Couldn't resolve import: %s", use_path); + ast_t *stmt_ast = stmt->ast; + if (stmt_ast->tag == Declare) + stmt_ast = Match(stmt_ast, Declare)->value; + + if (stmt_ast->tag == Import) { + const char *path = Match(stmt_ast, Import)->path; + path = resolve_path(heap_strf("%s.tm", path), filename, ""); + if (!path) errx(1, "Couldn't resolve import: %s", path); if (Table$str_get(*to_compile, path)) continue; Table$str_set(to_compile, path, path); build_file_dependency_graph(path, to_compile, to_link); - } else { - const char *libfile = resolve_path(heap_strf("%s/lib%s.so", use_path, use_path), filename, getenv("TOMO_IMPORT_PATH")); - if (!libfile) errx(1, "Couldn't resolve path: %s", use_path); - const char *lib = heap_strf("-l%s", use_path); + } else if (stmt_ast->tag == Use) { + const char *name = Match(stmt_ast, Use)->name; + const char *libfile = resolve_path(heap_strf("%s/lib%s.so", name, name), filename, getenv("TOMO_IMPORT_PATH")); + if (!libfile) errx(1, "Couldn't resolve path: %s", name); + const char *lib = heap_strf("-l%s", name); Table$str_set(to_link, lib, lib); } } diff --git a/typecheck.c b/typecheck.c index 3b260feb..121135eb 100644 --- a/typecheck.c +++ b/typecheck.c @@ -102,20 +102,19 @@ 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) +static env_t *load_module(env_t *env, ast_t *module_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; - - if (strncmp(use->raw_path, "/", 1) == 0 || strncmp(use->raw_path, "./", 2) == 0 - || strncmp(use->raw_path, "../", 3) == 0 || strncmp(use->raw_path, "~/", 2) == 0) { - const char *path = heap_strf("%s.tm", use->raw_path); - const char *resolved_path = resolve_path(path, use_ast->file->filename, use_ast->file->filename); + if (module_ast->tag == Import) { + auto import = Match(module_ast, Import); + const char *name = file_base_name(import->path); + env_t *module_env = Table$str_get(*env->imports, name); + if (module_env) + return module_env; + + const char *path = heap_strf("%s.tm", import->path); + const char *resolved_path = resolve_path(path, module_ast->file->filename, module_ast->file->filename); if (!resolved_path) - code_err(use_ast, "No such file exists: \"%s\"", path); + code_err(module_ast, "No such file exists: \"%s\"", path); file_t *f = load_file(resolved_path); if (!f) errx(1, "No such file: %s", resolved_path); @@ -123,11 +122,12 @@ static env_t *load_module(env_t *env, ast_t *use_ast) ast_t *ast = parse_file(f, NULL); if (!ast) errx(1, "Could not compile!"); return load_module_env(env, ast); - } else { - const char *files_filename = heap_strf("%s/lib%s.files", use->raw_path, use->raw_path); - const char *resolved_path = resolve_path(files_filename, use_ast->file->filename, getenv("TOMO_IMPORT_PATH")); + } else if (module_ast->tag == Use) { + const char *name = Match(module_ast, Use)->name; + const char *files_filename = heap_strf("%s/lib%s.files", name, name); + const char *resolved_path = resolve_path(files_filename, module_ast->file->filename, getenv("TOMO_IMPORT_PATH")); if (!resolved_path) - code_err(use_ast, "No such library exists: \"lib%s.files\"", use->raw_path); + code_err(module_ast, "No such library exists: \"lib%s.files\"", name); file_t *files_f = load_file(resolved_path); if (!files_f) errx(1, "Couldn't open file: %s", resolved_path); @@ -138,7 +138,7 @@ static env_t *load_module(env_t *env, ast_t *use_ast) line = heap_strn(line, strcspn(line, "\r\n")); if (!line || line[0] == '\0') continue; const char *tm_path = resolve_path(line, resolved_path, "."); - if (!tm_path) errx(1, "Couldn't find library %s dependency: %s", use->raw_path, line); + if (!tm_path) errx(1, "Couldn't find library %s dependency: %s", name, line); file_t *tm_f = load_file(tm_path); if (!tm_f) errx(1, "No such file: %s", tm_path); @@ -154,6 +154,8 @@ static env_t *load_module(env_t *env, ast_t *use_ast) } } return env; + } else { + code_err(module_ast, "This is not a module import"); } } @@ -219,7 +221,7 @@ 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); - if (decl->value->tag == Use) + if (decl->value->tag == Use || decl->value->tag == Import) (void)load_module(env, decl->value); else bind_statement(env, decl->value); @@ -331,7 +333,7 @@ void bind_statement(env_t *env, ast_t *statement) bind_statement(ns_env, stmt->ast); break; } - case Use: { + case Use: case Import: { 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++) { @@ -702,10 +704,13 @@ type_t *get_type(env_t *env, ast_t *ast) case Declare: case Assign: case DocTest: case LinkerDirective: { return Type(VoidType); } - case Use: { - const char *path = Match(ast, Use)->raw_path; + case Import: { + const char *path = Match(ast, Import)->path; return Type(ModuleType, file_base_name(path)); } + case Use: { + return Type(ModuleType, Match(ast, Use)->name); + } case Return: case Stop: case Skip: { return Type(AbortType); } @@ -1015,7 +1020,8 @@ type_t *get_type(env_t *env, ast_t *ast) bool is_discardable(env_t *env, ast_t *ast) { switch (ast->tag) { - case UpdateAssign: case Assign: case Declare: case FunctionDef: case StructDef: case EnumDef: case LangDef: case Use: + case UpdateAssign: case Assign: case Declare: case FunctionDef: case StructDef: case EnumDef: + case LangDef: case Use: case Import: return true; default: break; } @@ -1124,7 +1130,7 @@ bool is_constant(env_t *env, ast_t *ast) return is_constant(env, binop->lhs) && is_constant(env, binop->rhs); } } - case Use: return true; + case Use: case Import: return true; case FunctionCall: { // Constructors are allowed: auto call = Match(ast, FunctionCall); |
