Split import/use into separate concepts
This commit is contained in:
parent
5d682df66c
commit
dab2c399f1
3
ast.c
3
ast.c
@ -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
|
||||
|
7
ast.h
7
ast.h
@ -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;
|
||||
|
26
compile.c
26
compile.c
@ -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;
|
||||
|
24
parse.c
24
parse.c
@ -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) {
|
||||
|
29
tomo.c
29
tomo.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
50
typecheck.c
50
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 (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;
|
||||
|
||||
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);
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user