Merge remote-tracking branch 'origin/master'

This commit is contained in:
Irmen de Jong 2018-10-29 10:24:30 +01:00
commit 1e776b1f53
20 changed files with 1058 additions and 1010 deletions

2
.gitignore vendored
View File

@ -23,3 +23,5 @@ __pycache__/
parser.out
parsetab.py
.pytest_cache/
compiler/src/prog8_kotlin.jar
compiler/src/compiled_java

View File

@ -7,10 +7,10 @@
sub start() {
ubyte pixely = 255
ubyte ub = 0
byte b = 99
byte b2 = 100
ubyte ub = 255
ubyte ub2 = 0
word w = 999
word w2 = 3
uword uw = 40000
@ -33,7 +33,6 @@ sub start() {
ubyte[2,3] ubmatrix1
ubyte[2,3] ubmatrix2
memory byte mbyte = $c000
memory byte mbyte2 = $d000
memory ubyte mubyte = $c001
@ -44,156 +43,138 @@ sub start() {
memory uword muword2 = $d004
memory float mfloat = $c006
memory float mfloat2 = $d006
memory byte[3] mbarr1 = $e000
memory ubyte[3] mubarr1 = $e100
memory word[3] mwarr1 = $e100
memory uword[3] muwarr1 = $e100
;label:
str string = "hello"
str_p stringp = "hello"
; all possible assignments to a BYTE VARIABLE (not array)
byte_assignment_to_register:
A = 42
A = X
A = ub
A = mubyte
A = AY[4]
A = ubarr1[2]
A = string[4]
A = string[X]
A = string[b]
A = string[ub]
A = ubarr1[X]
A = ubarr1[b]
A = ubarr1[ub]
A = AY[Y]
A = AY[b]
A = AY[ub]
A = ubmatrix1[1,2]
;A = ubmatrix1[1,Y] ; todo via evaluation
A = ubmatrix1[X,2] ; todo via evaluation TODO fix error constant y dimension of index should have been const-folded with x into one value
;A = ubmatrix1[X,Y] ; todo via evaluation
;A = ubmatrix1[1,b2] ; todo via evaluation
;A = ubmatrix1[X,b2] ; todo via evaluation
A = ubmatrix1[b2,2] ; todo FIX ERROR constant y dimension of index should have been const-folded with x into one value
;A = ubmatrix1[b2,X] ; todo via evaluation
;A = ubmatrix1[b,b2] ; todo via evaluation
;A = ubmatrix1[ub,ub2] ; todo via evaluation
;byte_assignment_to_bytevar:
; b = 42
; b = b2
; b = mbyte
; b = barr1[2]
; b = bmatrix1[1,2]
;
; while A>99 {
; X=22
; }
; ub = 42
; ub = X
; ub = ub2
; ub = mubyte
; ub = ubarr1[2]
; ub = ubmatrix1[1,2]
; ub = string[4]
; ub = AY[4]
;
; repeat {
; X=22
; } until A>99
;
; for X in 0 to 99 {
; Y=33
; }
;; all possible assignments to a WORD VARIABLE (not array)
;
; for ubyte derp in 2 to 44 {
; X=44
; }
;word_assignment_to_registerpair:
; AY = 42
; AY = 42.w
; AY = 42555
; AY = X
; AY = XY
; AY = ub
; AY = mubyte
; AY = ubarr1[2]
; AY = ubmatrix1[1,2]
; AY = string[4]
; AY = uw
; AY = muword
; AY = uwarr1[2]
; AY = string[4]
; AY = XY[4]
;
; if A<22 goto label
;;word_assignment_to_wordvar:
; w = -42
; w = -42.w
; w = -12345
; w = X
; w = b2
; w = ub2
; w = w2
; w = mbyte
; w = mubyte
; w = mword
; w = barr1[2]
; w = ubarr1[2]
; w = warr1[2]
; w = bmatrix1[1,2]
; w = ubmatrix1[1,2]
; w = string[4]
; w = AY[4]
;
; if X<22 {
; A=99
; } else {
; Y=42
; }
Y=42
AY=42
AY=42555
Y = ub
AY= ub
AY= uw
Y = mubyte
AY = mubyte
AY = muword
Y = ubarr1[2]
AY = ubarr1[2]
AY = uwarr1[2]
barr1[2]=42
ubarr1[2]=42
warr1[2]=12555
uwarr1[2]=42555
farr1[2]=42.5678
ubarr1[2]=X
uwarr1[2]=XY
; farr1[2]=XY ; @todo
barr1[2] = b
ubarr1[2] = ub
warr1[2] = w
uwarr1[2] = uw
farr1[2] = fl1
barr1[2] = mbyte
ubarr1[2] = mubyte
warr1[2] = mword
uwarr1[2] = muword
farr1[2] = mfloat
b= barr1[2]
ub = ubarr1[2]
w = warr1[2]
uw = uwarr1[2]
; fl1 = farr1[2] ; @todo
mbyte= barr1[2]
mubyte = ubarr1[2]
mword = warr1[2]
muword = uwarr1[2]
; mfloat = farr1[2] ; @todo
barr1[2] = barr2[3]
ubarr1[2] = ubarr2[3]
warr1[2] = warr2[3]
uwarr1[2] = uwarr2[3]
; farr1[2] = farr2[3] ; @todo
XY[2]=42
XY[2] = ub
XY[2] = mubyte
ub = XY[2]
uw = XY[2]
;fl1 = XY[2] ; @todo
mubyte = XY[2]
muword = XY[2]
;mfloat = XY[2] ; @todo
XY[2] = AY[3] ; @todo wat is de output hiervan???
b = 1
ub = 1
w = 1
uw = 1
fl1 = 2.345
b = b2
ub = pixely
w = b2
w = w2
w = ub
uw = ub
uw = uw2
;fl1 = ub ; @todo
;fl1 = b2 ; @todo
;fl1 = uw2 ; @todo
;fl1 = w2 ; @todo
fl1 = fl2
b = mbyte
ub = mubyte
w = mword
w = mbyte
w = mubyte
uw = mubyte
uw = muword
fl1 = mfloat
;fl1 = mbyte ; @todo
;fl1 = mword ; @todo
;fl1 = mubyte ; @todo
;fl1 = muword ; @todo
mbyte = 1
mubyte = 1
mword = 1
muword = 1
mfloat = 3.456
%breakpoint
mbyte = b
mubyte = ub
mword = w
muword = uw
mfloat = fl2
%breakpoint
mbyte = mbyte2
mubyte = mubyte2
mword = mword2
muword = muword2
mfloat = mfloat2
; uw = 42
; uw = 42.w
; uw = 42555
; uw = X
; uw = AY
; uw = ub2
; uw = uw2
; uw = mubyte
; uw = muword
; uw = ubarr1[2]
; uw = uwarr1[2]
; uw = ubmatrix1[1,2]
; uw = string[4]
; uw = AY[4]
;
;
;; all possible assignments to a FLOAT VARIABLE
;float_assignment_to_floatvar:
; fl1 = 34
; fl1 = 34555.w
; fl1 = 3.33e22
; fl1 = X
; fl1 = AY
; fl1 = b2
; fl1 = ub2
; fl1 = w2
; fl1 = uw2
; fl1 = mbyte
; fl1 = mubyte
; fl1 = mword
; fl1 = muword
; fl1 = barr1[2]
; fl1 = ubarr1[2]
; fl1 = warr1[2]
; fl1 = uwarr1[2]
; fl1 = bmatrix1[1,2]
; fl1 = ubmatrix1[1,2]
; fl1 = string[4]
return
}

View File

@ -1,6 +1,6 @@
mkdir compiled_java
java -jar ../antlr/lib/antlr-4.7.1-complete.jar -o ./prog8/parser -no-listener -no-visitor -package prog8.parser ../antlr/prog8.g4
java -jar ../antlr/lib/antlr-4.7.1-complete.jar -o ./prog8/parser -Xexact-output-dir -no-listener -no-visitor -package prog8.parser ../antlr/prog8.g4
@dir /b /S src *.java > sources.txt
javac -verbose -d compiled_java -cp ../antlr/lib/antlr-runtime-4.7.1.jar @sources.txt

View File

@ -1,5 +1,7 @@
#!/usr/bin/env bash
java -jar ../antlr/lib/antlr-4.7.1-complete.jar -o ./prog8/parser -Xexact-output-dir -no-listener -no-visitor -package prog8.parser ../antlr/prog8.g4
find prog8 -name \*.java > javasources.txt
mkdir -p compiled_java
javac -verbose -d compiled_java -cp ../antlr/lib/antlr-runtime-4.7.1.jar @javasources.txt

View File

@ -67,7 +67,7 @@ enum class BranchCondition {
}
val IterableDatatypes = setOf(
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS,
DataType.STR, DataType.STR_S, // note: the STR_P/STR_PS types aren't iterable because they store their length as the first byte
DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_F, DataType.MATRIX_UB, DataType.MATRIX_B)
@ -231,7 +231,7 @@ interface IAstProcessor {
fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression {
arrayIndexedExpression.identifier?.process(this)
arrayIndexedExpression.array.process(this)
arrayIndexedExpression.arrayspec.process(this)
return arrayIndexedExpression
}
@ -622,7 +622,7 @@ class VarDecl(val type: VarDeclType,
DataType.WORD -> DataType.ARRAY_W
DataType.FLOAT -> DataType.ARRAY_F
else -> {
datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats", position))
datatypeErrors.add(SyntaxError("arrayspec can only contain bytes/words/floats", position))
DataType.UBYTE
}
}
@ -898,13 +898,13 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
class ArrayIndexedExpression(val identifier: IdentifierReference?,
val register: Register?,
var array: ArraySpec,
var arrayspec: ArraySpec,
override val position: Position) : IExpression {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
identifier?.linkParents(this)
array.linkParents(this)
arrayspec.linkParents(this)
}
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
@ -930,6 +930,10 @@ class ArrayIndexedExpression(val identifier: IdentifierReference?,
}
throw FatalAstException("cannot get indexed element on $target")
}
override fun toString(): String {
return "ArrayIndexed(ident=$identifier, reg=$register, arrayspec=$arrayspec; pos=$position)"
}
}
@ -1046,8 +1050,8 @@ class LiteralValue(val type: DataType,
else "str:$strvalue"
}
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F -> {
if(heapId!=null) "array:#$heapId"
else "array:$arrayvalue"
if(heapId!=null) "arrayspec:#$heapId"
else "arrayspec:$arrayvalue"
}
DataType.MATRIX_UB, DataType.MATRIX_B -> {
if(heapId!=null) "matrix:#$heapId"
@ -1936,7 +1940,7 @@ private fun prog8Parser.ExpressionContext.toAst() : IExpression {
litval.charliteral()!=null -> LiteralValue(DataType.UBYTE, bytevalue = Petscii.encodePetscii(litval.charliteral().text.unescape(), true)[0], position = litval.toPosition())
litval.arrayliteral()!=null -> {
val array = litval.arrayliteral()?.toAst()
// the actual type of the array can not yet be determined here (missing namespace & heap)
// the actual type of the arrayspec can not yet be determined here (missing namespace & heap)
// the ConstantFolder takes care of that and converts the type if needed.
LiteralValue(DataType.ARRAY_UB, arrayvalue = array, position = litval.toPosition())
}

View File

@ -247,7 +247,7 @@ class AstChecker(private val namespace: INameScope,
if(param.second.register==Register.AX || param.second.register==Register.AY ||
param.second.register==Register.XY) {
if(param.first.type!=DataType.UWORD && param.first.type !in StringDatatypes && param.first.type !in ArrayDatatypes)
err("parameter '${param.first.name}' should be uword/str/array")
err("parameter '${param.first.name}' should be uword/str/arrayspec")
}
}
for(ret in subroutine.returntypes.withIndex().zip(subroutine.asmReturnvaluesRegisters)) {
@ -260,7 +260,7 @@ class AstChecker(private val namespace: INameScope,
ret.second.register==Register.XY) {
if(ret.first.value!=DataType.UWORD && ret.first.value != DataType.UBYTE &&
ret.first.value !in StringDatatypes && ret.first.value !in ArrayDatatypes)
err("return value #${ret.first.index+1} should be uword/ubyte/string/array")
err("return value #${ret.first.index+1} should be uword/ubyte/string/arrayspec")
}
}
@ -330,11 +330,17 @@ class AstChecker(private val namespace: INameScope,
return super.process(assignment)
}
}
} else if(assignment.target.arrayindexed!=null) {
if(assignment.target.arrayindexed!!.register!=null) {
val value = assignment.value
if (value is ArrayIndexedExpression && value.register in setOf(Register.AX, Register.AY, Register.XY))
checkResult.add(SyntaxError("reading AND writing from registerpair arrays not supported due to register overlap", assignment.position))
}
}
// it is not possible to assign a new array to something.
// it is not possible to assign a new arrayspec to something.
if(assignment.value.resultingDatatype(namespace, heap) in ArrayDatatypes)
checkResult.add(SyntaxError("it's not possible to assign an array literal value to something, use it as a variable decl initializer instead", assignment.position))
checkResult.add(SyntaxError("it's not possible to assign an arrayspec literal value to something, use it as a variable decl initializer instead", assignment.position))
if(assignment.aug_op!=null) {
// check augmented assignment:
@ -440,6 +446,25 @@ class AstChecker(private val namespace: INameScope,
}
}
VarDeclType.MEMORY -> {
if(decl.arrayspec!=null) {
val arraySize = decl.arrayspec.size() ?: 1
when(decl.datatype) {
DataType.ARRAY_B, DataType.ARRAY_UB ->
if(arraySize > 256)
err("byte arrayspec length must be 1-256")
DataType.ARRAY_W, DataType.ARRAY_UW ->
if(arraySize > 128)
err("word arrayspec length must be 1-128")
DataType.ARRAY_F ->
if(arraySize > 51)
err("float arrayspec length must be 1-51")
DataType.MATRIX_B, DataType.MATRIX_UB ->
if(arraySize > 32768)
err("invalid matrix size, must be 1-32768")
else -> {}
}
}
if(decl.value !is LiteralValue) {
err("value of memory var decl is not a literal (it is a ${decl.value!!::class.simpleName}).", decl.value?.position)
} else {
@ -545,7 +570,7 @@ class AstChecker(private val namespace: INameScope,
}
in ArrayDatatypes -> {
if(lv.heapId==null)
throw FatalAstException("array/matrix should have been moved to heap at ${lv.position}")
throw FatalAstException("arrayspec/matrix should have been moved to heap at ${lv.position}")
}
else -> {}
}
@ -677,7 +702,7 @@ class AstChecker(private val namespace: INameScope,
val indexedRegister = postIncrDecr.target.arrayindexed?.register
if(indexedRegister!=null) {
if(indexedRegister==Register.A || indexedRegister==Register.X || indexedRegister==Register.Y)
checkResult.add(SyntaxError("array indexing on registers requires register pair variable", postIncrDecr.position))
checkResult.add(SyntaxError("indexing on registers requires register pair variable", postIncrDecr.position))
} else {
val target = postIncrDecr.target.arrayindexed?.identifier?.targetStatement(namespace)
if(target==null) {
@ -699,25 +724,45 @@ class AstChecker(private val namespace: INameScope,
val target = arrayIndexedExpression.identifier!!.targetStatement(namespace)
if(target is VarDecl) {
if(target.datatype !in IterableDatatypes)
checkResult.add(SyntaxError("array indexing requires an iterable variable", arrayIndexedExpression.position))
checkResult.add(SyntaxError("indexing requires an iterable variable", arrayIndexedExpression.position))
val arraysize = target.arrayspec?.size()
if(arraysize!=null) {
// check out of bounds
if((arrayIndexedExpression.array.y as? LiteralValue)?.asIntegerValue != null) {
throw FatalAstException("constant y dimension of index should have been const-folded with x into one value")
if((arrayIndexedExpression.arrayspec.y as? LiteralValue)?.asIntegerValue != null) {
throw FatalAstException("constant y dimension of index should have been const-folded with x into one value ${arrayIndexedExpression.arrayspec.position}")
}
val index = (arrayIndexedExpression.array.x as? LiteralValue)?.asIntegerValue
val index = (arrayIndexedExpression.arrayspec.x as? LiteralValue)?.asIntegerValue
if(index!=null && (index<0 || index>=arraysize))
checkResult.add(ExpressionError("array index out of bounds", arrayIndexedExpression.array.position))
checkResult.add(ExpressionError("arrayspec index out of bounds", arrayIndexedExpression.arrayspec.position))
} else if(target.datatype in StringDatatypes) {
// check string lengths
(arrayIndexedExpression.arrayspec.y as? LiteralValue)?.asIntegerValue
val heapId = (target.value as LiteralValue).heapId!!
val stringLen = heap.get(heapId).str!!.length
val index = (arrayIndexedExpression.arrayspec.x as? LiteralValue)?.asIntegerValue
if(index!=null && (index<0 || index>=stringLen))
checkResult.add(ExpressionError("index out of bounds", arrayIndexedExpression.arrayspec.position))
}
} else
checkResult.add(SyntaxError("array indexing requires a variable to act upon", arrayIndexedExpression.position))
checkResult.add(SyntaxError("indexing requires a variable to act upon", arrayIndexedExpression.position))
} else if(reg==Register.A || reg==Register.X || reg==Register.Y) {
checkResult.add(SyntaxError("array indexing on registers requires register pair variable", arrayIndexedExpression.position))
} else if(arrayIndexedExpression.array.y!=null) {
checkResult.add(SyntaxError("array indexing on registers can only use one index dimension", arrayIndexedExpression.position))
checkResult.add(SyntaxError("indexing on registers requires register pair variable", arrayIndexedExpression.position))
} else if(arrayIndexedExpression.arrayspec.y!=null) {
checkResult.add(SyntaxError("indexing on registers can only use one index dimension", arrayIndexedExpression.position))
}
// check index value 0..255
val regx = (arrayIndexedExpression.arrayspec.x as? RegisterExpr)?.register
val regy = (arrayIndexedExpression.arrayspec.y as? RegisterExpr)?.register
if((regx in setOf(Register.AX, Register.AY, Register.XY)) ||
(regy in setOf(Register.AX, Register.AY, Register.XY))) {
checkResult.add(SyntaxError("array indexing is limited to byte size 0..255", arrayIndexedExpression.position))
}
val dtx = arrayIndexedExpression.arrayspec.x.resultingDatatype(namespace, heap)
val dty = arrayIndexedExpression.arrayspec.y?.resultingDatatype(namespace, heap)
if(dtx!=DataType.UBYTE && dtx!=DataType.BYTE || (dty!=null && dty != DataType.UBYTE && dty != DataType.BYTE))
checkResult.add(SyntaxError("array indexing is limited to byte size 0..255", arrayIndexedExpression.position))
return super.process(arrayIndexedExpression)
}
@ -760,7 +805,7 @@ class AstChecker(private val namespace: INameScope,
val expectedSize = arrayspec!!.size()
val rangeSize=range.size()
if(rangeSize!=null && rangeSize != expectedSize) {
checkResult.add(ExpressionError("range size doesn't match array/matrix size, expected $expectedSize found $rangeSize", range.position))
checkResult.add(ExpressionError("range size doesn't match arrayspec/matrix size, expected $expectedSize found $rangeSize", range.position))
return false
}
return true
@ -825,61 +870,61 @@ class AstChecker(private val namespace: INameScope,
return err("string length must be 0-255")
}
DataType.ARRAY_UB, DataType.ARRAY_B -> {
// value may be either a single byte, or a byte array (of all constant values)
// value may be either a single byte, or a byte arrayspec (of all constant values)
if(value.type==targetDt) {
val arraySpecSize = arrayspec.size()
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>256)
return err("byte array length must be 1-256")
return err("byte arrayspec length must be 1-256")
val constX = arrayspec.x.constValue(namespace, heap)
if(constX?.asIntegerValue==null)
return err("array size specifier must be constant integer value")
return err("arrayspec size specifier must be constant integer value")
val expectedSize = constX.asIntegerValue
if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
return err("initializer arrayspec size mismatch (expecting $expectedSize, got $arraySize)")
return true
}
return err("invalid byte array size, must be 1-256")
return err("invalid byte arrayspec size, must be 1-256")
}
return err("invalid byte array initialization value ${value.type}, expected $targetDt")
return err("invalid byte arrayspec initialization value ${value.type}, expected $targetDt")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
// value may be either a single word, or a word array
// value may be either a single word, or a word arrayspec
if(value.type==targetDt) {
val arraySpecSize = arrayspec.size()
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>128)
return err("word array length must be 1-128")
return err("word arrayspec length must be 1-128")
val constX = arrayspec.x.constValue(namespace, heap)
if(constX?.asIntegerValue==null)
return err("array size specifier must be constant integer value")
return err("arrayspec size specifier must be constant integer value")
val expectedSize = constX.asIntegerValue
if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
return err("initializer arrayspec size mismatch (expecting $expectedSize, got $arraySize)")
return true
}
return err("invalid word array size, must be 1-128")
return err("invalid word arrayspec size, must be 1-128")
}
return err("invalid word array initialization value ${value.type}, expected $targetDt")
return err("invalid word arrayspec initialization value ${value.type}, expected $targetDt")
}
DataType.ARRAY_F -> {
// value may be either a single float, or a float array
// value may be either a single float, or a float arrayspec
if(value.type==targetDt) {
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).doubleArray!!.size
val arraySpecSize = arrayspec.size()
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize < 1 || arraySpecSize>51)
return err("float array length must be 1-51")
return err("float arrayspec length must be 1-51")
val constX = arrayspec.x.constValue(namespace, heap)
if(constX?.asIntegerValue==null)
return err("array size specifier must be constant integer value")
return err("arrayspec size specifier must be constant integer value")
val expectedSize = constX.asIntegerValue
if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
return err("initializer arrayspec size mismatch (expecting $expectedSize, got $arraySize)")
} else
return err("invalid float array size, must be 1-51")
return err("invalid float arrayspec size, must be 1-51")
// check if the floating point values are all within range
val doubles = if(value.arrayvalue!=null)
@ -890,17 +935,17 @@ class AstChecker(private val namespace: INameScope,
return err("floating point value overflow")
return true
}
return err("invalid float array initialization value ${value.type}, expected $targetDt")
return err("invalid float arrayspec initialization value ${value.type}, expected $targetDt")
}
DataType.MATRIX_UB, DataType.MATRIX_B -> {
// value can only be a single byte, or a byte array (which represents the matrix)
// value can only be a single byte, or a byte arrayspec (which represents the matrix)
if(value.type==targetDt ||
(targetDt==DataType.MATRIX_UB && value.type==DataType.ARRAY_UB) ||
(targetDt==DataType.MATRIX_B && value.type==DataType.ARRAY_B)) {
val arraySpecSize = arrayspec.size()
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>256)
return err("invalid matrix size, must be 1-256")
if(arraySpecSize<1 || arraySpecSize>32768)
return err("invalid matrix size, must be 1-32768")
val constX = arrayspec.x.constValue(namespace, heap)
val constY = arrayspec.y?.constValue(namespace, heap)
if (constX?.asIntegerValue == null || (constY!=null && constY.asIntegerValue == null))
@ -912,9 +957,9 @@ class AstChecker(private val namespace: INameScope,
return err("initializer matrix size mismatch (expecting $expectedSize, got ${matrix.size} elements)")
return true
}
return err("invalid matrix size, must be 1-256")
return err("invalid matrix size, must be 1-32768")
}
return err("invalid matrix initialization value of type ${value.type} - expecting byte array")
return err("invalid matrix initialization value of type ${value.type} - expecting byte arrayspec")
}
}
return true

View File

@ -126,7 +126,7 @@ class AstIdentifiersChecker : IAstProcessor {
if(forLoop.decltype!=null)
checkResult.add(SyntaxError("register loop variables cannot be explicitly declared with a datatype", forLoop.position))
if(forLoop.loopRegister == Register.X || forLoop.loopRegister==Register.XY || forLoop.loopRegister==Register.AX)
checkResult.add(SyntaxError("it's not possible to write to the X register because it's used as an internal pointer", forLoop.position))
printWarning("possible problem writing to the X register, because it's used as an internal pointer", forLoop.position)
} else if(forLoop.loopVar!=null) {
val varName = forLoop.loopVar.nameInSource.last()
when (forLoop.decltype) {
@ -148,7 +148,7 @@ class AstIdentifiersChecker : IAstProcessor {
override fun process(assignTarget: AssignTarget): AssignTarget {
if(assignTarget.register==Register.X || assignTarget.register==Register.AX || assignTarget.register==Register.XY)
checkResult.add(SyntaxError("it's not possible to write to the X register because it's used as an internal pointer", assignTarget.position))
printWarning("possible problem writing to the X register, because it's used as an internal pointer", assignTarget.position)
return super.process(assignTarget)
}
}

View File

@ -312,7 +312,9 @@ private class StatementTranslator(private val prog: IntermediateProgram,
DataType.ARRAY_UW, DataType.ARRAY_W -> Opcode.READ_INDEXED_VAR_WORD
DataType.ARRAY_F -> Opcode.READ_INDEXED_VAR_FLOAT
DataType.MATRIX_UB, DataType.MATRIX_B -> Opcode.READ_INDEXED_VAR_BYTE
else -> throw CompilerException("invalid dt for indexed $dt")
DataType.STR, DataType.STR_S -> Opcode.READ_INDEXED_VAR_BYTE
DataType.STR_P, DataType.STR_PS -> throw CompilerException("cannot access pascal-string type $dt with index")
else -> throw CompilerException("invalid dt for indexed access $dt")
}
}
@ -322,7 +324,9 @@ private class StatementTranslator(private val prog: IntermediateProgram,
DataType.ARRAY_UW, DataType.ARRAY_W -> Opcode.WRITE_INDEXED_VAR_WORD
DataType.ARRAY_F -> Opcode.WRITE_INDEXED_VAR_FLOAT
DataType.MATRIX_UB, DataType.MATRIX_B -> Opcode.WRITE_INDEXED_VAR_BYTE
else -> throw CompilerException("invalid dt for indexed $dt")
DataType.STR, DataType.STR_S -> Opcode.WRITE_INDEXED_VAR_BYTE
DataType.STR_P, DataType.STR_PS -> throw CompilerException("cannot access pascal-string type $dt with index")
else -> throw CompilerException("invalid dt for indexed access $dt")
}
}
@ -350,14 +354,12 @@ private class StatementTranslator(private val prog: IntermediateProgram,
private fun opcodePopmem(dt: DataType): Opcode {
return when (dt) {
DataType.UBYTE -> Opcode.POP_MEM_UB
DataType.BYTE -> Opcode.POP_MEM_B
DataType.UWORD -> Opcode.POP_MEM_UW
DataType.WORD -> Opcode.POP_MEM_W
DataType.UBYTE, DataType.BYTE -> Opcode.POP_MEM_BYTE
DataType.UWORD, DataType.WORD -> Opcode.POP_MEM_WORD
DataType.FLOAT -> Opcode.POP_MEM_FLOAT
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS,
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F, DataType.MATRIX_UB,
DataType.ARRAY_B, DataType.ARRAY_W, DataType.MATRIX_B -> Opcode.POP_MEM_UW
DataType.ARRAY_B, DataType.ARRAY_W, DataType.MATRIX_B -> Opcode.POP_MEM_WORD
}
}
@ -628,7 +630,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F, DataType.MATRIX_UB,
DataType.ARRAY_B, DataType.ARRAY_W, DataType.MATRIX_B -> {
if(lv.heapId==null)
throw CompilerException("array/matrix should have been moved into heap ${lv.position}")
throw CompilerException("arrayspec/matrix should have been moved into heap ${lv.position}")
prog.instr(Opcode.PUSH_WORD, Value(lv.type, lv.heapId))
}
}
@ -1046,14 +1048,14 @@ private class StatementTranslator(private val prog: IntermediateProgram,
val reg=arrayindexed.register
if(reg==Register.A || reg==Register.X || reg==Register.Y)
throw CompilerException("requires register pair")
if(arrayindexed.array.y!=null)
if(arrayindexed.arrayspec.y!=null)
throw CompilerException("when using an address, can only use one index dimension")
reg.toString()
} else {
variable!!.scopedname
}
translate(arrayindexed.array.x)
val y = arrayindexed.array.y
translate(arrayindexed.arrayspec.x)
val y = arrayindexed.arrayspec.y
if(y!=null) {
// calc matrix index i=y*columns+x
// (the const-folding will have removed this for us when both x and y are constants)
@ -1167,12 +1169,12 @@ private class StatementTranslator(private val prog: IntermediateProgram,
when (valueDt) {
DataType.UBYTE -> prog.instr(Opcode.UB2FLOAT)
DataType.BYTE -> prog.instr(Opcode.B2FLOAT)
DataType.UWORD -> prog.instr(Opcode.W2FLOAT)
DataType.WORD -> prog.instr(Opcode.UW2FLOAT)
DataType.UWORD -> prog.instr(Opcode.UW2FLOAT)
DataType.WORD -> prog.instr(Opcode.W2FLOAT)
else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
}
}
// todo: maybe if you assign byte or word to array/matrix, clear it with that value?
// todo: maybe if you assign byte or word to arrayspec/matrix, clear it with that value?
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_F, DataType.MATRIX_UB, DataType.MATRIX_B -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")

View File

@ -16,15 +16,15 @@ abstract class Zeropage(private val options: CompilationOptions) {
val size =
if(vardecl.arrayspec!=null) {
printWarning("allocating a large value (array) in zeropage", vardecl.position)
printWarning("allocating a large value (arrayspec) in zeropage", vardecl.position)
val y = (vardecl.arrayspec.y as? LiteralValue)?.asIntegerValue
if(y==null) {
// 1 dimensional array
// 1 dimensional arrayspec
when(vardecl.datatype) {
DataType.UBYTE -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!!
DataType.UWORD -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 2
DataType.FLOAT -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 5
else -> throw CompilerException("array can only be of byte, word, float")
else -> throw CompilerException("arrayspec can only be of byte, word, float")
}
} else {
// 2 dimensional matrix (only bytes for now)

View File

@ -25,10 +25,6 @@ open class Instruction(val opcode: Opcode,
// opcodes that manipulate a variable
"${opcode.toString().toLowerCase()} ${callLabel?:""} ${callLabel2?:""}".trimEnd()
}
opcode in setOf(Opcode.COPY_MEM_BYTE, Opcode.COPY_MEM_WORD, Opcode.COPY_MEM_FLOAT) -> {
// opcodes with two (address) args
"${opcode.toString().toLowerCase()} $arg $arg2"
}
callLabel==null -> "${opcode.toString().toLowerCase()} $argStr"
else -> "${opcode.toString().toLowerCase()} $callLabel $argStr"
}

View File

@ -79,54 +79,47 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
when (it[0].value.opcode) {
Opcode.PUSH_VAR_BYTE ->
if (it[1].value.opcode == Opcode.POP_VAR_BYTE) {
if (it[0].value.callLabel != it[1].value.callLabel)
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_VAR_BYTE, null, null, it[0].value.callLabel, it[1].value.callLabel)
else
if (it[0].value.callLabel == it[1].value.callLabel) {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
}
Opcode.PUSH_VAR_WORD ->
if (it[1].value.opcode == Opcode.POP_VAR_WORD) {
if (it[0].value.callLabel != it[1].value.callLabel)
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_VAR_WORD, null, null, it[0].value.callLabel, it[1].value.callLabel)
else
if (it[0].value.callLabel == it[1].value.callLabel) {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
}
Opcode.PUSH_VAR_FLOAT ->
if (it[1].value.opcode == Opcode.POP_VAR_FLOAT) {
if (it[0].value.callLabel != it[1].value.callLabel)
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_VAR_FLOAT, null, null, it[0].value.callLabel, it[1].value.callLabel)
else
if (it[0].value.callLabel == it[1].value.callLabel) {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
}
Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB ->
if(it[1].value.opcode == Opcode.POP_MEM_B || it[1].value.opcode == Opcode.POP_MEM_UB) {
if(it[0].value.arg != it[1].value.arg)
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_MEM_BYTE, it[0].value.arg, it[1].value.arg)
else
if(it[1].value.opcode == Opcode.POP_MEM_BYTE) {
if(it[0].value.arg == it[1].value.arg) {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
}
Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW ->
if(it[1].value.opcode == Opcode.POP_MEM_W || it[1].value.opcode == Opcode.POP_MEM_UW) {
if(it[0].value.arg != it[1].value.arg)
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_MEM_WORD, it[0].value.arg, it[1].value.arg)
else
if(it[1].value.opcode == Opcode.POP_MEM_WORD) {
if(it[0].value.arg == it[1].value.arg) {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
}
Opcode.PUSH_MEM_FLOAT ->
if(it[1].value.opcode == Opcode.POP_MEM_FLOAT) {
if(it[0].value.arg != it[1].value.arg)
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_MEM_FLOAT, it[0].value.arg, it[1].value.arg)
else
if(it[0].value.arg == it[1].value.arg) {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
}
else -> {
}
else -> {}
}
}
@ -289,7 +282,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F -> {
val litval = (decl.value as LiteralValue)
if(litval.heapId==null)
throw CompilerException("array/matrix should already be in the heap")
throw CompilerException("arrayspec/matrix should already be in the heap")
Value(decl.datatype, litval.heapId)
}
}

View File

@ -4,7 +4,7 @@ enum class Opcode {
// pushing values on the (evaluation) stack
PUSH_BYTE, // push byte value
PUSH_WORD, // push word value (or 'address' of string / array / matrix)
PUSH_WORD, // push word value (or 'address' of string / arrayspec / matrix)
PUSH_FLOAT, // push float value
PUSH_MEM_B, // push byte value from memory to stack
PUSH_MEM_UB, // push unsigned byte value from memory to stack
@ -19,23 +19,13 @@ enum class Opcode {
DISCARD_BYTE, // discard top byte value
DISCARD_WORD, // discard top word value
DISCARD_FLOAT, // discard top float value
POP_MEM_B, // pop byte value into destination memory address
POP_MEM_UB, // pop byte value into destination memory address
POP_MEM_W, // pop word value into destination memory address
POP_MEM_UW, // pop word value into destination memory address
POP_MEM_BYTE, // pop (u)byte value into destination memory address
POP_MEM_WORD, // pop (u)word value into destination memory address
POP_MEM_FLOAT, // pop float value into destination memory address
POP_VAR_BYTE, // pop byte value into variable (byte, ubyte)
POP_VAR_WORD, // pop word value into variable (word, uword)
POP_VAR_BYTE, // pop (u)byte value into variable
POP_VAR_WORD, // pop (u)word value into variable
POP_VAR_FLOAT, // pop float value into variable
// optimized copying of one var to another (replaces push+pop)
COPY_VAR_BYTE,
COPY_VAR_WORD,
COPY_VAR_FLOAT,
COPY_MEM_BYTE,
COPY_MEM_WORD,
COPY_MEM_FLOAT,
// numeric arithmetic
ADD_UB,
ADD_B,
@ -187,7 +177,7 @@ enum class Opcode {
NOTEQUAL_WORD,
NOTEQUAL_F,
// array access
// arrayspec access
READ_INDEXED_VAR_BYTE,
READ_INDEXED_VAR_WORD,
READ_INDEXED_VAR_FLOAT,
@ -233,32 +223,6 @@ val opcodesWithVarArgument = setOf(
Opcode.ROL2_VAR_BYTE, Opcode.ROL2_VAR_WORD, Opcode.ROR2_VAR_BYTE, Opcode.ROR2_VAR_WORD,
Opcode.POP_VAR_BYTE, Opcode.POP_VAR_WORD, Opcode.POP_VAR_FLOAT,
Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_FLOAT,
Opcode.COPY_VAR_BYTE, Opcode.COPY_VAR_WORD, Opcode.COPY_VAR_FLOAT,
Opcode.READ_INDEXED_VAR_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.READ_INDEXED_VAR_FLOAT,
Opcode.WRITE_INDEXED_VAR_BYTE, Opcode.WRITE_INDEXED_VAR_WORD, Opcode.WRITE_INDEXED_VAR_FLOAT
)
val pushOpcodes = setOf(
Opcode.PUSH_BYTE,
Opcode.PUSH_WORD,
Opcode.PUSH_FLOAT,
Opcode.PUSH_MEM_B,
Opcode.PUSH_MEM_UB,
Opcode.PUSH_MEM_W,
Opcode.PUSH_MEM_UW,
Opcode.PUSH_MEM_FLOAT,
Opcode.PUSH_VAR_BYTE,
Opcode.PUSH_VAR_WORD,
Opcode.PUSH_VAR_FLOAT
)
val popOpcodes = setOf(
Opcode.POP_MEM_B,
Opcode.POP_MEM_UB,
Opcode.POP_MEM_W,
Opcode.POP_MEM_UW,
Opcode.POP_MEM_FLOAT,
Opcode.POP_VAR_BYTE,
Opcode.POP_VAR_WORD,
Opcode.POP_VAR_FLOAT
)

File diff suppressed because it is too large Load Diff

View File

@ -100,7 +100,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
if(arglist.type==DataType.ARRAY_UB || arglist.type==DataType.ARRAY_UW || arglist.type==DataType.ARRAY_F || arglist.type==DataType.MATRIX_UB) {
val dt = arglist.arrayvalue!!.map {it.resultingDatatype(namespace, heap)}
if(dt.any { it!=DataType.UBYTE && it!=DataType.UWORD && it!=DataType.FLOAT}) {
throw FatalAstException("fuction $function only accepts array of numeric values")
throw FatalAstException("fuction $function only accepts arrayspec of numeric values")
}
if(dt.any { it==DataType.FLOAT }) return DataType.FLOAT
if(dt.any { it==DataType.UWORD }) return DataType.UWORD
@ -119,10 +119,10 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
DataType.ARRAY_F -> DataType.FLOAT
DataType.MATRIX_UB -> DataType.UBYTE
DataType.MATRIX_B -> DataType.BYTE
null -> throw FatalAstException("function requires one argument which is an array $function")
null -> throw FatalAstException("function requires one argument which is an arrayspec $function")
}
}
throw FatalAstException("function requires one argument which is an array $function")
throw FatalAstException("function requires one argument which is an arrayspec $function")
}
val func = BuiltinFunctions[function]!!
@ -237,7 +237,7 @@ private fun collectionArgOutputBoolean(args: List<IExpression>, position: Positi
throw NotConstArgumentException()
function(constants.map { it!!.toDouble() })
} else {
val array = heap.get(iterable.heapId!!).array ?: throw SyntaxError("function requires array/matrix argument", position)
val array = heap.get(iterable.heapId!!).array ?: throw SyntaxError("function requires arrayspec/matrix argument", position)
function(array.map { it.toDouble() })
}
return LiteralValue.fromBoolean(result, position)
@ -313,7 +313,7 @@ private fun builtinAbs(args: List<IExpression>, position: Position, namespace:IN
private fun builtinAvg(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
if(args.size!=1)
throw SyntaxError("avg requires array/matrix argument", position)
throw SyntaxError("avg requires arrayspec/matrix argument", position)
val iterable = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val result = if(iterable.arrayvalue!=null) {
@ -323,7 +323,7 @@ private fun builtinAvg(args: List<IExpression>, position: Position, namespace:IN
(constants.map { it!!.toDouble() }).average()
}
else {
val array = heap.get(iterable.heapId!!).array ?: throw SyntaxError("avg requires array/matrix argument", position)
val array = heap.get(iterable.heapId!!).array ?: throw SyntaxError("avg requires arrayspec/matrix argument", position)
array.average()
}
return numericLiteral(result, args[0].position)

View File

@ -56,10 +56,10 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.MATRIX_UB, DataType.MATRIX_B -> {
val litval = decl.value as? LiteralValue
if(litval?.type==DataType.FLOAT)
errors.add(ExpressionError("array requires only integers here", litval.position))
errors.add(ExpressionError("arrayspec requires only integers here", litval.position))
val size = decl.arrayspec!!.size()
if(litval!=null && litval.isArray) {
// array initializer value is an array already, keep as-is (or convert to WORDs if needed)
// arrayspec initializer value is an arrayspec already, keep as-is (or convert to WORDs if needed)
if(litval.heapId!=null) {
if (decl.datatype == DataType.MATRIX_UB && litval.type != DataType.MATRIX_UB) {
val array = heap.get(litval.heapId).copy(type = DataType.MATRIX_UB)
@ -79,7 +79,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
}
}
} else if (size != null) {
// array initializer is empty or a single int, and we know the size; create the array.
// arrayspec initializer is empty or a single int, and we know the size; create the arrayspec.
val fillvalue = if (litval == null) 0 else litval.asIntegerValue ?: 0
when(decl.datatype){
DataType.ARRAY_UB, DataType.MATRIX_UB -> {
@ -109,7 +109,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
val litval = decl.value as? LiteralValue
val size = decl.arrayspec!!.size()
if(litval!=null && litval.isArray) {
// array initializer value is an array already, make sure to convert to floats
// arrayspec initializer value is an arrayspec already, make sure to convert to floats
if(litval.heapId!=null) {
val array = heap.get(litval.heapId)
if (array.doubleArray == null) {
@ -119,7 +119,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
}
}
} else if (size != null) {
// array initializer is empty or a single int, and we know the size; create the array.
// arrayspec initializer is empty or a single int, and we know the size; create the arrayspec.
val fillvalue = if (litval == null) 0.0 else litval.asNumericValue?.toDouble() ?: 0.0
if(fillvalue< FLOAT_MAX_NEGATIVE || fillvalue> FLOAT_MAX_POSITIVE)
errors.add(ExpressionError("float value overflow", litval?.position ?: decl.position))
@ -492,7 +492,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
val array: Array<IExpression> = arraylit.arrayvalue!!.map { it.process(this) }.toTypedArray()
val allElementsAreConstant = array.fold(true) { c, expr-> c and (expr is LiteralValue)}
if(!allElementsAreConstant) {
addError(ExpressionError("array/matrix literal can contain only constant values", arraylit.position))
addError(ExpressionError("arrayspec/matrix literal can contain only constant values", arraylit.position))
return arraylit
} else {
val valuesInArray = array.map { it.constValue(namespace, heap)!!.asNumericValue!! }
@ -533,24 +533,24 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
DataType.MATRIX_UB,
DataType.MATRIX_B -> heap.add(arrayDt, integerArray)
DataType.ARRAY_F -> heap.add(arrayDt, doubleArray)
else -> throw CompilerException("invalid array type")
else -> throw CompilerException("invalid arrayspec type")
}
return LiteralValue(arrayDt, heapId = heapId, position = arraylit.position)
}
}
override fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression {
if(arrayIndexedExpression.array.y!=null) {
if(arrayIndexedExpression.array.size()!=null) {
if(arrayIndexedExpression.arrayspec.y!=null) {
if(arrayIndexedExpression.arrayspec.size()!=null) {
// both x and y are known
// calculate the 2-dimension index i = y*columns + x
if(arrayIndexedExpression.identifier!=null) {
val x = (arrayIndexedExpression.array.x as LiteralValue).asIntegerValue!!
val y = (arrayIndexedExpression.array.y as LiteralValue).asIntegerValue!!
val x = (arrayIndexedExpression.arrayspec.x as LiteralValue).asIntegerValue!!
val y = (arrayIndexedExpression.arrayspec.y as LiteralValue).asIntegerValue!!
val variable = arrayIndexedExpression.identifier.targetStatement(namespace) as? VarDecl
if(variable!=null) {
val index = x + y * (variable.arrayspec!!.x as LiteralValue).asIntegerValue!!
arrayIndexedExpression.array = ArraySpec(LiteralValue.optimalInteger(index, arrayIndexedExpression.array.position), null, arrayIndexedExpression.array.position)
arrayIndexedExpression.arrayspec = ArraySpec(LiteralValue.optimalInteger(index, arrayIndexedExpression.arrayspec.position), null, arrayIndexedExpression.arrayspec.position)
}
}
}

View File

@ -124,16 +124,6 @@ class Program (val name: String,
val args = if(parts.size==2) parts[1] else null
val instruction = when(opcode) {
Opcode.LINE -> Instruction(opcode, null, callLabel = args)
Opcode.COPY_VAR_BYTE, Opcode.COPY_VAR_WORD, Opcode.COPY_VAR_FLOAT -> {
val (v1, v2) = args!!.split(splitpattern, limit = 2)
Instruction(opcode, null, null, v1, v2)
}
Opcode.COPY_MEM_BYTE, Opcode.COPY_MEM_WORD, Opcode.COPY_MEM_FLOAT -> {
val (v1, v2) = args!!.split(splitpattern, limit = 2)
val address1 = getArgValue(v1, heap)
val address2 = getArgValue(v2, heap)
Instruction(opcode, address1, address2)
}
Opcode.JUMP, Opcode.CALL, Opcode.BNEG, Opcode.BPOS,
Opcode.BZ, Opcode.BNZ, Opcode.BCS, Opcode.BCC -> {
if(args!!.startsWith('$')) {
@ -222,7 +212,7 @@ class Program (val name: String,
DataType.MATRIX_UB,
DataType.MATRIX_B -> {
if(!valueStr.startsWith("heap:"))
throw VmExecutionException("invalid array/matrix value, should be a heap reference")
throw VmExecutionException("invalid arrayspec/matrix value, should be a heap reference")
else {
val heapId = valueStr.substring(5).toInt()
Value(type, heapId)
@ -303,4 +293,4 @@ class Program (val name: String,
}
}
}
}
}

View File

@ -218,30 +218,24 @@ class StackVm(private var traceOutputFile: String?) {
}
}
private fun checkDt(value: Value?, expected: DataType) {
if(value==null)
throw VmExecutionException("expected value")
if(value.type!=expected)
throw VmExecutionException("expected $expected value, found ${value.type}")
}
private fun checkDt(value: Value?, expected: Set<DataType>) {
private fun checkDt(value: Value?, vararg expected: DataType) {
if(value==null)
throw VmExecutionException("expected value")
if(value.type !in expected)
throw VmExecutionException("incompatible type found ${value.type}")
throw VmExecutionException("incompatible type ${value.type}")
}
private fun dispatch(ins: Instruction) : Instruction {
traceOutput?.println("\n$ins")
when (ins.opcode) {
Opcode.NOP -> {}
Opcode.PUSH_BYTE -> {
checkDt(ins.arg, setOf(DataType.UBYTE, DataType.BYTE))
checkDt(ins.arg, DataType.UBYTE, DataType.BYTE)
evalstack.push(ins.arg)
}
Opcode.PUSH_WORD -> {
checkDt(ins.arg, setOf(DataType.UWORD, DataType.WORD) + IterableDatatypes)
checkDt(ins.arg, *(setOf(DataType.UWORD, DataType.WORD) + IterableDatatypes).toTypedArray())
evalstack.push(ins.arg)
}
Opcode.PUSH_FLOAT -> {
@ -280,29 +274,23 @@ class StackVm(private var traceOutputFile: String?) {
val value = evalstack.pop()
checkDt(value, DataType.FLOAT)
}
Opcode.POP_MEM_UB -> {
Opcode.POP_MEM_BYTE -> {
val value = evalstack.pop()
checkDt(value, DataType.UBYTE)
checkDt(value, DataType.BYTE, DataType.UBYTE)
val address = ins.arg!!.integerValue()
mem.setUByte(address, value.integerValue().toShort())
if(value.type==DataType.BYTE)
mem.setSByte(address, value.integerValue().toShort())
else
mem.setUByte(address, value.integerValue().toShort())
}
Opcode.POP_MEM_B -> {
Opcode.POP_MEM_WORD -> {
val value = evalstack.pop()
checkDt(value, DataType.BYTE)
checkDt(value, DataType.WORD, DataType.UWORD)
val address = ins.arg!!.integerValue()
mem.setSByte(address, value.integerValue().toShort())
}
Opcode.POP_MEM_UW -> {
val value = evalstack.pop()
checkDt(value, DataType.UWORD)
val address = ins.arg!!.integerValue()
mem.setUWord(address, value.integerValue())
}
Opcode.POP_MEM_W -> {
val value = evalstack.pop()
checkDt(value, DataType.WORD)
val address = ins.arg!!.integerValue()
mem.setSWord(address, value.integerValue())
if(value.type==DataType.WORD)
mem.setSWord(address, value.integerValue())
else
mem.setUWord(address, value.integerValue())
}
Opcode.POP_MEM_FLOAT -> {
val value = evalstack.pop()
@ -765,12 +753,12 @@ class StackVm(private var traceOutputFile: String?) {
}
Opcode.PUSH_VAR_BYTE -> {
val value = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
checkDt(value, setOf(DataType.UBYTE, DataType.BYTE))
checkDt(value, DataType.UBYTE, DataType.BYTE)
evalstack.push(value)
}
Opcode.PUSH_VAR_WORD -> {
val value = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
checkDt(value, setOf(DataType.UWORD, DataType.WORD, DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.MATRIX_UB))
checkDt(value, *(setOf(DataType.UWORD, DataType.WORD) + IterableDatatypes).toTypedArray())
evalstack.push(value)
}
Opcode.PUSH_VAR_FLOAT -> {
@ -780,62 +768,22 @@ class StackVm(private var traceOutputFile: String?) {
}
Opcode.POP_VAR_BYTE -> {
val value = evalstack.pop()
checkDt(value, setOf(DataType.UBYTE, DataType.BYTE))
checkDt(value, DataType.UBYTE, DataType.BYTE)
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
checkDt(variable, setOf(DataType.UBYTE, DataType.BYTE))
checkDt(variable, DataType.UBYTE, DataType.BYTE)
if(value.type!=variable.type)
throw VmExecutionException("datatype mismatch")
variables[ins.callLabel!!] = value
}
Opcode.POP_VAR_WORD -> {
val value = evalstack.pop()
checkDt(value, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
checkDt(value, DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
checkDt(variable, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
checkDt(variable, DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)
if(value.type!=variable.type)
throw VmExecutionException("datatype mismatch")
variables[ins.callLabel!!] = value
}
Opcode.COPY_VAR_BYTE -> {
val source = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
val dest = variables[ins.callLabel2] ?: throw VmExecutionException("unknown variable: ${ins.callLabel2}")
checkDt(source, setOf(DataType.UBYTE, DataType.BYTE))
checkDt(dest, setOf(DataType.UBYTE, DataType.BYTE))
if(dest.type!=source.type)
throw VmExecutionException("datatype mismatch")
variables[ins.callLabel2!!] = source
}
Opcode.COPY_VAR_WORD -> {
val source = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
val dest = variables[ins.callLabel2] ?: throw VmExecutionException("unknown variable: ${ins.callLabel2}")
checkDt(source, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
checkDt(dest, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
if(dest.type!=source.type)
throw VmExecutionException("datatype mismatch")
variables[ins.callLabel2!!] = source
}
Opcode.COPY_VAR_FLOAT -> {
val source = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
val dest = variables[ins.callLabel2] ?: throw VmExecutionException("unknown variable: ${ins.callLabel2}")
checkDt(source, DataType.FLOAT)
checkDt(dest, DataType.FLOAT)
variables[ins.callLabel2!!] = source
}
Opcode.COPY_MEM_BYTE -> {
val sourceAddr = ins.arg!!.integerValue()
val destAddr = ins.arg2!!.integerValue()
mem.setUByte(destAddr, mem.getUByte(sourceAddr))
}
Opcode.COPY_MEM_WORD -> {
val sourceAddr = ins.arg!!.integerValue()
val destAddr = ins.arg2!!.integerValue()
mem.setUWord(destAddr, mem.getUWord(sourceAddr))
}
Opcode.COPY_MEM_FLOAT -> {
val sourceAddr = ins.arg!!.integerValue()
val destAddr = ins.arg2!!.integerValue()
mem.setFloat(destAddr, mem.getFloat(sourceAddr))
}
Opcode.POP_VAR_FLOAT -> {
val value = evalstack.pop()
checkDt(value, DataType.FLOAT)
@ -1229,13 +1177,13 @@ class StackVm(private var traceOutputFile: String?) {
// assume the variable is a pointer (address) and get the ubyte value from that memory location
evalstack.push(Value(DataType.UBYTE, mem.getUByte(variable.integerValue())))
} else {
// get indexed byte element from the array
// get indexed byte element from the arrayspec
val array = heap.get(variable.heapId)
when(array.type) {
DataType.ARRAY_UB, DataType.MATRIX_UB -> evalstack.push(Value(DataType.UBYTE, array.array!![index]))
DataType.ARRAY_B, DataType.MATRIX_B -> evalstack.push(Value(DataType.BYTE, array.array!![index]))
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> evalstack.push(Value(DataType.UBYTE, Petscii.encodePetscii(array.str!![index].toString(), true)[0]))
else -> throw VmExecutionException("not a proper array/matrix/string variable with byte elements")
else -> throw VmExecutionException("not a proper arrayspec/matrix/string variable with byte elements")
}
}
}
@ -1247,12 +1195,12 @@ class StackVm(private var traceOutputFile: String?) {
// assume the variable is a pointer (address) and get the word value from that memory location
evalstack.push(Value(DataType.UWORD, mem.getUWord(variable.integerValue())))
} else {
// get indexed word element from the array
// get indexed word element from the arrayspec
val array = heap.get(variable.heapId)
when(array.type){
DataType.ARRAY_UW -> evalstack.push(Value(DataType.UWORD, array.array!![index]))
DataType.ARRAY_W -> evalstack.push(Value(DataType.WORD, array.array!![index]))
else -> throw VmExecutionException("not a proper array var with word elements")
else -> throw VmExecutionException("not a proper arrayspec var with word elements")
}
}
}
@ -1264,10 +1212,10 @@ class StackVm(private var traceOutputFile: String?) {
// assume the variable is a pointer (address) and get the float value from that memory location
evalstack.push(Value(DataType.UWORD, mem.getFloat(variable.integerValue())))
} else {
// get indexed float element from the array
// get indexed float element from the arrayspec
val array = heap.get(variable.heapId)
if(array.type!=DataType.ARRAY_F)
throw VmExecutionException("not a proper array var with float elements")
throw VmExecutionException("not a proper arrayspec var with float elements")
evalstack.push(Value(DataType.FLOAT, array.doubleArray!![index]))
}
}
@ -1275,13 +1223,13 @@ class StackVm(private var traceOutputFile: String?) {
// store byte value on the stack in variable[index] (index is on the stack as well)
val index = evalstack.pop().integerValue()
val value = evalstack.pop()
checkDt(value, setOf(DataType.UBYTE, DataType.BYTE))
checkDt(value, DataType.UBYTE, DataType.BYTE)
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
if(variable.type==DataType.UWORD) {
// assume the variable is a pointer (address) and write the byte value to that memory location
mem.setUByte(variable.integerValue(), value.integerValue().toShort())
} else {
// set indexed byte element in the array
// set indexed byte element in the arrayspec
val array = heap.get(variable.heapId)
when(array.type) {
DataType.ARRAY_UB, DataType.MATRIX_UB -> array.array!![index] = value.integerValue()
@ -1294,7 +1242,7 @@ class StackVm(private var traceOutputFile: String?) {
chars[index] = Petscii.decodePetscii(listOf(value.integerValue().toShort()), true)[0]
heap.update(variable.heapId, chars.joinToString(""))
}
else -> throw VmExecutionException("not a proper array/matrix/string var with byte elements")
else -> throw VmExecutionException("not a proper arrayspec/matrix/string var with byte elements")
}
}
}
@ -1302,18 +1250,18 @@ class StackVm(private var traceOutputFile: String?) {
// store word value on the stack in variable[index] (index is on the stack as well)
val index = evalstack.pop().integerValue()
val value = evalstack.pop()
checkDt(value, setOf(DataType.UWORD, DataType.WORD))
checkDt(value, DataType.UWORD, DataType.WORD)
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
if(variable.type==DataType.UWORD) {
// assume the variable is a pointer (address) and write the word value to that memory location
mem.setUWord(variable.integerValue(), value.integerValue())
} else {
// set indexed word element in the array
// set indexed word element in the arrayspec
val array = heap.get(variable.heapId)
when(array.type) {
DataType.ARRAY_UW -> array.array!![index] = value.integerValue()
DataType.ARRAY_W -> array.array!![index] = value.integerValue()
else -> throw VmExecutionException("not a proper array var with word elements")
else -> throw VmExecutionException("not a proper arrayspec var with word elements")
}
}
@ -1328,10 +1276,10 @@ class StackVm(private var traceOutputFile: String?) {
// assume the variable is a pointer (address) and write the float value to that memory location
mem.setFloat(variable.integerValue(), value.numericValue().toDouble())
} else {
// set indexed float element in the array
// set indexed float element in the arrayspec
val array = heap.get(variable.heapId)
if(array.type!=DataType.ARRAY_F)
throw VmExecutionException("not a proper array var with float elements")
throw VmExecutionException("not a proper arrayspec var with float elements")
array.doubleArray!![index] = value.numericValue().toDouble()
}
}
@ -1570,7 +1518,7 @@ class StackVm(private var traceOutputFile: String?) {
}
Syscall.FUNC_WRD -> {
val value = evalstack.pop()
checkDt(value, setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD))
checkDt(value, DataType.UBYTE, DataType.BYTE, DataType.UWORD)
when(value.type) {
DataType.UBYTE, DataType.BYTE -> evalstack.push(Value(DataType.WORD, value.integerValue()))
DataType.UWORD -> {
@ -1587,7 +1535,7 @@ class StackVm(private var traceOutputFile: String?) {
}
Syscall.FUNC_UWRD -> {
val value = evalstack.pop()
checkDt(value, setOf(DataType.UBYTE, DataType.BYTE, DataType.WORD))
checkDt(value, DataType.UBYTE, DataType.BYTE, DataType.WORD)
when(value.type) {
DataType.UBYTE -> evalstack.push(Value(DataType.UWORD, value.integerValue()))
DataType.UWORD -> evalstack.push(value)

View File

@ -222,10 +222,10 @@ class TestStackVmOpcodes {
Instruction(Opcode.PUSH_WORD, Value(DataType.WORD, -23456)),
Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 177)),
Instruction(Opcode.PUSH_BYTE, Value(DataType.BYTE, -55)),
Instruction(Opcode.POP_MEM_B, Value(DataType.UWORD, 0x2000)),
Instruction(Opcode.POP_MEM_UB, Value(DataType.UWORD, 0x2001)),
Instruction(Opcode.POP_MEM_W, Value(DataType.UWORD, 0x3000)),
Instruction(Opcode.POP_MEM_UW, Value(DataType.UWORD, 0x3002)),
Instruction(Opcode.POP_MEM_BYTE, Value(DataType.UWORD, 0x2000)),
Instruction(Opcode.POP_MEM_BYTE, Value(DataType.UWORD, 0x2001)),
Instruction(Opcode.POP_MEM_WORD, Value(DataType.UWORD, 0x3000)),
Instruction(Opcode.POP_MEM_WORD, Value(DataType.UWORD, 0x3002)),
Instruction(Opcode.POP_MEM_FLOAT, Value(DataType.UWORD, 0x4000)))
vm.load(makeProg(ins), null)
assertEquals(0, vm.mem.getUWord(0x2000))
@ -281,51 +281,6 @@ class TestStackVmOpcodes {
}
}
@Test
fun testCopyVar() {
val ins = mutableListOf(
Instruction(Opcode.COPY_VAR_BYTE, null, null, "bvar1", "bvar2"),
Instruction(Opcode.COPY_VAR_WORD, null, null, "wvar1", "wvar2"),
Instruction(Opcode.COPY_VAR_FLOAT, null, null, "fvar1", "fvar2"),
Instruction(Opcode.COPY_VAR_WORD, null, null, "wvar1", "bvar2"))
val vars = mapOf(
"bvar1" to Value(DataType.UBYTE, 1),
"bvar2" to Value(DataType.UBYTE, 2),
"wvar1" to Value(DataType.UWORD, 1111),
"wvar2" to Value(DataType.UWORD, 2222),
"fvar1" to Value(DataType.FLOAT, 11.11),
"fvar2" to Value(DataType.FLOAT, 22.22)
)
vm.load(makeProg(ins, vars), null)
assertEquals(12, vm.variables.size)
vm.step(3)
assertEquals(Value(DataType.UBYTE, 1), vm.variables["bvar2"])
assertEquals(Value(DataType.UWORD, 1111), vm.variables["wvar2"])
assertEquals(Value(DataType.FLOAT, 11.11), vm.variables["fvar2"])
assertFailsWith<VmExecutionException> {
vm.step(1)
}
}
@Test
fun testCopyMem() {
val ins = mutableListOf(
Instruction(Opcode.COPY_MEM_BYTE, Value(DataType.UWORD, 0xc000), Value(DataType.UWORD, 0xc001)),
Instruction(Opcode.COPY_MEM_WORD, Value(DataType.UWORD, 0xc100), Value(DataType.UWORD, 0xc102)),
Instruction(Opcode.COPY_MEM_FLOAT, Value(DataType.UWORD, 0xc200), Value(DataType.UWORD, 0xc300)))
val mem=mapOf(0xc000 to listOf(Value(DataType.UBYTE, 0x45)),
0xc100 to listOf(Value(DataType.UWORD, 0xc2ca)),
0xc200 to listOf(Value(DataType.FLOAT, 42.25)))
vm.load(makeProg(ins, mem=mem), null)
assertEquals(0, vm.mem.getUByte(0xc001))
assertEquals(0, vm.mem.getUWord(0xc102))
assertEquals(0.0, vm.mem.getFloat(0xc300))
vm.step(3)
assertEquals(0x45, vm.mem.getUByte(0xc001))
assertEquals(0xc2ca, vm.mem.getUWord(0xc102))
assertEquals(42.25, vm.mem.getFloat(0xc300))
}
@Test
fun testAdd() {
testBinaryOperator(Value(DataType.UBYTE, 140), Opcode.ADD_UB, Value(DataType.UBYTE, 222), Value(DataType.UBYTE, 106))

View File

@ -200,17 +200,19 @@ Arrays can be made of bytes, words and floats. Matrixes can oly be made of bytes
byte[4] array = [1, 2, 3, 4] ; initialize the array
byte[99] array = 255 ; initialize array with all 255's [255, 255, 255, 255, ...]
byte[100] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
byte[2,3] matrix = 1 ; a matrix of 2*3=6 bytes all with value 1
byte[2,3] matrix = [1,2,3,4,5,6] ; a 2*3 matrix with value |(1,2) (3,4) (5,6)|
ubyte[2,3] matrix = 255 ; a matrix of 2*3=6 unsigned bytes all with value 255
value = array[3] ; the fourth value in the array (index is 0-based)
value = matrix[4,2] ; the byte at the 5th column and 3rd row in the matrix
char = string[4] ; the fifth character (=byte) in the string
.. note::
Right now, the array and matrix size should be small enough to be indexable by a single byte index.
This means byte arrays and matrixes should be <= 256 elements, word arrays <= 128 elements, and float
arrays <= 51 elements. This limit may be lifted in a future version.
Right now, the array should be small enough to be indexable by a single byte index.
This means byte arrays should be <= 256 elements, word arrays <= 128 elements, and float
arrays <= 51 elements. This limit may or may not be lifted in a future version.
Matrixes can be indexed in each dimension only by a byte as well, this also means
their maximum size is 65536 elements (bytes).
Note that the various keywords for the data type and variable type (``byte``, ``word``, ``const``, etc.)

View File

@ -34,9 +34,15 @@ ror2_word
ub2float
rts
b2float
rts
uw2float
rts
w2float
rts
push_float
rts