prog8/compiler/test/TestImportedModulesOrderAndOptions.kt
2024-11-23 12:15:15 +01:00

314 lines
7.5 KiB
Kotlin

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
}
})