!636 (modern) public class main { !include #1001496 // Matches !include #1001065 // DialogIO static boolean debug = false; static new Map cache; public static void main(String[] args) throws IOException { L standardFunctions = (L) loadVariableDefinition( cacheGet("#761"), "standardFunctions"); long startTime = now(); String s = loadMainJava(); Map sf = new HashMap(); for (String x : standardFunctions) { String[] f = x.split("/"); sf.put(f[1], f[0]); } for (int i = 0; ; i++) { Set defd = new HashSet(findFunctions(s)); List tok = javaTok(s); // changes tok Set invocations = findFunctionInvocations(tok, sf); s = join(tok); print("Functions invoked: " + structure(invocations)); List needed = diff(invocations, defd); print("Functions needed: " + structure(needed)); if (needed.isEmpty()) break; new L added; new StringBuilder buf; for (String x : needed) { if (defd.contains(x)) continue; String id = sf.get(x); //print("Adding function: " + x + " (" + id + ")"); S function = cacheGet(id); if (("\n" + function).contains("\n!")) print("Warning: " + id + " contains translators."); buf.append(function).append("\n"); added.add(x); defd.addAll(findFunctions(function)); } s = addFunctions(s, str(buf)); defd = new HashSet(findFunctions(s)); print("Functions added: " + structure(added)); for (String x : needed) if (!defd.contains(x)) fail("Function not defined properly: " + x); print("Iteration " + (i+2)); if (i >= 1000) fail("Too many iterations"); } saveMainJava(s); print("629: " + (now()-startTime) + " ms"); } static boolean substringIs(String s, int i, String pat) { return i >= 0 && i+pat.length() < s.length() && s.substring(i, i+pat.length()).equals(pat); } // OK, this should be fast enough. static List findFunctions(String src) { int idx = src.indexOf("main {"); // yes it's a hack... if (idx >= 0) src = src.substring(idx); return findFunctionDefinitions(src); } public static String addFunction(String s, String fID) throws IOException { int i = s.lastIndexOf('}'); S function = cacheGet(fID); if (("\n" + function).contains("\n!")) print("Warning: " + fID + " contains translators."); return s.substring(0, i) + "\n" + function +"\n" + s.substring(i); } public static String addFunctions(String s, String functions) throws IOException { int i = s.lastIndexOf('}'); return s.substring(0, i) + "\n" + functions + s.substring(i); } public static List toLines(String s) { List lines = new ArrayList(); int start = 0; while (true) { int i = toLines_nextLineBreak(s, start); if (i < 0) { if (s.length() > start) lines.add(s.substring(start)); break; } lines.add(s.substring(start, i)); if (s.charAt(i) == '\r' && i+1 < s.length() && s.charAt(i+1) == '\n') i += 2; else ++i; start = i; } return lines; } private static int toLines_nextLineBreak(String s, int start) { for (int i = start; i < s.length(); i++) { char c = s.charAt(i); if (c == '\r' || c == '\n') return i; } return -1; } static String mainJava; static String loadMainJava() throws IOException { if (mainJava != null) return mainJava; return loadTextFile("input/main.java", ""); } static void saveMainJava(String s) throws IOException { if (mainJava != null) mainJava = s; else saveTextFile("output/main.java", s); } static void saveMainJava(List tok) throws IOException { saveMainJava(join(tok)); } public static long parseSnippetID(String snippetID) { return Long.parseLong(shortenSnippetID(snippetID)); } private 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 snippetID; } public static boolean isSnippetID(String snippetID) { snippetID = shortenSnippetID(snippetID); return isInteger(snippetID) && Long.parseLong(snippetID) != 0; } public static boolean isInteger(String s) { return Pattern.matches("\\-?\\d+", s); } private static String loadPage(URL url) throws IOException { print("Loading: " + url.toExternalForm()); URLConnection con = url.openConnection(); return loadPage(con, 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); //Log.info("Content-Type: " + contentType); String charset = 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(); } public static String guessCharset(String contentType) { Pattern p = Pattern.compile("text/html;\\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 RuntimeException fail() { throw new RuntimeException("fail"); } static RuntimeException fail(Object msg) { throw new RuntimeException(String.valueOf(msg)); } // 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) { ++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))); 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 ++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)); } 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); } // leaves tok properly tokenized // returns true iff anything was replaced static boolean jreplace(List tok, String in, String out) { return jreplace(tok, in, out, false, true); } static boolean jreplace(List tok, String in, String out, boolean ignoreCase, boolean reTok) { List tokin = javaTok(in); replaceSublist(tokin, litlist("<", "", "quoted", "", ">"), litlist("")); replaceSublist(tokin, litlist("<", "", "id", "", ">"), litlist("")); boolean anyChange = false; for (int n = 0; n < 10000; n++) { int i = findCodeTokens(tok, ignoreCase, toStringArray(codeTokensOnly(tokin))); if (i < 0) { if (anyChange && reTok) replaceCollection(tok, javaTok(tok)); return anyChange; } List subList = tok.subList(i-1, i+l(tokin)-1); // N to N String expansion = jreplace_expandRefs(out, subList); clearAllTokens(tok.subList(i, i+l(tokin)-2)); // code to code tok.set(i, expansion); anyChange = true; } throw fail("woot? 10000!"); } // "$1" is first code token, "$2" second code token etc. static String jreplace_expandRefs(String s, List tokref) { List tok = javaTok(s); for (int i = 1; i < l(tok)-2; i += 2) { if (tok.get(i).startsWith("$") && isInteger(tok.get(i).substring(1))) { String x = tokref.get(-1+parseInt(tok.get(i).substring(1))*2); tok.set(i, x); } } return join(tok); } static void replaceToken(List tok, String in, String out) { renameToken(tok, in, out); } static void replaceCollection(Collection dest, Collection src) { dest.clear(); dest.addAll(src); } static ArrayList litlist(A... a) { return new ArrayList(Arrays.asList(a)); } 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; } static void replaceSublist(List l, List x, List y) { int i = 0; while (true) { i = indexOfSubList(l, x, i); if (i < 0) return; // It's inefficient :D for (int j = 0; j < l(x); j++) l.remove(i); l.addAll(i, y); i += l(y); } } static int l(Object[] 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 int parseInt(String s) { return Integer.parseInt(s); } static void renameToken(List tok, String in, String out) { int renames = 0; for (int i = 1; i < tok.size(); i += 2) { if (tok.get(i).equals(in)) { tok.set(i, out); ++renames; } } } 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: " + o); } static int findCodeTokens(List tok, String... tokens) { return findCodeTokens(tok, 1, false, tokens); } static int findCodeTokens(List tok, boolean ignoreCase, String... tokens) { return findCodeTokens(tok, 1, ignoreCase, tokens); } static int findCodeTokens(List tok, int startIdx, boolean ignoreCase, String... tokens) { outer: for (int i = startIdx | 1; i+tokens.length*2-2 < tok.size(); i += 2) { for (int j = 0; j < tokens.length; j++) { String p = tokens[j], t = tok.get(i+j*2); boolean match; if (eq(p, "*")) match = true; else if (eq(p, "")) match = isQuoted(t); else if (eq(p, "")) match = isIdentifier(t); else match = ignoreCase ? eqic(p, t) : eq(p, t); if (!match) continue outer; } return i; } return -1; } static void clearAllTokens(List tok) { for (int i = 0; i < tok.size(); i++) tok.set(i, ""); } static void clearAllTokens(List tok, int i, int j) { for (; i < j; i++) tok.set(i, ""); } static boolean isIdentifier(String s) { return isJavaIdentifier(s); } static boolean eqic(String a, String b) { if ((a == null) != (b == null)) return false; if (a == null) return true; return a.equalsIgnoreCase(b); } // 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 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 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 int indexOfSubList(List x, List y, int i) { outer: for (; i+l(y) <= l(x); i++) { for (int j = 0; j < l(y); j++) if (neq(x.get(i+j), y.get(j))) continue outer; return i; } return -1; } static boolean neq(Object a, Object b) { return !eq(a, b); } static long now() { return System.currentTimeMillis(); } static List diff(Collection a, Set b) { List l = new ArrayList(); for (String s : a) if (!b.contains(s)) l.add(s); return l; } static int jfind(List tok, String in) { List tokin = javaTok(in); replaceSublist(tokin, litlist("<", "", "quoted", "", ">"), litlist("")); replaceSublist(tokin, litlist("<", "", "id", "", ">"), litlist("")); return findCodeTokens(tok, false, toStringArray(codeTokensOnly(tokin))); } static S cacheGet(S id) ctex { id = formatSnippetID(id); S text = cache.get(id); if (text != null) { //print("Snippet CACHED " + fID); } else { //print("Snippet LOAD " + fID); text = loadSnippet(id, false); cache.put(id, text); } ret text; } }