allow 'void' as dummy assign target in multi-assignment statements

This commit is contained in:
Irmen de Jong 2024-03-31 23:43:26 +02:00
parent 788f6b44a6
commit 641f6c05d8
24 changed files with 168 additions and 175 deletions

View File

@ -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)
}

View File

@ -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)

View File

@ -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<Pair<StRomSubParameter, PtNode>>) {
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)

View File

@ -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)

View File

@ -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")
}

View File

@ -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))

View File

@ -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)

View File

@ -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)

View File

@ -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<IAstModification> {
fun replaceWithIf(condition: Expression, trueBlock: AnonymousScope, elseBlock: AnonymousScope?): List<IAstModification> {
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

View File

@ -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<Directive>().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.size<targets.size) {
errors.err("too many assignment targets, ${targets.size} targets for ${fcallTarget.returntypes.size} return values", fcall.position)
if(fcallTarget.returntypes.size!=targets.size) {
errors.err("number of assignment targets doesn't match number of return values", fcall.position)
return
}
if(fcallTarget.returntypes.size>targets.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)
}
}

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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(

View File

@ -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"))

View File

@ -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
}
})

View File

@ -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)

View File

@ -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)
}
}

View File

@ -246,7 +246,7 @@ private fun Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
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<SubroutineParameter> =
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<CpuRegister> {
@ -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 "<multiple>"
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,

View File

@ -494,6 +494,7 @@ data class AssignTarget(var identifier: IdentifierReference?,
var arrayindexed: ArrayIndexedExpression?,
val memoryAddress: DirectMemoryWrite?,
val multi: List<AssignTarget>?,
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<String>): 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) {

View File

@ -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

View File

@ -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.
...

View File

@ -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

View File

@ -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)* ;