mirror of
https://github.com/irmen/prog8.git
synced 2025-01-12 19:29:50 +00:00
tweaking multiple assignment targets
This commit is contained in:
parent
4ade45f51f
commit
23c1167d7f
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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 -> {
|
||||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user