tweaking multiple assignment targets

This commit is contained in:
Irmen de Jong 2018-11-14 01:50:16 +01:00
parent 4ade45f51f
commit 23c1167d7f
8 changed files with 840 additions and 764 deletions

View File

@ -107,7 +107,9 @@ datatype: 'ubyte' | 'byte' | 'uword' | 'word' | 'float' | 'str' | 'str_p' | 'st
arrayspec: '[' expression ']' ; arrayspec: '[' expression ']' ;
assignment : assign_target '=' expression ; assignment : assign_targets '=' expression ;
assign_targets : assign_target (',' assign_target)* ;
augassignment : augassignment :
assign_target operator=('+=' | '-=' | '/=' | '//=' | '*=' | '**=' | '&=' | '|=' | '^=') expression assign_target operator=('+=' | '-=' | '/=' | '//=' | '*=' | '**=' | '&=' | '|=' | '^=') expression

View File

@ -7,11 +7,13 @@
sub start() { sub start() {
ubyte derp ubyte v1
ubyte v2
derp=foo() v1=foo()
derp=c64.GETADR() v1 , v2 =c64.GETADR()
return return
} }

View File

@ -191,7 +191,7 @@ interface IAstProcessor {
} }
fun process(assignment: Assignment): IStatement { fun process(assignment: Assignment): IStatement {
assignment.target = assignment.target.process(this) assignment.targets = assignment.targets.map { it.process(this) }
assignment.value = assignment.value.process(this) assignment.value = assignment.value.process(this)
return assignment return assignment
} }
@ -641,26 +641,31 @@ class VarDecl(val type: VarDeclType,
} }
open class Assignment(var target: AssignTarget, val aug_op : String?, var value: IExpression, override val position: Position) : IStatement { open class Assignment(var targets: List<AssignTarget>, val aug_op : String?, var value: IExpression, override val position: Position) : IStatement {
override lateinit var parent: Node override lateinit var parent: Node
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
target.linkParents(this) targets.forEach { it.linkParents(this) }
value.linkParents(this) value.linkParents(this)
} }
override fun process(processor: IAstProcessor) = processor.process(this) override fun process(processor: IAstProcessor) = processor.process(this)
override fun toString(): String { override fun toString(): String {
return("Assignment(augop: $aug_op, target: $target, value: $value, pos=$position)") return("Assignment(augop: $aug_op, targets: $targets, value: $value, pos=$position)")
} }
val singleTarget: AssignTarget?
get() {
return targets.singleOrNull() // common case
}
} }
// This is a special class so the compiler can see if the assignments are for initializing the vars in the scope, // This is a special class so the compiler can see if the assignments are for initializing the vars in the scope,
// or just a regular assignment. It may optimize the initialization step from this. // or just a regular assignment. It may optimize the initialization step from this.
class VariableInitializationAssignment(target: AssignTarget, aug_op: String?, value: IExpression, position: Position) class VariableInitializationAssignment(target: AssignTarget, aug_op: String?, value: IExpression, position: Position)
: Assignment(target, aug_op, value, position) : Assignment(listOf(target), aug_op, value, position)
data class AssignTarget(val register: Register?, data class AssignTarget(val register: Register?,
@ -1645,11 +1650,11 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
} }
assignment()?.let { assignment()?.let {
return Assignment(it.assign_target().toAst(),null, it.expression().toAst(), it.toPosition()) return Assignment(it.assign_targets().toAst(), null, it.expression().toAst(), it.toPosition())
} }
augassignment()?.let { augassignment()?.let {
return Assignment(it.assign_target().toAst(), return Assignment(listOf(it.assign_target().toAst()),
it.operator.text, it.operator.text,
it.expression().toAst(), it.expression().toAst(),
it.toPosition()) it.toPosition())
@ -1707,6 +1712,8 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text") throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
} }
private fun prog8Parser.Assign_targetsContext.toAst(): List<AssignTarget> = assign_target().map { it.toAst() }
private fun prog8Parser.AsmsubroutineContext.toAst(): IStatement { private fun prog8Parser.AsmsubroutineContext.toAst(): IStatement {
val name = identifier().text val name = identifier().text

View File

@ -303,22 +303,31 @@ class AstChecker(private val namespace: INameScope,
* Also check data type compatibility * Also check data type compatibility
*/ */
override fun process(assignment: Assignment): IStatement { override fun process(assignment: Assignment): IStatement {
if(assignment.target.identifier!=null) { var resultingAssignment = assignment
val targetName = assignment.target.identifier!!.nameInSource for (target in assignment.targets) {
resultingAssignment = processAssignmentTarget(resultingAssignment, target)
}
return super.process(resultingAssignment)
}
private fun processAssignmentTarget(assignment: Assignment, target: AssignTarget): Assignment {
if(target.identifier!=null) {
val targetName = target.identifier.nameInSource
val targetSymbol = namespace.lookup(targetName, assignment) val targetSymbol = namespace.lookup(targetName, assignment)
when { when {
targetSymbol == null -> { targetSymbol == null -> {
checkResult.add(ExpressionError("undefined symbol: ${targetName.joinToString(".")}", assignment.position)) checkResult.add(ExpressionError("undefined symbol: ${targetName.joinToString(".")}", assignment.position))
return super.process(assignment) return assignment
} }
targetSymbol !is VarDecl -> { targetSymbol !is VarDecl -> {
checkResult.add(SyntaxError("assignment LHS must be register or variable", assignment.position)) checkResult.add(SyntaxError("assignment LHS must be register or variable", assignment.position))
return super.process(assignment) return assignment
} }
targetSymbol.type == VarDeclType.CONST -> { targetSymbol.type == VarDeclType.CONST -> {
checkResult.add(ExpressionError("cannot assign new value to a constant", assignment.position)) checkResult.add(ExpressionError("cannot assign new value to a constant", assignment.position))
return super.process(assignment) return assignment
} }
else -> {}
} }
} }
@ -329,27 +338,27 @@ class AstChecker(private val namespace: INameScope,
if(assignment.aug_op!=null) { if(assignment.aug_op!=null) {
// check augmented assignment: // check augmented assignment:
// A /= 3 -> check as if it was A = A / 3 // A /= 3 -> check as if it was A = A / 3
val target: IExpression = val newTarget: IExpression =
when { when {
assignment.target.register!=null -> RegisterExpr(assignment.target.register!!, assignment.target.position) target.register!=null -> RegisterExpr(target.register, target.position)
assignment.target.identifier!=null -> assignment.target.identifier!! target.identifier!=null -> target.identifier
assignment.target.arrayindexed!=null -> assignment.target.arrayindexed!! target.arrayindexed!=null -> target.arrayindexed
else -> throw FatalAstException("strange assignment") else -> throw FatalAstException("strange assignment")
} }
val expression = BinaryExpression(target, assignment.aug_op.substringBeforeLast('='), assignment.value, assignment.position) val expression = BinaryExpression(newTarget, assignment.aug_op.substringBeforeLast('='), assignment.value, assignment.position)
expression.linkParents(assignment.parent) expression.linkParents(assignment.parent)
val assignment2 = Assignment(assignment.target, null, expression, assignment.position) val assignment2 = Assignment(listOf(target), null, expression, assignment.position)
assignment2.linkParents(assignment.parent) assignment2.linkParents(assignment.parent)
return process(assignment2) return assignment2
} }
val targetDatatype = assignment.target.determineDatatype(namespace, heap, assignment) val targetDatatype = target.determineDatatype(namespace, heap, assignment)
if(targetDatatype!=null) { if(targetDatatype!=null) {
val constVal = assignment.value.constValue(namespace, heap) val constVal = assignment.value.constValue(namespace, heap)
if(constVal!=null) { if(constVal!=null) {
val arrayspec = if(assignment.target.identifier!=null) { val arrayspec = if(target.identifier!=null) {
val targetVar = namespace.lookup(assignment.target.identifier!!.nameInSource, assignment) as? VarDecl val targetVar = namespace.lookup(target.identifier.nameInSource, assignment) as? VarDecl
targetVar?.arrayspec targetVar?.arrayspec
} else null } else null
checkValueTypeAndRange(targetDatatype, checkValueTypeAndRange(targetDatatype,
@ -375,8 +384,7 @@ class AstChecker(private val namespace: INameScope,
} }
} }
} }
return assignment
return super.process(assignment)
} }

View File

@ -1089,10 +1089,11 @@ private class StatementTranslator(private val prog: IntermediateProgram,
} }
private fun translate(stmt: Assignment) { private fun translate(stmt: Assignment) {
val assignTarget= stmt.singleTarget ?: throw CompilerException("cannot use assignment to multiple assignment targets ${stmt.position}")
prog.line(stmt.position) prog.line(stmt.position)
translate(stmt.value) translate(stmt.value)
val valueDt = stmt.value.resultingDatatype(namespace, heap) val valueDt = stmt.value.resultingDatatype(namespace, heap)
val targetDt = stmt.target.determineDatatype(namespace, heap, stmt) val targetDt = assignTarget.determineDatatype(namespace, heap, stmt)
if(valueDt!=targetDt) { if(valueDt!=targetDt) {
// convert value to target datatype if possible // convert value to target datatype if possible
when(targetDt) { when(targetDt) {
@ -1124,26 +1125,26 @@ private class StatementTranslator(private val prog: IntermediateProgram,
if(stmt.aug_op!=null) { if(stmt.aug_op!=null) {
// augmented assignment // augmented assignment
when { when {
stmt.target.identifier!=null -> { assignTarget.identifier!=null -> {
val target = stmt.target.identifier!!.targetStatement(namespace)!! val target = assignTarget.identifier.targetStatement(namespace)!!
when(target) { when(target) {
is VarDecl -> { is VarDecl -> {
val opcode = opcodePushvar(stmt.target.determineDatatype(namespace, heap, stmt)!!) val opcode = opcodePushvar(assignTarget.determineDatatype(namespace, heap, stmt)!!)
prog.instr(opcode, callLabel = target.scopedname) prog.instr(opcode, callLabel = target.scopedname)
} }
else -> throw CompilerException("invalid assignment target type ${target::class}") else -> throw CompilerException("invalid assignment target type ${target::class}")
} }
} }
stmt.target.register!=null -> prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = stmt.target.register.toString()) assignTarget.register!=null -> prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = assignTarget.register.toString())
stmt.target.arrayindexed!=null -> translate(stmt.target.arrayindexed!!, false) assignTarget.arrayindexed!=null -> translate(assignTarget.arrayindexed, false)
} }
translateAugAssignOperator(stmt.aug_op, stmt.value.resultingDatatype(namespace, heap)) translateAugAssignOperator(stmt.aug_op, stmt.value.resultingDatatype(namespace, heap))
} }
// pop the result value back into the assignment target // pop the result value back into the assignment target
val datatype = stmt.target.determineDatatype(namespace, heap, stmt)!! val datatype = assignTarget.determineDatatype(namespace, heap, stmt)!!
popValueIntoTarget(stmt.target, datatype) popValueIntoTarget(assignTarget, datatype)
} }
private fun popValueIntoTarget(assignTarget: AssignTarget, datatype: DataType) { private fun popValueIntoTarget(assignTarget: AssignTarget, datatype: DataType) {
@ -1394,7 +1395,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
else else
AssignTarget(null, loop.loopVar!!.copy(), null, loop.position) AssignTarget(null, loop.loopVar!!.copy(), null, loop.position)
val arrayspec = ArraySpec(RegisterExpr(Register.X, loop.position), loop.position) val arrayspec = ArraySpec(RegisterExpr(Register.X, loop.position), loop.position)
val assignLv = Assignment(assignTarget, null, ArrayIndexedExpression((loop.iterable as IdentifierReference).copy(), arrayspec, loop.position), loop.position) val assignLv = Assignment(listOf(assignTarget), null, ArrayIndexedExpression((loop.iterable as IdentifierReference).copy(), arrayspec, loop.position), loop.position)
assignLv.linkParents(loop.body) assignLv.linkParents(loop.body)
translate(assignLv) translate(assignLv)
translate(loop.body) translate(loop.body)
@ -1519,7 +1520,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
AssignTarget(register, null, null, range.position) AssignTarget(register, null, null, range.position)
} }
val startAssignment = Assignment(makeAssignmentTarget(), null, range.from, range.position) val startAssignment = Assignment(listOf(makeAssignmentTarget()), null, range.from, range.position)
startAssignment.linkParents(range.parent) startAssignment.linkParents(range.parent)
translate(startAssignment) translate(startAssignment)

View File

@ -536,7 +536,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
super.process(assignment) super.process(assignment)
val lv = assignment.value as? LiteralValue val lv = assignment.value as? LiteralValue
if(lv!=null) { if(lv!=null) {
val targetDt = assignment.target.determineDatatype(namespace, heap, assignment) val targetDt = assignment.singleTarget?.determineDatatype(namespace, heap, assignment)
// see if we can promote/convert a literal value to the required datatype // see if we can promote/convert a literal value to the required datatype
when(targetDt) { when(targetDt) {
DataType.UWORD -> { DataType.UWORD -> {

View File

@ -105,7 +105,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
if(range.size()==1) { if(range.size()==1) {
// 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 assignment = Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, forLoop.position), null, range.from, forLoop.position) val assignment = Assignment(listOf(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, forLoop.position)), null, range.from, forLoop.position)
forLoop.body.statements.add(0, assignment) forLoop.body.statements.add(0, assignment)
optimizationsDone++ optimizationsDone++
return forLoop.body return forLoop.body
@ -174,4 +174,4 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
} }
return jump return jump
} }
} }

File diff suppressed because it is too large Load Diff