mirror of
https://github.com/irmen/prog8.git
synced 2025-06-14 11:23:37 +00:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
81bd5c784e | |||
b526e132a7 | |||
1860f66de5 | |||
ded9ada9bc | |||
d0e6a2eb8b | |||
4e103a1963 |
10
.idea/libraries/antlr_antlr4.xml
generated
10
.idea/libraries/antlr_antlr4.xml
generated
@ -1,17 +1,17 @@
|
||||
<component name="libraryTable">
|
||||
<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>
|
||||
<dependency maven-id="com.ibm.icu:icu4j" />
|
||||
</exclude>
|
||||
</properties>
|
||||
<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-runtime/4.10.1/antlr4-runtime-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.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/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/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>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
@ -873,20 +873,20 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
throw AssemblyError("containment check of floats not supported")
|
||||
}
|
||||
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)
|
||||
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
||||
asmgen.restoreRegisterLocal(CpuRegister.A)
|
||||
asmgen.out(" ldy #${arrayVal.value.size}")
|
||||
asmgen.out(" ldy #$numElements")
|
||||
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||
return
|
||||
}
|
||||
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)
|
||||
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")
|
||||
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) {
|
||||
RegisterOrPair.A,
|
||||
RegisterOrPair.X,
|
||||
@ -1069,8 +1069,10 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(targetDt in IntegerDatatypes && valueDt in IntegerDatatypes && valueDt.isAssignableTo(targetDt)) {
|
||||
require(targetDt in WordDatatypes && valueDt in ByteDatatypes) { "should be byte to word assignment ${origTypeCastExpression.position}"}
|
||||
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}"
|
||||
}
|
||||
when(target.kind) {
|
||||
// TargetStorageKind.VARIABLE -> {
|
||||
// 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")
|
||||
}
|
||||
return
|
||||
} else {
|
||||
// No more special optmized cases yet. Do the rest via more complex evaluation
|
||||
// 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..... :-/
|
||||
asmgen.assignExpressionTo(origTypeCastExpression, target)
|
||||
return
|
||||
}
|
||||
|
||||
// No more special optmized cases yet. Do the rest via more complex evaluation
|
||||
// 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..... :-/
|
||||
asmgen.assignExpressionTo(origTypeCastExpression, target)
|
||||
}
|
||||
|
||||
private fun assignCastViaLsbFunc(value: Expression, target: AsmAssignTarget) {
|
||||
|
@ -120,12 +120,26 @@ class UnusedCodeRemover(private val program: Program,
|
||||
if(singleUse is AssignTarget) {
|
||||
val assignment = singleUse.parent as Assignment
|
||||
if(assignment.origin==AssignmentOrigin.VARINIT) {
|
||||
if(!decl.definingModule.isLibrary)
|
||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||
return listOf(
|
||||
IAstModification.Remove(decl, parent as IStatementContainer),
|
||||
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
|
||||
)
|
||||
if(assignment.value.isSimple) {
|
||||
// remove the vardecl
|
||||
if(!decl.definingModule.isLibrary)
|
||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||
return listOf(
|
||||
IAstModification.Remove(decl, 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ dependencies {
|
||||
implementation project(':codeGenIntermediate')
|
||||
implementation project(':codeGenExperimental')
|
||||
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-reflect"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.4'
|
||||
|
@ -23,7 +23,7 @@ diskio {
|
||||
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()
|
||||
if status!=0 {
|
||||
status = 1
|
||||
@ -84,7 +84,6 @@ io_error:
|
||||
if c64.READST()!=0
|
||||
goto io_error
|
||||
|
||||
; while not key pressed / EOF encountered, read data.
|
||||
cx16.r0 = &list_filename
|
||||
repeat {
|
||||
@(cx16.r0) = c64.CHRIN()
|
||||
|
@ -1 +1 @@
|
||||
8.8
|
||||
8.8.1
|
||||
|
@ -7,6 +7,7 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.compiler.BuiltinFunctions
|
||||
import prog8.compiler.InplaceModifyingBuiltinFunctions
|
||||
import prog8.compiler.builtinFunctionReturnType
|
||||
@ -1259,8 +1260,14 @@ internal class AstChecker(private val program: Program,
|
||||
val elementDt = containment.element.inferType(program)
|
||||
val iterableDt = containment.iterable.inferType(program)
|
||||
|
||||
if(containment.parent is BinaryExpression)
|
||||
errors.err("containment check is currently not supported inside complex expressions", containment.position)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
if(iterableDt.isIterable && containment.iterable !is RangeExpression) {
|
||||
val iterableEltDt = ArrayToElementTypes.getValue(iterableDt.getOr(DataType.UNDEFINED))
|
||||
|
@ -120,16 +120,19 @@ class AstPreprocessor(val program: Program,
|
||||
movements.add(IAstModification.InsertFirst(decl, parentscope))
|
||||
replacements.add(IAstModification.Remove(decl, scope))
|
||||
} else {
|
||||
val declToInsert: VarDecl
|
||||
if(decl.value!=null && decl.datatype in NumericDatatypes) {
|
||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||
val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position)
|
||||
replacements.add(IAstModification.ReplaceNode(decl, assign, scope))
|
||||
decl.value = null
|
||||
decl.allowInitializeWithZero = false
|
||||
declToInsert = decl.copy()
|
||||
} else {
|
||||
replacements.add(IAstModification.Remove(decl, scope))
|
||||
declToInsert = decl
|
||||
}
|
||||
movements.add(IAstModification.InsertFirst(decl, parentscope))
|
||||
movements.add(IAstModification.InsertFirst(declToInsert, parentscope))
|
||||
}
|
||||
}
|
||||
return movements + replacements
|
||||
@ -138,12 +141,15 @@ class AstPreprocessor(val program: Program,
|
||||
}
|
||||
|
||||
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") {
|
||||
val containment = ContainmentCheck(expr.left, expr.right, expr.position)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -163,4 +163,12 @@ _after:
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,7 @@ import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.AnonymousScope
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.ConditionalBranch
|
||||
import prog8.ast.statements.IfElse
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
@ -65,10 +62,18 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
}
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
val nextAssign = assignment.nextSibling() as? Assignment
|
||||
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
|
||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
// 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
|
||||
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
|
||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
@ -175,6 +180,19 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
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> {
|
||||
if(stringVal.value.isEmpty())
|
||||
return replaceWithFalse()
|
||||
@ -198,8 +216,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
return checkString(stringVal)
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
val array = (variable.value as ArrayLiteral).value
|
||||
return checkArray(array)
|
||||
return checkArray(variable)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import prog8.ast.statements.*
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.compiler.printProgram
|
||||
import prog8tests.helpers.*
|
||||
|
||||
@ -736,4 +737,60 @@ class TestOptimization: FunSpec({
|
||||
val stmts = result.program.entrypoint.statements
|
||||
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
|
||||
}
|
||||
})
|
||||
|
@ -913,7 +913,7 @@ class TestProg8Parser: FunSpec( {
|
||||
val text="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte cc
|
||||
ubyte @shared cc
|
||||
ubyte[] array = [1,2,3]
|
||||
cc = 99 in 12345
|
||||
cc = 9999 in array
|
||||
|
@ -5,17 +5,13 @@ import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.types.instanceOf
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.expressions.StringLiteral
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.InlineAssembly
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.compiler.printProgram
|
||||
import prog8tests.helpers.compileText
|
||||
|
||||
class TestVarious: FunSpec({
|
||||
@ -148,7 +144,6 @@ main {
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=false, src, writeAssembly=false)!!
|
||||
printProgram(result.program)
|
||||
val stmts = result.program.entrypoint.statements
|
||||
stmts.size shouldBe 7
|
||||
val assign1expr = (stmts[3] as Assignment).value as BinaryExpression
|
||||
@ -161,5 +156,50 @@ main {
|
||||
leftval2.type shouldBe DataType.UWORD
|
||||
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
|
||||
}
|
||||
})
|
||||
|
||||
|
22
compiler/test/codegeneration/TestVarious.kt
Normal file
22
compiler/test/codegeneration/TestVarious.kt
Normal 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
|
||||
}
|
||||
})
|
@ -327,4 +327,19 @@ main {
|
||||
instructions.size shouldBe 18
|
||||
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
|
||||
}
|
||||
})
|
@ -24,7 +24,7 @@ compileTestKotlin {
|
||||
dependencies {
|
||||
implementation project(':codeCore')
|
||||
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 project(':parser')
|
||||
}
|
||||
|
@ -532,8 +532,7 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
||||
} else false
|
||||
}
|
||||
ident != null -> {
|
||||
val decl = ident.targetVarDecl(definingModule.program) ?:
|
||||
throw FatalAstException("invalid identifier ${ident.nameInSource}")
|
||||
val decl = ident.targetVarDecl(definingModule.program) ?: throw FatalAstException("invalid identifier ${ident.nameInSource}")
|
||||
return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteral)
|
||||
machine.isIOAddress((decl.value as NumericLiteral).number.toUInt())
|
||||
else
|
||||
|
@ -3,6 +3,11 @@ TODO
|
||||
|
||||
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
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
@ -5,37 +5,44 @@ main {
|
||||
sub start() {
|
||||
gfx2.screen_mode(4)
|
||||
|
||||
uword[128] xx
|
||||
uword[128] yy
|
||||
uword[128] flakes1_xx
|
||||
uword[128] flakes1_yy
|
||||
uword[128] flakes2_xx
|
||||
uword[128] flakes2_yy
|
||||
|
||||
ubyte @zp idx
|
||||
for idx in 0 to 127 {
|
||||
xx[idx] = math.rndw() % 320
|
||||
yy[idx] = math.rndw() % 240
|
||||
flakes1_xx[idx] = math.rndw() % 320
|
||||
flakes1_yy[idx] = math.rndw() % 240
|
||||
flakes2_xx[idx] = math.rndw() % 320
|
||||
flakes2_yy[idx] = math.rndw() % 240
|
||||
}
|
||||
|
||||
gfx2.text(96, 90, 2, sc:"**************")
|
||||
gfx2.text(104, 100, 5, sc:"let it snow!")
|
||||
gfx2.text(96, 110, 2, sc:"**************")
|
||||
draw_screen()
|
||||
|
||||
const ubyte FALLING_SNOW_COLOR = 1
|
||||
const ubyte FALLING_SNOW_COLOR2 = 12
|
||||
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 {
|
||||
sys.waitvsync()
|
||||
for idx in 0 to 127 {
|
||||
gfx2.plot(xx[idx], yy[idx], FALLING_SNOW_COLOR)
|
||||
for idx in 0 to len(flakes1_xx)-1 {
|
||||
gfx2.plot(flakes1_xx[idx], flakes1_yy[idx], FALLING_SNOW_COLOR)
|
||||
gfx2.plot(flakes2_xx[idx], flakes2_yy[idx], FALLING_SNOW_COLOR2)
|
||||
}
|
||||
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
|
||||
gfx2.plot(xx[idx], yy[idx], PILED_SNOW_COLOR)
|
||||
yy[idx] = 0
|
||||
xx[idx] = math.rndw() % 320
|
||||
} else if gfx2.pget(xx[idx], yy[idx]+1)==PILED_SNOW_COLOR {
|
||||
gfx2.plot(flakes1_xx[idx], flakes1_yy[idx], PILED_SNOW_COLOR)
|
||||
flakes1_yy[idx] = 0
|
||||
flakes1_xx[idx] = math.rndw() % 320
|
||||
} else if gfx2.pget(flakes1_xx[idx], flakes1_yy[idx]+1) in PILED_SNOW_COLORS {
|
||||
; 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
|
||||
uword pilex1
|
||||
uword pilex2
|
||||
@ -46,30 +53,63 @@ main {
|
||||
pilex1 = snowx+1
|
||||
pilex2 = snowx-1
|
||||
}
|
||||
if gfx2.pget(pilex1, yy[idx]+1)==0 {
|
||||
gfx2.plot(snowx, yy[idx], 0)
|
||||
gfx2.plot(pilex1, yy[idx]+1, PILED_SNOW_COLOR)
|
||||
} else if gfx2.pget(pilex2, yy[idx]+1)==0 {
|
||||
gfx2.plot(snowx, yy[idx], 0)
|
||||
gfx2.plot(pilex2, yy[idx]+1, PILED_SNOW_COLOR)
|
||||
ubyte pixel_side1 = gfx2.pget(pilex1, flakes1_yy[idx]+1)
|
||||
ubyte pixel_side2 = gfx2.pget(pilex2, flakes1_yy[idx]+1)
|
||||
if pixel_side1 == 0 or pixel_side1 == FALLING_SNOW_COLOR2 {
|
||||
gfx2.plot(snowx, flakes1_yy[idx], 0)
|
||||
gfx2.plot(pilex1, flakes1_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 {
|
||||
gfx2.plot(snowx, yy[idx], PILED_SNOW_COLOR)
|
||||
gfx2.plot(snowx, flakes1_yy[idx], PILED_SNOW_COLOR)
|
||||
}
|
||||
}
|
||||
yy[idx] = 0
|
||||
xx[idx] = math.rndw() % 320
|
||||
flakes1_yy[idx] = 0
|
||||
flakes1_xx[idx] = math.rndw() % 320
|
||||
} else {
|
||||
; fall
|
||||
gfx2.plot(xx[idx], yy[idx], 0)
|
||||
yy[idx]++
|
||||
gfx2.plot(flakes1_xx[idx], flakes1_yy[idx], 0)
|
||||
flakes1_yy[idx]++
|
||||
when math.rnd() & 3 {
|
||||
1 -> {
|
||||
if xx[idx]
|
||||
xx[idx]--
|
||||
if flakes1_xx[idx]
|
||||
flakes1_xx[idx]--
|
||||
}
|
||||
2 -> {
|
||||
if xx[idx] < 319
|
||||
xx[idx]++
|
||||
if flakes1_xx[idx] < 319
|
||||
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 {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,6 +170,7 @@ 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
|
||||
| left = expression EOL? bop = 'in' EOL? right = expression
|
||||
| left = expression EOL? bop = 'not in' EOL? right = expression
|
||||
| prefix = 'not' expression
|
||||
| left = expression EOL? bop = 'and' EOL? right = expression
|
||||
| left = expression EOL? bop = 'or' EOL? right = expression
|
||||
|
@ -10,8 +10,8 @@ java {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
antlr 'org.antlr:antlr4:4.10.1'
|
||||
implementation 'org.antlr:antlr4-runtime:4.10.1'
|
||||
antlr 'org.antlr:antlr4:4.11.1'
|
||||
implementation 'org.antlr:antlr4-runtime:4.11.1'
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
|
Reference in New Issue
Block a user