commit 8241033ebaea1703ace6b10cd2116a293c825736 Author: Gabriel Tofvesson Date: Tue Jul 4 22:28:16 2017 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39fb081 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..96cc43e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..fe7cc92 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..57b2756 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..3b31283 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..635999d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..087c655 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..e8e7678 --- /dev/null +++ b/build.gradle @@ -0,0 +1,25 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + ext.kotlin_version = '1.1.3-2' + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.0.0-alpha5' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..2e5d625 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,17 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx4G + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..0f3828b --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Jul 01 20:00:00 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-milestone-1-all.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..9d82f78 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/libtlang/.gitignore b/libtlang/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/libtlang/.gitignore @@ -0,0 +1 @@ +/build diff --git a/libtlang/build.gradle b/libtlang/build.gradle new file mode 100644 index 0000000..432a46a --- /dev/null +++ b/libtlang/build.gradle @@ -0,0 +1,8 @@ +apply plugin: 'java-library' + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) +} + +sourceCompatibility = "1.8" +targetCompatibility = "1.8" diff --git a/libtlang/src/main/java/net/tofvesson/libtlang/Compiler.java b/libtlang/src/main/java/net/tofvesson/libtlang/Compiler.java new file mode 100644 index 0000000..367546b --- /dev/null +++ b/libtlang/src/main/java/net/tofvesson/libtlang/Compiler.java @@ -0,0 +1,52 @@ +package net.tofvesson.libtlang; + +import java.util.ArrayList; +import java.util.List; + +public interface Compiler { + byte[] compile(Routine r); + Instruction load(byte[] b); + + class DefaultCompiler implements Compiler{ + + protected static final byte[] LID = "tfvsnlng".getBytes(); + protected long CID = 0b00100100; + + @Override + public byte[] compile(Routine r) { + List build = new ArrayList<>(); + for(byte b : LID) build.add(b); + for(byte b : split(CID)) build.add(b); + + + + byte[] b = new byte[build.size()]; + for(int i = 0; i load(byte[] b) { + return null; + } + + protected byte[] split(long l){ + return new byte[]{ + (byte)(l&0xFF), + (byte)((l>>8)&0xFF), + (byte)((l>>16)&0xFF), + (byte)((l>>24)&0xFF), + (byte)((l>>32)&0xFF), + (byte)((l>>40)&0xFF), + (byte)((l>>48)&0xFF), + (byte)(l>>56) + }; + } + protected long combine(byte[] b){ + int size = Math.min(b.length, 8); + long out = 0; + for(int i = 0; i[] getParamTypes() { + return new Class[]{ Object.class, Object.class }; + } + + @Override + public Object eval(Routine s, Object... params) { + return (params[0]==null && params[1]==null) || (params[0]!=null && params[0].equals(params[1])) || (params[1]!=null && params[1].equals(params[0])); + } + }, NOT{ + @Override + public Class[] getParamTypes() { + return new Class[]{ Boolean.class }; + } + @Override + public Object eval(Routine s, Object... params) { + return !((Boolean)params[0]); + } + }, OR{ + @Override + public Class[] getParamTypes() { + return new Class[]{ Boolean.class, Boolean.class }; + } + @Override + public Object eval(Routine s, Object... params) { + return ((Boolean)params[0]) || ((Boolean)params[1]); + } + }, AND{ + @Override + public Class[] getParamTypes() { + return new Class[]{ Boolean.class, Boolean.class }; + } + @Override + public Object eval(Routine s, Object... params) { + return ((Boolean)params[0]) && ((Boolean)params[1]); + } + }, XOR{ + @Override + public Class[] getParamTypes() { + return new Class[]{ Boolean.class, Boolean.class }; + } + @Override + public Boolean eval(Routine s, Object... params) { + return ((Boolean)params[0]) ^ ((Boolean)params[1]); + } + }; + + + @Override public final Class getReturnType() { return Boolean.class; } +} diff --git a/libtlang/src/main/java/net/tofvesson/libtlang/DeclareVariable.java b/libtlang/src/main/java/net/tofvesson/libtlang/DeclareVariable.java new file mode 100644 index 0000000..0f1c89f --- /dev/null +++ b/libtlang/src/main/java/net/tofvesson/libtlang/DeclareVariable.java @@ -0,0 +1,25 @@ +package net.tofvesson.libtlang; + +public class DeclareVariable implements Instruction { + + protected final Variable var; + protected final String name; + protected final T initialValue; + + public DeclareVariable(String name, Class type, T initialValue){ + var = new Variable<>(initialValue, type); + this.name = name; + this.initialValue = initialValue; + } + + @Override + public Object eval(Routine s, Object... params) { + if(s.vars.containsKey(name)) throw new ExecutionException(s.stackPointer, "Attempted to declare variable that already exists: "+name); + var.handleSet(initialValue); + s.vars.put(name, var); + return null; + } + + @Override public Class[] getParamTypes() { return new Class[0]; } + @Override public Class getReturnType() { return Void.class; } +} diff --git a/libtlang/src/main/java/net/tofvesson/libtlang/ExecutionException.java b/libtlang/src/main/java/net/tofvesson/libtlang/ExecutionException.java new file mode 100644 index 0000000..8ac7333 --- /dev/null +++ b/libtlang/src/main/java/net/tofvesson/libtlang/ExecutionException.java @@ -0,0 +1,9 @@ +package net.tofvesson.libtlang; + +public class ExecutionException extends RuntimeException { + public final int stackPointer; + public ExecutionException(int stackPointer) { this.stackPointer = stackPointer; } + public ExecutionException(int stackPointer, String message) { super(message); this.stackPointer = stackPointer; } + public ExecutionException(int stackPointer, String message, Throwable cause) { super(message, cause); this.stackPointer = stackPointer;} + public ExecutionException(int stackPointer, Throwable cause) { super(cause); this.stackPointer = stackPointer; } +} diff --git a/libtlang/src/main/java/net/tofvesson/libtlang/Instruction.java b/libtlang/src/main/java/net/tofvesson/libtlang/Instruction.java new file mode 100644 index 0000000..d714998 --- /dev/null +++ b/libtlang/src/main/java/net/tofvesson/libtlang/Instruction.java @@ -0,0 +1,3 @@ +package net.tofvesson.libtlang; + +public interface Instruction { T eval(Routine s, Object... params); Class[] getParamTypes(); Class getReturnType(); } \ No newline at end of file diff --git a/libtlang/src/main/java/net/tofvesson/libtlang/Label.java b/libtlang/src/main/java/net/tofvesson/libtlang/Label.java new file mode 100644 index 0000000..501658f --- /dev/null +++ b/libtlang/src/main/java/net/tofvesson/libtlang/Label.java @@ -0,0 +1,10 @@ +package net.tofvesson.libtlang; + +public class Label implements Instruction { + protected String name; + public Label(String name){ this.name = name; } + @Override public Object eval(Routine s, Object... params) { return null; } + @Override public Class[] getParamTypes() { return new Class[0]; } + @Override public Class getReturnType() { return Void.class; } + public String getName(){ return name; } +} diff --git a/libtlang/src/main/java/net/tofvesson/libtlang/MathOp.java b/libtlang/src/main/java/net/tofvesson/libtlang/MathOp.java new file mode 100644 index 0000000..f1de60b --- /dev/null +++ b/libtlang/src/main/java/net/tofvesson/libtlang/MathOp.java @@ -0,0 +1,53 @@ +package net.tofvesson.libtlang; + +public enum MathOp implements Instruction { + ADD { + @Override + public Double evaluate(Double n1, Double n2) { + return n1 + n2; + } + }, SUB { + @Override + public Double evaluate(Double n1, Double n2) { + return n1 - n2; + } + }, MUL { + @Override + public Double evaluate(Double n1, Double n2) { + return n1 * n2; + } + }, DIV { + @Override + public Double evaluate(Double n1, Double n2) { + return n1 / n2; + } + }, POW { + @Override + public Double evaluate(Double n1, Double n2) { + return Math.pow(n1, n2); + } + }, MOD { + @Override + public Double evaluate(Double n1, Double n2) { + return n1 % n2; + } + }; + + public abstract Double evaluate(Double n1, Double n2); + + @Override + public Class[] getParamTypes() { + return new Class[]{Number.class, Number.class}; + } + + @Override + public Class getReturnType() { + return Double.class; + } + + @Override + public Double eval(Routine s, Object... params) { + //noinspection unchecked + return evaluate(((Number) params[0]).doubleValue(), ((Number) params[1]).doubleValue()); + } +} diff --git a/libtlang/src/main/java/net/tofvesson/libtlang/RetroGenGoto.java b/libtlang/src/main/java/net/tofvesson/libtlang/RetroGenGoto.java new file mode 100644 index 0000000..b769490 --- /dev/null +++ b/libtlang/src/main/java/net/tofvesson/libtlang/RetroGenGoto.java @@ -0,0 +1,54 @@ +package net.tofvesson.libtlang; + +import java.util.Stack; + +public class RetroGenGoto implements Instruction { + + final Label link; + + public RetroGenGoto(){ this.link = new Label(""); } + + @Override + public Object eval(Routine s, Object... params) { + for(int i = 0 ; i[] getParamTypes() { + return new Class[]{ Boolean.class }; + } + + @Override + public Class getReturnType() { + return Void.class; + } + + public void retrogen(Routine r){ + Stack s = new Stack<>(); + s.push((char)0); + String genName; + OUTER: + do{ + StringBuilder sb = new StringBuilder(); + for(Character c : s) sb.append(c); + genName = sb.toString(); + for(int i = 0; i < r.set.size(); ++i) + if(r.set.get(i)==link) break; + else if(r.set.get(i) instanceof Label && ((Label)r.set.get(i)).name.equals(genName)){ + boolean next = true; + for(int j = 0; j < s.size(); ++j) + if(next){ + char c = s.get(j); + if(++c != 0) next = false; + s.set(j, c); + }else break; + if(next) s.push((char)0); + continue OUTER; + } + link.name = genName; + break; + }while(true); + } +} diff --git a/libtlang/src/main/java/net/tofvesson/libtlang/Routine.java b/libtlang/src/main/java/net/tofvesson/libtlang/Routine.java new file mode 100644 index 0000000..9713751 --- /dev/null +++ b/libtlang/src/main/java/net/tofvesson/libtlang/Routine.java @@ -0,0 +1,201 @@ +package net.tofvesson.libtlang; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +public class Routine implements Instruction { + + protected final Class returnType; + protected final Class[] paramTypes; + + protected boolean retroNames = false; + + protected int stackPointer = 0; + protected List> set = new ArrayList<>(); + protected HashMap> vars = new HashMap<>(); + protected Stack operands = new Stack<>(); + protected boolean strictOperands = true; + + public Routine(Class returnType, Class... paramTypes){ this.returnType = returnType; this.paramTypes = paramTypes; } + + @Override + public T eval(Routine s, Object... params) { + for(int i = 0; i("$"+i, Object.class, params[i])); + operands.clear(); + if(!retroNames){ + for(int i = 0; i i; + for(stackPointer = 0; stackPointer < set.size(); ++stackPointer){ + Object o = (i=set.get(stackPointer)).eval(this, popOpStack(i.getParamTypes())); + if(i.getReturnType()!=Void.class) pushOpStack(o); + } + vars.clear(); + if(strictOperands && ((returnType==Void.class && operands.size()!=0)|| + (returnType!=Void.class && operands.size()>1)|| + (returnType!=Void.class && operands.peek()!=null && !returnType.isAssignableFrom(operands.peek().getClass())))) + throw new RuntimeException("Unclean operand stack at end of routine: "+ Arrays.toString(operands.toArray())); + List> idx = new ArrayList<>(); + for(int j = 0; j)set.get(k)).name.equals("$"+j)) + idx.add((DeclareVariable) set.get(k)); + set.removeAll(idx); + //noinspection unchecked + return returnType==Void.class ? null : operands.size() == 0 ? null : (T) operands.pop(); + } + + public T eval(){ return eval(null); } + + protected Object[] popOpStack(Class[] operandTypes){ + if(operandTypes.length>operands.size()) throw new ExecutionException(stackPointer, "Operand stack underflow (Operator stack pointer value: "+stackPointer+")"); + Object o; + for(int i = 0; i < operandTypes.length; ++i) + if(((o=operands.get(operands.size()-i-1))==null && operandTypes[i]==Number.class) + || (o!=null && !operandTypes[i].isAssignableFrom(o.getClass()) && !Number.class.isAssignableFrom(operandTypes[i]) && !String.class.isAssignableFrom(operandTypes[i]))) + throw new ExecutionException(stackPointer, "Parameter mismatch for routine eval: "+Arrays.toString(operands.toArray())); + Object[] o1 = new Object[operandTypes.length]; + for(int i = 0; i < operandTypes.length; ++i) + o1[i] = String.class==operandTypes[i] ? String.valueOf(operands.pop()) : + Number.class.isAssignableFrom(operandTypes[i]) ? Double.parseDouble(String.valueOf(operands.pop())) : operands.pop(); + return o1; + } + + protected void pushOpStack(Object v){ operands.push(v); } + + public void pushMath(MathOp op, String var1, String var2){ + loadVar(var1); + loadVar(var2); + set.add(op); + } + + public void pushMath(MathOp op, Number var1, String var2){ + set.add(new StackPush(var1)); + loadVar(var2); + set.add(op); + } + + public void pushMath(MathOp op, String var1, Number var2){ + loadVar(var1); + set.add(new StackPush(var2)); + set.add(op); + } + + public void pushMath(MathOp op, Number var1, Number var2){ + set.add(new StackPush(var1)); + set.add(new StackPush(var2)); + set.add(op); + } + + public void pushMath(MathOp op, Routine var1, String var2){ + set.add(var1); + loadVar(var2); + set.add(op); + } + + public void pushMath(MathOp op, String var1, Routine var2){ + loadVar(var1); + set.add(var2); + set.add(op); + } + + public void pushMath(MathOp op, Routine var1, Routine var2){ + set.add(var1); + set.add(new StackPush(var2)); + set.add(op); + } + + public void pushMath(MathOp op, Routine var1, Number var2){ + set.add(var1); + set.add(new StackPush(var2)); + set.add(op); + } + + public void pushMath(MathOp op, Number var1, Routine var2){ + set.add(new StackPush(var1)); + set.add(var2); + set.add(op); + } + + public void pushPop(int maxPopCount){ + set.add(new StackPush(maxPopCount)); + set.add(StandardOps.StackPop); + } + + public void pushPop(String maxPopCount){ + loadVar(maxPopCount); + set.add(StandardOps.StackPop); + } + + public void pushPop(){ pushPop(1); } + + public void loadVar(String var){ + set.add(new StackPush(var)); + set.add(StandardOps.LoadVar); + set.add(StandardOps.ReadVar); + } + + public void storeVal(String var, Object val){ + set.add(new StackPush(var)); + set.add(StandardOps.LoadVar); + set.add(new StackPush(val)); + set.add(StandardOps.StoreVar); + } + + public void gotoLabel(String name, boolean clearOperands){ + set.add(new StackPush(clearOperands)); + set.add(new StackPush(name)); + set.add(StandardOps.GOTO); + } + + public void condition(V _true, V _false){ + set.add(StandardOps.Condition); + RetroGenGoto t = new RetroGenGoto(); + set.add(new StackPush(_true)); + set.add(new StackPush(false)); + set.add(t); + set.add(new StackPush(_false)); + set.add(t.link); + } + + public void gotoLabel(String name){ gotoLabel(name, true); } + + public void add(Instruction i){ set.add(i); } + + public void print(int output){ + set.add(new StackPush(0)); + set.add(StandardOps.Debug); + } + + public void print(String s, int output){ + set.add(new StackPush(s)); + set.add(new StackPush(output)); + set.add(StandardOps.Debug); + } + + public void inline(Routine r){ + r.strictOperands = false; + r.operands = operands; // Simulate inline actions (as if it were run in the same routine) + set.add(r); + } + + public void swapStack(int idx1, int idx2){ + set.add(new StackPush(idx1)); + set.add(new StackPush(idx2)); + set.add(StandardOps.StackSwap); + } + + List> getSet(){ return set; } + HashMap> getVars(){ return vars; } + Stack getOperands(){ return operands; } + public int getStackPointer(){ return stackPointer; } + int setStackPointer(int newVal){ return stackPointer = newVal; } + + @Override public Class[] getParamTypes() { return paramTypes; } + + @Override public Class getReturnType() { return returnType; } +} diff --git a/libtlang/src/main/java/net/tofvesson/libtlang/StackPush.java b/libtlang/src/main/java/net/tofvesson/libtlang/StackPush.java new file mode 100644 index 0000000..a50d9dc --- /dev/null +++ b/libtlang/src/main/java/net/tofvesson/libtlang/StackPush.java @@ -0,0 +1,25 @@ +package net.tofvesson.libtlang; + +public class StackPush implements Instruction { + + protected final Object item; + + public StackPush(Object o){ item = o; } + + @Override + public Object eval(Routine s, Object... params) { + //noinspection unchecked + s.operands.push(item); + return null; + } + + @Override + public Class[] getParamTypes() { + return new Class[0]; + } + + @Override + public Class getReturnType() { + return Void.class; + } +} diff --git a/libtlang/src/main/java/net/tofvesson/libtlang/StandardOps.java b/libtlang/src/main/java/net/tofvesson/libtlang/StandardOps.java new file mode 100644 index 0000000..d1952d2 --- /dev/null +++ b/libtlang/src/main/java/net/tofvesson/libtlang/StandardOps.java @@ -0,0 +1,142 @@ +package net.tofvesson.libtlang; + + +import java.util.Set; + +public enum StandardOps implements Instruction { + Condition{ + @Override + public Object eval(Routine s, Object... params) { + if(!((Boolean) params[0])) s.stackPointer+=3; + return null; + } + @Override public Class[] getParamTypes() { return new Class[]{ Boolean.class }; } + @Override public Class getReturnType() { return Void.class; } + }, + Debug{ + @Override + public Void eval(Routine s, Object... params) { + (((Number) params[0]).intValue()%2 == 0 ? System.out : System.err).println(String.valueOf(params[1])); + return null; + } + + @Override + public Class[] getParamTypes() { + return new Class[]{ Number.class, Object.class }; + } + + @Override + public Class getReturnType() { + return Void.class; + } + }, + GOTO{ + @Override + public Object eval(Routine s, Object... params) { + Instruction t; + for(int i =0 ; i[] getParamTypes() { + return new Class[]{ String.class, Boolean.class }; + } + + @Override + public Class getReturnType() { + return Void.class; + } + }, + LoadVar{ + @Override + public Variable eval(Routine s, Object... params) { + for(String n : (Set) s.vars.keySet()) if(n == params[0] || (n != null && n.equals(params[0]))) return (Variable) s.vars.get(n); + throw new ExecutionException(s.stackPointer, "Couldn't find any variable called "+params[0]); + } + + @Override + public Class[] getParamTypes() { + return new Class[]{ String.class }; + } + + @Override + public Class getReturnType() { + return Variable.class; + } + }, + StoreVar{ + @Override + public Object eval(Routine s, Object... params) { + ((Variable)params[1]).handleSet(params[0]); + return null; + } + + @Override + public Class[] getParamTypes() { + return new Class[]{ Object.class, Variable.class }; + } + + @Override + public Class getReturnType() { + return Void.class; + } + }, + ReadVar{ + @Override + public Object eval(Routine s, Object... params) { + return ((Variable) params[0]).getValue(); + } + + @Override + public Class[] getParamTypes() { + return new Class[]{ Variable.class }; + } + + @Override + public Class getReturnType() { + return Object.class; + } + }, + StackPop{ + @Override + public Object eval(Routine s, Object... params) { + for(int i = Math.min(((Number)params[0]).intValue(), s.operands.size()); i>0; --i) s.operands.pop(); + return null; + } + + @Override + public Class[] getParamTypes() { + return new Class[]{ Number.class }; + } + + @Override + public Class getReturnType() { + return Void.class; + } + }, + NOP{ + @Override public Class[] getParamTypes() { return new Class[0]; } + @Override public Class getReturnType() { return Void.class; } + @Override public Object eval(Routine s, Object... params) { return null; } + }, + DUP{ + @Override public Class[] getParamTypes() { return new Class[0]; } + @Override public Class getReturnType() { return Object.class; } + @Override public Object eval(Routine s, Object... params) { return s.operands.peek(); } + }, + StackSwap{ + @Override public Class[] getParamTypes() { return new Class[]{ Number.class, Number.class }; } + @Override public Class getReturnType() { return Void.class; } + @Override public Object eval(Routine s, Object... params) { + int i1 = s.operands.size()-1-((Number)params[0]).intValue(), i2 = s.operands.size()-1-((Number)params[1]).intValue(); + Object o = s.operands.get(i1); + //noinspection unchecked + s.operands.set(i1, s.operands.get(i2)); + //noinspection unchecked + s.operands.set(i2, o); + return null; + } + } +} diff --git a/libtlang/src/main/java/net/tofvesson/libtlang/StringAppend.java b/libtlang/src/main/java/net/tofvesson/libtlang/StringAppend.java new file mode 100644 index 0000000..5cff1e8 --- /dev/null +++ b/libtlang/src/main/java/net/tofvesson/libtlang/StringAppend.java @@ -0,0 +1,24 @@ +package net.tofvesson.libtlang; + +public class StringAppend implements Instruction { + + protected final boolean paramOrder; + + public StringAppend(boolean paramOrder){ this.paramOrder = paramOrder; } + public StringAppend(){ this(true); } + + @Override + public String eval(Routine s, Object... params) { + return String.valueOf(params[paramOrder?1:0]) + params[paramOrder?0:1]; + } + + @Override + public Class[] getParamTypes() { + return new Class[]{ Object.class, Object.class }; + } + + @Override + public Class getReturnType() { + return String.class; + } +} diff --git a/libtlang/src/main/java/net/tofvesson/libtlang/Variable.java b/libtlang/src/main/java/net/tofvesson/libtlang/Variable.java new file mode 100644 index 0000000..b2125d2 --- /dev/null +++ b/libtlang/src/main/java/net/tofvesson/libtlang/Variable.java @@ -0,0 +1,20 @@ +package net.tofvesson.libtlang; + +public class Variable{ + + protected T value; + protected final Class type; + + public Variable(T t, Class type){ + this.type = type; + handleSet(t); + } + public Variable( Class type){ this(null, type); } + + public void handleSet(Object value){ + if(value!=null && !type.isAssignableFrom(value.getClass())) throw new RuntimeException("Variable assignment type mismatch: "+value); + this.value = (T) (Number.class.isAssignableFrom(type) && value==null ? 0 : (value instanceof Number && Number.class.isAssignableFrom(type)) ? ((Number) value).doubleValue() : value); + } + + public T getValue(){ return value; } +} diff --git a/mobile/.gitignore b/mobile/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/mobile/.gitignore @@ -0,0 +1 @@ +/build diff --git a/mobile/CMakeLists.txt b/mobile/CMakeLists.txt new file mode 100644 index 0000000..7e279b3 --- /dev/null +++ b/mobile/CMakeLists.txt @@ -0,0 +1,46 @@ +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html + +# Sets the minimum version of CMake required to build the native library. + +cmake_minimum_required(VERSION 3.4.1) + +# Creates and names a library, sets it as either STATIC +# or SHARED, and provides the relative paths to its source code. +# You can define multiple libraries, and CMake builds them for you. +# Gradle automatically packages shared libraries with your APK. + +add_library(lib-tlang SHARED src/main/cpp/lib-tlang.cpp) +add_library(lib-tlang-cbc SHARED src/main/cpp/lib-tlang-cbc.cpp) + +#add_library( # Sets the name of the library. +# native-lib +# +# # Sets the library as a shared library. +# SHARED +# +# # Provides a relative path to your source file(s). +# src/main/cpp/native-lib.cpp ) +## Searches for a specified prebuilt library and stores the path as a +## variable. Because CMake includes system libraries in the search path by +## default, you only need to specify the name of the public NDK library +## you want to add. CMake verifies that the library exists before +## completing its build. +# +#find_library( # Sets the name of the path variable. +# log-lib +# +# # Specifies the name of the NDK library that +# # you want CMake to locate. +# log ) +# +## Specifies libraries CMake should link to your target library. You +## can link multiple libraries, such as libraries you define in this +## build script, prebuilt third-party libraries, or system libraries. +# +#target_link_libraries( # Specifies the target library. +# native-lib +# +# # Links the target library to the log library +# # included in the NDK. +# ${log-lib} ) \ No newline at end of file diff --git a/mobile/build.gradle b/mobile/build.gradle new file mode 100644 index 0000000..e0a6212 --- /dev/null +++ b/mobile/build.gradle @@ -0,0 +1,55 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion 26 + buildToolsVersion '26.0.0' + defaultConfig { + applicationId "net.tofvesson.coloursbycontrol" + minSdkVersion 14 + targetSdkVersion 26 + versionCode 4 + versionName "1.3" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + externalNativeBuild { + cmake { + cppFlags "-std=c++11 -fexceptions" + } + } + vectorDrawables.useSupportLibrary true + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } + } + //dexOptions.preDexLibraries false + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + //wearApp project(':wear') + compile project(":libtlang") + compile 'com.google.android.gms:play-services-wearable:11.0.2' + compile 'com.android.support:appcompat-v7:25.3.1' + compile 'com.android.support.constraint:constraint-layout:1.0.2' + compile 'com.android.support:design:25.3.1' + testCompile 'junit:junit:4.12' + compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" +} +repositories { + mavenCentral() +} diff --git a/mobile/mobile-release.apk b/mobile/mobile-release.apk new file mode 100644 index 0000000..ed63351 Binary files /dev/null and b/mobile/mobile-release.apk differ diff --git a/mobile/proguard-rules.pro b/mobile/proguard-rules.pro new file mode 100644 index 0000000..56fa575 --- /dev/null +++ b/mobile/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in E:\Users\Gabriel\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mobile/src/androidTest/java/net/tofvesson/coloursbycontrol/ExampleInstrumentedTest.java b/mobile/src/androidTest/java/net/tofvesson/coloursbycontrol/ExampleInstrumentedTest.java new file mode 100644 index 0000000..a03340a --- /dev/null +++ b/mobile/src/androidTest/java/net/tofvesson/coloursbycontrol/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package net.tofvesson.coloursbycontrol; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("net.tofvesson.coloursbycontrol", appContext.getPackageName()); + } +} diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml new file mode 100644 index 0000000..2eced4c --- /dev/null +++ b/mobile/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mobile/src/main/cpp/lib-tlang-cbc.cpp b/mobile/src/main/cpp/lib-tlang-cbc.cpp new file mode 100644 index 0000000..8acaad6 --- /dev/null +++ b/mobile/src/main/cpp/lib-tlang-cbc.cpp @@ -0,0 +1 @@ +// Add bindings to app/activity here \ No newline at end of file diff --git a/mobile/src/main/cpp/lib-tlang.cpp b/mobile/src/main/cpp/lib-tlang.cpp new file mode 100644 index 0000000..e69de29 diff --git a/mobile/src/main/cpp/lib-tlang.h b/mobile/src/main/cpp/lib-tlang.h new file mode 100644 index 0000000..1226fad --- /dev/null +++ b/mobile/src/main/cpp/lib-tlang.h @@ -0,0 +1,9 @@ +#include +#include +#include +#include + + +#ifndef COLOURSBYCONTROL_LIB_TLANG_H +#define COLOURSBYCONTROL_LIB_TLANG_H +#endif //COLOURSBYCONTROL_LIB_TLANG_H \ No newline at end of file diff --git a/mobile/src/main/cpp/native-lib.cpp b/mobile/src/main/cpp/native-lib.cpp new file mode 100644 index 0000000..e69de29 diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/CodeCtx.kt b/mobile/src/main/java/net/tofvesson/coloursbycontrol/CodeCtx.kt new file mode 100644 index 0000000..9f8430f --- /dev/null +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/CodeCtx.kt @@ -0,0 +1,285 @@ +package net.tofvesson.coloursbycontrol + +import java.util.* +import kotlin.collections.ArrayList +import kotlin.collections.HashMap +import kotlin.experimental.and + +class CodeCtx(ctxName: String, const: Array, code: ByteArray): Operation{ + val name = ctxName + 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 + var stackPointer = -1 + var popFlag = false + var isRunning = false + + 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{ + var first = true + var paired = 0 + for(b in code){ + if(!first){ + if(paired>0){ + pairedCalls.add(Pair(operators.size-1, b)) + --paired + }else{ + val o = Operations.values()[b.toInt()] + paired = o.getPairCount() + operators.add(o) + } + } + first = false + } + var v = paramCount+1 + while(--v!=0) vars.put(v.toString(), null) + } + + override fun hasReturnValue(): Boolean = hasReturn + override fun getParamCount(): Int = paramCount.toInt() + + override fun eval(stack: Stack, params: Array): 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, 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{ + var count = 0 + for((first, second) in pairedCalls) + if(first == atStack){ + if(count==which) return second + else ++count + } + 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 + } + override fun toString(): String = "Context{name=$name, pointer=$stackPointer ("+operators[stackPointer].name+")}" +} + +interface Operation{ + fun hasReturnValue(): Boolean + fun getParamCount(): Int + fun eval(stack: Stack, params: Array): Any? +} + +infix fun HashMap.containsKeyI(key: K) = contains(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 = + 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 "")) + +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? { + var i = stack.size-1 + while(i!=-1){ + if(stack[i].vars containsKeyI params[0].toString()){ + return stack[i].vars[params[0].toString()] + } + --i + } + throw exception(stack, "Variable \""+params[0]+"\" cannot be found!") + } + override fun getPairCount(): Int = 0 + }, + STV{ // Store variable + override fun hasReturnValue(): Boolean = false + override fun getParamCount(): Int = 2 + override fun eval(stack: Stack, params: Array): String? { + var i = stack.size + while(i!=-1){ + if(stack[i].vars containsKeyI params[0].toString()){ + stack[i].vars.put(params[0].toString(), params[1].toString()) + return null + } + --i + } + throw exception(stack, "Variable \""+params[0]+"\" cannot be found!") + } + override fun getPairCount(): Int = 0 + }, + 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 getPairCount(): Int = 1 + }, + EXT{ // Call external + override fun hasReturnValue(): Boolean = true + override fun getParamCount(): Int = 2 + override fun eval(stack: Stack, params: Array): Any? { + val m = Class.forName(params[0].toString()).getDeclaredMethod(params[1].toString(), Object::class.java) + m.isAccessible = true + stack[stack.size-1].popFlag = m.returnType==Void::class.java + val static = (m.modifiers and 0x8) == 0 + val paramCount = m.parameterTypes.size + if(static) 0 else 1 + val caller = stack[stack.size-1] + if(paramCount > caller.operands.size) throw exception(stack, "Operand stack underflow! Required parameter count: "+paramCount) + val callee = if(static) null else caller.operands.pop() + val v = Array(paramCount, { caller.operands.pop() }) + caller.popFlag = m.returnType == Void.TYPE + return m.invoke(callee, v) + } + override fun getPairCount(): Int = 1 + }, + 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 getPairCount(): Int = 0 + }, + DCV{ // Declare variable + override fun hasReturnValue(): Boolean = false + override fun getParamCount(): Int = 2 + override fun eval(stack: Stack, params: Array): 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 + } + override fun getPairCount(): Int = 0 + }, + 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 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 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 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 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?{ + stack[stack.size-1].stackPointer = (if(params[0].asBoolean()) stack[stack.size-1].stackPointer else 0) + params[1].asDouble().toInt() + return null + } + override fun getPairCount(): Int = 0 + }, + CALL{ // Call context + override fun hasReturnValue(): Boolean = true + override fun getParamCount(): Int = 1 + override fun eval(stack: Stack, params: Array): 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() })) + } + 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 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? { + stack[stack.size-1].stackPointer += params[0].asDouble().toInt() + return null + } + override fun getPairCount(): Int = 1 + }, + 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 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 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 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 getPairCount(): Int = 0 + }, + LOP{ // Logical operation + override fun hasReturnValue(): Boolean = true + override fun getParamCount(): Int = 2 + override fun eval(stack: Stack, params: Array): 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() + else if(params[2]==3) params[0].asDouble() / params[1].asDouble() + else if(params[2]==4) params[0].asDouble() % params[1].asDouble() + else throw exception(stack, "Invalid operation "+params[2]) + override fun getPairCount(): Int = 1 + }, + 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 getPairCount(): Int = 0 + }; + abstract fun getPairCount(): Int +} \ No newline at end of file diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/activity/MainActivity.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/activity/MainActivity.java new file mode 100644 index 0000000..9f99a2b --- /dev/null +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/activity/MainActivity.java @@ -0,0 +1,31 @@ +package net.tofvesson.coloursbycontrol.activity; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import net.tofvesson.coloursbycontrol.R; +import net.tofvesson.coloursbycontrol.view.WelcomeView; + +public class MainActivity extends AppCompatActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + //if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + setContentView(R.layout.activity_main); + ((WelcomeView) findViewById(R.id.welcome)).setOnFinishedListener(new WelcomeView.OnWelcomeFinishedListener() { + @Override + public void onTriggerFinished(WelcomeView view) { + view.triggerDraw(); + } + }).triggerDraw(); + findViewById(R.id.newProj).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + startActivity(new Intent(getApplicationContext(), ScrollingActivity.class)); + } + }); + } + +} diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/activity/ScrollingActivity.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/activity/ScrollingActivity.java new file mode 100644 index 0000000..fd74e7d --- /dev/null +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/activity/ScrollingActivity.java @@ -0,0 +1,83 @@ +package net.tofvesson.coloursbycontrol.activity; + +import android.app.Activity; +import android.app.Dialog; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +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.Toast; +import net.tofvesson.coloursbycontrol.CodeCtx; +import net.tofvesson.coloursbycontrol.R; +import net.tofvesson.coloursbycontrol.view.StackPushCard; +import java.util.Stack; +import static net.tofvesson.coloursbycontrol.Operations.*; + +public class ScrollingActivity extends AppCompatActivity { + + static{ + System.loadLibrary("lib-tlang"); + System.loadLibrary("lib-tlang-cbc"); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_scrolling); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + + System.out.println(void.class); + + 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, + 3, 0 + } + ); + System.out.println(ctx.eval(new Stack<>(), new Object[]{ this })); + + + + + + + final StackPushCard cnst = (StackPushCard) 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)); + d.setContentView(R.layout.dialog_constant); + d.setTitle("Constant value"); + 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(); + d.dismiss(); + }); + d.show(); + }); + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setOnClickListener(view -> { + + }); + 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/ActionCard.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/ActionCard.java new file mode 100644 index 0000000..b7071be --- /dev/null +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/ActionCard.java @@ -0,0 +1,132 @@ +package net.tofvesson.coloursbycontrol.view; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.RectF; +import android.support.annotation.Nullable; +import android.util.AttributeSet; + +@SuppressWarnings("unused") +public abstract class ActionCard extends ActionView { + protected static final float rX = 16, rY = 9, cornerSharpness = 30; // Ratio + protected float sRel; + private Bitmap cardSurface; + private Canvas cardDraw; + private final RectF ovCard = new RectF(); + public ActionCard(Context c) { super(c); } + public ActionCard(Context c, @Nullable AttributeSet a) { super(c, a); } + public ActionCard(Context c, @Nullable AttributeSet a, int d) { super(c, a, d); } + @android.support.annotation.RequiresApi(api = android.os.Build.VERSION_CODES.LOLLIPOP) public ActionCard(Context c, @Nullable AttributeSet a, int s, int r) { super(c, a, s, r); } + + @SuppressLint("DrawAllocation") + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int desiredWidth = (int) rX*10; + int desiredHeight = (int) rY*10; + + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + + int width; + int height; + + boolean wDef = widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST, hDef = heightMode == MeasureSpec.EXACTLY || heightMode == MeasureSpec.AT_MOST; + + if(wDef && !hDef) { + width = widthSize; + height = (int)(width * rY / rX); + }else if(hDef && !wDef) { + height = heightSize; + width = (int)(height * rX / rY); + }else if(hDef){ + width = widthSize; + height = heightSize; + }else{ + height = desiredHeight; + width = desiredWidth; + } + + setMeasuredDimension(width, height); + + if(cardSurface == null || cardSurface.getWidth() != getMeasuredWidth() || cardSurface.getHeight() != getMeasuredHeight()) { + boolean verticalPad; + float + ratio = ((float)getMeasuredHeight()) / ((float)getMeasuredWidth()), + ratioI = ((float)getMeasuredWidth()) / ((float)getMeasuredHeight()), + iRY = rY * ratioI, + padding = (verticalPad=iRY>rX)?getMeasuredWidth() - (rX*getMeasuredHeight()/rY) : getMeasuredHeight() - (rY*getMeasuredWidth()/rX); + int x = (int) (((float)getMeasuredWidth())-(verticalPad?padding:0.0F)), y = (int) (((float)getMeasuredHeight()) - (verticalPad?0.0F:padding)); + cardSurface = Bitmap.createBitmap(x<=0?1:x, y<=0?1:y, Bitmap.Config.ARGB_8888); + cardDraw = new Canvas(cardSurface); + } + } + + private int autoDef(int size, int cT, int cN){ + return size * cN / cT; + } + + @SuppressWarnings("SuspiciousNameCombination") // They don't know + @Override + public final void onDraw(Canvas canvas) { + //if(cardDraw.getWidth()==1 || cardDraw.getHeight()==1) return; // View is impossibly small (probably off-screen or something) so just skip rendering + + // Call pre-draw function in case something may need to be partially occluded + preDraw(cardDraw); + + // Clear everything + cardDraw.drawColor(0); + + // Draw actual card + ovCard.left = 0; + ovCard.top = 0; + ovCard.right = cardDraw.getWidth()/cornerSharpness; + ovCard.bottom = ovCard.right; + + cardDraw.drawArc(ovCard, 180, 90, true, color); + + ovCard.right = cardDraw.getWidth(); + ovCard.left = cardDraw.getWidth() - cardDraw.getWidth()/cornerSharpness; + ovCard.top = 0; + ovCard.bottom = cardDraw.getWidth()/cornerSharpness; + + cardDraw.drawArc(ovCard, 270, 90, true, color); + + ovCard.top = (ovCard.bottom = cardDraw.getHeight()) - cardDraw.getWidth()/cornerSharpness; + + cardDraw.drawArc(ovCard, 0, 90, true, color); + + ovCard.left = 0; + ovCard.right = cardDraw.getWidth()/cornerSharpness; + + cardDraw.drawArc(ovCard, 90, 90, true, color); + + ovCard.top = ovCard.right/2F; + + ovCard.bottom = cardDraw.getHeight() - ovCard.top; + + cardDraw.drawRect(0, cardDraw.getWidth()/(2*cornerSharpness), cardDraw.getWidth()/cornerSharpness, cardDraw.getHeight()-cardDraw.getWidth()/(2*cornerSharpness), color); + cardDraw.drawRect(cardDraw.getWidth()-cardDraw.getWidth()/(2*cornerSharpness), cardDraw.getWidth()/(2*cornerSharpness), cardDraw.getWidth(), cardDraw.getHeight()-cardDraw.getWidth()/(2*cornerSharpness), color); + cardDraw.drawRect(cardDraw.getWidth()/(2*cornerSharpness), 0, cardDraw.getWidth()-cardDraw.getWidth()/(2*cornerSharpness), cardDraw.getHeight(), color); + + // Let sub-classes draw class-specific items on top of card + drawItems(cardDraw); + + // Draw to actual canvas + canvas.drawBitmap(cardSurface,(canvas.getWidth()-cardDraw.getWidth())/2, (canvas.getHeight()-cardDraw.getHeight())/2, null); // Draw bitmap with calculated padding + + // Process whether or not view should be invalidated + if(shouldUpdate()) invalidate(); + } + + /** + * Draw action-specific card items on top of card. + * @param canvas Canvas to draw to + */ + protected abstract void drawItems (Canvas canvas); + protected void preDraw (Canvas canvas){ } + protected boolean shouldUpdate() { return false; } +} diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/ActionView.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/ActionView.java new file mode 100644 index 0000000..ad674b1 --- /dev/null +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/ActionView.java @@ -0,0 +1,40 @@ +package net.tofvesson.coloursbycontrol.view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Paint; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.View; +import net.tofvesson.coloursbycontrol.R; +import net.tofvesson.libtlang.Routine; + +public abstract class ActionView extends View { + protected final Paint color = new Paint(), backgroundColor = new Paint(), accentColor = new Paint(), textColor = new Paint(), errorColor = new Paint(); + public ActionView(Context c) { super(c); init(c, null, 0, 0); } + public ActionView(Context c, @Nullable AttributeSet a) { super(c, a); init(c, a, 0, 0); } + public ActionView(Context c, @Nullable AttributeSet a, int d) { super(c, a, d); init(c, a, d, 0); } + @android.support.annotation.RequiresApi(api = android.os.Build.VERSION_CODES.LOLLIPOP) public ActionView(Context c, @Nullable AttributeSet a, int s, int r) { super(c, a, s, r); init(c, a, s, r); } + private void init(Context ctx, AttributeSet set, int da, int dr){ + if(set==null){ + color.setColor(0xffffffff); + backgroundColor.setColor(0xffffffff); + accentColor.setColor(0xffffffff); + textColor.setColor(0xffffffff); + errorColor.setColor(0xffffffff); + } + else{ + TypedArray a = ctx.getTheme().obtainStyledAttributes(set, R.styleable.CustomVal, da, dr); + try { + color.setColor(a.getColor(R.styleable.CustomVal_color, 0xffffffff)); + backgroundColor.setColor(a.getColor(R.styleable.CustomVal_backgroundColor, 0xffffffff)); + accentColor.setColor(a.getColor(R.styleable.CustomVal_accentColor, 0xffffffff)); + textColor.setColor(a.getColor(R.styleable.CustomVal_textColor, 0xffffffff)); + errorColor.setColor(a.getColor(R.styleable.CustomVal_errorColor, 0xffffffff)); + } finally { a.recycle(); } + } + } + + public abstract void processInstructions(Routine r); + public abstract void onDraw(android.graphics.Canvas canvas); +} diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/CircleHideView.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/CircleHideView.java new file mode 100644 index 0000000..57dafac --- /dev/null +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/CircleHideView.java @@ -0,0 +1,74 @@ +package net.tofvesson.coloursbycontrol.view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.os.Build; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.util.AttributeSet; +import android.view.View; + +import net.tofvesson.coloursbycontrol.R; + +public class CircleHideView extends View { + protected static final float FRAME_TOTAL = 60; + protected final RectF circleSize = new RectF(); + protected Bitmap drawOn = null; + protected Canvas c; + protected boolean trigger = false; + protected long frame = 0; + protected Paint drawColor = new Paint(); + public boolean reverse = false; + protected OnHideFinishListener listener; + + public CircleHideView(Context c) { super(c); init(c, null, 0, 0); } + public CircleHideView(Context c, @Nullable AttributeSet a) { super(c, a); init(c, a, 0, 0); } + public CircleHideView(Context c, @Nullable AttributeSet a, int d) { super(c, a, d); init(c, a, d, 0); } + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public CircleHideView(Context c, @Nullable AttributeSet a, int d, int r) { super(c, a, d, r); init(c, a, d, r); } + + private void init(Context ctx, AttributeSet set, int da, int dr){ + if(set==null) drawColor.setColor(0xffffffff); + else{ + TypedArray a = ctx.getTheme().obtainStyledAttributes(set, R.styleable.CustomVal, da, dr); + try { drawColor.setColor(a.getColor(R.styleable.CustomVal_color, 0xffffffff)); } finally { a.recycle(); } + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if(drawOn == null) drawOn = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888); // Allocate once + if(c == null) c = new Canvas(drawOn); + } + + public void triggerDraw(){ if(!trigger){ trigger = true; invalidate(); } } + public CircleHideView setOnFinishedListner(OnHideFinishListener listener){ this.listener = listener; return this; } + + @Override + protected void onDraw(Canvas canvas) { + if(!trigger) canvas.drawPaint(drawColor); + else if(frame<=FRAME_TOTAL){ + float vCenter = canvas.getHeight() / 2.0F, hCenter = canvas.getWidth() / 2.0F, time = frame / FRAME_TOTAL; + circleSize.top = reverse ? (1-time) * vCenter : time * vCenter; + circleSize.bottom = reverse ? (1+time)*vCenter : (2-time) * vCenter; + circleSize.left = reverse ? (1-time) * hCenter : time * hCenter; + circleSize.right = reverse ? (1+time)*hCenter : (2-time) * hCenter; + c.drawColor(Color.TRANSPARENT); + c.drawCircle(hCenter, vCenter, (float) Math.sqrt((vCenter*vCenter)+(hCenter*hCenter))*time, drawColor); + canvas.drawBitmap(drawOn, 0, 0, null); + ++frame; + invalidate(); + }else{ + trigger = false; + frame = 0; + if(listener!=null) listener.onHideFinished(this); + } + } + + public interface OnHideFinishListener{ void onHideFinished(CircleHideView view); } +} diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/FunctionCard.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/FunctionCard.java new file mode 100644 index 0000000..976afe8 --- /dev/null +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/FunctionCard.java @@ -0,0 +1,59 @@ +package net.tofvesson.coloursbycontrol.view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.support.annotation.Nullable; +import android.util.AttributeSet; + +import net.tofvesson.libtlang.Routine; + +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 boolean inline = false; + private final Rect tMeasure = new Rect(); + + public FunctionCard(Context c) { super(c); init(c, null); } + public FunctionCard(Context c, @Nullable AttributeSet a) { super(c, a); init(c, a); } + public FunctionCard(Context c, @Nullable AttributeSet a, int d) { super(c, a, d); init(c, a); } + @android.support.annotation.RequiresApi(api = android.os.Build.VERSION_CODES.LOLLIPOP) public FunctionCard(Context c, @Nullable AttributeSet a, int s, int r) { super(c, a, s, r); init(c, a); } + + private void init(Context c, AttributeSet a){ + if(a!=null) { + TypedArray a1 = c.getTheme().obtainStyledAttributes(a, R.styleable.CustomVal, 0, 0); + try { setName(a1.getString(R.styleable.CustomVal_name)); } finally { a1.recycle(); } + } + if (name == null) name = "NAMELESS"; + } + + 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) { + if(instruction == null) return; + if(inline) r.inline(instruction); + else r.add(instruction); + } + + @Override + public void drawItems(android.graphics.Canvas canvas) { + String s; + textColor.setTextSize((float) Math.sqrt(canvas.getWidth()*canvas.getHeight())/7.5F); + 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; + textColor.getTextBounds(s, 0, s.length(), tMeasure); + canvas.drawText(s, canvas.getWidth()/2 - tMeasure.exactCenterX(), canvas.getHeight()/2.5F, textColor); + } +} diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/GOTOCard.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/GOTOCard.java new file mode 100644 index 0000000..e99fa03 --- /dev/null +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/GOTOCard.java @@ -0,0 +1,21 @@ +package net.tofvesson.coloursbycontrol.view; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.AttributeSet; + +import net.tofvesson.libtlang.Routine; + +public class GOTOCard extends ActionCard { + public GOTOCard(Context c) { super(c); } + public GOTOCard(Context c, @Nullable AttributeSet a) { super(c, a); } + 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 drawItems(android.graphics.Canvas canvas) { + + } +} diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/LinearActionLayout.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/LinearActionLayout.java new file mode 100644 index 0000000..afe136b --- /dev/null +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/LinearActionLayout.java @@ -0,0 +1,37 @@ +package net.tofvesson.coloursbycontrol.view; + +import android.content.Context; +import android.os.Build; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +public class LinearActionLayout extends LinearLayout { + public LinearActionLayout(Context context) { + super(context); + } + + public LinearActionLayout(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public LinearActionLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public LinearActionLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + public final void addView(View child, int index, ViewGroup.LayoutParams params) { + if(!(child instanceof ActionView)) throw new RuntimeException("Attempted to add a non-ActionView view to Action layout!"); + super.addView(child, index, params); + } + + @Override public ActionView getChildAt(int index) { return (ActionView) super.getChildAt(index); } +} diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/OperationCard.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/OperationCard.java new file mode 100644 index 0000000..060b13e --- /dev/null +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/OperationCard.java @@ -0,0 +1,22 @@ +package net.tofvesson.coloursbycontrol.view; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.AttributeSet; + +import net.tofvesson.libtlang.Routine; + + +public class OperationCard extends ActionCard { + public OperationCard(Context c) { super(c); } + public OperationCard(Context c, @Nullable AttributeSet a) { super(c, a); } + 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 drawItems(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/StackPushCard.java new file mode 100644 index 0000000..2fc8fdd --- /dev/null +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/StackPushCard.java @@ -0,0 +1,54 @@ +package net.tofvesson.coloursbycontrol.view; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.os.Build; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.util.AttributeSet; + +import net.tofvesson.libtlang.Routine; +import net.tofvesson.libtlang.StackPush; + +public class StackPushCard extends ActionCard { + protected static final String name = "Constant", help = "Click to edit..."; + public Object push; + private final Rect tMeasure = new Rect(); + + public StackPushCard(Context c) { + super(c); + } + + public StackPushCard(Context c, @Nullable AttributeSet a) { + super(c, a); + } + + public StackPushCard(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) { + super(c, a, s, r); + } + + @Override + public void processInstructions(Routine r) { r.add(new StackPush(push)); } + + @Override + protected void drawItems(Canvas canvas) { + String s = String.valueOf(push); + if (s.length()>15) s = s.substring(0, 15)+"..."; + s = '\"' + s + '\"'; + textColor.setTextSize((float) Math.sqrt(canvas.getWidth()*canvas.getHeight())/7.5F); + textColor.getTextBounds(name, 0, name.length(), tMeasure); + canvas.drawText(name, canvas.getWidth()/2 - tMeasure.exactCenterX(), -tMeasure.exactCenterY() * 4, textColor); + textColor.setTextSize((float) Math.sqrt(canvas.getWidth()*canvas.getHeight())/10F); + textColor.getTextBounds(s, 0, s.length(), tMeasure); + canvas.drawText(s, canvas.getWidth()/2 - tMeasure.exactCenterX(), canvas.getHeight()/1.5F, textColor); + textColor.setTextSize((float) Math.sqrt(canvas.getWidth()*canvas.getHeight())/16F); + textColor.getTextBounds(help, 0, help.length(), tMeasure); + canvas.drawText(help, canvas.getWidth()/2 - tMeasure.exactCenterX(), canvas.getHeight()/1.125F, textColor); + } +} diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/StartIcon.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/StartIcon.java new file mode 100644 index 0000000..a256d03 --- /dev/null +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/StartIcon.java @@ -0,0 +1,27 @@ +package net.tofvesson.coloursbycontrol.view; + +import android.content.Context; +import android.graphics.Rect; +import android.support.annotation.Nullable; +import android.util.AttributeSet; + +import net.tofvesson.libtlang.Routine; + +public class StartIcon extends ActionView { + protected final Rect bounds = new Rect(); + public StartIcon(Context c) { super(c); } + public StartIcon(Context c, @Nullable AttributeSet a) { super(c, a); } + public StartIcon(Context c, @Nullable AttributeSet a, int d) { super(c, a, d); } + @android.support.annotation.RequiresApi(api = android.os.Build.VERSION_CODES.LOLLIPOP) public StartIcon(Context c, @Nullable AttributeSet a, int s, int r) { super(c, a, s, r); } + + @Override public void processInstructions(Routine r) { } + + @Override + public void onDraw(android.graphics.Canvas canvas) { + float f; + accentColor.setTextSize((float) Math.sqrt(canvas.getWidth()*canvas.getHeight())/1.25F); + accentColor.getTextBounds("S", 0, 1, bounds); + canvas.drawCircle(canvas.getWidth()/2F, canvas.getHeight()/2F, f=Math.min(canvas.getWidth(), canvas.getHeight())/2F, color); + canvas.drawText("S", f - bounds.exactCenterX(), f - bounds.exactCenterY(), accentColor); + } +} diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/VarDeclareCard.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/VarDeclareCard.java new file mode 100644 index 0000000..c990d81 --- /dev/null +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/VarDeclareCard.java @@ -0,0 +1,21 @@ +package net.tofvesson.coloursbycontrol.view; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.AttributeSet; + +import net.tofvesson.libtlang.Routine; + +public class VarDeclareCard extends ActionCard { + public VarDeclareCard(Context c) { super(c); } + public VarDeclareCard(Context c, @Nullable AttributeSet a) { super(c, a); } + 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 drawItems(android.graphics.Canvas canvas) { + + } +} diff --git a/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/WelcomeView.java b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/WelcomeView.java new file mode 100644 index 0000000..11d6079 --- /dev/null +++ b/mobile/src/main/java/net/tofvesson/coloursbycontrol/view/WelcomeView.java @@ -0,0 +1,159 @@ +package net.tofvesson.coloursbycontrol.view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.os.Build; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.util.AttributeSet; +import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.Interpolator; + +import net.tofvesson.coloursbycontrol.R; + +public class WelcomeView extends View { + //static{ System.loadLibrary("native-lib"); } // Load native (rendering) code for this class + + protected static final float precision = 30, delimiter = 30, milestone = delimiter+precision; + protected static final Interpolator i = new AccelerateDecelerateInterpolator(); + protected long frame; + private int maxHeight; + private int maxWidth; + protected final RectF s1_oval = new RectF(), s1_innerOval = new RectF(), s2_topCir = new RectF(), s2_botCir = new RectF(), s2_topInnerCir = new RectF(), s2_botInnerCir = new RectF(); + protected final Paint drawColor = new Paint(Paint.ANTI_ALIAS_FLAG), clearColor = new Paint(Paint.ANTI_ALIAS_FLAG); + protected boolean trigger = false; + protected OnWelcomeFinishedListener listener; + + public WelcomeView(Context c) { super(c); init(c, null, 0, 0); } + public WelcomeView(Context c, @Nullable AttributeSet a) { super(c, a); init(c, a, 0, 0); } + public WelcomeView(Context c, @Nullable AttributeSet a, int d) { super(c, a, d); init(c, a, d, 0); } + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public WelcomeView(Context c, @Nullable AttributeSet a, int d, int r) { super(c, a, d, r); init(c, a, d, r); } + + private void init(Context ctx, AttributeSet set, int da, int dr){ + if(set==null){ + clearColor.setColor(0xffffffff); + drawColor.setColor(0xffffffff); + } + else{ + TypedArray a = ctx.getTheme().obtainStyledAttributes(set, R.styleable.CustomVal, da, dr); + try { + drawColor.setColor(a.getColor(R.styleable.CustomVal_color, 0xffffffff)); + clearColor.setColor(a.getColor(R.styleable.CustomVal_backgroundColor, 0xffffffff)); + } finally { a.recycle(); } + } + s1_oval.left = 1; + s1_oval.top = 1; + } + public void setDrawColor(Paint drawColor) { this.drawColor.setColor(drawColor.getColor()); } + public void setClearColor(Paint clearColor) { this.clearColor.setColor(clearColor.getColor()); } + public void triggerDraw(){ if(!trigger){ trigger = true; invalidate(); } } + public WelcomeView setOnFinishedListener(OnWelcomeFinishedListener listener){ this.listener = listener; return this; } + + @Override + protected final void onDraw(Canvas canvas){ + if(!trigger){ + canvas.drawPaint(drawColor); + return; + } + if(frame=0.3){ + s2_topCir.left = -hq*0.5F; + s2_topCir.right = width - hq*1.5F; + s2_topCir.bottom = canvas.getHeight() / 2.5F; + + float tWFact = (s2_topCir.right - s2_topCir.left) * 0.15F, tHFact = s2_topCir.bottom * 0.15F; + + s2_topInnerCir.left = s2_topCir.left + tWFact; + s2_topInnerCir.right = s2_topCir.right - tWFact; + s2_topInnerCir.top = s2_topCir.top + tHFact; + s2_topInnerCir.bottom = s2_topCir.bottom - tHFact; + + canvas.drawArc(s2_topCir, 270, f<=0.6F ? 180 * (f1=(f-0.3F)/0.3F): 180, true, drawColor); + canvas.drawArc(s2_topInnerCir, 270, f<=0.6F ? 180 * f1: 180, true, clearColor); + } + if(f>=0.6){ + s2_botCir.left = -hq; + s2_botCir.top = canvas.getHeight() / 2.5F; + s2_botCir.right = width - hq; + s2_botCir.bottom = canvas.getHeight(); + + float bWFact = (s2_botCir.right - s2_botCir.left) * 0.15F, bHFact = (s2_botCir.bottom - s2_botCir.top) * 0.15F; + + s2_botInnerCir.left = s2_botCir.left + bWFact; + s2_botInnerCir.right = s2_botCir.right - bWFact; + s2_botInnerCir.top = s2_botCir.top + bHFact; + s2_botInnerCir.bottom = s2_botCir.bottom - bHFact; + + canvas.drawArc(s2_botCir, 270, f<=1 ? 180 * (f1=(f-0.6F)/0.4F) : 180, true, drawColor); + canvas.drawArc(s2_botInnerCir, 270, f<=1 ? 180 * f1 : 180, true, clearColor); + } + invalidate(); + } + + protected void clearB(Canvas canvas){ + float + hf = canvas.getHeight() / 2.0F, + f = frame = 0.5F ? hf * (f-0.5F)/0.5F : 0, canvas.getWidth() / 4.0F, f >= 0.5F ? hf * (2-(f-0.5F)/0.5F) : 2 * hf, drawColor); + + canvas.drawArc(s2_topCir, 270, f<=0.5F ? 180 * (1-(f/0.5F)) : 0, true, drawColor); + canvas.drawArc(s2_topInnerCir, 270, f<=0.5F ? 180 * (1-(f/0.5F)) : 0, true, clearColor); + + canvas.drawArc(s2_botCir, 270 + (f<=0.5F ? 180 * (f/0.5F) : 180), f<=0.5F ? 180 * (1-(f/0.5F)) : 0, true, drawColor); + canvas.drawArc(s2_botInnerCir, 270 + (f<=0.5F ? 180 * (f/0.5F) : 180), f<=0.5F ? 180 * (1-(f/0.5F)) : 0, true, clearColor); + invalidate(); + } + + protected void drawC2(Canvas canvas){ + boolean b = frame + + + + \ No newline at end of file diff --git a/mobile/src/main/res/drawable/ic_bright.xml b/mobile/src/main/res/drawable/ic_bright.xml new file mode 100644 index 0000000..a7099da --- /dev/null +++ b/mobile/src/main/res/drawable/ic_bright.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/mobile/src/main/res/drawable/ic_dark.xml b/mobile/src/main/res/drawable/ic_dark.xml new file mode 100644 index 0000000..33b326c --- /dev/null +++ b/mobile/src/main/res/drawable/ic_dark.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/mobile/src/main/res/drawable/ic_day.xml b/mobile/src/main/res/drawable/ic_day.xml new file mode 100644 index 0000000..978f00c --- /dev/null +++ b/mobile/src/main/res/drawable/ic_day.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/mobile/src/main/res/drawable/ic_run.xml b/mobile/src/main/res/drawable/ic_run.xml new file mode 100644 index 0000000..1b1bec2 --- /dev/null +++ b/mobile/src/main/res/drawable/ic_run.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/mobile/src/main/res/drawable/ic_stack.xml b/mobile/src/main/res/drawable/ic_stack.xml new file mode 100644 index 0000000..8b669d5 --- /dev/null +++ b/mobile/src/main/res/drawable/ic_stack.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/mobile/src/main/res/layout/activity_main.xml b/mobile/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..c5f42c0 --- /dev/null +++ b/mobile/src/main/res/layout/activity_main.xml @@ -0,0 +1,37 @@ + + + + + +