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:
Gabriel Tofvesson 2017-07-18 01:26:35 +02:00
parent bf749f838a
commit 989897ca29
10 changed files with 286 additions and 98 deletions

View File

@ -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

View File

@ -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)

View File

@ -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; }
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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;

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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"