mirror of
				https://github.com/irmen/prog8.git
				synced 2025-11-04 10:16:13 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			317 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Kotlin
		
	
	
	
	
	
			
		
		
	
	
			317 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Kotlin
		
	
	
	
	
	
package prog8tests.compiler
 | 
						|
 | 
						|
import io.kotest.assertions.withClue
 | 
						|
import io.kotest.core.spec.style.FunSpec
 | 
						|
import io.kotest.engine.spec.tempdir
 | 
						|
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.INTERNED_STRINGS_MODULENAME
 | 
						|
import prog8.code.core.ZeropageType
 | 
						|
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
 | 
						|
 | 
						|
 | 
						|
class TestImportedModulesOrderAndOptions: FunSpec({
 | 
						|
 | 
						|
    val outputDir = tempdir().toPath()
 | 
						|
 | 
						|
    test("testImportedModuleOrderAndMainModuleCorrect") {
 | 
						|
        val result = compileText(
 | 
						|
            C64Target(), false, """
 | 
						|
%import textio
 | 
						|
%import floats
 | 
						|
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        ; nothing
 | 
						|
    }
 | 
						|
}
 | 
						|
""", outputDir)!!
 | 
						|
        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(
 | 
						|
                INTERNED_STRINGS_MODULENAME,
 | 
						|
                "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
 | 
						|
    }
 | 
						|
}
 | 
						|
""", outputDir)!!
 | 
						|
        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(), emptyList(), false)
 | 
						|
 | 
						|
        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(
 | 
						|
                    INTERNED_STRINGS_MODULENAME,
 | 
						|
                    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, outputDir) 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, outputDir, 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, outputDir, 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, outputDir, 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, outputDir, 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, outputDir, writeAssembly=false) shouldNotBe null
 | 
						|
        compileText(Cx16Target(), optimize=false, src, outputDir, 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, outputDir, writeAssembly=false) shouldNotBe null
 | 
						|
        compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
 | 
						|
    }
 | 
						|
})
 |