Revised structure
Started integrating with UI Updated Views to use new language Added builder object to make runtime generation of code easier Replaced StackPushCard with ConstantCard Added convenience functions for directly getting bytecode from CodeContext object Added bytecode signature Minor changes/fixes
This commit is contained in:
parent
bf749f838a
commit
989897ca29
@ -6,7 +6,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.0-alpha5'
|
||||
classpath 'com.android.tools.build:gradle:3.0.0-alpha6'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
@ -1,3 +1,5 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package net.tofvesson.coloursbycontrol
|
||||
|
||||
import java.lang.reflect.Method
|
||||
@ -6,26 +8,167 @@ import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.experimental.and
|
||||
import kotlin.experimental.or
|
||||
|
||||
class CodeCtx(ctxName: String, const: Array<String>, code: ByteArray): Operation{
|
||||
val name = ctxName
|
||||
|
||||
class ContextNotFoundException(message: String?) : Exception(message)
|
||||
class ContextAlreadyLoadedException(message: String?) : RuntimeException(message)
|
||||
class CodeMismatchException(message: String?) : RuntimeException(message)
|
||||
class ValidationException(message: String?): Exception(message)
|
||||
|
||||
|
||||
fun ByteArray.subSequence(start: Int, end: Int): ByteArray = ByteArray(end-start, {this[it+start]})
|
||||
inline fun <reified T> Array<T>.subSequence(start: Int, end: Int): Array<T> = Array(end-start, {this[it+start]})
|
||||
inline fun <reified T> Array<T>.editElement(index: Int, action: (T) -> T) { this[index] = action(this[index]) }
|
||||
inline fun <reified T> Array<T>.lastElement(): T = this[this.lastIndex]
|
||||
inline fun <reified T> ArrayList<T>.lastElement(): T = this[this.lastIndex]
|
||||
fun String.splitExcept(ignore: String, regex: String): Array<String>{
|
||||
val refined = ArrayList<String>()
|
||||
val beforeRGX = ignore.indexOf(regex)
|
||||
this.split(regex).forEach {
|
||||
if(!refined.isEmpty() && (refined.lastElement().substring(refined.lastElement().length-beforeRGX) + regex + it).startsWith(ignore)) refined[refined.lastIndex] = refined[refined.lastIndex] + regex + it
|
||||
else refined.add(it)
|
||||
}
|
||||
return refined.toTypedArray()
|
||||
}
|
||||
inline fun <reified T> Array<T>.forEachUpdate(action: (T) -> T){
|
||||
for(i in this.indices) this[i] = action(this[i])
|
||||
}
|
||||
|
||||
class CodeBuilder(hasReturn: Boolean, paramSize: Byte, internal val name: String, vararg constants: String){
|
||||
constructor(hasReturn: Boolean, paramSize: Int, name: String, vararg constants: String): this(hasReturn, paramSize.toByte(), name, *constants)
|
||||
constructor(hasReturn: Boolean, name: String, vararg constants: String): this(hasReturn, 0.toByte(), name, *constants)
|
||||
constructor(paramSize: Byte, name: String, vararg constants: String): this(false, paramSize, name, *constants)
|
||||
constructor(paramSize: Int, name: String, vararg constants: String): this(false, paramSize.toByte(), name, *constants)
|
||||
constructor(name: String, vararg constants: String): this(false, 0.toByte(), name, *constants)
|
||||
|
||||
var signature: Byte = ((if(hasReturn) 0b10000000 else 0) or (paramSize.toInt() and 0b01111111)).toByte()
|
||||
internal val code = ArrayList<Byte>()
|
||||
internal val constantPool = ArrayList<String>()
|
||||
init{ constants.forEach { constantPool.add(it) } }
|
||||
fun add(operation: Byte, vararg pairedValues: Byte): CodeBuilder {
|
||||
if(operation<0 || operation>=Operations.values().size) throw ValidationException("Operation $operation is out of range!")
|
||||
var pairCount = -1
|
||||
code.add(operation)
|
||||
while(++pairCount<Operations.values()[operation.toInt()].getPairCount()) code.add(if(pairCount>=pairedValues.size) 0 else pairedValues[pairCount])
|
||||
return this
|
||||
}
|
||||
fun add(operation: Operations, vararg pairedValues: Byte): CodeBuilder = add(operation.ordinal.toByte(), *pairedValues)
|
||||
fun add(operation: Byte, vararg pairedValues: Int) = add(operation, *ByteArray(pairedValues.size, { pairedValues[it].toByte() }))
|
||||
fun add(operation: Operations, vararg pairedValues: Int) = add(operation, *ByteArray(pairedValues.size, { pairedValues[it].toByte() }))
|
||||
fun setHasReturn(hasReturn: Boolean){ signature = (signature and 0b01111111) or (if(hasReturn) 0b10000000 else 0).toByte() }
|
||||
fun setParamCount(paramCount: Byte){ signature = (signature and -128) or (paramCount and 0b01111111) }
|
||||
fun addConstant(const: String): Int {
|
||||
val v: Int = constantPool.indices.firstOrNull { constantPool[it]==const } ?: -1
|
||||
if(v==-1) constantPool.add(const)
|
||||
return if(v==-1) constantPool.size-1 else v
|
||||
}
|
||||
}
|
||||
|
||||
open class ContextPool(superContextPool: ContextPool?) {
|
||||
companion object {
|
||||
internal val signature = ByteArray(8, {
|
||||
if(it==0) 1.toByte()
|
||||
else if(it<3) 3.toByte()
|
||||
else if(it==3) 7.toByte()
|
||||
else if(it==4) 116.toByte()
|
||||
else if(it==5) 108.toByte()
|
||||
else if(it==6) 110.toByte()
|
||||
else 103.toByte()
|
||||
})
|
||||
fun getSignature(): ByteArray = signature.clone()
|
||||
internal fun sign(code: ArrayList<Byte>) = getSignature().forEachIndexed { index, byte -> code.add(index, byte) }
|
||||
internal fun addPool(code: ArrayList<Byte>, name: String, vararg constants: String){
|
||||
val offset = if(isSigned(code)) 8 else 0
|
||||
var consts = ""
|
||||
for(s in constants) consts += s.replace("%", "\\%") + "%"
|
||||
(consts+name+";").toByteArray().forEachIndexed({index, byte -> code.add(index+offset, byte) })
|
||||
}
|
||||
internal fun addCode(codeTarget: ArrayList<Byte>, code: ByteArray) = code.forEach{ codeTarget.add(it) }
|
||||
internal fun addCode(codeTarget: ArrayList<Byte>, code: ArrayList<Byte>) = codeTarget.addAll(code)
|
||||
private fun isSigned(code: List<Byte>): Boolean{
|
||||
if(code.size<8) return false
|
||||
signature.forEachIndexed({ index, byte -> if(code[index]!=byte) return false })
|
||||
return true
|
||||
}
|
||||
}
|
||||
constructor() : this(null)
|
||||
protected val superPool: ContextPool? = superContextPool
|
||||
protected val pool = ArrayList<CodeContext>()
|
||||
|
||||
@Throws(ContextAlreadyLoadedException::class, CodeMismatchException::class)
|
||||
fun load(code: ByteArray): CodeContext {
|
||||
getSignature().forEachIndexed { index, byte -> if(code[index] != byte) throw CodeMismatchException("Invalid signature!") }
|
||||
val codeIdx: Int =
|
||||
code.indices.firstOrNull {
|
||||
code[it]==0x3b.toByte() && (it==0 || code[it-1]!='\\'.toByte())
|
||||
} ?: throw CodeMismatchException("Constant pool not delimited!")
|
||||
val constants: Array<String> = String(code.subSequence(8, codeIdx)).splitExcept("\\%", "%")
|
||||
if(constants.isEmpty()) throw CodeMismatchException("No context name supplied!")
|
||||
constants.forEachUpdate { it.replace("\\%", "%") }
|
||||
try{
|
||||
lookup(constants.lastElement())
|
||||
throw ContextAlreadyLoadedException("Cannot load context "+constants.lastElement())
|
||||
}catch(e: ContextNotFoundException){}
|
||||
val v = _loadAfter(_loadBefore(constants.lastElement(), code.subSequence(codeIdx+1, code.size), constants.subSequence(0, constants.lastIndex)))
|
||||
pool.add(v)
|
||||
return v
|
||||
}
|
||||
fun load(code: List<Byte>): CodeContext = load(ByteArray(code.size, { code[it] }))
|
||||
fun load(code: CodeBuilder): CodeContext{
|
||||
val collected = ArrayList<Byte>()
|
||||
sign(collected)
|
||||
addPool(collected, code.name, *code.constantPool.toTypedArray())
|
||||
collected.add(code.signature)
|
||||
addCode(collected, code.code)
|
||||
return load(collected)
|
||||
}
|
||||
|
||||
fun load(context: CodeContext): CodeContext {
|
||||
try{
|
||||
lookup(context.name)
|
||||
throw ContextAlreadyLoadedException("Cannot load context "+context.name)
|
||||
}catch(e: ContextNotFoundException){ }
|
||||
pool.add(context)
|
||||
return context
|
||||
}
|
||||
|
||||
@Throws(ContextNotFoundException::class)
|
||||
fun lookup(name: String): CodeContext {
|
||||
return _lookup(name) ?: superPool?.lookup(name) ?: throw ContextNotFoundException(name)
|
||||
}
|
||||
|
||||
protected open fun _lookup(name: String): CodeContext?{
|
||||
pool.filter { it.name==name }.forEach { return it }
|
||||
return null
|
||||
}
|
||||
|
||||
protected open fun _loadBefore(name: String, code: ByteArray, constants: Array<String>): CodeContext { return CodeContext(name, constants, code, this) }
|
||||
protected open fun _loadAfter(ctx: CodeContext): CodeContext = ctx
|
||||
}
|
||||
|
||||
open class CodeContext : Operation{
|
||||
val name: String
|
||||
val vars = HashMap<String, Any?>()
|
||||
val pairedCalls = ArrayList<Pair<Int, Byte>>()
|
||||
val operands = Stack<Any?>()
|
||||
val operators = ArrayList<Operations>()
|
||||
val hasReturn = !(code.isEmpty() || ((code[0] and -128).toInt() ushr 7) == 0)
|
||||
val paramCount = if(code.isEmpty()) 0 else code[0] and 0b01111111
|
||||
val constants = const
|
||||
val hasReturn: Boolean
|
||||
val paramCount: Byte
|
||||
val constants: Array<String>
|
||||
var stackPointer = -1
|
||||
var popFlag = false
|
||||
var isRunning = false
|
||||
protected val ctxPool: ContextPool
|
||||
|
||||
private constructor(ctxName: String, const: Array<String>, op: ArrayList<Operations>, pC: ArrayList<Pair<Int, Byte>>) : this(ctxName, const, kotlin.ByteArray(0)) {
|
||||
operators.addAll(op)
|
||||
for((i, b) in pC) pairedCalls.add(Pair(i, b))
|
||||
}
|
||||
|
||||
init{
|
||||
@Throws(ValidationException::class)
|
||||
internal constructor(ctxName: String, constants: Array<String>, code: ByteArray, ctxPool: ContextPool){
|
||||
name = if(ctxName.contains("%") || ctxName.contains(";")) throw ValidationException("Invalid context name: "+ctxName) else ctxName
|
||||
constants.filter { it.contains("%") || it.contains(";") }.forEach { throw ValidationException("Invalid constant name: "+it) }
|
||||
hasReturn = !(code.isEmpty() || ((code[0] and -128).toInt() ushr 7) == 0)
|
||||
paramCount = if(code.isEmpty()) 0 else code[0] and 0b01111111
|
||||
this.constants = constants
|
||||
this.ctxPool = ctxPool
|
||||
var first = true
|
||||
var paired = 0
|
||||
for(b in code){
|
||||
@ -45,18 +188,43 @@ class CodeCtx(ctxName: String, const: Array<String>, code: ByteArray): Operation
|
||||
while(--v!=0) vars.put(v.toString(), null)
|
||||
}
|
||||
|
||||
private constructor(ctxName: String, const: Array<String>, op: ArrayList<Operations>, pC: ArrayList<Pair<Int, Byte>>, ctxPool: ContextPool) : this(ctxName, const, kotlin.ByteArray(0), ctxPool) {
|
||||
operators.addAll(op)
|
||||
for((i, b) in pC) pairedCalls.add(Pair(i, b))
|
||||
}
|
||||
|
||||
fun toBytes(): ByteArray {
|
||||
val emptyStack = Stack<CodeContext>()
|
||||
val build = ArrayList<Byte>()
|
||||
var constants = ""
|
||||
|
||||
ContextPool.getSignature().forEach { build.add(it) }
|
||||
|
||||
for(s in this.constants) constants+=s.replace("%", "\\%")+"%"
|
||||
constants+=name+";"
|
||||
constants.toByteArray().forEach { build.add(it) }
|
||||
build.add((paramCount and 0b01111111) or if(hasReturn) 0b10000000.toByte() else 0)
|
||||
for(i in operators.indices){
|
||||
build.add(operators[i].ordinal.toByte())
|
||||
val max = operators[i].getPairCount()
|
||||
var v = -1
|
||||
while(++v<max) build.add(loadPairedVal(emptyStack, v, i))
|
||||
}
|
||||
return ByteArray(build.size, { build[it] })
|
||||
}
|
||||
|
||||
override fun hasReturnValue(): Boolean = hasReturn
|
||||
override fun getParamCount(): Int = paramCount.toInt()
|
||||
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? {
|
||||
if(isRunning) return loadContext(stack, name).eval(stack, params) // Prevent errors with multi-threading or self-referencing code
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? {
|
||||
if(isRunning) return loadContext(stack, name).eval(stack, *params) // Prevent errors with multi-threading or self-referencing code
|
||||
isRunning = true
|
||||
stack.push(this)
|
||||
if(params.size > paramCount) throw exception(stack, "Context given too many parameters!")
|
||||
params.forEachIndexed { index, it -> vars[index.toString()] = it }
|
||||
while (stackPointer<operators.size-1){
|
||||
val o = operators[++stackPointer]
|
||||
val a = o.eval(stack, loadOperands(stack, o))
|
||||
val a = o.eval(stack, *(loadOperands(stack, o)))
|
||||
if(o.hasReturnValue()) operands.push(a)
|
||||
if(popFlag) operands.pop()
|
||||
popFlag = false
|
||||
@ -69,14 +237,14 @@ class CodeCtx(ctxName: String, const: Array<String>, code: ByteArray): Operation
|
||||
return v
|
||||
}
|
||||
|
||||
private fun loadOperands(stack: Stack<CodeCtx>, op: Operations): Array<Any?> {
|
||||
private fun loadOperands(stack: Stack<CodeContext>, op: Operations): Array<Any?> {
|
||||
if(op.getParamCount()>operands.size) throw exception(stack, "Operand stack underflow!")
|
||||
return Array(op.getParamCount() + op.getPairCount(), {
|
||||
if(it<op.getParamCount()) operands.pop() else loadPairedVal(stack, it-op.getParamCount(), stackPointer)
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadPairedVal(stack: Stack<CodeCtx>, which: Int, atStack: Int): Byte{
|
||||
private fun loadPairedVal(stack: Stack<CodeContext>, which: Int, atStack: Int): Byte{
|
||||
var count = 0
|
||||
for((first, second) in pairedCalls)
|
||||
if(first == atStack){
|
||||
@ -86,10 +254,11 @@ class CodeCtx(ctxName: String, const: Array<String>, code: ByteArray): Operation
|
||||
throw exception(stack, "Can't load paired call value $which from $atStack!")
|
||||
}
|
||||
|
||||
fun loadContext(stack: Stack<CodeCtx>, ctxName: String): CodeCtx {
|
||||
// TODO: Make better 'n load from files 'n other stuff
|
||||
if(ctxName == name) return CodeCtx(name, constants, operators, pairedCalls)
|
||||
else throw exception(stack, "Could not find any context called \"$ctxName\"!") // TODO: Do search for other contexts
|
||||
fun loadContext(stack: Stack<CodeContext>, ctxName: String): CodeContext {
|
||||
if(ctxName == name) return CodeContext(name, constants, operators, pairedCalls, ctxPool)
|
||||
try {
|
||||
return ctxPool.lookup(ctxName)
|
||||
}catch(e: Exception){ throw exception(stack, e.message) }
|
||||
}
|
||||
override fun toString(): String = "Context{name=$name, pointer=$stackPointer ("+operators[stackPointer].name+")}"
|
||||
}
|
||||
@ -97,15 +266,15 @@ class CodeCtx(ctxName: String, const: Array<String>, code: ByteArray): Operation
|
||||
interface Operation{
|
||||
fun hasReturnValue(): Boolean
|
||||
fun getParamCount(): Int
|
||||
fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any?
|
||||
fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any?
|
||||
}
|
||||
|
||||
infix fun <K, V> HashMap<K, V>.containsKeyI(key: K) = containsKey(key)
|
||||
infix fun <K, V> HashMap<K, V>.containsKeyI(key: K): Boolean = containsKey(key)
|
||||
fun Any?.asBoolean(): Boolean =
|
||||
if(this==null) false else this as? Boolean ?: if((this.toString() == "true") or (this.toString() == "false")) this.toString().toBoolean() else !((this.toString()=="0") or (this.toString()=="0.0"))
|
||||
fun Any?.asDouble(): Double = if(this==null) 0.0 else (this as? Number ?: try{ this.toString().toDouble() }catch(e: NumberFormatException){ 0.0 }).toDouble()
|
||||
|
||||
fun exception(stack: Stack<CodeCtx>, reason: String?): RuntimeException =
|
||||
fun exception(stack: Stack<CodeContext>, reason: String?): RuntimeException =
|
||||
RuntimeException((reason ?: "")+" Trace: "+Arrays.toString(stack.toArray())+
|
||||
(if(stack.size>0) " at "+stack[stack.size-1].operators[stack[stack.size-1].stackPointer].ordinal+" ("+stack[stack.size-1].operators[stack[stack.size-1].stackPointer].name+")" else ""))
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@ -122,7 +291,7 @@ enum class Operations: Operation{
|
||||
LDV{ // Load variable
|
||||
override fun hasReturnValue(): Boolean = true
|
||||
override fun getParamCount(): Int = 1
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? {
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? {
|
||||
var i = stack.size-1
|
||||
while(i!=-1){
|
||||
if(stack[i].vars containsKeyI params[0].toString()){
|
||||
@ -137,7 +306,7 @@ enum class Operations: Operation{
|
||||
STV{ // Store variable
|
||||
override fun hasReturnValue(): Boolean = false
|
||||
override fun getParamCount(): Int = 2
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): String? {
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): String? {
|
||||
var i = stack.size
|
||||
while(i!=-1){
|
||||
if(stack[i].vars containsKeyI params[0].toString()){
|
||||
@ -153,13 +322,13 @@ enum class Operations: Operation{
|
||||
LDC{ // Load constant
|
||||
override fun hasReturnValue(): Boolean = true
|
||||
override fun getParamCount(): Int = 0
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): String? = stack[stack.size-1].constants[params[0].asDouble().toInt()]
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): String? = stack[stack.size-1].constants[params[0].asDouble().toInt()]
|
||||
override fun getPairCount(): Int = 1
|
||||
},
|
||||
EXT{ // Call external
|
||||
override fun hasReturnValue(): Boolean = true
|
||||
override fun getParamCount(): Int = 2
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? {
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? {
|
||||
val caller = stack[stack.size-1]
|
||||
val c = Class.forName(params[0].toString())
|
||||
if(params[2].toString().toInt() > caller.operands.size) throw exception(stack, "Operand stack underflow! Required parameter count: "+params[2].toString())
|
||||
@ -170,7 +339,7 @@ enum class Operations: Operation{
|
||||
callParams = arrayOf(caller.operands.pop(), *callParams)
|
||||
}
|
||||
caller.popFlag = callMethod.returnType==Void.TYPE
|
||||
val v = invoke(callMethod, callParams)
|
||||
val v = invoke(callMethod, *callParams)
|
||||
return v
|
||||
}
|
||||
override fun getPairCount(): Int = 1
|
||||
@ -178,13 +347,13 @@ enum class Operations: Operation{
|
||||
POP{ // Pop one operand
|
||||
override fun hasReturnValue(): Boolean = false
|
||||
override fun getParamCount(): Int = 1
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): String? = null
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): String? = null
|
||||
override fun getPairCount(): Int = 0
|
||||
},
|
||||
DCV{ // Declare variable
|
||||
override fun hasReturnValue(): Boolean = false
|
||||
override fun getParamCount(): Int = 2
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? {
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? {
|
||||
if(stack[stack.size-1].vars containsKeyI params[0].toString()) return null
|
||||
stack[stack.size-1].vars.put(params[0].toString(), params[1])
|
||||
return null
|
||||
@ -194,31 +363,31 @@ enum class Operations: Operation{
|
||||
CMP{ // Compare
|
||||
override fun hasReturnValue(): Boolean = true
|
||||
override fun getParamCount(): Int = 2
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? = (params[0].toString() == params[1].toString()) or (if(params[0]==null) params[1]==null else params[0]?.equals(params[1]) as Boolean)
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? = (params[0].toString() == params[1].toString()) or (if(params[0]==null) params[1]==null else params[0]?.equals(params[1]) as Boolean)
|
||||
override fun getPairCount(): Int = 0
|
||||
},
|
||||
LNT{ // Logical NOT
|
||||
override fun hasReturnValue(): Boolean = true
|
||||
override fun getParamCount(): Int = 1
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? = !params[0].asBoolean()
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? = !params[0].asBoolean()
|
||||
override fun getPairCount(): Int = 0
|
||||
},
|
||||
LOR{ // Logical OR
|
||||
override fun hasReturnValue(): Boolean = true
|
||||
override fun getParamCount(): Int = 2
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? = params[0].asBoolean() or params[1].asBoolean()
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? = params[0].asBoolean() or params[1].asBoolean()
|
||||
override fun getPairCount(): Int = 0
|
||||
},
|
||||
CND{ // Conditional Jump (false = jump 2, true = no jump)
|
||||
override fun hasReturnValue(): Boolean = false
|
||||
override fun getParamCount(): Int = 1
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? = if(!params[0].asBoolean()) stack[stack.size-1].stackPointer+=2 else 0
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? = if(!params[0].asBoolean()) stack[stack.size-1].stackPointer+=2 else 0
|
||||
override fun getPairCount(): Int = 0
|
||||
},
|
||||
JMP{ // Unconditional jump (1 operand)
|
||||
override fun hasReturnValue(): Boolean = false
|
||||
override fun getParamCount(): Int = 2
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any?{
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any?{
|
||||
stack[stack.size-1].stackPointer = (if(params[0].asBoolean()) stack[stack.size-1].stackPointer else 0) + params[1].asDouble().toInt()
|
||||
return null
|
||||
}
|
||||
@ -227,25 +396,25 @@ enum class Operations: Operation{
|
||||
CALL{ // Call context
|
||||
override fun hasReturnValue(): Boolean = true
|
||||
override fun getParamCount(): Int = 1
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? {
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? {
|
||||
val caller = stack[stack.size-1]
|
||||
val ctx = caller.loadContext(stack, params[0].toString())
|
||||
if(ctx.getParamCount() > caller.operands.size) throw exception(stack, "Operand stack underflow! Required parameter count: "+ctx.getParamCount())
|
||||
caller.popFlag = !ctx.hasReturn
|
||||
return ctx.eval(stack, Array(ctx.getParamCount(), { caller.operands.pop() }))
|
||||
return ctx.eval(stack, *Array(ctx.getParamCount(), { caller.operands.pop() }))
|
||||
}
|
||||
override fun getPairCount(): Int = 0
|
||||
},
|
||||
LDN{ // Load constant number
|
||||
override fun hasReturnValue(): Boolean = true
|
||||
override fun getParamCount(): Int = 0
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? = params[0]
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? = params[0]
|
||||
override fun getPairCount(): Int = 1
|
||||
},
|
||||
CJP{ // Constant jump (unconditional, no operands)
|
||||
override fun hasReturnValue(): Boolean = false
|
||||
override fun getParamCount(): Int = 0
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? {
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? {
|
||||
stack[stack.size-1].stackPointer += params[0].asDouble().toInt()
|
||||
return null
|
||||
}
|
||||
@ -254,31 +423,31 @@ enum class Operations: Operation{
|
||||
VLD{ // Constant load from variable (load constant based on value of variable)
|
||||
override fun hasReturnValue(): Boolean = true
|
||||
override fun getParamCount(): Int = 1
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? = stack[stack.size-1].constants[params[0].asDouble().toInt()]
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? = stack[stack.size-1].constants[params[0].asDouble().toInt()]
|
||||
override fun getPairCount(): Int = 0
|
||||
},
|
||||
NOP{ // No operation
|
||||
override fun hasReturnValue(): Boolean = false
|
||||
override fun getParamCount(): Int = 0
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? = null
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? = null
|
||||
override fun getPairCount(): Int = 0
|
||||
},
|
||||
INC{ // Increment variable
|
||||
override fun hasReturnValue(): Boolean = true
|
||||
override fun getParamCount(): Int = 1
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? = params[0].asDouble() + 1
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? = params[0].asDouble() + 1
|
||||
override fun getPairCount(): Int = 0
|
||||
},
|
||||
DEC { // Decrement variable
|
||||
override fun hasReturnValue(): Boolean = true
|
||||
override fun getParamCount(): Int = 1
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? = params[0].asDouble() - 1
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? = params[0].asDouble() - 1
|
||||
override fun getPairCount(): Int = 0
|
||||
},
|
||||
LOP{ // Logical operation
|
||||
override fun hasReturnValue(): Boolean = true
|
||||
override fun getParamCount(): Int = 2
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? =
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? =
|
||||
if(params[2]==0) params[0].asDouble() + params[1].asDouble()
|
||||
else if(params[2]==1) params[0].asDouble() - params[1].asDouble()
|
||||
else if(params[2]==2) params[0].asDouble() * params[1].asDouble()
|
||||
@ -290,7 +459,7 @@ enum class Operations: Operation{
|
||||
DUP{ // Duplicate top operand
|
||||
override fun hasReturnValue(): Boolean = true
|
||||
override fun getParamCount(): Int = 1
|
||||
override fun eval(stack: Stack<CodeCtx>, params: Array<Any?>): Any? = stack[stack.size-1].operands.push(params[0])
|
||||
override fun eval(stack: Stack<CodeContext>, vararg params: Any?): Any? = stack[stack.size-1].operands.push(params[0])
|
||||
override fun getPairCount(): Int = 0
|
||||
};
|
||||
abstract fun getPairCount(): Int
|
||||
@ -338,13 +507,13 @@ fun getMethod(params: Array<Any?>, name: String, owner: Class<*>, searchMask: In
|
||||
return match
|
||||
}
|
||||
|
||||
fun invoke(call: Method, params: Array<Any?>): Any? {
|
||||
fun invoke(call: Method, vararg params: Any?): Any? {
|
||||
try{
|
||||
call.isAccessible = true
|
||||
val isStatic = Modifier.isStatic(call.modifiers)
|
||||
val callParams = Array(if(call.parameterTypes.isNotEmpty()) call.parameterTypes.size - (if(isStatic) 0 else 1) else 0,
|
||||
{
|
||||
if(it + (if(isStatic) 0 else 1)>=params.size) null
|
||||
if((it + if(isStatic) 0 else 1)>=params.size) null
|
||||
else getOrCreate(call.parameterTypes[it], params[it + (if(isStatic) 0 else 1)], true, true)
|
||||
})
|
||||
return call.invoke(if(isStatic) null else params[0], *callParams)
|
@ -8,14 +8,19 @@ import android.support.design.widget.Snackbar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import net.tofvesson.coloursbycontrol.CodeCtx;
|
||||
import net.tofvesson.coloursbycontrol.CodeBuilder;
|
||||
import net.tofvesson.coloursbycontrol.CodeContext;
|
||||
import net.tofvesson.coloursbycontrol.ContextPool;
|
||||
import net.tofvesson.coloursbycontrol.R;
|
||||
import net.tofvesson.coloursbycontrol.view.StackPushCard;
|
||||
|
||||
import net.tofvesson.coloursbycontrol.view.ConstantCard;
|
||||
import net.tofvesson.coloursbycontrol.view.FunctionCard;
|
||||
import net.tofvesson.coloursbycontrol.view.LinearActionLayout;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Stack;
|
||||
import static net.tofvesson.coloursbycontrol.Operations.*;
|
||||
|
||||
public class ScrollingActivity extends AppCompatActivity {
|
||||
|
||||
@ -45,29 +50,36 @@ public class ScrollingActivity extends AppCompatActivity {
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
|
||||
final CodeCtx ctx = new CodeCtx("RunMe",
|
||||
new String[]{ "Hello World", "makeText", Toast.class.getName(), "show" }, // Constant pool
|
||||
new byte[] // Instructions
|
||||
{
|
||||
-127 , // 10000001 (Return: true, Params: 1)
|
||||
12, 0,
|
||||
2, 0,
|
||||
12, 0,
|
||||
0,
|
||||
2, 1,
|
||||
2, 2,
|
||||
3, 3,
|
||||
2, 3,
|
||||
2, 2,
|
||||
3, 0,
|
||||
2, 0
|
||||
}
|
||||
);
|
||||
System.out.println(ctx.eval(new Stack<>(), new Object[]{ this }));
|
||||
CodeBuilder RunMe = new CodeBuilder(true, 1, "RunMe", "getToast", "makeText", Toast.class.getName(), "show");
|
||||
RunMe // Operations: | Consumption | | Addition | | Delta | |Definite|
|
||||
.add(LDN /*, 0*/) // Load 0 to the stack (consumes 0 operand(s)) (adds 1 operand(s)) (impact: 1) (stack: 1)
|
||||
.add(LDC /*, 0*/) // Load constant at index 0 to the stack: "getToast" (consumes 0 operand(s)) (adds 1 operand(s)) (impact: 1) (stack: 2)
|
||||
.add(CALL) // Call context "getToast" (consumes 1 operand(s)) (adds 1 operand(s)) (impact: 0) (stack: 2)
|
||||
.add(LDN /*, 0*/) // Load 0 to the stack (consumes 0 operand(s)) (adds 1 operand(s)) (impact: 1) (stack: 3)
|
||||
.add(LDV) // Load a variable to the stack: <Application context> (variable 0 is the first and only parameter) (consumes 1 operand(s)) (adds 1 operand(s)) (impact: 0) (stack: 3)
|
||||
.add(LDC, 1) // Load constant at index 1 to the stack: "makeText" (consumes 0 operand(s)) (adds 1 operand(s)) (impact: 1) (stack: 4)
|
||||
.add(LDC, 2) // Load constant at index 2 to the stack: "android.widget.Toast" (consumes 0 operand(s)) (adds 1 operand(s)) (impact: 1) (stack: 5)
|
||||
.add(EXT, 3) // Call external (Java) code: Toast.makeText(<Application context>, "Hello World", 0) (consumes 5 operand(s)) (adds 1 operand(s)) (impact: -4) (stack: 1)
|
||||
.add(LDC, 3) // Load constant at index 3 to the stack: "show" (consumes 0 operand(s)) (adds 1 operand(s)) (impact: 1) (stack: 2)
|
||||
.add(LDC, 2) // Load constant at index 2 to the stack: "android.widget.Toast" (consumes 0 operand(s)) (adds 1 operand(s)) (impact: 1) (stack: 3)
|
||||
.add(EXT /*, 0*/) // Call external (Java) code: <Toast Object>.show() (consumes 3 operand(s)) (adds 0 operand(s)) (impact: -3) (stack: 0)
|
||||
.add(LDC /*, 0*/); // Load constant at index 0 to the stack: "Hello World" (consumes 0 operand(s)) (adds 1 operand(s)) (impact: 1) (stack: 1)
|
||||
/* implicit return */ // Return from this subroutine to the super-routine (caller) (consumes 1 operand[s]) (adds 0 operand(s)) (impact: -1) (stack: 0)
|
||||
|
||||
CodeBuilder getToast = new CodeBuilder(true, 0, "getToast", "Hey, World!");
|
||||
getToast.add(LDC);
|
||||
|
||||
CodeBuilder toast = new CodeBuilder(false, 2, Toast.class.getName(), "show", "makeText");
|
||||
//TODO: Make CodeContext for showing toast
|
||||
|
||||
final StackPushCard cnst = (StackPushCard) findViewById(R.id.stackPush);
|
||||
ContextPool pool = new ContextPool();// Create a new pool (sandbox for execution)
|
||||
|
||||
CodeContext ctx = pool.load(RunMe); // Load context "RunMe" to pool
|
||||
pool.load(getToast); // Load context "getToast" to pool
|
||||
|
||||
System.out.println(ctx.eval(new Stack<>(), this)); // Execute "RunMe"
|
||||
|
||||
final ConstantCard cnst = (ConstantCard) findViewById(R.id.stackPush);
|
||||
cnst.push = "Hello World";
|
||||
cnst.setOnClickListener(v -> {
|
||||
final Dialog d = new Dialog(new android.view.ContextThemeWrapper(ScrollingActivity.this, R.style.AppThemeLight));
|
||||
@ -76,20 +88,19 @@ public class ScrollingActivity extends AppCompatActivity {
|
||||
if(cnst.push!=null) ((EditText) d.findViewById(R.id.newConst)).setText(String.valueOf(cnst.push));
|
||||
d.findViewById(R.id.cancel_action).setOnClickListener(v12 -> d.dismiss());
|
||||
d.findViewById(R.id.confirm_action).setOnClickListener(v1 -> {
|
||||
cnst.push = ((EditText) d.findViewById(R.id.newConst)).getText();
|
||||
cnst.push = ((EditText) d.findViewById(R.id.newConst)).getText().toString();
|
||||
d.dismiss();
|
||||
});
|
||||
d.show();
|
||||
});
|
||||
((FunctionCard)findViewById(R.id.test)).instruction = "toast";
|
||||
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
|
||||
fab.setOnClickListener(view -> {
|
||||
|
||||
CodeBuilder builder = new CodeBuilder(false, 1, "exec");
|
||||
LinearActionLayout lal = (LinearActionLayout) findViewById(R.id.instructions);
|
||||
for(int i = 0; i<lal.getChildCount(); ++i) lal.getChildAt(i).processInstructions(builder);
|
||||
pool.load(builder).eval(new Stack<>(), this);
|
||||
});
|
||||
findViewById(R.id.fab1).setOnClickListener(v -> Toast.makeText(getApplicationContext(), "I'm going to analyze the defined stack to check whether or not there will be and error on execution", Toast.LENGTH_LONG).show());
|
||||
}
|
||||
|
||||
private static void toast(Object o){ ExternalData d = (ExternalData) o; Toast.makeText(d.ctx, d.message, Toast.LENGTH_SHORT).show(); }
|
||||
private static void snackbar(Object o){ ExternalData d = (ExternalData) o; Snackbar.make(d.ctx.findViewById(android.R.id.content), d.message, Snackbar.LENGTH_LONG).show(); }
|
||||
|
||||
static class ExternalData{ public String message; public Activity ctx; }
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ import android.graphics.Paint;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import net.tofvesson.coloursbycontrol.CodeBuilder;
|
||||
import net.tofvesson.coloursbycontrol.R;
|
||||
import net.tofvesson.libtlang.Routine;
|
||||
|
||||
@ -35,6 +37,6 @@ public abstract class ActionView extends View {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void processInstructions(Routine<?> r);
|
||||
public abstract void processInstructions(CodeBuilder c);
|
||||
public abstract void onDraw(android.graphics.Canvas canvas);
|
||||
}
|
||||
|
@ -7,34 +7,32 @@ import android.os.Build;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.util.AttributeSet;
|
||||
import net.tofvesson.coloursbycontrol.CodeBuilder;
|
||||
import net.tofvesson.coloursbycontrol.Operations;
|
||||
|
||||
import net.tofvesson.libtlang.Routine;
|
||||
import net.tofvesson.libtlang.StackPush;
|
||||
|
||||
public class StackPushCard extends ActionCard {
|
||||
public class ConstantCard extends ActionCard {
|
||||
protected static final String name = "Constant", help = "Click to edit...";
|
||||
public Object push;
|
||||
public String push;
|
||||
private final Rect tMeasure = new Rect();
|
||||
|
||||
public StackPushCard(Context c) {
|
||||
public ConstantCard(Context c) {
|
||||
super(c);
|
||||
}
|
||||
|
||||
public StackPushCard(Context c, @Nullable AttributeSet a) {
|
||||
public ConstantCard(Context c, @Nullable AttributeSet a) {
|
||||
super(c, a);
|
||||
}
|
||||
|
||||
public StackPushCard(Context c, @Nullable AttributeSet a, int d) {
|
||||
public ConstantCard(Context c, @Nullable AttributeSet a, int d) {
|
||||
super(c, a, d);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public StackPushCard(Context c, @Nullable AttributeSet a, int s, int r) {
|
||||
public ConstantCard(Context c, @Nullable AttributeSet a, int s, int r) {
|
||||
super(c, a, s, r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processInstructions(Routine<?> r) { r.add(new StackPush(push)); }
|
||||
@Override public void processInstructions(CodeBuilder c) { c.add(Operations.LDC, c.addConstant(push==null?"null":push)); }
|
||||
|
||||
@Override
|
||||
protected void drawItems(Canvas canvas) {
|
@ -6,6 +6,8 @@ import android.graphics.Rect;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import net.tofvesson.coloursbycontrol.CodeBuilder;
|
||||
import net.tofvesson.coloursbycontrol.Operations;
|
||||
import net.tofvesson.libtlang.Routine;
|
||||
|
||||
import net.tofvesson.coloursbycontrol.R;
|
||||
@ -13,7 +15,7 @@ import net.tofvesson.coloursbycontrol.R;
|
||||
public class FunctionCard extends ActionCard {
|
||||
private static final String func = "Function", inputs = "Inputs: ", out = "Output: ", n = "Name: ";
|
||||
protected String name;
|
||||
public Routine<?> instruction;
|
||||
public String instruction;
|
||||
public boolean inline = false;
|
||||
private final Rect tMeasure = new Rect();
|
||||
|
||||
@ -33,10 +35,9 @@ public class FunctionCard extends ActionCard {
|
||||
public void setName(String name){ if(name==null || name.length()==0) return; this.name = name; }
|
||||
public String getName(){ return name; }
|
||||
|
||||
@Override public void processInstructions(Routine<?> r) {
|
||||
@Override public void processInstructions(CodeBuilder c) {
|
||||
if(instruction == null) return;
|
||||
if(inline) r.inline(instruction);
|
||||
else r.add(instruction);
|
||||
c.add(Operations.LDC, c.addConstant(instruction));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -46,10 +47,14 @@ public class FunctionCard extends ActionCard {
|
||||
textColor.getTextBounds(func, 0, func.length(), tMeasure);
|
||||
canvas.drawText(func, canvas.getWidth()/2 - tMeasure.exactCenterX(), -tMeasure.exactCenterY() * 4, textColor);
|
||||
textColor.setTextSize((float) Math.sqrt(canvas.getWidth()*canvas.getHeight())/12F);
|
||||
|
||||
/*
|
||||
textColor.getTextBounds(s=inputs + (instruction==null?"0":instruction.getParamTypes().length), 0, s.length(), tMeasure);
|
||||
canvas.drawText(s, canvas.getWidth()/2 - tMeasure.exactCenterX(), canvas.getHeight()*2F/3F - tMeasure.exactCenterY() * 3, textColor);
|
||||
textColor.getTextBounds(s=out+(instruction==null || instruction.getReturnType()==Void.class?"No":"Yes"), 0, s.length(), tMeasure);
|
||||
canvas.drawText(s, canvas.getWidth()/2 - tMeasure.exactCenterX(), canvas.getHeight()+tMeasure.exactCenterY() * 4, textColor);
|
||||
*/
|
||||
|
||||
textColor.setTextSize((float) Math.sqrt(canvas.getWidth()*canvas.getHeight())/14F);
|
||||
if (name.length()>20) s = n+name.substring(0, 20)+"...";
|
||||
else s = n+name;
|
||||
|
@ -4,6 +4,7 @@ import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import net.tofvesson.coloursbycontrol.CodeBuilder;
|
||||
import net.tofvesson.libtlang.Routine;
|
||||
|
||||
public class GOTOCard extends ActionCard {
|
||||
@ -12,7 +13,7 @@ public class GOTOCard extends ActionCard {
|
||||
public GOTOCard(Context c, @Nullable AttributeSet a, int d) { super(c, a, d); }
|
||||
@android.support.annotation.RequiresApi(api = android.os.Build.VERSION_CODES.LOLLIPOP) public GOTOCard(Context c, @Nullable AttributeSet a, int s, int r) { super(c, a, s, r); }
|
||||
|
||||
@Override public void processInstructions(Routine<?> r) { }
|
||||
@Override public void processInstructions(CodeBuilder c) { }
|
||||
|
||||
@Override
|
||||
public void drawItems(android.graphics.Canvas canvas) {
|
||||
|
@ -4,6 +4,7 @@ import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import net.tofvesson.coloursbycontrol.CodeBuilder;
|
||||
import net.tofvesson.libtlang.Routine;
|
||||
|
||||
|
||||
@ -13,7 +14,7 @@ public class OperationCard extends ActionCard {
|
||||
public OperationCard(Context c, @Nullable AttributeSet a, int d) { super(c, a, d); }
|
||||
@android.support.annotation.RequiresApi(api = android.os.Build.VERSION_CODES.LOLLIPOP) public OperationCard(Context c, @Nullable AttributeSet a, int s, int r) { super(c, a, s, r); }
|
||||
|
||||
@Override public void processInstructions(Routine<?> r) { }
|
||||
@Override public void processInstructions(CodeBuilder c) { }
|
||||
|
||||
@Override
|
||||
public void drawItems(android.graphics.Canvas canvas) {
|
||||
|
@ -4,6 +4,7 @@ import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import net.tofvesson.coloursbycontrol.CodeBuilder;
|
||||
import net.tofvesson.libtlang.Routine;
|
||||
|
||||
public class VarDeclareCard extends ActionCard {
|
||||
@ -12,7 +13,7 @@ public class VarDeclareCard extends ActionCard {
|
||||
public VarDeclareCard(Context c, @Nullable AttributeSet a, int d) { super(c, a, d); }
|
||||
@android.support.annotation.RequiresApi(api = android.os.Build.VERSION_CODES.LOLLIPOP) public VarDeclareCard(Context c, @Nullable AttributeSet a, int s, int r) { super(c, a, s, r); }
|
||||
|
||||
@Override public void processInstructions(Routine<?> r) { }
|
||||
@Override public void processInstructions(CodeBuilder c) { }
|
||||
|
||||
@Override
|
||||
public void drawItems(android.graphics.Canvas canvas) {
|
||||
|
@ -26,7 +26,7 @@
|
||||
app:color="?colorPrimary"
|
||||
app:backgroundColor="?colorPrimaryDark"
|
||||
app:textColor="?colorAccent"/>
|
||||
<net.tofvesson.coloursbycontrol.view.StackPushCard
|
||||
<net.tofvesson.coloursbycontrol.view.ConstantCard
|
||||
android:id="@+id/stackPush"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
Loading…
x
Reference in New Issue
Block a user