/* * SmojoVM ver 0.1.0 * Published by arnold on 22-Apr-2024 14:37 UTC * (c) Terra Weather Pte. Ltd. * This code is released under the * Creative Commons Share-and-Share Alike with Attribution, * version 4.0. * Refer to https://creativecommons.org/licenses/by-sa/4.0/ * for the full license. */ package smojo.ai; import java.io.Serializable; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.BufferedReader; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Map; import java.util.HashMap; import java.util.TreeMap; import java.util.Arrays; import java.util.Iterator; import java.io.PrintWriter; import java.io.FileInputStream; import java.util.Properties; import java.nio.file.Files; import java.util.ArrayList; public class SmojoVM { private static Properties cp = null; public static void main(String[] args) { Instance c = loadInstance(args[0]); for(int i = 1; i < args.length; ++i){ c.push(args[i]); } c.run(); } private static Properties loadProperties(){ if(cp != null) return cp; File f = new File("smojo.txt"); if(!f.exists()){ error("Can't locate smojo.txt file."); return null; }else{ Properties p = new Properties(); try{ p.load(new FileInputStream(f)); cp = p; return cp; }catch(Exception e){ error("Corrupted smojo.txt file."); return null; } } } private static void error(String s) { System.out.println("ERROR: " + s); } private static SmojoVM.Instance loadInstance(String uri) { if(uri.endsWith(".json")) return loadJson(uri); if(uri.endsWith(".byte")) return loadBytecode(uri); System.out.println("Error: unknown bytecode format - " + uri); return null; } private static SmojoVM.Instance loadBytecode(String uri) { throw new UnsupportedOperationException(); } private static String readStringLocal(String uri) throws FileNotFoundException, IOException, URISyntaxException { return (new BufferedReader(new FileReader(new File(new URI(uri))))).readLine(); } private static String readStringRemote(String uri) throws IOException { URL url = new URL(uri); BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream())); String aline; StringBuffer sb = new StringBuffer(); while ((aline = reader.readLine()) != null) { sb.append(aline); sb.append("\n"); } return sb.toString(); } private static String readString(String uri){ try { if(uri.startsWith("file://")) return readStringLocal(uri); return readStringRemote(uri); } catch (FileNotFoundException e) { error("No such file " + uri); } catch (URISyntaxException e) { error("Bad URL " + uri); } catch (IOException ioe) { error("Exception reading file " + uri); ioe.printStackTrace(); } return null; } private static String getJsonElement(String key, String json, String startDelim, String endDelim) { String s = "\"" + key + "\":" + startDelim; int a = json.indexOf(s); if(a < 0) return null; a += s.length(); int b = json.indexOf(endDelim,a); if(b < 0) { error("Malformed json for " + key); return null; } return json.substring(a,b); } private static String getJsonArray(String key, String json) { return getJsonElement(key,json,"[","]"); } private static String getJsonHash(String key, String json) { return getJsonElement(key,json,"{","}"); } private static Map getStringIntHash(String json) { String[] xs = getStringArray(json); if(null == xs) return null; Map h = new HashMap(); String[] kv ; for(int i = 0 ; i < xs.length; ++i){ kv = xs[i].split(":"); h.put(unescape(kv[0]),Integer.parseInt(kv[1])); } return h; } private static int[] getIntArray(String json) { String[] xs = getStringArray(json); if(null == xs) return null; int[] ins = new int[xs.length]; for(int i = 0 ; i < ins.length; ++i) ins[i] = Integer.parseInt(xs[i]); return ins; } private static String unescape(String s) { return s.replace("\"", "") .replace("%20", " ") .replace("%0A", "\n") .replace("%0D", "\r") .replace("%09", "\t") .replace("%2C", ",") .replace("%22", "\"") .replace("%5B", "[") .replace("%3A", ":") .replace("%5D", "]") .replace("%7B", "{") .replace("%7D", "}") .replace("%5C","\\") .replace("%25","%") ; } private static String[] getStringArray(String json) { if(null == json) return null; if(json.length() == 0) return null; String[] ss = json.split(","); for(int i = 0; i < ss.length; ++i) ss[i] = unescape(ss[i]); return ss; } private static Double[] toDouble(String[] xs){ if(null == xs) return null; Double[] rs = new Double[xs.length]; for(int i = 0; i < xs.length; ++i){ rs[i] = Double.parseDouble(xs[i]); } return rs; } private static SmojoVM.Instance loadJsonFromString(String json) { String[] ss = getStringArray(getJsonArray("strings", json)); String[] ls = getStringArray(getJsonArray("libraries", json)); Double[] rs = toDouble(getStringArray(getJsonArray("reals", json))); int[] ins = getIntArray(getJsonArray("instructions", json)); if( null == ins ){ error("Can't find instructions."); return null; } loadJsonLibraries(ls); return new SmojoVM.Instance(ss, rs, ls, ins); } private static SmojoVM.Instance loadJson(String uri) { return loadJsonFromString(readString(uri)); } private static void loadJsonLibrary(String name , String json) { String[] ss = getStringArray(getJsonArray("strings", json)); Double[] rs = toDouble(getStringArray(getJsonArray("reals", json))); String[] ls = getStringArray(getJsonArray("libraries", json)); int[] ins = getIntArray(getJsonArray("instructions", json)); Map sys = getStringIntHash(getJsonHash("symbols", json)); addLibrary(name,ss,rs,ls,ins,sys); if(null == ls) return; loadJsonLibraries(ls); } private static void loadJsonLibraries(String[] ls) { if( null == ls ) return; for(int i=0; i LIBRARIES = new HashMap(); private static boolean hasLibrary(String name){ return LIBRARIES.containsKey(name); } private static void addLibrary(String name, String[] strings, Double[] reals, String[] libs, int[] instructions, Map symbols){ int id = LIBRARIES.size() + 1; LIBRARIES.put( name, new Context(strings,reals,libs,instructions, symbols,new HashMap(),id) ); } private static Context getLibrary(String name){ return LIBRARIES.get(name); } public static interface Seq extends Serializable { public Object head(); public Seq tail(); public boolean isEmpty(); } public static class Cons implements Seq{ public static Seq nil = new Cons(null,null); protected Object head; protected Seq tail; public Cons(Object head, Seq tail) { this.head = head; this.tail = tail; } @Override public boolean isEmpty() { return null == tail; } @Override public Object head() { return this.head; } @Override public Seq tail() { return this.tail; } } public static abstract class CachedSeq implements Seq{ protected Object head; protected Seq tail; protected boolean notUpdated = true; protected boolean empty = false; @Override public Object head() { if(notUpdated){ update(); notUpdated = false; } return head; } @Override public Seq tail() { if(notUpdated){ update(); notUpdated = false; } return tail; } @Override public boolean isEmpty(){ if(notUpdated){ update(); notUpdated = false; } return empty; } protected abstract void update(); } private static class MapSeq extends CachedSeq{ private XT xt; private Seq seq; private transient Instance rt; public MapSeq(Instance rt, XT xt, Seq seq){ this.rt = rt; this.xt = xt; this.seq = seq; } protected synchronized void update(){ if(seq.isEmpty()){ head = null; tail = Cons.nil; empty = true; }else{ rt.push(seq.head()); rt.runSafe(xt); head = rt.pop(); Seq s = seq.tail(); tail = (s == Cons.nil)? Cons.nil : new MapSeq(rt,xt,s); } } } private static class FilterSeq extends CachedSeq{ private XT xt; private Seq seq; private transient Instance rt; public FilterSeq(Instance rt, XT xt, Seq seq){ this.rt = rt; this.xt = xt; this.seq = seq; } protected synchronized void update(){ if(null == seq) return; Object aa; empty = true; while(!seq.isEmpty()){ aa = seq.head(); rt.push(aa); rt.runSafe(xt); if(rt.toBool()){ head = aa; empty = false; break; } seq = seq.tail(); } if(empty){ tail = Cons.nil; }else{ Seq s = seq.tail(); tail = (s == Cons.nil)? Cons.nil : new FilterSeq(rt,xt,s); } seq = null; } } public static class ArraySeq extends CachedSeq{ private Object[] arr; private int index; public ArraySeq(ArrayList arr, int i){ this(arr.toArray(),i); } public ArraySeq(Object[] arr,int i){ this.arr = arr; index = i; } protected synchronized void update(){ if(index >= arr.length){ head = null; tail = Cons.nil; empty = true; }else{ head = arr[index]; if(index+1>= arr.length){ tail = Cons.nil; }else{ tail = new ArraySeq(arr,index+1); } } } } public static class JoinSeq implements Seq { private Seq l1; private Seq l0; public JoinSeq(Seq a , Seq b){ if(a.isEmpty()){ l0 = b; l1 = Cons.nil; }else{ l0 = a; l1 = b; } } @Override public Object head() { return l0.head(); } @Override public Seq tail() { Seq t = l0.tail(); return (t.isEmpty())? l1 : new JoinSeq(t,l1); } @Override public boolean isEmpty() { return l1.isEmpty() && l0.isEmpty(); } } public static class ZipWithSeq extends CachedSeq{ public Seq a, b; private XT xt; private transient Instance rt; public ZipWithSeq(Instance rt, XT xt, Seq a , Seq b){ this.rt = rt; this.xt = xt; this.b = b; this.a = a; } protected synchronized void update(){ if(a.isEmpty() || b.isEmpty()){ head = null; tail = Cons.nil; empty = true; return; } Object aa = a.head(); Object bb = b.head(); rt.push(aa); rt.push(bb); rt.runSafe(xt); head = rt.pop(); Seq ta = a.tail(); Seq tb = b.tail(); tail = (ta == Cons.nil || tb == Cons.nil)? Cons.nil : new ZipWithSeq(rt,xt,ta,tb); } } public static class TakeSeq extends CachedSeq{ private long n; private Seq seq; public TakeSeq(long n, Seq seq) { this.n = n; this.seq = seq; } protected synchronized void update(){ if(null == seq) return; if(0 == n || seq.isEmpty()){ head = null; tail = Cons.nil; empty = true; return; } head = seq.head(); long z = n - 1; if(0 == z){ tail = Cons.nil; }else{ tail = new TakeSeq(z,seq.tail()); } seq = null; } } public static class TakeWhileSeq extends CachedSeq{ private XT xtf; private Seq seq; private transient Instance rt; public TakeWhileSeq(Instance rt, XT xtf, Seq seq) { this.rt = rt; this.xtf = xtf; this.seq = seq; } protected synchronized void update(){ if(seq.isEmpty()){ head = null; tail = Cons.nil; empty = true; return; } head = seq.head(); rt.push(head); rt.runSafe(xtf); if(!rt.toBool()){ tail = Cons.nil; head = null; empty = true; }else{ tail = new TakeWhileSeq(rt,xtf,seq.tail()); } } } public static class Ellipses implements Seq{ protected long start, skip; public Ellipses(long s, long p){ start = s; skip= p; } @Override public Object head() { return start; } @Override public Seq tail() { return new Ellipses(start + skip,skip); } @Override public boolean isEmpty() { return false; } } public static class FunctionSeq extends CachedSeq{ private XT xt; private transient Instance rt; public FunctionSeq(Instance rt, XT xt) { this.rt = rt; this.xt = xt; } protected synchronized void update(){ rt.runSafe(xt); head = rt.pop(); tail = new FunctionSeq(rt,xt); } } public static class XT{ public int ip; public Context context; public Object[] environment; public XT(Instance rt, int ip){ this(rt,ip,null); } public XT(Instance rt, int ip, Object[] environment){ this.environment = environment; this.ip = ip; context = rt.context; } public String toString(){ StringBuffer sb = new StringBuffer("\nXT("); sb.append(this.hashCode()) .append("){\n") .append("\tip=") .append(ip) .append(";\n") .append("\tcontext=") .append(context.id) .append(";\n") .append("\tenv="); if(null == environment){ sb.append("null"); }else{ sb.append("[" + environment.length + "]"); for(int i = 0; i < environment.length; ++i){ sb.append(environment[i] + "\n"); } } sb.append(";\n}"); return sb.toString(); } } private static class Context{ public String[] strings; public Double[] reals; public String[] libs; public int[] instructions; public Map symbols; public int id = 0; public transient Map lcalls; public Context( String[] strings , Double[] reals, String[] libs , int[] instructions, Map symbols, Map lcalls, int id ){ this.strings = strings; this.reals = reals; this.libs = libs; this.instructions = instructions; this.symbols = symbols; this.lcalls = lcalls; this.id = id; } } public static class Instance{ final int MAX_LOCALS = 15; int IP,RP,SP,LP,DP; Object[] dstack = new Object[64]; Object[] sstack = new Object[16]; Object[] lstack = new Object[1024]; int[] rstack = new int[64]; Context context; Object[] globals = null; boolean running = true; public Instance(String[] strings , Double[] reals, String[] libs , int[] instructions) { context = new Context(strings,reals,libs,instructions, null, new HashMap(),0); IP = LP = 0; DP = RP = SP = ISP = -1; initLibs(); } public void reset(){ LP = 0; DP = RP = SP = -1; } public void debug(){ System.out.println( "\n" + "id=" + context.id + " IP=" + IP + " DP=" + DP + " LP=" + LP + " RP=" + RP + " SP=" + SP + " ISP=" + ISP); showStack("DSTACK", dstack, DP,0); showStack("LSTACK", lstack, LP,15); showStack("SSTACK", sstack, SP,0); showRStack(); } public void showStack(String hdr, Object[] xs, int sz, int ext){ int n = ext + sz; if(n <= 0){ System.out.println(hdr + " is empty."); return; } System.out.println(hdr + "=["); for(int j=0; j < n; ++j){ System.out.println(((j == sz)? "***" : "") + xs[j]); } System.out.println("]"); } public void showRStack(){ if(RP <= 0){ System.out.println("RP is empty."); return; } System.out.println("RP=["); for(int j=0; j < RP; ++j){ System.out.println(rstack[j]); } System.out.println("]"); } private void pushLocals(){ LP += MAX_LOCALS; } private void popLocals(){ LP -= MAX_LOCALS; } public void error( String s ){ System.out.println("ERROR: " + s ); } public void message( String s ){ System.out.print(s); } private Long bool( boolean f ){ return f ? 1L : 0L; } private boolean toBool(){ return ((Long) pop()).equals(1L); } private Long toi(){ return ((Number) dstack[DP--]).longValue(); } public Object pop(){ return dstack[DP--]; } public void push(Object x){ dstack[++DP] = x; } public Object peek(){ return dstack[DP]; } public void run(XT xt){ rstack[++RP] = -1; IP = xt.ip; context = xt.context; pushLocals(); if(xt.environment != null) System.arraycopy(xt.environment, 0, lstack, LP, xt.environment.length); run(); popLocals(); } public void runSafe(XT xt){ pushInstance(); run(xt); popInstance(); } /* under development! */ public boolean tryRun(XT xt){ int RP0 = RP; int DP0 = DP; int IP0 = IP + 1; int LP0 = LP; try{ runSafe(xt); return false; }catch(Exception e){ error("Running XT:\n" + xt.toString() + "\nMessage:\n" + e.getMessage() + "\n"); e.printStackTrace(); } RP = RP0; DP = DP0; IP = IP0; LP = LP0; return true; } int ISP; Context[] istack = new Context[64]; static Map initializedLibs = new HashMap(); private void initLibs(){ ISP = -1; globals = new Object[LIBRARIES.size() + 1]; for(String l : LIBRARIES.keySet()){ initLib(l); } } private void initLib(String lname){ if(initializedLibs.containsKey(lname)) return; Map sys = getLibrary(lname).symbols; if(!sys.containsKey("_init")) return; lcall(new Object[]{lname,sys.get("_init")}); initializedLibs.put(lname,lname); } public void pushInstance(){ rstack[++RP] = IP; istack[++ISP]= context; } public void popInstance(){ IP = rstack[RP--]; context = istack[ISP--]; } private Object[] resolveLibrary(int n){ String fn = context.strings[n]; int x = fn.indexOf(':'); String lname = context.libs[Integer.parseInt(fn.substring(0,x))]; String wname = fn.substring(x+1); Context lib = getLibrary(lname); Object[] stub = new Object[]{lname,lib.symbols.get(wname)}; context.lcalls.put(n,stub); return stub; } private void lcall(int n){ Object[] stub = context.lcalls.get(n); if(null == stub) stub = resolveLibrary(n); lcall(stub); } private void lcall(Object[] stub){ pushInstance(); IP = (Integer)stub[1]; context = getLibrary((String)stub[0]); } public void run() { int i,k,j; long lk; Seq xs; Object obj; String s; Object[] objs; XT xt; double r; ArrayList lst; Iterator iter; while(running) { i = context.instructions[IP]; switch(i){ case 0: running=false; break;case 1: ++RP; rstack[RP] = IP + 2; IP = context.instructions[IP+1]; break;case 2: IP = context.instructions[IP+1]; break;case 3: if(((Long)dstack[DP]) != 0){ IP += 2; }else{ IP = context.instructions[IP+1]; } --DP; break;case 4: ++DP; dstack[DP] = (long) context.instructions[IP+1]; IP += 2; break;case 5: ++DP; dstack[DP] = context.strings[context.instructions[IP+1]]; IP += 2; break;case 6: lstack[LP + context.instructions[IP+1]] = dstack[DP]; --DP; IP += 2; break;case 7: ++DP; dstack[DP] = lstack[LP + context.instructions[IP+1]]; IP += 2; break;case 8: ++RP; rstack[RP]=LP; LP = LP + context.instructions[IP+1]; IP += 2; break;case 10: dstack[++DP] = new XT(this,context.instructions[IP+1]); IP += 2; break;case 11: ++DP; dstack[DP] = context.reals[context.instructions[IP+1]]; IP += 2; break;case 32: IP = rstack[RP]; --RP; if(IP < 0) return; break;case 33: LP = rstack[RP]; --RP; ++IP; break;case 36: dstack[DP - 1] = bool((((Number)dstack[DP-1]).longValue()) == (((Number)dstack[DP]).longValue())); --DP; ++IP; break;case 37: dstack[DP - 1] = bool(((Number)dstack[DP-1]).longValue() < ((Number)dstack[DP]).longValue()); --DP; ++IP; break;case 38: dstack[DP - 1] = bool(((Number)dstack[DP-1]).longValue() > ((Number)dstack[DP]).longValue()); --DP; ++IP; break;case 39: dstack[DP - 1] = bool(((Number)dstack[DP-1]).longValue() <= ((Number)dstack[DP]).longValue()); --DP; ++IP; break;case 40: dstack[DP - 1] = bool(((Number)dstack[DP-1]).longValue() >= ((Number)dstack[DP]).longValue()); --DP; ++IP; break;case 41: dstack[DP - 1] = ((Number)dstack[DP-1]).longValue() % ((Number)dstack[DP]).longValue(); --DP; ++IP; break;case 42: message(dstack[DP] + ""); dstack[DP] = null; --DP; ++IP; break;case 43: dstack[DP - 1] = ((Number)dstack[DP-1]).longValue() + ((Number)dstack[DP]).longValue(); --DP; ++IP; break;case 44: dstack[DP - 1] = ((Number)dstack[DP-1]).longValue() - ((Number)dstack[DP]).longValue(); --DP; ++IP; break;case 45: dstack[DP - 1] = ((Number)dstack[DP-1]).longValue() * ((Number)dstack[DP]).longValue(); --DP; ++IP; break;case 46: dstack[DP - 1] = ((Number)dstack[DP-1]).longValue() / ((Number)dstack[DP]).longValue(); --DP; ++IP; break;case 47: ++DP; dstack[DP] = dstack[DP-1]; ++IP; break;case 48: obj = dstack[DP]; dstack[DP] = dstack[DP-1]; dstack[DP-1] = obj; ++IP; break;case 49: dstack[DP] = null; --DP; ++IP; break;case 53: dstack[DP] = dstack[((Long)dstack[DP]).intValue()]; ++IP; break;case 54: ++SP; sstack[SP] = dstack[DP]; dstack[DP] = null; --DP; ++IP; break;case 55: ++DP; dstack[DP] = sstack[SP]; sstack[SP] = null; --SP; ++IP; break;case 56: ++DP; dstack[DP] = sstack[SP]; ++IP; break;case 57: ++DP; dstack[DP] = (long) DP; ++IP; break;case 58: popInstance(); break;case 59: IP += 2; lcall(context.instructions[IP-1]); break;case 60: dstack[DP - 1] = bool(dstack[DP-1].toString().equals(dstack[DP].toString()) ); --DP; ++IP; break;case 62: dstack[DP - 1] = (long)dstack[DP-1].toString().indexOf(dstack[DP].toString()); --DP; ++IP; break;case 63: dstack[DP - 2] = (long) dstack[DP-2].toString().indexOf(dstack[DP-1].toString(),((Long)dstack[DP]).intValue()); DP-=2; ++IP; break;case 64: dstack[DP - 1] = (long)dstack[DP-1].toString().lastIndexOf(dstack[DP].toString()); --DP; ++IP; break;case 65: dstack[DP] = dstack[DP].toString().toLowerCase(); ++IP; break;case 66: dstack[DP] = dstack[DP].toString().toUpperCase(); ++IP; break;case 67: dstack[DP - 1] = dstack[DP-1].toString() + dstack[DP].toString() ; --DP; ++IP; break;case 68: dstack[DP] = (long)(dstack[DP].toString().length()); ++IP; break;case 69: dstack[DP - 2] = dstack[DP-2].toString().replaceAll(dstack[DP-1].toString(),dstack[DP].toString()); DP-=2; ++IP; break;case 70: dstack[DP - 1] = bool(dstack[DP-1].toString().startsWith(dstack[DP].toString()) ); --DP; ++IP; break;case 71: dstack[DP - 1] = bool(dstack[DP-1].toString().endsWith(dstack[DP].toString()) ); --DP; ++IP; break;case 72: dstack[DP - 1] = dstack[DP-1].toString().substring(((Long)dstack[DP]).intValue()); --DP; ++IP; break;case 73: dstack[DP - 2] = dstack[DP-2].toString().substring(((Long)dstack[DP-1]).intValue(),((Long)dstack[DP]).intValue()); DP-=2; ++IP; break;case 74: k = ((Long)dstack[DP]).intValue(); DP -= k; objs = new Object[k]; for(j = 0; j ((Number)dstack[DP]).doubleValue()); dstack[--DP] = lk; ++IP; break;case 119: lk = bool(((Number)dstack[DP-1]).doubleValue() == (((Number)dstack[DP]).doubleValue())); dstack[--DP] = lk; ++IP; break;case 120: lk = bool(((Number)dstack[DP-1]).doubleValue() <= ((Number)dstack[DP]).doubleValue()); dstack[--DP] = lk; ++IP; break;case 121: lk = bool(((Number)dstack[DP-1]).doubleValue() >= ((Number)dstack[DP]).doubleValue()); dstack[--DP] = lk; ++IP; break;case 122: r = ((Number)dstack[DP-1]).doubleValue() + ((Number)dstack[DP]).doubleValue(); dstack[--DP] = r; ++IP; break;case 123: r = ((Number)dstack[DP-1]).doubleValue() - ((Number)dstack[DP]).doubleValue(); dstack[--DP] = r; ++IP; break;case 124: r = ((Number)dstack[DP-1]).doubleValue() * ((Number)dstack[DP]).doubleValue(); dstack[--DP] = r; ++IP; break;case 125: r = ((Number)dstack[DP-1]).doubleValue() / ((Number)dstack[DP]).doubleValue(); dstack[--DP] = r; ++IP; break;case 126: lk = (long) dstack[--DP]; dstack[DP] = new Ellipses(lk,(long) dstack[DP+1] - lk); ++IP; break;case 127: xt = (XT) dstack[DP--]; lk = toi(); pushInstance(); for(long c = 0; c < lk; ++c){ run(xt); } popInstance(); ++IP; break;case 128: xt = (XT) dstack[DP--]; lk = toi(); pushInstance(); for(long c = 0; c < lk; ++c){ push(c); run(xt); } popInstance(); ++IP; break;case 129: k = ((Long) dstack[DP--]).intValue(); ((Object[])globals[context.id])[k] = dstack[DP--]; ++IP; break;case 130: k = ((Long) dstack[DP]).intValue(); dstack[DP] =((Object[])globals[context.id])[k]; ++IP; break;case 131: k = ((Long)dstack[DP--]).intValue(); if( null != globals[context.id] ){ error("Unable to overwrite globals!"); }else{ globals[context.id] = new Object[k]; } ++IP; break;case 135: dstack[DP-1] = bool(((Map)dstack[DP]).containsKey(dstack[DP-1])); --DP; ++IP; break;case 136: dstack[DP] = (dstack[DP] instanceof Object[])? 1L : 0L ; ++IP; break;case 137: dstack[DP] = (dstack[DP] instanceof Seq)? 1L : 0L ; ++IP; break;case 140: r = Math.pow(((Number)dstack[DP-1]).doubleValue(),((Number)dstack[DP]).doubleValue()); dstack[--DP] = r; ++IP; break;case 141: dstack[DP] = Math.exp(((Number)dstack[DP]).doubleValue()); ++IP; break;case 142: dstack[DP] = Math.log(((Number)dstack[DP]).doubleValue()); ++IP; break;case 143: r = Math.atan2(((Number)dstack[DP-1]).doubleValue(),((Number)dstack[DP]).doubleValue()); dstack[--DP] = r; ++IP; break;case 144: dstack[DP] = Math.sin(((Number)dstack[DP]).doubleValue()); ++IP; break;case 145: dstack[DP] = Math.cos(((Number)dstack[DP]).doubleValue()); ++IP; break;case 146: dstack[DP] = Math.tan(((Number)dstack[DP]).doubleValue()); ++IP; break;case 147: dstack[DP] = Math.round(((Number)dstack[DP]).doubleValue()); ++IP; break;case 148: dstack[DP] = Math.ceil(((Number)dstack[DP]).doubleValue()); ++IP; break;case 149: dstack[DP] = Math.floor(((Number)dstack[DP]).doubleValue()); ++IP; break;case 150: r = Math.max(((Number)dstack[DP-1]).doubleValue(),((Number)dstack[DP]).doubleValue()); dstack[--DP] = r; ++IP; break;case 151: r = Math.min(((Number)dstack[DP-1]).doubleValue(),((Number)dstack[DP]).doubleValue()); dstack[--DP] = r; ++IP; break;case 152: dstack[DP] = Math.abs(((Number)dstack[DP]).doubleValue()); ++IP; break;case 153: dstack[DP] = Long.parseLong((String)dstack[DP]); ++IP; break;case 154: dstack[DP] = Double.parseDouble((String)dstack[DP]); ++IP; break;case 155: ++DP; dstack[DP] = null; ++IP; break;case 156: dstack[DP] = ((long) dstack[DP] == 1)? 0L : 1L; ++IP; break;case 157: dstack[DP - 1] = bool((((Number)dstack[DP-1]).longValue()) > 0 || (((Number)dstack[DP]).longValue()) > 0); --DP; ++IP; break;case 158: dstack[DP - 1] = bool((((Number)dstack[DP-1]).longValue()) > 0 && (((Number)dstack[DP]).longValue()) > 0); --DP; ++IP; break;case 1024: debug(); ++IP; break;case 1025: showStack("LSTACK",lstack,LP,((Long)dstack[DP--]).intValue()); ++IP; break; default: error("Unknown opcode " + i); running = false; break; } } } private void loadNative(long isCached, String name) { name = name.trim().toLowerCase(); String[] xs = name.split("/"); String uname = xs[0].trim(); String mname = xs[1].trim(); String className = mname.substring(0,1).toUpperCase() + mname.substring(1); String canonicalClassName = "smojo.ai.user.s" + uname + "." + className; final String LBASE = "./lib/"; String bpath = LBASE + uname + "/" + mname + "/"; String javaFileName = bpath + className + ".java"; if(!exists(bpath + className + ".class") || (0 == isCached)){ if(mkdirs(bpath)) return; if(saveSource(name,javaFileName)) return; if(compileNative(javaFileName)) return; } FileClassLoader fcl = new FileClassLoader(LBASE); fcl.loadFromFile(canonicalClassName); } private boolean exists(String path){ try{ File f = new File(path); return f.exists(); }catch(Exception e){ return false; } } private boolean mkdirs(String p) { File path = new File(p); if(!path.exists()){ if(!path.mkdirs()){ error("Unable to create path: " + p); return true; } } return false; } private boolean saveSource(String name, String jname) { try{ PrintWriter pw = new PrintWriter(jname); pw.print(readString("https://app.smojo.org/" + name + ".java")); pw.close(); return false; }catch(Exception e){ error("Unable to write java file: " + jname); return true; } } private boolean compileNative(String jname) { Properties p = loadProperties(); if(null == p){ error("Unable to load smojo.txt"); return true; } String javac = p.getProperty("javac"); if(null == javac){ error("Unable to locate java compiler from smojo.txt"); return true; } javac = javac.replaceAll("\"",""); String cpath = p.getProperty("classpath"); if(null == cpath){ error("Unable to locate classpath from smojo.txt"); return true; } cpath = cpath.replaceAll("\"",""); int status = -1; Process proc = null; try{ proc = Runtime.getRuntime().exec( javac + " -cp " + cpath + " " + jname ); status = proc.waitFor(); }catch(Exception e){ error("Unable to run java compiler"); e.printStackTrace(); } if(0 == status){ return false; }else{ error("Unable to compile java module '" + jname + "'"); printCompilationError(proc); return true; } } private void printCompilationError(Process p){ try{ BufferedReader br = new BufferedReader( new InputStreamReader(p.getErrorStream()) ); String s; while((s = br.readLine()) != null){ System.out.println(trunc(s,256)); } }catch(Exception ignore){} } private String trunc(String s, int n){ if(s.length() <= n) return s; return s.substring(0,n); } private static class FileClassLoader extends ClassLoader{ final String BPATH; final static Map cache = new HashMap(); public FileClassLoader(String bpath){ BPATH = (bpath.endsWith("/"))? bpath : bpath + "/"; } public FileClassLoader(){ this("./lib/"); } public boolean loadFromFile(String name){ try{ loadClass(name, true); return false; }catch(Exception e){ return true; } } public Class findClass(String name) throws ClassNotFoundException{ if(cache.containsKey(name)){ return cache.get(name); } try{ byte[] b; b = loadClassData(name); Class c = defineClass(name, b, 0, b.length); cache.put(name,c); return c; }catch(IOException e){ throw new ClassNotFoundException(name); } } private byte[] loadClassData(String name) throws IOException { name = name.replace("smojo.ai.user.s",""); String[] xs = name.split("\\."); String uname = xs[0]; String cname = xs[1]; xs = xs[1].split("\\$"); String mname = xs[0].toLowerCase(); File f = new File(BPATH + uname + "/" + mname + "/" + cname + ".class"); return Files.readAllBytes(f.toPath()); } public boolean isLoaded(String name){ try{ findClass(name); return true; }catch(Exception e){ return false; } } } private static ClassLoader instanceClassLoader = null; public static void setClassLoader(ClassLoader cl){ instanceClassLoader = cl; } public static ClassLoader getClassLoader(){ return instanceClassLoader; } public void runNative(Seq xs){ String fn = (String) xs.tail().tail().head(); Args args = null; try{ args = new Args((Seq) xs.tail().head()); }catch(ClassNotFoundException e){ error("runNative() - can't load class: " + e.getMessage() + "\nargs='" + args + "'"); return; } if(fn.startsWith(".")){ callStatic((String)xs.head(),fn,args); }else if(fn.equals("new")){ push(createObject((String)xs.head(),args)); }else{ callInstance(xs.head(),fn,args); } } private void callInstance(Object obj, String functionName, Args xs){ push( invokeOnObject( obj, functionName, xs ) ); } private void callStatic(String className, String functionName, Args xs){ try{ push( invokeStatic( getClassFrom(className), functionName.substring(1), xs ) ); }catch(ClassNotFoundException cnfe){ error("callStatic("+ functionName + ") - can't load class: " + canonicalName(className) + "\nargs='" + xs + "'"); } } private Object createObject(String name, Args args){ try{ return getClassFrom(name) .getConstructor(args.types) .newInstance(args.values); }catch(ClassNotFoundException cnfe){ error("createObject() - can't load class: " + canonicalName(name) + "\nargs='" + args + "'"); }catch(Exception e){ error("Can't create native object from class: " + canonicalName(name)); e.printStackTrace(); } return null; } private static Class getClassFrom(String name) throws ClassNotFoundException { if(name.indexOf("/") < 0){ return getCanonicalClassFrom(name); }else{ FileClassLoader fcl = new FileClassLoader(); return fcl.findClass(canonicalName(name)); } } private static String canonicalName(String name){ if(name.indexOf("/") < 0) return name; String[] xs = name.split("/"); String uname = xs[0].trim(); String mname = xs[1].trim(); String className = mname.substring(0,1).toUpperCase() + mname.substring(1); return "smojo.ai.user.s" + uname + "." + className; } private Object invokeStatic(Class clazz, String name , Args args) { try { return clazz.getMethod(name, args.types).invoke(null, args.values); }catch(NullPointerException npe){ error("No such static method: '" + clazz.getName() + name + "'"); return null; }catch(Exception e){ throw new RuntimeException(e); } } private Object invokeOnObject(Object obj, String name, Args args) { try { return obj.getClass().getMethod(name, args.types).invoke(obj, args.values); }catch(NullPointerException npe){ error("No such method: '" + obj.getClass().getName() + "." + name + "'"); return null; } catch (Exception e) { throw new RuntimeException(e); } } private static Class getCanonicalClassFrom(String name) throws ClassNotFoundException { if("int".equals(name)) return int.class; if("long".equals(name)) return long.class; if("double".equals(name)) return double.class; if("boolean".equals(name)) return boolean.class; if("byte".equals(name)) return byte.class; if("short".equals(name)) return short.class; if("float".equals(name)) return float.class; if("char".equals(name)) return char.class; if(null == instanceClassLoader){ return Class.forName(name); }else{ return Class.forName(name,true,instanceClassLoader); } } private static Class downgrade(Class c) { if(Integer.class.equals(c)) return int.class; if(Long.class.equals(c)) return long.class; if(Double.class.equals(c)) return double.class; if(Boolean.class.equals(c)) return boolean.class; if(Byte.class.equals(c)) return byte.class; if(Short.class.equals(c)) return short.class; if(Float.class.equals(c)) return float.class; return c; } private static class Args{ public static Map cached = new HashMap(); public Object[] values; public Class[] types; public Args(Seq xs) throws ClassNotFoundException { ArrayList vs = new ArrayList(); ArrayList cs = new ArrayList(); Object x; Argument a; Class c; String cn; while(!xs.isEmpty()){ x = xs.head(); if(x instanceof Argument){ a = (Argument) x; vs.add(a.value); cn = a.className; if(cached.containsKey(cn)){ cs.add(cached.get(cn)); }else{ c = getClassFrom(cn); cached.put(cn,c); cs.add(c); } }else{ vs.add(x); cs.add(downgrade(x.getClass())); } xs = xs.tail(); } values = vs.toArray(); types = cs.toArray(new Class[cs.size()]); } public String toString(){ StringBuffer sb = new StringBuffer("ARGS>\n"); for(int i = 0; i < values.length; ++i){ sb.append("\t").append(types[i].getCanonicalName()).append("=").append(values[i].toString()).append("\n"); } sb.append("