mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
allowing vardecl with non-const initial value assignment
This commit is contained in:
parent
2e4b5768b0
commit
42e4891fcf
@ -53,38 +53,22 @@
|
||||
; rotate around origin (0,0,0)
|
||||
|
||||
; set up the 3d rotation matrix values
|
||||
float cosa
|
||||
float sina
|
||||
float cosb
|
||||
float sinb
|
||||
float cosc
|
||||
float sinc
|
||||
float Axx
|
||||
float Axy
|
||||
float Axz
|
||||
float Ayx
|
||||
float Ayy
|
||||
float Ayz
|
||||
float Azx
|
||||
float Azy
|
||||
float Azz
|
||||
float cosa = cos(t)
|
||||
float sina = sin(t)
|
||||
float cosb = cos(t*0.33)
|
||||
float sinb = sin(t*0.33)
|
||||
float cosc = cos(t*0.78)
|
||||
float sinc = sin(t*0.78)
|
||||
|
||||
cosa = cos(t)
|
||||
sina = sin(t)
|
||||
cosb = cos(t*0.33)
|
||||
sinb = sin(t*0.33)
|
||||
cosc = cos(t*0.78)
|
||||
sinc = sin(t*0.78)
|
||||
|
||||
Axx = cosa*cosb
|
||||
Axy = cosa*sinb*sinc - sina*cosc
|
||||
Axz = cosa*sinb*cosc + sina*sinc
|
||||
Ayx = sina*cosb
|
||||
Ayy = sina*sinb*sinc + cosa*cosc
|
||||
Ayz = sina*sinb*cosc - cosa*sinc
|
||||
Azx = -sinb
|
||||
Azy = cosb*sinc
|
||||
Azz = cosb*cosc
|
||||
float Axx = cosa*cosb
|
||||
float Axy = cosa*sinb*sinc - sina*cosc
|
||||
float Axz = cosa*sinb*cosc + sina*sinc
|
||||
float Ayx = sina*cosb
|
||||
float Ayy = sina*sinb*sinc + cosa*cosc
|
||||
float Ayz = sina*sinb*cosc - cosa*sinc
|
||||
float Azx = -sinb
|
||||
float Azy = cosb*sinc
|
||||
float Azz = cosb*cosc
|
||||
|
||||
for i in 0 to len(xcoor)-1 {
|
||||
rotatedx[i] = Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i]
|
||||
|
@ -5,7 +5,6 @@
|
||||
str name = " "
|
||||
str guess = "000000"
|
||||
byte guessednumber
|
||||
byte secretnumber
|
||||
byte attempts_left
|
||||
|
||||
_vm_write_str("Let's play a number guessing game!\n")
|
||||
@ -15,7 +14,7 @@
|
||||
_vm_write_str(name)
|
||||
_vm_write_str(".\nI am thinking of a number from 1 to 100! You'll have to guess it!\n")
|
||||
|
||||
secretnumber = rnd() % 100
|
||||
byte secretnumber = rnd() % 100
|
||||
|
||||
for attempts_left in 10 to 1 step -1 {
|
||||
_vm_write_str("\nYou have ")
|
||||
|
@ -4,73 +4,24 @@
|
||||
|
||||
~ main {
|
||||
|
||||
X=0
|
||||
|
||||
sub sub1() -> byte {
|
||||
return 11
|
||||
}
|
||||
word mainvar = 44*Y
|
||||
|
||||
return
|
||||
|
||||
sub sub2() -> byte {
|
||||
return 22
|
||||
}
|
||||
|
||||
A=55
|
||||
|
||||
sub sub3() -> byte {
|
||||
return 33
|
||||
}
|
||||
|
||||
X=34
|
||||
|
||||
sub sub4() -> byte {
|
||||
return 44
|
||||
}
|
||||
|
||||
A=sub1()
|
||||
A=sub2()
|
||||
A=sub3()
|
||||
A=sub4()
|
||||
|
||||
sub start() {
|
||||
|
||||
byte bvar = 128
|
||||
word wvar = 128
|
||||
float fvar = 128
|
||||
A=99
|
||||
|
||||
bvar = 1
|
||||
bvar = 2.0
|
||||
bvar = 2.w
|
||||
bvar = 255.w
|
||||
wvar = 1
|
||||
wvar = 2.w
|
||||
wvar = 2.0
|
||||
wvar = bvar
|
||||
fvar = 1
|
||||
fvar = 2.w
|
||||
fvar = 22.33
|
||||
fvar = bvar
|
||||
fvar = wvar
|
||||
byte bvar = 4*X
|
||||
word wvar = 44*XY
|
||||
float fvar = 128.34+XY
|
||||
|
||||
A=100
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
A=sub1()
|
||||
A=sub2()
|
||||
A=sub3()
|
||||
A=sub4()
|
||||
sub4()
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
~ test {
|
||||
|
||||
sub test() -> byte {
|
||||
return 44
|
||||
}
|
||||
sub test2() -> byte {
|
||||
return 43
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ fun main(args: Array<String>) {
|
||||
val heap = HeapValues()
|
||||
moduleAst.checkIdentifiers()
|
||||
moduleAst.constantFold(namespace, heap)
|
||||
StatementReorderer().process(moduleAst) // reorder statements to please the compiler later
|
||||
StatementReorderer(namespace, heap).process(moduleAst) // reorder statements to please the compiler later
|
||||
moduleAst.checkValid(namespace, compilerOptions, heap) // check if tree is valid
|
||||
|
||||
// optimize the parse tree
|
||||
|
@ -585,7 +585,7 @@ enum class VarDeclType {
|
||||
}
|
||||
|
||||
class VarDecl(val type: VarDeclType,
|
||||
declaredDatatype: DataType,
|
||||
private val declaredDatatype: DataType,
|
||||
val arrayspec: ArraySpec?,
|
||||
val name: String,
|
||||
var value: IExpression?,
|
||||
@ -623,40 +623,20 @@ class VarDecl(val type: VarDeclType,
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
|
||||
val scopedname: String by lazy { makeScopedName(name).joinToString(".") }
|
||||
val memorySize: Int
|
||||
get() = when(datatype) {
|
||||
DataType.BYTE -> 1
|
||||
DataType.WORD -> 2
|
||||
DataType.FLOAT -> Mflpt5.MemorySize
|
||||
DataType.STR,
|
||||
DataType.STR_P,
|
||||
DataType.STR_S,
|
||||
DataType.STR_PS -> {
|
||||
val lv = value as? LiteralValue ?: throw ExpressionError("need constant initializer value expression", position)
|
||||
lv.strvalue!!.length + 1
|
||||
}
|
||||
DataType.ARRAY -> {
|
||||
val aX = arrayspec?.x as? LiteralValue ?: throw ExpressionError("need constant value expression for arrayspec", position)
|
||||
aX.asIntegerValue!!
|
||||
}
|
||||
DataType.ARRAY_W -> {
|
||||
val aX = arrayspec?.x as? LiteralValue ?: throw ExpressionError("need constant value expression for arrayspec", position)
|
||||
2*aX.asIntegerValue!!
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
val aX = arrayspec?.x as? LiteralValue ?: throw ExpressionError("need constant value expression for arrayspec", position)
|
||||
Mflpt5.MemorySize*aX.asIntegerValue!!
|
||||
}
|
||||
DataType.MATRIX -> {
|
||||
val aX = arrayspec?.x as? LiteralValue ?: throw ExpressionError("need constant value expression for arrayspec", position)
|
||||
val aY = arrayspec.y as? LiteralValue ?: throw ExpressionError("need constant value expression for arrayspec", position)
|
||||
aX.asIntegerValue!! * aY.asIntegerValue!!
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, value=$value, pos=$position)"
|
||||
}
|
||||
|
||||
fun asDefaultValueDecl(): VarDecl {
|
||||
val constValue = when(declaredDatatype) {
|
||||
DataType.BYTE -> LiteralValue(DataType.BYTE, 0, position=position)
|
||||
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue=0, position=position)
|
||||
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue=0.0, position=position)
|
||||
else -> throw FatalAstException("can only set a default value for a numeric type")
|
||||
}
|
||||
return VarDecl(type, declaredDatatype, arrayspec, name, constValue, position)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
package prog8.ast
|
||||
|
||||
class StatementReorderer: IAstProcessor {
|
||||
import prog8.compiler.HeapValues
|
||||
|
||||
class StatementReorderer(private val namespace: INameScope, private val heap: HeapValues): IAstProcessor {
|
||||
// Reorders the statements in a way the compiler needs.
|
||||
// - 'main' block must be the very first statement.
|
||||
// - in every scope:
|
||||
@ -12,8 +14,10 @@ class StatementReorderer: IAstProcessor {
|
||||
// - all other subroutines will be moved to the end of their block.
|
||||
|
||||
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%address", "%option")
|
||||
private val vardeclsToAdd = mutableMapOf<INameScope, MutableList<VarDecl>>()
|
||||
|
||||
override fun process(module: Module) {
|
||||
super.process(module)
|
||||
val mainBlock = module.statements.single { it is Block && it.name=="main" }
|
||||
module.statements.remove(mainBlock)
|
||||
module.statements.add(0, mainBlock)
|
||||
@ -23,7 +27,13 @@ class StatementReorderer: IAstProcessor {
|
||||
val directives = module.statements.filter {it is Directive && it.directive in directivesToMove}
|
||||
module.statements.removeAll(directives)
|
||||
module.statements.addAll(0, directives)
|
||||
super.process(module)
|
||||
|
||||
// add any new vardecls
|
||||
for(decl in vardeclsToAdd)
|
||||
for(d in decl.value) {
|
||||
d.linkParents(decl.key as Node)
|
||||
decl.key.statements.add(0, d)
|
||||
}
|
||||
}
|
||||
|
||||
override fun process(block: Block): IStatement {
|
||||
@ -48,7 +58,7 @@ class StatementReorderer: IAstProcessor {
|
||||
|
||||
// make sure there is a 'return' in front of the first subroutine
|
||||
// (if it isn't the first statement in the block itself, and isn't the program's entrypoint)
|
||||
if(block.statements.size > numSubroutinesAtEnd) {
|
||||
if(numSubroutinesAtEnd>0 && block.statements.size > (numSubroutinesAtEnd+1)) {
|
||||
val firstSub = block.statements[block.statements.size - numSubroutinesAtEnd] as Subroutine
|
||||
if(firstSub.name != "start" && block.name != "main") {
|
||||
val stmtBeforeFirstSub = block.statements[block.statements.size - numSubroutinesAtEnd - 1]
|
||||
@ -73,12 +83,34 @@ class StatementReorderer: IAstProcessor {
|
||||
}
|
||||
|
||||
override fun process(subroutine: Subroutine): IStatement {
|
||||
super.process(subroutine)
|
||||
val varDecls = subroutine.statements.filter { it is VarDecl }
|
||||
subroutine.statements.removeAll(varDecls)
|
||||
subroutine.statements.addAll(0, varDecls)
|
||||
val directives = subroutine.statements.filter {it is Directive && it.directive in directivesToMove}
|
||||
subroutine.statements.removeAll(directives)
|
||||
subroutine.statements.addAll(0, directives)
|
||||
return super.process(subroutine)
|
||||
return subroutine
|
||||
}
|
||||
|
||||
override fun process(decl: VarDecl): IStatement {
|
||||
super.process(decl)
|
||||
if(decl.type==VarDeclType.VAR || decl.type==VarDeclType.CONST) {
|
||||
if(decl.value!=null && decl.value?.constValue(namespace, heap)==null) {
|
||||
// the value assigned to the variable isn't a constant.
|
||||
// replace the var decl with an assignment and add a new vardecl with the default constant value.
|
||||
val scope = decl.definingScope()
|
||||
if(scope !in vardeclsToAdd)
|
||||
vardeclsToAdd[scope] = mutableListOf()
|
||||
vardeclsToAdd[scope]!!.add(decl.asDefaultValueDecl())
|
||||
return Assignment(
|
||||
AssignTarget(null, IdentifierReference(decl.scopedname.split("."), decl.position), null, decl.position),
|
||||
null,
|
||||
decl.value!!,
|
||||
decl.position
|
||||
)
|
||||
}
|
||||
}
|
||||
return decl
|
||||
}
|
||||
}
|
||||
|
@ -674,7 +674,7 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
}
|
||||
Opcode.PUSH_VAR_W -> {
|
||||
val value = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
|
||||
checkDt(value, DataType.WORD)
|
||||
checkDt(value, setOf(DataType.WORD, DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.MATRIX))
|
||||
evalstack.push(value)
|
||||
}
|
||||
Opcode.PUSH_VAR_F -> {
|
||||
@ -691,7 +691,7 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
}
|
||||
Opcode.POP_VAR_W -> {
|
||||
val value = evalstack.pop()
|
||||
checkDt(value, DataType.WORD)
|
||||
checkDt(value, setOf(DataType.WORD, DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.MATRIX))
|
||||
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
|
||||
checkDt(variable, DataType.WORD)
|
||||
variables[ins.callLabel!!] = value
|
||||
|
@ -247,383 +247,166 @@ class TestStackVmOpcodes {
|
||||
|
||||
@Test
|
||||
fun testAdd() {
|
||||
val values = listOf(
|
||||
Value(DataType.FLOAT, 42.25),
|
||||
Value(DataType.WORD, 4000),
|
||||
Value(DataType.BYTE, 40))
|
||||
val expected = listOf(
|
||||
Value(DataType.WORD, 4000+40),
|
||||
Value(DataType.FLOAT, 42.25+(4000+40)))
|
||||
val operator = Opcode.ADD
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
testBinaryOperator(Value(DataType.WORD, 4000), Opcode.ADD, Value(DataType.BYTE, 40), Value(DataType.WORD, 4000+40))
|
||||
testBinaryOperator(Value(DataType.WORD, 4000+40), Opcode.ADD, Value(DataType.WORD, 123), Value(DataType.WORD, 4000+40+123))
|
||||
assertFailsWith<VmExecutionException> {
|
||||
testBinaryOperator(Value(DataType.WORD, 4000 + 40), Opcode.ADD, Value(DataType.FLOAT, 42.25), Value(DataType.FLOAT, 42.25 + (4000 + 40)))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSub() {
|
||||
val values = listOf(
|
||||
Value(DataType.FLOAT, 42.25),
|
||||
Value(DataType.WORD, 4000),
|
||||
Value(DataType.BYTE, 40))
|
||||
val expected = listOf(
|
||||
Value(DataType.WORD, 4000-40),
|
||||
Value(DataType.FLOAT, 42.25-(4000-40)))
|
||||
val operator = Opcode.SUB
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
testBinaryOperator(Value(DataType.WORD, 4000), Opcode.SUB, Value(DataType.BYTE, 40), Value(DataType.WORD, 4000-40))
|
||||
testBinaryOperator(Value(DataType.WORD, 4000), Opcode.SUB, Value(DataType.WORD, 123), Value(DataType.WORD, 4000-123))
|
||||
assertFailsWith<VmExecutionException> {
|
||||
testBinaryOperator(Value(DataType.WORD, 4000 - 40), Opcode.SUB, Value(DataType.FLOAT, 42.25), Value(DataType.FLOAT, 42.25 - (4000 - 40)))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMul() {
|
||||
val values = listOf(
|
||||
Value(DataType.FLOAT, 42.2533),
|
||||
Value(DataType.WORD, 401),
|
||||
Value(DataType.BYTE, 4))
|
||||
val expected = listOf(
|
||||
Value(DataType.WORD, 401*4),
|
||||
Value(DataType.FLOAT, 42.2533*(401*4)))
|
||||
val operator = Opcode.MUL
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
testBinaryOperator(Value(DataType.WORD, 401), Opcode.MUL, Value(DataType.BYTE, 4), Value(DataType.WORD, 401*4))
|
||||
testBinaryOperator(Value(DataType.WORD, 401), Opcode.MUL, Value(DataType.WORD, 4), Value(DataType.WORD, 401*4))
|
||||
assertFailsWith<VmExecutionException> {
|
||||
testBinaryOperator(Value(DataType.WORD, 401 * 4), Opcode.MUL, Value(DataType.FLOAT, 42.2533), Value(DataType.FLOAT, 42.2533 * (401 * 4)))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDiv() {
|
||||
val values = listOf(
|
||||
Value(DataType.FLOAT, 42.25),
|
||||
Value(DataType.WORD, 3999),
|
||||
Value(DataType.BYTE, 40)
|
||||
)
|
||||
val expected = listOf(
|
||||
Value(DataType.WORD, 99),
|
||||
Value(DataType.FLOAT, 42.25/99))
|
||||
val operator = Opcode.DIV
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
testBinaryOperator(Value(DataType.WORD, 3999), Opcode.DIV, Value(DataType.BYTE, 40), Value(DataType.WORD, 99))
|
||||
testBinaryOperator(Value(DataType.WORD, 3999), Opcode.DIV, Value(DataType.WORD, 40), Value(DataType.WORD, 99))
|
||||
testBinaryOperator(Value(DataType.FLOAT, 42.25), Opcode.DIV, Value(DataType.WORD, 99), Value(DataType.FLOAT, 42.25/99))
|
||||
assertFailsWith<VmExecutionException> {
|
||||
testBinaryOperator(Value(DataType.WORD, 3333), Opcode.DIV, Value(DataType.FLOAT, 2.22), Value(DataType.FLOAT, 3333 / 2.22))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFloorDiv() {
|
||||
val values = listOf(
|
||||
Value(DataType.FLOAT, 4000.25),
|
||||
Value(DataType.WORD, 3999),
|
||||
Value(DataType.BYTE, 40)
|
||||
)
|
||||
val expected = listOf(
|
||||
Value(DataType.WORD, 99),
|
||||
Value(DataType.FLOAT, 40.0))
|
||||
val operator = Opcode.FLOORDIV
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
testBinaryOperator(Value(DataType.WORD, 3999), Opcode.FLOORDIV, Value(DataType.BYTE, 99), Value(DataType.WORD, 40))
|
||||
testBinaryOperator(Value(DataType.WORD, 3999), Opcode.FLOORDIV, Value(DataType.WORD, 99), Value(DataType.WORD, 40))
|
||||
testBinaryOperator(Value(DataType.FLOAT, 4000.25), Opcode.FLOORDIV, Value(DataType.BYTE, 40), Value(DataType.FLOAT, 100.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPow() {
|
||||
val values = listOf(
|
||||
Value(DataType.FLOAT, 1.1),
|
||||
Value(DataType.WORD, 3),
|
||||
Value(DataType.BYTE, 4)
|
||||
)
|
||||
val expected = listOf(
|
||||
Value(DataType.WORD, 81),
|
||||
Value(DataType.FLOAT, 2253.2402360440274))
|
||||
val operator = Opcode.POW
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
testBinaryOperator(Value(DataType.WORD, 3), Opcode.POW, Value(DataType.BYTE, 4), Value(DataType.WORD, 81))
|
||||
testBinaryOperator(Value(DataType.WORD, 3), Opcode.POW, Value(DataType.WORD, 4), Value(DataType.WORD, 81))
|
||||
testBinaryOperator(Value(DataType.FLOAT, 1.1), Opcode.POW, Value(DataType.BYTE, 81), Value(DataType.FLOAT, 2253.2402360440274))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemainder() {
|
||||
val values = listOf(
|
||||
Value(DataType.FLOAT, 2022.5),
|
||||
Value(DataType.WORD, 500),
|
||||
Value(DataType.BYTE, 29)
|
||||
)
|
||||
val expected = listOf(
|
||||
Value(DataType.BYTE, 7),
|
||||
Value(DataType.FLOAT, 6.5))
|
||||
val operator = Opcode.REMAINDER
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
testBinaryOperator(Value(DataType.WORD, 500), Opcode.REMAINDER, Value(DataType.BYTE, 29), Value(DataType.BYTE, 7))
|
||||
testBinaryOperator(Value(DataType.WORD, 500), Opcode.REMAINDER, Value(DataType.WORD, 29), Value(DataType.BYTE, 7))
|
||||
testBinaryOperator(Value(DataType.FLOAT, 2022.5), Opcode.REMAINDER, Value(DataType.BYTE, 7), Value(DataType.FLOAT, 6.5))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBitand() {
|
||||
val values = listOf(
|
||||
Value(DataType.WORD, 0b0011001011110001),
|
||||
Value(DataType.BYTE, 0b10011111),
|
||||
Value(DataType.BYTE, 0b11111101))
|
||||
val expected = listOf(
|
||||
Value(DataType.BYTE, 0b10011101),
|
||||
Value(DataType.WORD, 0b0000000010010001))
|
||||
val operator = Opcode.BITAND
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
testBinaryOperator(Value(DataType.BYTE, 0b10011111), Opcode.BITAND, Value(DataType.BYTE, 0b11111101), Value(DataType.BYTE, 0b10011101))
|
||||
testBinaryOperator(Value(DataType.WORD, 0b0011001011110001), Opcode.BITAND, Value(DataType.BYTE, 0b10011101), Value(DataType.WORD, 0b0000000010010001))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBitor() {
|
||||
val values = listOf(
|
||||
Value(DataType.WORD, 0b0011001011100000),
|
||||
Value(DataType.BYTE, 0b00011101),
|
||||
Value(DataType.BYTE, 0b10010001))
|
||||
val expected = listOf(
|
||||
Value(DataType.BYTE, 0b10011101),
|
||||
Value(DataType.WORD, 0b0011001011111101))
|
||||
val operator = Opcode.BITOR
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
testBinaryOperator(Value(DataType.BYTE, 0b00011101), Opcode.BITOR, Value(DataType.BYTE, 0b10010001), Value(DataType.BYTE, 0b10011101))
|
||||
testBinaryOperator(Value(DataType.WORD, 0b0011001011100000), Opcode.BITOR, Value(DataType.BYTE, 0b10011101), Value(DataType.WORD, 0b0011001011111101))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBitxor() {
|
||||
val values = listOf(
|
||||
Value(DataType.WORD, 0b0011001011100000),
|
||||
Value(DataType.BYTE, 0b00011101),
|
||||
Value(DataType.BYTE, 0b10010001))
|
||||
val expected = listOf(
|
||||
Value(DataType.BYTE, 0b10001100),
|
||||
Value(DataType.WORD, 0b0011001001101100))
|
||||
val operator = Opcode.BITXOR
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
testBinaryOperator(Value(DataType.BYTE, 0b00011101), Opcode.BITXOR, Value(DataType.BYTE, 0b10010001), Value(DataType.BYTE, 0b10001100))
|
||||
testBinaryOperator(Value(DataType.WORD, 0b0011001011100000), Opcode.BITXOR, Value(DataType.BYTE, 0b10001100), Value(DataType.WORD, 0b0011001001101100))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAnd() {
|
||||
val values = listOf(
|
||||
Value(DataType.ARRAY, 111),
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.WORD, 0),
|
||||
Value(DataType.STR, 222),
|
||||
Value(DataType.STR, 333),
|
||||
Value(DataType.ARRAY, 444),
|
||||
Value(DataType.FLOAT, 300.33),
|
||||
Value(DataType.WORD, 5000),
|
||||
Value(DataType.BYTE, 200),
|
||||
Value(DataType.BYTE, 1))
|
||||
val expected = listOf(
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 0))
|
||||
val operator = Opcode.AND
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
testBinaryOperator(Value(DataType.BYTE, 200), Opcode.AND, Value(DataType.BYTE, 1), Value(DataType.BYTE, 1))
|
||||
testBinaryOperator(Value(DataType.BYTE, 200), Opcode.AND, Value(DataType.BYTE, 0), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.BYTE, 0), Opcode.AND, Value(DataType.BYTE, 101), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.WORD, 200), Opcode.AND, Value(DataType.WORD, 13455), Value(DataType.BYTE, 1))
|
||||
testBinaryOperator(Value(DataType.WORD, 200), Opcode.AND, Value(DataType.WORD, 0), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.WORD, 0), Opcode.AND, Value(DataType.WORD, 101), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.FLOAT, 200.22), Opcode.AND, Value(DataType.FLOAT, 13455.55), Value(DataType.BYTE, 1))
|
||||
testBinaryOperator(Value(DataType.FLOAT, 200.22), Opcode.AND, Value(DataType.FLOAT, 0.0), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.FLOAT, 0.0), Opcode.AND, Value(DataType.FLOAT, 101.11), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.STR, 222), Opcode.AND, Value(DataType.STR, 333), Value(DataType.BYTE, 1))
|
||||
testBinaryOperator(Value(DataType.ARRAY, 444), Opcode.AND, Value(DataType.ARRAY, 444), Value(DataType.BYTE, 1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOr() {
|
||||
val values = listOf(
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.WORD, 0),
|
||||
Value(DataType.STR, 222),
|
||||
Value(DataType.STR, 333),
|
||||
Value(DataType.ARRAY, 444),
|
||||
Value(DataType.FLOAT, 0),
|
||||
Value(DataType.WORD, 1),
|
||||
Value(DataType.WORD, 0),
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 0))
|
||||
val expected = listOf(
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 1))
|
||||
val operator = Opcode.OR
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
testBinaryOperator(Value(DataType.BYTE, 200), Opcode.OR, Value(DataType.BYTE, 1), Value(DataType.BYTE, 1))
|
||||
testBinaryOperator(Value(DataType.BYTE, 200), Opcode.OR, Value(DataType.BYTE, 0), Value(DataType.BYTE, 1))
|
||||
testBinaryOperator(Value(DataType.BYTE, 0), Opcode.OR, Value(DataType.BYTE, 0), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.WORD, 200), Opcode.OR, Value(DataType.WORD, 13455), Value(DataType.BYTE, 1))
|
||||
testBinaryOperator(Value(DataType.WORD, 200), Opcode.OR, Value(DataType.WORD, 0), Value(DataType.BYTE, 1))
|
||||
testBinaryOperator(Value(DataType.WORD, 0), Opcode.OR, Value(DataType.WORD, 0), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.FLOAT, 200.22), Opcode.OR, Value(DataType.FLOAT, 13455.55), Value(DataType.BYTE, 1))
|
||||
testBinaryOperator(Value(DataType.FLOAT, 200.22), Opcode.OR, Value(DataType.FLOAT, 0.0), Value(DataType.BYTE, 1))
|
||||
testBinaryOperator(Value(DataType.FLOAT, 0.0), Opcode.OR, Value(DataType.FLOAT, 0.0), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.STR, 222), Opcode.OR, Value(DataType.STR, 333), Value(DataType.BYTE, 1))
|
||||
testBinaryOperator(Value(DataType.ARRAY, 444), Opcode.OR, Value(DataType.ARRAY, 444), Value(DataType.BYTE, 1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testXor() {
|
||||
val values = listOf(
|
||||
Value(DataType.ARRAY, 111),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.WORD, 0),
|
||||
Value(DataType.STR, 222),
|
||||
Value(DataType.STR, 333),
|
||||
Value(DataType.ARRAY, 444),
|
||||
Value(DataType.FLOAT, 300.33),
|
||||
Value(DataType.WORD, 5000),
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 20))
|
||||
val expected = listOf(
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 0))
|
||||
val operator = Opcode.XOR
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
testBinaryOperator(Value(DataType.BYTE, 200), Opcode.XOR, Value(DataType.BYTE, 1), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.BYTE, 200), Opcode.XOR, Value(DataType.BYTE, 0), Value(DataType.BYTE, 1))
|
||||
testBinaryOperator(Value(DataType.BYTE, 0), Opcode.XOR, Value(DataType.BYTE, 0), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.WORD, 200), Opcode.XOR, Value(DataType.WORD, 13455), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.WORD, 200), Opcode.XOR, Value(DataType.WORD, 0), Value(DataType.BYTE, 1))
|
||||
testBinaryOperator(Value(DataType.WORD, 0), Opcode.XOR, Value(DataType.WORD, 0), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.FLOAT, 200.22), Opcode.XOR, Value(DataType.FLOAT, 13455.55), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.FLOAT, 200.22), Opcode.XOR, Value(DataType.FLOAT, 0.0), Value(DataType.BYTE, 1))
|
||||
testBinaryOperator(Value(DataType.FLOAT, 0.0), Opcode.XOR, Value(DataType.FLOAT, 0.0), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.STR, 222), Opcode.XOR, Value(DataType.STR, 333), Value(DataType.BYTE, 0))
|
||||
testBinaryOperator(Value(DataType.ARRAY, 444), Opcode.XOR, Value(DataType.ARRAY, 444), Value(DataType.BYTE, 0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNot() {
|
||||
val values = listOf(
|
||||
Value(DataType.STR, 111),
|
||||
Value(DataType.STR, 222),
|
||||
Value(DataType.FLOAT, 0.0),
|
||||
Value(DataType.FLOAT, 300.33),
|
||||
Value(DataType.WORD, 0),
|
||||
Value(DataType.WORD, 5000),
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 20))
|
||||
val expected = listOf(
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 1),
|
||||
Value(DataType.BYTE, 0)
|
||||
)
|
||||
val operator = Opcode.NOT
|
||||
|
||||
testUnaryOperator(values, operator, expected, listOf(DataType.BYTE, DataType.BYTE,DataType.BYTE,DataType.BYTE,DataType.BYTE,DataType.BYTE,DataType.BYTE,DataType.BYTE))
|
||||
testUnaryOperator(Value(DataType.BYTE, 0), Opcode.NOT, Value(DataType.BYTE, 1))
|
||||
testUnaryOperator(Value(DataType.BYTE, 20), Opcode.NOT, Value(DataType.BYTE, 0))
|
||||
testUnaryOperator(Value(DataType.WORD, 0), Opcode.NOT, Value(DataType.BYTE, 1))
|
||||
testUnaryOperator(Value(DataType.WORD, 5000), Opcode.NOT, Value(DataType.BYTE, 0))
|
||||
testUnaryOperator(Value(DataType.FLOAT, 0.0), Opcode.NOT, Value(DataType.BYTE, 1))
|
||||
testUnaryOperator(Value(DataType.FLOAT, 5000.0), Opcode.NOT, Value(DataType.BYTE, 0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInc() {
|
||||
val values = listOf(
|
||||
Value(DataType.BYTE, 255),
|
||||
Value(DataType.BYTE, 99)
|
||||
)
|
||||
val expected = listOf(
|
||||
Value(DataType.BYTE, 0),
|
||||
Value(DataType.BYTE, 100)
|
||||
)
|
||||
testUnaryOperator(values, Opcode.INC, expected)
|
||||
|
||||
val valuesw = listOf(
|
||||
Value(DataType.WORD, 65535),
|
||||
Value(DataType.WORD, 999)
|
||||
)
|
||||
val expectedw = listOf(
|
||||
Value(DataType.WORD, 0),
|
||||
Value(DataType.WORD, 1000)
|
||||
)
|
||||
testUnaryOperator(valuesw, Opcode.INC_W, expectedw)
|
||||
|
||||
val valuesf = listOf(
|
||||
Value(DataType.FLOAT, -1.0),
|
||||
Value(DataType.FLOAT, 2022.5)
|
||||
)
|
||||
val expectedf = listOf(
|
||||
Value(DataType.FLOAT, 0.0),
|
||||
Value(DataType.FLOAT, 2023.5)
|
||||
)
|
||||
testUnaryOperator(valuesf, Opcode.INC_F, expectedf)
|
||||
testUnaryOperator(Value(DataType.BYTE, 255), Opcode.INC, Value(DataType.BYTE, 0))
|
||||
testUnaryOperator(Value(DataType.BYTE, 99), Opcode.INC, Value(DataType.BYTE, 100))
|
||||
testUnaryOperator(Value(DataType.WORD, 65535), Opcode.INC_W, Value(DataType.WORD, 0))
|
||||
testUnaryOperator(Value(DataType.WORD, 999), Opcode.INC_W, Value(DataType.WORD, 1000))
|
||||
testUnaryOperator(Value(DataType.FLOAT, -1.0), Opcode.INC_F, Value(DataType.FLOAT, 0.0))
|
||||
testUnaryOperator(Value(DataType.FLOAT, 2022.5), Opcode.INC_F, Value(DataType.FLOAT, 2023.5))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDec() {
|
||||
val values = listOf(
|
||||
Value(DataType.BYTE, 100),
|
||||
Value(DataType.BYTE, 0)
|
||||
)
|
||||
val expected = listOf(
|
||||
Value(DataType.BYTE, 99),
|
||||
Value(DataType.BYTE, 255)
|
||||
)
|
||||
testUnaryOperator(values, Opcode.DEC, expected)
|
||||
|
||||
val valuesw = listOf(
|
||||
Value(DataType.WORD, 1000),
|
||||
Value(DataType.WORD, 0)
|
||||
)
|
||||
val expectedw = listOf(
|
||||
Value(DataType.WORD, 999),
|
||||
Value(DataType.WORD, 65535)
|
||||
)
|
||||
testUnaryOperator(valuesw, Opcode.DEC_W, expectedw)
|
||||
|
||||
val valuesf = listOf(
|
||||
Value(DataType.FLOAT, 0.5),
|
||||
Value(DataType.FLOAT, 123.456)
|
||||
)
|
||||
val expectedf = listOf(
|
||||
Value(DataType.FLOAT, -0.5),
|
||||
Value(DataType.FLOAT, 122.456)
|
||||
)
|
||||
testUnaryOperator(valuesf, Opcode.DEC_F, expectedf)
|
||||
testUnaryOperator(Value(DataType.BYTE, 100), Opcode.DEC, Value(DataType.BYTE, 99))
|
||||
testUnaryOperator(Value(DataType.BYTE, 0), Opcode.DEC, Value(DataType.BYTE, 255))
|
||||
testUnaryOperator(Value(DataType.WORD, 1000), Opcode.DEC_W, Value(DataType.WORD, 999))
|
||||
testUnaryOperator(Value(DataType.WORD, 0), Opcode.DEC_W, Value(DataType.WORD, 65535))
|
||||
testUnaryOperator(Value(DataType.FLOAT, 0.5), Opcode.DEC_F, Value(DataType.FLOAT, -0.5))
|
||||
testUnaryOperator(Value(DataType.FLOAT, 2022.5), Opcode.DEC_F, Value(DataType.FLOAT, 2021.5))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNeg() {
|
||||
val ins = mutableListOf(
|
||||
Instruction(Opcode.PUSH_F, Value(DataType.FLOAT, 123.456)),
|
||||
Instruction(Opcode.NEG),
|
||||
Instruction(Opcode.NEG)
|
||||
)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertThat(vm.evalstack, empty())
|
||||
vm.step(2)
|
||||
assertEquals(1, vm.evalstack.size)
|
||||
assertEquals(Value(DataType.FLOAT, -123.456), vm.evalstack.peek())
|
||||
vm.step(1)
|
||||
assertEquals(1, vm.evalstack.size)
|
||||
assertEquals(Value(DataType.FLOAT, 123.456), vm.evalstack.peek())
|
||||
|
||||
val ins2 = mutableListOf(
|
||||
Instruction(Opcode.PUSH_W, Value(DataType.WORD, 1234)),
|
||||
Instruction(Opcode.NEG)
|
||||
)
|
||||
vm.load(makeProg(ins2), null)
|
||||
vm.step(2)
|
||||
assertEquals(Value(DataType.WORD, 64302), vm.evalstack.pop())
|
||||
|
||||
val ins3 = mutableListOf(
|
||||
Instruction(Opcode.PUSH, Value(DataType.BYTE, 12)),
|
||||
Instruction(Opcode.NEG)
|
||||
)
|
||||
vm.load(makeProg(ins3), null)
|
||||
vm.step(2)
|
||||
assertEquals(Value(DataType.BYTE, 244), vm.evalstack.pop())
|
||||
testUnaryOperator(Value(DataType.BYTE, 12), Opcode.NEG, Value(DataType.BYTE, 244))
|
||||
testUnaryOperator(Value(DataType.WORD, 1234), Opcode.NEG, Value(DataType.WORD, 64302))
|
||||
testUnaryOperator(Value(DataType.FLOAT, 123.456), Opcode.NEG, Value(DataType.FLOAT, -123.456))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInv() {
|
||||
val ins = mutableListOf(
|
||||
Instruction(Opcode.PUSH, Value(DataType.BYTE, 123)),
|
||||
Instruction(Opcode.PUSH_W, Value(DataType.WORD, 4044)),
|
||||
Instruction(Opcode.INV),
|
||||
Instruction(Opcode.INV),
|
||||
Instruction(Opcode.INV)
|
||||
)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertThat(vm.evalstack, empty())
|
||||
vm.step(3)
|
||||
assertEquals(2, vm.evalstack.size)
|
||||
assertEquals(Value(DataType.WORD, 0xf033), vm.evalstack.pop())
|
||||
vm.step(1)
|
||||
assertEquals(1, vm.evalstack.size)
|
||||
assertEquals(Value(DataType.BYTE, 0x84), vm.evalstack.pop())
|
||||
|
||||
val ins2 = mutableListOf(
|
||||
Instruction(Opcode.PUSH_W, Value(DataType.FLOAT, 1234.33)), // todo should crash
|
||||
Instruction(Opcode.INV)
|
||||
)
|
||||
vm.load(makeProg(ins2), null)
|
||||
assertFailsWith<VmExecutionException> {
|
||||
vm.step(2)
|
||||
}
|
||||
testUnaryOperator(Value(DataType.BYTE, 123), Opcode.INV, Value(DataType.BYTE, 0x84))
|
||||
testUnaryOperator(Value(DataType.WORD, 4044), Opcode.INV, Value(DataType.WORD, 0xf033))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -1480,17 +1263,6 @@ class TestStackVmOpcodes {
|
||||
assertEquals(Value(DataType.WORD, 0b1001001100001101), vm.evalstack.peek())
|
||||
}
|
||||
|
||||
|
||||
private fun discardOpcode(dt: DataType): Opcode {
|
||||
return when(dt) {
|
||||
DataType.BYTE -> Opcode.DISCARD
|
||||
DataType.WORD -> Opcode.DISCARD_W
|
||||
DataType.FLOAT -> Opcode.DISCARD_F
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS,
|
||||
DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> Opcode.DISCARD_W
|
||||
}
|
||||
}
|
||||
|
||||
private fun pushOpcode(dt: DataType): Opcode {
|
||||
return when (dt) {
|
||||
DataType.BYTE -> Opcode.PUSH
|
||||
@ -1519,50 +1291,27 @@ class TestStackVmOpcodes {
|
||||
}
|
||||
}
|
||||
|
||||
private fun testBinaryOperator(valuesToPush: List<Value>, operator: Opcode, expected: List<Value>) {
|
||||
assertEquals(valuesToPush.size, expected.size+1)
|
||||
val ins = mutableListOf<Instruction>()
|
||||
for (value in valuesToPush) {
|
||||
ins.add(Instruction(pushOpcode(value.type), value))
|
||||
}
|
||||
for (i in 1 until valuesToPush.size)
|
||||
ins.add(Instruction(operator))
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(valuesToPush.size)
|
||||
assertEquals(valuesToPush.size, vm.evalstack.size)
|
||||
for (expectedVal in expected) {
|
||||
vm.step(1)
|
||||
assertEquals(expectedVal, vm.evalstack.peek())
|
||||
}
|
||||
assertFailsWith<VmTerminationException> {
|
||||
vm.step(1)
|
||||
}
|
||||
private fun testBinaryOperator(left: Value, operator: Opcode, right: Value, result: Value) {
|
||||
val program=makeProg(mutableListOf(
|
||||
Instruction(pushOpcode(left.type), left),
|
||||
Instruction(pushOpcode(right.type), right),
|
||||
Instruction(operator)
|
||||
))
|
||||
vm.load(program, null)
|
||||
vm.step(3)
|
||||
assertEquals(1, vm.evalstack.size)
|
||||
assertEquals(result, vm.evalstack.pop())
|
||||
}
|
||||
|
||||
private fun testUnaryOperator(valuesToPush: List<Value>, operator: Opcode, expected: List<Value>, expectedTypes: List<DataType>?=null) {
|
||||
assertEquals(valuesToPush.size, expected.size)
|
||||
val typesExpected = expectedTypes?.reversed() ?: expected.reversed().map{it.type}
|
||||
|
||||
val ins = mutableListOf<Instruction>()
|
||||
for (value in valuesToPush) {
|
||||
ins.add(Instruction(pushOpcode(value.type), value))
|
||||
}
|
||||
for (type in typesExpected) {
|
||||
ins.add(Instruction(operator))
|
||||
ins.add(Instruction(discardOpcode(type)))
|
||||
}
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(valuesToPush.size)
|
||||
assertEquals(valuesToPush.size, vm.evalstack.size)
|
||||
|
||||
for (expectedVal in expected.reversed()) {
|
||||
vm.step(1)
|
||||
assertEquals(expectedVal, vm.evalstack.peek())
|
||||
vm.step(1)
|
||||
}
|
||||
assertTrue(vm.evalstack.empty())
|
||||
assertFailsWith<VmTerminationException> {
|
||||
vm.step(1)
|
||||
}
|
||||
private fun testUnaryOperator(value: Value, operator: Opcode, result: Value) {
|
||||
val program=makeProg(mutableListOf(
|
||||
Instruction(pushOpcode(value.type), value),
|
||||
Instruction(operator)
|
||||
))
|
||||
vm.load(program, null)
|
||||
vm.step(2)
|
||||
assertEquals(1, vm.evalstack.size)
|
||||
assertEquals(result, vm.evalstack.pop())
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ Variable declarations
|
||||
|
||||
Variables should be declared with their exact type and size so the compiler can allocate storage
|
||||
for them. You must give them an initial value as well. That value can be a simple literal value,
|
||||
or a (constant) expression. The syntax is::
|
||||
or an expression. The syntax is::
|
||||
|
||||
<datatype> <variable name> [ = <initial value> ]
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user