From 989897ca293a79147c03e2aebba24217370fe464 Mon Sep 17 00:00:00 2001 From: Gabriel Tofvesson Date: Tue, 18 Jul 2017 01:26:35 +0200 Subject: [PATCH] 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 --- build.gradle | 2 +- .../{CodeCtx.kt => CodeContext.kt} | 263 ++++++++++++++---- .../activity/ScrollingActivity.java | 71 +++-- .../coloursbycontrol/view/ActionView.java | 4 +- .../{StackPushCard.java => ConstantCard.java} | 20 +- .../coloursbycontrol/view/FunctionCard.java | 13 +- .../coloursbycontrol/view/GOTOCard.java | 3 +- .../coloursbycontrol/view/OperationCard.java | 3 +- .../coloursbycontrol/view/VarDeclareCard.java | 3 +- .../src/main/res/layout/content_scrolling.xml | 2 +- 10 files changed, 286 insertions(+), 98 deletions(-) rename mobile/src/main/java/net/tofvesson/coloursbycontrol/{CodeCtx.kt => CodeContext.kt} (55%) rename mobile/src/main/java/net/tofvesson/coloursbycontrol/view/{StackPushCard.java => ConstantCard.java} (74%) diff --git a/build.gradle b/build.gradle index e8e7678..420e015 100644 --- a/build.gradle +++ b/build.gradle @@ -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 diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/CodeCtx.kt b/mobile/src/main/java/net/tofvesson/coloursbycontrol/CodeContext.kt similarity index 55% rename from mobile/src/main/java/net/tofvesson/coloursbycontrol/CodeCtx.kt rename to mobile/src/main/java/net/tofvesson/coloursbycontrol/CodeContext.kt index c04525a..789a047 100644 --- a/mobile/src/main/java/net/tofvesson/coloursbycontrol/CodeCtx.kt +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/CodeContext.kt @@ -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, 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 Array.subSequence(start: Int, end: Int): Array = Array(end-start, {this[it+start]}) +inline fun Array.editElement(index: Int, action: (T) -> T) { this[index] = action(this[index]) } +inline fun Array.lastElement(): T = this[this.lastIndex] +inline fun ArrayList.lastElement(): T = this[this.lastIndex] +fun String.splitExcept(ignore: String, regex: String): Array{ + val refined = ArrayList() + 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 Array.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() + internal val constantPool = ArrayList() + 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=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) = getSignature().forEachIndexed { index, byte -> code.add(index, byte) } + internal fun addPool(code: ArrayList, 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, code: ByteArray) = code.forEach{ codeTarget.add(it) } + internal fun addCode(codeTarget: ArrayList, code: ArrayList) = codeTarget.addAll(code) + private fun isSigned(code: List): 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() + + @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(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): CodeContext = load(ByteArray(code.size, { code[it] })) + fun load(code: CodeBuilder): CodeContext{ + val collected = ArrayList() + 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): 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() val pairedCalls = ArrayList>() val operands = Stack() val operators = ArrayList() - 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 var stackPointer = -1 var popFlag = false var isRunning = false + protected val ctxPool: ContextPool - private constructor(ctxName: String, const: Array, op: ArrayList, pC: ArrayList>) : 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, 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, code: ByteArray): Operation while(--v!=0) vars.put(v.toString(), null) } + private constructor(ctxName: String, const: Array, op: ArrayList, pC: ArrayList>, 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() + val build = ArrayList() + 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, params: Array): Any? { - if(isRunning) return loadContext(stack, name).eval(stack, params) // Prevent errors with multi-threading or self-referencing code + override fun eval(stack: Stack, 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, code: ByteArray): Operation return v } - private fun loadOperands(stack: Stack, op: Operations): Array { + private fun loadOperands(stack: Stack, op: Operations): Array { if(op.getParamCount()>operands.size) throw exception(stack, "Operand stack underflow!") return Array(op.getParamCount() + op.getPairCount(), { if(it, which: Int, atStack: Int): Byte{ + private fun loadPairedVal(stack: Stack, 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, code: ByteArray): Operation throw exception(stack, "Can't load paired call value $which from $atStack!") } - fun loadContext(stack: Stack, 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, 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, code: ByteArray): Operation interface Operation{ fun hasReturnValue(): Boolean fun getParamCount(): Int - fun eval(stack: Stack, params: Array): Any? + fun eval(stack: Stack, vararg params: Any?): Any? } -infix fun HashMap.containsKeyI(key: K) = containsKey(key) +infix fun HashMap.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, reason: String?): RuntimeException = +fun exception(stack: Stack, 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, params: Array): Any? { + override fun eval(stack: Stack, 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, params: Array): String? { + override fun eval(stack: Stack, 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, params: Array): String? = stack[stack.size-1].constants[params[0].asDouble().toInt()] + override fun eval(stack: Stack, 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, params: Array): Any? { + override fun eval(stack: Stack, 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, params: Array): String? = null + override fun eval(stack: Stack, 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, params: Array): Any? { + override fun eval(stack: Stack, 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, params: Array): 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, 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, params: Array): Any? = !params[0].asBoolean() + override fun eval(stack: Stack, 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, params: Array): Any? = params[0].asBoolean() or params[1].asBoolean() + override fun eval(stack: Stack, 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, params: Array): Any? = if(!params[0].asBoolean()) stack[stack.size-1].stackPointer+=2 else 0 + override fun eval(stack: Stack, 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, params: Array): Any?{ + override fun eval(stack: Stack, 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, params: Array): Any? { + override fun eval(stack: Stack, 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, params: Array): Any? = params[0] + override fun eval(stack: Stack, 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, params: Array): Any? { + override fun eval(stack: Stack, 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, params: Array): Any? = stack[stack.size-1].constants[params[0].asDouble().toInt()] + override fun eval(stack: Stack, 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, params: Array): Any? = null + override fun eval(stack: Stack, 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, params: Array): Any? = params[0].asDouble() + 1 + override fun eval(stack: Stack, 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, params: Array): Any? = params[0].asDouble() - 1 + override fun eval(stack: Stack, 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, params: Array): Any? = + override fun eval(stack: Stack, 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, params: Array): Any? = stack[stack.size-1].operands.push(params[0]) + override fun eval(stack: Stack, 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, name: String, owner: Class<*>, searchMask: In return match } -fun invoke(call: Method, params: Array): 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) diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/activity/ScrollingActivity.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/activity/ScrollingActivity.java index 0819f26..8b1baf5 100644 --- a/mobile/src/main/java/net/tofvesson/coloursbycontrol/activity/ScrollingActivity.java +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/activity/ScrollingActivity.java @@ -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: (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(, "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: .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(), 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; } } diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/ActionView.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/ActionView.java index ad674b1..d7eb13b 100644 --- a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/ActionView.java +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/ActionView.java @@ -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); } diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/StackPushCard.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/ConstantCard.java similarity index 74% rename from mobile/src/main/java/net/tofvesson/coloursbycontrol/view/StackPushCard.java rename to mobile/src/main/java/net/tofvesson/coloursbycontrol/view/ConstantCard.java index 2fc8fdd..2df25cf 100644 --- a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/StackPushCard.java +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/ConstantCard.java @@ -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) { diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/FunctionCard.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/FunctionCard.java index 976afe8..3fc38fe 100644 --- a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/FunctionCard.java +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/FunctionCard.java @@ -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; diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/GOTOCard.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/GOTOCard.java index e99fa03..c9b470f 100644 --- a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/GOTOCard.java +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/GOTOCard.java @@ -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) { diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/OperationCard.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/OperationCard.java index 060b13e..f75f910 100644 --- a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/OperationCard.java +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/OperationCard.java @@ -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) { diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/VarDeclareCard.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/VarDeclareCard.java index c990d81..c30c836 100644 --- a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/VarDeclareCard.java +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/VarDeclareCard.java @@ -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) { diff --git a/mobile/src/main/res/layout/content_scrolling.xml b/mobile/src/main/res/layout/content_scrolling.xml index 62c9cbe..2eccb8f 100644 --- a/mobile/src/main/res/layout/content_scrolling.xml +++ b/mobile/src/main/res/layout/content_scrolling.xml @@ -26,7 +26,7 @@ app:color="?colorPrimary" app:backgroundColor="?colorPrimaryDark" app:textColor="?colorAccent"/> -