From c45fbe631073fcac7dd829dd159018c9e2557ab2 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 19 Nov 2023 17:52:43 +0100 Subject: [PATCH] continue stmt added --- .../src/prog8/buildversion/BuildVersion.kt | 14 +-- .../compiler/astprocessing/CodeDesugarer.kt | 35 +++++++- .../astprocessing/IntermediateAstMaker.kt | 1 + .../src/prog8/ast/antlr/Antlr2Kotlin.kt | 10 ++- .../src/prog8/ast/statements/AstStatements.kt | 14 +++ compilerAst/src/prog8/ast/walk/AstWalker.kt | 7 ++ compilerAst/src/prog8/ast/walk/IAstVisitor.kt | 3 + docs/source/programming.rst | 4 +- docs/source/syntaxreference.rst | 6 +- examples/test.p8 | 87 +++++++++++-------- parser/antlr/Prog8ANTLR.g4 | 3 + syntax-files/IDEA/Prog8.xml | 2 +- syntax-files/NotepadPlusPlus/Prog8.xml | 2 +- syntax-files/NotepadPlusPlus/syntax-test.p8 | 1 + syntax-files/Vim/prog8.vim | 2 +- 15 files changed, 141 insertions(+), 50 deletions(-) diff --git a/compiler/src/prog8/buildversion/BuildVersion.kt b/compiler/src/prog8/buildversion/BuildVersion.kt index 16e49cc7d..33051e1c2 100644 --- a/compiler/src/prog8/buildversion/BuildVersion.kt +++ b/compiler/src/prog8/buildversion/BuildVersion.kt @@ -5,11 +5,11 @@ package prog8.buildversion */ const val MAVEN_GROUP = "prog8" const val MAVEN_NAME = "compiler" -const val VERSION = "9.6-SNAPSHOT" -const val GIT_REVISION = 4159 -const val GIT_SHA = "335213b55f971452a05c904216cb0fd72f5518a1" -const val GIT_DATE = "2023-10-21T00:16:58Z" -const val GIT_BRANCH = "master" -const val BUILD_DATE = "2023-10-21T20:22:14Z" -const val BUILD_UNIX_TIME = 1697919734197L +const val VERSION = "9.7-SNAPSHOT" +const val GIT_REVISION = 4212 +const val GIT_SHA = "f81061dd42403294e7cb5ebd3c0ed57d6c57c3d5" +const val GIT_DATE = "2023-11-18T00:03:34Z" +const val GIT_BRANCH = "continue-stmt" +const val BUILD_DATE = "2023-11-19T16:19:02Z" +const val BUILD_UNIX_TIME = 1700410742198L const val DIRTY = 1 diff --git a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt index f9604067b..a90edf96b 100644 --- a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt +++ b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt @@ -19,7 +19,7 @@ internal class CodeDesugarer(val program: Program, private val errors: IErrorRep // have been performed (because those could re-introduce nodes that have to be desugared) // // List of modifications: - // - replace 'break' statements by a goto + generated after label. + // - replace 'break' and 'continue' statements by a goto + generated after label. // - replace while and do-until loops by just jumps. // - replace peek() and poke() by direct memory accesses. // - repeat-forever loops replaced by label+jump. @@ -52,6 +52,39 @@ internal class CodeDesugarer(val program: Program, private val errors: IErrorRep } } + override fun before(continueStmt: Continue, parent: Node): Iterable { + fun jumpToBottom(scope: IStatementContainer): Iterable { + val label = program.makeLabel("cont", continueStmt.position) + return listOf( + IAstModification.ReplaceNode(continueStmt, program.jumpLabel(label), parent), + IAstModification.InsertLast(label, scope) + ) + } + + fun jumpToBefore(loop: WhileLoop): Iterable { + val label = program.makeLabel("cont", continueStmt.position) + return listOf( + IAstModification.ReplaceNode(continueStmt, program.jumpLabel(label), parent), + IAstModification.InsertBefore(loop, label, loop.parent as IStatementContainer) + ) + } + + var partof = parent + while(true) { + when (partof) { + is Subroutine, is Block, is ParentSentinel -> { + errors.err("continue in wrong scope", continueStmt.position) + return noModifications + } + is ForLoop -> return jumpToBottom(partof.body) + is RepeatLoop -> return jumpToBottom(partof.body) + is UntilLoop -> return jumpToBottom(partof.body) + is WhileLoop -> return jumpToBefore(partof) + else -> partof = partof.parent + } + } + } + override fun after(untilLoop: UntilLoop, parent: Node): Iterable { /* do { STUFF } until CONDITION diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt index b554ea835..89ad48750 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt @@ -40,6 +40,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr is Assignment -> transform(statement) is Block -> transform(statement) is Break -> throw FatalAstException("break should have been replaced by Goto") + is Continue -> throw FatalAstException("continue should have been replaced by Goto") is BuiltinFunctionCallStatement -> transform(statement) is BuiltinFunctionPlaceholder -> throw FatalAstException("BuiltinFunctionPlaceholder should not occur in Ast here") is ConditionalBranch -> transform(statement) diff --git a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt index 4903aa1cc..654b14538 100644 --- a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt +++ b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt @@ -7,7 +7,6 @@ import prog8.ast.base.SyntaxError import prog8.ast.expressions.* import prog8.ast.statements.* import prog8.code.core.* -import prog8.parser.Prog8ANTLRParser import prog8.parser.Prog8ANTLRParser.* import kotlin.io.path.Path import kotlin.io.path.isRegularFile @@ -145,11 +144,14 @@ private fun StatementContext.toAst() : Statement { val repeatloop = repeatloop()?.toAst() if(repeatloop!=null) return repeatloop + val whenstmt = whenstmt()?.toAst() + if(whenstmt!=null) return whenstmt + val breakstmt = breakstmt()?.toAst() if(breakstmt!=null) return breakstmt - val whenstmt = whenstmt()?.toAst() - if(whenstmt!=null) return whenstmt + val continuestmt = continuestmt()?.toAst() + if(continuestmt!=null) return continuestmt val unrollstmt = unrollloop()?.toAst() if(unrollstmt!=null) return unrollstmt @@ -573,6 +575,8 @@ private fun ForloopContext.toAst(): ForLoop { private fun BreakstmtContext.toAst() = Break(toPosition()) +private fun ContinuestmtContext.toAst() = Continue(toPosition()) + private fun WhileloopContext.toAst(): WhileLoop { val condition = expression().toAst() val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst()) diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index 642517b08..3e2b82296 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -173,6 +173,20 @@ class Break(override val position: Position) : Statement() { override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) } +class Continue(override val position: Position) : Statement() { + override lateinit var parent: Node + + override fun linkParents(parent: Node) { + this.parent=parent + } + + override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here") + override fun referencesIdentifier(nameInSource: List): Boolean = false + override fun copy() = Break(position) + override fun accept(visitor: IAstVisitor) = visitor.visit(this) + override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) +} + enum class VarDeclOrigin { USERCODE, diff --git a/compilerAst/src/prog8/ast/walk/AstWalker.kt b/compilerAst/src/prog8/ast/walk/AstWalker.kt index 0d88e5c04..e7a3e4e9f 100644 --- a/compilerAst/src/prog8/ast/walk/AstWalker.kt +++ b/compilerAst/src/prog8/ast/walk/AstWalker.kt @@ -99,6 +99,7 @@ abstract class AstWalker { open fun before(block: Block, parent: Node): Iterable = noModifications open fun before(branch: ConditionalBranch, parent: Node): Iterable = noModifications open fun before(breakStmt: Break, parent: Node): Iterable = noModifications + open fun before(continueStmt: Continue, parent: Node): Iterable = noModifications open fun before(containment: ContainmentCheck, parent: Node): Iterable = noModifications open fun before(decl: VarDecl, parent: Node): Iterable = noModifications open fun before(directive: Directive, parent: Node): Iterable = noModifications @@ -142,6 +143,7 @@ abstract class AstWalker { open fun after(block: Block, parent: Node): Iterable = noModifications open fun after(branch: ConditionalBranch, parent: Node): Iterable = noModifications open fun after(breakStmt: Break, parent: Node): Iterable = noModifications + open fun after(continueStmt: Continue, parent: Node): Iterable = noModifications open fun after(containment: ContainmentCheck, parent: Node): Iterable = noModifications open fun after(decl: VarDecl, parent: Node): Iterable = noModifications open fun after(directive: Directive, parent: Node): Iterable = noModifications @@ -374,6 +376,11 @@ abstract class AstWalker { track(after(breakStmt, parent), breakStmt, parent) } + fun visit(continueStmt: Continue, parent: Node) { + track(before(continueStmt, parent), continueStmt, parent) + track(after(continueStmt, parent), continueStmt, parent) + } + fun visit(forLoop: ForLoop, parent: Node) { track(before(forLoop, parent), forLoop, parent) forLoop.loopVar.accept(this, forLoop) diff --git a/compilerAst/src/prog8/ast/walk/IAstVisitor.kt b/compilerAst/src/prog8/ast/walk/IAstVisitor.kt index 4aaa2a189..e69e34f94 100644 --- a/compilerAst/src/prog8/ast/walk/IAstVisitor.kt +++ b/compilerAst/src/prog8/ast/walk/IAstVisitor.kt @@ -115,6 +115,9 @@ interface IAstVisitor { fun visit(breakStmt: Break) { } + fun visit(continueStmt: Continue) { + } + fun visit(forLoop: ForLoop) { forLoop.loopVar.accept(this) forLoop.iterable.accept(this) diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 735ed7edb..a4ef38775 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -501,7 +501,9 @@ The *repeat* loop is used as a short notation of a for loop where the loop varia You can also create loops by using the ``goto`` statement, but this should usually be avoided. -Breaking out of a loop prematurely is possible with the ``break`` statement. +Breaking out of a loop prematurely is possible with the ``break`` statement, +immediately continue into the next cycle of the loop with the ``continue`` statement. +(These are just shorthands for a goto + a label) The *unroll* loop is not really a loop, but looks like one. It actually duplicates the statements in its block on the spot by the given number of times. It's meant to "unroll loops" - trade memory for speed by avoiding the actual repeat loop counting code. diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 680f2eadc..1fe94ea8b 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -745,6 +745,7 @@ You can use a single statement, or a statement block like in the example below:: for in [ step ] { ; do something... break ; break out of the loop + continue ; immediately next iteration } For example, this is a for loop using a byte variable ``i``, defined before, to loop over a certain range of numbers:: @@ -778,6 +779,7 @@ You can use a single statement, or a statement block like in the example below:: while { ; do something... break ; break out of the loop + continue ; immediately next iteration } @@ -790,6 +792,7 @@ You can use a single statement, or a statement block like in the example below:: do { ; do something... break ; break out of the loop + continue ; immediately next iteration } until @@ -802,6 +805,7 @@ It's a short hand for a for loop without an explicit loop variable:: repeat 15 { ; do something... break ; you can break out of the loop + continue ; immediately next iteration } If you omit the iteration count, it simply loops forever. @@ -820,7 +824,7 @@ Also, only simple statements such as assignments and function calls can be insid cx16.VERA_DATA0 = 255 } -A `break` statement cannot occur in an unroll loop, as there is not really a loop to break out of. +A `break` or `continue` statement cannot occur in an unroll loop, as there is no actual loop to break out of. Conditional Execution and Jumps diff --git a/examples/test.p8 b/examples/test.p8 index 04bbbb1bc..0919ebfce 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,43 +1,62 @@ %import textio -%import bmx -%option no_sysinit %zeropage basicsafe main { sub start() { - str filename = "?"*40 - repeat { - txt.print("\nenter bmx image filename: ") - if txt.input_chars(&filename) { - if bmx.load_header(8, filename) { - txt.print("\nwidth: ") - txt.print_uw(bmx.width) - txt.print("\nheight: ") - txt.print_uw(bmx.height) - txt.print("\nbpp: ") - txt.print_uw(bmx.bitsperpixel) - txt.nl() - sys.wait(100) - - ; switch to correct screen mode and color depth - void cx16.screen_mode($80, false) - cx16.VERA_L0_CONFIG = cx16.VERA_L0_CONFIG & %11111100 | bmx.vera_colordepth - ; actually load - if bmx.load(8, filename, 0, 0, 320) { - void txt.waitkey() - } - } - - cbm.CINT() ; reset screen - - if bmx.error_message { - txt.print("load error:\n") - txt.print(bmx.error_message) - txt.nl() - sys.wait(120) - } - } + txt.print("for:\n") + for cx16.r0L in 10 to 20 { + txt.print_ub(cx16.r0L) + txt.print(" before...") + if cx16.r0L > 15 + break + if cx16.r0L ==14 + continue + txt.print("after\n") } + txt.nl() + + txt.print("repeat:\n") + cx16.r0L=10 + repeat 10 { + cx16.r0L++ + txt.print_ub(cx16.r0L) + txt.print(" before...") + if cx16.r0L > 15 + break + if cx16.r0L ==14 + continue + txt.print("after\n") + } + txt.nl() + + txt.print("while:\n") + cx16.r0L=10 + while cx16.r0L<20 { + cx16.r0L++ + txt.print_ub(cx16.r0L) + txt.print(" before...") + if cx16.r0L > 15 + break + if cx16.r0L ==14 + continue + txt.print("after\n") + } + txt.nl() + + txt.print("until:\n") + cx16.r0L=10 + do { + cx16.r0L++ + txt.print_ub(cx16.r0L) + txt.print(" before...") + if cx16.r0L > 15 + break + if cx16.r0L ==14 + continue + txt.print("after\n") + } until cx16.r0L>20 + txt.nl() + } } diff --git a/parser/antlr/Prog8ANTLR.g4 b/parser/antlr/Prog8ANTLR.g4 index b9ca853ef..9ab62d363 100644 --- a/parser/antlr/Prog8ANTLR.g4 +++ b/parser/antlr/Prog8ANTLR.g4 @@ -100,6 +100,7 @@ statement : | unrollloop | whenstmt | breakstmt + | continuestmt | labeldef ; @@ -210,6 +211,8 @@ returnstmt : 'return' expression? ; breakstmt : 'break'; +continuestmt: 'continue'; + identifier : NAME ; scoped_identifier : NAME ('.' NAME)* ; diff --git a/syntax-files/IDEA/Prog8.xml b/syntax-files/IDEA/Prog8.xml index 1110074ad..a197516af 100644 --- a/syntax-files/IDEA/Prog8.xml +++ b/syntax-files/IDEA/Prog8.xml @@ -11,7 +11,7 @@