Disallow 'use' statements that aren't top level

This commit is contained in:
Bruce Hill 2024-09-04 13:48:26 -04:00
parent f605df8230
commit d1b2e9f598
6 changed files with 40 additions and 21 deletions

2
ast.c
View File

@ -152,7 +152,7 @@ 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.name))
T(Use, "<Use>%r</Use>", xml_escape(data.path))
T(LinkerDirective, "<LinkerDirective>%r</LinkerDirective>", xml_escape(data.directive))
T(InlineCCode, "<InlineCode>%r</InlineCode>", xml_escape(data.code))
#undef T

2
ast.h
View File

@ -291,7 +291,7 @@ struct ast_s {
bool skip_source:1;
} DocTest;
struct {
const char *name;
const char *path;
enum { USE_LOCAL, USE_MODULE, USE_SHARED_OBJECT, USE_HEADER } what;
} Use;
struct {

View File

@ -1169,10 +1169,10 @@ CORD compile_statement(env_t *env, ast_t *ast)
case Use: {
auto use = Match(ast, Use);
if (use->what == USE_LOCAL) {
CORD name = file_base_name(Match(ast, Use)->name);
CORD name = file_base_name(Match(ast, Use)->path);
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 *libname = file_base_name(use->path);
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)
@ -1183,7 +1183,7 @@ CORD compile_statement(env_t *env, ast_t *ast)
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");
env->code->variable_initializers, use->path, "$", file_base_name(line), "$$initialize();\n");
}
}
return CORD_EMPTY;
@ -3262,11 +3262,11 @@ CORD compile_statement_imports(env_t *env, ast_t *ast)
auto use = Match(ast, Use);
switch (use->what) {
case USE_MODULE:
return CORD_all("#include <tomo/lib", use->name, ".h>\n");
return CORD_all("#include <tomo/lib", use->path, ".h>\n");
case USE_LOCAL:
return CORD_all("#include \"", use->name, ".h\"\n");
return CORD_all("#include \"", use->path, ".h\"\n");
case USE_HEADER:
return CORD_all("#include ", use->name, "\n");
return CORD_all("#include ", use->path, "\n");
default:
return CORD_EMPTY;
}

23
parse.c
View File

@ -110,6 +110,7 @@ static PARSER(parse_func_def);
static PARSER(parse_extern);
static PARSER(parse_inline_c);
static PARSER(parse_declaration);
static PARSER(parse_top_declaration);
static PARSER(parse_doctest);
static PARSER(parse_say);
static PARSER(parse_use);
@ -1558,6 +1559,24 @@ ast_t *parse_expr(parse_ctx_t *ctx, const char *pos) {
}
PARSER(parse_declaration) {
const char *start = pos;
ast_t *var = parse_var(ctx, pos);
if (!var) return NULL;
pos = var->end;
spaces(&pos);
if (!match(&pos, ":=")) return NULL;
spaces(&pos);
ast_t *val = optional(ctx, &pos, parse_extended_expr);
if (!val) {
if (optional(ctx, &pos, parse_use))
parser_err(ctx, start, pos, "'use' statements are only allowed at the top level of a file");
else
parser_err(ctx, pos, strchrnul(pos, '\n'), "This is not a valid expression");
}
return NewAST(ctx->file, start, pos, Declare, .var=var, .value=val);
}
PARSER(parse_top_declaration) {
const char *start = pos;
ast_t *var = parse_var(ctx, pos);
if (!var) return NULL;
@ -1789,7 +1808,7 @@ PARSER(parse_file_body) {
||(stmt=optional(ctx, &pos, parse_linker))
||(stmt=optional(ctx, &pos, parse_extern))
||(stmt=optional(ctx, &pos, parse_inline_c))
||(stmt=optional(ctx, &pos, parse_declaration)))
||(stmt=optional(ctx, &pos, parse_top_declaration)))
{
statements = new(ast_list_t, .ast=stmt, .next=statements);
pos = stmt->end;
@ -2194,7 +2213,7 @@ PARSER(parse_use) {
what = USE_SHARED_OBJECT;
else
what = USE_MODULE;
return NewAST(ctx->file, start, pos, Use, .name=name, .what=what);
return NewAST(ctx->file, start, pos, Use, .path=name, .what=what);
}
PARSER(parse_linker) {

10
tomo.c
View File

@ -324,9 +324,9 @@ void build_file_dependency_graph(const char *filename, table_t *to_compile, tabl
switch (use->what) {
case USE_LOCAL: {
const char *path = use->name;
const char *path = use->path;
path = resolve_path(path, filename, "");
if (!path) errx(1, "Couldn't resolve import: %s", use->name);
if (!path) errx(1, "Couldn't resolve import: %s", use->path);
if (Table$str_get(*to_compile, path))
continue;
build_file_dependency_graph(path, to_compile, to_link);
@ -334,16 +334,16 @@ void build_file_dependency_graph(const char *filename, table_t *to_compile, tabl
break;
}
case USE_MODULE: {
const char *base_name = file_base_name(use->name);
const char *base_name = file_base_name(use->path);
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);
const char *lib = heap_strf("-l%s", use->path);
Table$str_set(to_link, lib, lib);
break;
}
case USE_SHARED_OBJECT: {
const char *lib = heap_strf("-l:%s", use->name);
const char *lib = heap_strf("-l:%s", use->path);
Table$str_set(to_link, lib, lib);
break;
}

View File

@ -130,20 +130,20 @@ static env_t *load_module(env_t *env, ast_t *module_ast)
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);
const char *resolved_path = resolve_path(use->path, 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;
if (!resolved_path)
code_err(module_ast, "No such file exists: \"%s\"", use->name);
code_err(module_ast, "No such file exists: \"%s\"", use->path);
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);
}
case USE_MODULE: {
const char *libname = file_base_name(use->name);
const char *libname = file_base_name(use->path);
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)
@ -152,7 +152,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, use->name, module_env);
Table$str_set(env->imports, use->path, module_env);
char *libname_id = GC_strdup(libname);
for (char *c = libname_id; *c; c++) {
if (!isalnum(*c) && *c != '_')
@ -834,9 +834,9 @@ type_t *get_type(env_t *env, ast_t *ast)
case Use: {
switch (Match(ast, Use)->what) {
case USE_LOCAL:
return Type(ModuleType, resolve_path(Match(ast, Use)->name, ast->file->filename, ast->file->filename));
return Type(ModuleType, resolve_path(Match(ast, Use)->path, ast->file->filename, ast->file->filename));
default:
return Type(ModuleType, Match(ast, Use)->name);
return Type(ModuleType, Match(ast, Use)->path);
}
}
case Return: {