function call arguments

This commit is contained in:
Irmen de Jong 2019-06-26 22:29:10 +02:00
parent 595e58ec46
commit 0782f6ecf1
4 changed files with 230 additions and 140 deletions

View File

@ -1,5 +1,7 @@
package prog8.ast
import prog8.functions.BuiltinFunctions
internal fun Program.reorderStatements() {
val initvalueCreator = VarInitValueAndAddressOfCreator(namespace)
initvalueCreator.process(this)
@ -24,6 +26,7 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
// - all other subroutines will be moved to the end of their block.
//
// Also, makes sure any value assignments get the proper type casts if needed to cast them into the target variable's type.
// (this includes function call arguments)
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
@ -192,7 +195,6 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
statements.addAll(result)
}
override fun process(assignment: Assignment): IStatement {
val target=assignment.singleTarget
if(target!=null) {
@ -210,6 +212,56 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
return super.process(assignment)
}
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
checkFunctionCallArguments(functionCallStatement, functionCallStatement.definingScope())
return super.process(functionCallStatement)
}
override fun process(functionCall: FunctionCall): IExpression {
checkFunctionCallArguments(functionCall, functionCall.definingScope())
return super.process(functionCall)
}
private fun checkFunctionCallArguments(call: IFunctionCall, scope: INameScope) {
// see if a typecast is needed to convert the arguments into the required parameter's type
val sub = call.target.targetStatement(scope)
when(sub) {
is Subroutine -> {
for(arg in sub.parameters.zip(call.arglist.withIndex())) {
val argtype = arg.second.value.resultingDatatype(program)
if(argtype!=null) {
val requiredType = arg.first.type
if (requiredType != argtype) {
if (argtype isAssignableTo requiredType) {
val typecasted = TypecastExpression(arg.second.value, requiredType, arg.second.value.position)
call.arglist[arg.second.index] = typecasted
}
// if they're not assignable, we'll get a proper error later from the AstChecker
}
}
}
}
is BuiltinFunctionStatementPlaceholder -> {
val func = BuiltinFunctions.getValue(sub.name)
for(arg in func.parameters.zip(call.arglist.withIndex())) {
val argtype = arg.second.value.resultingDatatype(program)
if(argtype!=null) {
if(arg.first.possibleDatatypes.any{ argtype == it})
continue
for(possibleType in arg.first.possibleDatatypes) {
if(argtype isAssignableTo possibleType) {
val typecasted = TypecastExpression(arg.second.value, possibleType, arg.second.value.position)
call.arglist[arg.second.index] = typecasted
break
}
}
}
}
}
else -> TODO("call to something weird $sub")
}
}
private fun sortConstantAssignmentSequence(first: Assignment, stmtIter: MutableIterator<IStatement>): Pair<List<Assignment>, IStatement?> {
val sequence= mutableListOf(first)
var trailing: IStatement? = null

View File

@ -87,7 +87,7 @@ class AstVm(val program: Program) {
init.process(program)
// initialize all global variables
for(m in program.modules) {
for (m in program.modules) {
for (b in m.statements.filterIsInstance<Block>()) {
for (s in b.statements.filterIsInstance<Subroutine>()) {
if (s.name == initvarsSubName) {
@ -109,7 +109,7 @@ class AstVm(val program: Program) {
}
println("PROGRAM EXITED!")
dialog.title = "PROGRAM EXITED"
} catch(bp: VmBreakpointException) {
} catch (bp: VmBreakpointException) {
println("Breakpoint: execution halted. Press enter to resume.")
readLine()
} catch (tx: VmTerminationException) {
@ -123,15 +123,24 @@ class AstVm(val program: Program) {
private val runtimeVariables = RuntimeVariables()
private val functions = BuiltinFunctions()
class LoopControlBreak: Exception()
class LoopControlContinue: Exception()
class LoopControlReturn(val returnvalues: List<RuntimeValue>): Exception()
class LoopControlBreak : Exception()
class LoopControlContinue : Exception()
class LoopControlReturn(val returnvalues: List<RuntimeValue>) : Exception()
internal fun executeSubroutine(sub: Subroutine, arguments: List<RuntimeValue>): List<RuntimeValue> {
assert(!sub.isAsmSubroutine)
if (sub.statements.isEmpty())
throw VmTerminationException("scope contains no statements: $sub")
// TODO process arguments if it's a subroutine
if (arguments.size != sub.parameters.size)
throw VmTerminationException("number of args doesn't match number of required parameters")
for (arg in sub.parameters.zip(arguments)) {
val idref = IdentifierReference(listOf(arg.first.name), sub.position)
performAssignment(AssignTarget(null, idref, null, null, idref.position),
arg.second, sub.statements.first(),
EvalContext(program, mem, runtimeVariables, functions, ::executeSubroutine))
}
try {
for (s in sub.statements) {
executeStatement(sub, s)
@ -151,16 +160,16 @@ class AstVm(val program: Program) {
private fun executeStatement(sub: INameScope, stmt: IStatement) {
val evalCtx = EvalContext(program, mem, runtimeVariables, functions, ::executeSubroutine)
instructionCounter++
if(instructionCounter % 100 == 0)
if (instructionCounter % 100 == 0)
Thread.sleep(1)
when (stmt) {
is NopStatement, is Label, is Subroutine -> {
// do nothing, skip this instruction
}
is Directive -> {
if(stmt.directive=="%breakpoint")
if (stmt.directive == "%breakpoint")
throw VmBreakpointException()
else if(stmt.directive=="%asm")
else if (stmt.directive == "%asm")
throw VmExecutionException("can't execute assembly code")
}
is VarDecl -> {
@ -168,14 +177,14 @@ class AstVm(val program: Program) {
}
is FunctionCallStatement -> {
val target = stmt.target.targetStatement(program.namespace)
when(target) {
when (target) {
is Subroutine -> {
val args = evaluate(stmt.arglist)
if(target.isAsmSubroutine) {
if (target.isAsmSubroutine) {
performSyscall(target, args)
} else {
val results = executeSubroutine(target, args)
// TODO process result values
executeSubroutine(target, args)
// any return value(s) are discarded
}
}
is BuiltinFunctionStatementPlaceholder -> {
@ -183,7 +192,7 @@ class AstVm(val program: Program) {
functions.performBuiltinFunction(target.name, args)
}
else -> {
TODO("CALL $target")
TODO("weird call $target")
}
}
}
@ -194,101 +203,31 @@ class AstVm(val program: Program) {
is Continue -> throw LoopControlContinue()
is Break -> throw LoopControlBreak()
is Assignment -> {
if(stmt.aug_op!=null)
if (stmt.aug_op != null)
throw VmExecutionException("augmented assignment should have been converted into regular one $stmt")
val target = stmt.singleTarget
if(target!=null) {
if (target != null) {
val value = evaluate(stmt.value, evalCtx)
when {
target.identifier!=null -> {
val decl = stmt.definingScope().lookup(target.identifier.nameInSource, stmt) as? VarDecl
?: throw VmExecutionException("can't find assignment target ${target.identifier}")
if(decl.type==VarDeclType.MEMORY) {
val address = runtimeVariables.getMemoryAddress(decl.definingScope(), decl.name)
when(decl.datatype) {
DataType.UBYTE -> mem.setUByte(address, value.byteval!!)
DataType.BYTE -> mem.setSByte(address, value.byteval!!)
DataType.UWORD -> mem.setUWord(address, value.wordval!!)
DataType.WORD -> mem.setSWord(address, value.wordval!!)
DataType.FLOAT -> mem.setFloat(address, value.floatval!!)
DataType.STR -> mem.setString(address, value.str!!)
DataType.STR_S -> mem.setScreencodeString(address, value.str!!)
else -> TODO("set memvar $decl")
}
} else
runtimeVariables.set(decl.definingScope(), decl.name, value)
}
target.memoryAddress!=null -> {
TODO("assign memory $stmt")
}
target.arrayindexed!=null -> {
val array = evaluate(target.arrayindexed.identifier, evalCtx)
val index = evaluate(target.arrayindexed.arrayspec.index, evalCtx)
when(array.type) {
DataType.ARRAY_UB -> {
if(value.type!=DataType.UBYTE)
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
}
DataType.ARRAY_B -> {
if(value.type!=DataType.BYTE)
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
}
DataType.ARRAY_UW -> {
if(value.type!=DataType.UWORD)
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
}
DataType.ARRAY_W -> {
if(value.type!=DataType.WORD)
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
}
DataType.ARRAY_F -> {
if(value.type!=DataType.FLOAT)
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
}
DataType.STR, DataType.STR_S -> {
if(value.type !in ByteDatatypes)
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
}
else -> throw VmExecutionException("strange array type ${array.type}")
}
if(array.type in ArrayDatatypes)
array.array!![index.integerValue()] = value.numericValue()
else if(array.type in StringDatatypes) {
val indexInt = index.integerValue()
val newchr = Petscii.decodePetscii(listOf(value.numericValue().toShort()), true)
val newstr = array.str!!.replaceRange(indexInt, indexInt+1, newchr)
val ident = stmt.definingScope().lookup(target.arrayindexed.identifier.nameInSource, stmt) as? VarDecl
?: throw VmExecutionException("can't find assignment target ${target.identifier}")
val identScope = ident.definingScope()
program.heap.update(array.heapId!!, newstr)
runtimeVariables.set(identScope, ident.name, RuntimeValue(array.type, str=newstr, heapId=array.heapId))
}
}
target.register!=null -> {
runtimeVariables.set(program.namespace, target.register.name, value)
}
else -> TODO("assign $target")
}
}
else TODO("assign multitarget $stmt")
performAssignment(target, value, stmt, evalCtx)
} else TODO("assign multitarget $stmt")
}
is PostIncrDecr -> {
when {
stmt.target.identifier!=null -> {
stmt.target.identifier != null -> {
val ident = stmt.definingScope().lookup(stmt.target.identifier!!.nameInSource, stmt) as VarDecl
val identScope = ident.definingScope()
var value = runtimeVariables.get(identScope, ident.name)
value = when {
stmt.operator=="++" -> value.add(RuntimeValue(value.type, 1))
stmt.operator=="--" -> value.sub(RuntimeValue(value.type, 1))
stmt.operator == "++" -> value.add(RuntimeValue(value.type, 1))
stmt.operator == "--" -> value.sub(RuntimeValue(value.type, 1))
else -> throw VmExecutionException("strange postincdec operator $stmt")
}
runtimeVariables.set(identScope, ident.name, value)
}
stmt.target.memoryAddress!=null -> {
stmt.target.memoryAddress != null -> {
TODO("postincrdecr memory $stmt")
}
stmt.target.arrayindexed!=null -> {
stmt.target.arrayindexed != null -> {
TODO("postincrdecr array $stmt")
}
}
@ -297,8 +236,8 @@ class AstVm(val program: Program) {
TODO("jump $stmt")
}
is InlineAssembly -> {
if(sub is Subroutine) {
when(sub.scopedname) {
if (sub is Subroutine) {
when (sub.scopedname) {
"c64flt.print_f" -> {
val arg = runtimeVariables.get(sub, sub.parameters.single().name)
performSyscall(sub, listOf(arg))
@ -312,7 +251,7 @@ class AstVm(val program: Program) {
is AnonymousScope -> executeAnonymousScope(stmt)
is IfStatement -> {
val condition = evaluate(stmt.condition, evalCtx)
if(condition.asBoolean)
if (condition.asBoolean)
executeAnonymousScope(stmt.truepart)
else
executeAnonymousScope(stmt.elsepart)
@ -326,16 +265,15 @@ class AstVm(val program: Program) {
throw VmExecutionException("can only iterate over an iterable value: $stmt")
val loopvarDt: DataType
val loopvar: IdentifierReference
if(stmt.loopRegister!=null) {
if (stmt.loopRegister != null) {
loopvarDt = DataType.UBYTE
loopvar = IdentifierReference(listOf(stmt.loopRegister.name), stmt.position)
}
else {
} else {
loopvarDt = stmt.loopVar!!.resultingDatatype(program)!!
loopvar = stmt.loopVar
}
val iterator = iterable.iterator()
for(loopvalue in iterator) {
for (loopvalue in iterator) {
try {
oneForCycle(stmt, loopvarDt, loopvalue, loopvar)
} catch (b: LoopControlBreak) {
@ -351,9 +289,9 @@ class AstVm(val program: Program) {
try {
executeAnonymousScope(stmt.body)
condition = evaluate(stmt.condition, evalCtx)
} catch(b: LoopControlBreak) {
} catch (b: LoopControlBreak) {
break
} catch(c: LoopControlContinue){
} catch (c: LoopControlContinue) {
continue
}
}
@ -363,12 +301,12 @@ class AstVm(val program: Program) {
val condition = evaluate(stmt.untilCondition, evalCtx)
try {
executeAnonymousScope(stmt.body)
} catch(b: LoopControlBreak) {
} catch (b: LoopControlBreak) {
break
} catch(c: LoopControlContinue){
} catch (c: LoopControlContinue) {
continue
}
} while(!condition.asBoolean)
} while (!condition.asBoolean)
}
else -> {
TODO("implement $stmt")
@ -376,37 +314,101 @@ class AstVm(val program: Program) {
}
}
private fun oneForCycle(stmt: ForLoop, loopvarDt: DataType, loopValue: Number, loopVar: IdentifierReference) {
val value: LiteralValue =
when (loopvarDt) {
DataType.UBYTE -> LiteralValue(DataType.UBYTE, loopValue.toShort(), position = stmt.position)
DataType.BYTE -> LiteralValue(DataType.BYTE, loopValue.toShort(), position = stmt.position)
DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = (loopValue as Int), position = stmt.position)
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = (loopValue as Int), position = stmt.position)
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = (loopValue as Double), position = stmt.position)
else -> TODO("weird loopvar type $loopvarDt")
fun performAssignment(target: AssignTarget, value: RuntimeValue, contextStmt: IStatement, evalCtx: EvalContext) {
when {
target.identifier != null -> {
val decl = contextStmt.definingScope().lookup(target.identifier.nameInSource, contextStmt) as? VarDecl
?: throw VmExecutionException("can't find assignment target ${target.identifier}")
if (decl.type == VarDeclType.MEMORY) {
val address = runtimeVariables.getMemoryAddress(decl.definingScope(), decl.name)
when (decl.datatype) {
DataType.UBYTE -> mem.setUByte(address, value.byteval!!)
DataType.BYTE -> mem.setSByte(address, value.byteval!!)
DataType.UWORD -> mem.setUWord(address, value.wordval!!)
DataType.WORD -> mem.setSWord(address, value.wordval!!)
DataType.FLOAT -> mem.setFloat(address, value.floatval!!)
DataType.STR -> mem.setString(address, value.str!!)
DataType.STR_S -> mem.setScreencodeString(address, value.str!!)
else -> TODO("set memvar $decl")
}
} else
runtimeVariables.set(decl.definingScope(), decl.name, value)
}
target.memoryAddress != null -> {
TODO("assign memory")
}
target.arrayindexed != null -> {
val array = evaluate(target.arrayindexed.identifier, evalCtx)
val index = evaluate(target.arrayindexed.arrayspec.index, evalCtx)
when (array.type) {
DataType.ARRAY_UB -> {
if (value.type != DataType.UBYTE)
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
}
DataType.ARRAY_B -> {
if (value.type != DataType.BYTE)
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
}
DataType.ARRAY_UW -> {
if (value.type != DataType.UWORD)
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
}
DataType.ARRAY_W -> {
if (value.type != DataType.WORD)
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
}
DataType.ARRAY_F -> {
if (value.type != DataType.FLOAT)
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
}
DataType.STR, DataType.STR_S -> {
if (value.type !in ByteDatatypes)
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
}
else -> throw VmExecutionException("strange array type ${array.type}")
}
val assignment = Assignment(listOf(AssignTarget(null, loopVar, null, null,
position = loopVar.position)), null, value, stmt.iterable.position)
assignment.linkParents(stmt.body)
executeStatement(stmt.body, assignment) // assign the new loop value to the loopvar
executeAnonymousScope(stmt.body) // and run the code
if (array.type in ArrayDatatypes)
array.array!![index.integerValue()] = value.numericValue()
else if (array.type in StringDatatypes) {
val indexInt = index.integerValue()
val newchr = Petscii.decodePetscii(listOf(value.numericValue().toShort()), true)
val newstr = array.str!!.replaceRange(indexInt, indexInt + 1, newchr)
val ident = contextStmt.definingScope().lookup(target.arrayindexed.identifier.nameInSource, contextStmt) as? VarDecl
?: throw VmExecutionException("can't find assignment target ${target.identifier}")
val identScope = ident.definingScope()
program.heap.update(array.heapId!!, newstr)
runtimeVariables.set(identScope, ident.name, RuntimeValue(array.type, str = newstr, heapId = array.heapId))
}
}
target.register != null -> {
runtimeVariables.set(program.namespace, target.register.name, value)
}
else -> TODO("assign $target")
}
}
private fun evaluate(args: List<IExpression>): List<RuntimeValue> = args.map {
evaluate(it, EvalContext(program, mem, runtimeVariables, functions, ::executeSubroutine))
private fun oneForCycle(stmt: ForLoop, loopvarDt: DataType, loopValue: Number, loopVar: IdentifierReference) {
// assign the new loop value to the loopvar, and run the code
performAssignment(AssignTarget(null, loopVar, null, null, loopVar.position),
RuntimeValue(loopvarDt, loopValue), stmt.body.statements.first(),
EvalContext(program, mem, runtimeVariables, functions, ::executeSubroutine))
executeAnonymousScope(stmt.body)
}
private fun evaluate(args: List<IExpression>): List<RuntimeValue> {
val ctx = EvalContext(program, mem, runtimeVariables, functions, ::executeSubroutine)
return args.map { evaluate(it, ctx) }
}
private fun performSyscall(sub: Subroutine, args: List<RuntimeValue>) {
assert(sub.isAsmSubroutine)
when(sub.scopedname) {
when (sub.scopedname) {
"c64scr.print" -> {
// if the argument is an UWORD, consider it to be the "address" of the string (=heapId)
if(args[0].wordval!=null) {
if (args[0].wordval != null) {
val str = program.heap.get(args[0].wordval!!).str!!
dialog.canvas.printText(str, 1, true)
}
else
} else
dialog.canvas.printText(args[0].str!!, 1, true)
}
"c64scr.print_ub" -> {
@ -416,7 +418,7 @@ class AstVm(val program: Program) {
dialog.canvas.printText(args[0].byteval!!.toString(), 1, true)
}
"c64scr.print_ubhex" -> {
val prefix = if(args[0].asBoolean) "$" else ""
val prefix = if (args[0].asBoolean) "$" else ""
val number = args[1].byteval!!
dialog.canvas.printText("$prefix${number.toString(16).padStart(2, '0')}", 1, true)
}
@ -427,7 +429,7 @@ class AstVm(val program: Program) {
dialog.canvas.printText(args[0].wordval!!.toString(), 1, true)
}
"c64scr.print_uwhex" -> {
val prefix = if(args[0].asBoolean) "$" else ""
val prefix = if (args[0].asBoolean) "$" else ""
val number = args[1].wordval!!
dialog.canvas.printText("$prefix${number.toString(16).padStart(4, '0')}", 1, true)
}
@ -441,34 +443,33 @@ class AstVm(val program: Program) {
}
}
private fun setFlags(value: LiteralValue?) {
if(value!=null) {
when(value.type) {
if (value != null) {
when (value.type) {
DataType.UBYTE -> {
val v = value.bytevalue!!.toInt()
P_negative = v>127
P_zero = v==0
P_negative = v > 127
P_zero = v == 0
}
DataType.BYTE -> {
val v = value.bytevalue!!.toInt()
P_negative = v<0
P_zero = v==0
P_negative = v < 0
P_zero = v == 0
}
DataType.UWORD -> {
val v = value.wordvalue!!
P_negative = v>32767
P_zero = v==0
P_negative = v > 32767
P_zero = v == 0
}
DataType.WORD -> {
val v = value.wordvalue!!
P_negative = v<0
P_zero = v==0
P_negative = v < 0
P_zero = v == 0
}
DataType.FLOAT -> {
val flt = value.floatvalue!!
P_negative = flt < 0.0
P_zero = flt==0.0
P_zero = flt == 0.0
}
else -> {
// no flags for non-numeric type
@ -477,3 +478,4 @@ class AstVm(val program: Program) {
}
}
}

View File

@ -93,6 +93,19 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
}
}
fun asLiteralValue(): LiteralValue {
return when(type) {
in ByteDatatypes -> LiteralValue(type, byteval, position = Position("", 0, 0, 0))
in WordDatatypes -> LiteralValue(type, wordvalue = wordval, position = Position("", 0, 0, 0))
DataType.FLOAT -> LiteralValue(type, floatvalue = floatval, position = Position("", 0, 0, 0))
in StringDatatypes -> LiteralValue(type, strvalue = str, position = Position("", 0, 0, 0))
in ArrayDatatypes -> LiteralValue(type,
arrayvalue = array?.map { LiteralValue.optimalNumeric(it, Position("", 0, 0, 0)) }?.toTypedArray(),
position = Position("", 0, 0, 0))
else -> TODO("strange type")
}
}
override fun toString(): String {
return when(type) {
DataType.UBYTE -> "ub:%02x".format(byteval)

View File

@ -6,6 +6,28 @@
~ main {
sub start() {
ubyte u1 = 100
ubyte u2 = 30
byte ub = -30
abs(ub)
word r = moo(u1,u2)
c64scr.print_w(r)
}
sub moo(ubyte p1, word p2) -> word {
c64scr.print_ub(p1)
c64.CHROUT(',')
c64scr.print_w(p2)
c64.CHROUT('\n')
c64.CHROUT('\n')
for word ww in 200 to 300 step 13 {
c64scr.print_w(ww)
c64.CHROUT('\n')
}
ubyte u1 = 100
ubyte u2 = 30
@ -17,5 +39,6 @@
c64.CHROUT('\n')
c64scr.print_ub(u2 * 7)
c64.CHROUT('\n')
return 12345
}
}