From 4011dce31b976432a8060fd95a7af0fd2a143e12 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 19 Oct 2021 00:26:02 +0200 Subject: [PATCH 1/9] added a few more tests for the file element of Position --- compiler/src/prog8/compiler/ModuleImporter.kt | 1 - compilerAst/test/TestProg8Parser.kt | 65 +++++++++++++++++-- compilerAst/test/TestSourceCode.kt | 18 +++-- compilerAst/test/fixtures/simple_main.p8 | 3 + 4 files changed, 74 insertions(+), 13 deletions(-) diff --git a/compiler/src/prog8/compiler/ModuleImporter.kt b/compiler/src/prog8/compiler/ModuleImporter.kt index c4eaa0317..9a60e2d1f 100644 --- a/compiler/src/prog8/compiler/ModuleImporter.kt +++ b/compiler/src/prog8/compiler/ModuleImporter.kt @@ -51,7 +51,6 @@ class ModuleImporter(private val program: Program, return executeImportDirective(import, null) } - //private fun importModule(stream: CharStream, modulePath: Path, isLibrary: Boolean): Module { private fun importModule(src: SourceCode) : Module { val moduleAst = Prog8Parser.parseModule(src) program.addModule(moduleAst) diff --git a/compilerAst/test/TestProg8Parser.kt b/compilerAst/test/TestProg8Parser.kt index 4910fc7e6..da566e58a 100644 --- a/compilerAst/test/TestProg8Parser.kt +++ b/compilerAst/test/TestProg8Parser.kt @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import prog8.ast.IFunctionCall +import prog8.ast.Module import prog8.ast.Node import prog8.ast.base.Position import prog8.ast.expressions.CharLiteral @@ -17,12 +18,11 @@ import prog8.parser.SourceCode import prog8tests.helpers.assumeNotExists import prog8tests.helpers.assumeReadableFile import prog8tests.helpers.fixturesDir +import kotlin.io.path.Path +import kotlin.io.path.isRegularFile import kotlin.io.path.name import kotlin.io.path.nameWithoutExtension -import kotlin.test.assertContains -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertIs +import kotlin.test.* @TestInstance(TestInstance.Lifecycle.PER_CLASS) @@ -330,9 +330,9 @@ class TestProg8Parser { val mpf = module.position.file assertPositionOf(module, SourceCode.relative(path).toString(), 1, 0) // TODO: endCol wrong val mainBlock = module.statements.filterIsInstance()[0] - assertPositionOf(mainBlock, mpf, 1, 0) // TODO: endCol wrong! + assertPositionOf(mainBlock, mpf, 2, 0) // TODO: endCol wrong! val startSub = mainBlock.statements.filterIsInstance()[0] - assertPositionOf(startSub, mpf, 2, 4) // TODO: endCol wrong! + assertPositionOf(startSub, mpf, 3, 4) // TODO: endCol wrong! } @@ -378,6 +378,59 @@ class TestProg8Parser { } } + @Nested + inner class PositionFile { + @Test + fun `isn't absolute for filesystem paths`() { + val path = assumeReadableFile(fixturesDir, "simple_main.p8") + val module = parseModule(SourceCode.File(path)) + assertSomethingForAllNodes(module) { + assertFalse(Path(it.position.file).isAbsolute) + assertTrue(Path(it.position.file).isRegularFile()) + } + } + + @Test + fun `is mangled string id for string sources`() + { + val srcText=""" + %zeropage basicsafe + main { + sub start() { + ubyte aa=99 + aa++ + } + } + """.trimIndent() + val module = parseModule(SourceCode.Text(srcText)) + assertSomethingForAllNodes(module) { + assertTrue(it.position.file.startsWith(SourceCode.stringSourcePrefix)) + } + } + + @Test + fun `is library prefixed path for resources`() + { + val resource = SourceCode.Resource("prog8lib/math.p8") + val module = parseModule(resource) + assertSomethingForAllNodes(module) { + assertTrue(it.position.file.startsWith(SourceCode.libraryFilePrefix)) + } + } + + private fun assertSomethingForAllNodes(module: Module, asserter: (Node) -> Unit) { + asserter(module) + module.statements.forEach(asserter) + module.statements.filterIsInstance().forEach { b -> + asserter(b) + b.statements.forEach(asserter) + b.statements.filterIsInstance().forEach { s -> + asserter(s) + s.statements.forEach(asserter) + } + } + } } + @Nested inner class CharLiterals { diff --git a/compilerAst/test/TestSourceCode.kt b/compilerAst/test/TestSourceCode.kt index 04d8e5d99..5f9027814 100644 --- a/compilerAst/test/TestSourceCode.kt +++ b/compilerAst/test/TestSourceCode.kt @@ -1,30 +1,32 @@ package prog8tests +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.core.StringStartsWith import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import prog8.parser.SourceCode import prog8.parser.SourceCode.Companion.libraryFilePrefix import prog8tests.helpers.* import kotlin.io.path.Path -import kotlin.test.assertContains -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue +import kotlin.test.* @TestInstance(TestInstance.Lifecycle.PER_CLASS) class TestSourceCode { @Test - fun testFactoryMethod_Of() { + fun testFromString() { val text = """ main { } - """.trimIndent() + """ val src = SourceCode.Text(text) val actualText = src.getCharStream().toString() assertContains(src.origin, Regex("^$")) assertEquals(text, actualText) + assertFalse(src.isFromResources) + assertFalse(src.isFromFilesystem) + assertThat(src.toString(), StringStartsWith("prog8.parser.SourceCode")) } @Test @@ -54,6 +56,8 @@ class TestSourceCode { val expectedOrigin = SourceCode.relative(path).toString() assertEquals(expectedOrigin, src.origin) assertEquals(path.toFile().readText(), src.asString()) + assertFalse(src.isFromResources) + assertTrue(src.isFromFilesystem) } @Test @@ -75,6 +79,8 @@ class TestSourceCode { assertEquals("$libraryFilePrefix/$pathString", src.origin) assertEquals(srcFile.readText(), src.asString()) + assertTrue(src.isFromResources) + assertFalse(src.isFromFilesystem) } @Test diff --git a/compilerAst/test/fixtures/simple_main.p8 b/compilerAst/test/fixtures/simple_main.p8 index fb81add50..e2795ef11 100644 --- a/compilerAst/test/fixtures/simple_main.p8 +++ b/compilerAst/test/fixtures/simple_main.p8 @@ -1,4 +1,7 @@ +%zeropage basicsafe main { sub start() { + ubyte aa=99 + aa++ } } From 40bf117497c58e273a78ef022d94e2d5455c9c20 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 19 Oct 2021 00:44:33 +0200 Subject: [PATCH 2/9] avoid crash when parser doesn't report an offending token for a parse error --- compilerAst/src/prog8/parser/Prog8Parser.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compilerAst/src/prog8/parser/Prog8Parser.kt b/compilerAst/src/prog8/parser/Prog8Parser.kt index 546615afa..5524be3ba 100644 --- a/compilerAst/src/prog8/parser/Prog8Parser.kt +++ b/compilerAst/src/prog8/parser/Prog8Parser.kt @@ -98,7 +98,11 @@ object Prog8Parser { TODO("no RecognitionException - create your own ParseError") //throw ParseError() } else { - throw ParseError(msg, e.getPosition(src.origin), e) + if(e.offendingToken==null) { + throw ParseError(msg, Position(src.origin, line, charPositionInLine, charPositionInLine), e) + } else { + throw ParseError(msg, e.getPosition(src.origin), e) + } } } } From 502bf900073793d6c03d1524a6eeb8149900d7f8 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 19 Oct 2021 01:12:28 +0200 Subject: [PATCH 3/9] comments --- compiler/src/prog8/compiler/Compiler.kt | 2 ++ compiler/src/prog8/compiler/ModuleImporter.kt | 2 +- compiler/test/fixtures/asmIncludeFromSameFolder.p8 | 2 +- .../src/prog8/ast/expressions/AstExpressions.kt | 12 ++++++------ docs/source/todo.rst | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 363b776c7..8d1d9e255 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -280,6 +280,8 @@ private fun processAst(programAst: Program, errors: IErrorReporter, compilerOpti programAst.checkIdentifiers(errors, compilerOptions) errors.report() // TODO: turning char literals into UBYTEs via an encoding should really happen in code gen - but for that we'd need DataType.CHAR + // NOTE: we will then lose the opportunity to do constant-folding on any expression containing a char literal, but how often will those occur? + // Also they might be optimized away eventually in codegen or by the assembler even programAst.charLiteralsToUByteLiterals(errors, compilerOptions.compTarget) errors.report() programAst.constantFold(errors, compilerOptions.compTarget) diff --git a/compiler/src/prog8/compiler/ModuleImporter.kt b/compiler/src/prog8/compiler/ModuleImporter.kt index 9a60e2d1f..f15374151 100644 --- a/compiler/src/prog8/compiler/ModuleImporter.kt +++ b/compiler/src/prog8/compiler/ModuleImporter.kt @@ -132,7 +132,7 @@ class ModuleImporter(private val program: Program, } else { val dropCurDir = if(sourcePaths.isNotEmpty() && sourcePaths[0].name == ".") 1 else 0 sourcePaths.drop(dropCurDir) + - // FIXME: won't work until Prog8Parser is fixed s.t. it fully initialzes the modules it returns + // TODO: won't work until Prog8Parser is fixed s.t. it fully initializes the modules it returns. ??? (what won't work?) listOf(Path(importingModule.position.file).parent ?: Path("")) + listOf(Path(".", "prog8lib")) } diff --git a/compiler/test/fixtures/asmIncludeFromSameFolder.p8 b/compiler/test/fixtures/asmIncludeFromSameFolder.p8 index 6fe983fba..8c5617d77 100644 --- a/compiler/test/fixtures/asmIncludeFromSameFolder.p8 +++ b/compiler/test/fixtures/asmIncludeFromSameFolder.p8 @@ -2,7 +2,7 @@ main { str myBar = "main.bar" ;foo_bar: -; %asminclude "foo_bar.asm" ; FIXME: should be accessible from inside start() but give assembler error. See github issue #62 +; %asminclude "foo_bar.asm" ; TODO: should be accessible from inside start() but give assembler error. See github issue #62 sub start() { txt.print(myBar) txt.print(&foo_bar) diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index d30c9aed2..3878278e9 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -301,7 +301,7 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun referencesIdentifier(vararg scopedName: String) = expression.referencesIdentifier(*scopedName) - override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type) + override fun inferType(program: Program) = InferredTypes.knownFor(type) override fun constValue(program: Program): NumericLiteralValue? { val cv = expression.constValue(program) ?: return null val cast = cv.cast(type) @@ -334,7 +334,7 @@ data class AddressOf(var identifier: IdentifierReference, override val position: override fun constValue(program: Program): NumericLiteralValue? = null override fun referencesIdentifier(vararg scopedName: String) = false - override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UWORD) + override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UWORD) override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) } @@ -359,7 +359,7 @@ class DirectMemoryRead(var addressExpression: Expression, override val position: override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun referencesIdentifier(vararg scopedName: String) = false - override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UBYTE) + override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UBYTE) override fun constValue(program: Program): NumericLiteralValue? = null override fun toString(): String { @@ -422,7 +422,7 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed override fun toString(): String = "NumericLiteral(${type.name}:$number)" - override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type) + override fun inferType(program: Program) = InferredTypes.knownFor(type) override fun hashCode(): Int = Objects.hash(type, number) @@ -518,7 +518,7 @@ class CharLiteral(val value: Char, override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun toString(): String = "'${escape(value.toString())}'" - override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UNDEFINED) // FIXME: CharLiteral.inferType + override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UBYTE) operator fun compareTo(other: CharLiteral): Int = value.compareTo(other.value) override fun hashCode(): Int = Objects.hash(value, altEncoding) override fun equals(other: Any?): Boolean { @@ -550,7 +550,7 @@ class StringLiteralValue(val value: String, override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun toString(): String = "'${escape(value)}'" - override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.STR) + override fun inferType(program: Program) = InferredTypes.knownFor(DataType.STR) operator fun compareTo(other: StringLiteralValue): Int = value.compareTo(other.value) override fun hashCode(): Int = Objects.hash(value, altEncoding) override fun equals(other: Any?): Boolean { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 2be82798f..e9117d513 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -14,7 +14,7 @@ Blocked by Commander-x16 v39 release Future ^^^^^^ -- get rid of all TODO's and FIXME's in the code +- get rid of all TODO's in the code - improve testability further, add more tests, address more questions/issues from the testability discussions. - replace certain uses of inferredType.getOr(DataType.UNDEFINED) by i.getOrElse({ errorhandler }) - see if we can remove more "[InferredType].getOr(DataType.UNDEFINED)" From e5b9e1f5e79095b33f356815242d4014c874baed Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 19 Oct 2021 21:08:15 +0200 Subject: [PATCH 4/9] string object identity hashcode can be negative sometimes, so allow a '-' character. --- compilerAst/test/TestProg8Parser.kt | 6 +++--- compilerAst/test/TestSourceCode.kt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compilerAst/test/TestProg8Parser.kt b/compilerAst/test/TestProg8Parser.kt index da566e58a..edac24c9c 100644 --- a/compilerAst/test/TestProg8Parser.kt +++ b/compilerAst/test/TestProg8Parser.kt @@ -219,7 +219,7 @@ class TestProg8Parser { val module = parseModule(SourceCode.Text(srcText)) // Note: assertContains has *actual* as first param - assertContains(module.name, Regex("^$")) + assertContains(module.name, Regex("^$")) } @Test @@ -289,7 +289,7 @@ class TestProg8Parser { try { parseModule(SourceCode.Text(srcText)) } catch (e: ParseError) { - assertPosition(e.position, Regex("^$"), 1, 4, 4) + assertPosition(e.position, Regex("^$"), 1, 4, 4) } } @@ -312,7 +312,7 @@ class TestProg8Parser { } """.trimIndent() val module = parseModule(SourceCode.Text(srcText)) - assertPositionOf(module, Regex("^$"), 1, 0) // TODO: endCol wrong + assertPositionOf(module, Regex("^$"), 1, 0) // TODO: endCol wrong } @Test diff --git a/compilerAst/test/TestSourceCode.kt b/compilerAst/test/TestSourceCode.kt index 5f9027814..cd0a337f0 100644 --- a/compilerAst/test/TestSourceCode.kt +++ b/compilerAst/test/TestSourceCode.kt @@ -22,7 +22,7 @@ class TestSourceCode { val src = SourceCode.Text(text) val actualText = src.getCharStream().toString() - assertContains(src.origin, Regex("^$")) + assertContains(src.origin, Regex("^$")) assertEquals(text, actualText) assertFalse(src.isFromResources) assertFalse(src.isFromFilesystem) From 552e0c224873c3150a33743f5b1c46e29ce0ce69 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 19 Oct 2021 21:49:05 +0200 Subject: [PATCH 5/9] rename mainModule to toplevelModule. failed module no longer retains in the Ast. improved some tests on that. --- compiler/src/prog8/compiler/Compiler.kt | 10 +++---- compiler/src/prog8/compiler/ModuleImporter.kt | 26 +++++++++++++------ .../compiler/astprocessing/AstChecker.kt | 3 +-- compiler/test/ModuleImporterTests.kt | 9 ++++--- compiler/test/TestCompilerOnExamples.kt | 8 +++--- .../TestImportedModulesOrderAndOptions.kt | 10 ++++--- compilerAst/src/prog8/ast/AstToplevel.kt | 7 ++--- 7 files changed, 43 insertions(+), 30 deletions(-) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 8d1d9e255..e464a59f0 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -215,12 +215,12 @@ fun parseImports(filepath: Path, } fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions { - val mainModule = program.mainModule - val outputDirective = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" } as? Directive) - val launcherDirective = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" } as? Directive) + val toplevelModule = program.toplevelModule + val outputDirective = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%output" } as? Directive) + val launcherDirective = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" } as? Directive) val outputTypeStr = outputDirective?.args?.single()?.name?.uppercase() val launcherTypeStr = launcherDirective?.args?.single()?.name?.uppercase() - val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" } + val zpoption: String? = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" } as? Directive)?.args?.single()?.name?.uppercase() val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" } .flatMap { (it as Directive).args }.toSet() @@ -242,7 +242,7 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget zpType = ZeropageType.BASICSAFE } - val zpReserved = mainModule.statements + val zpReserved = toplevelModule.statements .asSequence() .filter { it is Directive && it.directive == "%zpreserved" } .map { (it as Directive).args } diff --git a/compiler/src/prog8/compiler/ModuleImporter.kt b/compiler/src/prog8/compiler/ModuleImporter.kt index f15374151..a62e9f699 100644 --- a/compiler/src/prog8/compiler/ModuleImporter.kt +++ b/compiler/src/prog8/compiler/ModuleImporter.kt @@ -41,7 +41,11 @@ class ModuleImporter(private val program: Program, val logMsg = "importing '${filePath.nameWithoutExtension}' (from file $srcPath)" println(logMsg) - return Ok(importModule(SourceCode.File(srcPath))) + val module = importModule(SourceCode.File(srcPath)) + return if(module==null) + Err(NoSuchFileException(srcPath.toFile())) + else + Ok(module) } fun importLibraryModule(name: String): Module? { @@ -51,19 +55,24 @@ class ModuleImporter(private val program: Program, return executeImportDirective(import, null) } - private fun importModule(src: SourceCode) : Module { + private fun importModule(src: SourceCode) : Module? { val moduleAst = Prog8Parser.parseModule(src) program.addModule(moduleAst) // accept additional imports - val lines = moduleAst.statements.toMutableList() - lines.asSequence() + try { + val lines = moduleAst.statements.toMutableList() + lines.asSequence() .mapIndexed { i, it -> i to it } .filter { (it.second as? Directive)?.directive == "%import" } .forEach { executeImportDirective(it.second as Directive, moduleAst) } - - moduleAst.statements = lines - return moduleAst + moduleAst.statements = lines + return moduleAst + } catch (x: Exception) { + // in case of error, make sure the module we're importing is no longer in the Ast + program.removeModule(moduleAst) + throw x + } } private fun executeImportDirective(import: Directive, importingModule: Module?): Module? { @@ -103,7 +112,8 @@ class ModuleImporter(private val program: Program, } ) - removeDirectivesFromImportedModule(importedModule) + if(importedModule!=null) + removeDirectivesFromImportedModule(importedModule) return importedModule } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 001138b22..d2e704d8b 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -13,7 +13,6 @@ import prog8.compiler.ZeropageType import prog8.compiler.functions.BuiltinFunctions import prog8.compiler.functions.builtinFunctionReturnType import prog8.compiler.target.ICompilationTarget -import prog8.parser.SourceCode import java.io.CharConversionException import java.io.File import java.util.* @@ -46,7 +45,7 @@ internal class AstChecker(private val program: Program, if(compilerOptions.floats) { if (compilerOptions.zeropage !in arrayOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE )) - errors.err("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'", program.mainModule.position) + errors.err("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'", program.toplevelModule.position) } super.visit(program) diff --git a/compiler/test/ModuleImporterTests.kt b/compiler/test/ModuleImporterTests.kt index 74a2a1fe6..064f578b9 100644 --- a/compiler/test/ModuleImporterTests.kt +++ b/compiler/test/ModuleImporterTests.kt @@ -12,6 +12,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.Disabled import prog8.ast.Program +import prog8.ast.internedStringsModuleName import prog8.compiler.IErrorReporter import prog8.compiler.ModuleImporter import prog8.parser.ParseError @@ -207,7 +208,6 @@ class TestModuleImporter { } @Test - @Disabled("TODO: module that imports faulty module should not be kept in Program.modules") fun testImportingFileWithSyntaxError_twice() { doTestImportingFileWithSyntaxError(2) } @@ -227,7 +227,8 @@ class TestModuleImporter { assertThat("startCol; should be 0-based", it.position.startCol, equalTo(6)) assertThat("endCol; should be 0-based", it.position.endCol, equalTo(6)) } - assertThat(program.modules.size, equalTo(2)) + assertThat("imported module with error in it should not be present", program.modules.size, equalTo(1)) + assertThat(program.modules[0].name, equalTo(internedStringsModuleName)) } } } @@ -303,7 +304,8 @@ class TestModuleImporter { assertThat("startCol; should be 0-based", it.position.startCol, equalTo(6)) assertThat("endCol; should be 0-based", it.position.endCol, equalTo(6)) } - assertThat(program.modules.size, equalTo(2)) + assertThat("imported module with error in it should not be present", program.modules.size, equalTo(1)) + assertThat(program.modules[0].name, equalTo(internedStringsModuleName)) importer.errors.report() } } @@ -314,7 +316,6 @@ class TestModuleImporter { } @Test - @Disabled("TODO: module that imports faulty module should not be kept in Program.modules") fun testImportingFileWithSyntaxError_twice() { doTestImportingFileWithSyntaxError(2) } diff --git a/compiler/test/TestCompilerOnExamples.kt b/compiler/test/TestCompilerOnExamples.kt index f9d02d005..b53253a6a 100644 --- a/compiler/test/TestCompilerOnExamples.kt +++ b/compiler/test/TestCompilerOnExamples.kt @@ -19,7 +19,7 @@ import kotlin.io.path.exists * They are not really unit tests, but rather tests of the whole process, * from source file loading all the way through to running 64tass. */ -//@Disabled("to save some time") +// @Disabled("disable to save some time") @TestInstance(TestInstance.Lifecycle.PER_CLASS) class TestCompilerOnExamples { private val examplesDir = assumeDirectory(workingDir, "../examples") @@ -49,7 +49,7 @@ class TestCompilerOnExamples { } @TestFactory -// @Disabled +// @Disabled("disable to save some time") fun bothCx16AndC64() = mapCombinations( dim1 = listOf( "animals", @@ -80,7 +80,7 @@ class TestCompilerOnExamples { ) @TestFactory -// @Disabled +// @Disabled("disable to save some time") fun onlyC64() = mapCombinations( dim1 = listOf( "balloonflight", @@ -99,7 +99,7 @@ class TestCompilerOnExamples { ) @TestFactory -// @Disabled +// @Disabled("disable to save some time") fun onlyCx16() = mapCombinations( dim1 = listOf( "vtui/testvtui", diff --git a/compiler/test/TestImportedModulesOrderAndOptions.kt b/compiler/test/TestImportedModulesOrderAndOptions.kt index cba58d438..e85ef6fcc 100644 --- a/compiler/test/TestImportedModulesOrderAndOptions.kt +++ b/compiler/test/TestImportedModulesOrderAndOptions.kt @@ -19,7 +19,7 @@ import kotlin.test.assertTrue class TestImportedModulesOrderAndOptions { @Test - fun testImportedModuleOrderCorrect() { + fun testImportedModuleOrderAndMainModuleCorrect() { val result = compileText(C64Target, false, """ %import textio %import floats @@ -30,7 +30,7 @@ main { } } """).assertSuccess() - assertTrue(result.programAst.mainModule.name.startsWith("on_the_fly_test")) + assertTrue(result.programAst.toplevelModule.name.startsWith("on_the_fly_test")) val moduleNames = result.programAst.modules.map { it.name } assertTrue(moduleNames[0].startsWith("on_the_fly_test"), "main module must be first") @@ -43,6 +43,8 @@ main { "math", "prog8_lib" ), moduleNames.drop(1), "module order in parse tree") + + assertTrue(result.programAst.toplevelModule.name.startsWith("on_the_fly_test")) } @Test @@ -59,7 +61,7 @@ main { } } """).assertSuccess() - assertTrue(result.programAst.mainModule.name.startsWith("on_the_fly_test")) + assertTrue(result.programAst.toplevelModule.name.startsWith("on_the_fly_test")) val options = determineCompilationOptions(result.programAst, C64Target) assertTrue(options.floats) assertEquals(ZeropageType.DONTUSE, options.zeropage) @@ -86,7 +88,7 @@ main { filepath.toFile().writeText(sourceText) val (program, options, importedfiles) = parseImports(filepath, errors, C64Target, emptyList()) - assertEquals(filenameBase, program.mainModule.name) + assertEquals(filenameBase, program.toplevelModule.name) assertEquals(1, importedfiles.size, "all imports other than the test source must have been internal resources library files") assertEquals(listOf( internedStringsModuleName, diff --git a/compilerAst/src/prog8/ast/AstToplevel.kt b/compilerAst/src/prog8/ast/AstToplevel.kt index b3e665848..0d06205d4 100644 --- a/compilerAst/src/prog8/ast/AstToplevel.kt +++ b/compilerAst/src/prog8/ast/AstToplevel.kt @@ -264,6 +264,8 @@ class Program(val name: String, return this } + fun removeModule(module: Module) = _modules.remove(module) + fun moveModuleToFront(module: Module): Program { require(_modules.contains(module)) { "Not a module of this program: '${module.name}'"} @@ -285,11 +287,11 @@ class Program(val name: String, } } - val mainModule: Module // TODO: rename Program.mainModule - it's NOT necessarily the one containing the main *block*! + val toplevelModule: Module get() = modules.first { it.name!=internedStringsModuleName } val definedLoadAddress: Int - get() = mainModule.loadAddress + get() = toplevelModule.loadAddress var actualLoadAddress: Int = 0 private val internedStringsUnique = mutableMapOf, List>() @@ -344,7 +346,6 @@ class Program(val name: String, _modules[idx] = replacement replacement.parent = this // TODO: why not replacement.program = this; replacement.linkParents(namespace)?! } - } open class Module(final override var statements: MutableList, From 7f15b7b716ad1f22d26bc265ea970a90399ee1ec Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 19 Oct 2021 22:12:54 +0200 Subject: [PATCH 6/9] remove unneeded check for duplicate module names as this is now caught by the logic in Program.addModule itself --- .../compiler/astprocessing/AstExtensions.kt | 21 ------------------- compilerAst/src/prog8/parser/Prog8Parser.kt | 5 ++--- compilerAst/test/ast/ProgramTests.kt | 8 +++---- 3 files changed, 6 insertions(+), 28 deletions(-) diff --git a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt index e608f4a84..fcff410d1 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt @@ -3,7 +3,6 @@ package prog8.compiler.astprocessing import prog8.ast.Node import prog8.ast.Program import prog8.ast.base.DataType -import prog8.ast.base.FatalAstException import prog8.ast.expressions.CharLiteral import prog8.ast.expressions.NumericLiteralValue import prog8.ast.expressions.RangeExpr @@ -127,26 +126,6 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati lit2decl.visit(this) lit2decl.applyModifications() } - - // Check if each module has a unique name. - // If not report those that haven't. - // TODO: move check for unique module names to earlier stage and/or to unit tests - val namesToModules = mapOf>().toMutableMap() - for (m in modules) { - val others = namesToModules[m.name] - if (others == null) { - namesToModules[m.name] = listOf(m).toMutableList() - } else { - others.add(m) - } - } - val nonUniqueNames = namesToModules.keys - .map { Pair(it, namesToModules[it]!!.size) } - .filter { it.second > 1 } - .map { "\"${it.first}\" (x${it.second})"} - if (nonUniqueNames.isNotEmpty()) { - throw FatalAstException("modules must have unique names; of the ttl ${modules.size} these have not: $nonUniqueNames") - } } internal fun Program.variousCleanups(program: Program, errors: IErrorReporter) { diff --git a/compilerAst/src/prog8/parser/Prog8Parser.kt b/compilerAst/src/prog8/parser/Prog8Parser.kt index 5524be3ba..8e8d60002 100644 --- a/compilerAst/src/prog8/parser/Prog8Parser.kt +++ b/compilerAst/src/prog8/parser/Prog8Parser.kt @@ -95,8 +95,7 @@ object Prog8Parser { private class AntlrErrorListener(val src: SourceCode): BaseErrorListener() { override fun syntaxError(recognizer: Recognizer<*, *>?, offendingSymbol: Any?, line: Int, charPositionInLine: Int, msg: String, e: RecognitionException?) { if (e == null) { - TODO("no RecognitionException - create your own ParseError") - //throw ParseError() + throw ParseError(msg, Position(src.origin, line, charPositionInLine, charPositionInLine), RuntimeException("parse error")) } else { if(e.offendingToken==null) { throw ParseError(msg, Position(src.origin, line, charPositionInLine, charPositionInLine), e) @@ -111,7 +110,7 @@ object Prog8Parser { val offending = this.offendingToken val line = offending.line val beginCol = offending.charPositionInLine - val endCol = beginCol + offending.stopIndex - offending.startIndex // TODO: point to col *after* token? + val endCol = beginCol + offending.stopIndex - offending.startIndex // TODO: point to col *after* token? / why, what's wrong with endCol being inclusive return Position(file, line, beginCol, endCol) } diff --git a/compilerAst/test/ast/ProgramTests.kt b/compilerAst/test/ast/ProgramTests.kt index 5e51bf10c..9a9e13c3a 100644 --- a/compilerAst/test/ast/ProgramTests.kt +++ b/compilerAst/test/ast/ProgramTests.kt @@ -50,12 +50,12 @@ class ProgramTests { assertSame(program, m1.program) assertSame(program.namespace, m1.parent) - assertFailsWith { program.addModule(m1) } - .let { assertThat(it.message, containsString(m1.name)) } + assertThat("module may not occur multiple times", + assertFailsWith { program.addModule(m1) }.message, containsString(m1.name)) val m2 = Module(mutableListOf(), m1.position, m1.source) - assertFailsWith { program.addModule(m2) } - .let { assertThat(it.message, containsString(m2.name)) } + assertThat("other module but with same name may not occur multiple times", + assertFailsWith { program.addModule(m2) }.message, containsString(m2.name)) } } From 5afa7e53f8c4ebd3fcad5dd3d0de9b3a28964745 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 19 Oct 2021 22:30:30 +0200 Subject: [PATCH 7/9] got rid of program arg for isInRegularRAM --- .../compiler/BeforeAsmGenerationAstChanger.kt | 2 +- .../compiler/target/ICompilationTarget.kt | 6 +- .../src/prog8/optimizer/BinExprSplitter.kt | 6 +- .../src/prog8/optimizer/UnusedCodeRemover.kt | 2 +- compiler/test/AsmgenTests.kt | 2 +- compiler/test/TestMemory.kt | 60 +++++++++---------- compilerAst/src/prog8/ast/AstToplevel.kt | 6 ++ 7 files changed, 44 insertions(+), 40 deletions(-) diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index 4436e78e9..a9894b9ff 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -39,7 +39,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I // But it can only be done if the target variable IS NOT OCCURRING AS AN OPERAND ITSELF. if(!assignment.isAugmentable && assignment.target.identifier != null - && compTarget.isInRegularRAM(assignment.target, program)) { + && compTarget.isInRegularRAM(assignment.target)) { val binExpr = assignment.value as? BinaryExpression if (binExpr != null && binExpr.operator !in comparisonOperators) { if (binExpr.left !is BinaryExpression) { diff --git a/compiler/src/prog8/compiler/target/ICompilationTarget.kt b/compiler/src/prog8/compiler/target/ICompilationTarget.kt index eb06b9a0f..9522ed26f 100644 --- a/compiler/src/prog8/compiler/target/ICompilationTarget.kt +++ b/compiler/src/prog8/compiler/target/ICompilationTarget.kt @@ -26,8 +26,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer { override fun decodeString(bytes: List, altEncoding: Boolean): String // TODO: rename param target, and also AST node AssignTarget - *different meaning of "target"!* - // TODO: remove param program - can be obtained from AST node - fun isInRegularRAM(target: AssignTarget, program: Program): Boolean { + fun isInRegularRAM(target: AssignTarget): Boolean { val memAddr = target.memoryAddress val arrayIdx = target.arrayindexed val ident = target.identifier @@ -38,6 +37,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer { machine.isRegularRAMaddress((memAddr.addressExpression as NumericLiteralValue).number.toInt()) } is IdentifierReference -> { + val program = target.definingModule.program val decl = (memAddr.addressExpression as IdentifierReference).targetVarDecl(program) if ((decl?.type == VarDeclType.VAR || decl?.type == VarDeclType.CONST) && decl.value is NumericLiteralValue) machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt()) @@ -48,6 +48,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer { } } arrayIdx != null -> { + val program = target.definingModule.program val targetStmt = arrayIdx.arrayvar.targetVarDecl(program) return if (targetStmt?.type == VarDeclType.MEMORY) { val addr = targetStmt.value as? NumericLiteralValue @@ -58,6 +59,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer { } else true } ident != null -> { + val program = target.definingModule.program val decl = ident.targetVarDecl(program)!! return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteralValue) machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt()) diff --git a/compiler/src/prog8/optimizer/BinExprSplitter.kt b/compiler/src/prog8/optimizer/BinExprSplitter.kt index 71631ecfe..fd18ac561 100644 --- a/compiler/src/prog8/optimizer/BinExprSplitter.kt +++ b/compiler/src/prog8/optimizer/BinExprSplitter.kt @@ -54,7 +54,7 @@ X = BinExpr X = LeftExpr */ - if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target, program)) { + if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target)) { if(assignment.target isSameAs binExpr.left || assignment.target isSameAs binExpr.right) return noModifications @@ -77,9 +77,9 @@ X = BinExpr X = LeftExpr return noModifications } - private fun isSimpleTarget(target: AssignTarget, program: Program) = + private fun isSimpleTarget(target: AssignTarget) = if (target.identifier!=null || target.memoryAddress!=null) - compTarget.isInRegularRAM(target, program) + compTarget.isInRegularRAM(target) else false diff --git a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt index 00ba0ddb6..7f9dd7ce2 100644 --- a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt +++ b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt @@ -122,7 +122,7 @@ internal class UnusedCodeRemover(private val program: Program, val assign1 = stmtPairs[0] as? Assignment val assign2 = stmtPairs[1] as? Assignment if (assign1 != null && assign2 != null && !assign2.isAugmentable) { - if (assign1.target.isSameAs(assign2.target, program) && compTarget.isInRegularRAM(assign1.target, program)) { + if (assign1.target.isSameAs(assign2.target, program) && compTarget.isInRegularRAM(assign1.target)) { if(assign2.target.identifier==null || !assign2.value.referencesIdentifier(*(assign2.target.identifier!!.nameInSource.toTypedArray()))) // only remove the second assignment if its value is a simple expression! when(assign2.value) { diff --git a/compiler/test/AsmgenTests.kt b/compiler/test/AsmgenTests.kt index c66db1ce6..73d3689d3 100644 --- a/compiler/test/AsmgenTests.kt +++ b/compiler/test/AsmgenTests.kt @@ -71,7 +71,7 @@ locallabel: val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test")) val program = Program("test", DummyFunctions, DummyMemsizer) .addModule(module) - module.linkParents(program.namespace) + module.linkIntoProgram(program) return program } diff --git a/compiler/test/TestMemory.kt b/compiler/test/TestMemory.kt index f5e2a5659..3310dd911 100644 --- a/compiler/test/TestMemory.kt +++ b/compiler/test/TestMemory.kt @@ -5,7 +5,6 @@ import org.junit.jupiter.api.TestInstance import prog8.ast.Module import prog8.ast.Program import prog8.ast.base.DataType -import prog8.ast.base.ParentSentinel import prog8.ast.base.Position import prog8.ast.base.VarDeclType import prog8.ast.expressions.ArrayIndexedExpression @@ -29,24 +28,23 @@ class TestMemory { var memexpr = NumericLiteralValue.optimalInteger(0x0000, Position.DUMMY) var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - val program = Program("test", DummyFunctions, DummyMemsizer) - assertTrue(C64Target.isInRegularRAM(target, program)) + assertTrue(C64Target.isInRegularRAM(target)) memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertTrue(C64Target.isInRegularRAM(target, program)) + assertTrue(C64Target.isInRegularRAM(target)) memexpr = NumericLiteralValue.optimalInteger(0x9fff, Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertTrue(C64Target.isInRegularRAM(target, program)) + assertTrue(C64Target.isInRegularRAM(target)) memexpr = NumericLiteralValue.optimalInteger(0xc000, Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertTrue(C64Target.isInRegularRAM(target, program)) + assertTrue(C64Target.isInRegularRAM(target)) memexpr = NumericLiteralValue.optimalInteger(0xcfff, Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertTrue(C64Target.isInRegularRAM(target, program)) + assertTrue(C64Target.isInRegularRAM(target)) } @Test @@ -54,20 +52,19 @@ class TestMemory { var memexpr = NumericLiteralValue.optimalInteger(0xa000, Position.DUMMY) var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - val program = Program("test", DummyFunctions, DummyMemsizer) - assertFalse(C64Target.isInRegularRAM(target, program)) + assertFalse(C64Target.isInRegularRAM(target)) memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertFalse(C64Target.isInRegularRAM(target, program)) + assertFalse(C64Target.isInRegularRAM(target)) memexpr = NumericLiteralValue.optimalInteger(0xd000, Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertFalse(C64Target.isInRegularRAM(target, program)) + assertFalse(C64Target.isInRegularRAM(target)) memexpr = NumericLiteralValue.optimalInteger(0xffff, Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertFalse(C64Target.isInRegularRAM(target, program)) + assertFalse(C64Target.isInRegularRAM(target)) } @Test @@ -75,15 +72,15 @@ class TestMemory { val program = Program("test", DummyFunctions, DummyMemsizer) var target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.VAR) - assertTrue(C64Target.isInRegularRAM(target, program)) + assertTrue(C64Target.isInRegularRAM(target)) target = createTestProgramForMemoryRefViaVar(program, 0xd020, VarDeclType.VAR) - assertFalse(C64Target.isInRegularRAM(target, program)) + assertFalse(C64Target.isInRegularRAM(target)) target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.CONST) - assertTrue(C64Target.isInRegularRAM(target, program)) + assertTrue(C64Target.isInRegularRAM(target)) target = createTestProgramForMemoryRefViaVar(program, 0xd020, VarDeclType.CONST) - assertFalse(C64Target.isInRegularRAM(target, program)) + assertFalse(C64Target.isInRegularRAM(target)) target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.MEMORY) - assertFalse(C64Target.isInRegularRAM(target, program)) + assertFalse(C64Target.isInRegularRAM(target)) } private fun createTestProgramForMemoryRefViaVar(program: Program, address: Int, vartype: VarDeclType): AssignTarget { @@ -93,7 +90,7 @@ class TestMemory { val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) - module.linkParents(program.namespace) + module.linkIntoProgram(program) return target } @@ -101,8 +98,7 @@ class TestMemory { fun testInValidRamC64_memory_expression() { val memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY) val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - val program = Program("test", DummyFunctions, DummyMemsizer) - assertFalse(C64Target.isInRegularRAM(target, program)) + assertFalse(C64Target.isInRegularRAM(target)) } @Test @@ -114,8 +110,8 @@ class TestMemory { val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) val program = Program("test", DummyFunctions, DummyMemsizer) .addModule(module) - module.linkParents(program.namespace) - assertTrue(C64Target.isInRegularRAM(target, program)) + module.linkIntoProgram(program) + assertTrue(C64Target.isInRegularRAM(target)) } @Test @@ -128,8 +124,8 @@ class TestMemory { val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) val program = Program("test", DummyFunctions, DummyMemsizer) .addModule(module) - module.linkParents(program.namespace) - assertTrue(C64Target.isInRegularRAM(target, program)) + module.linkIntoProgram(program) + assertTrue(C64Target.isInRegularRAM(target)) } @Test @@ -142,8 +138,8 @@ class TestMemory { val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) val program = Program("test", DummyFunctions, DummyMemsizer) .addModule(module) - module.linkParents(program.namespace) - assertFalse(C64Target.isInRegularRAM(target, program)) + module.linkIntoProgram(program) + assertFalse(C64Target.isInRegularRAM(target)) } @Test @@ -156,8 +152,8 @@ class TestMemory { val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) val program = Program("test", DummyFunctions, DummyMemsizer) .addModule(module) - module.linkParents(program.namespace) - assertTrue(C64Target.isInRegularRAM(target, program)) + module.linkIntoProgram(program) + assertTrue(C64Target.isInRegularRAM(target)) } @Test @@ -171,8 +167,8 @@ class TestMemory { val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) val program = Program("test", DummyFunctions, DummyMemsizer) .addModule(module) - module.linkParents(program.namespace) - assertTrue(C64Target.isInRegularRAM(target, program)) + module.linkIntoProgram(program) + assertTrue(C64Target.isInRegularRAM(target)) } @Test @@ -186,7 +182,7 @@ class TestMemory { val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) val program = Program("test", DummyFunctions, DummyMemsizer) .addModule(module) - module.linkParents(program.namespace) - assertFalse(C64Target.isInRegularRAM(target, program)) + module.linkIntoProgram(program) + assertFalse(C64Target.isInRegularRAM(target)) } } diff --git a/compilerAst/src/prog8/ast/AstToplevel.kt b/compilerAst/src/prog8/ast/AstToplevel.kt index 0d06205d4..f73abdc74 100644 --- a/compilerAst/src/prog8/ast/AstToplevel.kt +++ b/compilerAst/src/prog8/ast/AstToplevel.kt @@ -371,6 +371,12 @@ open class Module(final override var statements: MutableList, statements.forEach {it.linkParents(this)} } + fun linkIntoProgram(program: Program) { + this.program = program + linkParents(program.namespace) + // TODO do this in program.addModule() ? + } + override val definingScope: INameScope get() = program.namespace override fun replaceChildNode(node: Node, replacement: Node) { From 804bb06859804d98d0386daf062accc8c771b30c Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 19 Oct 2021 22:36:05 +0200 Subject: [PATCH 8/9] clarified isInRegularRAM() by making it an extension method on AssignTarget --- .../compiler/BeforeAsmGenerationAstChanger.kt | 3 +- .../compiler/astprocessing/AstExtensions.kt | 52 +++++++++++++++++-- .../compiler/target/ICompilationTarget.kt | 45 ---------------- .../src/prog8/optimizer/BinExprSplitter.kt | 3 +- .../src/prog8/optimizer/UnusedCodeRemover.kt | 3 +- compiler/test/TestMemory.kt | 43 +++++++-------- 6 files changed, 76 insertions(+), 73 deletions(-) diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index a9894b9ff..5761b5591 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -9,6 +9,7 @@ import prog8.ast.statements.* import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstVisitor +import prog8.compiler.astprocessing.isInRegularRAMof import prog8.compiler.target.ICompilationTarget @@ -39,7 +40,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I // But it can only be done if the target variable IS NOT OCCURRING AS AN OPERAND ITSELF. if(!assignment.isAugmentable && assignment.target.identifier != null - && compTarget.isInRegularRAM(assignment.target)) { + && assignment.target.isInRegularRAMof(compTarget.machine)) { val binExpr = assignment.value as? BinaryExpression if (binExpr != null && binExpr.operator !in comparisonOperators) { if (binExpr.left !is BinaryExpression) { diff --git a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt index fcff410d1..e7fabca4a 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt @@ -3,10 +3,9 @@ package prog8.compiler.astprocessing import prog8.ast.Node import prog8.ast.Program import prog8.ast.base.DataType -import prog8.ast.expressions.CharLiteral -import prog8.ast.expressions.NumericLiteralValue -import prog8.ast.expressions.RangeExpr -import prog8.ast.expressions.StringLiteralValue +import prog8.ast.base.VarDeclType +import prog8.ast.expressions.* +import prog8.ast.statements.AssignTarget import prog8.ast.statements.Directive import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification @@ -15,6 +14,7 @@ import prog8.compiler.CompilationOptions import prog8.compiler.IErrorReporter import prog8.compiler.IStringEncoding import prog8.compiler.target.ICompilationTarget +import prog8.compiler.target.IMachineDefinition import kotlin.math.abs @@ -165,3 +165,47 @@ internal fun Program.moveMainAndStartToFirst() { modules[0].statements.add(0, directive) } } + +internal fun AssignTarget.isInRegularRAMof(machine: IMachineDefinition): Boolean { + val memAddr = memoryAddress + val arrayIdx = arrayindexed + val ident = identifier + when { + memAddr != null -> { + return when (memAddr.addressExpression) { + is NumericLiteralValue -> { + machine.isRegularRAMaddress((memAddr.addressExpression as NumericLiteralValue).number.toInt()) + } + is IdentifierReference -> { + val program = definingModule.program + val decl = (memAddr.addressExpression as IdentifierReference).targetVarDecl(program) + if ((decl?.type == VarDeclType.VAR || decl?.type == VarDeclType.CONST) && decl.value is NumericLiteralValue) + machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt()) + else + false + } + else -> false + } + } + arrayIdx != null -> { + val program = definingModule.program + val targetStmt = arrayIdx.arrayvar.targetVarDecl(program) + return if (targetStmt?.type == VarDeclType.MEMORY) { + val addr = targetStmt.value as? NumericLiteralValue + if (addr != null) + machine.isRegularRAMaddress(addr.number.toInt()) + else + false + } else true + } + ident != null -> { + val program = definingModule.program + val decl = ident.targetVarDecl(program)!! + return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteralValue) + machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt()) + else + true + } + else -> return true + } +} diff --git a/compiler/src/prog8/compiler/target/ICompilationTarget.kt b/compiler/src/prog8/compiler/target/ICompilationTarget.kt index 9522ed26f..4fea51cd9 100644 --- a/compiler/src/prog8/compiler/target/ICompilationTarget.kt +++ b/compiler/src/prog8/compiler/target/ICompilationTarget.kt @@ -24,51 +24,6 @@ interface ICompilationTarget: IStringEncoding, IMemSizer { val machine: IMachineDefinition override fun encodeString(str: String, altEncoding: Boolean): List override fun decodeString(bytes: List, altEncoding: Boolean): String - - // TODO: rename param target, and also AST node AssignTarget - *different meaning of "target"!* - fun isInRegularRAM(target: AssignTarget): Boolean { - val memAddr = target.memoryAddress - val arrayIdx = target.arrayindexed - val ident = target.identifier - when { - memAddr != null -> { - return when (memAddr.addressExpression) { - is NumericLiteralValue -> { - machine.isRegularRAMaddress((memAddr.addressExpression as NumericLiteralValue).number.toInt()) - } - is IdentifierReference -> { - val program = target.definingModule.program - val decl = (memAddr.addressExpression as IdentifierReference).targetVarDecl(program) - if ((decl?.type == VarDeclType.VAR || decl?.type == VarDeclType.CONST) && decl.value is NumericLiteralValue) - machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt()) - else - false - } - else -> false - } - } - arrayIdx != null -> { - val program = target.definingModule.program - val targetStmt = arrayIdx.arrayvar.targetVarDecl(program) - return if (targetStmt?.type == VarDeclType.MEMORY) { - val addr = targetStmt.value as? NumericLiteralValue - if (addr != null) - machine.isRegularRAMaddress(addr.number.toInt()) - else - false - } else true - } - ident != null -> { - val program = target.definingModule.program - val decl = ident.targetVarDecl(program)!! - return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteralValue) - machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt()) - else - true - } - else -> return true - } - } } diff --git a/compiler/src/prog8/optimizer/BinExprSplitter.kt b/compiler/src/prog8/optimizer/BinExprSplitter.kt index fd18ac561..4eada5f08 100644 --- a/compiler/src/prog8/optimizer/BinExprSplitter.kt +++ b/compiler/src/prog8/optimizer/BinExprSplitter.kt @@ -9,6 +9,7 @@ import prog8.ast.statements.AssignTarget import prog8.ast.statements.Assignment import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification +import prog8.compiler.astprocessing.isInRegularRAMof import prog8.compiler.target.ICompilationTarget @@ -79,7 +80,7 @@ X = BinExpr X = LeftExpr private fun isSimpleTarget(target: AssignTarget) = if (target.identifier!=null || target.memoryAddress!=null) - compTarget.isInRegularRAM(target) + target.isInRegularRAMof(compTarget.machine) else false diff --git a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt index 7f9dd7ce2..1e362606e 100644 --- a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt +++ b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt @@ -10,6 +10,7 @@ import prog8.ast.statements.* import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.compiler.IErrorReporter +import prog8.compiler.astprocessing.isInRegularRAMof import prog8.compiler.target.ICompilationTarget @@ -122,7 +123,7 @@ internal class UnusedCodeRemover(private val program: Program, val assign1 = stmtPairs[0] as? Assignment val assign2 = stmtPairs[1] as? Assignment if (assign1 != null && assign2 != null && !assign2.isAugmentable) { - if (assign1.target.isSameAs(assign2.target, program) && compTarget.isInRegularRAM(assign1.target)) { + if (assign1.target.isSameAs(assign2.target, program) && assign1.target.isInRegularRAMof(compTarget.machine)) { if(assign2.target.identifier==null || !assign2.value.referencesIdentifier(*(assign2.target.identifier!!.nameInSource.toTypedArray()))) // only remove the second assignment if its value is a simple expression! when(assign2.value) { diff --git a/compiler/test/TestMemory.kt b/compiler/test/TestMemory.kt index 3310dd911..6906e63f8 100644 --- a/compiler/test/TestMemory.kt +++ b/compiler/test/TestMemory.kt @@ -12,6 +12,7 @@ import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.NumericLiteralValue import prog8.ast.expressions.PrefixExpression import prog8.ast.statements.* +import prog8.compiler.astprocessing.isInRegularRAMof import prog8.compiler.target.C64Target import prog8.parser.SourceCode import prog8tests.helpers.DummyFunctions @@ -28,23 +29,23 @@ class TestMemory { var memexpr = NumericLiteralValue.optimalInteger(0x0000, Position.DUMMY) var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertTrue(C64Target.isInRegularRAM(target)) + assertTrue(target.isInRegularRAMof(C64Target.machine)) memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertTrue(C64Target.isInRegularRAM(target)) + assertTrue(target.isInRegularRAMof(C64Target.machine)) memexpr = NumericLiteralValue.optimalInteger(0x9fff, Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertTrue(C64Target.isInRegularRAM(target)) + assertTrue(target.isInRegularRAMof(C64Target.machine)) memexpr = NumericLiteralValue.optimalInteger(0xc000, Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertTrue(C64Target.isInRegularRAM(target)) + assertTrue(target.isInRegularRAMof(C64Target.machine)) memexpr = NumericLiteralValue.optimalInteger(0xcfff, Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertTrue(C64Target.isInRegularRAM(target)) + assertTrue(target.isInRegularRAMof(C64Target.machine)) } @Test @@ -52,19 +53,19 @@ class TestMemory { var memexpr = NumericLiteralValue.optimalInteger(0xa000, Position.DUMMY) var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertFalse(C64Target.isInRegularRAM(target)) + assertFalse(target.isInRegularRAMof(C64Target.machine)) memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertFalse(C64Target.isInRegularRAM(target)) + assertFalse(target.isInRegularRAMof(C64Target.machine)) memexpr = NumericLiteralValue.optimalInteger(0xd000, Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertFalse(C64Target.isInRegularRAM(target)) + assertFalse(target.isInRegularRAMof(C64Target.machine)) memexpr = NumericLiteralValue.optimalInteger(0xffff, Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertFalse(C64Target.isInRegularRAM(target)) + assertFalse(target.isInRegularRAMof(C64Target.machine)) } @Test @@ -72,15 +73,15 @@ class TestMemory { val program = Program("test", DummyFunctions, DummyMemsizer) var target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.VAR) - assertTrue(C64Target.isInRegularRAM(target)) + assertTrue(target.isInRegularRAMof(C64Target.machine)) target = createTestProgramForMemoryRefViaVar(program, 0xd020, VarDeclType.VAR) - assertFalse(C64Target.isInRegularRAM(target)) + assertFalse(target.isInRegularRAMof(C64Target.machine)) target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.CONST) - assertTrue(C64Target.isInRegularRAM(target)) + assertTrue(target.isInRegularRAMof(C64Target.machine)) target = createTestProgramForMemoryRefViaVar(program, 0xd020, VarDeclType.CONST) - assertFalse(C64Target.isInRegularRAM(target)) + assertFalse(target.isInRegularRAMof(C64Target.machine)) target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.MEMORY) - assertFalse(C64Target.isInRegularRAM(target)) + assertFalse(target.isInRegularRAMof(C64Target.machine)) } private fun createTestProgramForMemoryRefViaVar(program: Program, address: Int, vartype: VarDeclType): AssignTarget { @@ -98,7 +99,7 @@ class TestMemory { fun testInValidRamC64_memory_expression() { val memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY) val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) - assertFalse(C64Target.isInRegularRAM(target)) + assertFalse(target.isInRegularRAMof(C64Target.machine)) } @Test @@ -111,7 +112,7 @@ class TestMemory { val program = Program("test", DummyFunctions, DummyMemsizer) .addModule(module) module.linkIntoProgram(program) - assertTrue(C64Target.isInRegularRAM(target)) + assertTrue(target.isInRegularRAMof(C64Target.machine)) } @Test @@ -125,7 +126,7 @@ class TestMemory { val program = Program("test", DummyFunctions, DummyMemsizer) .addModule(module) module.linkIntoProgram(program) - assertTrue(C64Target.isInRegularRAM(target)) + assertTrue(target.isInRegularRAMof(C64Target.machine)) } @Test @@ -139,7 +140,7 @@ class TestMemory { val program = Program("test", DummyFunctions, DummyMemsizer) .addModule(module) module.linkIntoProgram(program) - assertFalse(C64Target.isInRegularRAM(target)) + assertFalse(target.isInRegularRAMof(C64Target.machine)) } @Test @@ -153,7 +154,7 @@ class TestMemory { val program = Program("test", DummyFunctions, DummyMemsizer) .addModule(module) module.linkIntoProgram(program) - assertTrue(C64Target.isInRegularRAM(target)) + assertTrue(target.isInRegularRAMof(C64Target.machine)) } @Test @@ -168,7 +169,7 @@ class TestMemory { val program = Program("test", DummyFunctions, DummyMemsizer) .addModule(module) module.linkIntoProgram(program) - assertTrue(C64Target.isInRegularRAM(target)) + assertTrue(target.isInRegularRAMof(C64Target.machine)) } @Test @@ -183,6 +184,6 @@ class TestMemory { val program = Program("test", DummyFunctions, DummyMemsizer) .addModule(module) module.linkIntoProgram(program) - assertFalse(C64Target.isInRegularRAM(target)) + assertFalse(target.isInRegularRAMof(C64Target.machine)) } } From 82d20dea396354ae05dbf57afa71bd453ccaf3a9 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 19 Oct 2021 23:20:34 +0200 Subject: [PATCH 9/9] a few comment and TODO cleanups. remove remark about chars UBYTE type, kotlin's closest native type that can contain 0-255 is a short. --- compiler/src/prog8/compiler/ModuleImporter.kt | 4 ++-- compiler/test/ModuleImporterTests.kt | 2 +- compiler/test/TestCompilerOnCharLit.kt | 8 ++++---- compilerAst/src/prog8/ast/AstToplevel.kt | 6 ++---- compilerAst/src/prog8/parser/Prog8Parser.kt | 3 --- examples/cx16/kefrenbars.p8 | 2 +- 6 files changed, 10 insertions(+), 15 deletions(-) diff --git a/compiler/src/prog8/compiler/ModuleImporter.kt b/compiler/src/prog8/compiler/ModuleImporter.kt index a62e9f699..b6b270dbb 100644 --- a/compiler/src/prog8/compiler/ModuleImporter.kt +++ b/compiler/src/prog8/compiler/ModuleImporter.kt @@ -35,7 +35,7 @@ class ModuleImporter(private val program: Program, file = filePath.normalize().toFile(), reason = "searched in $searchIn")) 1 -> candidates.first() - else -> candidates.first() // TODO: report error if more than 1 candidate? + else -> candidates.first() // when more candiates, pick the one from the first location } val logMsg = "importing '${filePath.nameWithoutExtension}' (from file $srcPath)" @@ -142,7 +142,7 @@ class ModuleImporter(private val program: Program, } else { val dropCurDir = if(sourcePaths.isNotEmpty() && sourcePaths[0].name == ".") 1 else 0 sourcePaths.drop(dropCurDir) + - // TODO: won't work until Prog8Parser is fixed s.t. it fully initializes the modules it returns. ??? (what won't work?) + // TODO: won't work until Prog8Parser is fixed s.t. it fully initializes the modules it returns. // hm, what won't work?) listOf(Path(importingModule.position.file).parent ?: Path("")) + listOf(Path(".", "prog8lib")) } diff --git a/compiler/test/ModuleImporterTests.kt b/compiler/test/ModuleImporterTests.kt index 064f578b9..ecc335f68 100644 --- a/compiler/test/ModuleImporterTests.kt +++ b/compiler/test/ModuleImporterTests.kt @@ -259,7 +259,7 @@ class TestModuleImporter { val result2 = importer.importLibraryModule(filenameWithExt) assertThat(count[n] + " call / with .p8 extension", result2, Is(nullValue())) assertFalse(importer.errors.noErrors(), count[n] + " call / with .p8 extension") - assertEquals(errors.errors.single(), "no module found with name i_do_not_exist.p8") // TODO don't add a p8 extension in the import logic... + assertEquals(errors.errors.single(), "no module found with name i_do_not_exist.p8") errors.report() assertThat(program.modules.size, equalTo(1)) } diff --git a/compiler/test/TestCompilerOnCharLit.kt b/compiler/test/TestCompilerOnCharLit.kt index f68075e40..583d9983a 100644 --- a/compiler/test/TestCompilerOnCharLit.kt +++ b/compiler/test/TestCompilerOnCharLit.kt @@ -42,7 +42,7 @@ class TestCompilerOnCharLit { "char literal should have been replaced by ubyte literal") val arg = funCall.args[0] as NumericLiteralValue assertEquals(DataType.UBYTE, arg.type) - assertEquals(platform.encodeString("\n", false)[0], arg.number.toShort()) // TODO: short/int/UBYTE - which should it be? + assertEquals(platform.encodeString("\n", false)[0], arg.number.toShort()) } @Test @@ -77,7 +77,7 @@ class TestCompilerOnCharLit { "char literal should have been replaced by ubyte literal") val initializerValue = decl.value as NumericLiteralValue assertEquals(DataType.UBYTE, initializerValue.type) - assertEquals(platform.encodeString("\n", false)[0], initializerValue.number.toShort()) // TODO: short/int/UBYTE - which should it be? + assertEquals(platform.encodeString("\n", false)[0], initializerValue.number.toShort()) } @Test @@ -105,12 +105,12 @@ class TestCompilerOnCharLit { assertEquals(DataType.UBYTE, decl.datatype) assertEquals( platform.encodeString("\n", false)[0], - (decl.value as NumericLiteralValue).number.toShort()) // TODO: short/int/UBYTE - which should it be? + (decl.value as NumericLiteralValue).number.toShort()) } is NumericLiteralValue -> { assertEquals( platform.encodeString("\n", false)[0], - arg.number.toShort()) // TODO: short/int/UBYTE - which should it be? + arg.number.toShort()) } else -> assertIs(funCall.args[0]) // make test fail } diff --git a/compilerAst/src/prog8/ast/AstToplevel.kt b/compilerAst/src/prog8/ast/AstToplevel.kt index f73abdc74..9144ab75a 100644 --- a/compilerAst/src/prog8/ast/AstToplevel.kt +++ b/compilerAst/src/prog8/ast/AstToplevel.kt @@ -259,8 +259,7 @@ class Program(val name: String, require(null == _modules.firstOrNull { it.name == module.name }) { "module '${module.name}' already present" } _modules.add(module) - module.linkParents(namespace) - module.program = this + module.linkIntoProgram(this) return this } @@ -344,7 +343,7 @@ class Program(val name: String, require(node is Module && replacement is Module) val idx = _modules.indexOfFirst { it===node } _modules[idx] = replacement - replacement.parent = this // TODO: why not replacement.program = this; replacement.linkParents(namespace)?! + replacement.linkIntoProgram(this) } } @@ -374,7 +373,6 @@ open class Module(final override var statements: MutableList, fun linkIntoProgram(program: Program) { this.program = program linkParents(program.namespace) - // TODO do this in program.addModule() ? } override val definingScope: INameScope diff --git a/compilerAst/src/prog8/parser/Prog8Parser.kt b/compilerAst/src/prog8/parser/Prog8Parser.kt index 8e8d60002..9801d6593 100644 --- a/compilerAst/src/prog8/parser/Prog8Parser.kt +++ b/compilerAst/src/prog8/parser/Prog8Parser.kt @@ -31,12 +31,9 @@ object Prog8Parser { parser.addErrorListener(antlrErrorListener) val parseTree = parser.module() - val module = ParsedModule(src) - // .linkParents called in ParsedModule.add parseTree.directive().forEach { module.add(it.toAst()) } - // TODO: remove Encoding parseTree.block().forEach { module.add(it.toAst(module.isLibrary)) } return module diff --git a/examples/cx16/kefrenbars.p8 b/examples/cx16/kefrenbars.p8 index 0b7b31306..7b7cafd41 100644 --- a/examples/cx16/kefrenbars.p8 +++ b/examples/cx16/kefrenbars.p8 @@ -22,7 +22,7 @@ main { ; Not yet implemented in ROM: cx16.FB_set_palette(&colors, 0, len(colors)*3) palette.set_rgb(&colors, len(colors)) - cx16.screen_set_mode(128) ; low-res bitmap 256 colors + void cx16.screen_set_mode(128) ; low-res bitmap 256 colors cx16.FB_init() cx16.VERA_DC_VSCALE = 0 ; display trick spoiler.......: stretch display all the way to the bottom cx16.set_rasterirq(&irq.irqhandler, 0)