Compare commits

...

6 Commits
v8.8 ... v8.8.1

21 changed files with 342 additions and 87 deletions

View File

@ -1,17 +1,17 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="antlr.antlr4" type="repository"> <library name="antlr.antlr4" type="repository">
<properties maven-id="org.antlr:antlr4:4.10.1"> <properties maven-id="org.antlr:antlr4:4.11.1">
<exclude> <exclude>
<dependency maven-id="com.ibm.icu:icu4j" /> <dependency maven-id="com.ibm.icu:icu4j" />
</exclude> </exclude>
</properties> </properties>
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.10.1/antlr4-4.10.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.11.1/antlr4-4.11.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.10.1/antlr4-runtime-4.10.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.11.1/antlr4-runtime-4.11.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.3/ST4-4.3.3.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.4/ST4-4.3.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.0.4/javax.json-1.0.4.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.1.4/javax.json-1.1.4.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />

View File

@ -873,20 +873,20 @@ internal class AssignmentAsmGen(private val program: Program,
throw AssemblyError("containment check of floats not supported") throw AssemblyError("containment check of floats not supported")
} }
DataType.ARRAY_B, DataType.ARRAY_UB -> { DataType.ARRAY_B, DataType.ARRAY_UB -> {
val arrayVal = variable.value as ArrayLiteral val numElements = variable.arraysize!!.constIndex()!!
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE) assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
asmgen.restoreRegisterLocal(CpuRegister.A) asmgen.restoreRegisterLocal(CpuRegister.A)
asmgen.out(" ldy #${arrayVal.value.size}") asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_bytearray") asmgen.out(" jsr prog8_lib.containment_bytearray")
return return
} }
DataType.ARRAY_W, DataType.ARRAY_UW -> { DataType.ARRAY_W, DataType.ARRAY_UW -> {
val arrayVal = variable.value as ArrayLiteral val numElements = variable.arraysize!!.constIndex()!!
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine) assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname)
asmgen.out(" ldy #${arrayVal.value.size}") asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_wordarray") asmgen.out(" jsr prog8_lib.containment_wordarray")
return return
} }
@ -1005,7 +1005,7 @@ internal class AssignmentAsmGen(private val program: Program,
} }
} }
if(valueDt==DataType.UBYTE) { if(valueDt==DataType.UBYTE || valueDt==DataType.BOOL) {
when(target.register) { when(target.register) {
RegisterOrPair.A, RegisterOrPair.A,
RegisterOrPair.X, RegisterOrPair.X,
@ -1069,8 +1069,10 @@ internal class AssignmentAsmGen(private val program: Program,
} }
} }
if(targetDt in IntegerDatatypes && valueDt in IntegerDatatypes && valueDt.isAssignableTo(targetDt)) { if(targetDt in IntegerDatatypes && valueDt in IntegerDatatypes && valueDt!=targetDt && valueDt.isAssignableTo(targetDt)) {
require(targetDt in WordDatatypes && valueDt in ByteDatatypes) { "should be byte to word assignment ${origTypeCastExpression.position}"} require(targetDt in WordDatatypes && valueDt in ByteDatatypes) {
"should be byte to word assignment ${origTypeCastExpression.position}"
}
when(target.kind) { when(target.kind) {
// TargetStorageKind.VARIABLE -> { // TargetStorageKind.VARIABLE -> {
// This has been handled already earlier on line 961. // This has been handled already earlier on line 961.
@ -1136,13 +1138,12 @@ internal class AssignmentAsmGen(private val program: Program,
asmgen.out(" jsr floats.MOVEF") asmgen.out(" jsr floats.MOVEF")
} }
return return
} else { }
// No more special optmized cases yet. Do the rest via more complex evaluation // No more special optmized cases yet. Do the rest via more complex evaluation
// note: cannot use assignTypeCastedValue because that is ourselves :P // note: cannot use assignTypeCastedValue because that is ourselves :P
// NOTE: THIS MAY TURN INTO A STACK OVERFLOW ERROR IF IT CAN'T SIMPLIFY THE TYPECAST..... :-/ // NOTE: THIS MAY TURN INTO A STACK OVERFLOW ERROR IF IT CAN'T SIMPLIFY THE TYPECAST..... :-/
asmgen.assignExpressionTo(origTypeCastExpression, target) asmgen.assignExpressionTo(origTypeCastExpression, target)
return
}
} }
private fun assignCastViaLsbFunc(value: Expression, target: AsmAssignTarget) { private fun assignCastViaLsbFunc(value: Expression, target: AsmAssignTarget) {

View File

@ -120,12 +120,26 @@ class UnusedCodeRemover(private val program: Program,
if(singleUse is AssignTarget) { if(singleUse is AssignTarget) {
val assignment = singleUse.parent as Assignment val assignment = singleUse.parent as Assignment
if(assignment.origin==AssignmentOrigin.VARINIT) { if(assignment.origin==AssignmentOrigin.VARINIT) {
if(assignment.value.isSimple) {
// remove the vardecl
if(!decl.definingModule.isLibrary) if(!decl.definingModule.isLibrary)
errors.warn("removing unused variable '${decl.name}'", decl.position) errors.warn("removing unused variable '${decl.name}'", decl.position)
return listOf( return listOf(
IAstModification.Remove(decl, parent as IStatementContainer), IAstModification.Remove(decl, parent as IStatementContainer),
IAstModification.Remove(assignment, assignment.parent as IStatementContainer) IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
) )
} else if(assignment.value is IFunctionCall) {
// replace the unused variable's initializer function call by a void
errors.warn("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
val fcall = assignment.value as IFunctionCall
val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position)
return listOf(
IAstModification.ReplaceNode(decl, voidCall, parent),
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
)
} else {
errors.warn("variable '${decl.name}' is unused but has non-trivial initialization assignment. Leaving this in but maybe it can be removed altogether", decl.position)
}
} }
} }
} }

View File

@ -34,7 +34,7 @@ dependencies {
implementation project(':codeGenIntermediate') implementation project(':codeGenIntermediate')
implementation project(':codeGenExperimental') implementation project(':codeGenExperimental')
implementation project(':virtualmachine') implementation project(':virtualmachine')
implementation 'org.antlr:antlr4-runtime:4.10.1' implementation 'org.antlr:antlr4-runtime:4.11.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.4' implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.4'

View File

@ -23,7 +23,7 @@ diskio {
void c64.CHRIN() ; skip the 4 prologue bytes void c64.CHRIN() ; skip the 4 prologue bytes
} }
; while not key pressed / EOF encountered, read data. ; while not stop key pressed / EOF encountered, read data.
status = c64.READST() status = c64.READST()
if status!=0 { if status!=0 {
status = 1 status = 1
@ -84,7 +84,6 @@ io_error:
if c64.READST()!=0 if c64.READST()!=0
goto io_error goto io_error
; while not key pressed / EOF encountered, read data.
cx16.r0 = &list_filename cx16.r0 = &list_filename
repeat { repeat {
@(cx16.r0) = c64.CHRIN() @(cx16.r0) = c64.CHRIN()

View File

@ -1 +1 @@
8.8 8.8.1

View File

@ -7,6 +7,7 @@ import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.compiler.BuiltinFunctions import prog8.compiler.BuiltinFunctions
import prog8.compiler.InplaceModifyingBuiltinFunctions import prog8.compiler.InplaceModifyingBuiltinFunctions
import prog8.compiler.builtinFunctionReturnType import prog8.compiler.builtinFunctionReturnType
@ -1259,8 +1260,14 @@ internal class AstChecker(private val program: Program,
val elementDt = containment.element.inferType(program) val elementDt = containment.element.inferType(program)
val iterableDt = containment.iterable.inferType(program) val iterableDt = containment.iterable.inferType(program)
if(containment.parent is BinaryExpression) if(compilerOptions.compTarget.name!=VMTarget.NAME) {
val parentBinexpr = containment.parent as? BinaryExpression
if(parentBinexpr!=null) {
// only supported if compared to 1 or 0, more complex expressions aren't supported in the 6502 code-gen.
if(parentBinexpr.operator!="==" || parentBinexpr.right.constValue(program)?.number !in listOf(0.0, 1.0))
errors.err("containment check is currently not supported inside complex expressions", containment.position) errors.err("containment check is currently not supported inside complex expressions", containment.position)
}
}
if(iterableDt.isIterable && containment.iterable !is RangeExpression) { if(iterableDt.isIterable && containment.iterable !is RangeExpression) {
val iterableEltDt = ArrayToElementTypes.getValue(iterableDt.getOr(DataType.UNDEFINED)) val iterableEltDt = ArrayToElementTypes.getValue(iterableDt.getOr(DataType.UNDEFINED))

View File

@ -120,16 +120,19 @@ class AstPreprocessor(val program: Program,
movements.add(IAstModification.InsertFirst(decl, parentscope)) movements.add(IAstModification.InsertFirst(decl, parentscope))
replacements.add(IAstModification.Remove(decl, scope)) replacements.add(IAstModification.Remove(decl, scope))
} else { } else {
val declToInsert: VarDecl
if(decl.value!=null && decl.datatype in NumericDatatypes) { if(decl.value!=null && decl.datatype in NumericDatatypes) {
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position) val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position) val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position)
replacements.add(IAstModification.ReplaceNode(decl, assign, scope)) replacements.add(IAstModification.ReplaceNode(decl, assign, scope))
decl.value = null decl.value = null
decl.allowInitializeWithZero = false decl.allowInitializeWithZero = false
declToInsert = decl.copy()
} else { } else {
replacements.add(IAstModification.Remove(decl, scope)) replacements.add(IAstModification.Remove(decl, scope))
declToInsert = decl
} }
movements.add(IAstModification.InsertFirst(decl, parentscope)) movements.add(IAstModification.InsertFirst(declToInsert, parentscope))
} }
} }
return movements + replacements return movements + replacements
@ -138,12 +141,15 @@ class AstPreprocessor(val program: Program,
} }
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> { override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
// this has to be done here becuse otherwise the string / range literal values will have been replaced by variables
if(expr.operator=="in") { if(expr.operator=="in") {
val containment = ContainmentCheck(expr.left, expr.right, expr.position) val containment = ContainmentCheck(expr.left, expr.right, expr.position)
return listOf(IAstModification.ReplaceNode(expr, containment, parent)) return listOf(IAstModification.ReplaceNode(expr, containment, parent))
} }
if(expr.operator=="not in") {
val containment = ContainmentCheck(expr.left, expr.right, expr.position)
val notContainment = PrefixExpression("not", containment, expr.position)
return listOf(IAstModification.ReplaceNode(expr, notContainment, parent))
}
return noModifications return noModifications
} }

View File

@ -163,4 +163,12 @@ _after:
} }
return noModifications return noModifications
} }
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
if(expr.operator=="in") {
val containment = ContainmentCheck(expr.left, expr.right, expr.position)
return listOf(IAstModification.ReplaceNode(expr, containment, parent))
}
return noModifications
}
} }

View File

@ -6,10 +6,7 @@ import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.FatalAstException import prog8.ast.base.FatalAstException
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.AnonymousScope import prog8.ast.statements.*
import prog8.ast.statements.Assignment
import prog8.ast.statements.ConditionalBranch
import prog8.ast.statements.IfElse
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.* import prog8.code.core.*
@ -65,11 +62,19 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
} }
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> { override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
// remove duplicated assignments, but not if it's a memory mapped IO register
val isIO = try {
assignment.target.isIOAddress(options.compTarget.machine)
} catch (_: FatalAstException) {
false
}
if(!isIO) {
val nextAssign = assignment.nextSibling() as? Assignment val nextAssign = assignment.nextSibling() as? Assignment
if(nextAssign!=null && nextAssign.target.isSameAs(assignment.target, program)) { if (nextAssign != null && nextAssign.target.isSameAs(assignment.target, program)) {
if(!nextAssign.isAugmentable && nextAssign.value isSameAs assignment.value && assignment.value !is IFunctionCall) // don't remove function calls even when they're duplicates if (!nextAssign.isAugmentable && nextAssign.value isSameAs assignment.value && assignment.value !is IFunctionCall) // don't remove function calls even when they're duplicates
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer)) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
} }
}
return noModifications return noModifications
} }
@ -175,6 +180,19 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
return noModifications return noModifications
} }
fun checkArray(variable: VarDecl): Iterable<IAstModification> {
return if(variable.value==null) {
val arraySpec = variable.arraysize!!
val size = arraySpec.indexExpr.constValue(program)?.number?.toInt() ?: throw FatalAstException("no array size")
return if(size==0)
replaceWithFalse()
else
noModifications
}
else
checkArray((variable.value as ArrayLiteral).value)
}
fun checkString(stringVal: StringLiteral): Iterable<IAstModification> { fun checkString(stringVal: StringLiteral): Iterable<IAstModification> {
if(stringVal.value.isEmpty()) if(stringVal.value.isEmpty())
return replaceWithFalse() return replaceWithFalse()
@ -198,8 +216,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
return checkString(stringVal) return checkString(stringVal)
} }
in ArrayDatatypes -> { in ArrayDatatypes -> {
val array = (variable.value as ArrayLiteral).value return checkArray(variable)
return checkArray(array)
} }
else -> {} else -> {}
} }

View File

@ -15,6 +15,7 @@ import prog8.ast.statements.*
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.Position import prog8.code.core.Position
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.code.target.Cx16Target
import prog8.compiler.printProgram import prog8.compiler.printProgram
import prog8tests.helpers.* import prog8tests.helpers.*
@ -736,4 +737,60 @@ class TestOptimization: FunSpec({
val stmts = result.program.entrypoint.statements val stmts = result.program.entrypoint.statements
stmts.size shouldBe 3 stmts.size shouldBe 3
} }
test("repeated assignments to IO register should remain") {
val srcX16="""
main {
sub start() {
ubyte @shared xx
xx = 42
xx = 42 ; removed
xx = 42 ; removed
cx16.VERA_DATA0 = 0
cx16.VERA_DATA0 = 0
cx16.VERA_DATA0 = 0
@($9fff) = 0
@($9fff) = 0
@($9fff) = 0
return
}
}"""
var result = compileText(Cx16Target(), true, srcX16, writeAssembly = true)!!
var statements = result.program.entrypoint.statements
statements.size shouldBe 9
(statements[1] as Assignment).target.identifier!!.nameInSource shouldBe listOf("xx")
(statements[2] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "VERA_DATA0")
(statements[3] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "VERA_DATA0")
(statements[4] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "VERA_DATA0")
(statements[5] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 0x9fff
(statements[6] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 0x9fff
(statements[7] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 0x9fff
val srcC64="""
main {
sub start() {
ubyte @shared xx
xx = 42
xx = 42 ;removed
xx = 42 ;removed
c64.EXTCOL = 0
c64.EXTCOL = 0
c64.EXTCOL = 0
@(53281) = 0
@(53281) = 0
@(53281) = 0
return
}
}"""
result = compileText(C64Target(), true, srcC64, writeAssembly = true)!!
statements = result.program.entrypoint.statements
statements.size shouldBe 9
(statements[1] as Assignment).target.identifier!!.nameInSource shouldBe listOf("xx")
(statements[2] as Assignment).target.identifier!!.nameInSource shouldBe listOf("c64", "EXTCOL")
(statements[3] as Assignment).target.identifier!!.nameInSource shouldBe listOf("c64", "EXTCOL")
(statements[4] as Assignment).target.identifier!!.nameInSource shouldBe listOf("c64", "EXTCOL")
(statements[5] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 53281.0
(statements[6] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 53281.0
(statements[7] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 53281.0
}
}) })

View File

@ -913,7 +913,7 @@ class TestProg8Parser: FunSpec( {
val text=""" val text="""
main { main {
sub start() { sub start() {
ubyte cc ubyte @shared cc
ubyte[] array = [1,2,3] ubyte[] array = [1,2,3]
cc = 99 in 12345 cc = 99 in 12345
cc = 9999 in array cc = 9999 in array

View File

@ -5,17 +5,13 @@ import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.types.instanceOf import io.kotest.matchers.types.instanceOf
import prog8.ast.IFunctionCall import prog8.ast.IFunctionCall
import prog8.ast.expressions.BinaryExpression import prog8.ast.expressions.*
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.Assignment import prog8.ast.statements.Assignment
import prog8.ast.statements.InlineAssembly import prog8.ast.statements.InlineAssembly
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.Position import prog8.code.core.Position
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.compiler.printProgram
import prog8tests.helpers.compileText import prog8tests.helpers.compileText
class TestVarious: FunSpec({ class TestVarious: FunSpec({
@ -148,7 +144,6 @@ main {
} }
}""" }"""
val result = compileText(C64Target(), optimize=false, src, writeAssembly=false)!! val result = compileText(C64Target(), optimize=false, src, writeAssembly=false)!!
printProgram(result.program)
val stmts = result.program.entrypoint.statements val stmts = result.program.entrypoint.statements
stmts.size shouldBe 7 stmts.size shouldBe 7
val assign1expr = (stmts[3] as Assignment).value as BinaryExpression val assign1expr = (stmts[3] as Assignment).value as BinaryExpression
@ -161,5 +156,50 @@ main {
leftval2.type shouldBe DataType.UWORD leftval2.type shouldBe DataType.UWORD
leftval2.number shouldBe 1.0 leftval2.number shouldBe 1.0
} }
test("hoisting vars with complex initializer expressions to outer scope") {
val src="""
main {
sub pget(uword @zp x, uword y) -> ubyte {
return lsb(x+y)
}
sub start() {
uword[128] YY
ubyte[] ARRAY = [1, 5, 2]
repeat {
ubyte pixel_side1 = pget(2, YY[2]+1) in ARRAY
ubyte pixel_side2 = pget(2, 2) in ARRAY
ubyte[] array2 = [1,2,3]
}
}
}"""
val result = compileText(C64Target(), optimize=false, src, writeAssembly=false)!!
val stmts = result.program.entrypoint.statements
stmts.size shouldBe 9
}
test("alternative notation for negative containment check") {
val src="""
main {
sub start() {
ubyte[] array=[1,2,3]
cx16.r0L = not (3 in array)
cx16.r1L = 3 not in array
}
}
"""
val result = compileText(C64Target(), optimize=false, src, writeAssembly=false)!!
val stmts = result.program.entrypoint.statements
stmts.size shouldBe 3
val value1 = (stmts[1] as Assignment).value as BinaryExpression
val value2 = (stmts[2] as Assignment).value as BinaryExpression
value1.operator shouldBe "=="
value1.left shouldBe instanceOf<ContainmentCheck>()
(value1.right as NumericLiteral).number shouldBe 0.0
value2.operator shouldBe "=="
value2.left shouldBe instanceOf<ContainmentCheck>()
(value2.right as NumericLiteral).number shouldBe 0.0
}
}) })

View File

@ -0,0 +1,22 @@
package prog8tests.codegeneration
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldNotBe
import prog8.code.target.C64Target
import prog8tests.helpers.compileText
class TestVarious: FunSpec({
test("bool to byte cast in expression is correct") {
val text="""
main {
sub start() {
ubyte[3] values
func(33 + (22 in values))
}
sub func(ubyte arg) {
arg++
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
})

View File

@ -327,4 +327,19 @@ main {
instructions.size shouldBe 18 instructions.size shouldBe 18
instructions.last().opcode shouldBe Opcode.RETURN instructions.last().opcode shouldBe Opcode.RETURN
} }
test("compile virtual: various expressions") {
val text="""
main {
sub start() {
ubyte[3] values = [1,2,3]
func(33 + (22 in values)) ; bool cast to byte
func(values[cx16.r0L] + (22 in values)) ; containment in complex expression
}
sub func(ubyte arg) {
arg++
}
}"""
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
}) })

View File

@ -24,7 +24,7 @@ compileTestKotlin {
dependencies { dependencies {
implementation project(':codeCore') implementation project(':codeCore')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation 'org.antlr:antlr4-runtime:4.10.1' implementation 'org.antlr:antlr4-runtime:4.11.1'
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
implementation project(':parser') implementation project(':parser')
} }

View File

@ -532,8 +532,7 @@ data class AssignTarget(var identifier: IdentifierReference?,
} else false } else false
} }
ident != null -> { ident != null -> {
val decl = ident.targetVarDecl(definingModule.program) ?: val decl = ident.targetVarDecl(definingModule.program) ?: throw FatalAstException("invalid identifier ${ident.nameInSource}")
throw FatalAstException("invalid identifier ${ident.nameInSource}")
return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteral) return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteral)
machine.isIOAddress((decl.value as NumericLiteral).number.toUInt()) machine.isIOAddress((decl.value as NumericLiteral).number.toUInt())
else else

View File

@ -3,6 +3,11 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- make sure bool value is always 0 or 1 (all casts should convert), then:
- rewrite bool=bool^1 into bool=not bool
- should solve: bool bb = not bb -> larger code than bool bb ^= 1
- should solve: bool xx = ~xx -> larger code than xx ^= 1
... ...

View File

@ -5,37 +5,44 @@ main {
sub start() { sub start() {
gfx2.screen_mode(4) gfx2.screen_mode(4)
uword[128] xx uword[128] flakes1_xx
uword[128] yy uword[128] flakes1_yy
uword[128] flakes2_xx
uword[128] flakes2_yy
ubyte @zp idx ubyte @zp idx
for idx in 0 to 127 { for idx in 0 to 127 {
xx[idx] = math.rndw() % 320 flakes1_xx[idx] = math.rndw() % 320
yy[idx] = math.rndw() % 240 flakes1_yy[idx] = math.rndw() % 240
flakes2_xx[idx] = math.rndw() % 320
flakes2_yy[idx] = math.rndw() % 240
} }
gfx2.text(96, 90, 2, sc:"**************") draw_screen()
gfx2.text(104, 100, 5, sc:"let it snow!")
gfx2.text(96, 110, 2, sc:"**************")
const ubyte FALLING_SNOW_COLOR = 1 const ubyte FALLING_SNOW_COLOR = 1
const ubyte FALLING_SNOW_COLOR2 = 12
const ubyte PILED_SNOW_COLOR = 15 const ubyte PILED_SNOW_COLOR = 15
ubyte[] PILED_SNOW_COLORS = [PILED_SNOW_COLOR, 5, 2] ; falling snow colors should NOT be in here!
bool fall_layer_2 = false
repeat { repeat {
sys.waitvsync() for idx in 0 to len(flakes1_xx)-1 {
for idx in 0 to 127 { gfx2.plot(flakes1_xx[idx], flakes1_yy[idx], FALLING_SNOW_COLOR)
gfx2.plot(xx[idx], yy[idx], FALLING_SNOW_COLOR) gfx2.plot(flakes2_xx[idx], flakes2_yy[idx], FALLING_SNOW_COLOR2)
} }
sys.waitvsync() sys.waitvsync()
for idx in 0 to 127 {
if yy[idx]==239 { for idx in 0 to len(flakes1_xx)-1 {
if flakes1_yy[idx]==239 {
; reached the floor ; reached the floor
gfx2.plot(xx[idx], yy[idx], PILED_SNOW_COLOR) gfx2.plot(flakes1_xx[idx], flakes1_yy[idx], PILED_SNOW_COLOR)
yy[idx] = 0 flakes1_yy[idx] = 0
xx[idx] = math.rndw() % 320 flakes1_xx[idx] = math.rndw() % 320
} else if gfx2.pget(xx[idx], yy[idx]+1)==PILED_SNOW_COLOR { } else if gfx2.pget(flakes1_xx[idx], flakes1_yy[idx]+1) in PILED_SNOW_COLORS {
; pile up ; pile up
uword @zp snowx = xx[idx] ; TODO somehow prevent growing horizontally/diagonally like a crystal
uword @zp snowx = flakes1_xx[idx]
if snowx!=0 and snowx!=319 { ; check to avoid x coordinate under/overflow if snowx!=0 and snowx!=319 { ; check to avoid x coordinate under/overflow
uword pilex1 uword pilex1
uword pilex2 uword pilex2
@ -46,30 +53,63 @@ main {
pilex1 = snowx+1 pilex1 = snowx+1
pilex2 = snowx-1 pilex2 = snowx-1
} }
if gfx2.pget(pilex1, yy[idx]+1)==0 { ubyte pixel_side1 = gfx2.pget(pilex1, flakes1_yy[idx]+1)
gfx2.plot(snowx, yy[idx], 0) ubyte pixel_side2 = gfx2.pget(pilex2, flakes1_yy[idx]+1)
gfx2.plot(pilex1, yy[idx]+1, PILED_SNOW_COLOR) if pixel_side1 == 0 or pixel_side1 == FALLING_SNOW_COLOR2 {
} else if gfx2.pget(pilex2, yy[idx]+1)==0 { gfx2.plot(snowx, flakes1_yy[idx], 0)
gfx2.plot(snowx, yy[idx], 0) gfx2.plot(pilex1, flakes1_yy[idx]+1, PILED_SNOW_COLOR)
gfx2.plot(pilex2, yy[idx]+1, PILED_SNOW_COLOR) } else if pixel_side2 == 0 or pixel_side2 == FALLING_SNOW_COLOR2 {
gfx2.plot(snowx, flakes1_yy[idx], 0)
gfx2.plot(pilex2, flakes1_yy[idx]+1, PILED_SNOW_COLOR)
} else { } else {
gfx2.plot(snowx, yy[idx], PILED_SNOW_COLOR) gfx2.plot(snowx, flakes1_yy[idx], PILED_SNOW_COLOR)
} }
} }
yy[idx] = 0 flakes1_yy[idx] = 0
xx[idx] = math.rndw() % 320 flakes1_xx[idx] = math.rndw() % 320
} else { } else {
; fall ; fall
gfx2.plot(xx[idx], yy[idx], 0) gfx2.plot(flakes1_xx[idx], flakes1_yy[idx], 0)
yy[idx]++ flakes1_yy[idx]++
when math.rnd() & 3 { when math.rnd() & 3 {
1 -> { 1 -> {
if xx[idx] if flakes1_xx[idx]
xx[idx]-- flakes1_xx[idx]--
} }
2 -> { 2 -> {
if xx[idx] < 319 if flakes1_xx[idx] < 319
xx[idx]++ flakes1_xx[idx]++
}
}
}
}
fall_layer_2 = not fall_layer_2
if fall_layer_2 {
for idx in 0 to len(flakes2_xx)-1 {
; the second 'layer' of snowflakes
if flakes2_yy[idx]==239 {
; reached the floor, don't pile up just fall again
flakes2_yy[idx] = 0
flakes2_xx[idx] = math.rndw() % 320
} else if gfx2.pget(flakes2_xx[idx], flakes2_yy[idx]+1) in PILED_SNOW_COLORS {
; reached an obstruction, don't pile up just fall again
flakes2_yy[idx] = 0
flakes2_xx[idx] = math.rndw() % 320
} else {
; fall normally
gfx2.plot(flakes2_xx[idx], flakes2_yy[idx], 0)
flakes2_yy[idx]+=1
when math.rnd() & 3 {
1 -> {
if flakes2_xx[idx]
flakes2_xx[idx]--
}
2 -> {
if flakes2_xx[idx] < 319
flakes2_xx[idx]++
}
} }
} }
} }
@ -79,4 +119,28 @@ main {
repeat { repeat {
} }
} }
sub draw_screen() {
gfx2.text(32, 130, 2, sc: "******************" )
gfx2.text(40, 140, 5, sc: "happy holidays !" )
gfx2.text(32, 150, 5, sc: "from commander x16" )
gfx2.text(32, 160, 2, sc: "******************" )
; draw a tree
const uword TREEX = 240
ubyte maxwidth = 20
ubyte branchesy = 100
gfx2.fillrect(TREEX-5, 210, 10, 30, 9)
repeat 5 {
ubyte width
for width in 1 to maxwidth {
gfx2.horizontal_line(TREEX-width/2, branchesy, width, 5)
branchesy++
}
branchesy -= maxwidth/2
maxwidth += 8
}
}
} }

View File

@ -170,6 +170,7 @@ expression :
| left = expression EOL? bop = ('==' | '!=') EOL? right = expression | left = expression EOL? bop = ('==' | '!=') EOL? right = expression
| rangefrom = expression rto = ('to'|'downto') rangeto = expression ('step' rangestep = expression)? // can't create separate rule due to mutual left-recursion | rangefrom = expression rto = ('to'|'downto') rangeto = expression ('step' rangestep = expression)? // can't create separate rule due to mutual left-recursion
| left = expression EOL? bop = 'in' EOL? right = expression | left = expression EOL? bop = 'in' EOL? right = expression
| left = expression EOL? bop = 'not in' EOL? right = expression
| prefix = 'not' expression | prefix = 'not' expression
| left = expression EOL? bop = 'and' EOL? right = expression | left = expression EOL? bop = 'and' EOL? right = expression
| left = expression EOL? bop = 'or' EOL? right = expression | left = expression EOL? bop = 'or' EOL? right = expression

View File

@ -10,8 +10,8 @@ java {
} }
dependencies { dependencies {
antlr 'org.antlr:antlr4:4.10.1' antlr 'org.antlr:antlr4:4.11.1'
implementation 'org.antlr:antlr4-runtime:4.10.1' implementation 'org.antlr:antlr4-runtime:4.11.1'
} }
configurations.all { configurations.all {