diff --git a/compiler/src/prog8/ast/AstToSourceCode.kt b/compiler/src/prog8/ast/AstToSourceCode.kt index 4c2c97908..e38c6ccb6 100644 --- a/compiler/src/prog8/ast/AstToSourceCode.kt +++ b/compiler/src/prog8/ast/AstToSourceCode.kt @@ -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") } diff --git a/compiler/src/prog8/ast/AstToplevel.kt b/compiler/src/prog8/ast/AstToplevel.kt index cfeafef6b..d737e8252 100644 --- a/compiler/src/prog8/ast/AstToplevel.kt +++ b/compiler/src/prog8/ast/AstToplevel.kt @@ -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 */ } diff --git a/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt b/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt index 9ec6ed4b6..958af0cbd 100644 --- a/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt +++ b/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt @@ -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 { diff --git a/compiler/src/prog8/ast/expressions/AstExpressions.kt b/compiler/src/prog8/ast/expressions/AstExpressions.kt index e7ed11625..dbffe27f4 100644 --- a/compiler/src/prog8/ast/expressions/AstExpressions.kt +++ b/compiler/src/prog8/ast/expressions/AstExpressions.kt @@ -455,31 +455,6 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed } } -class StructLiteralValue(var values: List, - 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, diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index 0c4756e01..35a2de5d0 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -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 } diff --git a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt index f3a66c9bc..c2ebc3f5d 100644 --- a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt @@ -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) } } diff --git a/compiler/src/prog8/ast/processing/AstWalker.kt b/compiler/src/prog8/ast/processing/AstWalker.kt index c2b02c7fe..3f42c3a02 100644 --- a/compiler/src/prog8/ast/processing/AstWalker.kt +++ b/compiler/src/prog8/ast/processing/AstWalker.kt @@ -94,7 +94,7 @@ abstract class AstWalker { open fun before(expr: BinaryExpression, parent: Node): Iterable = emptyList() open fun before(expr: PrefixExpression, parent: Node): Iterable = emptyList() open fun before(forLoop: ForLoop, parent: Node): Iterable = emptyList() - open fun before(foreverLoop: ForeverLoop, parent: Node): Iterable = emptyList() + open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable = emptyList() open fun before(functionCall: FunctionCall, parent: Node): Iterable = emptyList() open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable = emptyList() open fun before(identifier: IdentifierReference, parent: Node): Iterable = emptyList() @@ -110,12 +110,11 @@ abstract class AstWalker { open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable = emptyList() open fun before(program: Program, parent: Node): Iterable = emptyList() open fun before(range: RangeExpr, parent: Node): Iterable = emptyList() - open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable = emptyList() + open fun before(untilLoop: UntilLoop, parent: Node): Iterable = emptyList() open fun before(returnStmt: Return, parent: Node): Iterable = emptyList() open fun before(scope: AnonymousScope, parent: Node): Iterable = emptyList() open fun before(string: StringLiteralValue, parent: Node): Iterable = emptyList() open fun before(structDecl: StructDecl, parent: Node): Iterable = emptyList() - open fun before(structLv: StructLiteralValue, parent: Node): Iterable = emptyList() open fun before(subroutine: Subroutine, parent: Node): Iterable = emptyList() open fun before(typecast: TypecastExpression, parent: Node): Iterable = emptyList() open fun before(whenChoice: WhenChoice, parent: Node): Iterable = emptyList() @@ -137,7 +136,7 @@ abstract class AstWalker { open fun after(expr: BinaryExpression, parent: Node): Iterable = emptyList() open fun after(expr: PrefixExpression, parent: Node): Iterable = emptyList() open fun after(forLoop: ForLoop, parent: Node): Iterable = emptyList() - open fun after(foreverLoop: ForeverLoop, parent: Node): Iterable = emptyList() + open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable = emptyList() open fun after(functionCall: FunctionCall, parent: Node): Iterable = emptyList() open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable = emptyList() open fun after(identifier: IdentifierReference, parent: Node): Iterable = emptyList() @@ -153,12 +152,11 @@ abstract class AstWalker { open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable = emptyList() open fun after(program: Program, parent: Node): Iterable = emptyList() open fun after(range: RangeExpr, parent: Node): Iterable = emptyList() - open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable = emptyList() + open fun after(untilLoop: UntilLoop, parent: Node): Iterable = emptyList() open fun after(returnStmt: Return, parent: Node): Iterable = emptyList() open fun after(scope: AnonymousScope, parent: Node): Iterable = emptyList() open fun after(string: StringLiteralValue, parent: Node): Iterable = emptyList() open fun after(structDecl: StructDecl, parent: Node): Iterable = emptyList() - open fun after(structLv: StructLiteralValue, parent: Node): Iterable = emptyList() open fun after(subroutine: Subroutine, parent: Node): Iterable = emptyList() open fun after(typecast: TypecastExpression, parent: Node): Iterable = emptyList() open fun after(whenChoice: WhenChoice, parent: Node): Iterable = 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) - } } diff --git a/compiler/src/prog8/ast/processing/IAstVisitor.kt b/compiler/src/prog8/ast/processing/IAstVisitor.kt index 21e035f64..93f6a8250 100644 --- a/compiler/src/prog8/ast/processing/IAstVisitor.kt +++ b/compiler/src/prog8/ast/processing/IAstVisitor.kt @@ -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) } - } } diff --git a/compiler/src/prog8/ast/processing/StatementReorderer.kt b/compiler/src/prog8/ast/processing/StatementReorderer.kt index ed7192fda..91b4d87ea 100644 --- a/compiler/src/prog8/ast/processing/StatementReorderer.kt +++ b/compiler/src/prog8/ast/processing/StatementReorderer.kt @@ -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") diff --git a/compiler/src/prog8/ast/processing/TypecastsAdder.kt b/compiler/src/prog8/ast/processing/TypecastsAdder.kt index dba9d750b..a05604896 100644 --- a/compiler/src/prog8/ast/processing/TypecastsAdder.kt +++ b/compiler/src/prog8/ast/processing/TypecastsAdder.kt @@ -179,51 +179,6 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke return noModifications } - override fun after(structLv: StructLiteralValue, parent: Node): Iterable { - // assignment of a struct literal value, some member values may need proper typecast - - fun addTypecastsIfNeeded(struct: StructDecl): Iterable { - 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) : 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 { // add a typecast to the return type if it doesn't match the subroutine's signature val returnValue = returnStmt.value diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index c4cbe3cc5..bd4ad70f6 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -270,7 +270,7 @@ open class VarDecl(val type: VarDeclType, fun flattenStructMembers(): MutableList { 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) { diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index 193429fc0..ae8102675 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -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 -+ ++ """) } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt index 12af35bb9..f59699b02 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt @@ -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}") } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt index 840fd284c..85d27fa81 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt @@ -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") } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt index 1d6138914..03304abe4 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt @@ -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} -> { diff --git a/compiler/src/prog8/optimizer/StatementOptimizer.kt b/compiler/src/prog8/optimizer/StatementOptimizer.kt index a4f2d2f53..3bc311b1c 100644 --- a/compiler/src/prog8/optimizer/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizer/StatementOptimizer.kt @@ -251,18 +251,18 @@ internal class StatementOptimizer(private val program: Program, return noModifications } - override fun before(repeatLoop: RepeatLoop, parent: Node): Iterable { - val constvalue = repeatLoop.untilCondition.constValue(program) + override fun before(untilLoop: UntilLoop, parent: Node): Iterable { + 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 { + 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 { // remove empty choices class ChoiceRemover(val choice: WhenChoice) : IAstModification { diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 3d0ae6321..11d3534f7 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -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. diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 0b138d377..15d0b2deb 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -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 -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 ------------------------------- diff --git a/examples/balloonflight.p8 b/examples/balloonflight.p8 index 5dd52a0ee..22d002c9e 100644 --- a/examples/balloonflight.p8 +++ b/examples/balloonflight.p8 @@ -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++ diff --git a/examples/bdmusic.p8 b/examples/bdmusic.p8 index f68ecc39f..d84d5f6b2 100644 --- a/examples/bdmusic.p8 +++ b/examples/bdmusic.p8 @@ -16,7 +16,7 @@ sub start() { void c64.CHRIN() c64.CLEARSCR() - forever { + repeat { uword note for note in notes { ubyte note1 = lsb(note) diff --git a/examples/c64graphics.p8 b/examples/c64graphics.p8 index f1bbd09e2..31cbe6da3 100644 --- a/examples/c64graphics.p8 +++ b/examples/c64graphics.p8 @@ -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 diff --git a/examples/cube3d-float.p8 b/examples/cube3d-float.p8 index cac704538..0e09dc54c 100644 --- a/examples/cube3d-float.p8 +++ b/examples/cube3d-float.p8 @@ -19,7 +19,7 @@ main { sub start() { float time=0.0 - forever { + repeat { rotate_vertices(time) c64scr.clear_screenchars(32) draw_edges() diff --git a/examples/cube3d-sprites.p8 b/examples/cube3d-sprites.p8 index 59771ab42..56d388edc 100644 --- a/examples/cube3d-sprites.p8 +++ b/examples/cube3d-sprites.p8 @@ -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() diff --git a/examples/cube3d.p8 b/examples/cube3d.p8 index 5c0b78fbe..84e2117ed 100644 --- a/examples/cube3d.p8 +++ b/examples/cube3d.p8 @@ -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() diff --git a/examples/line-circle-gfx.p8 b/examples/line-circle-gfx.p8 index 570aa7b4e..384add700 100644 --- a/examples/line-circle-gfx.p8 +++ b/examples/line-circle-gfx.p8 @@ -10,7 +10,7 @@ main { graphics.enable_bitmap_mode() draw_lines() draw_circles() - forever { + repeat { } } diff --git a/examples/line-circle-txt.p8 b/examples/line-circle-txt.p8 index 366dc6b4a..464505334 100644 --- a/examples/line-circle-txt.p8 +++ b/examples/line-circle-txt.p8 @@ -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 diff --git a/examples/mandelbrot-gfx.p8 b/examples/mandelbrot-gfx.p8 index 96aa50cb4..3866c402d 100644 --- a/examples/mandelbrot-gfx.p8 +++ b/examples/mandelbrot-gfx.p8 @@ -48,7 +48,7 @@ main { } } - forever { + repeat { } } } diff --git a/examples/primes.p8 b/examples/primes.p8 index e4bbe90e2..c5841436e 100644 --- a/examples/primes.p8 +++ b/examples/primes.p8 @@ -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 diff --git a/examples/rasterbars.p8 b/examples/rasterbars.p8 index 5866ab7d2..614bdb923 100644 --- a/examples/rasterbars.p8 +++ b/examples/rasterbars.p8 @@ -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 :) } diff --git a/examples/structs.p8 b/examples/structs.p8 index 09e614d13..f0024cfb9 100644 --- a/examples/structs.p8 +++ b/examples/structs.p8 @@ -14,7 +14,7 @@ main { sub start() { - Color purple = {255, 0, 255} + Color purple = [255, 0, 255] Color other diff --git a/examples/swirl-float.p8 b/examples/swirl-float.p8 index 583df94c1..4f4bc8b9b 100644 --- a/examples/swirl-float.p8 +++ b/examples/swirl-float.p8 @@ -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) diff --git a/examples/swirl.p8 b/examples/swirl.p8 index a6f639bee..0ebda8249 100644 --- a/examples/swirl.p8 +++ b/examples/swirl.p8 @@ -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) diff --git a/examples/test.p8 b/examples/test.p8 index dd39efb92..f866196b5 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -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 { + } + } } diff --git a/examples/turtle-gfx.p8 b/examples/turtle-gfx.p8 index 06ea06c4f..a8cbdfeb9 100644 --- a/examples/turtle-gfx.p8 +++ b/examples/turtle-gfx.p8 @@ -22,7 +22,7 @@ main { turtle.rt(94) } - forever { + repeat { } } } diff --git a/parser/antlr/prog8.g4 b/parser/antlr/prog8.g4 index 5dd06c50c..bafdd9d9a 100644 --- a/parser/antlr/prog8.g4 +++ b/parser/antlr/prog8.g4 @@ -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? ;