From 641f6c05d810d4bd53ab70b0918d8340866b7d00 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 31 Mar 2024 23:43:26 +0200 Subject: [PATCH] allow 'void' as dummy assign target in multi-assignment statements --- codeCore/src/prog8/code/ast/AstStatements.kt | 4 +- codeCore/src/prog8/code/optimize/Optimizer.kt | 2 +- .../cpu6502/assignment/AssignmentAsmGen.kt | 60 ++++++++++--------- codeGenCpu6502/test/TestCodegen.kt | 8 +-- .../codegen/intermediate/AssignmentGen.kt | 16 +---- .../codegen/intermediate/BuiltinFuncGen.kt | 2 +- codeGenIntermediate/test/TestVmCodeGen.kt | 8 +-- .../optimizer/ConstantIdentifierReplacer.kt | 2 +- .../src/prog8/optimizer/StatementOptimizer.kt | 16 ++--- .../compiler/astprocessing/AstChecker.kt | 55 +++++++---------- .../compiler/astprocessing/AstPreprocessor.kt | 4 +- .../compiler/astprocessing/CodeDesugarer.kt | 4 +- .../astprocessing/IntermediateAstMaker.kt | 4 +- .../astprocessing/StatementReorderer.kt | 6 +- compiler/test/TestMemory.kt | 42 ++++++------- compiler/test/ast/TestSubroutines.kt | 32 +++++++--- .../test/codegeneration/TestAsmGenSymbols.kt | 2 +- .../src/prog8/ast/AstToSourceTextConverter.kt | 22 ++++--- .../src/prog8/ast/antlr/Antlr2Kotlin.kt | 23 ++++--- .../src/prog8/ast/statements/AstStatements.kt | 5 +- docs/source/syntaxreference.rst | 14 ++--- docs/source/todo.rst | 6 -- examples/test.p8 | 3 +- parser/antlr/Prog8ANTLR.g4 | 3 +- 24 files changed, 168 insertions(+), 175 deletions(-) diff --git a/codeCore/src/prog8/code/ast/AstStatements.kt b/codeCore/src/prog8/code/ast/AstStatements.kt index 558c1e6a8..083408304 100644 --- a/codeCore/src/prog8/code/ast/AstStatements.kt +++ b/codeCore/src/prog8/code/ast/AstStatements.kt @@ -60,7 +60,7 @@ class PtAssignment(position: Position) : PtNode(position), IPtAssignment class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment -class PtAssignTarget(position: Position) : PtNode(position) { +class PtAssignTarget(val void: Boolean, position: Position) : PtNode(position) { val identifier: PtIdentifier? get() = children.single() as? PtIdentifier val array: PtArrayIndexer? @@ -78,7 +78,7 @@ class PtAssignTarget(position: Position) : PtNode(position) { } } - infix fun isSameAs(expression: PtExpression): Boolean = expression.isSameAs(this) + infix fun isSameAs(expression: PtExpression): Boolean = !void && expression.isSameAs(this) } diff --git a/codeCore/src/prog8/code/optimize/Optimizer.kt b/codeCore/src/prog8/code/optimize/Optimizer.kt index 2e0d0323c..5113844bc 100644 --- a/codeCore/src/prog8/code/optimize/Optimizer.kt +++ b/codeCore/src/prog8/code/optimize/Optimizer.kt @@ -89,7 +89,7 @@ private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorRepor singleReplacement2.parent = occurrence2.parent val tempassign = PtAssignment(binexpr.position).also { assign -> - assign.add(PtAssignTarget(binexpr.position).also { tgt-> + assign.add(PtAssignTarget(false, binexpr.position).also { tgt-> tgt.add(PtIdentifier("$containerScopedName.$tempvarName", datatype, binexpr.position)) }) assign.add(occurrence1) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index b21cf45aa..15164682d 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -35,7 +35,6 @@ internal class AssignmentAsmGen(private val program: PtProgram, } fun translateMultiAssign(assignment: PtAssignment) { - // TODO("translate multi-value assignment ${assignment.position}") val values = assignment.value as? PtFunctionCall ?: throw AssemblyError("only function calls can return multiple values in a multi-assign") @@ -66,35 +65,48 @@ internal class AssignmentAsmGen(private val program: PtProgram, fun assignRegisterResults(registersResults: List>) { registersResults.forEach { (returns, target) -> - val targetIdent = (target as PtAssignTarget).identifier - val targetMem = target.memory - if(targetIdent!=null || targetMem!=null) { - val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen) - when(returns.type) { - in ByteDatatypesWithBoolean -> { - assignRegisterByte(tgt, returns.register.registerOrPair!!.asCpuRegister(), false, false) + target as PtAssignTarget + if(!target.void) { + val targetIdent = target.identifier + val targetMem = target.memory + if(targetIdent!=null || targetMem!=null) { + val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen) + when(returns.type) { + in ByteDatatypesWithBoolean -> { + assignRegisterByte(tgt, returns.register.registerOrPair!!.asCpuRegister(), false, false) + } + in WordDatatypes -> { + assignRegisterpairWord(tgt, returns.register.registerOrPair!!) + } + else -> throw AssemblyError("weird dt") } - in WordDatatypes -> { - assignRegisterpairWord(tgt, returns.register.registerOrPair!!) - } - else -> throw AssemblyError("weird dt") } + else TODO("array target for multi-value assignment") // Not done yet due to result register clobbering complexity } - else TODO("array target for multi-value assignment") // Not done yet due to result register clobbering complexity } } val assignmentTargets = assignment.children.dropLast(1) if(sub.returns.size==assignmentTargets.size) { // because we can only handle integer results right now we can just zip() it all up - val (statusFlagResult, registersResults) = sub.returns.zip(assignmentTargets).partition { it.first.register.statusflag!=null } - if(statusFlagResult.isNotEmpty()) { - val (returns, target) = statusFlagResult.single() + val (statusFlagResults, registersResults) = sub.returns.zip(assignmentTargets).partition { it.first.register.statusflag!=null } + if(statusFlagResults.isNotEmpty()) { + if(statusFlagResults.size>1) + TODO("handle multiple status flag results") + val (returns, target) = statusFlagResults.single() if(returns.register.statusflag!=Statusflag.Pc) TODO("other status flag for return value") target as PtAssignTarget - if(registersResults.all { (it.second as PtAssignTarget).identifier!=null}) { + if(target.void) { + // forget about the Carry status flag, only assign the normal return values + assignRegisterResults(registersResults) + return + } + if(registersResults.all { + val tgt = it.second as PtAssignTarget + tgt.void || tgt.identifier!=null}) + { // all other results are just stored into identifiers directly so first handle those // (simple store instructions that don't modify the carry flag) assignRegisterResults(registersResults) @@ -104,16 +116,6 @@ internal class AssignmentAsmGen(private val program: PtProgram, assignCarryResult(target, needsToSaveA(registersResults)) } assignRegisterResults(registersResults) - } else if (sub.returns.size>assignmentTargets.size) { - // Targets and values don't match. Skip status flag results, assign only the normal value results. - val targets = assignmentTargets.iterator() - sub.returns.forEach { - if(it.register.registerOrPair!=null) { - val target = targets.next() as PtAssignTarget - assignRegisterResults(listOf(it to target)) - } - } - require(!targets.hasNext()) } else { throw AssemblyError("number of values and targets don't match") } @@ -706,7 +708,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } TargetStorageKind.MEMORY -> { - val tgt = PtAssignTarget(assign.target.position) + val tgt = PtAssignTarget(false, assign.target.position) val targetmem = assign.target.memory!! val mem = PtMemoryByte(targetmem.position) mem.add(targetmem.address) @@ -716,7 +718,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, assignTrue.add(PtNumber.fromBoolean(true, assign.position)) } TargetStorageKind.ARRAY -> { - val tgt = PtAssignTarget(assign.target.position) + val tgt = PtAssignTarget(false, assign.target.position) val targetarray = assign.target.array!! val array = PtArrayIndexer(assign.target.datatype, targetarray.position) array.add(targetarray.variable) diff --git a/codeGenCpu6502/test/TestCodegen.kt b/codeGenCpu6502/test/TestCodegen.kt index e98c22096..b7b4a9dd8 100644 --- a/codeGenCpu6502/test/TestCodegen.kt +++ b/codeGenCpu6502/test/TestCodegen.kt @@ -53,7 +53,7 @@ class TestCodegen: FunSpec({ sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY)) val assign = PtAugmentedAssign("+=", Position.DUMMY) - val target = PtAssignTarget(Position.DUMMY).also { + val target = PtAssignTarget(false, Position.DUMMY).also { val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx -> idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY)) idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY)) @@ -68,7 +68,7 @@ class TestCodegen: FunSpec({ sub.add(assign) val prefixAssign = PtAugmentedAssign("-", Position.DUMMY) - val prefixTarget = PtAssignTarget(Position.DUMMY).also { + val prefixTarget = PtAssignTarget(false, Position.DUMMY).also { it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) } prefixAssign.add(prefixTarget) @@ -76,7 +76,7 @@ class TestCodegen: FunSpec({ sub.add(prefixAssign) val numberAssign = PtAugmentedAssign("-=", Position.DUMMY) - val numberAssignTarget = PtAssignTarget(Position.DUMMY).also { + val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also { it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) } numberAssign.add(numberAssignTarget) @@ -84,7 +84,7 @@ class TestCodegen: FunSpec({ sub.add(numberAssign) val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY) - val cxregAssignTarget = PtAssignTarget(Position.DUMMY).also { + val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also { it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) } cxregAssign.add(cxregAssignTarget) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt index cfe6b6aa1..291efa018 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt @@ -25,25 +25,15 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express val assignmentTargets = assignment.children.dropLast(1) addToResult(result, funcCall, funcCall.resultReg, funcCall.resultFpReg) if(sub.returns.size==assignmentTargets.size) { - // Targets and values match. Assign all the things. + // Targets and values match. Assign all the things. Skip 'void' targets. sub.returns.zip(assignmentTargets).zip(funcCall.multipleResultRegs).forEach { - val regNumber = it.second - val returns = it.first.first val target = it.first.second as PtAssignTarget - result += assignCpuRegister(returns, regNumber, target) - } - } else if (sub.returns.size>assignmentTargets.size) { - // Targets and values don't match. Skip status flag results, assign only the normal value results. - val targets = assignmentTargets.iterator() - sub.returns.zip(funcCall.multipleResultRegs).forEach { - val returns = it.first - if(returns.register.registerOrPair!=null) { - val target = targets.next() as PtAssignTarget + if(!target.void) { val regNumber = it.second + val returns = it.first.first result += assignCpuRegister(returns, regNumber, target) } } - require(!targets.hasNext()) } else { throw AssemblyError("number of values and targets don't match") } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index bc66cf4ed..299da12e3 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -854,7 +854,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks { val assignment = PtAssignment(target.position) - val assignTarget = PtAssignTarget(target.position) + val assignTarget = PtAssignTarget(false, target.position) assignTarget.children.add(target) assignment.children.add(assignTarget) assignment.children.add(PtIrRegister(register, target.type, target.position)) diff --git a/codeGenIntermediate/test/TestVmCodeGen.kt b/codeGenIntermediate/test/TestVmCodeGen.kt index ea443bd86..7bb99778b 100644 --- a/codeGenIntermediate/test/TestVmCodeGen.kt +++ b/codeGenIntermediate/test/TestVmCodeGen.kt @@ -50,7 +50,7 @@ class TestVmCodeGen: FunSpec({ sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY)) val assign = PtAugmentedAssign("+=", Position.DUMMY) - val target = PtAssignTarget(Position.DUMMY).also { + val target = PtAssignTarget(false, Position.DUMMY).also { val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx -> idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY)) idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY)) @@ -65,7 +65,7 @@ class TestVmCodeGen: FunSpec({ sub.add(assign) val prefixAssign = PtAugmentedAssign("-", Position.DUMMY) - val prefixTarget = PtAssignTarget(Position.DUMMY).also { + val prefixTarget = PtAssignTarget(false, Position.DUMMY).also { it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) } prefixAssign.add(prefixTarget) @@ -73,7 +73,7 @@ class TestVmCodeGen: FunSpec({ sub.add(prefixAssign) val numberAssign = PtAugmentedAssign("+=", Position.DUMMY) - val numberAssignTarget = PtAssignTarget(Position.DUMMY).also { + val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also { it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) } numberAssign.add(numberAssignTarget) @@ -81,7 +81,7 @@ class TestVmCodeGen: FunSpec({ sub.add(numberAssign) val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY) - val cxregAssignTarget = PtAssignTarget(Position.DUMMY).also { + val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also { it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) } cxregAssign.add(cxregAssignTarget) diff --git a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt index 9399a6b56..b3b097c19 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt @@ -309,7 +309,7 @@ internal class ConstantIdentifierReplacer( val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position) return if(arrayIdx.parent is AssignTarget) { val memwrite = DirectMemoryWrite(add, identifier.position) - val assignTarget = AssignTarget(null, null, memwrite, null, identifier.position) + val assignTarget = AssignTarget(null, null, memwrite, null, false, identifier.position) listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent)) } else { val memread = DirectMemoryRead(add, identifier.position) diff --git a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt index cadf0ba02..51cabd1c0 100644 --- a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt @@ -162,7 +162,7 @@ class StatementOptimizer(private val program: Program, // for loop over a (constant) range of just a single value-- optimize the loop away // loopvar/reg = range value , follow by block val scope = AnonymousScope(mutableListOf(), forLoop.position) - scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position)) + scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position)) scope.statements.addAll(forLoop.body.statements) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) } @@ -177,7 +177,7 @@ class StatementOptimizer(private val program: Program, val character = options.compTarget.encodeString(sv.value, sv.encoding)[0] val byte = NumericLiteral(DataType.UBYTE, character.toDouble(), iterable.position) val scope = AnonymousScope(mutableListOf(), forLoop.position) - scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position)) + scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position)) scope.statements.addAll(forLoop.body.statements) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) } @@ -190,7 +190,7 @@ class StatementOptimizer(private val program: Program, if(av!=null) { val scope = AnonymousScope(mutableListOf(), forLoop.position) scope.statements.add(Assignment( - AssignTarget(forLoop.loopVar, null, null, null, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position), + AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position), AssignmentOrigin.OPTIMIZER, forLoop.position)) scope.statements.addAll(forLoop.body.statements) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) @@ -206,7 +206,7 @@ class StatementOptimizer(private val program: Program, val pos = forLoop.position val loopVar = forLoop.loopVar val addSubOne = BinaryExpression(loopVar.copy(), if(inc) "+" else "-", NumericLiteral.optimalInteger(1, pos), pos, false) - return Assignment(AssignTarget(loopVar.copy(), null, null, null, pos), addSubOne, AssignmentOrigin.USERCODE, pos) + return Assignment(AssignTarget(loopVar.copy(), null, null, null, false, pos), addSubOne, AssignmentOrigin.USERCODE, pos) } if (range != null && range.to.constValue(program)?.number == 0.0 && range.step.constValue(program)?.number==-1.0) { @@ -219,7 +219,7 @@ class StatementOptimizer(private val program: Program, val decOne = incOrDec(false) forLoop.body.statements.add(decOne) val replacement = AnonymousScope(mutableListOf( - Assignment(AssignTarget(forLoop.loopVar.copy(), null, null, null, pos), + Assignment(AssignTarget(forLoop.loopVar.copy(), null, null, null, false, pos), fromExpr, AssignmentOrigin.OPTIMIZER, pos), UntilLoop(forLoop.body, condition, pos) ), pos) @@ -454,12 +454,6 @@ class StatementOptimizer(private val program: Program, override fun after(whenStmt: When, parent: Node): Iterable { - fun replaceWithIf(condition: Expression, trueBlock: AnonymousScope, elseBlock: AnonymousScope?): List { - val ifStmt = IfElse(condition, trueBlock, elseBlock ?: AnonymousScope(mutableListOf(), whenStmt.position), whenStmt.position) - errors.info("for boolean condition a normal if statement is preferred", whenStmt.position) - return listOf(IAstModification.ReplaceNode(whenStmt, ifStmt, parent)) - } - val constantValue = whenStmt.condition.constValue(program)?.number if(constantValue!=null) { // when condition is a constant diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index f79b109a8..db78393ae 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -53,7 +53,7 @@ internal class AstChecker(private val program: Program, override fun visit(module: Module) { super.visit(module) if(module.name.startsWith('_')) - errors.err("module names cannot start with an underscore", module.position) + errors.err("identifiers cannot start with an underscore", module.position) val directives = module.statements.filterIsInstance().groupBy { it.directive } directives.filter { it.value.size > 1 }.forEach{ entry -> when(entry.key) { @@ -67,6 +67,14 @@ internal class AstChecker(private val program: Program, if(identifier.nameInSource.any { it.startsWith('_') }) { errors.err("identifiers cannot start with an underscore", identifier.position) } + if(identifier.nameInSource.any { it=="void" }) { + // 'void' as "identifier" is only allowed as part of a multi-assignment expression + if (!(identifier.nameInSource == listOf("void") && (identifier.parent as? AssignTarget)?.multi?.isNotEmpty() == true + || identifier.parent is AssignTarget && (identifier.parent.parent as? AssignTarget)?.multi?.isNotEmpty() == true) + ) { + errors.err("identifiers cannot contain the 'void' keyword", identifier.position) + } + } checkLongType(identifier) val stmt = identifier.targetStatement(program) @@ -273,7 +281,7 @@ internal class AstChecker(private val program: Program, override fun visit(block: Block) { if(block.name.startsWith('_')) - errors.err("block names cannot start with an underscore", block.position) + errors.err("identifiers cannot start with an underscore", block.position) val addr = block.address if(addr!=null && addr>65535u) { @@ -305,7 +313,7 @@ internal class AstChecker(private val program: Program, override fun visit(label: Label) { if(label.name.startsWith('_')) - errors.err("labels cannot start with an underscore", label.position) + errors.err("identifiers cannot start with an underscore", label.position) // scope check if(label.parent !is Block && label.parent !is Subroutine && label.parent !is AnonymousScope) { @@ -349,7 +357,7 @@ internal class AstChecker(private val program: Program, fun err(msg: String) = errors.err(msg, subroutine.position) if(subroutine.name.startsWith('_')) - errors.err("subroutine names cannot start with an underscore", subroutine.position) + errors.err("identifiers cannot start with an underscore", subroutine.position) if(subroutine.name in BuiltinFunctions) err("cannot redefine a built-in function") @@ -490,7 +498,7 @@ internal class AstChecker(private val program: Program, // Instead, their reference (address) should be passed (as an UWORD). for(p in subroutine.parameters) { if(p.name.startsWith('_')) - errors.err("parameter names cannot start with an underscore", p.position) + errors.err("identifiers cannot start with an underscore", p.position) if(p.type in PassByReferenceDatatypes && p.type !in listOf(DataType.STR, DataType.ARRAY_UB)) { errors.err("this pass-by-reference type can't be used as a parameter type. Instead, use an uword to receive the address, or access the variable from the outer scope directly.", p.position) @@ -591,38 +599,15 @@ internal class AstChecker(private val program: Program, return } val targets = assignment.target.multi!! - if(fcallTarget.returntypes.sizetargets.size) { - // You can have LESS assign targets than the number of result values, - // as long as the result values contain booleans that are returned in cpu status flags (like Carry). - // These may be ignored in the assignment - only "true" values NEED to have a target. - val numberOfNormalValues = fcallTarget.asmReturnvaluesRegisters.count { it.registerOrPair!=null } - if(numberOfNormalValues != targets.size) { - errors.err("multiple return values and too few assignment targets, need at least $numberOfNormalValues", fcall.position) - return - } - // check the types of the 'normal' values that are being assigned - val returnTypesAndRegisters = fcallTarget.returntypes.zip(fcallTarget.asmReturnvaluesRegisters) - returnTypesAndRegisters.zip(targets).withIndex().forEach { (index, p) -> - val (returnType, register) = p.first - if(register.registerOrPair!=null) { - val target = p.second - val targetDt = target.inferType(program).getOr(DataType.UNDEFINED) - if (!(returnType isAssignableTo targetDt)) - errors.err("can't assign returnvalue #${index + 1} to corresponding target; $returnType vs $targetDt", target.position) - } - } - } else { - // check all the assigment target types - fcallTarget.returntypes.zip(targets).withIndex().forEach { (index, p) -> - val (returnType, target) = p - val targetDt = target.inferType(program).getOr(DataType.UNDEFINED) - if (!(returnType isAssignableTo targetDt)) - errors.err("can't assign returnvalue #${index + 1} to corresponding target; $returnType vs $targetDt", target.position) - } + fcallTarget.returntypes.zip(targets).withIndex().forEach { (index, p) -> + val (returnType, target) = p + val targetDt = target.inferType(program).getOr(DataType.UNDEFINED) + if (!target.void && !(returnType isAssignableTo targetDt)) + errors.err("can't assign returnvalue #${index + 1} to corresponding target; $returnType vs $targetDt", target.position) } } diff --git a/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt b/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt index 9795c0605..43c2c383a 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt @@ -121,7 +121,7 @@ class AstPreprocessor(val program: Program, // we need to handle multi-decl here too, the desugarer maybe has not processed it here yet... if(decl.value!=null) { decl.names.forEach { name -> - val target = AssignTarget(IdentifierReference(listOf(name), decl.position), null, null, null, decl.position) + val target = AssignTarget(IdentifierReference(listOf(name), decl.position), null, null, null, false, decl.position) val assign = Assignment(target.copy(), decl.value!!.copy(), AssignmentOrigin.VARINIT, decl.position) replacements.add(IAstModification.InsertAfter(decl, assign, scope)) } @@ -137,7 +137,7 @@ class AstPreprocessor(val program: Program, } else { // handle declaration of a single variable if(decl.value!=null && (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL)) { - val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, null, decl.position) + val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, null, false, decl.position) val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position) replacements.add(IAstModification.ReplaceNode(decl, assign, scope)) decl.value = null diff --git a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt index f27d11713..35443d0f1 100644 --- a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt +++ b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt @@ -183,7 +183,7 @@ _after: } if(functionCall.target.nameInSource==listOf("poke")) { // poke(a, v) is synonymous with @(a) = v - val tgt = AssignTarget(null, null, DirectMemoryWrite(functionCall.args[0], position), null, position) + val tgt = AssignTarget(null, null, DirectMemoryWrite(functionCall.args[0], position), null, false, position) val assign = Assignment(tgt, functionCall.args[1], AssignmentOrigin.OPTIMIZER, position) return listOf(IAstModification.ReplaceNode(functionCall as Node, assign, parent)) } @@ -222,7 +222,7 @@ _after: return if(parent is AssignTarget) { // assignment to array val memwrite = DirectMemoryWrite(address, arrayIndexedExpression.position) - val newtarget = AssignTarget(null, null, memwrite, null, arrayIndexedExpression.position) + val newtarget = AssignTarget(null, null, memwrite, null, false, arrayIndexedExpression.position) listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent)) } else { // read from array diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt index a99564617..ebc2e1c13 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt @@ -148,14 +148,14 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr } private fun transform(srcTarget: AssignTarget): PtAssignTarget { - val target = PtAssignTarget(srcTarget.position) + val target = PtAssignTarget(srcTarget.void, srcTarget.position) if(srcTarget.identifier!=null) target.add(transform(srcTarget.identifier!!)) else if(srcTarget.arrayindexed!=null) target.add(transform(srcTarget.arrayindexed!!)) else if(srcTarget.memoryAddress!=null) target.add(transform(srcTarget.memoryAddress!!)) - else + else if(!srcTarget.void) throw FatalAstException("invalid AssignTarget") return target } diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index 965ae6f43..24d0b1611 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -60,7 +60,7 @@ internal class StatementReorderer( // Add assignment to initialize with zero // Note: for block-level vars, this will introduce assignments in the block scope. These have to be dealt with correctly later. val identifier = IdentifierReference(listOf(decl.name), decl.position) - val assignzero = Assignment(AssignTarget(identifier, null, null, null, decl.position), + val assignzero = Assignment(AssignTarget(identifier, null, null, null, false, decl.position), decl.zeroElementValue(), AssignmentOrigin.VARINIT, decl.position) return listOf(IAstModification.InsertAfter( decl, assignzero, parent as IStatementContainer @@ -73,7 +73,7 @@ internal class StatementReorderer( // So basically consider 'ubyte xx=99' as a short form for 'ubyte xx; xx=99' val pos = decl.value!!.position val identifier = IdentifierReference(listOf(decl.name), pos) - val assign = Assignment(AssignTarget(identifier, null, null, null, pos), + val assign = Assignment(AssignTarget(identifier, null, null, null, false, pos), decl.value!!, AssignmentOrigin.VARINIT, pos) decl.value = null return listOf(IAstModification.InsertAfter( @@ -92,7 +92,7 @@ internal class StatementReorderer( if(target!=null && target.isArray) { val pos = decl.value!!.position val identifier = IdentifierReference(listOf(decl.name), pos) - val assign = Assignment(AssignTarget(identifier, null, null, null, pos), + val assign = Assignment(AssignTarget(identifier, null, null, null, false, pos), decl.value!!, AssignmentOrigin.VARINIT, pos) decl.value = null return listOf(IAstModification.InsertAfter( diff --git a/compiler/test/TestMemory.kt b/compiler/test/TestMemory.kt index ecb9b3b3e..f5fd76bc5 100644 --- a/compiler/test/TestMemory.kt +++ b/compiler/test/TestMemory.kt @@ -36,49 +36,49 @@ class TestMemory: FunSpec({ test("assignment target not in mapped IO space C64") { var memexpr = NumericLiteral.optimalInteger(0x0002, Position.DUMMY) - var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) target.isIOAddress(c64target.machine) shouldBe false memexpr = NumericLiteral.optimalInteger(0x1000, Position.DUMMY) - target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) target.isIOAddress(c64target.machine) shouldBe false memexpr = NumericLiteral.optimalInteger(0x9fff, Position.DUMMY) - target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) target.isIOAddress(c64target.machine) shouldBe false memexpr = NumericLiteral.optimalInteger(0xa000, Position.DUMMY) - target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) target.isIOAddress(c64target.machine) shouldBe false memexpr = NumericLiteral.optimalInteger(0xc000, Position.DUMMY) - target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) target.isIOAddress(c64target.machine) shouldBe false memexpr = NumericLiteral.optimalInteger(0xcfff, Position.DUMMY) - target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) target.isIOAddress(c64target.machine) shouldBe false memexpr = NumericLiteral.optimalInteger(0xeeee, Position.DUMMY) - target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) target.isIOAddress(c64target.machine) shouldBe false memexpr = NumericLiteral.optimalInteger(0xffff, Position.DUMMY) - target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) target.isIOAddress(c64target.machine) shouldBe false @@ -87,25 +87,25 @@ class TestMemory: FunSpec({ test("assign target in mapped IO space C64") { var memexpr = NumericLiteral.optimalInteger(0x0000, Position.DUMMY) - var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) target.isIOAddress(c64target.machine) shouldBe true memexpr = NumericLiteral.optimalInteger(0x0001, Position.DUMMY) - target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) target.isIOAddress(c64target.machine) shouldBe true memexpr = NumericLiteral.optimalInteger(0xd000, Position.DUMMY) - target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) target.isIOAddress(c64target.machine) shouldBe true memexpr = NumericLiteral.optimalInteger(0xdfff, Position.DUMMY) - target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) target.isIOAddress(c64target.machine) shouldBe true @@ -114,7 +114,7 @@ class TestMemory: FunSpec({ fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget { val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) val memexpr = IdentifierReference(listOf("address"), Position.DUMMY) - val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(decl, assignment)) return target @@ -137,13 +137,13 @@ class TestMemory: FunSpec({ test("memory expression mapped to IO memory on C64") { var memexpr = PrefixExpression("+", NumericLiteral.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY) - var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) target.isIOAddress(c64target.machine) shouldBe false memexpr = PrefixExpression("+", NumericLiteral.optimalInteger(0xd020, Position.DUMMY), Position.DUMMY) - target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) wrapWithProgram(listOf(assign)) target.isIOAddress(c64target.machine) shouldBe true @@ -151,7 +151,7 @@ class TestMemory: FunSpec({ test("regular variable not in mapped IO ram on C64") { val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), null, false, false, Position.DUMMY) - val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, Position.DUMMY) + val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) @@ -163,7 +163,7 @@ class TestMemory: FunSpec({ test("memory mapped variable not in mapped IO ram on C64") { val address = 0x1000u val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) - val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, Position.DUMMY) + val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) @@ -175,7 +175,7 @@ class TestMemory: FunSpec({ test("memory mapped variable in mapped IO ram on C64") { val address = 0xd020u val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) - val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, Position.DUMMY) + val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) @@ -187,7 +187,7 @@ class TestMemory: FunSpec({ test("array not in mapped IO ram") { val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), null, false, false, Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) - val target = AssignTarget(null, arrayindexed, null, null, Position.DUMMY) + val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) @@ -200,7 +200,7 @@ class TestMemory: FunSpec({ val address = 0x1000u val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) - val target = AssignTarget(null, arrayindexed, null, null, Position.DUMMY) + val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) @@ -213,7 +213,7 @@ class TestMemory: FunSpec({ val address = 0xd800u val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) - val target = AssignTarget(null, arrayindexed, null, null, Position.DUMMY) + val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test")) diff --git a/compiler/test/ast/TestSubroutines.kt b/compiler/test/ast/TestSubroutines.kt index d6810783a..92f9578ff 100644 --- a/compiler/test/ast/TestSubroutines.kt +++ b/compiler/test/ast/TestSubroutines.kt @@ -115,7 +115,8 @@ main { cx16.r0L, flag = test2(12345, 5566, flag, -42) cx16.r1, flag, bytevar = test3() - cx16.r1, bytevar = test3() ; omitting the status flag result should also work + cx16.r1, void, bytevar = test3() + void, void, void = test3() } asmsub test2(uword arg @AY, uword arg2 @R1, bool flag @Pc, byte value @X) -> ubyte @A, bool @Pc { @@ -140,23 +141,30 @@ main { val result = compileText(Cx16Target(), false, src, errors, true)!! errors.errors.size shouldBe 0 val start = result.codegenAst!!.entrypoint()!! - start.children.size shouldBe 8 + start.children.size shouldBe 9 val a1_1 = start.children[4] as PtAssignment val a1_2 = start.children[5] as PtAssignment val a1_3 = start.children[6] as PtAssignment + val a1_4 = start.children[7] as PtAssignment a1_1.multiTarget shouldBe true a1_2.multiTarget shouldBe true a1_3.multiTarget shouldBe true + a1_4.multiTarget shouldBe true a1_1.children.size shouldBe 3 a1_2.children.size shouldBe 4 - a1_3.children.size shouldBe 3 + a1_3.children.size shouldBe 4 + a1_4.children.size shouldBe 4 (a1_1.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r0L") (a1_1.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_flag") (a1_2.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r1") (a1_2.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_flag") (a1_2.children[2] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_bytevar") (a1_3.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r1") - (a1_3.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_bytevar") + (a1_3.children[1] as PtAssignTarget).void shouldBe true + (a1_3.children[2] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_bytevar") + (a1_4.children[0] as PtAssignTarget).void shouldBe true + (a1_4.children[1] as PtAssignTarget).void shouldBe true + (a1_4.children[2] as PtAssignTarget).void shouldBe true } test("multi-assign from romsub") { @@ -169,7 +177,8 @@ main { flag = test(42) cx16.r0L, flag = test2(12345, 5566, flag, -42) cx16.r1, flag, bytevar = test3() - cx16.r1, bytevar = test3() ; omitting the status flag result should also work + cx16.r1, void, bytevar = test3() + void, void, void = test3() } romsub ${'$'}8000 = test(ubyte arg @A) -> bool @Pc @@ -182,22 +191,29 @@ main { val result = compileText(Cx16Target(), false, src, errors, true)!! errors.errors.size shouldBe 0 val start = result.codegenAst!!.entrypoint()!! - start.children.size shouldBe 9 + start.children.size shouldBe 10 val a1_1 = start.children[5] as PtAssignment val a1_2 = start.children[6] as PtAssignment val a1_3 = start.children[7] as PtAssignment + val a1_4 = start.children[8] as PtAssignment a1_1.multiTarget shouldBe true a1_2.multiTarget shouldBe true a1_3.multiTarget shouldBe true + a1_4.multiTarget shouldBe true a1_1.children.size shouldBe 3 a1_2.children.size shouldBe 4 - a1_3.children.size shouldBe 3 + a1_3.children.size shouldBe 4 + a1_4.children.size shouldBe 4 (a1_1.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r0L") (a1_1.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_flag") (a1_2.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r1") (a1_2.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_flag") (a1_2.children[2] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_bytevar") (a1_3.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r1") - (a1_3.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_bytevar") + (a1_3.children[1] as PtAssignTarget).void shouldBe true + (a1_3.children[2] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_bytevar") + (a1_4.children[0] as PtAssignTarget).void shouldBe true + (a1_4.children[1] as PtAssignTarget).void shouldBe true + (a1_4.children[2] as PtAssignTarget).void shouldBe true } }) diff --git a/compiler/test/codegeneration/TestAsmGenSymbols.kt b/compiler/test/codegeneration/TestAsmGenSymbols.kt index ad5877620..3ea33d4af 100644 --- a/compiler/test/codegeneration/TestAsmGenSymbols.kt +++ b/compiler/test/codegeneration/TestAsmGenSymbols.kt @@ -50,7 +50,7 @@ class TestAsmGenSymbols: StringSpec({ val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", emptyList(), null, false, false, Position.DUMMY) val labelInSub = Label("locallabel", Position.DUMMY) - val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, null, Position.DUMMY) + val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, null, false, Position.DUMMY) val assign1 = Assignment(tgt, IdentifierReference(listOf("localvar"), Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assign2 = Assignment(tgt, AddressOf(IdentifierReference(listOf("locallabel"), Position.DUMMY), null, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) val assign3 = Assignment(tgt, AddressOf(IdentifierReference(listOf("var_outside"), Position.DUMMY), null, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) diff --git a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt index e486e5842..71d44805f 100644 --- a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt +++ b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt @@ -406,16 +406,20 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program: } override fun visit(assignTarget: AssignTarget) { - assignTarget.memoryAddress?.accept(this) - assignTarget.identifier?.accept(this) - assignTarget.arrayindexed?.accept(this) - val multi = assignTarget.multi - if(multi!=null) { - multi.dropLast(1).forEach { target -> - target.accept(this) - output(", ") + if(assignTarget.void) + output("void") + else { + assignTarget.memoryAddress?.accept(this) + assignTarget.identifier?.accept(this) + assignTarget.arrayindexed?.accept(this) + val multi = assignTarget.multi + if (multi != null) { + multi.dropLast(1).forEach { target -> + target.accept(this) + output(", ") + } + multi.last().accept(this) } - multi.last().accept(this) } } diff --git a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt index bd8cf164e..0764e2d75 100644 --- a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt +++ b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt @@ -246,7 +246,7 @@ private fun Asmsub_paramsContext.toAst(): List val identifiers = vardecl.identifier() if(identifiers.size>1) throw SyntaxError("parameter name must be singular", identifiers[0].toPosition()) - val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME() ?: identifiers[0].UNDERSCOREPLACEHOLDER() + val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME() ?: identifiers[0].VOID() AsmSubroutineParameter(identifiername.text, datatype, registerorpair, statusregister, toPosition()) } @@ -317,22 +317,27 @@ private fun Sub_paramsContext.toAst(): List = val identifiers = it.identifier() if(identifiers.size>1) throw SyntaxError("parameter name must be singular", identifiers[0].toPosition()) - val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME() ?: identifiers[0].UNDERSCOREPLACEHOLDER() + val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME() ?: identifiers[0].VOID() SubroutineParameter(identifiername.text, datatype, it.toPosition()) } private fun Assign_targetContext.toAst() : AssignTarget { return when(this) { - is IdentifierTargetContext -> - AssignTarget(scoped_identifier().toAst(), null, null, null, scoped_identifier().toPosition()) + is IdentifierTargetContext -> { + val identifier = scoped_identifier().toAst() + if(identifier.nameInSource==listOf("void")) + AssignTarget(null, null, null, null, true, scoped_identifier().toPosition()) + else + AssignTarget(identifier, null, null, null, false, scoped_identifier().toPosition()) + } is MemoryTargetContext -> - AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), directmemory().toPosition()), null, toPosition()) + AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), directmemory().toPosition()), null, false, toPosition()) is ArrayindexedTargetContext -> { val ax = arrayindexed() val arrayvar = ax.scoped_identifier().toAst() val index = ax.arrayindex().toAst() val arrayindexed = ArrayIndexedExpression(arrayvar, index, ax.toPosition()) - AssignTarget(null, arrayindexed, null, null, toPosition()) + AssignTarget(null, arrayindexed, null, null, false, toPosition()) } else -> throw FatalAstException("weird assign target node $this") } @@ -340,7 +345,7 @@ private fun Assign_targetContext.toAst() : AssignTarget { private fun Multi_assign_targetContext.toAst() : AssignTarget { val targets = this.assign_target().map { it.toAst() } - return AssignTarget(null, null, null, targets, toPosition()) + return AssignTarget(null, null, null, targets, false, toPosition()) } private fun ClobberContext.toAst() : Set { @@ -672,7 +677,7 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl else -> ZeropageWish.DONTCARE } val identifiers = identifier() - val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME() ?: identifiers[0].UNDERSCOREPLACEHOLDER() + val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME() ?: identifiers[0].VOID() val name = if(identifiers.size==1) identifiername.text else "" val isArray = ARRAYSIG() != null || arrayindex() != null val split = options.SPLIT().isNotEmpty() @@ -695,7 +700,7 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl arrayindex()?.toAst(), name, if(identifiers.size==1) emptyList() else identifiers.map { - val idname = it.NAME() ?: it.UNDERSCORENAME() ?: it.UNDERSCOREPLACEHOLDER() + val idname = it.NAME() ?: it.UNDERSCORENAME() ?: it.VOID() idname.text }, value, diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index 9d9b20374..b2ce0269a 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -494,6 +494,7 @@ data class AssignTarget(var identifier: IdentifierReference?, var arrayindexed: ArrayIndexedExpression?, val memoryAddress: DirectMemoryWrite?, val multi: List?, + val void: Boolean, override val position: Position) : Node { override lateinit var parent: Node @@ -517,7 +518,7 @@ data class AssignTarget(var identifier: IdentifierReference?, fun accept(visitor: IAstVisitor) = visitor.visit(this) fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) - override fun copy() = AssignTarget(identifier?.copy(), arrayindexed?.copy(), memoryAddress?.copy(), multi?.toList(), position) + override fun copy() = AssignTarget(identifier?.copy(), arrayindexed?.copy(), memoryAddress?.copy(), multi?.toList(), void, position) override fun referencesIdentifier(nameInSource: List): Boolean = identifier?.referencesIdentifier(nameInSource)==true || arrayindexed?.referencesIdentifier(nameInSource)==true || @@ -577,6 +578,8 @@ data class AssignTarget(var identifier: IdentifierReference?, fun isSameAs(other: AssignTarget, program: Program): Boolean { if (this === other) return true + if(void && other.void) + return true if (this.identifier != null && other.identifier != null) return this.identifier!!.nameInSource == other.identifier!!.nameInSource if (this.memoryAddress != null && other.memoryAddress != null) { diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 6b0033004..67bba57d2 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -684,13 +684,13 @@ are all assigned to individual assignment targets. You simply write them as a co asmsub multisub() -> uword @AY, bool @Pc, ubyte @X { ... } -**There is also a special rule:** you are allowed to omit assignments of the boolean values returned in status registers such as the carry flag. -So in the case of multisub() above, you could also write `wordvar, bytevar = multisub()` and leave out the carry flag. -The compiler will try to assign the normal numeric values and leave the status flags untouched, which then allows you to -use a conditional branch such as `if_cs` to do something with it. This is always more efficient -than storing it in a variable and then adding an `if flag...` statement afterwards. -It can sometimes be tricky to keep the status flags that are returned from the subroutine intact though, -if the assign targets are not simple variables. In such cases, make sure you check the generated assembly code to see if it all works out. +**Skipping values:** you are allowed to omit assignments of one or more values by putting ``void`` as the assignment target. +One of the cases where this is useful is with boolean values returned in status flags such as the carry flag. +Storing that flag as a boolean in a variable first, and then possibly adding an ``if flag...`` statement afterwards, is a lot less +efficient than just keeping the flag as-is and using a conditional branch such as ``if_cs`` to do something with it. +So in the case above that could be:: + + wordvar, void, bytevar = multisub() Subroutine definitions diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 47611f6bb..9791af165 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,12 +1,6 @@ TODO ==== -try to replace the variable number of assignment targets by allowing placeholder '_' target -or try to do this with 'void' instead, is more prog8-like? - - -check souce code of examples and library, for void calls that could now be turned into multi-assign calls. - ... diff --git a/examples/test.p8 b/examples/test.p8 index 4b4a19688..1acaa823a 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -6,8 +6,9 @@ main { sub start() { ubyte @shared bytevar uword @shared wordvar + bool flag - wordvar, bytevar = test4() + wordvar, bytevar, void = test4() if_cs txt.print("true! ") else diff --git a/parser/antlr/Prog8ANTLR.g4 b/parser/antlr/Prog8ANTLR.g4 index e5be9c67c..366d4f8f0 100644 --- a/parser/antlr/Prog8ANTLR.g4 +++ b/parser/antlr/Prog8ANTLR.g4 @@ -26,7 +26,6 @@ WS : [ \t] -> skip ; VOID: 'void'; NAME : [\p{Letter}][\p{Letter}\p{Mark}\p{Digit}_]* ; // match unicode properties UNDERSCORENAME : '_' NAME ; // match unicode properties -UNDERSCOREPLACEHOLDER: '_' ; DEC_INTEGER : DEC_DIGIT (DEC_DIGIT | '_')* ; HEX_INTEGER : '$' HEX_DIGIT (HEX_DIGIT | '_')* ; BIN_INTEGER : '%' BIN_DIGIT (BIN_DIGIT | '_')* ; @@ -222,7 +221,7 @@ breakstmt : 'break'; continuestmt: 'continue'; -identifier : NAME | UNDERSCORENAME | UNDERSCOREPLACEHOLDER; +identifier : NAME | UNDERSCORENAME | VOID; scoped_identifier : identifier ('.' identifier)* ;