!636 !modern main { static S programID; psvm { if (args.length == 0) { print("Hello, I'm The Learner."); L log = loadLog(); print("Last log entries:"); for (int i = Math.max(0, log.size()-2); i < log.size(); i++) print(" " + log.get(i)); return; } S a = args[0]; List prog = null, intent = null; if (a == "mappings") { print(structure(allMappings())); return; } if (a == "good" || a == "ok") { L log = loadLog(); /*for (int i = 0; i < log.size(); i++) print(i + ": " + log.get(i));*/ if (log.size() < 2) confused("Too few log lines."); prog = (List) unstructure(log.get(log.size()-1)); intent = (List) unstructure(log.get(log.size()-2)); print("prog: " + prog); print("intent: " + intent); if (!prog.get(0).equals("program")) fail("prog: wrong type"); if (!intent.get(0).equals("intent")) fail("intent: wrong type"); S p = prog.get(1), i = intent.get(1); if (a != "ok") { print("map? (type 'ok')"); } else { S map = findMapping(i); if (map != null) print("Remapping from " + map); if (p.equals(map)) print("Same mapping exists, skipping."); else { print("Mapping."); addMapping(intent.get(1), prog.get(1)); map = findMapping(i); boolean sane = p.equals(map); print("Done. Sanity check " + (sane ? "OK" : "not OK")); } } } else if (isSnippetID(a)) { logProgramRun(args); Class c = hotwire(a); callMain(c, drop(1, args)); } else { boolean ok = args[args.length-1].equals("ok"); if (ok) args = dropLast(args, 1); logIntent(args); S i = smartJoin(args); S map = findMapping(i); if (map == null) print("No mapping found for intent"); else { if (ok) { print("Invoking program: #" + map); S[] _args = map.split(" "); Class c = hotwire(_args[0]); callMain(c, drop(1, _args)); } else { print("Suggested program: #" + map); print("Type " + quote(i + " ok") + " to invoke."); } } } } static S logFile() { return new File(userHome(), ".javax/" + programID + "/learnerlog.txt").getPath(); } static S mapFile() { return new File(userHome(), ".javax/" + programID + "/learnermap.txt").getPath(); } static S smartJoin(S[] list) { return join(" ", list); } static void logIntent(S[] args) { stringLogAppend(structure(new Object[] {"intent", smartJoin(args)})); print("Logged intent: " + smartJoin(args)); } static void logProgramRun(S[] args) { stringLogAppend(structure(new Object[] {"program", smartJoin(args)})); } static void stringLogAppend(S s) { appendToFile(logFile(), "\n" + s + "\n"); } static void appendToFile(String path, String s) ctex { new File(path).getParentFile().mkdirs(); print("[Logging to " + path + "]"); Writer writer = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(path, true), "UTF-8")); writer.write(s); writer.close(); } static L loadLog() ctex { return toLinesFullTrim(readTextFile(logFile(), "")); } static class Consumer { L list; int i; *(L *list) {} S get() { return i < list.size() ? list.get(i) : null; } void next() { if (i < list.size()) ++i; } void next(int n) { i = Math.min(list.size(), i+n); } boolean has() { return i < list.size(); } boolean is(S s) { return s.equals(get()); } boolean isNot(S s) { return !s.equals(get()); } } static O unstructure(S s) { L tok = javaTok(s); Consumer con = new Consumer(tok); con.next(); // skip whitespace return unstructure(con); } static O unstructure(Consumer con) { S t = con.get(); if (t.startsWith("\"")) { con.next(2); return unquote(t); } if (t.equals("null")) { con.next(2); return null; } if (t.equals("{")) { new L list; con.next(2); while (con.has() && con.isNot("}")) { if (!con.has()) break; if (con.is(",")) con.next(2); O o = unstructure(con); list.add(o); } if (con.is("}")) con.next(2); return list; } throw todo(t); } static void addMapping(S intent, S prog) { S s = structure(new Object[] {"map", intent, prog}); appendToFile(mapFile(), "\n" + s + "\n"); } static Map allMappings() ctex { L list = toLinesFullTrim(readTextFile(mapFile(), "")); new Map map; for (int i = list.size()-1; i >= 0; i--) { // start from end to get newest mappings S s = list.get(i); List x = cast unstructure(s); if (x.get(0).equals("map") && !map.containsKey(x.get(1))) map.put((S) x.get(1), (S) x.get(2)); } return map; } static S findMapping(S intent) ctex { L list = toLinesFullTrim(readTextFile(mapFile(), "")); for (int i = list.size()-1; i >= 0; i--) { // start from end to get newest mappings S s = list.get(i); List x = cast unstructure(s); if (x.get(0).equals("map") && x.get(1).equals(intent)) return (S) x.get(2); } return null; } }