From 9ddda9fcf73bb64f4889aa50f5e4864fc1f58c31 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 29 Dec 2018 16:25:20 +0100 Subject: [PATCH] for loops over iterables now allow different numeric loopvar types --- compiler/examples/cube3d-c64.p8 | 4 +- compiler/examples/forloops.p8 | 58 +++++++++++++++++++ compiler/examples/test.p8 | 42 +++++++++----- compiler/src/prog8/ast/AstChecker.kt | 28 +++++++-- .../src/prog8/ast/AstIdentifiersChecker.kt | 30 +++++----- compiler/src/prog8/compiler/Compiler.kt | 9 +-- prog8lib/c64utils.p8 | 1 + 7 files changed, 128 insertions(+), 44 deletions(-) create mode 100644 compiler/examples/forloops.p8 diff --git a/compiler/examples/cube3d-c64.p8 b/compiler/examples/cube3d-c64.p8 index 589d138a4..6cd94e820 100644 --- a/compiler/examples/cube3d-c64.p8 +++ b/compiler/examples/cube3d-c64.p8 @@ -46,7 +46,7 @@ rotate_vertices(irq.global_time as float / 30.0) c64scr.print_ub(X) c64.CHROUT('\n') - draw_edges() ; @todo doesn't return from the loop... + draw_edges() c64scr.print_ub(X) c64.CHROUT('\n') } @@ -93,7 +93,7 @@ } ; draw all edges of the object - for uword edge in edges { ; @todo invalid loop code generated? (loop doesn't end?) + for uword edge in edges { ubyte e_from = msb(edge) ubyte e_to = lsb(edge) diff --git a/compiler/examples/forloops.p8 b/compiler/examples/forloops.p8 new file mode 100644 index 000000000..759a9f2c0 --- /dev/null +++ b/compiler/examples/forloops.p8 @@ -0,0 +1,58 @@ +%import c64utils + +~ main { + + ubyte[3] ubarray = [11,55,222] + byte[3] barray = [-11,-22,-33] + uword[3] uwarray = [111,2222,55555] + word[3] warray = [-111,-222,-555] + float[3] farray = [1.11, 2.22, -3.33] + str text = "hello\n" + + sub start() { + c64scr.print_ub(X) + c64.CHROUT('\n') + + c64scr.print("loop str\n") + for ubyte c in text { + c64scr.print_ub(c) + c64.CHROUT(',') + } + + c64scr.print("\nloop ub\n") + for ubyte ub in ubarray{ + c64scr.print_ub(ub) + c64.CHROUT(',') + } + + c64scr.print("\nloop b\n") + for byte b in barray { + c64scr.print_b(b) + c64.CHROUT(',') + } + + c64scr.print("\nloop uw\n") + for uword uw in uwarray { + c64scr.print_uw(uw) + c64.CHROUT(',') + } + + c64scr.print("\nloop w\n") + for word w in warray { + c64scr.print_w(w) + c64.CHROUT(',') + } + + c64scr.print("\nloop f\n") + for float f in farray { + c64flt.print_f(f) + c64.CHROUT(',') + } + + +ending: + c64scr.print("\nending\n") + c64scr.print_ub(X) + c64.CHROUT('\n') + } +} diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index e233a26a2..873ba8cba 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -1,4 +1,5 @@ %import c64utils +%option enable_floats ~ main { @@ -6,6 +7,7 @@ byte[3] barray = [-11,-22,-33] uword[3] uwarray = [111,2222,55555] word[3] warray = [-111,-222,-555] + float[3] farray = [1.11, 2.22, -3.33] str text = "hello\n" sub start() { @@ -14,33 +16,41 @@ c64scr.print("loop str\n") for ubyte c in text { - c64scr.print(" c ") c64scr.print_ub(c) - c64.CHROUT('\n') + c64.CHROUT(',') } - c64scr.print("loop ub\n") + c64scr.print("\nloop ub\n") for ubyte ub in ubarray{ - c64scr.print(" ub ") c64scr.print_ub(ub) - c64.CHROUT('\n') + c64.CHROUT(',') } -; c64scr.print("loop b\n") ; @todo allow signed loopvars -; for byte b in barray { ; @todo loop doesn't end because of register clobbering?? -; c64scr.print(" b ") -; c64scr.print_b(b) -; c64.CHROUT('\n') -; } + c64scr.print("\nloop b\n") + for byte b in barray { + c64scr.print_b(b) + c64.CHROUT(',') + } -errorloop: - c64scr.print("loop uw\n") - for uword uw in uwarray { ; @todo loop doesn't end because of register clobbering - c64scr.print(" uw ") + c64scr.print("\nloop uw\n") + for uword uw in uwarray { c64scr.print_uw(uw) - c64.CHROUT('\n') + c64.CHROUT(',') } + c64scr.print("\nloop w\n") + for word w in warray { + c64scr.print_w(w) + c64.CHROUT(',') + } + + c64scr.print("\nloop f\n") + for float f in farray { + c64flt.print_f(f) + c64.CHROUT(',') + } + + ending: c64scr.print("\nending\n") c64scr.print_ub(X) diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 820f60390..8133d9a84 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -89,6 +89,7 @@ class AstChecker(private val namespace: INameScope, is VarDecl->true is InlineAssembly->true is INameScope->true + is VariableInitializationAssignment->true else->false } if(!ok) { @@ -154,15 +155,27 @@ class AstChecker(private val namespace: INameScope, when (loopvar.datatype) { DataType.UBYTE -> { if(iterableDt!=DataType.UBYTE && iterableDt!=DataType.ARRAY_UB && iterableDt !in StringDatatypes) - checkResult.add(ExpressionError("byte loop variable can only loop over bytes", forLoop.position)) + checkResult.add(ExpressionError("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position)) } DataType.UWORD -> { if(iterableDt!=DataType.UBYTE && iterableDt!=DataType.UWORD && iterableDt !in StringDatatypes && iterableDt !=DataType.ARRAY_UB && iterableDt!=DataType.ARRAY_UW) + checkResult.add(ExpressionError("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)) + } + DataType.BYTE -> { + if(iterableDt!=DataType.BYTE && iterableDt!=DataType.ARRAY_B) + checkResult.add(ExpressionError("byte loop variable can only loop over bytes", forLoop.position)) + } + DataType.WORD -> { + if(iterableDt!=DataType.BYTE && iterableDt!=DataType.WORD && + iterableDt !=DataType.ARRAY_B && iterableDt!=DataType.ARRAY_W) checkResult.add(ExpressionError("word loop variable can only loop over bytes or words", forLoop.position)) } - // there's no support for a floating-point loop variable - else -> checkResult.add(ExpressionError("loop variable must be byte or word type", forLoop.position)) + DataType.FLOAT -> { + if(iterableDt!=DataType.FLOAT && iterableDt != DataType.ARRAY_F) + checkResult.add(ExpressionError("float loop variable can only loop over floats", forLoop.position)) + } + else -> checkResult.add(ExpressionError("loop variable must be numeric type", forLoop.position)) } } } @@ -477,6 +490,13 @@ class AstChecker(private val namespace: INameScope, err("const modifier can only be used on numeric types (byte, word, float)") } + // FLOATS + // @todo move floating point routines from c64utils into separately included file, then re-enable this check +// if(!compilerOptions.floats && decl.datatype==DataType.FLOAT && decl.type!=VarDeclType.MEMORY) { +// checkResult.add(SyntaxError("floating point used, but that is not enabled via options", decl.position)) +// } + + when(decl.type) { VarDeclType.VAR, VarDeclType.CONST -> { if (decl.value == null) { @@ -619,7 +639,7 @@ class AstChecker(private val namespace: INameScope, override fun process(literalValue: LiteralValue): LiteralValue { if(!compilerOptions.floats && literalValue.type==DataType.FLOAT) { - checkResult.add(SyntaxError("floating point value used, but floating point is not enabled via options", literalValue.position)) + checkResult.add(SyntaxError("floating point used, but that is not enabled via options", literalValue.position)) } val arrayspec = if(literalValue.isArray) diff --git a/compiler/src/prog8/ast/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/AstIdentifiersChecker.kt index 8e8fe6349..f992fa149 100644 --- a/compiler/src/prog8/ast/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/AstIdentifiersChecker.kt @@ -162,23 +162,21 @@ class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor { printWarning("writing to the X register is dangerous, because it's used as an internal pointer", forLoop.position) } else if(forLoop.loopVar!=null) { val varName = forLoop.loopVar.nameInSource.last() - when (forLoop.decltype) { - DataType.UBYTE, DataType.UWORD -> { - val existing = if(forLoop.body.isEmpty()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first()) - if(existing==null) { - // create the local scoped for loop variable itself - val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, null, varName, null, forLoop.loopVar.position) - vardecl.linkParents(forLoop.body) - forLoop.body.statements.add(0, vardecl) - forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body' - } - } - DataType.BYTE, DataType.WORD -> { - checkResult.add(SyntaxError("loop variables can only be unsigned byte or unsigned word", forLoop.position)) // TODO allow signed loopvars - } - null -> {} - else -> checkResult.add(SyntaxError("loop variables can only be a byte or word", forLoop.position)) + if(forLoop.iterable is RangeExpr && forLoop.decltype!=null && forLoop.decltype !in setOf(DataType.UBYTE, DataType.UWORD)) { + checkResult.add(SyntaxError("loop variables over a numeric range can only be ubyte or uword", forLoop.position)) // TODO allow signed range loopvars } + if(forLoop.decltype!=null) { + val existing = if(forLoop.body.isEmpty()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first()) + if(existing==null) { + // create the local scoped for loop variable itself + val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, null, varName, null, forLoop.loopVar.position) + vardecl.linkParents(forLoop.body) + forLoop.body.statements.add(0, vardecl) + forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body' + } + + } + if(forLoop.iterable !is RangeExpr) { val existing = if(forLoop.body.isEmpty()) null else forLoop.body.lookup(listOf(ForLoop.iteratorLoopcounterVarname), forLoop.body.statements.first()) if(existing==null) { diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 45d8c43fc..4d0d0a771 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -1603,21 +1603,18 @@ private class StatementTranslator(private val prog: IntermediateProgram, } } else { // ok, must be a literalvalue - val iterableValue: LiteralValue when { - loop.iterable is LiteralValue -> { - TODO("loop over literal value (move literal to auto-generated heap variable)") - } loop.iterable is IdentifierReference -> { val idRef = loop.iterable as IdentifierReference val vardecl = (idRef.targetStatement(namespace) as VarDecl) - iterableValue = vardecl.value as LiteralValue + val iterableValue = vardecl.value as LiteralValue if(!iterableValue.isIterable(namespace, heap)) throw CompilerException("loop over something that isn't iterable ${loop.iterable}") + translateForOverIterableVar(loop, loopVarDt, iterableValue) } + loop.iterable is LiteralValue -> throw CompilerException("literal value in loop must have been moved to heap already $loop") else -> throw CompilerException("loopvar is something strange ${loop.iterable}") } - translateForOverIterableVar(loop, loopVarDt, iterableValue) } } diff --git a/prog8lib/c64utils.p8 b/prog8lib/c64utils.p8 index deb1e90ef..e854e837f 100644 --- a/prog8lib/c64utils.p8 +++ b/prog8lib/c64utils.p8 @@ -413,6 +413,7 @@ _done rts ~ c64flt { ; ---- this block contains C-64 floating point related functions ---- + ; @todo move to c64fp.p8 and enable float-checkin astchecker.process(decl: VarDecl) again asmsub FREADS32 () -> clobbers(A,X,Y) -> () {