introduced repeat loop. repeat-until changed to do-util.

forever loop is gone (use repeat without iteration count).
struct literal is now same as array literal [...] to avoid parsing ambiguity with scope blocks.
This commit is contained in:
Irmen de Jong 2020-07-25 16:25:02 +02:00
parent b0e8738ab8
commit c38508c262
35 changed files with 274 additions and 226 deletions

View File

@ -324,16 +324,18 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
whileLoop.body.accept(this)
}
override fun visit(foreverLoop: ForeverLoop) {
output("forever ")
foreverLoop.body.accept(this)
}
override fun visit(repeatLoop: RepeatLoop) {
output("repeat ")
repeatLoop.iterations?.accept(this)
output(" ")
repeatLoop.body.accept(this)
}
override fun visit(untilLoop: UntilLoop) {
output("do ")
untilLoop.body.accept(this)
output(" until ")
repeatLoop.untilCondition.accept(this)
untilLoop.untilCondition.accept(this)
}
override fun visit(returnStmt: Return) {
@ -425,10 +427,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
outputln("")
}
override fun visit(structLv: StructLiteralValue) {
outputListMembers(structLv.values.asSequence(), '{', '}')
}
override fun visit(nopStatement: NopStatement) {
output("; NOP @ ${nopStatement.position} $nopStatement")
}

View File

@ -57,7 +57,7 @@ interface INameScope {
when(stmt) {
// NOTE: if other nodes are introduced that are a scope, or contain subscopes, they must be added here!
is ForLoop -> if(stmt.body.name==name) return stmt.body
is RepeatLoop -> if(stmt.body.name==name) return stmt.body
is UntilLoop -> if(stmt.body.name==name) return stmt.body
is WhileLoop -> if(stmt.body.name==name) return stmt.body
is BranchStatement -> {
if(stmt.truepart.name==name) return stmt.truepart
@ -175,8 +175,8 @@ interface INameScope {
find(it.truepart)
find(it.elsepart)
}
is UntilLoop -> find(it.body)
is RepeatLoop -> find(it.body)
is ForeverLoop -> find(it.body)
is WhileLoop -> find(it.body)
is WhenStatement -> it.choices.forEach { choice->find(choice.statements) }
else -> { /* do nothing */ }

View File

@ -205,14 +205,14 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
val forloop = forloop()?.toAst()
if(forloop!=null) return forloop
val repeatloop = repeatloop()?.toAst()
if(repeatloop!=null) return repeatloop
val untilloop = untilloop()?.toAst()
if(untilloop!=null) return untilloop
val whileloop = whileloop()?.toAst()
if(whileloop!=null) return whileloop
val foreverloop = foreverloop()?.toAst()
if(foreverloop!=null) return foreverloop
val repeatloop = repeatloop()?.toAst()
if(repeatloop!=null) return repeatloop
val breakstmt = breakstmt()?.toAst()
if(breakstmt!=null) return breakstmt
@ -487,10 +487,6 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
// the ConstantFold takes care of that and converts the type if needed.
ArrayLiteralValue(InferredTypes.InferredType.unknown(), array, position = litval.toPosition())
}
litval.structliteral()!=null -> {
val values = litval.structliteral().expression().map { it.toAst() }
StructLiteralValue(values, litval.toPosition())
}
else -> throw FatalAstException("invalid parsed literal")
}
}
@ -609,19 +605,20 @@ private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
return WhileLoop(condition, scope, toPosition())
}
private fun prog8Parser.ForeverloopContext.toAst(): ForeverLoop {
private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
val iterations = expression()?.toAst()
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
val scope = AnonymousScope(statements, statement_block()?.toPosition()
?: statement().toPosition())
return ForeverLoop(scope, toPosition())
return RepeatLoop(iterations, scope, toPosition())
}
private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
private fun prog8Parser.UntilloopContext.toAst(): UntilLoop {
val untilCondition = expression().toAst()
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
val scope = AnonymousScope(statements, statement_block()?.toPosition()
?: statement().toPosition())
return RepeatLoop(scope, untilCondition, toPosition())
return UntilLoop(scope, untilCondition, toPosition())
}
private fun prog8Parser.WhenstmtContext.toAst(): WhenStatement {

View File

@ -455,31 +455,6 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
}
}
class StructLiteralValue(var values: List<Expression>,
override val position: Position): Expression() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent=parent
values.forEach { it.linkParents(this) }
}
override fun replaceChildNode(node: Node, replacement: Node) {
throw FatalAstException("can't replace here")
}
override fun constValue(program: Program): NumericLiteralValue? = null
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifiers(vararg name: String) = values.any { it.referencesIdentifiers(*name) }
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.STRUCT)
override fun toString(): String {
return "struct{ ${values.joinToString(", ")} }"
}
}
private var heapIdSequence = 0 // unique ids for strings and arrays "on the heap"
class StringLiteralValue(val value: String,

View File

@ -316,10 +316,10 @@ internal class AstChecker(private val program: Program,
visitStatements(subroutine.statements)
}
override fun visit(repeatLoop: RepeatLoop) {
if(repeatLoop.untilCondition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
errors.err("condition value should be an integer type", repeatLoop.untilCondition.position)
super.visit(repeatLoop)
override fun visit(untilLoop: UntilLoop) {
if(untilLoop.untilCondition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
errors.err("condition value should be an integer type", untilLoop.untilCondition.position)
super.visit(untilLoop)
}
override fun visit(whileLoop: WhileLoop) {
@ -348,9 +348,9 @@ internal class AstChecker(private val program: Program,
if(targetIdent!=null) {
val targetVar = targetIdent.targetVarDecl(program.namespace)
if(targetVar?.struct != null) {
val sourceStructLv = assignment.value as? StructLiteralValue
val sourceStructLv = assignment.value as? ArrayLiteralValue
if (sourceStructLv != null) {
if (sourceStructLv.values.size != targetVar.struct?.numberOfElements)
if (sourceStructLv.value.size != targetVar.struct?.numberOfElements)
errors.err("number of elements doesn't match struct definition", sourceStructLv.position)
} else {
val sourceIdent = assignment.value as? IdentifierReference
@ -503,21 +503,14 @@ internal class AstChecker(private val program: Program,
checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteralValue)
}
is ArrayLiteralValue -> {
val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
checkValueTypeAndRangeArray(decl.datatype, decl.struct, arraySpec, decl.value as ArrayLiteralValue)
}
is NumericLiteralValue -> {
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
}
is StructLiteralValue -> {
if(decl.datatype==DataType.STRUCT) {
val struct = decl.struct!!
val structLv = decl.value as StructLiteralValue
if(struct.numberOfElements != structLv.values.size) {
val structLv = decl.value as ArrayLiteralValue
if(struct.numberOfElements != structLv.value.size) {
errors.err("struct value has incorrect number of elements", structLv.position)
return
}
for(value in structLv.values.zip(struct.statements)) {
for(value in structLv.value.zip(struct.statements)) {
val memberdecl = value.second as VarDecl
val constValue = value.first.constValue(program)
if(constValue==null) {
@ -531,9 +524,13 @@ internal class AstChecker(private val program: Program,
}
}
} else {
errors.err("struct literal is wrong type to initialize this variable", decl.value!!.position)
val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
checkValueTypeAndRangeArray(decl.datatype, decl.struct, arraySpec, decl.value as ArrayLiteralValue)
}
}
is NumericLiteralValue -> {
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
}
else -> {
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!.javaClass.simpleName}")
super.visit(decl)
@ -570,8 +567,18 @@ internal class AstChecker(private val program: Program,
}
val declValue = decl.value
if(declValue!=null && decl.type==VarDeclType.VAR && !declValue.inferType(program).istype(decl.datatype))
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
if(declValue!=null && decl.type==VarDeclType.VAR) {
if(decl.datatype==DataType.STRUCT) {
val valueIdt = declValue.inferType(program)
if(valueIdt.isUnknown)
throw AstException("invalid value type")
val valueDt = valueIdt.typeOrElse(DataType.STRUCT)
if(valueDt !in ArrayDatatypes)
err("initialisation of struct should be with array value", declValue.position)
} else if (!declValue.inferType(program).istype(decl.datatype)) {
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
}
}
super.visit(decl)
}
@ -1278,8 +1285,8 @@ internal class AstChecker(private val program: Program,
DataType.STR -> sourceDatatype== DataType.STR
DataType.STRUCT -> {
if(sourceDatatype==DataType.STRUCT) {
val structLv = sourceValue as StructLiteralValue
val numValues = structLv.values.size
val structLv = sourceValue as ArrayLiteralValue
val numValues = structLv.value.size
val targetstruct = target.identifier!!.targetVarDecl(program.namespace)!!.struct!!
return targetstruct.numberOfElements == numValues
}

View File

@ -57,8 +57,8 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
return super.visit(decl)
}
if (decl.value != null && decl.value !is StructLiteralValue) {
errors.err("initializing requires struct literal value", decl.value?.position ?: decl.position)
if (decl.value != null && decl.value !is ArrayLiteralValue) {
errors.err("initializing a struct requires array literal value", decl.value?.position ?: decl.position)
return super.visit(decl)
}
}

View File

@ -94,7 +94,7 @@ abstract class AstWalker {
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(foreverLoop: ForeverLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
@ -110,12 +110,11 @@ abstract class AstWalker {
open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
@ -137,7 +136,7 @@ abstract class AstWalker {
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(foreverLoop: ForeverLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
@ -153,12 +152,11 @@ abstract class AstWalker {
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
@ -336,19 +334,20 @@ abstract class AstWalker {
track(after(whileLoop, parent), whileLoop, parent)
}
fun visit(foreverLoop: ForeverLoop, parent: Node) {
track(before(foreverLoop, parent), foreverLoop, parent)
foreverLoop.body.accept(this, foreverLoop)
track(after(foreverLoop, parent), foreverLoop, parent)
}
fun visit(repeatLoop: RepeatLoop, parent: Node) {
track(before(repeatLoop, parent), repeatLoop, parent)
repeatLoop.untilCondition.accept(this, repeatLoop)
repeatLoop.iterations?.accept(this, repeatLoop)
repeatLoop.body.accept(this, repeatLoop)
track(after(repeatLoop, parent), repeatLoop, parent)
}
fun visit(untilLoop: UntilLoop, parent: Node) {
track(before(untilLoop, parent), untilLoop, parent)
untilLoop.untilCondition.accept(this, untilLoop)
untilLoop.body.accept(this, untilLoop)
track(after(untilLoop, parent), untilLoop, parent)
}
fun visit(returnStmt: Return, parent: Node) {
track(before(returnStmt, parent), returnStmt, parent)
returnStmt.value?.accept(this, returnStmt)
@ -434,11 +433,5 @@ abstract class AstWalker {
structDecl.statements.forEach { it.accept(this, structDecl) }
track(after(structDecl, parent), structDecl, parent)
}
fun visit(structLv: StructLiteralValue, parent: Node) {
track(before(structLv, parent), structLv, parent)
structLv.values.forEach { it.accept(this, structLv) }
track(after(structLv, parent), structLv, parent)
}
}

View File

@ -112,13 +112,14 @@ interface IAstVisitor {
whileLoop.body.accept(this)
}
fun visit(foreverLoop: ForeverLoop) {
foreverLoop.body.accept(this)
fun visit(repeatLoop: RepeatLoop) {
repeatLoop.iterations?.accept(this)
repeatLoop.body.accept(this)
}
fun visit(repeatLoop: RepeatLoop) {
repeatLoop.untilCondition.accept(this)
repeatLoop.body.accept(this)
fun visit(untilLoop: UntilLoop) {
untilLoop.untilCondition.accept(this)
untilLoop.body.accept(this)
}
fun visit(returnStmt: Return) {
@ -178,8 +179,4 @@ interface IAstVisitor {
fun visit(structDecl: StructDecl) {
structDecl.statements.forEach { it.accept(this) }
}
fun visit(structLv: StructLiteralValue) {
structLv.values.forEach { it.accept(this) }
}
}

View File

@ -106,8 +106,8 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
val valueType = assignment.value.inferType(program)
val targetType = assignment.target.inferType(program, assignment)
if(valueType.istype(DataType.STRUCT) && targetType.istype(DataType.STRUCT)) {
val assignments = if (assignment.value is StructLiteralValue) {
flattenStructAssignmentFromStructLiteral(assignment, program) // 'structvar = { ..... } '
val assignments = if (assignment.value is ArrayLiteralValue) {
flattenStructAssignmentFromStructLiteral(assignment, program) // 'structvar = [ ..... ] '
} else {
flattenStructAssignmentFromIdentifier(assignment, program) // 'structvar1 = structvar2'
}
@ -128,11 +128,11 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
val targetVar = identifier.targetVarDecl(program.namespace)!!
val struct = targetVar.struct!!
val slv = structAssignment.value as? StructLiteralValue
if(slv==null || slv.values.size != struct.numberOfElements)
val slv = structAssignment.value as? ArrayLiteralValue
if(slv==null || slv.value.size != struct.numberOfElements)
throw FatalAstException("element count mismatch")
return struct.statements.zip(slv.values).map { (targetDecl, sourceValue) ->
return struct.statements.zip(slv.value).map { (targetDecl, sourceValue) ->
targetDecl as VarDecl
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
@ -174,7 +174,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
assign
}
}
is StructLiteralValue -> {
is ArrayLiteralValue -> {
throw IllegalArgumentException("not going to flatten a structLv assignment here")
}
else -> throw FatalAstException("strange struct value")

View File

@ -179,51 +179,6 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
return noModifications
}
override fun after(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> {
// assignment of a struct literal value, some member values may need proper typecast
fun addTypecastsIfNeeded(struct: StructDecl): Iterable<IAstModification> {
val newValues = struct.statements.zip(structLv.values).map { (structMemberDecl, memberValue) ->
val memberDt = (structMemberDecl as VarDecl).datatype
val valueDt = memberValue.inferType(program)
if (valueDt.typeOrElse(memberDt) != memberDt)
TypecastExpression(memberValue, memberDt, true, memberValue.position)
else
memberValue
}
class StructLvValueReplacer(val targetStructLv: StructLiteralValue, val typecastValues: List<Expression>) : IAstModification {
override fun perform() {
targetStructLv.values = typecastValues
typecastValues.forEach { it.linkParents(targetStructLv) }
}
}
return if(structLv.values.zip(newValues).any { (v1, v2) -> v1 !== v2})
listOf(StructLvValueReplacer(structLv, newValues))
else
emptyList()
}
val decl = structLv.parent as? VarDecl
if(decl != null) {
val struct = decl.struct
if(struct != null)
return addTypecastsIfNeeded(struct)
} else {
val assign = structLv.parent as? Assignment
if (assign != null) {
val decl2 = assign.target.identifier?.targetVarDecl(program.namespace)
if(decl2 != null) {
val struct = decl2.struct
if(struct != null)
return addTypecastsIfNeeded(struct)
}
}
}
return noModifications
}
override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> {
// add a typecast to the return type if it doesn't match the subroutine's signature
val returnValue = returnStmt.value

View File

@ -270,7 +270,7 @@ open class VarDecl(val type: VarDeclType,
fun flattenStructMembers(): MutableList<Statement> {
val result = struct!!.statements.withIndex().map {
val member = it.value as VarDecl
val initvalue = if(value!=null) (value as StructLiteralValue).values[it.index] else null
val initvalue = if(value!=null) (value as ArrayLiteralValue).value[it.index] else null
VarDecl(
VarDeclType.VAR,
member.datatype,
@ -780,17 +780,21 @@ class WhileLoop(var condition: Expression,
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
}
class ForeverLoop(var body: AnonymousScope, override val position: Position) : Statement() {
class RepeatLoop(var iterations: Expression?, var body: AnonymousScope, override val position: Position) : Statement() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
iterations?.linkParents(this)
body.linkParents(this)
}
override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is AnonymousScope && node===body)
body = replacement
when {
node===iterations -> iterations = replacement as Expression
node===body -> body = replacement as AnonymousScope
else -> throw FatalAstException("invalid replace")
}
replacement.parent = this
}
@ -798,9 +802,9 @@ class ForeverLoop(var body: AnonymousScope, override val position: Position) : S
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
}
class RepeatLoop(var body: AnonymousScope,
var untilCondition: Expression,
override val position: Position) : Statement() {
class UntilLoop(var body: AnonymousScope,
var untilCondition: Expression,
override val position: Position) : Statement() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {

View File

@ -641,8 +641,8 @@ internal class AsmGen(private val program: Program,
is Continue -> out(" jmp ${loopContinueLabels.peek()}")
is Break -> out(" jmp ${loopEndLabels.peek()}")
is WhileLoop -> translate(stmt)
is ForeverLoop -> translate(stmt)
is RepeatLoop -> translate(stmt)
is UntilLoop -> translate(stmt)
is WhenStatement -> translate(stmt)
is BuiltinFunctionStatementPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore?")
is AnonymousScope -> translate(stmt)
@ -673,15 +673,72 @@ internal class AsmGen(private val program: Program,
}
}
private fun translate(stmt: ForeverLoop) {
val foreverLabel = makeLabel("forever")
val endLabel = makeLabel("foreverend")
private fun translate(stmt: RepeatLoop) {
val repeatLabel = makeLabel("repeat")
val endLabel = makeLabel("repeatend")
val counterLabel = makeLabel("repeatcounter")
loopEndLabels.push(endLabel)
loopContinueLabels.push(foreverLabel)
out(foreverLabel)
translate(stmt.body)
out(" jmp $foreverLabel")
out(endLabel)
loopContinueLabels.push(repeatLabel)
when (stmt.iterations) {
null -> {
// endless loop
out(repeatLabel)
translate(stmt.body)
out(" jmp $repeatLabel")
out(endLabel)
}
is NumericLiteralValue -> {
val iterations = (stmt.iterations as NumericLiteralValue).number.toInt()
if(iterations<0 || iterations > 65536)
throw AssemblyError("invalid number of iterations")
when {
iterations == 0 -> {}
iterations <= 255 -> {
out("""
lda #${iterations}
sta $counterLabel
$repeatLabel lda $counterLabel
beq $endLabel
dec $counterLabel
""")
translate(stmt.body)
out("""
jmp $repeatLabel
$counterLabel .byte 0
$endLabel""")
}
else -> {
out("""
lda #<${iterations}
sta $counterLabel
lda #>${iterations}
sta $counterLabel+1
$repeatLabel lda $counterLabel
bne +
lda $counterLabel+1
beq $endLabel
+ lda $counterLabel
bne +
dec $counterLabel+1
+ dec $counterLabel
""")
translate(stmt.body)
out("""
jmp $repeatLabel
$counterLabel .word 0
$endLabel""")
}
}
}
is IdentifierReference -> {
TODO("iterations ${stmt.iterations}")
}
else -> {
TODO("repeat loop with iterations ${stmt.iterations}")
}
}
loopEndLabels.pop()
loopContinueLabels.pop()
}
@ -714,7 +771,7 @@ internal class AsmGen(private val program: Program,
loopContinueLabels.pop()
}
private fun translate(stmt: RepeatLoop) {
private fun translate(stmt: UntilLoop) {
val repeatLabel = makeLabel("repeat")
val endLabel = makeLabel("repeatend")
loopEndLabels.push(endLabel)
@ -770,7 +827,7 @@ internal class AsmGen(private val program: Program,
bne +
cpy #>${value.toHex()}
beq $choiceLabel
+
+
""")
}
}

View File

@ -832,7 +832,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
assignFromEvalResult(assign.target)
}
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array assignment $assign")
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened ${assign.value.position}")
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values ${assign.value.position}")
}
}

View File

@ -28,7 +28,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
is IdentifierReference -> translateExpression(expression)
is FunctionCall -> translateExpression(expression)
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
}
}

View File

@ -41,7 +41,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
when {
stmt.args.all {it is AddressOf ||
it is NumericLiteralValue ||
it is StructLiteralValue ||
it is StringLiteralValue ||
it is ArrayLiteralValue ||
it is IdentifierReference} -> {

View File

@ -251,18 +251,18 @@ internal class StatementOptimizer(private val program: Program,
return noModifications
}
override fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
val constvalue = repeatLoop.untilCondition.constValue(program)
override fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
val constvalue = untilLoop.untilCondition.constValue(program)
if(constvalue!=null) {
if(constvalue.asBooleanValue) {
// always true -> keep only the statement block (if there are no continue and break statements)
errors.warn("condition is always true", repeatLoop.untilCondition.position)
if(!hasContinueOrBreak(repeatLoop.body))
return listOf(IAstModification.ReplaceNode(repeatLoop, repeatLoop.body, parent))
errors.warn("condition is always true", untilLoop.untilCondition.position)
if(!hasContinueOrBreak(untilLoop.body))
return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent))
} else {
// always false
val forever = ForeverLoop(repeatLoop.body, repeatLoop.position)
return listOf(IAstModification.ReplaceNode(repeatLoop, forever, parent))
val forever = RepeatLoop(null, untilLoop.body, untilLoop.position)
return listOf(IAstModification.ReplaceNode(untilLoop, forever, parent))
}
}
return noModifications
@ -273,7 +273,7 @@ internal class StatementOptimizer(private val program: Program,
if(constvalue!=null) {
return if(constvalue.asBooleanValue) {
// always true
val forever = ForeverLoop(whileLoop.body, whileLoop.position)
val forever = RepeatLoop(null, whileLoop.body, whileLoop.position)
listOf(IAstModification.ReplaceNode(whileLoop, forever, parent))
} else {
// always false -> remove the while statement altogether
@ -284,6 +284,24 @@ internal class StatementOptimizer(private val program: Program,
return noModifications
}
override fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
val iter = repeatLoop.iterations
if(iter!=null) {
if(repeatLoop.body.containsNoCodeNorVars()) {
errors.warn("empty loop removed", repeatLoop.position)
return listOf(IAstModification.Remove(repeatLoop, parent))
}
val iterations = iter.constValue(program)?.number?.toInt()
if (iterations == 0) {
errors.warn("iterations is always 0, removed loop", iter.position)
return listOf(IAstModification.Remove(repeatLoop, parent))
}
if (iterations == 1)
errors.warn("iterations is always 1", iter.position)
}
return noModifications
}
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
// remove empty choices
class ChoiceRemover(val choice: WhenChoice) : IAstModification {

View File

@ -50,7 +50,7 @@ Code
There are different kinds of instructions ('statements' is a better name) such as:
- value assignment
- looping (for, while, repeat, unconditional jumps)
- looping (for, while, do-until, repeat, unconditional jumps)
- conditional execution (if - then - else, when, and conditional jumps)
- subroutine calls
- label definition
@ -137,7 +137,7 @@ Scopes are created using either of these two statements:
.. important::
Unlike most other programming languages, a new scope is *not* created inside
for, while and repeat statements, the if statement, and the branching conditionals.
for, while, repeat, and do-until statements, the if statement, and the branching conditionals.
These all share the same scope from the subroutine they're defined in.
You can define variables in these blocks, but these will be treated as if they
were defined in the subroutine instead.
@ -391,11 +391,9 @@ The loop variable must be declared as byte or word earlier so you can reuse it f
Iterating with a floating point variable is not supported. If you want to loop over a floating-point array, use a loop with an integer index variable instead.
The *while*-loop is used to repeat a piece of code while a certain condition is still true.
The *repeat--until* loop is used to repeat a piece of code until a certain condition is true.
The *forever*-loop is used to simply run a piece of code in a loop, forever. You can still
break out of this loop if desired. A "while true" or "until false" loop is equivalent to
a forever-loop.
The *do--until* loop is used to repeat a piece of code until a certain condition is true.
The *repeat* loop is used as a short notation of a for loop where the loop variable doesn't matter and you're only interested in the number of iterations.
(without iteration count specified it simply loops forever).
You can also create loops by using the ``goto`` statement, but this should usually be avoided.

View File

@ -596,31 +596,33 @@ You can use a single statement, or a statement block like in the example below::
}
repeat-until loop
^^^^^^^^^^^^^^^^^
do-until loop
^^^^^^^^^^^^^
Until the given condition is true (1), repeat the given statement(s).
You can use a single statement, or a statement block like in the example below::
repeat {
do {
; do something...
break ; break out of the loop
continue ; immediately enter next iteration
} until <condition>
forever loop
^^^^^^^^^^^^
repeat loop
^^^^^^^^^^^
Simply run the code in a loop, forever. It's the same as a while true or until false loop,
or just a jump back to a previous label. You can still break out of this loop as well, if you want::
When you're only interested in repeating something a given number of times.
It's a short hand for a for loop without an explicit loop variable::
forever {
; .. do stuff
if something
break ; you can exit the loop if you want
repeat 15 {
; do something...
break ; you can break out of the loop
}
If you omit the iteration count, it simply loops forever.
You can still ``break`` out of such a loop if you want though.
Conditional Execution and Jumps
-------------------------------

View File

@ -21,7 +21,7 @@ main {
ubyte active_height = 24
ubyte upwards = true
forever {
repeat {
ubyte mountain = 223 ; slope upwards
if active_height < target_height {
active_height++

View File

@ -16,7 +16,7 @@ sub start() {
void c64.CHRIN()
c64.CLEARSCR()
forever {
repeat {
uword note
for note in notes {
ubyte note1 = lsb(note)

View File

@ -39,7 +39,7 @@ graphics {
if dx >= dy {
if positive_ix {
forever {
repeat {
plot(y1)
if plotx==x2
return
@ -51,7 +51,7 @@ graphics {
}
}
} else {
forever {
repeat {
plot(y1)
if plotx==x2
return
@ -66,7 +66,7 @@ graphics {
}
else {
if positive_ix {
forever {
repeat {
plot(y1)
if y1 == y2
return
@ -78,7 +78,7 @@ graphics {
}
}
} else {
forever {
repeat {
plot(y1)
if y1 == y2
return

View File

@ -19,7 +19,7 @@ main {
sub start() {
float time=0.0
forever {
repeat {
rotate_vertices(time)
c64scr.clear_screenchars(32)
draw_edges()

View File

@ -82,7 +82,7 @@ main {
uword anglex
uword angley
uword anglez
forever {
repeat {
c64.TIME_LO=0
rotate_vertices(msb(anglex), msb(angley), msb(anglez))
position_sprites()

View File

@ -21,7 +21,7 @@ main {
uword anglex
uword angley
uword anglez
forever {
repeat {
rotate_vertices(msb(anglex), msb(angley), msb(anglez))
c64scr.clear_screenchars(32)
draw_edges()

View File

@ -10,7 +10,7 @@ main {
graphics.enable_bitmap_mode()
draw_lines()
draw_circles()
forever {
repeat {
}
}

View File

@ -77,7 +77,7 @@ main {
ubyte y = y1
if dx >= dy {
forever {
repeat {
c64scr.setcc(x, y, 42, 5)
if x==x2
return
@ -89,7 +89,7 @@ main {
}
}
} else {
forever {
repeat {
c64scr.setcc(x, y, 42, 5)
if y == y2
return

View File

@ -48,7 +48,7 @@ main {
}
}
forever {
repeat {
}
}
}

View File

@ -12,7 +12,7 @@ main {
; calculate primes
c64scr.print("prime numbers up to 255:\n\n")
ubyte amount=0
forever {
repeat {
ubyte prime = find_next_prime()
if prime==0
break

View File

@ -7,7 +7,7 @@ main {
c64.SCROLY &= %11101111 ; blank the screen
c64utils.set_rasterirq_excl(40) ; register exclusive raster irq handler
forever {
repeat {
; enjoy the moving bars :)
}

View File

@ -14,7 +14,7 @@ main {
sub start() {
Color purple = {255, 0, 255}
Color purple = [255, 0, 255]
Color other

View File

@ -11,7 +11,7 @@ main {
float t
ubyte color
forever {
repeat {
ubyte xx=(sin(t) * width/2.2) + width/2.0 as ubyte
ubyte yy=(cos(t*1.1356) * height/2.2) + height/2.0 as ubyte
c64scr.setcc(xx, yy, 81, color)

View File

@ -15,7 +15,7 @@ main {
Ball ball
forever {
repeat {
ubyte x = msb(sin8u(msb(ball.anglex)) as uword * width)
ubyte y = msb(cos8u(msb(ball.angley)) as uword * height)
c64scr.setcc(x, y, 81, ball.color)

View File

@ -7,11 +7,64 @@
main {
; TODO check removal of empty loops
sub start() {
ubyte i
for i in 0 to 20 {
c64scr.print_ub(i)
c64.CHROUT('\n')
for i in 1 to 20 {
c64.CHROUT('*')
}
c64.CHROUT('\n')
i=0
do {
c64.CHROUT('*')
i++
} until i==20
c64.CHROUT('\n')
repeat {
break
continue
c64.CHROUT('*')
}
c64.CHROUT('\n')
repeat 0 {
c64.CHROUT('@')
break
continue
}
c64.CHROUT('\n')
repeat 1 {
c64.CHROUT('1')
continue
break
}
c64.CHROUT('\n')
repeat 255 {
c64.CHROUT('@')
}
c64.CHROUT('\n')
repeat 256 {
c64.CHROUT('!')
}
c64.CHROUT('\n')
uword teller
repeat 4000 {
teller++
}
c64scr.print_uw(teller)
c64.CHROUT('\n')
repeat {
}
}
}

View File

@ -22,7 +22,7 @@ main {
turtle.rt(94)
}
forever {
repeat {
}
}
}

View File

@ -94,8 +94,8 @@ statement :
| returnstmt
| forloop
| whileloop
| untilloop
| repeatloop
| foreverloop
| whenstmt
| breakstmt
| continuestmt
@ -228,8 +228,6 @@ booleanliteral : 'true' | 'false' ;
arrayliteral : '[' EOL? expression (',' EOL? expression)* EOL? ']' ; // you can split the values over several lines
structliteral : '{' EOL? expression (',' EOL? expression)* EOL? '}' ; // you can split the values over several lines
stringliteral : ALT_STRING_ENCODING? STRING ;
charliteral : ALT_STRING_ENCODING? SINGLECHAR ;
@ -244,7 +242,6 @@ literalvalue :
| stringliteral
| charliteral
| floatliteral
| structliteral
;
inlineasm : '%asm' INLINEASMBLOCK;
@ -304,9 +301,9 @@ forloop : 'for' identifier 'in' expression EOL? (statement | statement_block) ;
whileloop: 'while' expression EOL? (statement | statement_block) ;
repeatloop: 'repeat' (statement | statement_block) EOL? 'until' expression ;
untilloop: 'do' (statement | statement_block) EOL? 'until' expression ;
foreverloop: 'forever' EOL? (statement | statement_block) ;
repeatloop: 'repeat' expression? EOL? (statement | statement_block) ;
whenstmt: 'when' expression '{' EOL (when_choice | EOL) * '}' EOL? ;