fix builtin functions over non-const arrays/strings

This commit is contained in:
Irmen de Jong 2018-09-29 19:17:19 +02:00
parent 5ee427b72b
commit 34dec55eb2
5 changed files with 155 additions and 74 deletions

View File

@ -16,31 +16,59 @@ sub start() -> () {
byte[2,3] matrix1 = [1,2, 3,4, 5,6] byte[2,3] matrix1 = [1,2, 3,4, 5,6]
byte[2,3] matrix2 = [1,2, 3,4, 5,6] byte[2,3] matrix2 = [1,2, 3,4, 5,6]
byte[2,3] matrix3 = [11,22, 33,44, 55,66] byte[2,3] matrix3 = [11,22, 33,44, 55,66]
str message = "Calculating Mandelbrot Fractal..." byte num1 = 99
_vm_gfx_text(5, 5, 7, message) word num2 = 12345
_vm_gfx_text(5, 5, 7, "Calculating Mandelbrot Fractal...") float num3 = 98.555
A= len("abcdef")
A= len([4,5,99/X]) num1 = max(msg3)
A= max([4,5,99]) _vm_write_str(num1)
A= min([4,5,99]) _vm_write_char($8d)
A= avg([4,5,99]) num1 = min(msg3)
A= sum([4,5,99]) _vm_write_str(num1)
A= any([4,5,99]) _vm_write_char($8d)
A= all([4,5,99])
A= len(msg3) num3 = avg(array3)
_vm_write_str(num3)
_vm_write_char($8d)
num1 = all(array3)
_vm_write_str(num1)
_vm_write_char($8d)
num1 = any(array3)
_vm_write_str(num1)
_vm_write_char($8d)
float xx num1 = max(array3)
_vm_write_str(num1)
_vm_write_char($8d)
num1 = min(array3)
_vm_write_str(num1)
_vm_write_char($8d)
num2 = max(array5)
_vm_write_str(num2)
_vm_write_char($8d)
num2 = min(array5)
_vm_write_str(num2)
_vm_write_char($8d)
num2 = sum(array3)
_vm_write_str(num2)
_vm_write_char($8d)
num2 = sum(array5)
_vm_write_str(num2)
_vm_write_char($8d)
A= len(array3) num3 = avg(msg3)
A= max(array3) _vm_write_str(num3)
A= min(array3) _vm_write_char($8d)
xx= avg(array3) num2 = sum(msg3)
A= sum(array3) _vm_write_str(num2)
A= any(array3) _vm_write_char($8d)
A= all(array3) num1 = all(msg3)
_vm_write_str(num1)
_vm_write_char($8d)
num1 = any(msg3)
_vm_write_str(num1)
_vm_write_char($8d)
} }
} }

View File

@ -25,7 +25,7 @@ fun Number.toHex(): String {
class HeapValues { class HeapValues {
class HeapValue(val type: DataType, val str: String?, val array: IntArray?) { data class HeapValue(val type: DataType, val str: String?, val array: IntArray?) {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (javaClass != other?.javaClass) return false if (javaClass != other?.javaClass) return false
@ -62,6 +62,31 @@ class HeapValues {
return heap.size-1 return heap.size-1
} }
fun update(heapId: Int, str: String) {
when(heap[heapId].type){
DataType.STR,
DataType.STR_P,
DataType.STR_S,
DataType.STR_PS -> {
if(heap[heapId].str!!.length!=str.length)
throw IllegalArgumentException("heap string length mismatch")
heap[heapId] = heap[heapId].copy(str=str)
}
else-> throw IllegalArgumentException("heap data type mismatch")
}
}
fun update(heapId: Int, array: IntArray) {
when(heap[heapId].type){
DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX -> {
if(heap[heapId].array!!.size != array.size)
throw IllegalArgumentException("heap array length mismatch")
heap[heapId] = heap[heapId].copy(array=array)
}
else-> throw IllegalArgumentException("heap data type mismatch")
}
}
fun get(heapId: Int): HeapValue = heap[heapId] fun get(heapId: Int): HeapValue = heap[heapId]
fun allStrings() = heap.asSequence().withIndex().filter { it.value.str!=null }.toList() fun allStrings() = heap.asSequence().withIndex().filter { it.value.str!=null }.toList()

View File

@ -43,6 +43,17 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
return DataType.BYTE return DataType.BYTE
} }
} }
if(arglist is IdentifierReference) {
val dt = arglist.resultingDatatype(namespace, heap)
return when(dt) {
DataType.BYTE, DataType.WORD, DataType.FLOAT,
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> dt
DataType.ARRAY -> DataType.BYTE
DataType.ARRAY_W -> DataType.WORD
DataType.MATRIX -> DataType.BYTE
null -> throw FatalAstException("function requires one argument which is an array $function")
}
}
throw FatalAstException("function requires one argument which is an array $function") throw FatalAstException("function requires one argument which is an array $function")
} }
@ -53,7 +64,16 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
"rndw" -> DataType.WORD "rndw" -> DataType.WORD
"rol", "rol2", "ror", "ror2", "lsl", "lsr", "set_carry", "clear_carry", "set_irqd", "clear_irqd" -> null // no return value so no datatype "rol", "rol2", "ror", "ror2", "lsl", "lsr", "set_carry", "clear_carry", "set_irqd", "clear_irqd" -> null // no return value so no datatype
"abs" -> args.single().resultingDatatype(namespace, heap) "abs" -> args.single().resultingDatatype(namespace, heap)
"max", "min" -> datatypeFromListArg(args.single()) "max", "min" -> {
val dt = datatypeFromListArg(args.single())
when(dt) {
DataType.BYTE, DataType.WORD, DataType.FLOAT -> dt
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.BYTE
DataType.ARRAY -> DataType.BYTE
DataType.ARRAY_W -> DataType.WORD
DataType.MATRIX -> DataType.BYTE
}
}
"round", "floor", "ceil" -> integerDatatypeFromArg(args.single()) "round", "floor", "ceil" -> integerDatatypeFromArg(args.single())
"sum" -> { "sum" -> {
val dt=datatypeFromListArg(args.single()) val dt=datatypeFromListArg(args.single())
@ -61,8 +81,8 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
DataType.BYTE, DataType.WORD -> DataType.WORD DataType.BYTE, DataType.WORD -> DataType.WORD
DataType.FLOAT -> DataType.FLOAT DataType.FLOAT -> DataType.FLOAT
DataType.ARRAY, DataType.ARRAY_W -> DataType.WORD DataType.ARRAY, DataType.ARRAY_W -> DataType.WORD
DataType.MATRIX -> DataType.BYTE DataType.MATRIX -> DataType.WORD
else -> throw FatalAstException("cannot sum over type $dt") DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.WORD
} }
} }
"len" -> { "len" -> {
@ -135,13 +155,7 @@ private fun collectionArgOutputNumber(args: List<IExpression>, position: Positio
function: (arg: Collection<Double>)->Number): LiteralValue { function: (arg: Collection<Double>)->Number): LiteralValue {
if(args.size!=1) if(args.size!=1)
throw SyntaxError("builtin function requires one non-scalar argument", position) throw SyntaxError("builtin function requires one non-scalar argument", position)
var iterable = args[0].constValue(namespace, heap) var iterable = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
if(iterable==null) {
if(args[0] !is IdentifierReference)
throw SyntaxError("function over weird argument ${args[0]}", position)
iterable = ((args[0] as IdentifierReference).targetStatement(namespace) as? VarDecl)?.value?.constValue(namespace, heap)
?: throw SyntaxError("function over weird argument ${args[0]}", position)
}
val result = if(iterable.arrayvalue != null) { val result = if(iterable.arrayvalue != null) {
val constants = iterable.arrayvalue!!.map { it.constValue(namespace, heap)?.asNumericValue } val constants = iterable.arrayvalue!!.map { it.constValue(namespace, heap)?.asNumericValue }
@ -160,13 +174,7 @@ private fun collectionArgOutputBoolean(args: List<IExpression>, position: Positi
function: (arg: Collection<Double>)->Boolean): LiteralValue { function: (arg: Collection<Double>)->Boolean): LiteralValue {
if(args.size!=1) if(args.size!=1)
throw SyntaxError("builtin function requires one non-scalar argument", position) throw SyntaxError("builtin function requires one non-scalar argument", position)
var iterable = args[0].constValue(namespace, heap) var iterable = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
if(iterable==null) {
if(args[0] !is IdentifierReference)
throw SyntaxError("function over weird argument ${args[0]}", position)
iterable = ((args[0] as IdentifierReference).targetStatement(namespace) as? VarDecl)?.value?.constValue(namespace, heap)
?: throw SyntaxError("function over weird argument ${args[0]}", position)
}
val result = if(iterable.arrayvalue != null) { val result = if(iterable.arrayvalue != null) {
val constants = iterable.arrayvalue!!.map { it.constValue(namespace, heap)?.asNumericValue } val constants = iterable.arrayvalue!!.map { it.constValue(namespace, heap)?.asNumericValue }
@ -268,13 +276,7 @@ fun builtinSum(args: List<IExpression>, position: Position, namespace:INameScope
fun builtinAvg(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { fun builtinAvg(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
if(args.size!=1) if(args.size!=1)
throw SyntaxError("avg requires array/matrix argument", position) throw SyntaxError("avg requires array/matrix argument", position)
var iterable = args[0].constValue(namespace, heap) var iterable = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
if(iterable==null) {
if(args[0] !is IdentifierReference)
throw SyntaxError("avg over weird argument ${args[0]}", position)
iterable = ((args[0] as IdentifierReference).targetStatement(namespace) as? VarDecl)?.value?.constValue(namespace, heap)
?: throw SyntaxError("avg over weird argument ${args[0]}", position)
}
val result = if(iterable.arrayvalue!=null) { val result = if(iterable.arrayvalue!=null) {
val constants = iterable.arrayvalue!!.map { it.constValue(namespace, heap)?.asNumericValue } val constants = iterable.arrayvalue!!.map { it.constValue(namespace, heap)?.asNumericValue }

View File

@ -317,7 +317,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
val array = newArray.map { val array = newArray.map {
val litval = it as? LiteralValue val litval = it as? LiteralValue
if(litval==null) { if(litval==null) {
addError(ExpressionError("array/matrix can contain only constant values", literalValue.position)) addError(ExpressionError("array/matrix literal can contain only constant values", literalValue.position))
return super.process(literalValue) return super.process(literalValue)
} }
if(litval.bytevalue==null && litval.wordvalue==null) { if(litval.bytevalue==null && litval.wordvalue==null) {
@ -341,7 +341,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
val newValue = LiteralValue(arrayDt, heapId=heapId, position = literalValue.position) val newValue = LiteralValue(arrayDt, heapId=heapId, position = literalValue.position)
return super.process(newValue) return super.process(newValue)
} else { } else {
addError(ExpressionError("array/matrix can contain only constant values", literalValue.position)) addError(ExpressionError("array/matrix literal can contain only constant values", literalValue.position))
} }
val newValue = LiteralValue(arrayDt, arrayvalue = newArray, position = literalValue.position) val newValue = LiteralValue(arrayDt, arrayvalue = newArray, position = literalValue.position)

View File

@ -483,9 +483,11 @@ class StackVm(private var traceOutputFile: String?) {
} }
} }
Syscall.INPUT_STR -> { Syscall.INPUT_STR -> {
val maxlen = evalstack.pop().integerValue() val variable = evalstack.pop()
val input = readLine()?.substring(0, maxlen) ?: "" val value = heap.get(variable.heapId)
TODO("input_str opcode should put the string in a given heap location (overwriting old string)") val maxlen = value.str!!.length
val input = readLine() ?: ""
heap.update(variable.heapId, input.padEnd(maxlen, '\u0000').substring(0, maxlen))
} }
Syscall.GFX_PIXEL -> { Syscall.GFX_PIXEL -> {
// plot pixel at (x, y, color) from stack // plot pixel at (x, y, color) from stack
@ -556,45 +558,69 @@ class StackVm(private var traceOutputFile: String?) {
} }
Syscall.FUNC_MAX -> { Syscall.FUNC_MAX -> {
val iterable = evalstack.pop() val iterable = evalstack.pop()
val dt = val value = heap.get(iterable.heapId)
when(iterable.type) { val resultDt = when(iterable.type) {
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.BYTE
DataType.ARRAY, DataType.MATRIX -> DataType.BYTE DataType.ARRAY, DataType.MATRIX -> DataType.BYTE
DataType.ARRAY_W -> DataType.WORD DataType.ARRAY_W -> DataType.WORD
else -> throw VmExecutionException("uniterable value $iterable") else -> throw VmExecutionException("uniterable value $iterable")
} }
TODO("func_max on array/matrix/string") if(value.str!=null) {
val result = Petscii.encodePetscii(value.str.max().toString(), true)[0]
evalstack.push(Value(DataType.BYTE, result))
} else {
val result = value.array!!.max() ?: 0
evalstack.push(Value(resultDt, result))
}
} }
Syscall.FUNC_MIN -> { Syscall.FUNC_MIN -> {
val iterable = evalstack.pop() val iterable = evalstack.pop()
val dt = val value = heap.get(iterable.heapId)
when(iterable.type) { val resultDt = when(iterable.type) {
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.BYTE
DataType.ARRAY, DataType.MATRIX -> DataType.BYTE DataType.ARRAY, DataType.MATRIX -> DataType.BYTE
DataType.ARRAY_W -> DataType.WORD DataType.ARRAY_W -> DataType.WORD
else -> throw VmExecutionException("uniterable value $iterable") else -> throw VmExecutionException("uniterable value $iterable")
} }
TODO("func_min on array/matrix/string") if(value.str!=null) {
val result = Petscii.encodePetscii(value.str.min().toString(), true)[0]
evalstack.push(Value(DataType.BYTE, result))
} else {
val result = value.array!!.min() ?: 0
evalstack.push(Value(resultDt, result))
}
} }
Syscall.FUNC_AVG -> { Syscall.FUNC_AVG -> {
val iterable = evalstack.pop() val iterable = evalstack.pop()
TODO("func_avg") val value = heap.get(iterable.heapId)
// evalstack.push(Value(DataType.FLOAT, array.arrayvalue!!.average())) if(value.str!=null)
evalstack.push(Value(DataType.FLOAT, Petscii.encodePetscii(value.str, true).average()))
else
evalstack.push(Value(DataType.FLOAT, value.array!!.average()))
} }
Syscall.FUNC_SUM -> { Syscall.FUNC_SUM -> {
val iterable = evalstack.pop() val iterable = evalstack.pop()
TODO("func_sum") val value = heap.get(iterable.heapId)
// evalstack.push(Value(DataType.WORD, array.arrayvalue!!.sum())) if(value.str!=null)
evalstack.push(Value(DataType.WORD, Petscii.encodePetscii(value.str, true).sum()))
else
evalstack.push(Value(DataType.WORD, value.array!!.sum()))
} }
Syscall.FUNC_ANY -> { Syscall.FUNC_ANY -> {
val iterable = evalstack.pop() val iterable = evalstack.pop()
TODO("func_any") val value = heap.get(iterable.heapId)
// evalstack.push(Value(DataType.BYTE, if(array.arrayvalue!!.any{ v -> v != 0}) 1 else 0)) if (value.str != null)
evalstack.push(Value(DataType.BYTE, if (Petscii.encodePetscii(value.str, true).any { c -> c != 0.toShort() }) 1 else 0))
else
evalstack.push(Value(DataType.BYTE, if (value.array!!.any{v->v!=0}) 1 else 0))
} }
Syscall.FUNC_ALL -> { Syscall.FUNC_ALL -> {
val iterable = evalstack.pop() val iterable = evalstack.pop()
TODO("func_all") val value = heap.get(iterable.heapId)
// evalstack.push(Value(DataType.BYTE, if(array.arrayvalue!!.all{ v -> v != 0}) 1 else 0)) if (value.str != null)
evalstack.push(Value(DataType.BYTE, if (Petscii.encodePetscii(value.str, true).all { c -> c != 0.toShort() }) 1 else 0))
else
evalstack.push(Value(DataType.BYTE, if (value.array!!.all{v->v!=0}) 1 else 0))
} }
else -> throw VmExecutionException("unimplemented syscall $syscall") else -> throw VmExecutionException("unimplemented syscall $syscall")
} }