diff --git a/self_hosted/main.jou b/self_hosted/main.jou index 8be8f3aa..75932da6 100644 --- a/self_hosted/main.jou +++ b/self_hosted/main.jou @@ -90,6 +90,11 @@ def parse_args(argc: int, argv: byte**) -> CommandLineArgs: return result +def find_file(files: FileState*, nfiles: int, path: byte*) -> FileState*: + for i = 0; i < nfiles; i++: + if strcmp(files[i].ast.path, path) == 0: + return &files[i] + return NULL # C:\Users\myname\.foo-bar.jou --> "_foo_bar" # Result never contains "-", so you can add "-" separated suffixes without conflicts. @@ -117,6 +122,14 @@ def get_sane_filename(path: byte*) -> byte[50]: name[i] = '_' return name + +def check_main_function(ast: AstFile*) -> bool: + for i = 0; i < ast->body.nstatements; i++: + s = &ast->body.statements[i] + if s->kind == AstStatementKind::Function and strcmp(s->function.signature.name, "main") == 0: + return True + return False + def check_ast_and_import_conflicts(ast: AstFile*, symbol: ExportSymbol*) -> None: for i = 0; i < ast->body.nstatements; i++: ts = &ast->body.statements[i] @@ -210,6 +223,10 @@ class Compiler: evaluate_compile_time_if_statements_in_body(&ast.body) + if item.is_imported and check_main_function(&ast): + assert item.import_location.path != NULL + fail(item.import_location, "imported file should not have `main` function") + self->files = realloc(self->files, sizeof self->files[0] * (self->nfiles + 1)) self->files[self->nfiles++] = FileState{ast = ast} @@ -467,6 +484,13 @@ def main(argc: int, argv: byte**) -> int: compiler.process_imports_and_exports() compiler.typecheck_stage3_all_files() + mainfile = find_file(compiler.files, compiler.nfiles, args.main_path) + assert mainfile != NULL + + if not check_main_function(&mainfile->ast): + l = Location{path=mainfile->ast.path, lineno=0} + fail(l, "missing `main` function to execute the program") + object_files = compiler.create_object_files() executable = compiler.link(object_files) for i = 0; object_files[i] != NULL; i++: diff --git a/src/main.c b/src/main.c index c37a9882..23996951 100644 --- a/src/main.c +++ b/src/main.c @@ -196,6 +196,16 @@ static FILE *open_the_file(const char *path, const Location *import_location) return f; } +static bool defines_main(const AstFile *ast) +{ + for (int i = 0; i < ast->body.nstatements; i++) { + const AstStatement *s = &ast->body.statements[i]; + if (s->kind == AST_STMT_FUNCTION && !strcmp(s->data.function.signature.name, "main")) + return true; + } + return false; +} + static void parse_file(struct CompileState *compst, const char *filename, const Location *import_location) { if (find_file(compst, filename)) @@ -224,6 +234,16 @@ static void parse_file(struct CompileState *compst, const char *filename, const if(command_line_args.verbosity >= 2) print_ast(&fs.ast); + // If it's not the file passed on command line, it shouldn't define main() + if (strcmp(filename, command_line_args.infile) && defines_main(&fs.ast)) { + /* + Set error location to import, so user immediately knows which file + imports something that defines main(). + */ + assert(import_location); + fail(*import_location, "imported file should not have `main` function"); + } + for (const AstImport *imp = fs.ast.imports.ptr; imp < End(fs.ast.imports); imp++) { Append(&compst->parse_queue, (struct ParseQueueItem){ .filename = imp->resolved_path, @@ -492,6 +512,15 @@ int main(int argc, char **argv) for (struct FileState *fs = compst.files.ptr; fs < End(compst.files); fs++) objpaths[fs - compst.files.ptr] = compile_ast_to_object_file(fs); + /* + Check for missing main() as late as possible, so that other errors come first. + This way Jou users can work on other functions before main() function is written. + */ + struct FileState *mainfile = find_file(&compst, command_line_args.infile); + assert(mainfile); + if (!defines_main(&mainfile->ast)) + fail((Location){.filename=mainfile->path, .lineno=0}, "missing `main` function to execute the program"); + for (struct FileState *fs = compst.files.ptr; fs < End(compst.files); fs++) { free_ast(&fs->ast); free(fs->path); diff --git a/tests/other_errors/import_file_with_main.jou b/tests/other_errors/import_file_with_main.jou new file mode 100644 index 00000000..4fe9630e --- /dev/null +++ b/tests/other_errors/import_file_with_main.jou @@ -0,0 +1 @@ +import "../../examples/hello.jou" # Error: imported file should not have `main` function diff --git a/tests/other_errors/missing_main_function.jou b/tests/other_errors/missing_main_function.jou new file mode 100644 index 00000000..4f4cbb4f --- /dev/null +++ b/tests/other_errors/missing_main_function.jou @@ -0,0 +1,3 @@ +# Output: compiler error in file "tests/other_errors/missing_main_function.jou": missing `main` function to execute the program +def test() -> None: + _ = "lol no main func"