do..until condition can now refer to variables defined in the loop's inner scope.

This commit is contained in:
Irmen de Jong 2020-09-24 19:26:07 +02:00
parent e1d0dbed0c
commit f5db31b8ff
11 changed files with 318 additions and 85 deletions

View File

@ -338,7 +338,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
output("do ") output("do ")
untilLoop.body.accept(this) untilLoop.body.accept(this)
output(" until ") output(" until ")
untilLoop.untilCondition.accept(this) untilLoop.condition.accept(this)
} }
override fun visit(returnStmt: Return) { override fun visit(returnStmt: Return) {

View File

@ -334,8 +334,8 @@ internal class AstChecker(private val program: Program,
} }
override fun visit(untilLoop: UntilLoop) { override fun visit(untilLoop: UntilLoop) {
if(untilLoop.untilCondition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes) if(untilLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
errors.err("condition value should be an integer type", untilLoop.untilCondition.position) errors.err("condition value should be an integer type", untilLoop.condition.position)
super.visit(untilLoop) super.visit(untilLoop)
} }

View File

@ -348,7 +348,7 @@ abstract class AstWalker {
fun visit(untilLoop: UntilLoop, parent: Node) { fun visit(untilLoop: UntilLoop, parent: Node) {
track(before(untilLoop, parent), untilLoop, parent) track(before(untilLoop, parent), untilLoop, parent)
untilLoop.untilCondition.accept(this, untilLoop) untilLoop.condition.accept(this, untilLoop)
untilLoop.body.accept(this, untilLoop) untilLoop.body.accept(this, untilLoop)
track(after(untilLoop, parent), untilLoop, parent) track(after(untilLoop, parent), untilLoop, parent)
} }

View File

@ -115,7 +115,7 @@ interface IAstVisitor {
} }
fun visit(untilLoop: UntilLoop) { fun visit(untilLoop: UntilLoop) {
untilLoop.untilCondition.accept(this) untilLoop.condition.accept(this)
untilLoop.body.accept(this) untilLoop.body.accept(this)
} }

View File

@ -815,19 +815,19 @@ class RepeatLoop(var iterations: Expression?, var body: AnonymousScope, override
} }
class UntilLoop(var body: AnonymousScope, class UntilLoop(var body: AnonymousScope,
var untilCondition: Expression, var condition: Expression,
override val position: Position) : Statement() { override val position: Position) : Statement() {
override lateinit var parent: Node override lateinit var parent: Node
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
untilCondition.linkParents(this) condition.linkParents(this)
body.linkParents(this) body.linkParents(this)
} }
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
when { when {
node===untilCondition -> untilCondition = replacement as Expression node===condition -> condition = replacement as Expression
node===body -> body = replacement as AnonymousScope node===body -> body = replacement as AnonymousScope
else -> throw FatalAstException("invalid replace") else -> throw FatalAstException("invalid replace")
} }

View File

@ -164,4 +164,34 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
return noModifications return noModifications
} }
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
val binExpr = ifStatement.condition as? BinaryExpression
if(binExpr==null || binExpr.operator !in comparisonOperators) {
// if x -> if x!=0, if x+5 -> if x+5 != 0
val booleanExpr = BinaryExpression(ifStatement.condition, "!=", NumericLiteralValue.optimalInteger(0, ifStatement.condition.position), ifStatement.condition.position)
return listOf(IAstModification.ReplaceNode(ifStatement.condition, booleanExpr, ifStatement))
}
return noModifications
}
override fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
val binExpr = untilLoop.condition as? BinaryExpression
if(binExpr==null || binExpr.operator !in comparisonOperators) {
// until x -> until x!=0, until x+5 -> until x+5 != 0
val booleanExpr = BinaryExpression(untilLoop.condition, "!=", NumericLiteralValue.optimalInteger(0, untilLoop.condition.position), untilLoop.condition.position)
return listOf(IAstModification.ReplaceNode(untilLoop.condition, booleanExpr, untilLoop))
}
return noModifications
}
override fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
val binExpr = whileLoop.condition as? BinaryExpression
if(binExpr==null || binExpr.operator !in comparisonOperators) {
// while x -> while x!=0, while x+5 -> while x+5 != 0
val booleanExpr = BinaryExpression(whileLoop.condition, "!=", NumericLiteralValue.optimalInteger(0, whileLoop.condition.position), whileLoop.condition.position)
return listOf(IAstModification.ReplaceNode(whileLoop.condition, booleanExpr, whileLoop))
}
return noModifications
}
} }

View File

@ -845,47 +845,32 @@ $save .byte 0
} }
private fun translate(stmt: IfStatement) { private fun translate(stmt: IfStatement) {
when { checkBooleanExpression(stmt.condition) // we require the condition to be of the form 'x <comparison> <value>'
stmt.elsepart.containsNoCodeNorVars() -> { val booleanCondition = stmt.condition as BinaryExpression
// empty else
expressionsAsmGen.translateExpression(stmt.condition) if (stmt.elsepart.containsNoCodeNorVars()) {
translateTestStack(stmt.condition.inferType(program).typeOrElse(DataType.STRUCT)) // empty else
val endLabel = makeLabel("if_end") val endLabel = makeLabel("if_end")
out(" beq $endLabel") expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, endLabel)
translate(stmt.truepart) translate(stmt.truepart)
out(endLabel) out(endLabel)
} }
stmt.truepart.containsNoCodeNorVars() -> { else {
// empty true part // both true and else parts
expressionsAsmGen.translateExpression(stmt.condition) val elseLabel = makeLabel("if_else")
translateTestStack(stmt.condition.inferType(program).typeOrElse(DataType.STRUCT)) val endLabel = makeLabel("if_end")
val endLabel = makeLabel("if_end") expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, elseLabel)
out(" bne $endLabel") translate(stmt.truepart)
translate(stmt.elsepart) out(" jmp $endLabel")
out(endLabel) out(elseLabel)
} translate(stmt.elsepart)
else -> { out(endLabel)
expressionsAsmGen.translateExpression(stmt.condition)
translateTestStack(stmt.condition.inferType(program).typeOrElse(DataType.STRUCT))
val elseLabel = makeLabel("if_else")
val endLabel = makeLabel("if_end")
out(" beq $elseLabel")
translate(stmt.truepart)
out(" jmp $endLabel")
out(elseLabel)
translate(stmt.elsepart)
out(endLabel)
}
} }
} }
private fun translateTestStack(dataType: DataType) { private fun checkBooleanExpression(condition: Expression) {
when(dataType) { if(condition !is BinaryExpression || condition.operator !in comparisonOperators)
in ByteDatatypes -> out(" inx | lda P8ESTACK_LO,x") throw AssemblyError("expected boolean expression $condition")
in WordDatatypes -> out(" inx | lda P8ESTACK_LO,x | ora P8ESTACK_HI,x")
DataType.FLOAT -> throw AssemblyError("conditional value should be an integer (boolean)")
else -> throw AssemblyError("non-numerical dt")
}
} }
private fun translate(stmt: RepeatLoop) { private fun translate(stmt: RepeatLoop) {
@ -1004,25 +989,13 @@ $counterVar .byte 0""")
} }
private fun translate(stmt: WhileLoop) { private fun translate(stmt: WhileLoop) {
checkBooleanExpression(stmt.condition) // we require the condition to be of the form 'x <comparison> <value>'
val booleanCondition = stmt.condition as BinaryExpression
val whileLabel = makeLabel("while") val whileLabel = makeLabel("while")
val endLabel = makeLabel("whileend") val endLabel = makeLabel("whileend")
loopEndLabels.push(endLabel) loopEndLabels.push(endLabel)
out(whileLabel) out(whileLabel)
expressionsAsmGen.translateExpression(stmt.condition) expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, endLabel)
val conditionDt = stmt.condition.inferType(program)
if(!conditionDt.isKnown)
throw AssemblyError("unknown condition dt")
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes) {
out(" inx | lda P8ESTACK_LO,x | beq $endLabel")
} else {
out("""
inx
lda P8ESTACK_LO,x
bne +
lda P8ESTACK_HI,x
beq $endLabel
+ """)
}
translate(stmt.body) translate(stmt.body)
out(" jmp $whileLabel") out(" jmp $whileLabel")
out(endLabel) out(endLabel)
@ -1030,26 +1003,14 @@ $counterVar .byte 0""")
} }
private fun translate(stmt: UntilLoop) { private fun translate(stmt: UntilLoop) {
checkBooleanExpression(stmt.condition) // we require the condition to be of the form 'x <comparison> <value>'
val booleanCondition = stmt.condition as BinaryExpression
val repeatLabel = makeLabel("repeat") val repeatLabel = makeLabel("repeat")
val endLabel = makeLabel("repeatend") val endLabel = makeLabel("repeatend")
loopEndLabels.push(endLabel) loopEndLabels.push(endLabel)
out(repeatLabel) out(repeatLabel)
translate(stmt.body) translate(stmt.body)
expressionsAsmGen.translateExpression(stmt.untilCondition) expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, repeatLabel)
val conditionDt = stmt.untilCondition.inferType(program)
if(!conditionDt.isKnown)
throw AssemblyError("unknown condition dt")
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes) {
out(" inx | lda P8ESTACK_LO,x | beq $repeatLabel")
} else {
out("""
inx
lda P8ESTACK_LO,x
bne +
lda P8ESTACK_HI,x
beq $repeatLabel
+ """)
}
out(endLabel) out(endLabel)
loopEndLabels.pop() loopEndLabels.pop()
} }

View File

@ -28,6 +28,220 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
} }
} }
internal fun translateComparisonExpressionWithJumpIfFalse(expr: BinaryExpression, jumpIfFalseLabel: String) {
// first, if it is of the form: <constvalue> <comparison> X , swap the operands around.
var left = expr.left
var right = expr.right
var operator = expr.operator
var leftConstVal = left.constValue(program)
var rightConstVal = right.constValue(program)
if(leftConstVal!=null) {
val tmp = left
left = right
right = tmp
val tmp2 = leftConstVal
leftConstVal = rightConstVal
rightConstVal = tmp2
when(expr.operator) {
"<" -> operator = ">"
"<=" -> operator = ">="
">" -> operator = "<"
">=" -> operator = "<="
}
}
val dt = left.inferType(program).typeOrElse(DataType.STRUCT)
when (operator) {
"==" -> {
when (dt) {
in ByteDatatypes -> {
translateByteEquals(left, right, leftConstVal, rightConstVal)
asmgen.out(" bne $jumpIfFalseLabel")
}
in WordDatatypes -> {
translateWordEquals(left, right, leftConstVal, rightConstVal)
asmgen.out(" bne $jumpIfFalseLabel")
}
DataType.FLOAT -> {
TODO("float eq.")
}
else -> throw AssemblyError("weird operand datatype")
}
}
"!=" -> {
when (dt) {
in ByteDatatypes -> {
translateByteEquals(left, right, leftConstVal, rightConstVal)
asmgen.out(" beq $jumpIfFalseLabel")
}
in WordDatatypes -> {
translateWordEquals(left, right, leftConstVal, rightConstVal)
asmgen.out(" beq $jumpIfFalseLabel")
}
DataType.FLOAT -> {
TODO("float neq.")
}
else -> throw AssemblyError("weird operand datatype")
}
}
"<" -> {
when(dt) {
DataType.UBYTE -> translateUbyteLess(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.BYTE -> translateByteLess(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.UWORD -> translateUwordLess(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.WORD -> translateWordLess(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.FLOAT -> TODO("float l.")
else -> throw AssemblyError("weird operand datatype")
}
}
"<=" -> {
when(dt) {
DataType.UBYTE -> translateUbyteLessOrEqual(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.BYTE -> translateByteLessOrEqual(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.UWORD -> translateUwordLessOrEqual(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.WORD -> translateWordLessOrEqual(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.FLOAT -> TODO("float l.e.")
else -> throw AssemblyError("weird operand datatype")
}
}
">" -> {
when(dt) {
DataType.UBYTE -> translateUbyteGreater(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.BYTE -> translateByteGreater(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.UWORD -> translateUwordGreater(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.WORD -> translateWordGreater(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.FLOAT -> TODO("float g.")
else -> throw AssemblyError("weird operand datatype")
}
}
">=" -> {
when(dt) {
DataType.UBYTE -> translateUbyteGreaterOrEqual(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.BYTE -> translateByteGreaterOrEqual(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.UWORD -> translateUwordGreaterOrEqual(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.WORD -> translateWordGreaterOrEqual(left, operator, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.FLOAT -> TODO("float g.e.")
else -> throw AssemblyError("weird operand datatype")
}
}
}
}
private fun translateUbyteLess(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateByteLess(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateUwordLess(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateWordLess(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateUbyteLessOrEqual(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateByteLessOrEqual(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateUwordLessOrEqual(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateWordLessOrEqual(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateUbyteGreater(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateByteGreater(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateUwordGreater(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateWordGreater(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateUbyteGreaterOrEqual(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateByteGreaterOrEqual(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateUwordGreaterOrEqual(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateWordGreaterOrEqual(left: Expression, operator: String, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
TODO("Not yet implemented")
}
private fun translateByteEquals(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal==leftConstVal)
asmgen.out(" lda #0 | sec")
else
asmgen.out(" lda #1 | sec")
return
} else {
when(left) {
is IdentifierReference -> {
val name = asmgen.asmVariableName(left)
asmgen.out(" lda $name | cmp #${rightConstVal.number}")
return
}
// TODO optimize direct mem read here too
else -> TODO()
}
}
}
TODO("Not yet implemented")
}
private fun translateWordEquals(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal==leftConstVal)
asmgen.out(" lda #0 | sec")
else
asmgen.out(" lda #1 | sec")
} else {
when(left) {
is IdentifierReference -> {
val name = asmgen.asmVariableName(left)
asmgen.out("""
lda $name
cmp #<${rightConstVal.number}
bne +
lda $name+1
cmp #>${rightConstVal.number}
+""")
return
}
// TODO optimize direct mem read here too
else -> TODO()
}
}
}
TODO("Not yet implemented")
}
private fun translateExpression(expression: FunctionCall) { private fun translateExpression(expression: FunctionCall) {
val functionName = expression.target.nameInSource.last() val functionName = expression.target.nameInSource.last()
val builtinFunc = BuiltinFunctions[functionName] val builtinFunc = BuiltinFunctions[functionName]
@ -100,7 +314,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
DataType.UBYTE, DataType.BYTE -> {} DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> { DataType.UWORD, DataType.WORD -> {
// sign extend // sign extend
asmgen.out(""" asmgen.out("""
lda P8ESTACK_LO+1,x lda P8ESTACK_LO+1,x
ora #$7f ora #$7f
bmi + bmi +

View File

@ -249,11 +249,11 @@ internal class StatementOptimizer(private val program: Program,
} }
override fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> { override fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
val constvalue = untilLoop.untilCondition.constValue(program) val constvalue = untilLoop.condition.constValue(program)
if(constvalue!=null) { if(constvalue!=null) {
if(constvalue.asBooleanValue) { if(constvalue.asBooleanValue) {
// always true -> keep only the statement block (if there are no break statements) // always true -> keep only the statement block (if there are no break statements)
errors.warn("condition is always true", untilLoop.untilCondition.position) errors.warn("condition is always true", untilLoop.condition.position)
if(!hasBreak(untilLoop.body)) if(!hasBreak(untilLoop.body))
return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent)) return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent))
} else { } else {

View File

@ -4,9 +4,9 @@ TODO
- get rid of all other TODO's in the code ;-) - get rid of all other TODO's in the code ;-)
- line-circle-gfx examples are now a few hundred bytes larger than before (~4.0/4.1 version i think?). Why is that, can it be fixed? - line-circle-gfx examples are now a few hundred bytes larger than before (~4.0/4.1 version i think?). Why is that, can it be fixed?
- add support? example? for processing arguments to a sys call : sys 999, 1, 2, "aaa"
- make it possible for array literals to not only contain compile time constants - make it possible for array literals to not only contain compile time constants
- further optimize assignment codegeneration - further optimize assignment codegeneration
- further optimize expression codegeneration, most notably comparisons
- implement @stack for asmsub parameters - implement @stack for asmsub parameters
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_' - make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging) - option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)

View File

@ -4,15 +4,43 @@
%zeropage basicsafe %zeropage basicsafe
main $0900{ main {
sub start() { sub start() {
ubyte x= 1 word x= 1
do {
ubyte v = x if x==10 {
x=99
} else {
x=100
}
if x==10 {
;nothing
} else {
x=100
}
if x==10 {
x=99
} else {
; nothing
}
while 1==x {
txt.print_w(x)
txt.chrout('\n')
x++ x++
} until v==0 }
txt.chrout('\n')
x=0
do {
x++
txt.print_w(x)
txt.chrout('\n')
} until x==10
; @($c000) *= 99 ; TODO implement ; @($c000) *= 99 ; TODO implement