Merge branch 'v7.1'

This commit is contained in:
Irmen de Jong 2021-10-19 23:21:44 +02:00
commit f0cff661df
24 changed files with 243 additions and 183 deletions

View File

@ -9,6 +9,7 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.compiler.astprocessing.isInRegularRAMof
import prog8.compiler.target.ICompilationTarget 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. // But it can only be done if the target variable IS NOT OCCURRING AS AN OPERAND ITSELF.
if(!assignment.isAugmentable if(!assignment.isAugmentable
&& assignment.target.identifier != null && assignment.target.identifier != null
&& compTarget.isInRegularRAM(assignment.target, program)) { && assignment.target.isInRegularRAMof(compTarget.machine)) {
val binExpr = assignment.value as? BinaryExpression val binExpr = assignment.value as? BinaryExpression
if (binExpr != null && binExpr.operator !in comparisonOperators) { if (binExpr != null && binExpr.operator !in comparisonOperators) {
if (binExpr.left !is BinaryExpression) { if (binExpr.left !is BinaryExpression) {

View File

@ -215,12 +215,12 @@ fun parseImports(filepath: Path,
} }
fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions { fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions {
val mainModule = program.mainModule val toplevelModule = program.toplevelModule
val outputDirective = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" } as? Directive) val outputDirective = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%output" } as? Directive)
val launcherDirective = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" } as? Directive) val launcherDirective = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" } as? Directive)
val outputTypeStr = outputDirective?.args?.single()?.name?.uppercase() val outputTypeStr = outputDirective?.args?.single()?.name?.uppercase()
val launcherTypeStr = launcherDirective?.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() as? Directive)?.args?.single()?.name?.uppercase()
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" } val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }
.flatMap { (it as Directive).args }.toSet() .flatMap { (it as Directive).args }.toSet()
@ -242,7 +242,7 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
zpType = ZeropageType.BASICSAFE zpType = ZeropageType.BASICSAFE
} }
val zpReserved = mainModule.statements val zpReserved = toplevelModule.statements
.asSequence() .asSequence()
.filter { it is Directive && it.directive == "%zpreserved" } .filter { it is Directive && it.directive == "%zpreserved" }
.map { (it as Directive).args } .map { (it as Directive).args }
@ -280,6 +280,8 @@ private fun processAst(programAst: Program, errors: IErrorReporter, compilerOpti
programAst.checkIdentifiers(errors, compilerOptions) programAst.checkIdentifiers(errors, compilerOptions)
errors.report() 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 // 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) programAst.charLiteralsToUByteLiterals(errors, compilerOptions.compTarget)
errors.report() errors.report()
programAst.constantFold(errors, compilerOptions.compTarget) programAst.constantFold(errors, compilerOptions.compTarget)

View File

@ -35,13 +35,17 @@ class ModuleImporter(private val program: Program,
file = filePath.normalize().toFile(), file = filePath.normalize().toFile(),
reason = "searched in $searchIn")) reason = "searched in $searchIn"))
1 -> candidates.first() 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)" val logMsg = "importing '${filePath.nameWithoutExtension}' (from file $srcPath)"
println(logMsg) 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? { fun importLibraryModule(name: String): Module? {
@ -51,20 +55,24 @@ class ModuleImporter(private val program: Program,
return executeImportDirective(import, null) return executeImportDirective(import, null)
} }
//private fun importModule(stream: CharStream, modulePath: Path, isLibrary: Boolean): Module { private fun importModule(src: SourceCode) : Module? {
private fun importModule(src: SourceCode) : Module {
val moduleAst = Prog8Parser.parseModule(src) val moduleAst = Prog8Parser.parseModule(src)
program.addModule(moduleAst) program.addModule(moduleAst)
// accept additional imports // accept additional imports
val lines = moduleAst.statements.toMutableList() try {
lines.asSequence() val lines = moduleAst.statements.toMutableList()
lines.asSequence()
.mapIndexed { i, it -> i to it } .mapIndexed { i, it -> i to it }
.filter { (it.second as? Directive)?.directive == "%import" } .filter { (it.second as? Directive)?.directive == "%import" }
.forEach { executeImportDirective(it.second as Directive, moduleAst) } .forEach { executeImportDirective(it.second as Directive, moduleAst) }
moduleAst.statements = lines
moduleAst.statements = lines return moduleAst
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? { private fun executeImportDirective(import: Directive, importingModule: Module?): Module? {
@ -104,7 +112,8 @@ class ModuleImporter(private val program: Program,
} }
) )
removeDirectivesFromImportedModule(importedModule) if(importedModule!=null)
removeDirectivesFromImportedModule(importedModule)
return importedModule return importedModule
} }
@ -133,7 +142,7 @@ class ModuleImporter(private val program: Program,
} else { } else {
val dropCurDir = if(sourcePaths.isNotEmpty() && sourcePaths[0].name == ".") 1 else 0 val dropCurDir = if(sourcePaths.isNotEmpty() && sourcePaths[0].name == ".") 1 else 0
sourcePaths.drop(dropCurDir) + 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. // hm, what won't work?)
listOf(Path(importingModule.position.file).parent ?: Path("")) + listOf(Path(importingModule.position.file).parent ?: Path("")) +
listOf(Path(".", "prog8lib")) listOf(Path(".", "prog8lib"))
} }

View File

@ -13,7 +13,6 @@ import prog8.compiler.ZeropageType
import prog8.compiler.functions.BuiltinFunctions import prog8.compiler.functions.BuiltinFunctions
import prog8.compiler.functions.builtinFunctionReturnType import prog8.compiler.functions.builtinFunctionReturnType
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
import prog8.parser.SourceCode
import java.io.CharConversionException import java.io.CharConversionException
import java.io.File import java.io.File
import java.util.* import java.util.*
@ -46,7 +45,7 @@ internal class AstChecker(private val program: Program,
if(compilerOptions.floats) { if(compilerOptions.floats) {
if (compilerOptions.zeropage !in arrayOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE )) 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) super.visit(program)

View File

@ -3,11 +3,9 @@ package prog8.compiler.astprocessing
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.base.FatalAstException import prog8.ast.base.VarDeclType
import prog8.ast.expressions.CharLiteral import prog8.ast.expressions.*
import prog8.ast.expressions.NumericLiteralValue import prog8.ast.statements.AssignTarget
import prog8.ast.expressions.RangeExpr
import prog8.ast.expressions.StringLiteralValue
import prog8.ast.statements.Directive import prog8.ast.statements.Directive
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
@ -16,6 +14,7 @@ import prog8.compiler.CompilationOptions
import prog8.compiler.IErrorReporter import prog8.compiler.IErrorReporter
import prog8.compiler.IStringEncoding import prog8.compiler.IStringEncoding
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
import prog8.compiler.target.IMachineDefinition
import kotlin.math.abs import kotlin.math.abs
@ -127,26 +126,6 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
lit2decl.visit(this) lit2decl.visit(this)
lit2decl.applyModifications() 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<String, MutableList<prog8.ast.Module>>().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) { internal fun Program.variousCleanups(program: Program, errors: IErrorReporter) {
@ -186,3 +165,47 @@ internal fun Program.moveMainAndStartToFirst() {
modules[0].statements.add(0, directive) 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
}
}

View File

@ -24,49 +24,6 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
val machine: IMachineDefinition val machine: IMachineDefinition
override fun encodeString(str: String, altEncoding: Boolean): List<Short> override fun encodeString(str: String, altEncoding: Boolean): List<Short>
override fun decodeString(bytes: List<Short>, altEncoding: Boolean): String override fun decodeString(bytes: List<Short>, 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 {
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 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 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 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
}
}
} }

View File

@ -9,6 +9,7 @@ import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment import prog8.ast.statements.Assignment
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.compiler.astprocessing.isInRegularRAMof
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
@ -54,7 +55,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) if(assignment.target isSameAs binExpr.left || assignment.target isSameAs binExpr.right)
return noModifications return noModifications
@ -77,9 +78,9 @@ X = BinExpr X = LeftExpr
return noModifications return noModifications
} }
private fun isSimpleTarget(target: AssignTarget, program: Program) = private fun isSimpleTarget(target: AssignTarget) =
if (target.identifier!=null || target.memoryAddress!=null) if (target.identifier!=null || target.memoryAddress!=null)
compTarget.isInRegularRAM(target, program) target.isInRegularRAMof(compTarget.machine)
else else
false false

View File

@ -10,6 +10,7 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.compiler.IErrorReporter import prog8.compiler.IErrorReporter
import prog8.compiler.astprocessing.isInRegularRAMof
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
@ -122,7 +123,7 @@ internal class UnusedCodeRemover(private val program: Program,
val assign1 = stmtPairs[0] as? Assignment val assign1 = stmtPairs[0] as? Assignment
val assign2 = stmtPairs[1] as? Assignment val assign2 = stmtPairs[1] as? Assignment
if (assign1 != null && assign2 != null && !assign2.isAugmentable) { 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) && assign1.target.isInRegularRAMof(compTarget.machine)) {
if(assign2.target.identifier==null || !assign2.value.referencesIdentifier(*(assign2.target.identifier!!.nameInSource.toTypedArray()))) 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! // only remove the second assignment if its value is a simple expression!
when(assign2.value) { when(assign2.value) {

View File

@ -71,7 +71,7 @@ locallabel:
val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test")) val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer) val program = Program("test", DummyFunctions, DummyMemsizer)
.addModule(module) .addModule(module)
module.linkParents(program.namespace) module.linkIntoProgram(program)
return program return program
} }

View File

@ -12,6 +12,7 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Disabled
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.internedStringsModuleName
import prog8.compiler.IErrorReporter import prog8.compiler.IErrorReporter
import prog8.compiler.ModuleImporter import prog8.compiler.ModuleImporter
import prog8.parser.ParseError import prog8.parser.ParseError
@ -207,7 +208,6 @@ class TestModuleImporter {
} }
@Test @Test
@Disabled("TODO: module that imports faulty module should not be kept in Program.modules")
fun testImportingFileWithSyntaxError_twice() { fun testImportingFileWithSyntaxError_twice() {
doTestImportingFileWithSyntaxError(2) doTestImportingFileWithSyntaxError(2)
} }
@ -227,7 +227,8 @@ class TestModuleImporter {
assertThat("startCol; should be 0-based", it.position.startCol, equalTo(6)) assertThat("startCol; should be 0-based", it.position.startCol, equalTo(6))
assertThat("endCol; should be 0-based", it.position.endCol, 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))
} }
} }
} }
@ -258,7 +259,7 @@ class TestModuleImporter {
val result2 = importer.importLibraryModule(filenameWithExt) val result2 = importer.importLibraryModule(filenameWithExt)
assertThat(count[n] + " call / with .p8 extension", result2, Is(nullValue())) assertThat(count[n] + " call / with .p8 extension", result2, Is(nullValue()))
assertFalse(importer.errors.noErrors(), count[n] + " call / with .p8 extension") 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() errors.report()
assertThat(program.modules.size, equalTo(1)) assertThat(program.modules.size, equalTo(1))
} }
@ -303,7 +304,8 @@ class TestModuleImporter {
assertThat("startCol; should be 0-based", it.position.startCol, equalTo(6)) assertThat("startCol; should be 0-based", it.position.startCol, equalTo(6))
assertThat("endCol; should be 0-based", it.position.endCol, 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() importer.errors.report()
} }
} }
@ -314,7 +316,6 @@ class TestModuleImporter {
} }
@Test @Test
@Disabled("TODO: module that imports faulty module should not be kept in Program.modules")
fun testImportingFileWithSyntaxError_twice() { fun testImportingFileWithSyntaxError_twice() {
doTestImportingFileWithSyntaxError(2) doTestImportingFileWithSyntaxError(2)
} }

View File

@ -42,7 +42,7 @@ class TestCompilerOnCharLit {
"char literal should have been replaced by ubyte literal") "char literal should have been replaced by ubyte literal")
val arg = funCall.args[0] as NumericLiteralValue val arg = funCall.args[0] as NumericLiteralValue
assertEquals(DataType.UBYTE, arg.type) 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 @Test
@ -77,7 +77,7 @@ class TestCompilerOnCharLit {
"char literal should have been replaced by ubyte literal") "char literal should have been replaced by ubyte literal")
val initializerValue = decl.value as NumericLiteralValue val initializerValue = decl.value as NumericLiteralValue
assertEquals(DataType.UBYTE, initializerValue.type) 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 @Test
@ -105,12 +105,12 @@ class TestCompilerOnCharLit {
assertEquals(DataType.UBYTE, decl.datatype) assertEquals(DataType.UBYTE, decl.datatype)
assertEquals( assertEquals(
platform.encodeString("\n", false)[0], 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 -> { is NumericLiteralValue -> {
assertEquals( assertEquals(
platform.encodeString("\n", false)[0], platform.encodeString("\n", false)[0],
arg.number.toShort()) // TODO: short/int/UBYTE - which should it be? arg.number.toShort())
} }
else -> assertIs<IdentifierReference>(funCall.args[0]) // make test fail else -> assertIs<IdentifierReference>(funCall.args[0]) // make test fail
} }

View File

@ -19,7 +19,7 @@ import kotlin.io.path.exists
* They are not really unit tests, but rather tests of the whole process, * They are not really unit tests, but rather tests of the whole process,
* from source file loading all the way through to running 64tass. * 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) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestCompilerOnExamples { class TestCompilerOnExamples {
private val examplesDir = assumeDirectory(workingDir, "../examples") private val examplesDir = assumeDirectory(workingDir, "../examples")
@ -49,7 +49,7 @@ class TestCompilerOnExamples {
} }
@TestFactory @TestFactory
// @Disabled // @Disabled("disable to save some time")
fun bothCx16AndC64() = mapCombinations( fun bothCx16AndC64() = mapCombinations(
dim1 = listOf( dim1 = listOf(
"animals", "animals",
@ -80,7 +80,7 @@ class TestCompilerOnExamples {
) )
@TestFactory @TestFactory
// @Disabled // @Disabled("disable to save some time")
fun onlyC64() = mapCombinations( fun onlyC64() = mapCombinations(
dim1 = listOf( dim1 = listOf(
"balloonflight", "balloonflight",
@ -99,7 +99,7 @@ class TestCompilerOnExamples {
) )
@TestFactory @TestFactory
// @Disabled // @Disabled("disable to save some time")
fun onlyCx16() = mapCombinations( fun onlyCx16() = mapCombinations(
dim1 = listOf( dim1 = listOf(
"vtui/testvtui", "vtui/testvtui",

View File

@ -19,7 +19,7 @@ import kotlin.test.assertTrue
class TestImportedModulesOrderAndOptions { class TestImportedModulesOrderAndOptions {
@Test @Test
fun testImportedModuleOrderCorrect() { fun testImportedModuleOrderAndMainModuleCorrect() {
val result = compileText(C64Target, false, """ val result = compileText(C64Target, false, """
%import textio %import textio
%import floats %import floats
@ -30,7 +30,7 @@ main {
} }
} }
""").assertSuccess() """).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 } val moduleNames = result.programAst.modules.map { it.name }
assertTrue(moduleNames[0].startsWith("on_the_fly_test"), "main module must be first") assertTrue(moduleNames[0].startsWith("on_the_fly_test"), "main module must be first")
@ -43,6 +43,8 @@ main {
"math", "math",
"prog8_lib" "prog8_lib"
), moduleNames.drop(1), "module order in parse tree") ), moduleNames.drop(1), "module order in parse tree")
assertTrue(result.programAst.toplevelModule.name.startsWith("on_the_fly_test"))
} }
@Test @Test
@ -59,7 +61,7 @@ main {
} }
} }
""").assertSuccess() """).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) val options = determineCompilationOptions(result.programAst, C64Target)
assertTrue(options.floats) assertTrue(options.floats)
assertEquals(ZeropageType.DONTUSE, options.zeropage) assertEquals(ZeropageType.DONTUSE, options.zeropage)
@ -86,7 +88,7 @@ main {
filepath.toFile().writeText(sourceText) filepath.toFile().writeText(sourceText)
val (program, options, importedfiles) = parseImports(filepath, errors, C64Target, emptyList()) 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(1, importedfiles.size, "all imports other than the test source must have been internal resources library files")
assertEquals(listOf( assertEquals(listOf(
internedStringsModuleName, internedStringsModuleName,

View File

@ -5,7 +5,6 @@ import org.junit.jupiter.api.TestInstance
import prog8.ast.Module import prog8.ast.Module
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.base.ParentSentinel
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.base.VarDeclType import prog8.ast.base.VarDeclType
import prog8.ast.expressions.ArrayIndexedExpression import prog8.ast.expressions.ArrayIndexedExpression
@ -13,6 +12,7 @@ import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.PrefixExpression import prog8.ast.expressions.PrefixExpression
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.compiler.astprocessing.isInRegularRAMof
import prog8.compiler.target.C64Target import prog8.compiler.target.C64Target
import prog8.parser.SourceCode import prog8.parser.SourceCode
import prog8tests.helpers.DummyFunctions import prog8tests.helpers.DummyFunctions
@ -29,24 +29,23 @@ class TestMemory {
var memexpr = NumericLiteralValue.optimalInteger(0x0000, Position.DUMMY) var memexpr = NumericLiteralValue.optimalInteger(0x0000, Position.DUMMY)
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val program = Program("test", DummyFunctions, DummyMemsizer) assertTrue(target.isInRegularRAMof(C64Target.machine))
assertTrue(C64Target.isInRegularRAM(target, program))
memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(target.isInRegularRAMof(C64Target.machine))
memexpr = NumericLiteralValue.optimalInteger(0x9fff, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0x9fff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(target.isInRegularRAMof(C64Target.machine))
memexpr = NumericLiteralValue.optimalInteger(0xc000, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0xc000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(target.isInRegularRAMof(C64Target.machine))
memexpr = NumericLiteralValue.optimalInteger(0xcfff, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0xcfff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(target.isInRegularRAMof(C64Target.machine))
} }
@Test @Test
@ -54,20 +53,19 @@ class TestMemory {
var memexpr = NumericLiteralValue.optimalInteger(0xa000, Position.DUMMY) var memexpr = NumericLiteralValue.optimalInteger(0xa000, Position.DUMMY)
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val program = Program("test", DummyFunctions, DummyMemsizer) assertFalse(target.isInRegularRAMof(C64Target.machine))
assertFalse(C64Target.isInRegularRAM(target, program))
memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assertFalse(C64Target.isInRegularRAM(target, program)) assertFalse(target.isInRegularRAMof(C64Target.machine))
memexpr = NumericLiteralValue.optimalInteger(0xd000, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0xd000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assertFalse(C64Target.isInRegularRAM(target, program)) assertFalse(target.isInRegularRAMof(C64Target.machine))
memexpr = NumericLiteralValue.optimalInteger(0xffff, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0xffff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assertFalse(C64Target.isInRegularRAM(target, program)) assertFalse(target.isInRegularRAMof(C64Target.machine))
} }
@Test @Test
@ -75,15 +73,15 @@ class TestMemory {
val program = Program("test", DummyFunctions, DummyMemsizer) val program = Program("test", DummyFunctions, DummyMemsizer)
var target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.VAR) var target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.VAR)
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(target.isInRegularRAMof(C64Target.machine))
target = createTestProgramForMemoryRefViaVar(program, 0xd020, VarDeclType.VAR) target = createTestProgramForMemoryRefViaVar(program, 0xd020, VarDeclType.VAR)
assertFalse(C64Target.isInRegularRAM(target, program)) assertFalse(target.isInRegularRAMof(C64Target.machine))
target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.CONST) target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.CONST)
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(target.isInRegularRAMof(C64Target.machine))
target = createTestProgramForMemoryRefViaVar(program, 0xd020, VarDeclType.CONST) target = createTestProgramForMemoryRefViaVar(program, 0xd020, VarDeclType.CONST)
assertFalse(C64Target.isInRegularRAM(target, program)) assertFalse(target.isInRegularRAMof(C64Target.machine))
target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.MEMORY) target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.MEMORY)
assertFalse(C64Target.isInRegularRAM(target, program)) assertFalse(target.isInRegularRAMof(C64Target.machine))
} }
private fun createTestProgramForMemoryRefViaVar(program: Program, address: Int, vartype: VarDeclType): AssignTarget { private fun createTestProgramForMemoryRefViaVar(program: Program, address: Int, vartype: VarDeclType): AssignTarget {
@ -93,7 +91,7 @@ class TestMemory {
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) 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 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")) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
module.linkParents(program.namespace) module.linkIntoProgram(program)
return target return target
} }
@ -101,8 +99,7 @@ class TestMemory {
fun testInValidRamC64_memory_expression() { fun testInValidRamC64_memory_expression() {
val memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY) val memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val program = Program("test", DummyFunctions, DummyMemsizer) assertFalse(target.isInRegularRAMof(C64Target.machine))
assertFalse(C64Target.isInRegularRAM(target, program))
} }
@Test @Test
@ -114,8 +111,8 @@ class TestMemory {
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer) val program = Program("test", DummyFunctions, DummyMemsizer)
.addModule(module) .addModule(module)
module.linkParents(program.namespace) module.linkIntoProgram(program)
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(target.isInRegularRAMof(C64Target.machine))
} }
@Test @Test
@ -128,8 +125,8 @@ class TestMemory {
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer) val program = Program("test", DummyFunctions, DummyMemsizer)
.addModule(module) .addModule(module)
module.linkParents(program.namespace) module.linkIntoProgram(program)
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(target.isInRegularRAMof(C64Target.machine))
} }
@Test @Test
@ -142,8 +139,8 @@ class TestMemory {
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer) val program = Program("test", DummyFunctions, DummyMemsizer)
.addModule(module) .addModule(module)
module.linkParents(program.namespace) module.linkIntoProgram(program)
assertFalse(C64Target.isInRegularRAM(target, program)) assertFalse(target.isInRegularRAMof(C64Target.machine))
} }
@Test @Test
@ -156,8 +153,8 @@ class TestMemory {
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer) val program = Program("test", DummyFunctions, DummyMemsizer)
.addModule(module) .addModule(module)
module.linkParents(program.namespace) module.linkIntoProgram(program)
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(target.isInRegularRAMof(C64Target.machine))
} }
@Test @Test
@ -171,8 +168,8 @@ class TestMemory {
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer) val program = Program("test", DummyFunctions, DummyMemsizer)
.addModule(module) .addModule(module)
module.linkParents(program.namespace) module.linkIntoProgram(program)
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(target.isInRegularRAMof(C64Target.machine))
} }
@Test @Test
@ -186,7 +183,7 @@ class TestMemory {
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer) val program = Program("test", DummyFunctions, DummyMemsizer)
.addModule(module) .addModule(module)
module.linkParents(program.namespace) module.linkIntoProgram(program)
assertFalse(C64Target.isInRegularRAM(target, program)) assertFalse(target.isInRegularRAMof(C64Target.machine))
} }
} }

View File

@ -2,7 +2,7 @@
main { main {
str myBar = "main.bar" str myBar = "main.bar"
;foo_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() { sub start() {
txt.print(myBar) txt.print(myBar)
txt.print(&foo_bar) txt.print(&foo_bar)

View File

@ -259,11 +259,12 @@ class Program(val name: String,
require(null == _modules.firstOrNull { it.name == module.name }) require(null == _modules.firstOrNull { it.name == module.name })
{ "module '${module.name}' already present" } { "module '${module.name}' already present" }
_modules.add(module) _modules.add(module)
module.linkParents(namespace) module.linkIntoProgram(this)
module.program = this
return this return this
} }
fun removeModule(module: Module) = _modules.remove(module)
fun moveModuleToFront(module: Module): Program { fun moveModuleToFront(module: Module): Program {
require(_modules.contains(module)) require(_modules.contains(module))
{ "Not a module of this program: '${module.name}'"} { "Not a module of this program: '${module.name}'"}
@ -285,11 +286,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 } get() = modules.first { it.name!=internedStringsModuleName }
val definedLoadAddress: Int val definedLoadAddress: Int
get() = mainModule.loadAddress get() = toplevelModule.loadAddress
var actualLoadAddress: Int = 0 var actualLoadAddress: Int = 0
private val internedStringsUnique = mutableMapOf<Pair<String, Boolean>, List<String>>() private val internedStringsUnique = mutableMapOf<Pair<String, Boolean>, List<String>>()
@ -342,9 +343,8 @@ class Program(val name: String,
require(node is Module && replacement is Module) require(node is Module && replacement is Module)
val idx = _modules.indexOfFirst { it===node } val idx = _modules.indexOfFirst { it===node }
_modules[idx] = replacement _modules[idx] = replacement
replacement.parent = this // TODO: why not replacement.program = this; replacement.linkParents(namespace)?! replacement.linkIntoProgram(this)
} }
} }
open class Module(final override var statements: MutableList<Statement>, open class Module(final override var statements: MutableList<Statement>,
@ -370,6 +370,11 @@ open class Module(final override var statements: MutableList<Statement>,
statements.forEach {it.linkParents(this)} statements.forEach {it.linkParents(this)}
} }
fun linkIntoProgram(program: Program) {
this.program = program
linkParents(program.namespace)
}
override val definingScope: INameScope override val definingScope: INameScope
get() = program.namespace get() = program.namespace
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {

View File

@ -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 accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifier(vararg scopedName: String) = expression.referencesIdentifier(*scopedName) 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? { override fun constValue(program: Program): NumericLiteralValue? {
val cv = expression.constValue(program) ?: return null val cv = expression.constValue(program) ?: return null
val cast = cv.cast(type) 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 constValue(program: Program): NumericLiteralValue? = null
override fun referencesIdentifier(vararg scopedName: String) = false 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: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) 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 accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifier(vararg scopedName: String) = false 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 constValue(program: Program): NumericLiteralValue? = null
override fun toString(): String { 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 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) 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 accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
override fun toString(): String = "'${escape(value.toString())}'" 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) operator fun compareTo(other: CharLiteral): Int = value.compareTo(other.value)
override fun hashCode(): Int = Objects.hash(value, altEncoding) override fun hashCode(): Int = Objects.hash(value, altEncoding)
override fun equals(other: Any?): Boolean { 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 accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun toString(): String = "'${escape(value)}'" 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) operator fun compareTo(other: StringLiteralValue): Int = value.compareTo(other.value)
override fun hashCode(): Int = Objects.hash(value, altEncoding) override fun hashCode(): Int = Objects.hash(value, altEncoding)
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {

View File

@ -31,12 +31,9 @@ object Prog8Parser {
parser.addErrorListener(antlrErrorListener) parser.addErrorListener(antlrErrorListener)
val parseTree = parser.module() val parseTree = parser.module()
val module = ParsedModule(src) val module = ParsedModule(src)
// .linkParents called in ParsedModule.add
parseTree.directive().forEach { module.add(it.toAst()) } parseTree.directive().forEach { module.add(it.toAst()) }
// TODO: remove Encoding
parseTree.block().forEach { module.add(it.toAst(module.isLibrary)) } parseTree.block().forEach { module.add(it.toAst(module.isLibrary)) }
return module return module
@ -95,10 +92,13 @@ object Prog8Parser {
private class AntlrErrorListener(val src: SourceCode): BaseErrorListener() { private class AntlrErrorListener(val src: SourceCode): BaseErrorListener() {
override fun syntaxError(recognizer: Recognizer<*, *>?, offendingSymbol: Any?, line: Int, charPositionInLine: Int, msg: String, e: RecognitionException?) { override fun syntaxError(recognizer: Recognizer<*, *>?, offendingSymbol: Any?, line: Int, charPositionInLine: Int, msg: String, e: RecognitionException?) {
if (e == null) { if (e == null) {
TODO("no RecognitionException - create your own ParseError") throw ParseError(msg, Position(src.origin, line, charPositionInLine, charPositionInLine), RuntimeException("parse error"))
//throw ParseError()
} else { } 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)
}
} }
} }
} }
@ -107,7 +107,7 @@ object Prog8Parser {
val offending = this.offendingToken val offending = this.offendingToken
val line = offending.line val line = offending.line
val beginCol = offending.charPositionInLine 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) return Position(file, line, beginCol, endCol)
} }

View File

@ -4,6 +4,7 @@ import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
import prog8.ast.IFunctionCall import prog8.ast.IFunctionCall
import prog8.ast.Module
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.expressions.CharLiteral import prog8.ast.expressions.CharLiteral
@ -17,12 +18,11 @@ import prog8.parser.SourceCode
import prog8tests.helpers.assumeNotExists import prog8tests.helpers.assumeNotExists
import prog8tests.helpers.assumeReadableFile import prog8tests.helpers.assumeReadableFile
import prog8tests.helpers.fixturesDir import prog8tests.helpers.fixturesDir
import kotlin.io.path.Path
import kotlin.io.path.isRegularFile
import kotlin.io.path.name import kotlin.io.path.name
import kotlin.io.path.nameWithoutExtension import kotlin.io.path.nameWithoutExtension
import kotlin.test.assertContains import kotlin.test.*
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertIs
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ -219,7 +219,7 @@ class TestProg8Parser {
val module = parseModule(SourceCode.Text(srcText)) val module = parseModule(SourceCode.Text(srcText))
// Note: assertContains has *actual* as first param // Note: assertContains has *actual* as first param
assertContains(module.name, Regex("^<String@[0-9a-f]+>$")) assertContains(module.name, Regex("^<String@[0-9a-f\\-]+>$"))
} }
@Test @Test
@ -289,7 +289,7 @@ class TestProg8Parser {
try { try {
parseModule(SourceCode.Text(srcText)) parseModule(SourceCode.Text(srcText))
} catch (e: ParseError) { } catch (e: ParseError) {
assertPosition(e.position, Regex("^<String@[0-9a-f]+>$"), 1, 4, 4) assertPosition(e.position, Regex("^<String@[0-9a-f\\-]+>$"), 1, 4, 4)
} }
} }
@ -312,7 +312,7 @@ class TestProg8Parser {
} }
""".trimIndent() """.trimIndent()
val module = parseModule(SourceCode.Text(srcText)) val module = parseModule(SourceCode.Text(srcText))
assertPositionOf(module, Regex("^<String@[0-9a-f]+>$"), 1, 0) // TODO: endCol wrong assertPositionOf(module, Regex("^<String@[0-9a-f\\-]+>$"), 1, 0) // TODO: endCol wrong
} }
@Test @Test
@ -330,9 +330,9 @@ class TestProg8Parser {
val mpf = module.position.file val mpf = module.position.file
assertPositionOf(module, SourceCode.relative(path).toString(), 1, 0) // TODO: endCol wrong assertPositionOf(module, SourceCode.relative(path).toString(), 1, 0) // TODO: endCol wrong
val mainBlock = module.statements.filterIsInstance<Block>()[0] val mainBlock = module.statements.filterIsInstance<Block>()[0]
assertPositionOf(mainBlock, mpf, 1, 0) // TODO: endCol wrong! assertPositionOf(mainBlock, mpf, 2, 0) // TODO: endCol wrong!
val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0] val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[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<Block>().forEach { b ->
asserter(b)
b.statements.forEach(asserter)
b.statements.filterIsInstance<Subroutine>().forEach { s ->
asserter(s)
s.statements.forEach(asserter)
}
}
} }
@Nested @Nested
inner class CharLiterals { inner class CharLiterals {

View File

@ -1,30 +1,32 @@
package prog8tests package prog8tests
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.core.StringStartsWith
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
import prog8.parser.SourceCode import prog8.parser.SourceCode
import prog8.parser.SourceCode.Companion.libraryFilePrefix import prog8.parser.SourceCode.Companion.libraryFilePrefix
import prog8tests.helpers.* import prog8tests.helpers.*
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.test.assertContains import kotlin.test.*
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestSourceCode { class TestSourceCode {
@Test @Test
fun testFactoryMethod_Of() { fun testFromString() {
val text = """ val text = """
main { } main { }
""".trimIndent() """
val src = SourceCode.Text(text) val src = SourceCode.Text(text)
val actualText = src.getCharStream().toString() val actualText = src.getCharStream().toString()
assertContains(src.origin, Regex("^<String@[0-9a-f]+>$")) assertContains(src.origin, Regex("^<String@[0-9a-f\\-]+>$"))
assertEquals(text, actualText) assertEquals(text, actualText)
assertFalse(src.isFromResources)
assertFalse(src.isFromFilesystem)
assertThat(src.toString(), StringStartsWith("prog8.parser.SourceCode"))
} }
@Test @Test
@ -54,6 +56,8 @@ class TestSourceCode {
val expectedOrigin = SourceCode.relative(path).toString() val expectedOrigin = SourceCode.relative(path).toString()
assertEquals(expectedOrigin, src.origin) assertEquals(expectedOrigin, src.origin)
assertEquals(path.toFile().readText(), src.asString()) assertEquals(path.toFile().readText(), src.asString())
assertFalse(src.isFromResources)
assertTrue(src.isFromFilesystem)
} }
@Test @Test
@ -75,6 +79,8 @@ class TestSourceCode {
assertEquals("$libraryFilePrefix/$pathString", src.origin) assertEquals("$libraryFilePrefix/$pathString", src.origin)
assertEquals(srcFile.readText(), src.asString()) assertEquals(srcFile.readText(), src.asString())
assertTrue(src.isFromResources)
assertFalse(src.isFromFilesystem)
} }
@Test @Test

View File

@ -50,12 +50,12 @@ class ProgramTests {
assertSame(program, m1.program) assertSame(program, m1.program)
assertSame(program.namespace, m1.parent) assertSame(program.namespace, m1.parent)
assertFailsWith<IllegalArgumentException> { program.addModule(m1) } assertThat("module may not occur multiple times",
.let { assertThat(it.message, containsString(m1.name)) } assertFailsWith<IllegalArgumentException> { program.addModule(m1) }.message, containsString(m1.name))
val m2 = Module(mutableListOf(), m1.position, m1.source) val m2 = Module(mutableListOf(), m1.position, m1.source)
assertFailsWith<IllegalArgumentException> { program.addModule(m2) } assertThat("other module but with same name may not occur multiple times",
.let { assertThat(it.message, containsString(m2.name)) } assertFailsWith<IllegalArgumentException> { program.addModule(m2) }.message, containsString(m2.name))
} }
} }

View File

@ -1,4 +1,7 @@
%zeropage basicsafe
main { main {
sub start() { sub start() {
ubyte aa=99
aa++
} }
} }

View File

@ -14,7 +14,7 @@ Blocked by Commander-x16 v39 release
Future 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. - 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 }) - replace certain uses of inferredType.getOr(DataType.UNDEFINED) by i.getOrElse({ errorhandler })
- see if we can remove more "[InferredType].getOr(DataType.UNDEFINED)" - see if we can remove more "[InferredType].getOr(DataType.UNDEFINED)"

View File

@ -22,7 +22,7 @@ main {
; Not yet implemented in ROM: cx16.FB_set_palette(&colors, 0, len(colors)*3) ; Not yet implemented in ROM: cx16.FB_set_palette(&colors, 0, len(colors)*3)
palette.set_rgb(&colors, len(colors)) 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.FB_init()
cx16.VERA_DC_VSCALE = 0 ; display trick spoiler.......: stretch display all the way to the bottom cx16.VERA_DC_VSCALE = 0 ; display trick spoiler.......: stretch display all the way to the bottom
cx16.set_rasterirq(&irq.irqhandler, 0) cx16.set_rasterirq(&irq.irqhandler, 0)