From ad51b208b4924d04f1d6a804178a67994b4f9e59 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Thu, 22 Aug 2024 14:02:48 -0400 Subject: [PATCH] Overhaul of import syntax. Now everything uses `use`: `use foo`, `use ./foo.tm`, `use `, `use libfoo.so` --- ast.c | 1 - ast.h | 6 ++--- builtins/files.c | 4 +-- builtins/util.h | 2 ++ compile.c | 65 ++++++++++++++++++++++++++++------------------- docs/libraries.md | 2 +- environment.c | 4 +-- environment.h | 5 +++- parse.c | 28 ++++++++------------ repl.c | 6 ++--- test/import.tm | 2 +- tomo.c | 44 +++++++++++++++++++++----------- typecheck.c | 51 ++++++++++++++++++------------------- 13 files changed, 120 insertions(+), 100 deletions(-) diff --git a/ast.c b/ast.c index 07c27bf..6f1d20c 100644 --- a/ast.c +++ b/ast.c @@ -152,7 +152,6 @@ CORD ast_to_xml(ast_t *ast) T(FieldAccess, "%r", data.field, ast_to_xml(data.fielded)) T(Optional, "%r", ast_to_xml(data.value)) T(DocTest, "%r%r", optional_tagged("expression", data.expr), xml_escape(data.output)) - T(Import, "%r", xml_escape(data.path)) T(Use, "%r", xml_escape(data.name)) T(LinkerDirective, "%r", xml_escape(data.directive)) T(InlineCCode, "%r", xml_escape(data.code)) diff --git a/ast.h b/ast.h index c98ac67..1d295e5 100644 --- a/ast.h +++ b/ast.h @@ -122,7 +122,7 @@ typedef enum { StructDef, EnumDef, LangDef, Index, FieldAccess, Optional, DocTest, - Import, Use, + Use, LinkerDirective, InlineCCode, } ast_e; @@ -290,11 +290,9 @@ struct ast_s { const char *output; bool skip_source:1; } DocTest; - struct { - const char *path; - } Import; struct { const char *name; + enum { USE_LOCAL, USE_MODULE, USE_SHARED_OBJECT, USE_HEADER } what; } Use; struct { const char *directive; diff --git a/builtins/files.c b/builtins/files.c index 70c7648..f506d59 100644 --- a/builtins/files.c +++ b/builtins/files.c @@ -26,10 +26,10 @@ public char *resolve_path(const char *path, const char *relative_to, const char // Resolve the path to an absolute path, assuming it's relative to the file // it was found in: char buf[PATH_MAX] = {0}; - if (streq(path, "~") || strncmp(path, "~/", 2) == 0) { + if (streq(path, "~") || starts_with(path, "~/")) { char *resolved = realpath(heap_strf("%s%s", getenv("HOME"), path+1), buf); if (resolved) return GC_strdup(resolved); - } else if (streq(path, ".") || strncmp(path, "./", 2) == 0) { + } else if (streq(path, ".") || starts_with(path, "./")) { char *relative_dir = dirname(GC_strdup(relative_to)); char *resolved = realpath(heap_strf("%s/%s", relative_dir, path), buf); if (resolved) return GC_strdup(resolved); diff --git a/builtins/util.h b/builtins/util.h index 44b780c..271403f 100644 --- a/builtins/util.h +++ b/builtins/util.h @@ -10,6 +10,8 @@ #include #define streq(a, b) (((a) == NULL && (b) == NULL) || (((a) == NULL) == ((b) == NULL) && strcmp(a, b) == 0)) +#define starts_with(line, prefix) (strncmp(line, prefix, strlen(prefix)) == 0) +#define ends_with(line, suffix) (strlen(line) >= strlen(suffix) && strcmp(line + strlen(line) - strlen(suffix), suffix) == 0) #define new(t, ...) ((t*)memcpy(GC_MALLOC(sizeof(t)), &(t){__VA_ARGS__}, sizeof(t))) #define copy(obj_ptr) ((__typeof(obj_ptr))memcpy(GC_MALLOC(sizeof(*(obj_ptr))), obj_ptr, sizeof(*(obj_ptr)))) #define Match(x, _tag) ((x)->tag == _tag ? &(x)->__data._tag : (errx(1, __FILE__ ":%d This was supposed to be a " # _tag "\n", __LINE__), &(x)->__data._tag)) diff --git a/compile.c b/compile.c index 77ad988..219173b 100644 --- a/compile.c +++ b/compile.c @@ -333,7 +333,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 || decl->value->tag == Import) { + if (decl->value->tag == Use) { assert(compile_statement(env, test->expr) == CORD_EMPTY); return CORD_asprintf( "test(NULL, NULL, %r, %r, %ld, %ld);", @@ -452,7 +452,7 @@ CORD compile_statement(env_t *env, ast_t *ast) } case Declare: { auto decl = Match(ast, Declare); - if (decl->value->tag == Use || decl->value->tag == Import) { + if (decl->value->tag == Use) { return compile_statement(env, decl->value); } else { type_t *t = get_type(env, decl->value); @@ -482,7 +482,7 @@ CORD compile_statement(env_t *env, ast_t *ast) val = compile_int_to_type(val_env, assign->values->ast, rhs_t); } else { val = compile_maybe_incref(val_env, assign->values->ast); - if (!promote(val_env, &val, lhs_t, rhs_t)) + if (!promote(val_env, &val, rhs_t, lhs_t)) code_err(assign->values->ast, "You cannot assign a %T value to a %T operand", lhs_t, rhs_t); } return compile_assignment(env, assign->targets->ast, val); @@ -501,8 +501,8 @@ CORD compile_statement(env_t *env, ast_t *ast) val = compile_int_to_type(val_env, value->ast, rhs_t); } else { val = compile_maybe_incref(val_env, value->ast); - if (!promote(val_env, &val, lhs_t, rhs_t)) - code_err(value->ast, "You cannot assign a %T value to a %T operand", lhs_t, rhs_t); + if (!promote(val_env, &val, rhs_t, lhs_t)) + code_err(value->ast, "You cannot assign a %T value to a %T operand", rhs_t, lhs_t); } CORD_appendf(&code, "%r $%ld = %r;\n", compile_type(lhs_t), i++, val); } @@ -1166,13 +1166,25 @@ CORD compile_statement(env_t *env, ast_t *ast) case Extern: return CORD_EMPTY; case InlineCCode: return Match(ast, InlineCCode)->code; case Use: { - CORD name = Match(ast, Use)->name; - env->code->variable_initializers = CORD_all( env->code->variable_initializers, name, "$$initialize();\n"); - return CORD_EMPTY; - } - case Import: { - CORD name = file_base_name(Match(ast, Import)->path); - env->code->variable_initializers = CORD_all( env->code->variable_initializers, name, "$$initialize();\n"); + auto use = Match(ast, Use); + if (use->what == USE_LOCAL) { + CORD name = file_base_name(Match(ast, Use)->name); + env->code->variable_initializers = CORD_all(env->code->variable_initializers, name, "$$initialize();\n"); + } else if (use->what == USE_MODULE) { + const char *libname = file_base_name(use->name); + const char *files_filename = heap_strf("%s/lib%s.files", libname, libname); + const char *resolved_path = resolve_path(files_filename, ast->file->filename, getenv("TOMO_IMPORT_PATH")); + if (!resolved_path) + code_err(ast, "No such library exists: \"lib%s.files\"", libname); + file_t *files_f = load_file(resolved_path); + if (!files_f) errx(1, "Couldn't open file: %s", resolved_path); + for (int64_t i = 1; i <= files_f->num_lines; i++) { + const char *line = get_line(files_f, i); + line = GC_strndup(line, strcspn(line, "\r\n")); + env->code->variable_initializers = CORD_all( + env->code->variable_initializers, use->name, "$", file_base_name(line), "$$initialize();\n"); + } + } return CORD_EMPTY; } default: @@ -2788,7 +2800,6 @@ 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 Defer: code_err(ast, "Compiling 'defer' as expression!"); case LinkerDirective: code_err(ast, "Linker directives are not supported yet"); case Extern: code_err(ast, "Externs are not supported as expressions"); @@ -3118,7 +3129,7 @@ CORD compile_file(env_t *env, ast_t *ast) type_t *t = get_type(env, decl->value); if (t->tag == AbortType || t->tag == VoidType || t->tag == ReturnType) code_err(stmt->ast, "You can't declare a variable with a %T value", t); - if (!(decl->value->tag == Use || decl->value->tag == Import || is_constant(env, decl->value))) { + if (!(decl->value->tag == Use || is_constant(env, decl->value))) { CORD val_code = compile_maybe_incref(env, decl->value); if (t->tag == FunctionType) { assert(promote(env, &val_code, t, Type(ClosureType, t))); @@ -3143,7 +3154,7 @@ CORD compile_file(env_t *env, ast_t *ast) CORD full_name = CORD_all(namespace_prefix(env->libname, env->namespace), decl_name); bool is_private = (decl_name[0] == '_'); type_t *t = get_type(env, decl->value); - if (decl->value->tag == Use || decl->value->tag == Import) { + if (decl->value->tag == Use) { assert(compile_statement(env, stmt->ast) == CORD_EMPTY); } else if (!is_constant(env, decl->value)) { env->code->staticdefs = CORD_all( @@ -3197,20 +3208,22 @@ CORD compile_statement_imports(env_t *env, ast_t *ast) } case Declare: { auto decl = Match(ast, Declare); - if (decl->value->tag == Use || decl->value->tag == Import) + if (decl->value->tag == Use) return compile_statement_imports(env, decl->value); return CORD_EMPTY; } - case Import: { - const char *path = Match(ast, Import)->path; - return CORD_all("#include \"", path, ".tm.h\"\n"); - } case Use: { - const char *name = Match(ast, Use)->name; - if (strncmp(name, "-l", 2) == 0) + auto use = Match(ast, Use); + switch (use->what) { + case USE_MODULE: + return CORD_all("#include name, ".h>\n"); + case USE_LOCAL: + return CORD_all("#include \"", use->name, ".h\"\n"); + case USE_HEADER: + return CORD_all("#include ", use->name, "\n"); + default: return CORD_EMPTY; - else - return CORD_all("#include \n"); + } } default: return CORD_EMPTY; } @@ -3263,7 +3276,7 @@ CORD compile_statement_definitions(env_t *env, ast_t *ast) } case Declare: { auto decl = Match(ast, Declare); - if (decl->value->tag == Use || decl->value->tag == Import) { + if (decl->value->tag == Use) { return compile_statement_definitions(env, decl->value); } type_t *t = get_type(env, decl->value); @@ -3274,7 +3287,7 @@ CORD compile_statement_definitions(env_t *env, ast_t *ast) code_err(ast, "You can't declare a variable with a %T value", t); const char *decl_name = Match(decl->var, Var)->name; bool is_private = (decl_name[0] == '_'); - CORD code = (decl->value->tag == Use || decl->value->tag == Import) ? compile_statement_definitions(env, decl->value) : CORD_EMPTY; + CORD code = (decl->value->tag == Use) ? compile_statement_definitions(env, decl->value) : CORD_EMPTY; if (is_private) { return code; } else { diff --git a/docs/libraries.md b/docs/libraries.md index cd69a14..41f29cb 100644 --- a/docs/libraries.md +++ b/docs/libraries.md @@ -48,7 +48,7 @@ Now, what happens if we want to _use_ the compiled object file? ``` // File: baz.tm -foo := use ./foo +foo := use ./foo.tm func say_stuff(): say("I got {foo.my_variable} from foo") diff --git a/environment.c b/environment.c index c81e192..248588f 100644 --- a/environment.c +++ b/environment.c @@ -325,12 +325,12 @@ CORD namespace_prefix(CORD *libname, namespace_t *ns) env_t *load_module_env(env_t *env, ast_t *ast) { - const char *name = file_base_name(ast->file->filename); + const char *name = ast->file->filename; env_t *cached = Table$str_get(*env->imports, name); if (cached) return cached; env_t *module_env = fresh_scope(env); module_env->code = new(compilation_unit_t); - module_env->namespace = new(namespace_t, .name=name); + module_env->namespace = new(namespace_t, .name=file_base_name(name)); Table$str_set(module_env->imports, name, module_env); for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) diff --git a/environment.h b/environment.h index b69e0a9..0fd67b4 100644 --- a/environment.h +++ b/environment.h @@ -43,7 +43,10 @@ typedef struct namespace_s { typedef struct env_s { table_t *types, *globals, *locals; - table_t *imports; // Map of 'use' name -> env_t* + // Lookup table for env_t* where the key is: + // - Resolved path for local imports (so that `use ./foo.tm` is the same as `use ./baz/../foo.tm`) + // - Raw 'use' string for module imports + table_t *imports; compilation_unit_t *code; fn_ctx_t *fn_ctx; loop_ctx_t *loop_ctx; diff --git a/parse.c b/parse.c index 5ba6e81..9d80ccc 100644 --- a/parse.c +++ b/parse.c @@ -112,7 +112,6 @@ static PARSER(parse_declaration); static PARSER(parse_doctest); static PARSER(parse_say); static PARSER(parse_use); -static PARSER(parse_import); static PARSER(parse_linker); static PARSER(parse_namespace); static PARSER(parse_file_body); @@ -1563,7 +1562,6 @@ 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); @@ -1748,7 +1746,6 @@ 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)) @@ -1785,7 +1782,6 @@ PARSER(parse_file_body) { ||(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)) @@ -2185,20 +2181,16 @@ PARSER(parse_use) { char *name = GC_strndup(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 path here to import"); - char *path = GC_strndup(pos, path_len); - pos += path_len; - while (match(&pos, ";")) continue; - return NewAST(ctx->file, start, pos, Import, .path=path); + int what; + if (name[0] == '<') + what = USE_HEADER; + else if (starts_with(name, "./") || starts_with(name, "/") || starts_with(name, "../") || starts_with(name, "~/")) + what = USE_LOCAL; + else if (ends_with(name, ".so")) + what = USE_SHARED_OBJECT; + else + what = USE_MODULE; + return NewAST(ctx->file, start, pos, Use, .name=name, .what=what); } PARSER(parse_linker) { diff --git a/repl.c b/repl.c index 111aff1..8ac4c7d 100644 --- a/repl.c +++ b/repl.c @@ -9,6 +9,7 @@ #include #include "builtins/tomo.h" +#include "builtins/util.h" #include "typecheck.h" #include "parse.h" @@ -46,9 +47,8 @@ void repl(void) while ((len=getline(&line, &buf_size, stdin)) >= 0) { if (len > 1) { char *code = line; -#define starts_with(line, prefix) (strncmp(line, prefix " ", strlen(prefix)+1) == 0) - if (starts_with(line, "if") || starts_with(line, "for") || starts_with(line, "while") - || starts_with(line, "func") || starts_with(line, "struct") || starts_with(line, "lang")) { + if (starts_with(line, "if ") || starts_with(line, "for ") || starts_with(line, "while ") + || starts_with(line, "func ") || starts_with(line, "struct ") || starts_with(line, "lang ")) { printf("\x1b[33;1m..\x1b[m "); fflush(stdout); code = GC_strdup(line); diff --git a/test/import.tm b/test/import.tm index c84e18e..e6a9b4a 100644 --- a/test/import.tm +++ b/test/import.tm @@ -1,4 +1,4 @@ -imported := import ./use_import +imported := use ./use_import.tm func asdf()->imported.ImportedType: return imported.get_value() diff --git a/tomo.c b/tomo.c index 78ab833..8b69f1f 100644 --- a/tomo.c +++ b/tomo.c @@ -216,6 +216,7 @@ int main(int argc, char *argv[]) CORD h = compile_statement_definitions(env, stmt->ast); if (h) CORD_put(h, header_prog); } + fprintf(header_prog, "void %s$%s$$initialize(void);\n", libname, file_base_name(filename)); } if (pclose(header_prog) == -1) errx(1, "Failed to run autoformat program on header file: %s", autofmt); @@ -316,24 +317,37 @@ void build_file_dependency_graph(const char *filename, table_t *to_compile, tabl 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 (stmt_ast->tag != Use) continue; + auto use = Match(stmt_ast, Use); + + switch (use->what) { + case USE_LOCAL: { + const char *path = use->name; + path = resolve_path(path, filename, ""); + if (!path) errx(1, "Couldn't resolve import: %s", use->name); 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 if (stmt_ast->tag == Use) { - const char *name = Match(stmt_ast, Use)->name; - if (strncmp(name, "-l", 2) == 0) { - Table$str_set(to_link, name, name); - } else { - 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); - } + Table$str_set(to_compile, path, path); + break; + } + case USE_MODULE: { + const char *base_name = file_base_name(use->name); + const char *lib_path = heap_strf("%s/lib%s.so", base_name, base_name); + const char *libfile = resolve_path(lib_path, filename, getenv("TOMO_IMPORT_PATH")); + if (!libfile) errx(1, "Couldn't resolve path: %s", lib_path); + const char *lib = heap_strf("-l%s", use->name); + Table$str_set(to_link, lib, lib); + break; + } + case USE_SHARED_OBJECT: { + const char *lib = heap_strf("-l:%s", use->name); + Table$str_set(to_link, lib, lib); + break; + } + case USE_HEADER: { + break; + } } } free(file_dir); diff --git a/typecheck.c b/typecheck.c index 410619a..d8e0f6e 100644 --- a/typecheck.c +++ b/typecheck.c @@ -126,23 +126,23 @@ type_t *get_math_type(env_t *env, ast_t *ast, type_t *lhs_t, type_t *rhs_t) static env_t *load_module(env_t *env, ast_t *module_ast) { - 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); + auto use = Match(module_ast, Use); + switch (use->what) { + case USE_LOCAL: { + const char *resolved_path = resolve_path(use->name, module_ast->file->filename, module_ast->file->filename); + env_t *module_env = Table$str_get(*env->imports, resolved_path); 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(module_ast, "No such file exists: \"%s\"", path); + code_err(module_ast, "No such file exists: \"%s\"", use->name); ast_t *ast = parse_file(resolved_path, NULL); if (!ast) errx(1, "Could not compile file %s", resolved_path); return load_module_env(env, ast); - } else if (module_ast->tag == Use) { - const char *libname = Match(module_ast, Use)->name; + } + case USE_MODULE: { + const char *libname = file_base_name(use->name); const char *files_filename = heap_strf("%s/lib%s.files", libname, libname); const char *resolved_path = resolve_path(files_filename, module_ast->file->filename, getenv("TOMO_IMPORT_PATH")); if (!resolved_path) @@ -151,7 +151,7 @@ static env_t *load_module(env_t *env, ast_t *module_ast) if (!files_f) errx(1, "Couldn't open file: %s", resolved_path); env_t *module_env = fresh_scope(env); - Table$str_set(env->imports, libname, module_env); + Table$str_set(env->imports, use->name, module_env); char *libname_id = GC_strdup(libname); for (char *c = libname_id; *c; c++) { if (!isalnum(*c) && *c != '_') @@ -184,8 +184,10 @@ static env_t *load_module(env_t *env, ast_t *module_ast) } } return module_env; - } else { - code_err(module_ast, "This is not a module import"); + } + case USE_SHARED_OBJECT: return NULL; + case USE_HEADER: return NULL; + default: return NULL; } } @@ -254,9 +256,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 || decl->value->tag == Import) { - if (decl->value->tag == Use && strncmp(Match(decl->value, Use)->name, "-l", 2) == 0) - code_err(statement, "External library files specified with -l can't be assigned to a variable"); + if (decl->value->tag == Use) { (void)load_module(env, decl->value); } else { bind_statement(env, decl->value); @@ -368,11 +368,9 @@ void bind_statement(env_t *env, ast_t *statement) bind_statement(ns_env, stmt->ast); break; } - case Use: case Import: { - if (statement->tag == Use && strncmp(Match(statement, Use)->name, "-l", 2) == 0) - break; - + case Use: { env_t *module_env = load_module(env, statement); + if (!module_env) break; 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); @@ -832,12 +830,13 @@ type_t *get_type(env_t *env, ast_t *ast) case Declare: case Assign: case DocTest: case LinkerDirective: { return Type(VoidType); } - 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); + switch (Match(ast, Use)->what) { + case USE_LOCAL: + return Type(ModuleType, resolve_path(Match(ast, Use)->name, ast->file->filename, ast->file->filename)); + default: + return Type(ModuleType, Match(ast, Use)->name); + } } case Return: { ast_t *val = Match(ast, Return)->value; @@ -1271,7 +1270,7 @@ 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 Import: + case LangDef: case Use: return true; default: break; } @@ -1390,7 +1389,7 @@ bool is_constant(env_t *env, ast_t *ast) return is_constant(env, binop->lhs) && is_constant(env, binop->rhs); } } - case Use: case Import: return true; + case Use: return true; case FunctionCall: { // Constructors are allowed: auto call = Match(ast, FunctionCall);