package prog8tests.compiler import io.kotest.assertions.withClue import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldStartWith import prog8.code.core.ZeropageType import prog8.code.core.internedStringsModuleName import prog8.code.target.C64Target import prog8.code.target.Cx16Target import prog8.code.target.VMTarget import prog8.compiler.determineCompilationOptions import prog8.compiler.parseMainModule import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.compileText import prog8tests.helpers.outputDir class TestImportedModulesOrderAndOptions: FunSpec({ test("testImportedModuleOrderAndMainModuleCorrect") { val result = compileText( C64Target(), false, """ %import textio %import floats main { sub start() { ; nothing } } """)!! result.compilerAst.toplevelModule.name shouldStartWith "on_the_fly_test" val moduleNames = result.compilerAst.modules.map { it.name } withClue("main module must be first") { moduleNames[0] shouldStartWith "on_the_fly_test" } withClue("module order in parse tree") { moduleNames.drop(1) shouldBe listOf( internedStringsModuleName, "textio", "syslib", "conv", "shared_cbm_textio_functions", "floats", "shared_floats_functions", "prog8_math", "prog8_lib" ) } result.compilerAst.toplevelModule.name shouldStartWith "on_the_fly_test" } test("testCompilationOptionsCorrectFromMain") { val result = compileText( C64Target(), false, """ %import textio %import floats %zeropage dontuse %option no_sysinit main { sub start() { ; nothing } } """)!! result.compilerAst.toplevelModule.name shouldStartWith "on_the_fly_test" val options = determineCompilationOptions(result.compilerAst, C64Target()) options.floats shouldBe true options.zeropage shouldBe ZeropageType.DONTUSE options.noSysInit shouldBe true } test("testModuleOrderAndCompilationOptionsCorrectWithJustImports") { val errors = ErrorReporterForTests() val sourceText = """ %import textio %import floats %option no_sysinit %zeropage dontuse main { sub start() { ; nothing } } """ 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) = 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") { importedfiles.size shouldBe 1 } withClue("module order in parse tree") { program.modules.map { it.name } shouldBe listOf( internedStringsModuleName, filenameBase, "textio", "syslib", "conv", "shared_cbm_textio_functions", "floats", "shared_floats_functions", "prog8_math", "prog8_lib" ) } options.floats shouldBe true options.noSysInit shouldBe true withClue("zeropage option must be correctly taken from main module, not from float module import logic") { options.zeropage shouldBe ZeropageType.DONTUSE } } test("merge option works on library modules") { val src=""" %zeropage basicsafe %import textio txt { %option merge sub println(uword string) { txt.print(string) txt.nl() } } main { sub start() { txt.lowercase() txt.println("Hello, world1") txt.println("Hello, world2") txt.println("Hello, world3") } }""" compileText(VMTarget(), optimize = false, src) shouldNotBe null } test("double merge is invalid") { val src=""" main { sub start() { block1.sub1() block1.sub2() } } block1 { %option merge sub sub1() { cx16.r1++ } } block1 { %option merge sub sub2() { cx16.r2++ } }""" val errors=ErrorReporterForTests() compileText(VMTarget(), optimize = false, src, errors=errors) shouldBe null errors.errors.size shouldBe 1 errors.errors[0] shouldContain "all declarations of block 'block1' have %option merge" } test("merge works") { val src = """ %import textio main { sub start() { blah.test() } } txt { ; merges this block into the txt block coming from the textio library %option merge sub schrijf(str arg) { print(arg) } } blah { ; merges this block into the other 'blah' one %option merge sub test() { printit("test merge") } } blah { sub printit(str arg) { txt.schrijf(arg) } }""" compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null } test("merge override existing subroutine") { val src=""" %import textio main { sub start() { txt.print("sdfdsf") } } txt { %option merge sub print(str text) { cx16.r0++ ; just some dummy implementation to replace existing print } }""" val result = compileText(VMTarget(), optimize=false, src, writeAssembly=false) result shouldNotBe null } test("merge doesn't override existing subroutine if signature differs") { val src=""" %import textio main { sub start() { txt.print("sdfdsf") } } txt { %option merge sub print(str anotherparamname) { cx16.r0++ ; just some dummy implementation to replace existing print } }""" val errors = ErrorReporterForTests() compileText(VMTarget(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null errors.errors.size shouldBe 1 errors.errors[0] shouldContain "name conflict" } test("merge of float stuff into sys and txt - import order 1") { val src=""" %import textio %import floats main { sub start() { txt.print_b(sys.MIN_BYTE) txt.print_b(sys.MAX_BYTE) txt.print_ub(sys.MIN_UBYTE) txt.print_ub(sys.MAX_UBYTE) txt.print_w(sys.MIN_WORD) txt.print_w(sys.MAX_WORD) txt.print_uw(sys.MIN_UWORD) txt.print_uw(sys.MAX_UWORD) txt.print_f(floats.EPSILON) txt.print_f(sys.MIN_FLOAT) txt.print_f(sys.MAX_FLOAT) txt.print_f(floats.E) txt.print_ub(sys.SIZEOF_FLOAT) } }""" compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null compileText(Cx16Target(), optimize=false, src, writeAssembly=false) shouldNotBe null } test("merge of float stuff into sys and txt - import order 2") { val src=""" %import floats %import textio main { sub start() { txt.print_b(sys.MIN_BYTE) txt.print_b(sys.MAX_BYTE) txt.print_ub(sys.MIN_UBYTE) txt.print_ub(sys.MAX_UBYTE) txt.print_w(sys.MIN_WORD) txt.print_w(sys.MAX_WORD) txt.print_uw(sys.MIN_UWORD) txt.print_uw(sys.MAX_UWORD) txt.print_f(floats.EPSILON) txt.print_f(sys.MIN_FLOAT) txt.print_f(sys.MAX_FLOAT) txt.print_f(floats.E) txt.print_ub(sys.SIZEOF_FLOAT) } }""" compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null compileText(Cx16Target(), optimize=false, src, writeAssembly=false) shouldNotBe null } })