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">
<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 />

View File

@ -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) {

View File

@ -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)
}
}
}
}

View File

@ -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'

View File

@ -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()

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.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))

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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 -> {}
}

View File

@ -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
}
})

View File

@ -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

View File

@ -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
}
})

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.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 {
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')
}

View File

@ -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

View File

@ -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
...

View File

@ -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
}
}
}

View File

@ -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

View File

@ -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 {