mirror of
https://github.com/irmen/prog8.git
synced 2025-01-13 10:29:52 +00:00
allow 'void' as dummy assign target in multi-assignment statements
This commit is contained in:
parent
788f6b44a6
commit
641f6c05d8
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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"))
|
||||
|
@ -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
|
||||
}
|
||||
})
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)* ;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user