Warning : session_start(): open(/var/lib/php/sessions/sess_mt8baonpp1jsst92nu7550br6t, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning : session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
import java.util.*;
import java.util.zip.*;
import java.util.List;
import java.util.regex.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.table.*;
import java.io.*;
import java.net.*;
import java.lang.reflect.*;
import java.lang.ref.*;
import java.lang.management.*;
import java.security.*;
import java.security.spec.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.imageio.*;
import java.math.*;
public class main {
public static void main(String[] args) throws Exception {
Prolog p = new Prolog();
int n = p.mark();
p.addStatement(" \"Piko\" schreibt man \"Pico\" ");
p.addStatement(" \"Pico\" schreibt man \"Pico\" ");
String x = p.solveAsText(print(" \"Piko\" schreibt man $x "), "x");
assertEqualsVerbose(quote("Pico"), x);
print("ok");
}
static volatile StringBuffer local_log = new StringBuffer(); // not redirected
static volatile StringBuffer print_log = local_log; // might be redirected, e.g. to main bot
// in bytes - will cut to half that
static volatile int print_log_max = 1024*1024;
static volatile int local_log_max = 100*1024;
static void print() {
print("");
}
// slightly overblown signature to return original object...
static A print(A o) {
String s = String.valueOf(o) + "\n";
StringBuffer loc = local_log;
StringBuffer buf = print_log;
int loc_max = print_log_max;
if (buf != loc && buf != null) {
print_append(buf, s, print_log_max);
loc_max = local_log_max;
}
if (loc != null)
print_append(loc, s, loc_max);
System.out.print(s);
return o;
}
static void print(long l) {
print(String.valueOf(l));
}
static void print_append(StringBuffer buf, String s, int max) {
synchronized(buf) {
buf.append(s);
max /= 2;
if (buf.length() > max) try {
int newLength = max/2;
int ofs = buf.length()-newLength;
String newString = buf.substring(ofs);
buf.setLength(0);
buf.append("[...] ").append(newString);
} catch (Exception e) {
buf.setLength(0);
}
}
}
static String quote(String s) {
if (s == null) return "null";
return "\"" + s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\r", "\\r").replace("\n", "\\n") + "\"";
}
static String quote(long l) {
return quote("" + l);
}
static void assertEqualsVerbose(Object x, Object y) {
assertEqualsVerbose(null, x, y);
}
static void assertEqualsVerbose(String msg, Object x, Object y) {
if (!(x == null ? y == null : x.equals(y)))
fail((msg != null ? msg + ": " : "") + structure(x) + " != " + structure(y));
else
print("assertEqualsVerbose OK: " + structure(x));
}
static RuntimeException fail() {
throw new RuntimeException("fail");
}
static RuntimeException fail(Object msg) {
throw new RuntimeException(String.valueOf(msg));
}
static RuntimeException fail(String msg) {
throw new RuntimeException(unnull(msg));
}
static RuntimeException fail(String msg, Object... args) {
throw new RuntimeException(format(msg, args));
}
// "seen" is now default
static String structure(Object o) {
return structure(o, 0, new IdentityHashMap());
}
static String structure_seen(Object o) {
return structure(o, 0, new IdentityHashMap());
}
// leave to false, unless unstructure() breaks
static boolean structure_allowShortening = false;
static String structure(Object o, int stringSizeLimit, IdentityHashMap seen) {
if (o == null) return "null";
// these are never back-referenced (for readability)
if (o instanceof String)
return quote(stringSizeLimit != 0 ? shorten((String) o, stringSizeLimit) : (String) o);
if (o instanceof BigInteger)
return "bigint(" + o + ")";
if (o instanceof Double)
return "d(" + quote(str(o)) + ")";
if (o instanceof Long)
return o + "L";
if (seen != null) {
Integer ref = seen.get(o);
if (ref != null)
return "r" + ref;
seen.put(o, seen.size()+1);
}
String name = o.getClass().getName();
StringBuilder buf = new StringBuilder();
if (o instanceof HashSet)
return "hashset" + structure(new ArrayList((Set) o), stringSizeLimit, seen);
if (o instanceof TreeSet)
return "treeset" + structure(new ArrayList((Set) o), stringSizeLimit, seen);
if (o instanceof Collection) {
for (Object x : (Collection) o) {
if (buf.length() != 0) buf.append(", ");
buf.append(structure(x, stringSizeLimit, seen));
}
return "[" + buf + "]";
}
if (o instanceof Map) {
for (Object e : ((Map) o).entrySet()) {
if (buf.length() != 0) buf.append(", ");
buf.append(structure(((Map.Entry) e).getKey(), stringSizeLimit, seen));
buf.append("=");
buf.append(structure(((Map.Entry) e).getValue(), stringSizeLimit, seen));
}
return (o instanceof HashMap ? "hashmap" : "") + "{" + buf + "}";
}
if (o.getClass().isArray()) {
int n = Array.getLength(o);
for (int i = 0; i < n; i++) {
if (buf.length() != 0) buf.append(", ");
buf.append(structure(Array.get(o, i), stringSizeLimit, seen));
}
return "array{" + buf + "}";
}
if (o instanceof Class)
return "class(" + quote(((Class) o).getName()) + ")";
if (o instanceof Throwable)
return "exception(" + quote(((Throwable) o).getMessage()) + ")";
// Need more cases? This should cover all library classes...
if (name.startsWith("java.") || name.startsWith("javax."))
return String.valueOf(o);
String shortName = o.getClass().getName().replaceAll("^main\\$", "");
if (shortName.equals("Lisp")) {
buf.append("l(" + structure(getOpt(o, "head"), stringSizeLimit, seen));
List args = (List) ( getOpt(o, "args"));
if (nempty(args))
for (int i = 0; i < l(args); i++) {
buf.append(", ");
Object arg = args.get(i);
// sweet shortening
if (arg != null && eq(arg.getClass().getName(), "main$Lisp") && isTrue(call(arg, "isEmpty")))
arg = get(arg, "head");
buf.append(structure(arg, stringSizeLimit, seen));
}
buf.append(")");
return str(buf);
}
int numFields = 0;
String fieldName = "";
if (shortName.equals("DynamicObject")) {
shortName = (String) get(o, "className");
Map fieldValues = (Map) get(o, "fieldValues");
for (String _fieldName : fieldValues.keySet()) {
fieldName = _fieldName;
Object value = fieldValues.get(fieldName);
if (value != null) {
if (buf.length() != 0) buf.append(", ");
buf.append(fieldName + "=" + structure(value, stringSizeLimit, seen));
}
++numFields;
}
} else {
// regular class
// TODO: go to superclasses too
Field[] fields = o.getClass().getDeclaredFields();
for (Field field : fields) {
if ((field.getModifiers() & Modifier.STATIC) != 0)
continue;
fieldName = field.getName();
// skip outer object reference
if (fieldName.indexOf("$") >= 0) continue;
Object value;
try {
field.setAccessible(true);
value = field.get(o);
} catch (Exception e) {
value = "?";
}
// put special cases here...
if (value != null) {
if (buf.length() != 0) buf.append(", ");
buf.append(fieldName + "=" + structure(value, stringSizeLimit, seen));
}
++numFields;
}
}
String b = buf.toString();
if (numFields == 1 && structure_allowShortening)
b = b.replaceAll("^" + fieldName + "=", ""); // drop field name if only one
String s = shortName;
if (buf.length() != 0)
s += "(" + b + ")";
return s;
}
static String unnull(String s) {
return s == null ? "" : s;
}
static List unnull(List l) {
return l == null ? emptyList() : l;
}
static String format(String pat, Object... args) {
return format3(pat, args);
}
static boolean nempty(Collection c) {
return !isEmpty(c);
}
static boolean nempty(String s) {
return !isEmpty(s);
}
static int l(Object[] array) {
return array == null ? 0 : array.length;
}
static int l(byte[] array) {
return array == null ? 0 : array.length;
}
static int l(int[] array) {
return array == null ? 0 : array.length;
}
static int l(char[] array) {
return array == null ? 0 : array.length;
}
static int l(Collection c) {
return c == null ? 0 : c.size();
}
static int l(Map m) {
return m == null ? 0 : m.size();
}
static int l(String s) {
return s == null ? 0 : s.length();
}
static boolean eq(Object a, Object b) {
if (a == null) return b == null;
if (a.equals(b)) return true;
if (a instanceof BigInteger) {
if (b instanceof Integer) return a.equals(BigInteger.valueOf((Integer) b));
if (b instanceof Long) return a.equals(BigInteger.valueOf((Long) b));
}
return false;
}
static String str(Object o) {
return String.valueOf(o);
}
// varargs assignment fixer for a single string array argument
static Object call(Object o, String method, String[] arg) {
return call(o, method, new Object[] {arg});
}
static Object call(Object o, String method, Object... args) {
try {
if (o instanceof Class) {
Method m = call_findStaticMethod((Class) o, method, args, false);
m.setAccessible(true);
return m.invoke(null, args);
} else {
Method m = call_findMethod(o, method, args, false);
m.setAccessible(true);
return m.invoke(o, args);
}
} catch (Exception e) {
throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
}
}
static Method call_findStaticMethod(Class c, String method, Object[] args, boolean debug) {
Class _c = c;
while (c != null) {
for (Method m : c.getDeclaredMethods()) {
if (debug)
System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
if (!m.getName().equals(method)) {
if (debug) System.out.println("Method name mismatch: " + method);
continue;
}
if ((m.getModifiers() & Modifier.STATIC) == 0 || !call_checkArgs(m, args, debug))
continue;
return m;
}
c = c.getSuperclass();
}
throw new RuntimeException("Method '" + method + "' (static) with " + args.length + " parameter(s) not found in " + _c.getName());
}
static Method call_findMethod(Object o, String method, Object[] args, boolean debug) {
Class c = o.getClass();
while (c != null) {
for (Method m : c.getDeclaredMethods()) {
if (debug)
System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
if (m.getName().equals(method) && call_checkArgs(m, args, debug))
return m;
}
c = c.getSuperclass();
}
throw new RuntimeException("Method '" + method + "' (non-static) with " + args.length + " parameter(s) not found in " + o.getClass().getName());
}
private static boolean call_checkArgs(Method m, Object[] args, boolean debug) {
Class>[] types = m.getParameterTypes();
if (types.length != args.length) {
if (debug)
System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
return false;
}
for (int i = 0; i < types.length; i++)
if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
if (debug)
System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
return false;
}
return true;
}
static boolean isTrue(Object o) {
return booleanValue(o);
}
static String shorten(String s, int max) {
if (s == null) return "";
return s.length() <= max ? s : s.substring(0, Math.min(s.length(), max)) + "...";
}
static Object getOpt(Object o, String field) {
if (o instanceof String) o = getBot ((String) o);
if (o == null) return null;
if (o instanceof Class) return getOpt((Class) o, field);
if (o.getClass().getName().equals("main$DynamicObject"))
return call(getOpt_raw(o, "fieldValues"), "get", field);
if (o instanceof Map) return ((Map) o).get(field);
return getOpt_raw(o, field);
}
static Object getOpt_raw(Object o, String field) {
try {
Field f = getOpt_findField(o.getClass(), field);
if (f == null) return null;
f.setAccessible(true);
return f.get(o);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static Object getOpt(Class c, String field) {
try {
Field f = getOpt_findStaticField(c, field);
if (f == null) return null;
f.setAccessible(true);
return f.get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static Field getOpt_findStaticField(Class> c, String field) {
Class _c = c;
do {
for (Field f : _c.getDeclaredFields())
if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0)
return f;
_c = _c.getSuperclass();
} while (_c != null);
return null;
}
static Field getOpt_findField(Class> c, String field) {
Class _c = c;
do {
for (Field f : _c.getDeclaredFields())
if (f.getName().equals(field))
return f;
_c = _c.getSuperclass();
} while (_c != null);
return null;
}
// get purpose 1: access a list/array (safer version of x.get(y))
static A get(List l, int idx) {
return idx >= 0 && idx < l(l) ? l.get(idx) : null;
}
static A get(A[] l, int idx) {
return idx >= 0 && idx < l(l) ? l[idx] : null;
}
// get purpose 2: access a field by reflection or a map
static Object get(Object o, String field) {
if (o instanceof Class) return get((Class) o, field);
if (o instanceof Map)
return ((Map) o).get(field);
if (o.getClass().getName().equals("main$DynamicObject"))
return call(get_raw(o, "fieldValues"), "get", field);
return get_raw(o, field);
}
static Object get_raw(Object o, String field) {
try {
Field f = get_findField(o.getClass(), field);
f.setAccessible(true);
return f.get(o);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static Object get(Class c, String field) {
try {
Field f = get_findStaticField(c, field);
f.setAccessible(true);
return f.get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static Field get_findStaticField(Class> c, String field) {
Class _c = c;
do {
for (Field f : _c.getDeclaredFields())
if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0)
return f;
_c = _c.getSuperclass();
} while (_c != null);
throw new RuntimeException("Static field '" + field + "' not found in " + c.getName());
}
static Field get_findField(Class> c, String field) {
Class _c = c;
do {
for (Field f : _c.getDeclaredFields())
if (f.getName().equals(field))
return f;
_c = _c.getSuperclass();
} while (_c != null);
throw new RuntimeException("Field '" + field + "' not found in " + c.getName());
}
// extended over Class.isInstance() to handle primitive types
static boolean isInstanceX(Class type, Object arg) {
if (type == boolean.class) return arg instanceof Boolean;
if (type == int.class) return arg instanceof Integer;
if (type == long.class) return arg instanceof Long;
if (type == float.class) return arg instanceof Float;
if (type == short.class) return arg instanceof Short;
if (type == char.class) return arg instanceof Character;
if (type == byte.class) return arg instanceof Byte;
if (type == double.class) return arg instanceof Double;
return type.isInstance(arg);
}
static String format3(String pat, Object... args) {
if (args.length == 0) return pat;
List tok = javaTokPlusPeriod(pat);
int argidx = 0;
for (int i = 1; i < tok.size(); i += 2)
if (tok.get(i).equals("*"))
tok.set(i, format3_formatArg(argidx < args.length ? args[argidx++] : "null"));
return join(tok);
}
static String format3_formatArg(Object arg) {
if (arg == null) return "null";
if (arg instanceof String) {
String s = (String) arg;
return isIdentifier(s) || isNonNegativeInteger(s) ? s : quote(s);
}
if (arg instanceof Integer || arg instanceof Long) return String.valueOf(arg);
return quote(structure(arg));
}
static boolean isEmpty(Collection c) {
return c == null || c.isEmpty();
}
static boolean isEmpty(String s) {
return s == null || s.length() == 0;
}
static List emptyList() {
return Collections.emptyList();
}
static Object getBot(String botID) {
return call(getMainBot(), "getBot", botID);
}
static boolean booleanValue(Object o) {
return eq(true, o);
}
static boolean isIdentifier(String s) {
return isJavaIdentifier(s);
}
static Object mainBot;
static Object getMainBot() {
return mainBot;
}
public static String join(String glue, Iterable strings) {
StringBuilder buf = new StringBuilder();
Iterator i = strings.iterator();
if (i.hasNext()) {
buf.append(i.next());
while (i.hasNext())
buf.append(glue).append(i.next());
}
return buf.toString();
}
public static String join(String glue, String[] strings) {
return join(glue, Arrays.asList(strings));
}
public static String join(Iterable strings) {
return join("", strings);
}
public static String join(String[] strings) {
return join("", strings);
}
static boolean isNonNegativeInteger(String s) {
return s != null && Pattern.matches("\\d+", s);
}
// This is made for NL parsing.
// It's javaTok extended with "..." token, "$n" and "#n" and
// special quotes (which are converted to normal ones).
static List javaTokPlusPeriod(String s) {
List tok = new ArrayList();
int l = s.length();
int i = 0;
while (i < l) {
int j = i;
char c; String cc;
// scan for whitespace
while (j < l) {
c = s.charAt(j);
cc = s.substring(j, Math.min(j+2, l));
if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
++j;
else if (cc.equals("/*")) {
do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/"));
j = Math.min(j+2, l);
} else if (cc.equals("//")) {
do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0);
} else
break;
}
tok.add(s.substring(i, j));
i = j;
if (i >= l) break;
c = s.charAt(i);
cc = s.substring(i, Math.min(i+2, l));
// scan for non-whitespace
if (c == '\u201C' || c == '\u201D') c = '"'; // normalize quotes
if (c == '\'' || c == '"') {
char opener = c;
++j;
while (j < l) {
char _c = s.charAt(j);
if (_c == '\u201C' || _c == '\u201D') _c = '"'; // normalize quotes
if (_c == opener) {
++j;
break;
} else if (s.charAt(j) == '\\' && j+1 < l)
j += 2;
else
++j;
}
if (j-1 >= i+1) {
tok.add(opener + s.substring(i+1, j-1) + opener);
i = j;
continue;
}
} else if (Character.isJavaIdentifierStart(c))
do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || s.charAt(j) == '\'')); // for things like "this one's"
else if (Character.isDigit(c))
do ++j; while (j < l && Character.isDigit(s.charAt(j)));
else if (cc.equals("[[")) {
do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]"));
j = Math.min(j+2, l);
} else if (cc.equals("[=") && i+2 < l && s.charAt(i+2) == '[') {
do ++j; while (j+2 < l && !s.substring(j, j+3).equals("]=]"));
j = Math.min(j+3, l);
} else if (s.substring(j, Math.min(j+3, l)).equals("..."))
j += 3;
else if (c == '$' || c == '#')
do ++j; while (j < l && Character.isDigit(s.charAt(j)));
else
++j;
tok.add(s.substring(i, j));
i = j;
}
if ((tok.size() % 2) == 0) tok.add("");
return tok;
}
static boolean isJavaIdentifier(String s) {
if (s.length() == 0 || !Character.isJavaIdentifierStart(s.charAt(0)))
return false;
for (int i = 1; i < s.length(); i++)
if (!Character.isJavaIdentifierPart(s.charAt(i)))
return false;
return true;
}
static final class Prolog {
boolean upperCaseVariables = false; // true for SNL, false for NL
long varCount;
boolean showStuff;
List stack = new ArrayList();
Trail sofar = null;
List program = new ArrayList();
List> programByArity = new ArrayList>();
long steps;
List log;
int maxLogSize = 1000; // lines
int maxLevel = 10000; // max stack size
int timeLimit = 20; // 20 seconds
long startTime;
boolean allowEval = true;
// name of theory to memorize stuff to
String outTheory;
// stats
int maxLevelSeen; // maximum stack level reached during computation
long topUnifications, exceptions;
static interface Native {
public boolean yo(Prolog p, Lisp term);
}
boolean headeq(String a, String b) {
return isQuoted (a) ? eq(a, b) : eqic(a, b);
}
static class Var extends Lisp {
long id;
Lisp instance;
Var(String name) {
super(name);
instance = this;
}
Var(long id) {
super("___");
this.id = id;
instance = this;
}
void reset() { instance = this; }
public String toString() {
if (instance != this)
return instance.toString();
return prettyName();
}
String prettyName() {
return isUserVar() ? getName() : "_" + id;
}
boolean isUserVar() {
return id == 0;
}
String getName() {
return head;
}
Lisp getValue() {
Lisp l = instance;
while (l instanceof Var) {
Var v = (Var) ( l);
if (v.instance == v)
return v;
l = v.instance;
}
return l;
}
}
static class Collector extends Lisp {
List solutions = new ArrayList();
Collector() { super("___"); }
public String toString() {
return "";
}
}
static class Clause {
Lisp head;
String loadedFrom; // theory name
boolean used;
// Either body or nat will be set (or none)
Native nat;
Goal body;
Clause(Lisp head, Native nat) {
this.nat = nat;
this.head = head;}
Clause(Lisp head, Goal body) {
this.body = body;
this.head = head;}
Clause(Lisp head, Goal body, Native nat) {
this.nat = nat;
this.body = body;
this.head = head;}
Clause(Lisp head) {
this.head = head;}
Clause copy(Prolog p) {
return new Clause(p.copy(head), body == null ? null : body.copy(p), nat);
}
public String toString() {
//ret head + " :- " + (body == null ? "true" : body);
return nat != null ? head + " :- native" :
(body == null ? head.toString() : head + " :- " + body);
}
}
class Trail {
Var tcar;
Trail tcdr;
Trail(Var tcar, Trail tcdr) {
this.tcdr = tcdr;
this.tcar = tcar;}
}
Trail Trail_Note() { return sofar; }
void Trail_Set(Var x, Lisp value) {
sofar = new Trail(x, sofar);
x.instance = value;
/*if (showStuff)
log(indent(level()) + " Push " + x.prettyName() + " (" + x + ")");*/
}
void Trail_Undo(Trail whereto) {
for (; sofar != whereto; sofar = sofar.tcdr) {
/*if (showStuff)
log(indent(level()) + " Resetting variable " + sofar.tcar.prettyName() + " (" + sofar.tcar + ")");*/
sofar.tcar.reset();
}
}
static class TermVarMapping {
List vars = new ArrayList();
TermVarMapping(List vars) {
this.vars = vars;}
TermVarMapping(Var... vars) { this.vars.addAll(asList(vars)); }
void showanswer() {
print("TRUE.");
for (Var v : vars)
print(" " + v.prettyName() + " = " + v);
}
}
static class Goal {
Lisp car;
Goal cdr;
Goal(Lisp car, Goal cdr) {
this.cdr = cdr;
this.car = car;}
Goal(Lisp car) {
this.car = car;}
public String toString() {
return cdr == null ? car.toString() : car + "; " + cdr;
}
Goal copy(Prolog p) {
return new Goal(p.copy(car),
cdr == null ? null : cdr.copy(p));
}
Goal append(Goal l) {
return new Goal(car, cdr == null ? l : cdr.append(l));
}
} // class Goal
boolean unifyOrRollback(Lisp a, Lisp b) {
Trail t = Trail_Note();
if (unify(a, b)) return true;
Trail_Undo(t);
return false;
}
boolean unify(Lisp thiz, Lisp t) {
if (thiz == null) fail("thiz=null");
if (t == null) fail("t=null");
if (thiz instanceof Var) { // TermVar::unify
Var v = (Var) ( thiz);
if (v.instance != v)
return unify(v.instance, t);
Trail_Set(v, t);
return true;
}
// TermCons::unify
return unify2(t, thiz);
}
boolean unify2(Lisp thiz, Lisp t) {
if (thiz instanceof Var)
return unify(thiz, t);
int arity = thiz.size();
if (!headeq(thiz.head, t.head) || arity != t.size())
return false;
for (int i = 0; i < arity; i++)
if (!unify(thiz.get(i), t.get(i)))
return false;
return true;
}
Lisp copy(Lisp thiz) {
if (thiz instanceof Var) {
Var v = (Var) ( thiz);
if (v.instance == v) {
Trail_Set(v, newVar());
}
return v.instance;
}
// copy2 (copy non-var)
Lisp l = new Lisp(thiz.head);
for (Lisp arg : thiz)
l.add(copy(arg));
return l;
}
Var newVar() {
return new Var(++varCount);
}
Var newVar(String name) {
return new Var(name);
}
Clause clause(Lisp head, Goal body) {
return prologify(new Clause(head, body));
}
// primary processor for freshly parsed rules
Clause clause(Lisp rule) {
List ops = snlSplitOps(rule);
/*if (showStuff)
log("clause(Lisp): " + rule + " => " + structure(ops)); */
// expand rule shortcuts (say/memorize/...)
for (int i = 0; i < l(ops)-1; i++)
if (ops.get(i).is("memorize *"))
ops.set(i, lisp("and *", lisp("[]", "memorize", ops.get(i))));
if (!empty(ops) && last(ops).is("say *"))
ops.set(l(ops)-1, lisp("then *", lisp("[]", "say", last(ops).get(0))));
rule = empty(ops) ? rule : snlJoinOps(ops);
if (allowEval)
rule = new EvalTransform().evalTransformRule(rule);
ops = snlSplitOps(rule);
if (!empty(ops) && last(ops).is("then *")) {
Lisp head = last(ops).get(0);
Goal goal = null;
// TODO: check the actual words (if/and/...)
for (int i = l(ops)-2; i >= 0; i--)
goal = new Goal(ops.get(i).get(0), goal);
return clause(head, goal);
} else
return clause(rule, (Lisp) null);
}
Clause clause(Lisp head, Lisp body) {
return clause(head, body == null ? null : new Goal(body));
}
Lisp prologify(Lisp term) {
return prologify(term, new HashMap());
}
Clause prologify(Clause c) {
HashMap vars = new HashMap();
c = new Clause(
prologify(c.head, vars),
prologify(c.body, vars),
c.nat);
/*if (showStuff)
log("Clause made: " + structure_seen(c));*/
return c;
}
Goal prologify(Goal goal, Map vars) {
if (goal == null) return null;
return new Goal(
prologify(goal.car, vars),
prologify(goal.cdr, vars));
}
// Note: only for un-prologified
boolean isVar(Lisp term) {
return upperCaseVariables ? snlIsVar(term) : nlIsVar(term);
}
// for prologified (e.g. in native clauses)
boolean isLiveVar(Lisp term) {
return term instanceof Var;
}
Lisp prologify(Lisp term, Map vars) {
if (term == null) return null;
if (isVar(term)) {
Var v = vars.get(term.head);
if (v == null)
vars.put(term.head, v = newVar(term.head));
return v;
} else {
Lisp l = new Lisp(term.head);
for (Lisp arg : term)
l.add(prologify(arg, vars));
return l;
}
}
List findVars(Goal g) {
IdentityHashMap map = new IdentityHashMap();
while (g != null) {
findVars(g.car, map);
g = g.cdr;
}
return asList(map.keySet());
}
List findVars(Lisp term) {
IdentityHashMap map = new IdentityHashMap();
findVars(term, map);
return asList(map.keySet());
}
void findVars(Lisp term, IdentityHashMap map) {
if (term instanceof Var)
map.put((Var) term, Boolean.TRUE);
else
for (Lisp arg : term)
findVars(arg, map);
}
static Map makeVarMap(List vars) {
HashMap map = new HashMap();
for (Var v : vars)
map.put(v.getName(), v);
return map;
}
List emptyProgram = new ArrayList();
// Executor stack entry
class Entry {
Goal goal;
List program; // full program or filtered by arity
int programIdx = 0;
Trail trail;
boolean trailSet;
Entry(Goal goal) {
this.goal = goal;
Lisp car = resolve1(goal.car);
if (car instanceof Var) {
if (showStuff)
log("Weird: Goal is variable: " + car);
program = Prolog.this.program;
} else {
int arity = car.size();
if (showStuff)
log(indent(level()) + "Goal arity " + arity + ": " + render(car));
program = arity >= l(programByArity) ? emptyProgram : programByArity.get(arity);
}
}
}
void start(String goal) {
start(nlParse(goal));
}
void start(Lisp goal) {
start(new Goal(prologify(goal)));
}
String render(Lisp term) {
return nlUnparse(resolve(term));
}
String render(Goal goal) {
return goal == null ? "-" : render(goal.car);
}
String render(Clause c) {
return c == null ? "-" : render(c.head);
}
// warning: doesn't prologify the goal
void start(Goal goal) {
if (showStuff)
log("Starting on goal: " + render(goal));
steps = 0;
stack = new ArrayList();
Trail_Undo(null);
stackAdd(new Entry(goal));
startTime = now();
}
void log(String s) {
if (log != null) {
if (l(log) == maxLogSize) {
log.add("[Log overflow]");
showStuff = false;
}
else if (l(log) < maxLogSize)
log.add(s);
} else if (showStuff)
print("prolog: " + s);
}
int level() {
return l(stack)-1;
}
boolean done() {
boolean result = empty(stack);
/*if (showStuff && result)
log("Done with goal!");*/
return result;
}
boolean gnext(Goal g) {
Goal gdash = g.cdr;
if (gdash == null) {
if (showStuff)
log("gnext true");
return true;
} else {
stackAdd(new Entry(gdash));
return false;
}
}
void stackPop() {
Entry e = popLast(stack);
if (e.trailSet)
Trail_Undo(e.trail);
}
void backToCutPoint(int n) {
if (showStuff)
log("back to cut point " + n);
while (l(stack) >= n) {
if (showStuff)
log("cut: dropping " + structure(last(stack).goal));
stackPop();
}
}
boolean step() {
if (done()) fail("done!"); // safety
if (now() >= startTime+timeLimit*1000)
fail("time limit reached: " + timeLimit + " s");
++steps;
Entry e = last(stack);
if (e.trailSet) {
Trail_Undo(e.trail);
e.trailSet = false;
}
e.trail = Trail_Note();
e.trailSet = true;
// cut operator - suceeds first time
if (e.goal.car.is("!", 1)) {
if (showStuff)
log("cut " + e.programIdx + ". " + structure(e.goal));
if (e.programIdx == -1) {
++e.programIdx;
return gnext(e.goal);
} else if (e.programIdx == 0) {
++e.programIdx;
// fails 2nd time and cuts
//e.goal.car.head = "false"; // super-hack :D
backToCutPoint(parseInt(e.goal.car.get(0).raw()));
return false;
} else {
stackPop();
return false;
}
}
if (e.programIdx >= l(e.program)) { // program loop ends
removeLast(stack);
return false;
}
// now in program loop - get next clause to try
Clause cc = e.program.get(e.programIdx);
++e.programIdx;
// copy the clause; synchronize on it because it may come
// from a shared Prolog interpreter
Clause c;
synchronized(cc) {
c = cc.copy(this);
Trail_Undo(e.trail);
}
String text = null;
if (showStuff)
//text = " Goal: " + e.goal + ". Got clause: " + c;
text = "Got clause: " + render(c);
++topUnifications;
if (unify(e.goal.car, c.head)) {
cc.used = true; // mark clause used
if (showStuff) {
log(indent(level()) + text);
log(indent(level()) + " Clause unifies to: " + render(c));
}
Goal gdash;
if (c.nat != null) {
if (showStuff)
log(indent(level()) + " Clause is native.");
if (!c.nat.yo(this, c.head)) {
if (showStuff)
log(indent(level()) + "Native clause fails");
return false;
}
gdash = e.goal.cdr;
} else
gdash = c.body == null ? e.goal.cdr : resolveCut(c.body).append(e.goal.cdr);
if (showStuff)
log(indent(level()) + " gdash: " + render(gdash));
if (gdash == null) {
if (showStuff)
log("SUCCESS!");
return true;
} else {
Entry e2 = new Entry(gdash);
/*if (showStuff)
log(indent(level()) + "New goal: " + render(gdash));*/
stackAdd(e2);
return false;
}
} /*else
if (showStuff)
log(indent(level()) + "No match for clause.");*/
return false;
}
// resolve cut in clause body
Goal resolveCut(Goal g) {
if (g.car.is("!", 0))
return fixCut(g);
if (g.cdr == null)
return g;
return new Goal(g.car, resolveCut(g.cdr));
}
// note stack length in cut
Goal fixCut(Goal g) {
return new Goal(lisp("!", lstr(stack)), g.cdr); // max. one cut per clause
}
void stackAdd(Entry e) {
stack.add(e);
int n = l(stack);
if (n > maxLevel) fail("Maximum stack level reached: " + n);
if (n > maxLevelSeen) maxLevelSeen = n;
}
void addClause(String text) {
addClause(nlParse(text));
}
// synonym of addClause
void addStatement(String text) {
addClause(text);
}
void addClause(Lisp c) {
addClause(clause(c));
}
void addClause(Lisp c, String name) {
addClause(clause(c), name);
}
void addClause(Clause c) {
addClause(c, null);
}
void addClause(Clause c, String name) {
c.loadedFrom = name;
program.add(c);
if (c.head instanceof Var) {
if (showStuff)
log("WARNING: Clause is variable, will not be executed right now: " + c);
return;
}
int arity = c.head.size();
growArityList(arity);
programByArity.get(arity).add(c);
}
void growArityList(int arity) {
while (arity >= l(programByArity))
programByArity.add(new ArrayList());
}
boolean canSolve(Lisp goal) {
return canSolve(new Goal(prologify(goal)));
}
boolean canSolve(String goal) {
return canSolve(nlParse(goal));
}
boolean canSolve(Goal goal) {
start(goal);
while (!done())
if (step())
return true;
return false;
}
String solveAsText(String goal, String var) {
Map solution = solve(goal);
return solution == null ? null : nlUnparse(solution.get(addPrefix("$", var)));
}
// return variable map or null if unsolved
Map solve(Lisp goal) {
start(goal);
return nextSolution();
}
Map solve(String text) {
return solve(nlParse(text));
}
Map getUserVarMap() {
Goal g = stack.get(0).goal;
if (showStuff)
log("UserVarMap goal: " + g);
HashMap map = new HashMap();
for (Var v : findVars(g))
if (v.isUserVar()) {
Lisp term = resolve(v);
boolean ok = !(term instanceof Var) || (term != v && ((Var) term).isUserVar());
if (showStuff)
log("UserVarMap var " + v + " ok: " + ok);
if (ok)
map.put(v.getName(), term);
}
return map;
}
Map nextSolution() {
if (showStuff)
log("nextSolution");
int n = 0;
while (!done()) {
++n;
if (step()) {
if (showStuff)
log(" solution found in step " + n);
return getUserVarMap();
}
}
if (showStuff)
log("\nNo solution");
return null;
}
void addTheory(String text, String name) {
for (Clause c : parseProgram(text))
addClause(c, name);
}
void addTheory(String text, Lisp tree) {
addTheory(text, tree, null);
}
void addTheory(String text, Lisp tree, String name) {
for (Clause c : parseProgram(text, tree))
addClause(c, name);
}
List parseProgram(String text) {
return parseProgram(text, nlParse(text));
}
List parseProgram(String text, Lisp tree) {
List l = new ArrayList();
if (nlIsMultipleStatements(text))
for (Lisp part : tree)
l.add(clause(part));
else
l.add(clause(tree));
return l;
}
// Resolve top-level only
Lisp resolve1(Lisp term) {
if (term instanceof Var)
return ((Var) term).getValue();
return term;
}
// resolve all variables
Lisp resolve(Lisp term) {
term = resolve1(term);
// smart recurse
for (int i = 0; i < term.size(); i++) {
Lisp l = term.get(i);
Lisp r = resolve(l);
if (l != r) {
Lisp x = new Lisp(term.head);
for (int j = 0; j < i; j++)
x.add(term.get(j));
x.add(r);
for (i++; i < term.size(); i++)
x.add(resolve(term.get(i)));
return x;
}
}
return term;
}
// looks for a bodyless rule in the program that matches the term
// todo: eqic
boolean containsStatement(Lisp term) {
for (Clause c : program)
if (c.body == null && c.nat == null && eq(c.head, term))
return true;
return false;
}
// closed == contains no variables
boolean isClosedTerm(Lisp term) {
if (term instanceof Var)
return false;
else
for (Lisp arg : term)
if (!isClosedTerm(arg))
return false;
return true;
}
void addNative(BaseNative n) {
addClause(prologify(new Clause(nlParse(n.pat), n)), "nat");
}
void addNatives(BaseNative... l) {
for (BaseNative n : l)
addNative(n);
}
void addNatives(List l) {
for (BaseNative n : l)
addNative(n);
}
List getStackTerms() {
List l = new ArrayList();
for (Entry e : stack)
l.add(e.goal.car);
return l;
}
void logOn() {
log = new ArrayList();
showStuff = true;
}
static abstract class BaseNative implements Native {
String pat;
Prolog p;
Map m;
BaseNative(String pat) {
this.pat = pat;}
abstract boolean yo();
public boolean yo(Prolog p, Lisp term) {
m = new HashMap();
this.p = p;
try {
// Terms are always resolved for native code!
return nlMatch(pat, p.resolve(term), m) && yo();
} catch (Exception e) {
++p.exceptions;
if (p.showStuff)
p.log("Exception in native class " + getClass().getName() + ": " + getStackTrace(e));
return false;
} finally {
this.p = null;
}
}
boolean unify(String var, Lisp term) {
return set(var, term);
}
boolean set(String var, Lisp term) {
Lisp v = m.get(var);
if (v == null)
fail("Variable " + var + " not found");
return p.unify(v, term);
}
boolean unifyForeign(IdentityHashMap varMap, Map solution) {
if (p.showStuff)
p.log("unifyForeign with: " + structure(solution));
for (Lisp v : varMap.keySet()) {
String name = varMap.get(v);
Lisp value = solution.get(name);
if (value == null) continue;
if (!p.unify(v, escapeVariables(value))) {
return false; // rollback?
}
}
return true;
}
// fills varMap
Lisp exportVars(Lisp tree, IdentityHashMap varMap) {
if (tree instanceof Var) {
String name = varMap.get(tree);
if (name == null) {
name = "$_" + (varMap.size()+1);
varMap.put(tree, name);
}
return lisp(name);
}
Lisp lisp = new Lisp(tree.head);
for (Lisp sub : tree)
lisp.add(exportVars(sub, varMap));
return lisp;
}
// var = without $
Lisp get(String var) {
return m.get(var);
}
String unq(String var) {
return m.get(var).unq();
}
} // BaseNative
// Prolog constructor
Prolog() {
addClause(lisp("true"));
addBaseNatives();
}
static class State {
Trail trail;
List stack;
}
State saveState() {
State s = new State();
s.trail = sofar;
s.stack = stack;
sofar = null;
return s;
}
void restoreState(State s) {
sofar = s.trail;
stack = s.stack;
}
// auto-rewrite with all clauses in DB
List rewrite() {
return rewriteWith(program);
}
List rewriteWith(List rewriteTheory) {
List result = new ArrayList();
for (Prolog.Clause clause : rewriteTheory) {
if (clause.body == null) continue; // need conditions to rewrite anything
// usual safe clause copying
Trail t = Trail_Note();
Clause c;
synchronized(clause) {
c = clause.copy(this);
Trail_Undo(t);
}
State state = saveState();
try {
start(c.body);
while (!done()) {
if (step()) {
Lisp term = resolve(c.head);
if (!isClosedTerm(term)) {
if (showStuff)
log("Not a closed term, skipping: " + term);
continue;
}
if (containsStatement(term)) {
if (showStuff)
log("Statement exists, skipping: " + term);
continue;
}
if (result.contains(term))
continue;
if (showStuff)
log("Found new statement: " + term);
result.add(term);
}
}
} finally {
restoreState(state);
}
}
return result;
}
static class NewCollector extends BaseNative {
NewCollector() { super("$x = new collector"); }
boolean yo() {
return set("x", new Collector());
}
}
class Save extends BaseNative {
Save() { super("saveTo($x, $collector)"); }
boolean yo() {
Collector collector = (Collector) get("collector");
Lisp x = get("x");
collector.solutions.add(resolve(x));
return true;
}
}
static class Retrieve extends BaseNative {
Retrieve() { super("$x = retrieve($collector)"); }
boolean yo() {
print("Retrieve: collector = " + get("collector"));
Collector collector = (Collector) get("collector");
return set("x", nlList(collector.solutions));
}
}
void addBaseNatives() {
addNatives(new NewCollector(), new Save(), new Retrieve());
}
static Lisp escapeVariables(Lisp tree) {
if (tree instanceof Var) {
String name = ((Var) tree).prettyName();
return lisp("[]", "var", name);
}
Lisp lisp = new Lisp(tree.head);
for (Lisp sub : tree)
lisp.add(escapeVariables(sub));
return lisp;
}
boolean addClauseIfNew(Lisp clause, String name) {
if (containsStatement(clause)) return false;
addClause(clause, name);
return true;
}
List getUsedClauses() {
List l = new ArrayList();
for (Clause c : program)
if (c.used)
l.add(c);
return l;
}
List getUsedTheories() {
TreeSet theories = new TreeSet();
for (Clause c : getUsedClauses())
theories.add(or(c.loadedFrom, "?"));
return asList(theories);
}
void think(Lisp x) {
addClauseIfNew(x, "thought"); // TODO: vars and stuff?
}
List getMemorizedStatements() {
List l = new ArrayList();
for (Clause c : program)
if (c.body == null && c.nat == null && c.head.is("[]", 2) && c.head.get(0).is("memorized"))
l.add(c.head);
return l;
}
String showClause(Clause c) {
return nlUnparse(c.head) + (c.body == null ? "" : " :- ...");
}
String showProgram() {
List l = new ArrayList();
for (Clause c : program)
l.add(showClause(c));
return n(l(program), "statement") + " " + slackSnippet(joinLines(l ));
}
void copyProgramFrom(Prolog p) {
program.addAll(p.program);
growArityList(l(p.programByArity));
for (int i = 0; i < l(p.programByArity); i++)
programByArity.get(i).addAll(p.programByArity.get(i));
}
void addProgram(List clauses, String loadedFrom) {
for (Clause c : clauses)
addClause(c, loadedFrom);
}
void addClauses(String text) {
addTheory(text, "ad hoc");
}
List statementsAsText() {
return statementsAsText(0);
}
List statementsAsText(int startingFromIndex) {
List l = new ArrayList();
for (int i = startingFromIndex; i < l(program); i++) {
Clause c = program.get(i);
if (c.nat == null) {
if (c.body != null)
l.add("[if ? then " + nlUnparse(c.head, false) + "]"); // TODO
else
l.add(nlUnparse(c.head, false));
}
}
return l;
}
int mark() {
return l(program);
}
} // class Prolog
// class for linking Java methods to NL
static class Native {
Lisp snl;
SNLMatches m;
SNLMatches trans;
boolean printExceptions;
int exceptions;
List ins, strings;
Native() {} // good to have
Native(Lisp snl) {
this.snl = snl;}
// assume it's a Lisp structure from another bot -
// restructure to import it
/* *(O o) {
snl = (Lisp) restructure(o);
}*/
Native(String s) { snl = snlToTree(s); }
boolean match(String pat) {
m = new SNLMatches();
trans = new SNLMatches();
Lisp tree = snlToTree_cached(pat);
ins = new ArrayList();
strings = new ArrayList();
tree = findInVariables(tree, ins, strings);
//print("tree now: " + tree);
if (!snlMatch2(tree, snl, trans))
return false;
for (String in : ins)
if (get(in) == null)
return false;
for (String string : strings)
if (get(string) == null || !get(string).isEmpty())
return false;
return true;
}
Lisp findInVariables(Lisp tree, List ins, List strings) {
if (tree == null) return tree;
if (tree.isEmpty()) {
if (tree.head.startsWith("in S ")) {
String var = dropPrefix("in S ", tree.head);
if (startsWithUpperCase(var)) {
if (!strings.contains(var))
strings.add(var);
return lisp(var);
}
}
if (tree.head.startsWith("in ")) {
String var = dropPrefix("in ", tree.head);
if (startsWithUpperCase(var)) {
if (!ins.contains(var))
ins.add(var);
return lisp(var);
}
}
return tree;
} else {
// recurse
Lisp lisp = new Lisp(tree.head);
for (Lisp child : tree)
lisp.add(findInVariables(child, ins, strings));
return lisp;
}
}
Lisp get(String var) {
Lisp val = trans.get(var);
if (!isVar(val)) return val;
return getOuter(val.raw());
}
// if only one in var
Lisp get() {
return get(anyInVar());
}
// ditto
String str() {
return str(anyInVar());
}
String anyInVar() {
if (!empty(ins)) return first(ins);
return first(strings);
}
boolean isVar(Lisp l) {
return l != null && l.isEmpty() && startsWithUpperCase(l.raw());
}
// called from outside native
Lisp getOuter(String var) {
return m.get(var);
}
// called from inside native
String str(String var) {
Lisp val = get(var);
if (val == null) fail("variable * not set", var);
if (!val.isEmpty()) fail("variable * not a string: *", var, val);
return unquote(val.raw());
}
String strOuter(String var) {
Lisp val = getOuter(var);
if (val == null) fail("variable * not set", var);
if (!val.isEmpty()) fail("variable * not a string: *", var, val);
return unquote(val.raw());
}
// may throw an exception if variable problem (already set to different value)
void set(String var, Object val) {
if (val instanceof List) val = structure(val); // TODO
Lisp lisp = val instanceof Lisp ? (Lisp) val : lisp(quote(main.str(val)));
Lisp outer = trans.get(var);
if (isVar(outer)) {
if (!m.put(trans.raw(var), lisp))
fail();
} else
if (!eq(lisp, outer)) {
//print("neq " + lisp + " != " + outer);
fail();
}
}
boolean callExternal(Object bot) {
try {
Object copy = newObject(main.getClass(bot, "main$Native"), quickExport(snl, bot));
//O copy = quickExport(this, bot);
if (!isTrue(callOpt(bot, "yo", copy)))
return false;
m = (SNLMatches) quickImport(main.get(copy, "m"));
return true;
} catch (Exception e) {
handle(e);
return false;
}
}
boolean callExternal(Method yo) {
try {
Class c = yo.getDeclaringClass();
//print("Declaring class: " + c + " (" + identityHashCode(c) + ")");
Object copy = newObject(main.getClass(c, "main$Native"), quickExport(snl, c));
if (!isTrue(yo.invoke(null, copy)))
return false;
m = (SNLMatches) quickImport(main.get(copy, "m"));
return true;
} catch (Exception e) {
handle(e);
return false;
}
}
void handle(Throwable t) {
if (printExceptions)
printStackTrace(t);
++exceptions;
}
}
// a map of assignments that can be rolled back to some state
static class SNLMatches {
HashMap map = new HashMap();
List log = new ArrayList();
boolean put(String key, Lisp val) {
if (map.containsKey(key))
// existing
return eq(map.get(key), val);
// new
map.put(key, val);
log.add(key);
return true;
}
Lisp get(String key) {
return map.get(key);
}
String raw(String key) {
Lisp x = get(key);
if (x == null) fail("var not set: " + key);
return x.raw();
}
boolean containsKey(String key) {
return map.containsKey(key);
}
int save() {
return l(log);
}
void restore(int i) {
while (l(log) > i) {
String key = log.get(l(log)-1);
map.remove(key);
log.remove(l(log)-1);
}
}
boolean addAll(SNLMatches m) {
for (String var : m.keySet())
if (!put(var, m.get(var)))
return false;
return true;
}
Set keySet() {
return map.keySet();
}
}
// a Lisp-like form
static class Lisp implements Iterable {
String head;
List args = new ArrayList();
Object more; // additional info, user-defined
Lisp() {}
Lisp(String head) {
this.head = head;}
Lisp(String head, Lisp... args) {
this.head = head;
this.args.addAll(asList(args));
}
Lisp(String head, List args) {
this.head = head;
for (Object arg : args) add(arg);
}
// INEFFICIENT
public String toString() {
if (args.isEmpty())
return quoteIfNotIdentifier(head);
List bla = new ArrayList();
for (Lisp a : args)
bla.add(a.toString());
String inner = join(", ", bla);
if (head.equals(""))
return "{" + inner + "}"; // list
else
return quoteIfNotIdentifier(head) + "(" + inner + ")";
}
String raw() {
if (!isEmpty ()) fail("not raw: " + this);
return head;
}
Lisp add(Lisp l) {
args.add(l);
return this;
}
Lisp add(String s) {
args.add(new Lisp(s));
return this;
}
Lisp add(Object o) {
if (o instanceof Lisp) add((Lisp) o);
else if (o instanceof String) add((String) o);
else fail("Bad argument type: " + structure(o));
return this;
}
int size() {
return args.size();
}
boolean isEmpty() {
return args.isEmpty();
}
boolean isLeaf() {
return args.isEmpty();
}
Lisp get(int i) {
return args.get(i);
}
String getString(int i) {
return get(i).head;
}
String s(int i) {
return getString(i);
}
boolean isA(String head) {
return eq(head, this.head);
}
boolean is(String head, int size) {
return isA(head) && size() == size;
}
boolean is(String head) {
return isA(head);
}
boolean is(String... heads) {
return asList(heads).contains(head);
}
// check head for one of these (ignore case)
boolean isic(String... heads) {
return containsIgnoreCase(heads, head);
}
public Iterator iterator() {
return args.iterator();
}
Lisp subList(int fromIndex, int toIndex) {
Lisp l = new Lisp(head);
l.args.addAll(args.subList(fromIndex, toIndex)); // better to copy here I guess - safe
return l;
}
public boolean equals(Object o) {
if (o == null || o.getClass() != Lisp.class) return false;
Lisp l = (Lisp) ( o);
return eq (head, l.head) && eq(args, l.args);
}
Lisp addAll(List args) {
for (Object arg : args) add(arg);
return this;
}
String unquoted() {
return unquote(raw());
}
String unq() {
return unquoted();
}
}
public static String unquote(String s) {
if (s.startsWith("[")) {
int i = 1;
while (i < s.length() && s.charAt(i) == '=') ++i;
if (i < s.length() && s.charAt(i) == '[') {
String m = s.substring(1, i);
if (s.endsWith("]" + m + "]"))
return s.substring(i+1, s.length()-i-1);
}
}
if (s.startsWith("\"") /*&& s.endsWith("\"")*/ && s.length() > 1) {
String st = s.substring(1, s.endsWith("\"") ? s.length()-1 : s.length());
StringBuilder sb = new StringBuilder(st.length());
for (int i = 0; i < st.length(); i++) {
char ch = st.charAt(i);
if (ch == '\\') {
char nextChar = (i == st.length() - 1) ? '\\' : st
.charAt(i + 1);
// Octal escape?
if (nextChar >= '0' && nextChar <= '7') {
String code = "" + nextChar;
i++;
if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
&& st.charAt(i + 1) <= '7') {
code += st.charAt(i + 1);
i++;
if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
&& st.charAt(i + 1) <= '7') {
code += st.charAt(i + 1);
i++;
}
}
sb.append((char) Integer.parseInt(code, 8));
continue;
}
switch (nextChar) {
case '\\':
ch = '\\';
break;
case 'b':
ch = '\b';
break;
case 'f':
ch = '\f';
break;
case 'n':
ch = '\n';
break;
case 'r':
ch = '\r';
break;
case 't':
ch = '\t';
break;
case '\"':
ch = '\"';
break;
case '\'':
ch = '\'';
break;
// Hex Unicode: u????
case 'u':
if (i >= st.length() - 5) {
ch = 'u';
break;
}
int code = Integer.parseInt(
"" + st.charAt(i + 2) + st.charAt(i + 3)
+ st.charAt(i + 4) + st.charAt(i + 5), 16);
sb.append(Character.toChars(code));
i += 5;
continue;
default:
ch = nextChar; // added by Stefan
}
i++;
}
sb.append(ch);
}
return sb.toString();
} else
return s; // return original
}
static Class> getClass(String name) {
try {
return Class.forName(name);
} catch (ClassNotFoundException e) {
return null;
}
}
static Class getClass(Object o) {
return o instanceof Class ? (Class) o : o.getClass();
}
static Class getClass(Object realm, String name) { try {
return getClass(realm).getClassLoader().loadClass(name);
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static boolean nlMatch(String pat, String nl, Map matches) {
matches.clear();
return nlMatch_sub(nlParse/*_cached*/(pat), nlParse(nl), matches);
}
static boolean nlMatch(Lisp pat, String nl, Map matches) {
matches.clear();
return nlMatch_sub(pat, nlParse(nl), matches);
}
static boolean nlMatch(String pat, Lisp nl, Map matches) {
matches.clear();
return nlMatch_sub(nlParse/*_cached*/(pat), nl, matches);
}
static boolean nlMatch(Lisp pat, Lisp nl, Map matches) {
matches.clear();
return nlMatch_sub(pat, nl, matches);
}
static boolean nlMatch_sub(Lisp pat, Lisp nl, Map m) {
if (pat == null || nl == null) return false;
if (pat.isA("*"))
return true;
if (nlIsVar(pat))
return nlMatch_putMatch(m, pat.head, nl);
if (neq(pat.head, nl.head)) return false;
// heads identical, proceed to children
int n = pat.size();
if (n != nl.size()) return false;
for (int i = 0; i < n; i++)
if (!nlMatch_sub(pat.get(i), nl.get(i), m))
return false;
return true;
}
static boolean nlMatch_putMatch(Map matches, String key, Lisp val) {
key = dropPrefix("$", key);
if (matches.containsKey(key) && neq(matches.get(key), val))
fail("multi-matching not implemented");
matches.put(key, val);
return true;
}
public static String joinLines(List lines) {
return fromLines(lines);
}
static boolean nlIsVar(Lisp l) {
if (l == null || !l.isLeaf()) return false;
String h = l.head;
return l(h) > 1 && h.startsWith("$");
}
static ArrayList asList(A[] a) {
return new ArrayList (Arrays.asList(a));
}
static ArrayList asList(int[] a) {
ArrayList l = new ArrayList();
for (int i : a) l.add(i);
return l;
}
static ArrayList asList(Set s) {
return s == null ? new ArrayList() : new ArrayList(s);
}
static boolean empty(Collection c) {
return isEmpty(c);
}
static boolean empty(String s) {
return isEmpty(s);
}
static boolean empty(Map map) {
return map == null || map.isEmpty();
}
static String getStackTrace(Throwable throwable) {
StringWriter writer = new StringWriter();
throwable.printStackTrace(new PrintWriter(writer));
return writer.toString();
}
// supports the usual quotings (', ", variable length double brackets)
static boolean isQuoted(String s) {
if (s.startsWith("'") || s.startsWith("\"")) return true;
if (!s.startsWith("[")) return false;
int i = 1;
while (i < s.length() && s.charAt(i) == '=') ++i;
return i < s.length() && s.charAt(i) == '[';
//return Pattern.compile("^\\[=*\\[").matcher(s).find();
}
static String dropPrefix(String prefix, String s) {
return s.startsWith(prefix) ? s.substring(l(prefix)) : s;
}
// doesn't perform any sanity checks...
// and this is NL-level, not SNL.
static Lisp snlJoinOps(List ops) {
Lisp t = lisp("[]");
for (Lisp op : ops) {
int i = op.head.indexOf(' ');
t.add(lisp(substring(op.head, 0, i)));
t.add(op.get(0));
}
return t;
}
static boolean nlIsMultipleStatements(String text) {
List tok = codeTokens(snlTok(text));
//ret eq(first(tok), "[") && eq(last(tok), "]");
if (!(eq(first(tok), "[") && eq(last(tok), "]")))
return false;
List warnings1 = new ArrayList();
List warnings2 = new ArrayList();
Lisp parse1 = nlParse(tok, true, warnings1);
Lisp parse2 = nlParse(subList(tok, 1, l(tok)-1), true, warnings2);
//ret !eq(parse1, parse2);
return l(warnings2) > l(warnings1);
//ret isJuxta(tree) && snlSplitOps(tree) == null;
}
static A last(List l) {
return l.isEmpty() ? null : l.get(l.size()-1);
}
static void printStackTrace(Throwable e) {
// we go to system.out now - system.err is nonsense
print(getStackTrace(e));
}
static void printStackTrace() {
printStackTrace(new Throwable());
}
// make a lisp form
static Lisp lisp(String head, Object... args) {
Lisp l = new Lisp(head);
for (Object o : args)
l.add(o);
return l;
}
static Lisp lisp(String head, List args) {
return new Lisp(head, args);
}
static HashMapsnlToTree_cached_cache = new HashMap();
static Lisp snlToTree_cached(String s) {
Lisp l = snlToTree_cached_cache.get(s);
if (l == null)
snlToTree_cached_cache.put(s, l = snlToTree (s));
return l;
}
static boolean snlToTree_debug = false;
static Lisp snlToTree(String snl) {
return snlToTree(snlParse(snl));
}
static Lisp snlToTree(Explain e) {
class T {
List scanJuxta(Explain e) {
boolean isJuxta = isA(e, "juxta");
if (snlToTree_debug)
print("scanJuxta " + e.string() + " " + isJuxta);
if (isJuxta) {
Explain juxta = descend(e, "juxta");
Explain a = juxta.sub(0), b = juxta.sub(1);
return concatLists(scanJuxta(a), scanJuxta(b));
} else
return litlist(e);
}
boolean isSame(Explain a, Explain b) {
return a.fromToken() == b.fromToken() && a.toToken() == b.toToken();
}
List getClasses(Explain e) {
List l = new ArrayList();
while (e != null) {
l.add(e.className());
if (l(e.subs) == 1 && e.sub(0) != null && isSame(e, e.sub(0)))
e = e.sub(0);
else
break;
}
return l;
}
Explain castTo(Explain e, String className) {
return descend(e, className);
}
Explain descend(Explain e, String className) {
List l = new ArrayList();
while (e != null) {
if (eq(e.className(), className))
return e;
if (l(e.subs) == 1)
e = e.sub(0);
else
break;
}
return null;
}
boolean isA(Explain e, String className) {
return getClasses(e).contains(className);
}
boolean allAre(List l, String className) {
for (Explain e: l)
if (!isA(e, className))
return false;
return true;
}
boolean anyAre(List l, String className) {
for (Explain e : l)
if (isA(e, className))
return true;
return false;
}
List getOperators(List parts) {
List l = new ArrayList();
for (Explain e : parts)
l.add(makeOperator(e));
return l;
}
// true = it's a sub part
boolean[] opMap(List parts) {
boolean[] map = new boolean[l(parts)];
if (anyAre(parts, "symbol")) {
for (int i = 0; i < l(parts); i++)
if (!isA(parts.get(i), "symbol"))
map[i] = true;
} else
for (int i = 0; i < l(parts); i++)
//if (!isA(parts.get(i), "extidentifier"))
if (!startsWithLetter(parts.get(i).string()))
map[i] = true;
return map;
}
String makeOperatorFromJuxta(List parts) {
List l = new ArrayList();
boolean[] map = opMap(parts);
for (int i = 0; i < l(parts); i++) {
//print("Operator part " + (++i) + ": " + e.string() + " / " + structure(getClasses(e)));
if (map[i])
l.add("*");
else
l.add(parts.get(i).string());
}
String op = join(" ", l);
if (snlToTree_debug)
print("makeOperatorFromJuxta " + structure(parts) + " => " + op);
return op;
}
List getJuxtaArgs(List parts) {
List l = new ArrayList();
boolean[] map = opMap(parts);
for (int i = 0; i < l(parts); i++)
if (map[i])
l.add(parts.get(i));
return l;
}
String makeOperator(Explain e) {
if (isA(e, "realarrow"))
return descend(e, "realarrow").sub(0).string() + " *";
else if (isA(e, "juxta")) {
List j = scanJuxta(e);
//print("scanJuxta => " + structure(j));
return makeOperatorFromJuxta(j);
} else
return e.string();
}
Lisp getTree(Explain e) {
//print("getTree " + structure(getClasses(e)));
if (isA(e, "realarrow")) {
//print("realarrow");
Explain a = castTo(e, "realarrow");
return snlToTree_lisp(e, "<", getTree(a.sub(0)), getTree(a.sub(1)));
}
if (isA(e, "realarrowr")) {
//print("realarrowr");
Explain a = castTo(e, "realarrowr");
return snlToTree_lisp(e, ">", getTree(a.sub(0)), getTree(a.sub(1)));
}
if (isA(e, "square")) {
//print("square");
Explain a = castTo(e, "square");
return snlToTree_lisp(e, "[]", getTree(a.sub(0)));
}
if (isA(e, "round")) {
Explain a = castTo(e, "round");
return snlToTree_lisp(e, "()", getTree(a.sub(0)));
}
if (isA(e, "juxta")) {
Explain juxta = descend(e, "juxta");
List parts = scanJuxta(juxta);
if (snlToTree_debug) {
print("juxta " + e.string());
for (int i = 0; i < l(parts); i++)
print(" part " + parts.get(i).string() + " " + structure(getClasses(parts.get(i))));
}
if (l(parts) == 2 && eq(parts.get(0).string(), "="))
return snlToTree_lisp(e, "= *", removeBrackets(getTree(parts.get(1))));
else /*if (anyAre(parts, "subword") || anyAre(parts, "ucid")) */ {
if (snlToTree_debug)
print("subwords!");
List args = getJuxtaArgs(parts);
Lisp l = snlToTree_lisp(e, makeOperatorFromJuxta(parts));
for (Explain arg : args)
l.add(removeBrackets(getTree(arg)));
return l; // snlSimplifyJuxta(l);
}
// fall back to simple string
}
return snlToTree_lisp(e, e.string());
}
Lisp removeBrackets(Lisp l) {
while (l != null && eq(l.head, "[]") && l.size() == 1)
l = l.get(0);
return l;
}
}
if (e == null) return null;
return simplifySNLTree(new T().getTree(e));
}
static Lisp snlToTree_lisp(Explain e, String head, Lisp... args) {
Lisp l = lisp(head, args);
// this is problematic with serialization...
//SNLInfo info = new SNLInfo();
//info.originalExplain = e;
//l.more = info;
return l;
}
static boolean snlIsVar(Lisp l) {
if (l == null || !l.isLeaf()) return false;
String h = l.head;
return startsWithUpperCase(h)
|| (l(h) > 1 && h.startsWith("$"));
}
static Lisp nlList(List list) {
Lisp tree = lisp("[]");
for (Object s : list) {
if (!tree.isEmpty()) tree.add(",");
if (s instanceof Lisp)
tree.add((Lisp) s);
else if (s instanceof String)
tree.add(quote((String) s));
else
tree.add("?");
}
return tree;
}
static String indent(int indent) {
return repeat(' ', indent);
}
static String indent(int indent, String s) {
return indent(repeat(' ', indent), s);
}
static String indent(String indent, String s) {
return indent + s.replace("\n", "\n" + indent);
}
static List indent(String indent, List lines) {
List l = new ArrayList();
for (String s : lines)
l.add(indent + s);
return l;
}
static Object callOpt(Object o, String method, Object... args) {
try {
if (o == null) return null;
if (o instanceof Class) {
Method m = callOpt_findStaticMethod((Class) o, method, args, false);
if (m == null) return null;
m.setAccessible(true);
return m.invoke(null, args);
} else {
Method m = callOpt_findMethod(o, method, args, false);
if (m == null) return null;
m.setAccessible(true);
return m.invoke(o, args);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static Method callOpt_findStaticMethod(Class c, String method, Object[] args, boolean debug) {
Class _c = c;
while (c != null) {
for (Method m : c.getDeclaredMethods()) {
if (debug)
System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
if (!m.getName().equals(method)) {
if (debug) System.out.println("Method name mismatch: " + method);
continue;
}
if ((m.getModifiers() & Modifier.STATIC) == 0 || !callOpt_checkArgs(m, args, debug))
continue;
return m;
}
c = c.getSuperclass();
}
return null;
}
static Method callOpt_findMethod(Object o, String method, Object[] args, boolean debug) {
Class c = o.getClass();
while (c != null) {
for (Method m : c.getDeclaredMethods()) {
if (debug)
System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
if (m.getName().equals(method) && callOpt_checkArgs(m, args, debug))
return m;
}
c = c.getSuperclass();
}
return null;
}
private static boolean callOpt_checkArgs(Method m, Object[] args, boolean debug) {
Class>[] types = m.getParameterTypes();
if (types.length != args.length) {
if (debug)
System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
return false;
}
for (int i = 0; i < types.length; i++)
if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
if (debug)
System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
return false;
}
return true;
}
// snlMatch2 keeps existing matches and restores matches on false
static boolean snlMatch2(String pat, String snl, SNLMatches matches) {
return snlMatch2(snlToTree_cached(pat), snlToTree(snl), matches);
}
static boolean snlMatch2(Lisp pat, String snl, SNLMatches matches) {
return snlMatch2(pat, snlToTree(snl), matches);
}
static boolean snlMatch2(String pat, Lisp snl, SNLMatches matches) {
return snlMatch2(snlToTree_cached(pat), snl, matches);
}
static boolean snlMatch2(Lisp pat, Lisp snl, SNLMatches matches) {
int level = matches.save();
if (snlMatch2_sub(pat, snl, matches))
return true;
matches.restore(level);
return false;
}
static boolean snlMatch2_sub(Lisp pat, Lisp snl, SNLMatches m) {
if (pat == null || snl == null) return false;
if (pat.isA("*"))
return true;
if (startsWithUpperCase(pat.head))
return snlMatch2_putMatch(m, pat.head, snl);
if (neq(pat.head, snl.head)) return false;
// heads identical, proceed to children
int n = pat.size();
if (n != snl.size()) return false;
for (int i = 0; i < n; i++)
if (!snlMatch2_sub(pat.get(i), snl.get(i), m))
return false;
return true;
}
static boolean snlMatch2_putMatch(SNLMatches matches, String key, Lisp val) {
return matches.put(key, val);
}
static void removeLast(List l) {
if (!l.isEmpty())
l.remove(l(l)-1);
}
static String quoteIfNotIdentifier(String s) {
if (s == null) return null;
return isJavaIdentifier(s) ? s : quote(s);
}
static Object newObject(Class c, Object... args) {
return nuObject(c, args);
}
static Object newObject(String className, Object... args) {
return nuObject(className, args);
}
static long now_virtualTime;
static long now() {
return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis();
}
static Object quickImport(Object o) {
return quickExport(o, mc());
}
static AtomicInteger nlParse_count = new AtomicInteger(); // how often did we parse something?
static Lisp nlParse(String s) {
return nlParse(s, true);
}
static Lisp nlParse(String s, boolean unbracket) {
return nlParse(codeTokens(snlTok(s)), unbracket, null);
}
static Lisp nlParse(List tok) {
return nlParse(tok, true, null);
}
static Lisp nlParse(List tok, boolean unbracket, List warnings) {
nlParse_count.incrementAndGet();
class Entry {
int i;
Lisp tree;
String wrapper;
Entry(int i) {
this.i = i;
tree = lisp("[]");
}
Entry(int i, String bracket) {
this.i = i;
tree = lisp("[]");
wrapper = bracket;
}
Lisp wrapped() {
Lisp t = nlUnbracket(tree);
return wrapper == null ? t : lisp(wrapper, t);
}
}
List stack = new ArrayList();
stack.add(new Entry(0));
for (int i = 0; i < l(tok); i++) {
String t = tok.get(i);
if (eq(t, "[") || eq(t, "(")) {
stack.add(new Entry(i, eq(t, "(") ? "()" : null));
} else if (eq(t, "]") || eq(t, ")")) {
if (l(stack) == 1)
warn("too many closing brackets", warnings);
else {
Entry e = popLast(stack);
/*if (!bracketsMatch(tok.get(e.i), t))
warn("non-matching brackets");*/
last(stack).tree.add(e.wrapped());
}
} else
last(stack).tree.add(t);
}
while (l(stack) > 1) {
warn("too many opening brackets", warnings);
Entry e = popLast(stack);
last(stack).tree.add(e.wrapped());
}
Lisp result = last(stack).wrapped();
return unbracket ? nlUnbracket(result) : result;
}
static String addPrefix(String prefix, String s) {
return s.startsWith(prefix) ? s : prefix + s;
}
static List subList(List l, int startIndex) {
return subList(l, startIndex, l(l));
}
static List subList(List l, int startIndex, int endIndex) {
startIndex = max(0, min(l(l), startIndex));
endIndex = max(0, min(l(l), endIndex));
if (startIndex > endIndex) return litlist();
return l.subList(startIndex, endIndex);
}
static void set(Object o, String field, Object value) {
if (o instanceof Class) set((Class) o, field, value);
else try {
Field f = set_findField(o.getClass(), field);
smartSet(f, o, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static void set(Class c, String field, Object value) {
try {
Field f = set_findStaticField(c, field);
smartSet(f, null, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static Field set_findField(Class> c, String field) {
for (Field f : c.getDeclaredFields())
if (f.getName().equals(field))
return f;
throw new RuntimeException("Field '" + field + "' not found in " + c.getName());
}
static Field set_findStaticField(Class> c, String field) {
for (Field f : c.getDeclaredFields())
if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0)
return f;
throw new RuntimeException("Static field '" + field + "' not found in " + c.getName());
}
static A or(A a, A b) {
return a != null ? a : b;
}
// split smth like "if * then *" into "if *" and "then *"
// returns null if no split
// now also for NL trees
static List snlSplitOps(Lisp code) {
List out = new ArrayList();
if (code.is("[]")) {
// NL
if ((code.size() & 1) != 0) return null;
for (int i = 0; i < code.size(); i += 2) {
Lisp op = code.get(i), arg = code.get(i+1);
if (!op.isLeaf() || !isExtendedIdentifier(op.head)) return null;
out.add(lisp(op + " *", nlUnbracket(arg)));
}
return out;
}
String ops = code.head;
List tok = codeTokensOnly(javaTok(ops));
if ((l(tok) & 1) != 0) return null;
for (int i = 0; i < l(tok); i += 2) {
String op = tok.get(i);
if (!isIdentifier(op) || neq("*", tok.get(i+1))) return null;
out.add(lisp(op + " *", code.get(i/2)));
}
//print("splitOps => " + structure(out));
return out;
}
static boolean match(String pat, String s) {
return match3(pat, s);
}
static boolean match(String pat, String s, Matches matches) {
return match3(pat, s, matches);
}
static boolean eqic(String a, String b) {
if ((a == null) != (b == null)) return false;
if (a == null) return true;
return a.equalsIgnoreCase(b);
}
static String slackSnippet(Object contents) {
if (contents instanceof List) contents = join("\n", (List) contents);
String s = str(contents);
//ret "```" + (empty(s) ? "\n" : s) + "```";
return "```\n" + s + "```";
}
static String getString(Map map, Object key) {
return map == null ? null : (String) map.get(key);
}
static String getString(List l, int idx) {
return (String) get(l, idx);
}
static String getString(Object o, Object key) {
if (o instanceof Map) return getString((Map) o, key);
if (key instanceof String)
return (String) get(o, (String) key);
throw fail("Not a string key: " + getClassName(key));
}
static boolean containsIgnoreCase(List l, String s) {
for (String x : l)
if (eqic(x, s))
return true;
return false;
}
static boolean containsIgnoreCase(String[] l, String s) {
for (String x : l)
if (eqic(x, s))
return true;
return false;
}
static String n(long l, String name) {
return l + " " + (l == 1 ? name : getPlural(name));
}
static boolean startsWithUpperCase(String s) {
return nempty(s) && Character.isUpperCase(s.charAt(0));
}
static int parseInt(String s) {
return Integer.parseInt(s);
}
static Object quickExport(Object o, Object dest) { try {
if (o == null || o instanceof String || o instanceof Number) return o;
if (o instanceof List) {
List l = (List) ( o);
List destO = new ArrayList(l.size());
for (int i = 0; i < l.size(); i++)
destO.add(quickExport(l.get(i), dest));
return destO;
}
if (o instanceof Map) {
Map m = (Map) ( o);
Map destO = new HashMap();
for (Object e : ((Map) o).entrySet())
destO.put(
quickExport(((Map.Entry) e).getKey(), dest),
quickExport(((Map.Entry) e).getValue(), dest));
return destO;
}
String className = o.getClass().getName();
if (className.startsWith("main$")) {
Class destClass = getClass(dest, className);
//print(o.getClass() + " => " + destClass);
if (o.getClass() == destClass)
return o; // no export necessary
// actually make a new object(), copy fields
Object destO = nuObject(destClass);
// TODO: superclasses
Field[] fields = o.getClass().getDeclaredFields();
for (Field field : fields) {
if ((field.getModifiers() & Modifier.STATIC) != 0)
continue;
field.setAccessible(true);
Object value = field.get(o);
setOpt(destO, field.getName(), quickExport(value, dest));
}
return destO;
}
// assume it's a shared library object
return o;
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static A popLast(List l) {
return liftLast(l);
}
static String lstr(Map map) {
return str(l(map));
}
static String lstr(List l) {
return str(l(l));
}
static String lstr(String s) {
return str(l(s));
}
static boolean equals(Object a, Object b) {
return a == null ? b == null : a.equals(b);
}
static String nlUnparse(Lisp tree) {
return nlUnparse(tree, true);
}
static String nlUnparse(Lisp tree, boolean dropOuterBrackets) {
StringBuilder buf = new StringBuilder();
nlUnparse(tree, buf, dropOuterBrackets);
return str(buf);
}
static void nlUnparse(Lisp tree, StringBuilder buf, boolean dropOuterBrackets) {
if (tree == null) return;
// special classes - Var etc.
if (tree.getClass() != Lisp.class) {
buf.append(tree);
return;
}
if (tree.isA("[]")) {
if (!dropOuterBrackets) buf.append("[");
} else if (tree.isA("()"))
buf.append("(");
else {
if (tree.isLeaf())
buf.append(tree.head);
else
buf.append("?? " + tree);
return;
}
for (int i = 0; i < tree.size(); i++) {
if (i != 0) buf.append(" ");
nlUnparse(tree.get(i), buf, false);
}
if(tree.isA("[]")) {
if (!dropOuterBrackets) buf.append("]");
} else
buf.append(")");
}
static Object first(Object list) {
return ((List) list).isEmpty() ? null : ((List) list).get(0);
}
static A first(List list) {
return list.isEmpty() ? null : list.get(0);
}
static Lisp simplifySNLTree(Lisp tree) {
while (tree != null && tree.isA("[]") && tree.size() == 1)
tree = tree.get(0);
if (tree == null) return null;
Lisp lisp = new Lisp(tree.head);
for (Lisp child : tree)
lisp.add(simplifySNLTree(child));
return lisp;
}
static boolean startsWithLetter(String s) {
return nempty(s) && Character.isLetter(s.charAt(0));
}
static String getPlural(String s) {
if (s.endsWith("y")) return dropSuffix("y", s) + "ies";
return s + "s";
}
static List codeTokensOnly(List tok) {
List l = new ArrayList();
for (int i = 1; i < tok.size(); i += 2)
l.add(tok.get(i));
return l;
}
/*
Binding levels (well...):
0 = lowest
1 = arrows (> lowest)
2 = arrowsr (> arrows)
3 = word (> arrows)
4 = juxta, idword, subword (> word)
*/
static String snlParse_rules = "\n // highest (strongest binding) level \n = idword\n = subword\n = subword\n // = subword\n \n // random symbols we'd want to use\n // (they don't parse if not listed here)\n ? = symbol\n : = symbol\n * = symbol\n \"=\" = symbol\n + = symbol\n , = symbol\n . = symbol\n = idword\n \n = word\n = word\n \n // juxtaposition on highest level\n = juxta\n = word\n \n // bracketing takes us to highest level\n [ ] = square\n ( ) = round\n \n = subword\n = subword\n \n // < is intermediate\n = arrowsr\n > = realarrowr\n = arrowsr\n \n < = realarrow\n = arrows\n = arrows\n \n = lowest // allow empty here\n = lowest\n";
static Explain snlParse(String input) {
return explainFull(snlTok(input), snlParse_rules, "lowest");
}
static Lisp nlUnbracket(Lisp tree) {
while (tree.is("[]", 1))
tree = tree.get(0);
return tree;
}
static A liftLast(List l) {
if (l.isEmpty()) return null;
int i = l(l)-1;
A a = l.get(i);
l.remove(i);
return a;
}
static boolean isExtendedIdentifier(String s) {
if (empty(s)) return false;
for (int i = 0; i < l(s); i++) {
char c = s.charAt(i);
if (c != '\'' && !(i == 0 ? Character.isLetter(c) : Character.isLetterOrDigit(c)))
return false;
}
return true;
}
// replacement for class JavaTok
// maybe incomplete, might want to add floating point numbers
// todo also: extended multi-line strings
static List javaTok(String s) {
List tok = new ArrayList();
int l = s.length();
int i = 0;
while (i < l) {
int j = i;
char c; String cc;
// scan for whitespace
while (j < l) {
c = s.charAt(j);
cc = s.substring(j, Math.min(j+2, l));
if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
++j;
else if (cc.equals("/*")) {
do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/"));
j = Math.min(j+2, l);
} else if (cc.equals("//")) {
do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0);
} else
break;
}
tok.add(s.substring(i, j));
i = j;
if (i >= l) break;
c = s.charAt(i); // cc is not needed in rest of loop body
cc = s.substring(i, Math.min(i+2, l));
// scan for non-whitespace
if (c == '\'' || c == '"') {
char opener = c;
++j;
while (j < l) {
if (s.charAt(j) == opener || s.charAt(j) == '\n') { // end at \n to not propagate unclosed string literal errors
++j;
break;
} else if (s.charAt(j) == '\\' && j+1 < l)
j += 2;
else
++j;
}
} else if (Character.isJavaIdentifierStart(c))
do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || "'".indexOf(s.charAt(j)) >= 0)); // for stuff like "don't"
else if (Character.isDigit(c)) {
do ++j; while (j < l && Character.isDigit(s.charAt(j)));
if (j < l && s.charAt(j) == 'L') ++j; // Long constants like 1L
} else if (cc.equals("[[")) {
do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]"));
j = Math.min(j+2, l);
} else if (cc.equals("[=") && i+2 < l && s.charAt(i+2) == '[') {
do ++j; while (j+2 < l && !s.substring(j, j+3).equals("]=]"));
j = Math.min(j+3, l);
} else
++j;
tok.add(s.substring(i, j));
i = j;
}
if ((tok.size() % 2) == 0) tok.add("");
return tok;
}
static List javaTok(List tok) {
return javaTok(join(tok));
}
static int min(int a, int b) {
return Math.min(a, b);
}
static double min(double[] c) {
double x = Double.MAX_VALUE;
for (double d : c) x = Math.min(x, d);
return x;
}
static ArrayList litlist(A... a) {
return new ArrayList (Arrays.asList(a));
}
// This is made for SNL parsing.
// It does NOT recognize multiline strings as these conflict
// with syntax like [[a] [b]].
static List snlTok(String s) {
List tok = new ArrayList();
int l = s.length();
int i = 0;
while (i < l) {
int j = i;
char c; String cc;
// scan for whitespace
while (j < l) {
c = s.charAt(j);
cc = s.substring(j, Math.min(j+2, l));
if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
++j;
else if (cc.equals("/*")) {
do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/"));
j = Math.min(j+2, l);
} else if (cc.equals("//")) {
do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0);
} else
break;
}
tok.add(s.substring(i, j));
i = j;
if (i >= l) break;
c = s.charAt(i);
cc = s.substring(i, Math.min(i+2, l));
// scan for non-whitespace
if (c == '\u201C' || c == '\u201D') c = '"'; // normalize quotes
if (c == '\'' || c == '"') {
char opener = c;
++j;
while (j < l) {
char _c = s.charAt(j);
if (_c == '\u201C' || _c == '\u201D') _c = '"'; // normalize quotes
if (_c == opener) {
++j;
break;
} else if (s.charAt(j) == '\\' && j+1 < l)
j += 2;
else
++j;
}
if (j-1 >= i+1) {
tok.add(opener + s.substring(i+1, j-1) + opener);
i = j;
continue;
}
} else if (Character.isJavaIdentifierStart(c))
do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || s.charAt(j) == '\'')); // for things like "this one's"
else if (Character.isDigit(c))
do ++j; while (j < l && Character.isDigit(s.charAt(j)));
/*else if (cc.equals("[[")) {
do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]"));
j = Math.min(j+2, l);
}*/ else if (s.substring(j, Math.min(j+3, l)).equals("..."))
j += 3;
else if (c == '$' || c == '#')
do ++j; while (j < l && Character.isLetterOrDigit(s.charAt(j)));
else
++j;
tok.add(s.substring(i, j));
i = j;
}
if ((tok.size() % 2) == 0) tok.add("");
return tok;
}
static List codeTokens(List tok) {
return codeTokensOnly(tok);
}
static int max(int a, int b) {
return Math.max(a, b);
}
static long max(int a, long b) {
return Math.max((long) a, b);
}
static long max(long a, long b) {
return Math.max(a, b);
}
static double max(int a, double b) {
return Math.max((double) a, b);
}
static int max(Collection c) {
int x = Integer.MIN_VALUE;
for (int i : c) x = max(x, i);
return x;
}
static double max(double[] c) {
if (c.length == 0) return Double.MIN_VALUE;
double x = c[0];
for (int i = 1; i < c.length; i++) x = Math.max(x, c[i]);
return x;
}
// class Matches is added by #752
static boolean match3(String pat, String s) {
return match3(pat, s, null);
}
static boolean match3(String pat, String s, Matches matches) {
if (s == null) return false;
return match3(pat, parse3(s), matches);
}
static boolean match3(String pat, List toks, Matches matches) {
List tokpat = parse3(pat);
return match3(tokpat,toks,matches);
}
static boolean match3(List tokpat, List toks, Matches matches) {
String[] m = match2(tokpat, toks);
//print(structure(tokpat) + " on " + structure(toks) + " => " + structure(m));
if (m == null)
return false;
else {
if (matches != null) matches.m = m;
return true;
}
}
static Class mc() {
return getMainClass();
}
static boolean neq(Object a, Object b) {
return !eq(a, b);
}
static boolean warn_on = false;
static void warn(String s) {
if (warn_on)
print("Warn: " + s);
}
static void warn(String s, List warnings) {
warn(s);
if (warnings != null)
warnings.add(s);
}
static List concatLists(List ... lists) {
List l = new ArrayList ();
for (List list : lists)
if (list != null)
l.addAll(list);
return l;
}
static List concatLists(List> lists) {
List l = new ArrayList ();
for (List list : lists)
if (list != null)
l.addAll(list);
return l;
}
static Object nuObject(String className, Object... args) { try {
return nuObject(Class.forName(className), args);
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static Object nuObject(Class c, Object... args) { try {
Constructor m = nuObject_findConstructor(c, args);
m.setAccessible(true);
return m.newInstance(args);
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static Constructor nuObject_findConstructor(Class c, Object... args) {
for (Constructor m : c.getDeclaredConstructors()) {
if (!nuObject_checkArgs(m.getParameterTypes(), args, false))
continue;
return m;
}
throw new RuntimeException("Constructor with " + args.length + " matching parameter(s) not found in " + c.getName());
}
static boolean nuObject_checkArgs(Class[] types, Object[] args, boolean debug) {
if (types.length != args.length) {
if (debug)
System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
return false;
}
for (int i = 0; i < types.length; i++)
if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
if (debug)
System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
return false;
}
return true;
}
static String getClassName(Object o) {
return o == null ? "null" : o.getClass().getName();
}
public static String fromLines(List lines) {
StringBuilder buf = new StringBuilder();
if (lines != null)
for (String line : lines)
buf.append(line).append('\n');
return buf.toString();
}
static String substring(String s, int x) {
return safeSubstring(s, x);
}
static String substring(String s, int x, int y) {
return safeSubstring(s, x, y);
}
static String repeat(char c, int n) {
n = max(n, 0);
char[] chars = new char[n];
for (int i = 0; i < n; i++)
chars[i] = c;
return new String(chars);
}
static List repeat(A a, int n) {
List l = new ArrayList ();
for (int i = 0; i < n; i++)
l.add(a);
return l;
}
static void smartSet(Field f, Object o, Object value) throws Exception {
f.setAccessible(true);
// take care of common case (long to int)
if (f.getType() == int.class && value instanceof Long)
value = ((Long) value).intValue();
f.set(o, value);
}
static void setOpt(Object o, String field, Object value) {
if (o instanceof Class) setOpt((Class) o, field, value);
else try {
Field f = setOpt_findField(o.getClass(), field);
if (f != null)
smartSet(f, o, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static void setOpt(Class c, String field, Object value) {
try {
Field f = setOpt_findStaticField(c, field);
if (f != null)
smartSet(f, null, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static Field setOpt_findField(Class> c, String field) {
for (Field f : c.getDeclaredFields())
if (f.getName().equals(field))
return f;
return null;
}
static Field setOpt_findStaticField(Class> c, String field) {
for (Field f : c.getDeclaredFields())
if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0)
return f;
return null;
}
// hopefully covers all cases :)
static String safeSubstring(String s, int x, int y) {
if (s == null) return null;
if (x < 0) x = 0;
if (x > s.length()) return "";
if (y < x) y = x;
if (y > s.length()) y = s.length();
return s.substring(x, y);
}
static String safeSubstring(String s, int x) {
return safeSubstring(s, x, l(s));
}
static Explain explainFull(String input, String rules, String className) {
return explainFull(javaTok(input), rules, className);
}
static Explain explainFull(List tok, String rules, String className) {
Object parseResult = parse1(tok, rules);
List e = (List) ( call(parseResult, "explainFull", className));
if (e == null) return null;
return new Explain(parseResult, e);
}
static List parse3(String s) {
return dropPunctuation(javaTokPlusPeriod(s));
}
static Class getMainClass() { try {
return Class.forName("main");
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static Class getMainClass(Object o) { try {
return (o instanceof Class ? (Class) o : o.getClass()).getClassLoader().loadClass("main");
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
// match2 matches multiple "*" (matches a single token) wildcards and zero or one "..." wildcards (matches multiple tokens)
static String[] match2(List pat, List tok) {
// standard case (no ...)
int i = pat.indexOf("...");
if (i < 0) return match2_match(pat, tok);
pat = new ArrayList(pat); // We're modifying it, so copy first
pat.set(i, "*");
while (pat.size() < tok.size()) {
pat.add(i, "*");
pat.add(i+1, ""); // doesn't matter
}
return match2_match(pat, tok);
}
static String[] match2_match(List pat, List tok) {
List result = new ArrayList();
if (pat.size() != tok.size()) {
/*if (debug)
print("Size mismatch: " + structure(pat) + " vs " + structure(tok));*/
return null;
}
for (int i = 1; i < pat.size(); i += 2) {
String p = pat.get(i), t = tok.get(i);
/*if (debug)
print("Checking " + p + " against " + t);*/
if (eq(p, "*"))
result.add(t);
else if (!equalsIgnoreCase(unquote(p), unquote(t))) // bold change - match quoted and unquoted now
return null;
}
return result.toArray(new String[result.size()]);
}
static String dropSuffix(String suffix, String s) {
return s.endsWith(suffix) ? s.substring(0, l(s)-l(suffix)) : s;
}
static boolean equalsIgnoreCase(String a, String b) {
return a == null ? b == null : a.equalsIgnoreCase(b);
}
static Object parse1_parser;
static Object parse1(String text, String rules) {
return parse1(javaTok(text), rules);
}
// only parse, no explain
static synchronized Object parse1(List tok, String rules) {
if (parse1_parser == null)
parse1_parser = run_overBot(/*"#1002472"*/ "#1002719");
//setOpt(parse1_parser, "debug", true);
return call(parse1_parser, "parse", tok, rules);
}
static List dropPunctuation_keep = litlist("*", "<", ">");
static List dropPunctuation(List tok) {
tok = new ArrayList(tok);
for (int i = 1; i < tok.size(); i += 2) {
String t = tok.get(i);
if (t.length() == 1 && !Character.isLetter(t.charAt(0)) && !Character.isDigit(t.charAt(0)) && !dropPunctuation_keep.contains(t)) {
tok.set(i-1, tok.get(i-1) + tok.get(i+1));
tok.remove(i);
tok.remove(i);
i -= 2;
}
}
return tok;
}
static String dropPunctuation(String s) {
return join(dropPunctuation(nlTok(s)));
}
static Class run_overBot(String progID) {
Class main = hotwire_overBot(progID);
callMain(main);
return main;
}
static List nlTok(String s) {
return javaTokPlusPeriod(s);
}
static Class> hotwire_overBot(String progID) { try {
String compilerBot = "Compiler Bot with caching!";
startBot(compilerBot, "#1002203");
String ss = "please compile this javax snippet: *";
print("Sending to compiler bot: " + ss);
String s = sendToLocalBot_cached(compilerBot, ss, progID);
Matches m = new Matches();
print(s);
assertTrue("Compiler Bot response: " + s, match("ok, *", s, m));
String jarPath = m.unq(0);
File jar = new File(jarPath);
assertTrue(jar.getAbsolutePath(), jar.isFile());
// collect urls (program + libraries)
List urls = litlist(jar.toURI().toURL());
String dehlibs = unnull(loadTextFileFromZip(jar, "libraries"));
Matcher matcher = Pattern.compile("\\d+").matcher(dehlibs);
while (matcher.find()) {
String libID = matcher.group();
urls.add(loadLibrary(libID).toURI().toURL());
}
// make class loader
URLClassLoader classLoader = new URLClassLoader(urls.toArray(new URL[l(urls)]));
// load & return main class
Class> theClass = classLoader.loadClass("main");
Class j = getJavaX();
String src = loadTextFileFromZip(jar, "main.java");
call(j, "registerSourceCode", theClass, src);
synchronized(j) { // hopefully this goes well...
call(j, "setVars", theClass, progID);
callOpt(j, "addInstance", progID, theClass);
}
synchronized(StringBuffer.class) {
Object print_log = get(getMainClass(), "print_log");
assertTrue(print_log != null);
setOpt(theClass, "print_log", print_log);
}
return theClass;
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static void callMain(Object c, String... args) {
callOpt(c, "main", new Object[] {args});
}
static void assertTrue(Object o) {
assertEquals(true, o);
}
static boolean assertTrue(String msg, boolean b) {
if (!b)
fail(msg);
return b;
}
static boolean assertTrue(boolean b) {
if (!b)
fail("oops");
return b;
}
static Class __javax;
static Class getJavaX() {
return __javax;
}
static Map startBot_assignments = new TreeMap();
static void startBot(String botName, String botID) {
DialogIO bot = newFindBot(botName);
if (bot != null)
bot.close();
else {
print("Bot " + quote(botName) + " not found. Starting " + botID);
javaxBot(botID);
waitForBotStartUp(botName);
}
}
static void startBot(String botIDOrName) {
String botID = startBot_assignments.get(botIDOrName);
if (botID != null)
startBot(botIDOrName, botID);
else {
String botName = getBotNameFromSnippet(botIDOrName);
if (botName == null) fail("Bot name not found in source of " + botID);
startBot(botName, botIDOrName);
}
}
static File loadLibrary(String snippetID) {
return loadBinarySnippet(snippetID);
}
static String loadTextFileFromZip(File inZip, String fileName) {
return loadTextFileFromZipFile(inZip, fileName);
}
static String sendToLocalBot_cached(String botName, String s, Object... args) {
DialogIO io = newFindBot(botName);
if (io == null)
fail("Bot not found: " + botName);
try {
return io.ask(s, args);
} finally {
io.close();
}
}
static int newFindBot_botVM;
static synchronized DialogIO newFindBot(String name) {
if (newFindBot_botVM == 0) {
print("Looking for Bot VM.");
List botVMs = quickBotScan("This is a JavaX VM. Bot VM.");
if (!botVMs.isEmpty()) {
newFindBot_botVM = botVMs.get(0).port;
print("Bot VM is at port " + newFindBot_botVM + ".");
}
}
if (newFindBot_botVM != 0) {
DialogIO io = talkTo(newFindBot_botVM);
String q = format("has bot *", name);
String s = io.ask(q);
//print(format("Answer of Bot VM to *: *", q, s));
if (match("yes", s)) {
io = talkToSubBot(name, io);
call(io, "pushback", "?"); // put some hello string in (yes, this should be improved.)
return io;
}
}
return findBot(name);
}
static void javaxBot(String botID) {
startBotVM();
if (injectTo("Bot VM", botID) == null)
fail("Couldn't inject to Bot VM");
//nohupJavax(botID);
}
static long waitForBotStartUp_timeoutSeconds = 60;
// returns address or fails
static String waitForBotStartUp(String botName) {
for (int i = 0; i < waitForBotStartUp_timeoutSeconds; i++) {
sleepSeconds(i == 0 ? 0 : 1);
String addr = getBotAddress(botName);
if (addr != null)
return addr;
}
throw fail("Bot not found: " + quote(botName));
}
static File loadBinarySnippet(String snippetID) { try {
long id = parseSnippetID(snippetID);
File f = DiskSnippetCache_getLibrary(id);
if (f == null) {
byte[] data = loadDataSnippetImpl(snippetID);
DiskSnippetCache_putLibrary(id, data);
f = DiskSnippetCache_getLibrary(id);
}
return f;
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static void assertEquals(Object x, Object y) {
assertEquals(null, x, y);
}
static void assertEquals(String msg, Object x, Object y) {
if (!(x == null ? y == null : x.equals(y)))
fail((msg != null ? msg + ": " : "") + structure(x) + " != " + structure(y));
}
// covers the common cases
static String getBotNameFromSnippet(String programID) { try {
String src = loadSnippet(programID);
List tok = javaTok(src);
String[] m;
for (String pat : litlist(
"new Android3(*",
"makeAndroid(*", "makeAndroid3(*", "makeSilentAndroid(*",
"addToMultiPort(*")) {
m = find2(javaTok(pat), tok);
if (m != null && isQuoted(m[0])) return unquote(m[0]);
}
return null;
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static String loadTextFileFromZipFile(File inZip, String fileName) { try {
ZipFile zip = new ZipFile(inZip);
try {
ZipEntry entry = zip.getEntry(fileName);
if (entry == null)
//fail("Entry " + fileName + " not found in zip file: " + inZip.getAbsolutePath());
return null;
InputStream fin = zip.getInputStream(entry);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
copyStream(fin, baos);
fin.close();
return fromUTF8(baos.toByteArray());
} finally {
zip.close();
}
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static class Injection {
int vmPort;
String injectionID;
}
static Injection injectTo(int vmPort, String progID) {
return injectTo(String.valueOf(vmPort), progID);
}
static Injection injectTo(String vmName, String progID) {
String line = format3("Please inject program *.", progID);
DialogIO injectionPoint = findBot(isInteger(vmName) ? vmName : "This is a JavaX VM. " + vmName);
if (injectionPoint == null) return null;
try {
injectionPoint.readLine();
String answer = injectionPoint.askLoudly(line);
Matches m = new Matches();
if (match3("OK. Injection ID: *", answer, m)) {
Injection i = new Injection();
i.vmPort = injectionPoint.getSocket().getPort();
i.injectionID = m.unq(0);
return i;
}
throw fail(answer);
} finally {
injectionPoint.close();
}
}
static DialogIO talkTo(int port) {
return talkTo("localhost", port);
}
static DialogIO talkTo(String ip, int port) { try {
final Socket s = new Socket(ip, port);
//print("Talking to " + ip + ":" + port);
final Writer w = new OutputStreamWriter(s.getOutputStream(), "UTF-8");
final BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
return new DialogIO() {
boolean isLocalConnection() {
return s.getInetAddress().isLoopbackAddress();
}
boolean isStillConnected() {
return !(eos || s.isClosed());
}
void sendLine(String line) { try {
w.write(line + "\n");
w.flush();
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
String readLineImpl() { try {
return in.readLine();
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
void close() {
try {
s.close();
} catch (IOException e) {
// whatever
}
}
Socket getSocket() {
return s;
}
};
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static DialogIO talkToSubBot(final long vport, final DialogIO io) {
return talkToSubBot(String.valueOf(vport), io);
}
static DialogIO talkToSubBot(final String subBot, final DialogIO io) {
if (subBot == null) return io;
return new DialogIO() {
// delegate all but sendLine
boolean isStillConnected() { return io.isStillConnected(); }
String readLineImpl() { return io.readLineImpl(); }
boolean isLocalConnection() { return io.isLocalConnection(); }
Socket getSocket() { return io.getSocket(); }
void close() { io.close(); }
void sendLine(String line) {
io.sendLine(format3("please forward to bot *: *", subBot, line));
}
};
}
// Data files are immutable, use centralized cache
public static File DiskSnippetCache_getLibrary(long snippetID) throws IOException {
File file = new File(getGlobalCache(), "data_" + snippetID + ".jar");
return file.exists() ? file : null;
}
public static void DiskSnippetCache_putLibrary(long snippetID, byte[] data) throws IOException {
saveBinaryFile(new File(getGlobalCache(), "data_" + snippetID).getPath() + ".jar", data);
}
static byte[] loadDataSnippetImpl(String snippetID) throws IOException {
byte[] data;
try {
URL url = new URL("http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_"
+ parseSnippetID(snippetID) + "&contentType=application/binary");
System.err.println("Loading library: " + url);
try {
data = loadBinaryPage(url.openConnection());
} catch (IOException e) {
data = null;
}
if (data == null || data.length == 0) {
url = new URL("http://data.tinybrain.de/blobs/"
+ parseSnippetID(snippetID));
System.err.println("Loading library: " + url);
data = loadBinaryPage(url.openConnection());
}
System.err.println("Bytes loaded: " + data.length);
} catch (FileNotFoundException e) {
throw new IOException("Binary snippet #" + snippetID + " not found or not public");
}
return data;
}
// We dropped the "***" support here (use match3 for that)
static String[] find2(List pat, List tok) {
for (int idx = 0; idx < tok.size(); idx += 2) {
String[] result = find2(pat, tok, idx);
if (result != null) return result;
}
return null;
}
static String[] find2(List pat, List tok, int idx) {
if (idx+pat.size() > tok.size())
return null;
List result = new ArrayList();
for (int i = 1; i < pat.size(); i += 2) {
String p = pat.get(i), t = tok.get(idx+i);
if (eq(p, "*"))
result.add(t);
else if (!p.equalsIgnoreCase(t))
return null;
}
return toStringArray(result);
}
static Map findBot_cache = new TreeMap();
static int findBot_timeout = 5000;
static DialogIO findBot(String searchPattern) {
// first split off sub-bot suffix
String subBot = null;
int i = searchPattern.indexOf('/');
if (i >= 0 && (isJavaIdentifier(searchPattern.substring(0, i)) || isInteger(searchPattern.substring(0, i)))) {
subBot = searchPattern.substring(i+1);
searchPattern = searchPattern.substring(0, i);
if (!isInteger(searchPattern))
searchPattern = "Multi-Port at " + searchPattern + ".";
}
// assume it's a port if it's an integer
if (isInteger(searchPattern))
return talkToSubBot(subBot, talkTo(parseInt(searchPattern)));
if (eq(searchPattern, "remote"))
return talkToSubBot(subBot, talkTo("second.tinybrain.de", 4999));
Integer port = findBot_cache.get(searchPattern);
if (port != null) try {
DialogIO io = talkTo("localhost", port);
io.waitForLine(/*findBot_timeout*/); // TODO: implement
String line = io.readLineNoBlock();
if (indexOfIgnoreCase(line, searchPattern) == 0) {
call(io, "pushback", line); // put hello string back in
return talkToSubBot(subBot, io);
}
} catch (Exception e) {
e.printStackTrace();
}
List bots = quickBotScan();
// find top-level bots
for (ProgramScan.Program p : bots) {
if (indexOfIgnoreCase(p.helloString, searchPattern) == 0) { // strict matching - start of hello string only, but case-insensitive
findBot_cache.put(searchPattern, p.port);
return talkToSubBot(subBot, talkTo("localhost", p.port));
}
}
// find sub-bots
for (ProgramScan.Program p : bots) {
String botName = firstPartOfHelloString(p.helloString);
boolean isVM = startsWithIgnoreCase(p.helloString, "This is a JavaX VM.");
boolean shouldRecurse = startsWithIgnoreCase(botName, "Multi-Port") || isVM;
if (shouldRecurse) try {
Map subBots = (Map) unstructure(sendToLocalBot(p.port, "list bots"));
for (Number vport : subBots.keySet()) {
String name = subBots.get(vport);
if (startsWithIgnoreCase(name, searchPattern))
return talkToSubBot(vport.longValue(), talkTo("localhost", p.port));
}
} catch (Exception e) { e.printStackTrace(); }
}
return null;
}
static int getBotAddress_timeout = 5000;
static String getBotAddress(String searchPattern) {
String subBot = "";
int i = searchPattern.indexOf('/');
if (i >= 0 && (isJavaIdentifier(searchPattern.substring(0, i)) || isInteger(searchPattern.substring(0, i)))) {
subBot = searchPattern.substring(i);
searchPattern = searchPattern.substring(0, i);
if (!isInteger(searchPattern))
searchPattern = "Multi-Port at " + searchPattern + ".";
}
// assume it's a port if it's an integer
if (isInteger(searchPattern))
return searchPattern + subBot;
Integer port = findBot_cache.get(searchPattern);
if (port != null) try {
DialogIO io = talkTo("localhost", port);
try {
io.waitForLine(/*getBotAddress_timeout*/); // TODO: implement
String line = io.readLineNoBlock();
if (startsWithIgnoreCase(line, searchPattern))
return port + subBot;
} finally {
io.close();
}
} catch (Exception e) {
e.printStackTrace();
}
List bots = quickBotScan();
// find top-level bots
for (ProgramScan.Program p : bots) {
if (indexOfIgnoreCase(p.helloString, searchPattern) == 0) { // strict matching - start of hello string only, but case-insensitive
return p.port + subBot;
}
}
// find sub-bots
for (ProgramScan.Program p : bots) {
String botName = firstPartOfHelloString(p.helloString);
boolean isVM = startsWithIgnoreCase(p.helloString, "This is a JavaX VM.");
boolean shouldRecurse = startsWithIgnoreCase(botName, "Multi-Port") || isVM;
if (shouldRecurse) try {
Map subBots = (Map) unstructure(sendToLocalBot(p.port, "list bots"));
for (Number vport : subBots.keySet()) {
String name = subBots.get(vport);
if (startsWithIgnoreCase(name, searchPattern))
return p.port + "/" + vport;
}
} catch (Exception e) { e.printStackTrace(); }
}
return null;
}
static void sleepSeconds(long s) {
if (s > 0) sleep(s*1000);
}
static List quickBotScan() {
return ProgramScan.quickBotScan();
}
static List quickBotScan(int[] preferredPorts) {
return ProgramScan.quickBotScan(preferredPorts);
}
static List quickBotScan(String searchPattern) {
List l = new ArrayList();
for (ProgramScan.Program p : ProgramScan.quickBotScan())
if (indexOfIgnoreCase(p.helloString, searchPattern) == 0)
l.add(p);
return l;
}
public static long parseSnippetID(String snippetID) {
long id = Long.parseLong(shortenSnippetID(snippetID));
if (id == 0) fail("0 is not a snippet ID");
return id;
}
static boolean preferCached = false;
public static String loadSnippet(String snippetID) {
try {
return loadSnippet(parseSnippetID(snippetID), preferCached);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static String loadSnippet(String snippetID, boolean preferCached) throws IOException {
return loadSnippet(parseSnippetID(snippetID), preferCached);
}
public static String loadSnippet(long snippetID, boolean preferCached) throws IOException {
String text = getSnippetFromBossBot(snippetID);
if (text != null) return text;
initSnippetCache();
text = DiskSnippetCache_get(snippetID);
if (preferCached && text != null)
return text;
try {
if (text != null) System.err.println("md5: " + md5(text));
URL url = new URL("http://tinybrain.de:8080/getraw.php?id=" + snippetID + "&utf8=1");
text = loadPage(url);
} catch (RuntimeException e) {
e.printStackTrace();
throw new IOException("Snippet #" + snippetID + " not found or not public");
}
try {
initSnippetCache();
DiskSnippetCache_put(snippetID, text);
} catch (IOException e) {
System.err.println("Minor warning: Couldn't save snippet to cache (" + DiskSnippetCache_getDir() + ")");
}
return text;
}
static File DiskSnippetCache_dir;
public static void initDiskSnippetCache(File dir) {
DiskSnippetCache_dir = dir;
dir.mkdirs();
}
public static synchronized String DiskSnippetCache_get(long snippetID) throws IOException {
return loadTextFile(DiskSnippetCache_getFile(snippetID).getPath(), null);
}
private static File DiskSnippetCache_getFile(long snippetID) {
return new File(DiskSnippetCache_dir, "" + snippetID);
}
public static synchronized void DiskSnippetCache_put(long snippetID, String snippet) throws IOException {
saveTextFile(DiskSnippetCache_getFile(snippetID).getPath(), snippet);
}
public static File DiskSnippetCache_getDir() {
return DiskSnippetCache_dir;
}
public static void initSnippetCache() {
if (DiskSnippetCache_dir == null)
initDiskSnippetCache(new File(System.getProperty("user.home"), ".tinybrain/snippet-cache"));
}
static void startBotVM() {
startBotSeparateVM("This is a JavaX VM. Bot VM.", "#1001710");
}
static String fromUTF8(byte[] bytes) {
return fromUtf8(bytes);
}
public static void copyStream(InputStream in, OutputStream out) throws IOException {
byte[] buf = new byte[65536];
while (true) {
int n = in.read(buf);
if (n <= 0) return;
out.write(buf, 0, n);
}
}
static String shortenSnippetID(String snippetID) {
if (snippetID.startsWith("#"))
snippetID = snippetID.substring(1);
String httpBlaBla = "http://tinybrain.de/";
if (snippetID.startsWith(httpBlaBla))
snippetID = snippetID.substring(httpBlaBla.length());
return "" + parseLong(snippetID);
}
static class DynamicObject {
String className;
Map fieldValues = new TreeMap();
}
static Object unstructure(String text) {
return unstructure(text, false);
}
// actually it's now almost the same as jsonDecode :)
static Object unstructure(String text, final boolean allDynamic) {
final List tok = javaTok(text);
class X {
int i = 1;
Object parse() {
String t = tok.get(i);
if (t.startsWith("\"")) {
String s = unquote(tok.get(i));
i += 2;
return s;
}
if (t.equals("hashset"))
return parseHashSet();
if (t.equals("treeset"))
return parseTreeSet();
if (t.equals("hashmap"))
return parseHashMap();
if (t.equals("{"))
return parseMap();
if (t.equals("["))
return parseList();
if (t.equals("array"))
return parseArray();
if (t.equals("class"))
return parseClass();
if (t.equals("bigint"))
return parseBigInt();
if (t.equals("d"))
return parseDouble();
if (t.equals("l"))
return parseLisp();
if (t.equals("null")) {
i += 2; return null;
}
if (t.equals("false")) {
i += 2; return false;
}
if (t.equals("true")) {
i += 2; return true;
}
if (t.equals("-")) {
t = tok.get(i+2);
i += 4;
t = dropSuffix("L", t);
long l = -Long.parseLong(t);
return l == (int) l ? (int) l : l;
}
if (isInteger(t) || isLongConstant(t)) {
i += 2;
t = dropSuffix("L", t);
long l = Long.parseLong(t);
return l == (int) l ? (int) l : l;
}
if (isJavaIdentifier(t)) {
Class c = allDynamic ? null : findClass(t);
DynamicObject dO = null;
Object o = null;
if (c != null)
o = nuObject(c);
else {
dO = new DynamicObject();
dO.className = t;
}
i += 2;
if (i < tok.size() && tok.get(i).equals("(")) {
consume("(");
while (!tok.get(i).equals(")")) {
// It's like parsing a map.
//Object key = parse();
//if (tok.get(i).equals(")"))
// key = onlyField();
String key = unquote(tok.get(i));
i += 2;
consume("=");
Object value = parse();
if (o != null)
setOpt(o, key, value);
else
dO.fieldValues.put(key, value);
if (tok.get(i).equals(",")) i += 2;
}
consume(")");
}
return o != null ? o : dO;
}
throw new RuntimeException("Unknown token " + (i+1) + ": " + t);
}
Object parseSet(Set set) {
set.addAll((List) parseList());
return set;
}
Object parseLisp() {
consume("l");
consume("(");
List list = new ArrayList();
while (!tok.get(i).equals(")")) {
list.add(parse());
if (tok.get(i).equals(",")) i += 2;
}
consume(")");
return newObject("main$Lisp", (String) list.get(0), subList(list, 1));
}
Object parseList() {
consume("[");
List list = new ArrayList();
while (!tok.get(i).equals("]")) {
list.add(parse());
if (tok.get(i).equals(",")) i += 2;
}
consume("]");
return list;
}
Object parseArray() {
consume("array");
consume("{");
List list = new ArrayList();
while (!tok.get(i).equals("}")) {
list.add(parse());
if (tok.get(i).equals(",")) i += 2;
}
consume("}");
return list.toArray();
}
Object parseClass() {
consume("class");
consume("(");
String name = tok.get(i);
i += 2;
consume(")");
Class c = allDynamic ? null : findClass(name);
if (c != null) return c;
DynamicObject dO = new DynamicObject();
dO.className = "java.lang.Class";
dO.fieldValues.put("name", name);
return dO;
}
Object parseBigInt() {
consume("bigint");
consume("(");
String val = tok.get(i);
i += 2;
if (eq(val, "-")) {
val = "-" + tok.get(i);
i += 2;
}
consume(")");
return new BigInteger(val);
}
Object parseDouble() {
consume("d");
consume("(");
String val = unquote(tok.get(i));
i += 2;
consume(")");
return Double.parseDouble(val);
}
Object parseHashMap() {
consume("hashmap");
return parseMap(new HashMap());
}
Object parseHashSet() {
consume("hashset");
return parseSet(new HashSet());
}
Object parseTreeSet() {
consume("treeset");
return parseSet(new TreeSet());
}
Object parseMap() {
return parseMap(new TreeMap());
}
Object parseMap(Map map) {
consume("{");
while (!tok.get(i).equals("}")) {
Object key = unstructure(tok.get(i));
i += 2;
consume("=");
Object value = parse();
map.put(key, value);
if (tok.get(i).equals(",")) i += 2;
}
consume("}");
return map;
}
void consume(String s) {
if (!tok.get(i).equals(s)) {
String prevToken = i-2 >= 0 ? tok.get(i-2) : "";
String nextTokens = join(tok.subList(i, Math.min(i+4, tok.size())));
fail(quote(s) + " expected: " + prevToken + " " + nextTokens + " (" + i + "/" + tok.size() + ")");
}
i += 2;
}
}
return new X().parse();
}
static void sleep(long ms) {
try {
Thread.sleep(ms);
} catch (Exception e) { throw new RuntimeException(e); }
}
static void sleep() { try {
print("Sleeping.");
synchronized(main.class) { main.class.wait(); }
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static ThreadLocal loadPage_charset = new ThreadLocal();
public static String loadPageSilently(String url) {
try {
return loadPageSilently(new URL(loadPage_preprocess(url)));
} catch (IOException e) { throw new RuntimeException(e); }
}
public static String loadPageSilently(URL url) {
try {
IOException e = null;
for (int tries = 0; tries < 60; tries++)
try {
URLConnection con = url.openConnection();
return loadPage(con, url);
} catch (IOException _e) {
e = _e;
print("Retrying because of: " + e);
sleepSeconds(1);
}
throw e;
} catch (IOException e) { throw new RuntimeException(e); }
}
static String loadPage_preprocess(String url) {
if (url.startsWith("tb/"))
url = "tinybrain.de:8080/" + url;
if (url.indexOf("://") < 0)
url = "http://" + url;
return url;
}
public static String loadPage(String url) {
try {
return loadPage(new URL(loadPage_preprocess(url)));
} catch (IOException e) { throw new RuntimeException(e); }
}
public static String loadPage(URL url) {
print("Loading: " + url.toExternalForm());
return loadPageSilently(url);
}
public static String loadPage(URLConnection con, URL url) throws IOException {
String contentType = con.getContentType();
if (contentType == null)
throw new IOException("Page could not be read: " + url);
//print("Content-Type: " + contentType);
String charset = loadPage_charset.get();
if (charset == null) charset = loadPage_guessCharset(contentType);
Reader r = new InputStreamReader(con.getInputStream(), charset);
StringBuilder buf = new StringBuilder();
while (true) {
int ch = r.read();
if (ch < 0)
break;
//Log.info("Chars read: " + buf.length());
buf.append((char) ch);
}
return buf.toString();
}
static String loadPage_guessCharset(String contentType) {
Pattern p = Pattern.compile("text/[a-z]+;\\s+charset=([^\\s]+)\\s*");
Matcher m = p.matcher(contentType);
/* If Content-Type doesn't match this pre-conception, choose default and hope for the best. */
return m.matches() ? m.group(1) : "ISO-8859-1";
}
static String[] toStringArray(List list) {
return list.toArray(new String[list.size()]);
}
static String[] toStringArray(Object o) {
if (o instanceof String[])
return (String[]) o;
else if (o instanceof List)
return toStringArray((List) o);
else
throw fail("Not a list or array: " + structure(o));
}
static File getGlobalCache() {
File file = new File(userHome(), ".tinybrain/snippet-cache");
file.mkdirs();
return file;
}
static String sendToLocalBot(String bot, String text, Object... args) {
text = format3(text, args);
DialogIO channel = findBot(bot);
if (channel == null)
fail(quote(bot) + " not found");
try {
channel.readLine();
print(bot + "> " + shorten(text, 80));
channel.sendLine(text);
String s = channel.readLine();
print(bot + "< " + shorten(s, 80));
return s;
} catch (Throwable e) {
e.printStackTrace();
return null;
} finally {
channel.close();
}
}
static String sendToLocalBot(int port, String text, Object... args) {
text = format3(text, args);
DialogIO channel = talkTo(port);
try {
channel.readLine();
print(port + "> " + shorten(text, 80));
channel.sendLine(text);
String s = channel.readLine();
print(port + "< " + shorten(s, 80));
return s;
} catch (Throwable e) {
e.printStackTrace();
return null;
} finally {
if (channel != null)
channel.close();
}
}
// works on lists and strings and null
static int indexOfIgnoreCase(Object a, Object b) {
if (a == null) return -1;
if (a instanceof String) {
Matcher m = Pattern.compile((String) b, Pattern.CASE_INSENSITIVE + Pattern.LITERAL).matcher((String) a);
if (m.find()) return m.start(); else return -1;
}
if (a instanceof List) {
for (int i = 0; i < ((List) a).size(); i++) {
Object o = ((List) a).get(i);
if (o != null && ((String) o).equalsIgnoreCase((String) b))
return i;
}
return -1;
}
throw fail("Unknown type: " + a);
}
static boolean startsWithIgnoreCase(String a, String b) {
return a != null && a.regionMatches(true, 0, b, 0, b.length());
}
static String md5(String text) { try {
if (text == null) return "-";
return bytesToHex(md5_impl(text.getBytes("UTF-8"))); // maybe different than the way PHP does it...
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static String md5(byte[] data) {
return bytesToHex(md5_impl(data));
}
static byte[] md5_impl(byte[] data) {
try {
return MessageDigest.getInstance("MD5").digest(data);
} catch (Exception e) { throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e); }
}
static String md5(File file) { try {
return md5(loadBinaryFile(file));
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static String firstPartOfHelloString(String s) {
int i = s.lastIndexOf('/');
return i < 0 ? s : rtrim(s.substring(0, i));
}
static boolean isInteger(String s) {
return s != null && Pattern.matches("\\-?\\d+", s);
}
static String getSnippetFromBossBot(long snippetID) {
return boss(format3("get text for *", snippetID));
}
/** writes safely (to temp file, then rename) */
public static void saveTextFile(String fileName, String contents) throws IOException {
File file = new File(fileName);
File parentFile = file.getParentFile();
if (parentFile != null)
parentFile.mkdirs();
String tempFileName = fileName + "_temp";
if (contents != null) {
FileOutputStream fileOutputStream = new FileOutputStream(tempFileName);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");
PrintWriter printWriter = new PrintWriter(outputStreamWriter);
printWriter.print(contents);
printWriter.close();
}
if (file.exists() && !file.delete())
throw new IOException("Can't delete " + fileName);
if (contents != null)
if (!new File(tempFileName).renameTo(file))
throw new IOException("Can't rename " + tempFileName + " to " + fileName);
}
public static void saveTextFile(File fileName, String contents) {
try {
saveTextFile(fileName.getPath(), contents);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/** writes safely (to temp file, then rename) */
public static void saveBinaryFile(String fileName, byte[] contents) throws IOException {
File file = new File(fileName);
File parentFile = file.getParentFile();
if (parentFile != null)
parentFile.mkdirs();
String tempFileName = fileName + "_temp";
FileOutputStream fileOutputStream = new FileOutputStream(tempFileName);
fileOutputStream.write(contents);
fileOutputStream.close();
if (file.exists() && !file.delete())
throw new IOException("Can't delete " + fileName);
if (!new File(tempFileName).renameTo(file))
throw new IOException("Can't rename " + tempFileName + " to " + fileName);
}
static void saveBinaryFile(File fileName, byte[] contents) {
try {
saveBinaryFile(fileName.getPath(), contents);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static String loadTextFile(String fileName) {
try {
return loadTextFile(fileName, null);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static String loadTextFile(String fileName, String defaultContents) throws IOException {
if (!new File(fileName).exists())
return defaultContents;
FileInputStream fileInputStream = new FileInputStream(fileName);
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8");
return loadTextFile(inputStreamReader);
}
public static String loadTextFile(File fileName) {
try {
return loadTextFile(fileName, null);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static String loadTextFile(File fileName, String defaultContents) throws IOException {
try {
return loadTextFile(fileName.getPath(), defaultContents);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static String loadTextFile(Reader reader) throws IOException {
StringBuilder builder = new StringBuilder();
try {
char[] buffer = new char[1024];
int n;
while (-1 != (n = reader.read(buffer)))
builder.append(buffer, 0, n);
} finally {
reader.close();
}
return builder.toString();
}
static byte[] loadBinaryPage(String url) throws IOException {
return loadBinaryPage(new URL(url).openConnection());
}
public static byte[] loadBinaryPage(URLConnection con) throws IOException {
//setHeaders(con);
ByteArrayOutputStream buf = new ByteArrayOutputStream();
InputStream inputStream = con.getInputStream();
int n = 0;
while (true) {
int ch = inputStream.read();
if (ch < 0)
break;
buf.write(ch);
if (++n % 100000 == 0)
System.err.println(" " + n + " bytes loaded.");
}
inputStream.close();
return buf.toByteArray();
}
static String fromUtf8(byte[] bytes) { try {
return new String(bytes, "UTF-8");
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static void startBotSeparateVM(String botName, String scriptID) {
DialogIO bot = findBot(botName);
if (bot != null)
bot.close();
else {
print("Bot " + quote(botName) + " not found. Starting separately: " + scriptID);
nohupJavax(scriptID);
waitForBotStartUp(botName);
}
}
// currently finds only inner classes of class "main"
// returns null on not found
// this is the simple version that is not case-tolerant
static Class findClass(String name) {
try {
return Class.forName("main$" + name);
} catch (ClassNotFoundException e) {
return null;
}
}
static String boss(String line) {
try {
//S s = sendToLocalBotOpt("Boss Bot", line);
DialogIO io = talkTo(4990); // Boss Bot port
io.readLine();
io.sendLine(line);
String s = io.readLine();
Matches m = new Matches();
if (match3("text: *", s, m))
return unquote(m.m[0]);
return null;
} catch (Exception e) {
//e.printStackTrace();
return null;
}
}
static String _userHome;
static String userHome() {
if (_userHome == null) {
if (isAndroid())
_userHome = "/storage/sdcard0/";
else
_userHome = System.getProperty("user.home");
//System.out.println("userHome: " + _userHome);
}
return _userHome;
}
static boolean isLongConstant(String s) {
if (!s.endsWith("L")) return false;
s = s.substring(0, l(s)-1);
return isInteger(s);
}
static void nohupJavax(String javaxargs) {
nohupJavax(javaxargs, "");
}
// vm args are ignored if pre-spun VM found...
static void nohupJavax(String javaxargs, String vmArgs) {
javaxargs = javaxargs.trim();
if (javaxargs.startsWith("#")) javaxargs = javaxargs.substring(1);
String snippetID = javaTok(javaxargs).get(1);
int idx = javaxargs.indexOf(' ');
String args = idx < 0 ? "" : javaxargs.substring(idx+1).trim();
String line;
if (args.length() != 0)
line = format3("please start program * with arguments *", snippetID, args);
else
line = format3("please start program *", snippetID);
String answer = sendToLocalBotOpt("A pre-spun VM.", line);
if (match3("ok", answer)) {
print("OK, used pre-spun VM.");
} else {
if (answer != null)
print("> " + answer);
print("Using standard nohup.");
classicNohupJavax(javaxargs, vmArgs);
}
}
static long parseLong(String s) {
return Long.parseLong(s);
}
public static String bytesToHex(byte[] bytes) {
return bytesToHex(bytes, 0, bytes.length);
}
public static String bytesToHex(byte[] bytes, int ofs, int len) {
StringBuilder stringBuilder = new StringBuilder(len*2);
for (int i = 0; i < len; i++) {
String s = "0" + Integer.toHexString(bytes[ofs+i]);
stringBuilder.append(s.substring(s.length()-2, s.length()));
}
return stringBuilder.toString();
}
static double parseDouble(String s) {
return Double.parseDouble(s);
}
public static String rtrim(String s) {
int i = s.length();
while (i > 0 && " \t\r\n".indexOf(s.charAt(i-1)) >= 0)
--i;
return i < s.length() ? s.substring(0, i) : s;
}
public static byte[] loadBinaryFile(String fileName) throws IOException {
if (!new File(fileName).exists())
return null;
FileInputStream in = new FileInputStream(fileName);
byte buf[] = new byte[1024];
ByteArrayOutputStream out = new ByteArrayOutputStream();
int l;
while (true) {
l = in.read(buf);
if (l <= 0) break;
out.write(buf, 0, l);
}
in.close();
return out.toByteArray();
}
public static byte[] loadBinaryFile(File file) throws IOException {
return loadBinaryFile(file.getPath());
}
static void classicNohupJavax(String javaxargs) {
classicNohupJavax(javaxargs, "");
}
static void classicNohupJavax(String javaxargs, String vmArgs) { try {
int x = latestInstalledJavaX();
File xfile = new File(userHome(), ".javax/x" + Math.max(x, 30) + ".jar");
if (!xfile.isFile()) {
String url = "http://tinybrain.de/x30.jar";
byte[] data = loadBinaryPage(url);
if (data.length < 1000000)
fail("Could not load " + url);
saveBinaryFile(xfile.getPath(), data);
}
String jarPath = xfile.getPath();
if (javaxargs.startsWith("#")) javaxargs = javaxargs.substring(1);
nohup("java " + vmArgs + " -jar " + (isWindows() ? winQuote(jarPath) : bashQuote(jarPath)) + " " + javaxargs);
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
static String sendToLocalBotOpt(String bot, String text) {
if (bot == null) return null;
DialogIO channel = findBot(bot);
if (channel == null) {
print(quote(bot) + " not found, skipping send: " + quote(text));
return null;
}
try {
channel.readLine();
print(bot + "> " + text);
channel.sendLine(text);
String s = channel.readLine();
print(bot + "< " + s);
return s;
} catch (Throwable e) {
e.printStackTrace();
return null;
} finally {
channel.close();
}
}
static boolean isAndroid() { return System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0; }
public static boolean isWindows() {
return System.getProperty("os.name").contains("Windows");
}
/** possibly improvable */
public static String bashQuote(String text) {
if (text == null) return null;
return "\"" + text
.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r") + "\"";
}
/** possibly improvable */
public static String winQuote(String text) {
if (text == null) return null;
return "\"" + text
.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r") + "\"";
}
static int latestInstalledJavaX() {
File[] files = new File(userHome(), ".javax").listFiles();
int v = 0;
if (files != null) for (File f : files) {
Matcher m = Pattern.compile("x(\\d\\d\\d?)\\.jar").matcher(f.getName());
if (m.matches())
v = Math.max(v, Integer.parseInt(m.group(1)));
}
return v;
}
public static File nohup(String cmd) throws IOException {
File outFile = File.createTempFile("nohup_" + nohup_sanitize(cmd), ".out");
nohup(cmd, outFile, false);
return outFile;
}
static String nohup_sanitize(String s) {
return s.replaceAll("[^a-zA-Z0-9\\-_]", "");
}
/** outFile takes stdout and stderr. */
public static void nohup(String cmd, File outFile, boolean append) throws IOException {
String command = nohup_makeNohupCommand(cmd, outFile, append);
File scriptFile = File.createTempFile("_realnohup", isWindows() ? ".bat" : "");
System.out.println("[Nohup] " + command);
try {
//System.out.println("[RealNohup] Script file: " + scriptFile.getPath());
saveTextFile(scriptFile.getPath(), command);
String[] command2;
if (isWindows())
command2 = new String[] {"cmd", "/c", "start", "/b", scriptFile.getPath() };
else
command2 = new String[] {"/bin/bash", scriptFile.getPath() };
Process process = Runtime.getRuntime().exec(command2);
try {
process.waitFor();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int value = process.exitValue();
//System.out.println("exit value: " + value);
} finally {
if (!isWindows())
scriptFile.delete();
}
}
public static String nohup_makeNohupCommand(String cmd, File outFile, boolean append) {
mkdirsForFile(outFile);
String command;
if (isWindows())
command = cmd + (append ? " >>" : " >") + winQuote(outFile.getPath()) + " 2>&1";
else
command = "nohup " + cmd + (append ? " >>" : " >") + bashQuote(outFile.getPath()) + " 2>&1 &";
return command;
}
public static void mkdirsForFile(File file) {
File dir = file.getParentFile();
if (dir != null) // is null if file is in current dir
dir.mkdirs();
}
static class EvalTransform {
int varCount;
Exception exception;
boolean recurse = true;
Lisp newVar() {
return lisp("$e" + (++varCount));
}
Lisp transformEval(Lisp tree, List out) {
if (tree.is("[]", 2) && tree.get(0).is("eval", 0) && tree.get(1).is("()", 1)) {
Lisp exp = tree.get(1).get(0);
if (recurse)
exp = transformEval(exp, out);
Lisp var = newVar();
out.add(lisp("[]", var, "=").addAll(nlUnroll(exp)));
return var;
}
// recurse
Lisp n = new Lisp(tree.head);
for (Lisp t : tree)
n.add(transformEval(t, out));
return n;
}
Lisp evalTransformRule(Lisp rule) {
try {
List ops = snlSplitOps(rule);
// expand unconditional rule
if (ops == null || ops.isEmpty() || !ops.get(0).is("if *", 1))
ops = litlist(lisp("then *", rule));
for (int i = 0; i < l(ops); i++) {
Lisp op = ops.get(i).get(0);
List out = new ArrayList();
Lisp op2 = transformEval(op, out);
if (out.isEmpty()) continue;
for (Lisp l : out) {
ops.add(i, lisp(i == 0 ? "if *" : "and *", l));
++i;
}
ops.set(i, lisp(i == l(ops)-1 ? "then *" : "and *", op2));
}
return snlJoinOps(ops);
} catch (Exception e) {
exception = e;
return rule;
}
}
Lisp evalTransformQuestion(Lisp question) {
try {
List out = new ArrayList();
Lisp tree = transformEval(question, out);
for (int i = l(out)-1; i >= 0; i--)
tree = lisp("[]", out.get(i), "and", tree);
return tree;
} catch (Exception e) {
exception = e;
return question;
}
}
}
static class ProgramScan {
static int threads = isWindows() ? 500 : 10;
static int timeout = 5000; // hmm...
static String ip = "127.0.0.1";
static int quickScanFrom = 10000, quickScanTo = 10999;
static int maxNumberOfBotPorts = 100;
static boolean verbose;
static class Program {
int port;
String helloString;
Program(int port, String helloString) {
this.helloString = helloString;
this.port = port;}
}
static List