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 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? val identifier: PtIdentifier?
get() = children.single() as? PtIdentifier get() = children.single() as? PtIdentifier
val array: PtArrayIndexer? 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 singleReplacement2.parent = occurrence2.parent
val tempassign = PtAssignment(binexpr.position).also { assign -> 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)) tgt.add(PtIdentifier("$containerScopedName.$tempvarName", datatype, binexpr.position))
}) })
assign.add(occurrence1) assign.add(occurrence1)

View File

@@ -35,7 +35,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
fun translateMultiAssign(assignment: PtAssignment) { fun translateMultiAssign(assignment: PtAssignment) {
// TODO("translate multi-value assignment ${assignment.position}")
val values = assignment.value as? PtFunctionCall val values = assignment.value as? PtFunctionCall
?: throw AssemblyError("only function calls can return multiple values in a multi-assign") ?: 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>>) { fun assignRegisterResults(registersResults: List<Pair<StRomSubParameter, PtNode>>) {
registersResults.forEach { (returns, target) -> registersResults.forEach { (returns, target) ->
val targetIdent = (target as PtAssignTarget).identifier target as PtAssignTarget
val targetMem = target.memory if(!target.void) {
if(targetIdent!=null || targetMem!=null) { val targetIdent = target.identifier
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen) val targetMem = target.memory
when(returns.type) { if(targetIdent!=null || targetMem!=null) {
in ByteDatatypesWithBoolean -> { val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
assignRegisterByte(tgt, returns.register.registerOrPair!!.asCpuRegister(), false, false) 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) val assignmentTargets = assignment.children.dropLast(1)
if(sub.returns.size==assignmentTargets.size) { if(sub.returns.size==assignmentTargets.size) {
// because we can only handle integer results right now we can just zip() it all up // 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 } val (statusFlagResults, registersResults) = sub.returns.zip(assignmentTargets).partition { it.first.register.statusflag!=null }
if(statusFlagResult.isNotEmpty()) { if(statusFlagResults.isNotEmpty()) {
val (returns, target) = statusFlagResult.single() if(statusFlagResults.size>1)
TODO("handle multiple status flag results")
val (returns, target) = statusFlagResults.single()
if(returns.register.statusflag!=Statusflag.Pc) if(returns.register.statusflag!=Statusflag.Pc)
TODO("other status flag for return value") TODO("other status flag for return value")
target as PtAssignTarget 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 // all other results are just stored into identifiers directly so first handle those
// (simple store instructions that don't modify the carry flag) // (simple store instructions that don't modify the carry flag)
assignRegisterResults(registersResults) assignRegisterResults(registersResults)
@@ -104,16 +116,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignCarryResult(target, needsToSaveA(registersResults)) assignCarryResult(target, needsToSaveA(registersResults))
} }
assignRegisterResults(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 { } else {
throw AssemblyError("number of values and targets don't match") throw AssemblyError("number of values and targets don't match")
} }
@@ -706,7 +708,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
} }
TargetStorageKind.MEMORY -> { TargetStorageKind.MEMORY -> {
val tgt = PtAssignTarget(assign.target.position) val tgt = PtAssignTarget(false, assign.target.position)
val targetmem = assign.target.memory!! val targetmem = assign.target.memory!!
val mem = PtMemoryByte(targetmem.position) val mem = PtMemoryByte(targetmem.position)
mem.add(targetmem.address) mem.add(targetmem.address)
@@ -716,7 +718,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignTrue.add(PtNumber.fromBoolean(true, assign.position)) assignTrue.add(PtNumber.fromBoolean(true, assign.position))
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
val tgt = PtAssignTarget(assign.target.position) val tgt = PtAssignTarget(false, assign.target.position)
val targetarray = assign.target.array!! val targetarray = assign.target.array!!
val array = PtArrayIndexer(assign.target.datatype, targetarray.position) val array = PtArrayIndexer(assign.target.datatype, targetarray.position)
array.add(targetarray.variable) 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)) sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY))
val assign = PtAugmentedAssign("+=", 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 -> val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY)) idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY))
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY)) idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
@@ -68,7 +68,7 @@ class TestCodegen: FunSpec({
sub.add(assign) sub.add(assign)
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY) 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)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
prefixAssign.add(prefixTarget) prefixAssign.add(prefixTarget)
@@ -76,7 +76,7 @@ class TestCodegen: FunSpec({
sub.add(prefixAssign) sub.add(prefixAssign)
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY) 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)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
numberAssign.add(numberAssignTarget) numberAssign.add(numberAssignTarget)
@@ -84,7 +84,7 @@ class TestCodegen: FunSpec({
sub.add(numberAssign) sub.add(numberAssign)
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY) 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)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
cxregAssign.add(cxregAssignTarget) 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) val assignmentTargets = assignment.children.dropLast(1)
addToResult(result, funcCall, funcCall.resultReg, funcCall.resultFpReg) addToResult(result, funcCall, funcCall.resultReg, funcCall.resultFpReg)
if(sub.returns.size==assignmentTargets.size) { 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 { sub.returns.zip(assignmentTargets).zip(funcCall.multipleResultRegs).forEach {
val regNumber = it.second
val returns = it.first.first
val target = it.first.second as PtAssignTarget val target = it.first.second as PtAssignTarget
result += assignCpuRegister(returns, regNumber, target) if(!target.void) {
}
} 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
val regNumber = it.second val regNumber = it.second
val returns = it.first.first
result += assignCpuRegister(returns, regNumber, target) result += assignCpuRegister(returns, regNumber, target)
} }
} }
require(!targets.hasNext())
} else { } else {
throw AssemblyError("number of values and targets don't match") 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 { private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
val assignment = PtAssignment(target.position) val assignment = PtAssignment(target.position)
val assignTarget = PtAssignTarget(target.position) val assignTarget = PtAssignTarget(false, target.position)
assignTarget.children.add(target) assignTarget.children.add(target)
assignment.children.add(assignTarget) assignment.children.add(assignTarget)
assignment.children.add(PtIrRegister(register, target.type, target.position)) 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)) sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY))
val assign = PtAugmentedAssign("+=", 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 -> val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY)) idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY))
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY)) idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
@@ -65,7 +65,7 @@ class TestVmCodeGen: FunSpec({
sub.add(assign) sub.add(assign)
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY) 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)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
prefixAssign.add(prefixTarget) prefixAssign.add(prefixTarget)
@@ -73,7 +73,7 @@ class TestVmCodeGen: FunSpec({
sub.add(prefixAssign) sub.add(prefixAssign)
val numberAssign = PtAugmentedAssign("+=", Position.DUMMY) 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)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
numberAssign.add(numberAssignTarget) numberAssign.add(numberAssignTarget)
@@ -81,7 +81,7 @@ class TestVmCodeGen: FunSpec({
sub.add(numberAssign) sub.add(numberAssign)
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY) 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)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
cxregAssign.add(cxregAssignTarget) 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) val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position)
return if(arrayIdx.parent is AssignTarget) { return if(arrayIdx.parent is AssignTarget) {
val memwrite = DirectMemoryWrite(add, identifier.position) 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)) listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
} else { } else {
val memread = DirectMemoryRead(add, identifier.position) 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 // for loop over a (constant) range of just a single value-- optimize the loop away
// loopvar/reg = range value , follow by block // loopvar/reg = range value , follow by block
val scope = AnonymousScope(mutableListOf(), forLoop.position) 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) scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) 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 character = options.compTarget.encodeString(sv.value, sv.encoding)[0]
val byte = NumericLiteral(DataType.UBYTE, character.toDouble(), iterable.position) val byte = NumericLiteral(DataType.UBYTE, character.toDouble(), iterable.position)
val scope = AnonymousScope(mutableListOf(), forLoop.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) scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
} }
@@ -190,7 +190,7 @@ class StatementOptimizer(private val program: Program,
if(av!=null) { if(av!=null) {
val scope = AnonymousScope(mutableListOf(), forLoop.position) val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment( 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)) AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.addAll(forLoop.body.statements) scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
@@ -206,7 +206,7 @@ class StatementOptimizer(private val program: Program,
val pos = forLoop.position val pos = forLoop.position
val loopVar = forLoop.loopVar val loopVar = forLoop.loopVar
val addSubOne = BinaryExpression(loopVar.copy(), if(inc) "+" else "-", NumericLiteral.optimalInteger(1, pos), pos, false) 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) { 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) val decOne = incOrDec(false)
forLoop.body.statements.add(decOne) forLoop.body.statements.add(decOne)
val replacement = AnonymousScope(mutableListOf( 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), fromExpr, AssignmentOrigin.OPTIMIZER, pos),
UntilLoop(forLoop.body, condition, pos) UntilLoop(forLoop.body, condition, pos)
), pos) ), pos)
@@ -454,12 +454,6 @@ class StatementOptimizer(private val program: Program,
override fun after(whenStmt: When, parent: Node): Iterable<IAstModification> { 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 val constantValue = whenStmt.condition.constValue(program)?.number
if(constantValue!=null) { if(constantValue!=null) {
// when condition is a constant // when condition is a constant

View File

@@ -53,7 +53,7 @@ internal class AstChecker(private val program: Program,
override fun visit(module: Module) { override fun visit(module: Module) {
super.visit(module) super.visit(module)
if(module.name.startsWith('_')) 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 } val directives = module.statements.filterIsInstance<Directive>().groupBy { it.directive }
directives.filter { it.value.size > 1 }.forEach{ entry -> directives.filter { it.value.size > 1 }.forEach{ entry ->
when(entry.key) { when(entry.key) {
@@ -67,6 +67,14 @@ internal class AstChecker(private val program: Program,
if(identifier.nameInSource.any { it.startsWith('_') }) { if(identifier.nameInSource.any { it.startsWith('_') }) {
errors.err("identifiers cannot start with an underscore", identifier.position) 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) checkLongType(identifier)
val stmt = identifier.targetStatement(program) val stmt = identifier.targetStatement(program)
@@ -273,7 +281,7 @@ internal class AstChecker(private val program: Program,
override fun visit(block: Block) { override fun visit(block: Block) {
if(block.name.startsWith('_')) 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 val addr = block.address
if(addr!=null && addr>65535u) { if(addr!=null && addr>65535u) {
@@ -305,7 +313,7 @@ internal class AstChecker(private val program: Program,
override fun visit(label: Label) { override fun visit(label: Label) {
if(label.name.startsWith('_')) 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 // scope check
if(label.parent !is Block && label.parent !is Subroutine && label.parent !is AnonymousScope) { 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) fun err(msg: String) = errors.err(msg, subroutine.position)
if(subroutine.name.startsWith('_')) 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) if(subroutine.name in BuiltinFunctions)
err("cannot redefine a built-in function") 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). // Instead, their reference (address) should be passed (as an UWORD).
for(p in subroutine.parameters) { for(p in subroutine.parameters) {
if(p.name.startsWith('_')) 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)) { 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) 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 return
} }
val targets = assignment.target.multi!! val targets = assignment.target.multi!!
if(fcallTarget.returntypes.size<targets.size) { if(fcallTarget.returntypes.size!=targets.size) {
errors.err("too many assignment targets, ${targets.size} targets for ${fcallTarget.returntypes.size} return values", fcall.position) errors.err("number of assignment targets doesn't match number of return values", fcall.position)
return return
} }
if(fcallTarget.returntypes.size>targets.size) { fcallTarget.returntypes.zip(targets).withIndex().forEach { (index, p) ->
// You can have LESS assign targets than the number of result values, val (returnType, target) = p
// as long as the result values contain booleans that are returned in cpu status flags (like Carry). val targetDt = target.inferType(program).getOr(DataType.UNDEFINED)
// These may be ignored in the assignment - only "true" values NEED to have a target. if (!target.void && !(returnType isAssignableTo targetDt))
val numberOfNormalValues = fcallTarget.asmReturnvaluesRegisters.count { it.registerOrPair!=null } errors.err("can't assign returnvalue #${index + 1} to corresponding target; $returnType vs $targetDt", target.position)
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)
}
} }
} }

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... // we need to handle multi-decl here too, the desugarer maybe has not processed it here yet...
if(decl.value!=null) { if(decl.value!=null) {
decl.names.forEach { name -> 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) val assign = Assignment(target.copy(), decl.value!!.copy(), AssignmentOrigin.VARINIT, decl.position)
replacements.add(IAstModification.InsertAfter(decl, assign, scope)) replacements.add(IAstModification.InsertAfter(decl, assign, scope))
} }
@@ -137,7 +137,7 @@ class AstPreprocessor(val program: Program,
} else { } else {
// handle declaration of a single variable // handle declaration of a single variable
if(decl.value!=null && (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL)) { 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) val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position)
replacements.add(IAstModification.ReplaceNode(decl, assign, scope)) replacements.add(IAstModification.ReplaceNode(decl, assign, scope))
decl.value = null decl.value = null

View File

@@ -183,7 +183,7 @@ _after:
} }
if(functionCall.target.nameInSource==listOf("poke")) { if(functionCall.target.nameInSource==listOf("poke")) {
// poke(a, v) is synonymous with @(a) = v // 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) val assign = Assignment(tgt, functionCall.args[1], AssignmentOrigin.OPTIMIZER, position)
return listOf(IAstModification.ReplaceNode(functionCall as Node, assign, parent)) return listOf(IAstModification.ReplaceNode(functionCall as Node, assign, parent))
} }
@@ -222,7 +222,7 @@ _after:
return if(parent is AssignTarget) { return if(parent is AssignTarget) {
// assignment to array // assignment to array
val memwrite = DirectMemoryWrite(address, arrayIndexedExpression.position) 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)) listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
} else { } else {
// read from array // 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 { private fun transform(srcTarget: AssignTarget): PtAssignTarget {
val target = PtAssignTarget(srcTarget.position) val target = PtAssignTarget(srcTarget.void, srcTarget.position)
if(srcTarget.identifier!=null) if(srcTarget.identifier!=null)
target.add(transform(srcTarget.identifier!!)) target.add(transform(srcTarget.identifier!!))
else if(srcTarget.arrayindexed!=null) else if(srcTarget.arrayindexed!=null)
target.add(transform(srcTarget.arrayindexed!!)) target.add(transform(srcTarget.arrayindexed!!))
else if(srcTarget.memoryAddress!=null) else if(srcTarget.memoryAddress!=null)
target.add(transform(srcTarget.memoryAddress!!)) target.add(transform(srcTarget.memoryAddress!!))
else else if(!srcTarget.void)
throw FatalAstException("invalid AssignTarget") throw FatalAstException("invalid AssignTarget")
return target return target
} }

View File

@@ -60,7 +60,7 @@ internal class StatementReorderer(
// Add assignment to initialize with zero // 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. // 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 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) decl.zeroElementValue(), AssignmentOrigin.VARINIT, decl.position)
return listOf(IAstModification.InsertAfter( return listOf(IAstModification.InsertAfter(
decl, assignzero, parent as IStatementContainer 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' // So basically consider 'ubyte xx=99' as a short form for 'ubyte xx; xx=99'
val pos = decl.value!!.position val pos = decl.value!!.position
val identifier = IdentifierReference(listOf(decl.name), pos) 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!!, AssignmentOrigin.VARINIT, pos)
decl.value = null decl.value = null
return listOf(IAstModification.InsertAfter( return listOf(IAstModification.InsertAfter(
@@ -92,7 +92,7 @@ internal class StatementReorderer(
if(target!=null && target.isArray) { if(target!=null && target.isArray) {
val pos = decl.value!!.position val pos = decl.value!!.position
val identifier = IdentifierReference(listOf(decl.name), pos) 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!!, AssignmentOrigin.VARINIT, pos)
decl.value = null decl.value = null
return listOf(IAstModification.InsertAfter( return listOf(IAstModification.InsertAfter(

View File

@@ -36,49 +36,49 @@ class TestMemory: FunSpec({
test("assignment target not in mapped IO space C64") { test("assignment target not in mapped IO space C64") {
var memexpr = NumericLiteral.optimalInteger(0x0002, Position.DUMMY) 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) var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign)) wrapWithProgram(listOf(assign))
target.isIOAddress(c64target.machine) shouldBe false target.isIOAddress(c64target.machine) shouldBe false
memexpr = NumericLiteral.optimalInteger(0x1000, Position.DUMMY) 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) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign)) wrapWithProgram(listOf(assign))
target.isIOAddress(c64target.machine) shouldBe false target.isIOAddress(c64target.machine) shouldBe false
memexpr = NumericLiteral.optimalInteger(0x9fff, Position.DUMMY) 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) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign)) wrapWithProgram(listOf(assign))
target.isIOAddress(c64target.machine) shouldBe false target.isIOAddress(c64target.machine) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xa000, Position.DUMMY) 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) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign)) wrapWithProgram(listOf(assign))
target.isIOAddress(c64target.machine) shouldBe false target.isIOAddress(c64target.machine) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xc000, Position.DUMMY) 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) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign)) wrapWithProgram(listOf(assign))
target.isIOAddress(c64target.machine) shouldBe false target.isIOAddress(c64target.machine) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xcfff, Position.DUMMY) 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) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign)) wrapWithProgram(listOf(assign))
target.isIOAddress(c64target.machine) shouldBe false target.isIOAddress(c64target.machine) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xeeee, Position.DUMMY) 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) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign)) wrapWithProgram(listOf(assign))
target.isIOAddress(c64target.machine) shouldBe false target.isIOAddress(c64target.machine) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xffff, Position.DUMMY) 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) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign)) wrapWithProgram(listOf(assign))
target.isIOAddress(c64target.machine) shouldBe false target.isIOAddress(c64target.machine) shouldBe false
@@ -87,25 +87,25 @@ class TestMemory: FunSpec({
test("assign target in mapped IO space C64") { test("assign target in mapped IO space C64") {
var memexpr = NumericLiteral.optimalInteger(0x0000, Position.DUMMY) 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) var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign)) wrapWithProgram(listOf(assign))
target.isIOAddress(c64target.machine) shouldBe true target.isIOAddress(c64target.machine) shouldBe true
memexpr = NumericLiteral.optimalInteger(0x0001, Position.DUMMY) 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) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign)) wrapWithProgram(listOf(assign))
target.isIOAddress(c64target.machine) shouldBe true target.isIOAddress(c64target.machine) shouldBe true
memexpr = NumericLiteral.optimalInteger(0xd000, Position.DUMMY) 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) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign)) wrapWithProgram(listOf(assign))
target.isIOAddress(c64target.machine) shouldBe true target.isIOAddress(c64target.machine) shouldBe true
memexpr = NumericLiteral.optimalInteger(0xdfff, Position.DUMMY) 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) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign)) wrapWithProgram(listOf(assign))
target.isIOAddress(c64target.machine) shouldBe true target.isIOAddress(c64target.machine) shouldBe true
@@ -114,7 +114,7 @@ class TestMemory: FunSpec({
fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget { 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 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 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) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(decl, assignment)) wrapWithProgram(listOf(decl, assignment))
return target return target
@@ -137,13 +137,13 @@ class TestMemory: FunSpec({
test("memory expression mapped to IO memory on C64") { test("memory expression mapped to IO memory on C64") {
var memexpr = PrefixExpression("+", NumericLiteral.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY) 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) var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign)) wrapWithProgram(listOf(assign))
target.isIOAddress(c64target.machine) shouldBe false target.isIOAddress(c64target.machine) shouldBe false
memexpr = PrefixExpression("+", NumericLiteral.optimalInteger(0xd020, Position.DUMMY), Position.DUMMY) 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) assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign)) wrapWithProgram(listOf(assign))
target.isIOAddress(c64target.machine) shouldBe true target.isIOAddress(c64target.machine) shouldBe true
@@ -151,7 +151,7 @@ class TestMemory: FunSpec({
test("regular variable not in mapped IO ram on C64") { 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 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 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 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")) 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") { test("memory mapped variable not in mapped IO ram on C64") {
val address = 0x1000u 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 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 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 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")) 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") { test("memory mapped variable in mapped IO ram on C64") {
val address = 0xd020u 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 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 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 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")) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
@@ -187,7 +187,7 @@ class TestMemory: FunSpec({
test("array not in mapped IO ram") { 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 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 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 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 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")) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
@@ -200,7 +200,7 @@ class TestMemory: FunSpec({
val address = 0x1000u 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 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 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 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 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")) val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
@@ -213,7 +213,7 @@ class TestMemory: FunSpec({
val address = 0xd800u 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 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 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 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 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")) 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.r0L, flag = test2(12345, 5566, flag, -42)
cx16.r1, flag, bytevar = test3() 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 { 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)!! val result = compileText(Cx16Target(), false, src, errors, true)!!
errors.errors.size shouldBe 0 errors.errors.size shouldBe 0
val start = result.codegenAst!!.entrypoint()!! 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_1 = start.children[4] as PtAssignment
val a1_2 = start.children[5] as PtAssignment val a1_2 = start.children[5] as PtAssignment
val a1_3 = start.children[6] as PtAssignment val a1_3 = start.children[6] as PtAssignment
val a1_4 = start.children[7] as PtAssignment
a1_1.multiTarget shouldBe true a1_1.multiTarget shouldBe true
a1_2.multiTarget shouldBe true a1_2.multiTarget shouldBe true
a1_3.multiTarget shouldBe true a1_3.multiTarget shouldBe true
a1_4.multiTarget shouldBe true
a1_1.children.size shouldBe 3 a1_1.children.size shouldBe 3
a1_2.children.size shouldBe 4 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[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r0L")
(a1_1.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_flag") (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[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[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_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[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") { test("multi-assign from romsub") {
@@ -169,7 +177,8 @@ main {
flag = test(42) flag = test(42)
cx16.r0L, flag = test2(12345, 5566, flag, -42) cx16.r0L, flag = test2(12345, 5566, flag, -42)
cx16.r1, flag, bytevar = test3() 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 romsub ${'$'}8000 = test(ubyte arg @A) -> bool @Pc
@@ -182,22 +191,29 @@ main {
val result = compileText(Cx16Target(), false, src, errors, true)!! val result = compileText(Cx16Target(), false, src, errors, true)!!
errors.errors.size shouldBe 0 errors.errors.size shouldBe 0
val start = result.codegenAst!!.entrypoint()!! 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_1 = start.children[5] as PtAssignment
val a1_2 = start.children[6] as PtAssignment val a1_2 = start.children[6] as PtAssignment
val a1_3 = start.children[7] as PtAssignment val a1_3 = start.children[7] as PtAssignment
val a1_4 = start.children[8] as PtAssignment
a1_1.multiTarget shouldBe true a1_1.multiTarget shouldBe true
a1_2.multiTarget shouldBe true a1_2.multiTarget shouldBe true
a1_3.multiTarget shouldBe true a1_3.multiTarget shouldBe true
a1_4.multiTarget shouldBe true
a1_1.children.size shouldBe 3 a1_1.children.size shouldBe 3
a1_2.children.size shouldBe 4 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[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r0L")
(a1_1.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_flag") (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[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[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_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[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 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 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 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 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) 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) { override fun visit(assignTarget: AssignTarget) {
assignTarget.memoryAddress?.accept(this) if(assignTarget.void)
assignTarget.identifier?.accept(this) output("void")
assignTarget.arrayindexed?.accept(this) else {
val multi = assignTarget.multi assignTarget.memoryAddress?.accept(this)
if(multi!=null) { assignTarget.identifier?.accept(this)
multi.dropLast(1).forEach { target -> assignTarget.arrayindexed?.accept(this)
target.accept(this) val multi = assignTarget.multi
output(", ") 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() val identifiers = vardecl.identifier()
if(identifiers.size>1) if(identifiers.size>1)
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition()) 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()) AsmSubroutineParameter(identifiername.text, datatype, registerorpair, statusregister, toPosition())
} }
@@ -317,22 +317,27 @@ private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
val identifiers = it.identifier() val identifiers = it.identifier()
if(identifiers.size>1) if(identifiers.size>1)
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition()) 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()) SubroutineParameter(identifiername.text, datatype, it.toPosition())
} }
private fun Assign_targetContext.toAst() : AssignTarget { private fun Assign_targetContext.toAst() : AssignTarget {
return when(this) { return when(this) {
is IdentifierTargetContext -> is IdentifierTargetContext -> {
AssignTarget(scoped_identifier().toAst(), null, null, null, scoped_identifier().toPosition()) 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 -> 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 -> { is ArrayindexedTargetContext -> {
val ax = arrayindexed() val ax = arrayindexed()
val arrayvar = ax.scoped_identifier().toAst() val arrayvar = ax.scoped_identifier().toAst()
val index = ax.arrayindex().toAst() val index = ax.arrayindex().toAst()
val arrayindexed = ArrayIndexedExpression(arrayvar, index, ax.toPosition()) 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") 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 { private fun Multi_assign_targetContext.toAst() : AssignTarget {
val targets = this.assign_target().map { it.toAst() } 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> { private fun ClobberContext.toAst() : Set<CpuRegister> {
@@ -672,7 +677,7 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
else -> ZeropageWish.DONTCARE else -> ZeropageWish.DONTCARE
} }
val identifiers = identifier() 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 name = if(identifiers.size==1) identifiername.text else "<multiple>"
val isArray = ARRAYSIG() != null || arrayindex() != null val isArray = ARRAYSIG() != null || arrayindex() != null
val split = options.SPLIT().isNotEmpty() val split = options.SPLIT().isNotEmpty()
@@ -695,7 +700,7 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
arrayindex()?.toAst(), arrayindex()?.toAst(),
name, name,
if(identifiers.size==1) emptyList() else identifiers.map { 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 idname.text
}, },
value, value,

View File

@@ -494,6 +494,7 @@ data class AssignTarget(var identifier: IdentifierReference?,
var arrayindexed: ArrayIndexedExpression?, var arrayindexed: ArrayIndexedExpression?,
val memoryAddress: DirectMemoryWrite?, val memoryAddress: DirectMemoryWrite?,
val multi: List<AssignTarget>?, val multi: List<AssignTarget>?,
val void: Boolean,
override val position: Position) : Node { override val position: Position) : Node {
override lateinit var parent: 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: IAstVisitor) = visitor.visit(this)
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) 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 = override fun referencesIdentifier(nameInSource: List<String>): Boolean =
identifier?.referencesIdentifier(nameInSource)==true || identifier?.referencesIdentifier(nameInSource)==true ||
arrayindexed?.referencesIdentifier(nameInSource)==true || arrayindexed?.referencesIdentifier(nameInSource)==true ||
@@ -577,6 +578,8 @@ data class AssignTarget(var identifier: IdentifierReference?,
fun isSameAs(other: AssignTarget, program: Program): Boolean { fun isSameAs(other: AssignTarget, program: Program): Boolean {
if (this === other) if (this === other)
return true return true
if(void && other.void)
return true
if (this.identifier != null && other.identifier != null) if (this.identifier != null && other.identifier != null)
return this.identifier!!.nameInSource == other.identifier!!.nameInSource return this.identifier!!.nameInSource == other.identifier!!.nameInSource
if (this.memoryAddress != null && other.memoryAddress != null) { 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 { ... } 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. **Skipping values:** you are allowed to omit assignments of one or more values by putting ``void`` as the assignment target.
So in the case of multisub() above, you could also write `wordvar, bytevar = multisub()` and leave out the carry flag. One of the cases where this is useful is with boolean values returned in status flags such as the carry flag.
The compiler will try to assign the normal numeric values and leave the status flags untouched, which then allows you to Storing that flag as a boolean in a variable first, and then possibly adding an ``if flag...`` statement afterwards, is a lot less
use a conditional branch such as `if_cs` to do something with it. This is always more efficient efficient than just keeping the flag as-is and using a conditional branch such as ``if_cs`` to do something with it.
than storing it in a variable and then adding an `if flag...` statement afterwards. So in the case above that could be::
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. wordvar, void, bytevar = multisub()
Subroutine definitions Subroutine definitions

View File

@@ -1,12 +1,6 @@
TODO 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() { sub start() {
ubyte @shared bytevar ubyte @shared bytevar
uword @shared wordvar uword @shared wordvar
bool flag
wordvar, bytevar = test4() wordvar, bytevar, void = test4()
if_cs if_cs
txt.print("true! ") txt.print("true! ")
else else

View File

@@ -26,7 +26,6 @@ WS : [ \t] -> skip ;
VOID: 'void'; VOID: 'void';
NAME : [\p{Letter}][\p{Letter}\p{Mark}\p{Digit}_]* ; // match unicode properties NAME : [\p{Letter}][\p{Letter}\p{Mark}\p{Digit}_]* ; // match unicode properties
UNDERSCORENAME : '_' NAME ; // match unicode properties UNDERSCORENAME : '_' NAME ; // match unicode properties
UNDERSCOREPLACEHOLDER: '_' ;
DEC_INTEGER : DEC_DIGIT (DEC_DIGIT | '_')* ; DEC_INTEGER : DEC_DIGIT (DEC_DIGIT | '_')* ;
HEX_INTEGER : '$' HEX_DIGIT (HEX_DIGIT | '_')* ; HEX_INTEGER : '$' HEX_DIGIT (HEX_DIGIT | '_')* ;
BIN_INTEGER : '%' BIN_DIGIT (BIN_DIGIT | '_')* ; BIN_INTEGER : '%' BIN_DIGIT (BIN_DIGIT | '_')* ;
@@ -222,7 +221,7 @@ breakstmt : 'break';
continuestmt: 'continue'; continuestmt: 'continue';
identifier : NAME | UNDERSCORENAME | UNDERSCOREPLACEHOLDER; identifier : NAME | UNDERSCORENAME | VOID;
scoped_identifier : identifier ('.' identifier)* ; scoped_identifier : identifier ('.' identifier)* ;