From dbfe4140e1ed71373339b1eacd21097697c9366b Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 28 Feb 2023 20:08:11 +0100 Subject: [PATCH] improved import search paths --- compiler/src/prog8/compiler/Compiler.kt | 24 +++----- compiler/src/prog8/compiler/ModuleImporter.kt | 60 +++++++------------ compiler/test/ModuleImporterTests.kt | 26 ++++---- .../TestImportedModulesOrderAndOptions.kt | 4 +- docs/source/todo.rst | 5 -- 5 files changed, 44 insertions(+), 75 deletions(-) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 330d798c3..e189ee3b4 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -65,11 +65,8 @@ fun compileProgram(args: CompilerArguments): CompilationResult? { try { val totalTime = measureTimeMillis { - // import main module and everything it needs - val (programresult, options, imported) = parseImports(args.filepath, args.errors, compTarget, args.sourceDirs) + val (programresult, options, imported) = parseMainModule(args.filepath, args.errors, compTarget, args.sourceDirs) compilationOptions = options - print("Parsed ${args.filepath}") - ModuleImporter.ansiEraseRestOfLine(true) with(compilationOptions) { slowCodegenWarnings = args.slowCodegenWarnings @@ -237,17 +234,16 @@ private class BuiltinFunctionsFacade(functions: Map): IBuilt override fun returnType(funcName: String) = builtinFunctionReturnType(funcName) } -fun parseImports(filepath: Path, - errors: IErrorReporter, - compTarget: ICompilationTarget, - sourceDirs: List): Triple> { - println("Compilation target: ${compTarget.name}") +fun parseMainModule(filepath: Path, + errors: IErrorReporter, + compTarget: ICompilationTarget, + sourceDirs: List): Triple> { val bf = BuiltinFunctionsFacade(BuiltinFunctions) val program = Program(filepath.nameWithoutExtension, bf, compTarget, compTarget) bf.program = program val importer = ModuleImporter(program, compTarget.name, errors, sourceDirs) - val importedModuleResult = importer.importModule(filepath) + val importedModuleResult = importer.importMainModule(filepath) importedModuleResult.onFailure { throw it } errors.report() @@ -257,11 +253,11 @@ fun parseImports(filepath: Path, val compilerOptions = determineCompilationOptions(program, compTarget) // depending on the machine and compiler options we may have to include some libraries for(lib in compTarget.machine.importLibs(compilerOptions, compTarget.name)) - importer.importLibraryModule(lib) + importer.importImplicitLibraryModule(lib) // always import prog8_lib and math - importer.importLibraryModule("math") - importer.importLibraryModule("prog8_lib") + importer.importImplicitLibraryModule("math") + importer.importImplicitLibraryModule("prog8_lib") if (compilerOptions.launcher == CbmPrgLauncherType.BASIC && compilerOptions.output != OutputType.PRG) errors.err("BASIC launcher requires output type PRG", program.toplevelModule.position) @@ -339,7 +335,6 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget } private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) { - println("Analyzing code...") program.preprocessAst(errors, compilerOptions) program.checkIdentifiers(errors, compilerOptions) errors.report() @@ -364,7 +359,6 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions } private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget) { - println("Optimizing...") val remover = UnusedCodeRemover(program, errors, compTarget) remover.visit(program) remover.applyModifications() diff --git a/compiler/src/prog8/compiler/ModuleImporter.kt b/compiler/src/prog8/compiler/ModuleImporter.kt index fa06feb19..a3384dc35 100644 --- a/compiler/src/prog8/compiler/ModuleImporter.kt +++ b/compiler/src/prog8/compiler/ModuleImporter.kt @@ -13,7 +13,9 @@ import prog8.code.core.SourceCode import prog8.parser.Prog8Parser import java.io.File import java.nio.file.Path -import kotlin.io.path.* +import kotlin.io.path.Path +import kotlin.io.path.absolute +import kotlin.io.path.exists class ModuleImporter(private val program: Program, @@ -21,30 +23,25 @@ class ModuleImporter(private val program: Program, val errors: IErrorReporter, sourceDirs: List) { - private val sourcePaths: List = sourceDirs.map { Path(it) } + private val sourcePaths: List = sourceDirs.map { Path(it).absolute().normalize() }.toSortedSet().toList() - fun importModule(filePath: Path): Result { - val currentDir = Path("").absolute() - val searchIn = listOf(currentDir) + sourcePaths - val candidates = searchIn - .map { it.absolute().div(filePath).normalize().absolute() } - .filter { it.exists() } - .map { currentDir.relativize(it) } - .map { if (it.isAbsolute) it else Path(".", "$it") } - - val srcPath = when (candidates.size) { - 0 -> return Err(NoSuchFileException( - file = filePath.normalize().toFile(), - reason = "Searched in $searchIn")) - 1 -> candidates.first() - else -> candidates.first() // when more candiates, pick the one from the first location + fun importMainModule(filePath: Path): Result { + val searchIn = (listOf(Path("").absolute()) + sourcePaths).toSortedSet() + val normalizedFilePath = filePath.normalize() + for(path in searchIn) { + val programPath = path.resolve(normalizedFilePath) + if(programPath.exists()) { + println("Compiling program ${Path("").absolute().relativize(programPath)}") + val source = SourceCode.File(programPath) + return Ok(importModule(source)) + } } - - val source = SourceCode.File(srcPath) - return Ok(importModule(source)) + return Err(NoSuchFileException( + file = normalizedFilePath.toFile(), + reason = "Searched in $searchIn")) } - fun importLibraryModule(name: String): Module? { + fun importImplicitLibraryModule(name: String): Module? { val import = Directive("%import", listOf( DirectiveArg("", name, 42u, position = Position("<<>>", 0, 0, 0)) ), Position("<<>>", 0, 0, 0)) @@ -52,7 +49,6 @@ class ModuleImporter(private val program: Program, } private fun importModule(src: SourceCode) : Module { - printImportingMessage(src.name, src.origin) val moduleAst = Prog8Parser.parseModule(src) program.addModule(moduleAst) @@ -146,10 +142,8 @@ class ModuleImporter(private val program: Program, if (importingModule == null) { // <=> imported from library module sourcePaths } else { - val dropCurDir = if(sourcePaths.isNotEmpty() && sourcePaths[0].name == ".") 1 else 0 - sourcePaths.drop(dropCurDir) + - listOf(Path(importingModule.position.file).parent ?: Path("")) + - listOf(Path(".", "prog8lib")) + val pathFromImportingModule = (Path(importingModule.position.file).parent ?: Path("")).absolute() + listOf(pathFromImportingModule) + sourcePaths } locations.forEach { @@ -161,18 +155,4 @@ class ModuleImporter(private val program: Program, return Err(NoSuchFileException(File("name"))) } - - fun printImportingMessage(module: String, origin: String) { - print(" importing '$module' (from ${origin})") - ansiEraseRestOfLine(false) - print("\r") - } - - companion object { - fun ansiEraseRestOfLine(newline: Boolean) { - print("\u001b[0K") - if(newline) - println() - } - } } diff --git a/compiler/test/ModuleImporterTests.kt b/compiler/test/ModuleImporterTests.kt index b816b7561..9e5af853d 100644 --- a/compiler/test/ModuleImporterTests.kt +++ b/compiler/test/ModuleImporterTests.kt @@ -43,7 +43,7 @@ class TestModuleImporter: FunSpec({ val importer = makeImporter(null, dirRel.invariantSeparatorsPathString) val srcPathRel = assumeNotExists(dirRel, "i_do_not_exist") val srcPathAbs = srcPathRel.absolute() - val error1 = importer.importModule(srcPathRel).getErrorOrElse { fail("should have import error") } + val error1 = importer.importMainModule(srcPathRel).getErrorOrElse { fail("should have import error") } withClue(".file should be normalized") { "${error1.file}" shouldBe "${error1.file.normalize()}" } @@ -51,7 +51,7 @@ class TestModuleImporter: FunSpec({ error1.file.absolutePath shouldBe "${srcPathAbs.normalize()}" } program.modules.size shouldBe 1 - val error2 = importer.importModule(srcPathAbs).getErrorOrElse { fail("should have import error") } + val error2 = importer.importMainModule(srcPathAbs).getErrorOrElse { fail("should have import error") } withClue(".file should be normalized") { "${error2.file}" shouldBe "${error2.file.normalize()}" } @@ -67,7 +67,7 @@ class TestModuleImporter: FunSpec({ val searchIn = Path(".", "$srcPathRel").invariantSeparatorsPathString val importer = makeImporter(null, searchIn) - shouldThrow { importer.importModule(srcPathRel) } + shouldThrow { importer.importMainModule(srcPathRel) } .let { withClue(".file should be normalized") { "${it.file}" shouldBe "${it.file.normalize()}" @@ -78,7 +78,7 @@ class TestModuleImporter: FunSpec({ } program.modules.size shouldBe 1 - shouldThrow { importer.importModule(srcPathAbs) } + shouldThrow { importer.importMainModule(srcPathAbs) } .let { withClue(".file should be normalized") { "${it.file}" shouldBe "${it.file.normalize()}" @@ -101,7 +101,7 @@ class TestModuleImporter: FunSpec({ val fileName = "ast_simple_main.p8" val path = assumeReadableFile(searchIn[0], fileName) - val module = importer.importModule(path.absolute()).getOrElse { throw it } + val module = importer.importMainModule(path.absolute()).getOrElse { throw it } program.modules.size shouldBe 2 module shouldBeIn program.modules module.program shouldBe program @@ -118,7 +118,7 @@ class TestModuleImporter: FunSpec({ path.isAbsolute shouldBe false } - val module = importer.importModule(path).getOrElse { throw it } + val module = importer.importMainModule(path).getOrElse { throw it } program.modules.size shouldBe 2 module shouldBeIn program.modules module.program shouldBe program @@ -133,7 +133,7 @@ class TestModuleImporter: FunSpec({ val path = Path(".", fileName) assumeReadableFile(searchIn, path) - val module = importer.importModule(path).getOrElse { throw it } + val module = importer.importMainModule(path).getOrElse { throw it } program.modules.size shouldBe 2 module shouldBeIn program.modules module.program shouldBe program @@ -145,7 +145,7 @@ class TestModuleImporter: FunSpec({ val importer = makeImporter(null, searchIn.invariantSeparatorsPathString) val srcPath = assumeReadableFile(fixturesDir, "ast_file_with_syntax_error.p8") - val act = { importer.importModule(srcPath) } + val act = { importer.importMainModule(srcPath) } repeat(2) { n -> withClue(count[n] + " call") { shouldThrow { act() }.let { @@ -165,7 +165,7 @@ class TestModuleImporter: FunSpec({ val importing = assumeReadableFile(fixturesDir, "import_file_with_syntax_error.p8") val imported = assumeReadableFile(fixturesDir, "file_with_syntax_error.p8") - val act = { importer.importModule(importing) } + val act = { importer.importMainModule(importing) } repeat(repetitions) { n -> withClue(count[n] + " call") { shouldThrow { act() }.let { @@ -201,14 +201,14 @@ class TestModuleImporter: FunSpec({ val filenameWithExt = assumeNotExists(fixturesDir, "i_do_not_exist.p8").name repeat(2) { n -> - val result = importer.importLibraryModule(filenameNoExt) + val result = importer.importImplicitLibraryModule(filenameNoExt) withClue(count[n] + " call / NO .p8 extension") { result shouldBe null } withClue(count[n] + " call / NO .p8 extension") { errors.noErrors() shouldBe false } errors.errors.single() shouldContain "0:0: no module found with name i_do_not_exist" errors.report() program.modules.size shouldBe 1 - val result2 = importer.importLibraryModule(filenameWithExt) + val result2 = importer.importImplicitLibraryModule(filenameWithExt) withClue(count[n] + " call / with .p8 extension") { result2 shouldBe null } withClue(count[n] + " call / with .p8 extension") { importer.errors.noErrors() shouldBe false } errors.errors.single() shouldContain "0:0: no module found with name i_do_not_exist.p8" @@ -228,7 +228,7 @@ class TestModuleImporter: FunSpec({ repeat(2) { n -> withClue(count[n] + " call") { shouldThrow() { - importer.importLibraryModule(srcPath.nameWithoutExtension) }.let { + importer.importImplicitLibraryModule(srcPath.nameWithoutExtension) }.let { it.position.file shouldBe SourceCode.relative(srcPath).toString() withClue("line; should be 1-based") { it.position.line shouldBe 2 } withClue("startCol; should be 0-based") { it.position.startCol shouldBe 6 } @@ -246,7 +246,7 @@ class TestModuleImporter: FunSpec({ val importing = assumeReadableFile(fixturesDir, "import_file_with_syntax_error.p8") val imported = assumeReadableFile(fixturesDir, "file_with_syntax_error.p8") - val act = { importer.importLibraryModule(importing.nameWithoutExtension) } + val act = { importer.importImplicitLibraryModule(importing.nameWithoutExtension) } repeat(repetitions) { n -> withClue(count[n] + " call") { shouldThrow { diff --git a/compiler/test/TestImportedModulesOrderAndOptions.kt b/compiler/test/TestImportedModulesOrderAndOptions.kt index d4b6ef54d..a877e71b2 100644 --- a/compiler/test/TestImportedModulesOrderAndOptions.kt +++ b/compiler/test/TestImportedModulesOrderAndOptions.kt @@ -8,7 +8,7 @@ import prog8.code.core.ZeropageType import prog8.code.core.internedStringsModuleName import prog8.code.target.C64Target import prog8.compiler.determineCompilationOptions -import prog8.compiler.parseImports +import prog8.compiler.parseMainModule import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.compileText import prog8tests.helpers.outputDir @@ -87,7 +87,7 @@ main { val filenameBase = "on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16) val filepath = outputDir.resolve("$filenameBase.p8") filepath.toFile().writeText(sourceText) - val (program, options, importedfiles) = parseImports(filepath, errors, C64Target(), emptyList()) + val (program, options, importedfiles) = parseMainModule(filepath, errors, C64Target(), emptyList()) program.toplevelModule.name shouldBe filenameBase withClue("all imports other than the test source must have been internal resources library files") { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 7e67df90f..345dbb67c 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,11 +3,6 @@ TODO For next minor release ^^^^^^^^^^^^^^^^^^^^^^ -- improve search paths for %import; importing something from code in a subdir - won't find a module in the current directory where the compiler is launched in. - also it prints '.' as search path which is confusing. - - ...