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