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(FieldAccess, "<FieldAccess field=\"%s\">%r</FieldAccess>", data.field, ast_to_xml(data.fielded))
T(Optional, "<Optional>%r</Optional>", ast_to_xml(data.value)) 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(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(LinkerDirective, "<LinkerDirective>%r</LinkerDirective>", xml_escape(data.directive))
T(InlineCCode, "<InlineCode>%r</InlineCode>", xml_escape(data.code)) T(InlineCCode, "<InlineCode>%r</InlineCode>", xml_escape(data.code))
#undef T #undef T

2
ast.h
View File

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

View File

@ -1169,10 +1169,10 @@ CORD compile_statement(env_t *env, ast_t *ast)
case Use: { case Use: {
auto use = Match(ast, Use); auto use = Match(ast, Use);
if (use->what == USE_LOCAL) { 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"); env->code->variable_initializers = CORD_all(env->code->variable_initializers, name, "$$initialize();\n");
} else if (use->what == USE_MODULE) { } 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 *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")); const char *resolved_path = resolve_path(files_filename, ast->file->filename, getenv("TOMO_IMPORT_PATH"));
if (!resolved_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); const char *line = get_line(files_f, i);
line = GC_strndup(line, strcspn(line, "\r\n")); line = GC_strndup(line, strcspn(line, "\r\n"));
env->code->variable_initializers = CORD_all( 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; return CORD_EMPTY;
@ -3262,11 +3262,11 @@ CORD compile_statement_imports(env_t *env, ast_t *ast)
auto use = Match(ast, Use); auto use = Match(ast, Use);
switch (use->what) { switch (use->what) {
case USE_MODULE: 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: case USE_LOCAL:
return CORD_all("#include \"", use->name, ".h\"\n"); return CORD_all("#include \"", use->path, ".h\"\n");
case USE_HEADER: case USE_HEADER:
return CORD_all("#include ", use->name, "\n"); return CORD_all("#include ", use->path, "\n");
default: default:
return CORD_EMPTY; return CORD_EMPTY;
} }

23
parse.c
View File

@ -110,6 +110,7 @@ static PARSER(parse_func_def);
static PARSER(parse_extern); static PARSER(parse_extern);
static PARSER(parse_inline_c); static PARSER(parse_inline_c);
static PARSER(parse_declaration); static PARSER(parse_declaration);
static PARSER(parse_top_declaration);
static PARSER(parse_doctest); static PARSER(parse_doctest);
static PARSER(parse_say); static PARSER(parse_say);
static PARSER(parse_use); static PARSER(parse_use);
@ -1558,6 +1559,24 @@ ast_t *parse_expr(parse_ctx_t *ctx, const char *pos) {
} }
PARSER(parse_declaration) { 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; const char *start = pos;
ast_t *var = parse_var(ctx, pos); ast_t *var = parse_var(ctx, pos);
if (!var) return NULL; if (!var) return NULL;
@ -1789,7 +1808,7 @@ PARSER(parse_file_body) {
||(stmt=optional(ctx, &pos, parse_linker)) ||(stmt=optional(ctx, &pos, parse_linker))
||(stmt=optional(ctx, &pos, parse_extern)) ||(stmt=optional(ctx, &pos, parse_extern))
||(stmt=optional(ctx, &pos, parse_inline_c)) ||(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); statements = new(ast_list_t, .ast=stmt, .next=statements);
pos = stmt->end; pos = stmt->end;
@ -2194,7 +2213,7 @@ PARSER(parse_use) {
what = USE_SHARED_OBJECT; what = USE_SHARED_OBJECT;
else else
what = USE_MODULE; 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) { 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) { switch (use->what) {
case USE_LOCAL: { case USE_LOCAL: {
const char *path = use->name; const char *path = use->path;
path = resolve_path(path, filename, ""); 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)) if (Table$str_get(*to_compile, path))
continue; continue;
build_file_dependency_graph(path, to_compile, to_link); 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; break;
} }
case USE_MODULE: { 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 *lib_path = heap_strf("%s/lib%s.so", base_name, base_name);
const char *libfile = resolve_path(lib_path, filename, getenv("TOMO_IMPORT_PATH")); const char *libfile = resolve_path(lib_path, filename, getenv("TOMO_IMPORT_PATH"));
if (!libfile) errx(1, "Couldn't resolve path: %s", lib_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); Table$str_set(to_link, lib, lib);
break; break;
} }
case USE_SHARED_OBJECT: { 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); Table$str_set(to_link, lib, lib);
break; 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); auto use = Match(module_ast, Use);
switch (use->what) { switch (use->what) {
case USE_LOCAL: { 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); env_t *module_env = Table$str_get(*env->imports, resolved_path);
if (module_env) if (module_env)
return module_env; return module_env;
if (!resolved_path) 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); ast_t *ast = parse_file(resolved_path, NULL);
if (!ast) errx(1, "Could not compile file %s", resolved_path); if (!ast) errx(1, "Could not compile file %s", resolved_path);
return load_module_env(env, ast); return load_module_env(env, ast);
} }
case USE_MODULE: { 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 *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")); const char *resolved_path = resolve_path(files_filename, module_ast->file->filename, getenv("TOMO_IMPORT_PATH"));
if (!resolved_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); if (!files_f) errx(1, "Couldn't open file: %s", resolved_path);
env_t *module_env = fresh_scope(env); 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); char *libname_id = GC_strdup(libname);
for (char *c = libname_id; *c; c++) { for (char *c = libname_id; *c; c++) {
if (!isalnum(*c) && *c != '_') if (!isalnum(*c) && *c != '_')
@ -834,9 +834,9 @@ type_t *get_type(env_t *env, ast_t *ast)
case Use: { case Use: {
switch (Match(ast, Use)->what) { switch (Match(ast, Use)->what) {
case USE_LOCAL: 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: default:
return Type(ModuleType, Match(ast, Use)->name); return Type(ModuleType, Match(ast, Use)->path);
} }
} }
case Return: { case Return: {