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 java.util.concurrent.locks.*; 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 { static boolean autoQuine = true; static int maxQuineLength = 80; static List functionsToAlwaysInclude = ll("_registerThread"); static class Matches { String[] m; Matches() {} Matches(String... m) { this.m = m;} String get(int i) { return i < m.length ? m[i] : null; } String unq(int i) { return unquote(get(i)); } String fsi(int i) { return formatSnippetID(unq(i)); } String fsi() { return fsi(0); } String tlc(int i) { return unq(i).toLowerCase(); } boolean bool(int i) { return "true".equals(unq(i)); } String rest() { return m[m.length-1]; } // for matchStart int psi(int i) { return Integer.parseInt(unq(i)); } } /** * A class to compare vectors of objects. The result of comparison * is a list of change objects which form an * edit script. The objects compared are traditionally lines * of text from two files. Comparison options such as "ignore * whitespace" are implemented by modifying the equals * and hashcode methods for the objects compared. *

* The basic algorithm is described in:
* "An O(ND) Difference Algorithm and its Variations", Eugene Myers, * Algorithmica Vol. 1 No. 2, 1986, p 251. *

* This class outputs different results from GNU diff 1.15 on some * inputs. Our results are actually better (smaller change list, smaller * total size of changes), but it would be nice to know why. Perhaps * there is a memory overwrite bug in GNU diff 1.15. * * @author Stuart D. Gathman, translated from GNU diff 1.15 * Copyright (C) 2000 Business Management Systems, Inc. *

* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. *

* This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *

* You should have received a copy of the * GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ static class EGDiff { /** * Prepare to find differences between two arrays. Each element of * the arrays is translated to an "equivalence number" based on * the result of equals. The original Object arrays * are no longer needed for computing the differences. They will * be needed again later to print the results of the comparison as * an edit script, if desired. */ public EGDiff(Object[] a, Object[] b) { Hashtable h = new Hashtable(a.length + b.length); filevec[0] = new file_data(a, h); filevec[1] = new file_data(b, h); } /** * 1 more than the maximum equivalence value used for this or its * sibling file. */ private int equiv_max = 1; /** * When set to true, the comparison uses a heuristic to speed it up. * With this heuristic, for files with a constant small density * of changes, the algorithm is linear in the file size. */ public boolean heuristic = false; /** * When set to true, the algorithm returns a guarranteed minimal * set of changes. This makes things slower, sometimes much slower. */ public boolean no_discards = false; private int[] xvec, yvec; /* Vectors being compared. */ private int[] fdiag; /* Vector, indexed by diagonal, containing the X coordinate of the point furthest along the given diagonal in the forward search of the edit matrix. */ private int[] bdiag; /* Vector, indexed by diagonal, containing the X coordinate of the point furthest along the given diagonal in the backward search of the edit matrix. */ private int fdiagoff, bdiagoff; private final file_data[] filevec = new file_data[2]; private int cost; /** * Find the midpoint of the shortest edit script for a specified * portion of the two files. *

* We scan from the beginnings of the files, and simultaneously from the ends, * doing a breadth-first search through the space of edit-sequence. * When the two searches meet, we have found the midpoint of the shortest * edit sequence. *

* The value returned is the number of the diagonal on which the midpoint lies. * The diagonal number equals the number of inserted lines minus the number * of deleted lines (counting only lines before the midpoint). * The edit cost is stored into COST; this is the total number of * lines inserted or deleted (counting only lines before the midpoint). *

* This function assumes that the first lines of the specified portions * of the two files do not match, and likewise that the last lines do not * match. The caller must trim matching lines from the beginning and end * of the portions it is going to specify. *

* Note that if we return the "wrong" diagonal value, or if * the value of bdiag at that diagonal is "wrong", * the worst this can do is cause suboptimal diff output. * It cannot cause incorrect diff output. */ private int diag(int xoff, int xlim, int yoff, int ylim) { final int[] fd = fdiag; // Give the compiler a chance. final int[] bd = bdiag; // Additional help for the compiler. final int[] xv = xvec; // Still more help for the compiler. final int[] yv = yvec; // And more and more . . . final int dmin = xoff - ylim; // Minimum valid diagonal. final int dmax = xlim - yoff; // Maximum valid diagonal. final int fmid = xoff - yoff; // Center diagonal of top-down search. final int bmid = xlim - ylim; // Center diagonal of bottom-up search. int fmin = fmid, fmax = fmid; // Limits of top-down search. int bmin = bmid, bmax = bmid; // Limits of bottom-up search. /* True if southeast corner is on an odd diagonal with respect to the northwest. */ final boolean odd = (fmid - bmid & 1) != 0; fd[fdiagoff + fmid] = xoff; bd[bdiagoff + bmid] = xlim; for (int c = 1; ; ++c) { int d; /* Active diagonal. */ boolean big_snake = false; /* Extend the top-down search by an edit step in each diagonal. */ if (fmin > dmin) fd[fdiagoff + --fmin - 1] = -1; else ++fmin; if (fmax < dmax) fd[fdiagoff + ++fmax + 1] = -1; else --fmax; for (d = fmax; d >= fmin; d -= 2) { int x, y, oldx, tlo = fd[fdiagoff + d - 1], thi = fd[fdiagoff + d + 1]; if (tlo >= thi) x = tlo + 1; else x = thi; oldx = x; y = x - d; while (x < xlim && y < ylim && xv[x] == yv[y]) { ++x; ++y; } if (x - oldx > 20) big_snake = true; fd[fdiagoff + d] = x; if (odd && bmin <= d && d <= bmax && bd[bdiagoff + d] <= fd[fdiagoff + d]) { cost = 2 * c - 1; return d; } } /* Similar extend the bottom-up search. */ if (bmin > dmin) bd[bdiagoff + --bmin - 1] = Integer.MAX_VALUE; else ++bmin; if (bmax < dmax) bd[bdiagoff + ++bmax + 1] = Integer.MAX_VALUE; else --bmax; for (d = bmax; d >= bmin; d -= 2) { int x, y, oldx, tlo = bd[bdiagoff + d - 1], thi = bd[bdiagoff + d + 1]; if (tlo < thi) x = tlo; else x = thi - 1; oldx = x; y = x - d; while (x > xoff && y > yoff && xv[x - 1] == yv[y - 1]) { --x; --y; } if (oldx - x > 20) big_snake = true; bd[bdiagoff + d] = x; if (!odd && fmin <= d && d <= fmax && bd[bdiagoff + d] <= fd[fdiagoff + d]) { cost = 2 * c; return d; } } /* Heuristic: check occasionally for a diagonal that has made lots of progress compared with the edit distance. If we have any such, find the one that has made the most progress and return it as if it had succeeded. With this heuristic, for files with a constant small density of changes, the algorithm is linear in the file size. */ if (c > 200 && big_snake && heuristic) { int best = 0; int bestpos = -1; for (d = fmax; d >= fmin; d -= 2) { int dd = d - fmid; if ((fd[fdiagoff + d] - xoff) * 2 - dd > 12 * (c + (dd > 0 ? dd : -dd))) { if (fd[fdiagoff + d] * 2 - dd > best && fd[fdiagoff + d] - xoff > 20 && fd[fdiagoff + d] - d - yoff > 20) { int k; int x = fd[fdiagoff + d]; /* We have a good enough best diagonal; now insist that it end with a significant snake. */ for (k = 1; k <= 20; k++) if (xvec[x - k] != yvec[x - d - k]) break; if (k == 21) { best = fd[fdiagoff + d] * 2 - dd; bestpos = d; } } } } if (best > 0) { cost = 2 * c - 1; return bestpos; } best = 0; for (d = bmax; d >= bmin; d -= 2) { int dd = d - bmid; if ((xlim - bd[bdiagoff + d]) * 2 + dd > 12 * (c + (dd > 0 ? dd : -dd))) { if ((xlim - bd[bdiagoff + d]) * 2 + dd > best && xlim - bd[bdiagoff + d] > 20 && ylim - (bd[bdiagoff + d] - d) > 20) { /* We have a good enough best diagonal; now insist that it end with a significant snake. */ int k; int x = bd[bdiagoff + d]; for (k = 0; k < 20; k++) if (xvec[x + k] != yvec[x - d + k]) break; if (k == 20) { best = (xlim - bd[bdiagoff + d]) * 2 + dd; bestpos = d; } } } } if (best > 0) { cost = 2 * c - 1; return bestpos; } } } } /** * Compare in detail contiguous subsequences of the two files * which are known, as a whole, to match each other. *

* The results are recorded in the vectors filevec[N].changed_flag, by * storing a 1 in the element for each line that is an insertion or deletion. *

* The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1. *

* Note that XLIM, YLIM are exclusive bounds. * All line numbers are origin-0 and discarded lines are not counted. */ private void compareseq(int xoff, int xlim, int yoff, int ylim) { /* Slide down the bottom initial diagonal. */ while (xoff < xlim && yoff < ylim && xvec[xoff] == yvec[yoff]) { ++xoff; ++yoff; } /* Slide up the top initial diagonal. */ while (xlim > xoff && ylim > yoff && xvec[xlim - 1] == yvec[ylim - 1]) { --xlim; --ylim; } /* Handle simple cases. */ if (xoff == xlim) while (yoff < ylim) filevec[1].changed_flag[1 + filevec[1].realindexes[yoff++]] = true; else if (yoff == ylim) while (xoff < xlim) filevec[0].changed_flag[1 + filevec[0].realindexes[xoff++]] = true; else { /* Find a point of correspondence in the middle of the files. */ int d = diag(xoff, xlim, yoff, ylim); int c = cost; int b = bdiag[bdiagoff + d]; if (c == 1) { /* This should be impossible, because it implies that one of the two subsequences is empty, and that case was handled above without calling `diag'. Let's verify that this is true. */ throw new IllegalArgumentException("Empty subsequence"); } else { /* Use that point to split this problem into two subproblems. */ compareseq(xoff, b, yoff, b - d); /* This used to use f instead of b, but that is incorrect! It is not necessarily the case that diagonal d has a snake from b to f. */ compareseq(b, xlim, b - d, ylim); } } } /** * Discard lines from one file that have no matches in the other file. */ private void discard_confusing_lines() { filevec[0].discard_confusing_lines(filevec[1]); filevec[1].discard_confusing_lines(filevec[0]); } private boolean inhibit = false; /** * Adjust inserts/deletes of blank lines to join changes * as much as possible. */ private void shift_boundaries() { if (inhibit) return; filevec[0].shift_boundaries(filevec[1]); filevec[1].shift_boundaries(filevec[0]); } public interface ScriptBuilder { /** * Scan the tables of which lines are inserted and deleted, * producing an edit script. * * @param changed0 true for lines in first file which do not match 2nd * @param len0 number of lines in first file * @param changed1 true for lines in 2nd file which do not match 1st * @param len1 number of lines in 2nd file * @return a linked list of changes - or null */ public change build_script(boolean[] changed0, int len0, boolean[] changed1, int len1); } /** * Scan the tables of which lines are inserted and deleted, * producing an edit script in reverse order. */ static class ReverseScript implements ScriptBuilder { public change build_script(final boolean[] changed0, int len0, final boolean[] changed1, int len1) { change script = null; int i0 = 0, i1 = 0; while (i0 < len0 || i1 < len1) { if (changed0[1 + i0] || changed1[1 + i1]) { int line0 = i0, line1 = i1; /* Find # lines changed here in each file. */ while (changed0[1 + i0]) ++i0; while (changed1[1 + i1]) ++i1; /* Record this change. */ script = new change(line0, line1, i0 - line0, i1 - line1, script); } /* We have reached lines in the two files that match each other. */ i0++; i1++; } return script; } } static class ForwardScript implements ScriptBuilder { /** * Scan the tables of which lines are inserted and deleted, * producing an edit script in forward order. */ public change build_script(final boolean[] changed0, int len0, final boolean[] changed1, int len1) { change script = null; int i0 = len0, i1 = len1; while (i0 >= 0 || i1 >= 0) { if (changed0[i0] || changed1[i1]) { int line0 = i0, line1 = i1; /* Find # lines changed here in each file. */ while (changed0[i0]) --i0; while (changed1[i1]) --i1; /* Record this change. */ script = new change(i0, i1, line0 - i0, line1 - i1, script); } /* We have reached lines in the two files that match each other. */ i0--; i1--; } return script; } } /** * Standard ScriptBuilders. */ public final static ScriptBuilder forwardScript = new ForwardScript(), reverseScript = new ReverseScript(); /* Report the differences of two files. DEPTH is the current directory depth. */ public final change diff_2(final boolean reverse) { return diff(reverse ? reverseScript : forwardScript); } /** * Get the results of comparison as an edit script. The script * is described by a list of changes. The standard ScriptBuilder * implementations provide for forward and reverse edit scripts. * Alternate implementations could, for instance, list common elements * instead of differences. * * @param bld an object to build the script from change flags * @return the head of a list of changes */ public change diff(final ScriptBuilder bld) { /* Some lines are obviously insertions or deletions because they don't match anything. Detect them now, and avoid even thinking about them in the main comparison algorithm. */ discard_confusing_lines(); /* Now do the main comparison algorithm, considering just the undiscarded lines. */ xvec = filevec[0].undiscarded; yvec = filevec[1].undiscarded; int diags = filevec[0].nondiscarded_lines + filevec[1].nondiscarded_lines + 3; fdiag = new int[diags]; fdiagoff = filevec[1].nondiscarded_lines + 1; bdiag = new int[diags]; bdiagoff = filevec[1].nondiscarded_lines + 1; compareseq(0, filevec[0].nondiscarded_lines, 0, filevec[1].nondiscarded_lines); fdiag = null; bdiag = null; /* Modify the results slightly to make them prettier in cases where that can validly be done. */ shift_boundaries(); /* Get the results of comparison in the form of a chain of `struct change's -- an edit script. */ return bld.build_script(filevec[0].changed_flag, filevec[0].buffered_lines, filevec[1].changed_flag, filevec[1].buffered_lines); } /** * The result of comparison is an "edit script": a chain of change objects. * Each change represents one place where some lines are deleted * and some are inserted. *

* LINE0 and LINE1 are the first affected lines in the two files (origin 0). * DELETED is the number of lines deleted here from file 0. * INSERTED is the number of lines inserted here in file 1. *

* If DELETED is 0 then LINE0 is the number of the line before * which the insertion was done; vice versa for INSERTED and LINE1. */ public static class change { /** * Previous or next edit command. */ public change link; /** * # lines of file 1 changed here. */ public final int inserted; /** * # lines of file 0 changed here. */ public final int deleted; /** * Line number of 1st deleted line. */ public final int line0; /** * Line number of 1st inserted line. */ public final int line1; /** * Cons an additional entry onto the front of an edit script OLD. * LINE0 and LINE1 are the first affected lines in the two files (origin 0). * DELETED is the number of lines deleted here from file 0. * INSERTED is the number of lines inserted here in file 1. *

* If DELETED is 0 then LINE0 is the number of the line before * which the insertion was done; vice versa for INSERTED and LINE1. */ public change(int line0, int line1, int deleted, int inserted, change old) { this.line0 = line0; this.line1 = line1; this.inserted = inserted; this.deleted = deleted; this.link = old; //System.err.println(line0+","+line1+","+inserted+","+deleted); } } /** * Data on one input file being compared. */ class file_data { /** * Allocate changed array for the results of comparison. */ void clear() { /* Allocate a flag for each line of each file, saying whether that line is an insertion or deletion. Allocate an extra element, always zero, at each end of each vector. */ changed_flag = new boolean[buffered_lines + 2]; } /** * Return equiv_count[I] as the number of lines in this file * that fall in equivalence class I. * * @return the array of equivalence class counts. */ int[] equivCount() { int[] equiv_count = new int[equiv_max]; for (int i = 0; i < buffered_lines; ++i) ++equiv_count[equivs[i]]; return equiv_count; } /** * Discard lines that have no matches in another file. *

* A line which is discarded will not be considered by the actual * comparison algorithm; it will be as if that line were not in the file. * The file's `realindexes' table maps virtual line numbers * (which don't count the discarded lines) into real line numbers; * this is how the actual comparison algorithm produces results * that are comprehensible when the discarded lines are counted. *

* When we discard a line, we also mark it as a deletion or insertion * so that it will be printed in the output. * * @param f the other file */ void discard_confusing_lines(file_data f) { clear(); /* Set up table of which lines are going to be discarded. */ final byte[] discarded = discardable(f.equivCount()); /* Don't really discard the provisional lines except when they occur in a run of discardables, with nonprovisionals at the beginning and end. */ filterDiscards(discarded); /* Actually discard the lines. */ discard(discarded); } /** * Mark to be discarded each line that matches no line of another file. * If a line matches many lines, mark it as provisionally discardable. * * @param counts The count of each equivalence number for the other file. * @return 0=nondiscardable, 1=discardable or 2=provisionally discardable * for each line */ private byte[] discardable(final int[] counts) { final int end = buffered_lines; final byte[] discards = new byte[end]; final int[] equivs = this.equivs; int many = 5; int tem = end / 64; /* Multiply MANY by approximate square root of number of lines. That is the threshold for provisionally discardable lines. */ while ((tem = tem >> 2) > 0) many *= 2; for (int i = 0; i < end; i++) { int nmatch; if (equivs[i] == 0) continue; nmatch = counts[equivs[i]]; if (nmatch == 0) discards[i] = 1; else if (nmatch > many) discards[i] = 2; } return discards; } /** * Don't really discard the provisional lines except when they occur * in a run of discardables, with nonprovisionals at the beginning * and end. */ private void filterDiscards(final byte[] discards) { final int end = buffered_lines; for (int i = 0; i < end; i++) { /* Cancel provisional discards not in middle of run of discards. */ if (discards[i] == 2) discards[i] = 0; else if (discards[i] != 0) { /* We have found a nonprovisional discard. */ int j; int length; int provisional = 0; /* Find end of this run of discardable lines. Count how many are provisionally discardable. */ for (j = i; j < end; j++) { if (discards[j] == 0) break; if (discards[j] == 2) ++provisional; } /* Cancel provisional discards at end, and shrink the run. */ while (j > i && discards[j - 1] == 2) { discards[--j] = 0; --provisional; } /* Now we have the length of a run of discardable lines whose first and last are not provisional. */ length = j - i; /* If 1/4 of the lines in the run are provisional, cancel discarding of all provisional lines in the run. */ if (provisional * 4 > length) { while (j > i) if (discards[--j] == 2) discards[j] = 0; } else { int consec; int minimum = 1; int tem = length / 4; /* MINIMUM is approximate square root of LENGTH/4. A subrun of two or more provisionals can stand when LENGTH is at least 16. A subrun of 4 or more can stand when LENGTH >= 64. */ while ((tem = tem >> 2) > 0) minimum *= 2; minimum++; /* Cancel any subrun of MINIMUM or more provisionals within the larger run. */ for (j = 0, consec = 0; j < length; j++) if (discards[i + j] != 2) consec = 0; else if (minimum == ++consec) /* Back up to start of subrun, to cancel it all. */ j -= consec; else if (minimum < consec) discards[i + j] = 0; /* Scan from beginning of run until we find 3 or more nonprovisionals in a row or until the first nonprovisional at least 8 lines in. Until that point, cancel any provisionals. */ for (j = 0, consec = 0; j < length; j++) { if (j >= 8 && discards[i + j] == 1) break; if (discards[i + j] == 2) { consec = 0; discards[i + j] = 0; } else if (discards[i + j] == 0) consec = 0; else consec++; if (consec == 3) break; } /* I advances to the last line of the run. */ i += length - 1; /* Same thing, from end. */ for (j = 0, consec = 0; j < length; j++) { if (j >= 8 && discards[i - j] == 1) break; if (discards[i - j] == 2) { consec = 0; discards[i - j] = 0; } else if (discards[i - j] == 0) consec = 0; else consec++; if (consec == 3) break; } } } } } /** * Actually discard the lines. * * @param discards flags lines to be discarded */ private void discard(final byte[] discards) { final int end = buffered_lines; int j = 0; for (int i = 0; i < end; ++i) if (no_discards || discards[i] == 0) { undiscarded[j] = equivs[i]; realindexes[j++] = i; } else changed_flag[1 + i] = true; nondiscarded_lines = j; } file_data(Object[] data, Hashtable h) { buffered_lines = data.length; equivs = new int[buffered_lines]; undiscarded = new int[buffered_lines]; realindexes = new int[buffered_lines]; for (int i = 0; i < data.length; ++i) { Integer ir = (Integer) h.get(data[i]); if (ir == null) h.put(data[i], new Integer(equivs[i] = equiv_max++)); else equivs[i] = ir.intValue(); } } /** * Adjust inserts/deletes of blank lines to join changes * as much as possible. *

* We do something when a run of changed lines include a blank * line at one end and have an excluded blank line at the other. * We are free to choose which blank line is included. * `compareseq' always chooses the one at the beginning, * but usually it is cleaner to consider the following blank line * to be the "change". The only exception is if the preceding blank line * would join this change to other changes. * * @param f the file being compared against */ void shift_boundaries(file_data f) { final boolean[] changed = changed_flag; final boolean[] other_changed = f.changed_flag; int i = 0; int j = 0; int i_end = buffered_lines; int preceding = -1; int other_preceding = -1; for (; ;) { int start, end, other_start; /* Scan forwards to find beginning of another run of changes. Also keep track of the corresponding point in the other file. */ while (i < i_end && !changed[1 + i]) { while (other_changed[1 + j++]) /* Non-corresponding lines in the other file will count as the preceding batch of changes. */ other_preceding = j; i++; } if (i == i_end) break; start = i; other_start = j; for (; ;) { /* Now find the end of this run of changes. */ while (i < i_end && changed[1 + i]) i++; end = i; /* If the first changed line matches the following unchanged one, and this run does not follow right after a previous run, and there are no lines deleted from the other file here, then classify the first changed line as unchanged and the following line as changed in its place. */ /* You might ask, how could this run follow right after another? Only because the previous run was shifted here. */ if (end != i_end && equivs[start] == equivs[end] && !other_changed[1 + j] && end != i_end && !((preceding >= 0 && start == preceding) || (other_preceding >= 0 && other_start == other_preceding))) { changed[1 + end] = true; changed[1 + start++] = false; ++i; /* Since one line-that-matches is now before this run instead of after, we must advance in the other file to keep in synch. */ ++j; } else break; } preceding = i; other_preceding = j; } } /** * Number of elements (lines) in this file. */ final int buffered_lines; /** * Vector, indexed by line number, containing an equivalence code for * each line. It is this vector that is actually compared with that * of another file to generate differences. */ private final int[] equivs; /** * Vector, like the previous one except that * the elements for discarded lines have been squeezed out. */ final int[] undiscarded; /** * Vector mapping virtual line numbers (not counting discarded lines) * to real ones (counting those lines). Both are origin-0. */ final int[] realindexes; /** * Total number of nondiscarded lines. */ int nondiscarded_lines; /** * Array, indexed by real origin-1 line number, * containing true for a line that is an insertion or a deletion. * The results of comparison are stored here. */ boolean[] changed_flag; } } static class BlockDiff { public CopyBlock asCopyBlock() { return null; } public NewBlock asNewBlock () { return null; } } static class CopyBlock extends BlockDiff { int firstLine, lines; CopyBlock(int firstLine, int lines) { this.firstLine = firstLine; this.lines = lines; } public CopyBlock asCopyBlock() { return this; } public int getFirstLine() { return firstLine; } public int getLines() { return lines; } } static class NewBlock extends BlockDiff { int originalStart; List contents; NewBlock(int originalStart, List contents) { this.originalStart = originalStart; this.contents = contents; } public NewBlock asNewBlock () { return this; } public int getOriginalStart() { return originalStart; } public List getContents() { return contents; } } static class ExplodedLine { int type; String left, right; int leftIndex, rightIndex; ExplodedLine(int type, String left, String right, int leftIndex, int rightIndex) { this.type = type; this.left = left; this.right = right; this.leftIndex = leftIndex; this.rightIndex = rightIndex; } public int getType() { return type; } public String getLeft() { return left; } public String getRight() { return right; } public int getLeftIndex() { return leftIndex; } public int getRightIndex() { return rightIndex; } } static class BlockDiffer { public static final int IDENTICAL = 0; public static final int DIFFERENT = 1; public static final int LEFT_ONLY = 2; public static final int RIGHT_ONLY = 3; private static void printChange(EGDiff.change change) { if (change != null) { System.out.println("line0="+change.line0+", line1="+change.line1 +", inserted="+change.inserted+", deleted="+change.deleted); printChange(change.link); } } /** Generates the text content of a Unified-format context diff between 2 files * (NB the 'files-changed' header must be added separately). */ public static List generateUniDiff(List fileA, List fileB, int contextSize) { EGDiff diff = new EGDiff(fileA.toArray(), fileB.toArray()); EGDiff.change change = diff.diff_2(false); if (change != null) { int inserted, deleted; List hunkLines = new ArrayList(); int cumulExtraLinesBwrtA = 0; // Each hunk is generated with a header do { int line0 = change.line0, line1 = change.line1; int changeStart = ((line1 < line0) ? line1 : line0); int contextStart = ((changeStart > contextSize) ? changeStart - contextSize : 0); int headerPosn = hunkLines.size(); // Provide the first lines of context for (int i = contextStart; i < changeStart; i++) //System.out.println(" " + fileA.get(i)); hunkLines.add(" " + fileA.get(i)); boolean hunkFinish = false; // Step through each change giving the change lines and following context do { inserted = change.inserted; deleted = change.deleted; line0 = change.line0; line1 = change.line1; if (line1 < line0) // An insert comes earlier while (inserted-- > 0) hunkLines.add("+" + fileB.get(line1++)); while (deleted-- > 0) hunkLines.add("-" + fileA.get(line0++)); while (inserted-- > 0) hunkLines.add("+" + fileB.get(line1++)); // Lines following are trailing context, identical in fileA and fileB // The next change may overlap the context, so check and if so, form one hunk EGDiff.change nextChange = change.link; int nextChangeStart = fileA.size(); if (nextChange != null) nextChangeStart = ((nextChange.line1 < nextChange.line0) ? nextChange.line1 : nextChange.line0); if (nextChangeStart - line0 > contextSize * 2) { // A separate hunk nextChangeStart = line0 + contextSize; hunkFinish = true; } if (nextChangeStart > fileA.size()) nextChangeStart = fileA.size(); // Limit to file size while (line0 < nextChangeStart) { hunkLines.add(" " + fileA.get(line0++)); line1++; // Keep in sync with trailing context } change = change.link; } while (!hunkFinish && change != null); int hunkStartB = contextStart + cumulExtraLinesBwrtA; int hunkTotA = line0 - contextStart; int hunkTotB = line1 - hunkStartB; hunkLines.add(headerPosn, "@@ -" + (contextStart + 1) + ',' + hunkTotA + " +" + (hunkStartB + 1) + ',' + hunkTotB + " @@"); cumulExtraLinesBwrtA += hunkTotB - hunkTotA; } while (change != null); return hunkLines; } return null; } /* For testing: private static void printUniDiff(List fileA, List fileB, int contextSize) { List uniDiff = generateUniDiff(fileA, fileB, contextSize); if (uniDiff != null) for (int j = 0; j < uniDiff.size(); j++) System.out.println(uniDiff.get(j)); } */ public static List diffLines(List lines, List reference) { List diffs = new ArrayList(); EGDiff diff = new EGDiff(reference.toArray(), lines.toArray()); EGDiff.change change = diff.diff_2(false); //printChange(change); //printUniDiff(reference, lines, 3); int l0 = 0, l1 = 0; while (change != null) { if (change.line0 > l0 && change.line1 > l1) diffs.add(new CopyBlock(l0, change.line0-l0)); if (change.inserted != 0) diffs.add(new NewBlock(change.line1, lines.subList(change.line1, change.line1+change.inserted))); l0 = change.line0 + change.deleted; l1 = change.line1 + change.inserted; change = change.link; } if (l0 < reference.size()) diffs.add(new CopyBlock(l0, reference.size()-l0)); return diffs; } /** fills files with empty lines to align matching blocks * * @param file1 first file * @param file2 second file * @return an array with two lists */ public static List explode(List file1, List file2) { List lines = new ArrayList(); List diffs = BlockDiffer.diffLines(file2, file1); int lastLineCopied = 0, rightOnlyStart = -1, rightPosition = 0; for (int i = 0; i < diffs.size(); i++) { BlockDiff diff = diffs.get(i); if (diff instanceof CopyBlock) { CopyBlock copyBlock = (CopyBlock) diff; if (lastLineCopied < copyBlock.getFirstLine()) { if (rightOnlyStart >= 0) { int overlap = Math.min(lines.size()-rightOnlyStart, copyBlock.getFirstLine()-lastLineCopied); //lines.subList(rightOnlyStart, rightOnlyStart+overlap).clear(); convertRightOnlyToDifferent(lines, rightOnlyStart, overlap, file1, lastLineCopied); lastLineCopied += overlap; } addBlock(lines, LEFT_ONLY, file1, lastLineCopied, copyBlock.getFirstLine(), lastLineCopied, -1); } addBlock(lines, IDENTICAL, file1, copyBlock.getFirstLine(), copyBlock.getFirstLine()+copyBlock.getLines(), copyBlock.getFirstLine(), rightPosition); rightPosition += copyBlock.getLines(); lastLineCopied = copyBlock.getFirstLine()+copyBlock.getLines(); rightOnlyStart = -1; } else if (diff instanceof NewBlock) { NewBlock newBlock = (NewBlock) diff; /*if (nextDiff instanceof BlockDiffer.CopyBlock) { BlockDiffer.CopyBlock copyBlock = (BlockDiffer.CopyBlock) nextDiff; copyBlock.getFirstLine()-lastLineCopied*/ rightOnlyStart = lines.size(); addBlock(lines, RIGHT_ONLY, newBlock.getContents(), 0, newBlock.getContents().size(), -1, rightPosition); rightPosition += newBlock.getContents().size(); } } if (rightOnlyStart >= 0) { int overlap = Math.min(lines.size()-rightOnlyStart, file1.size()-lastLineCopied); //lines.subList(rightOnlyStart, rightOnlyStart+overlap).clear(); convertRightOnlyToDifferent(lines, rightOnlyStart, overlap, file1, lastLineCopied); lastLineCopied += overlap; } addBlock(lines, LEFT_ONLY, file1, lastLineCopied, file1.size(), lastLineCopied, -1); return lines; } private static void convertRightOnlyToDifferent(List lines, int start, int numLines, List leftLines, int leftStart) { for (int i = 0; i < numLines; i++) { ExplodedLine line = lines.get(start+i); lines.set(start+i, new ExplodedLine(DIFFERENT, leftLines.get(i+leftStart), line.getRight(), i+leftStart, line.getRightIndex())); } } private static void addBlock(List lines, int type, List srcLines, int start, int end, int leftStart, int rightStart) { for (int i = start; i < end; i++) lines.add(new ExplodedLine(type, type == RIGHT_ONLY ? "" : srcLines.get(i), type == LEFT_ONLY ? "" : srcLines.get(i), type == RIGHT_ONLY ? -1 : i - start + leftStart, type == LEFT_ONLY ? -1 : i - start + rightStart)); } public static List condense(List lines) { List result = new ArrayList(); for (Iterator i = lines.iterator(); i.hasNext();) { ExplodedLine line = i.next(); if (line.getType() == IDENTICAL) { if (result.isEmpty() || result.get(result.size()-1).getType() != IDENTICAL) result.add(new ExplodedLine(IDENTICAL, "[...]", "[...]", -1, -1)); } else result.add(line); } return result; } } static abstract class DialogIO { String line; boolean eos, loud, noClose; abstract String readLineImpl(); abstract boolean isStillConnected(); abstract void sendLine(String line); abstract boolean isLocalConnection(); abstract Socket getSocket(); abstract void close(); int getPort() { Socket s = getSocket(); return s == null ? 0 : s.getPort(); } boolean helloRead; int shortenOutputTo = 500; String readLineNoBlock() { String l = line; line = null; return l; } boolean waitForLine() { try { if (line != null) return true; //print("Readline"); line = readLineImpl(); //print("Readline done: " + line); if (line == null) eos = true; return line != null; } catch (Exception __e) { throw rethrow(__e); } } String readLine() { waitForLine(); helloRead = true; return readLineNoBlock(); } String ask(String s, Object... args) { if (loud) return askLoudly(s, args); if (!helloRead) readLine(); if (args.length != 0) s = format3(s, args); sendLine(s); return readLine(); } String askLoudly(String s, Object... args) { if (!helloRead) readLine(); if (args.length != 0) s = format3(s, args); print("> " + shorten(s, shortenOutputTo)); sendLine(s); String answer = readLine(); print("< " + shorten(answer, shortenOutputTo)); return answer; } void pushback(String l) { if (line != null) throw fail(); line = l; helloRead = false; } } static abstract class DialogHandler { abstract void run(DialogIO io); } static class IndexedList2 extends AbstractList { ArrayList l = new ArrayList(); MultiSet index = new MultiSet(false); // HashMap static boolean debug; static int instances; IndexedList2() { ++instances; } IndexedList2(List x) { this(); l.ensureCapacity(l(x)); addAll(x); } // required immutable list methods @Override public A get(int i) { return l.get(i); } @Override public int size() { return l.size(); } // required mutable list methods @Override public A set(int i, A a) { A prev = l.get(i); if (prev != a) { index.remove(prev); index.add(a); } l.set(i, a); return prev; } @Override public void add(int i, A a) { index.add(a); l.add(i, a); } @Override public A remove(int i) { A a = l.get(i); index.remove(a); l.remove(i); return a; } // speed up methods @Override protected void removeRange(int fromIndex, int toIndex) { for (int i = fromIndex; i < toIndex; i++) unindex(i); l.subList(fromIndex, toIndex).clear(); } @Override public int indexOf(Object a) { if (!contains(a)) return -1; return l.indexOf(a); } @Override public boolean contains(Object a) { boolean b = index.contains((A) a); if (debug) print("IndexedList2.contains " + a + " => " + b); return b; } @Override public void clear() { index.clear(); l.clear(); } @Override public boolean addAll(int i, Collection c) { index.addAll((Collection) c); return l.addAll(i, c); } // indexing methods void unindex(int i) { index.remove(l.get(i)); } void index(int i) { index.add(l.get(i)); } // static methods static IndexedList2 ensureIndexed(List l) { return l instanceof IndexedList2 ? (IndexedList2) l : new IndexedList2(l); } } // uses HashMap by default static class MultiSet { Map map = new HashMap(); public MultiSet(boolean useTreeMap) { if (useTreeMap) map = new TreeMap(); } public MultiSet() { } public MultiSet(Collection c) { addAll(c); } public void add(A key) { add(key, 1); } public void addAll(Collection c) { if (c != null) for (A a : c) add(a); } void addAll(MultiSet ms) { for (A a : ms.keySet()) add(a, ms.get(a)); } public void add(A key, int count) { if (map.containsKey(key)) map.put(key, map.get(key)+count); else map.put(key, count); } public int get(A key) { return key != null && map.containsKey(key) ? map.get(key) : 0; } public boolean contains(A key) { return map.containsKey(key); } public void remove(A key) { Integer i = map.get(key); if (i != null && i > 1) map.put(key, i - 1); else map.remove(key); } public List getTopTen() { return getTopTen(10); } public List getTopTen(int maxSize) { List list = getSortedListDescending(); return list.size() > maxSize ? list.subList(0, maxSize) : list; } List highestFirst() { return getSortedListDescending(); } List lowestFirst() { return reversedList(getSortedListDescending()); } public List getSortedListDescending() { List list = new ArrayList(map.keySet()); Collections.sort(list, new Comparator() { public int compare(A a, A b) { return map.get(b).compareTo(map.get(a)); } }); return list; } public int getNumberOfUniqueElements() { return map.size(); } int uniqueSize() { return map.size(); } public Set asSet() { return map.keySet(); } Set keySet() { // synonym for idiots return map.keySet(); } public A getMostPopularEntry() { int max = 0; A a = null; for (Map.Entry entry : map.entrySet()) { if (entry.getValue() > max) { max = entry.getValue(); a = entry.getKey(); } } return a; } public void removeAll(A key) { map.remove(key); } public int size() { int size = 0; for (int i : map.values()) size += i; return size; } public MultiSet mergeWith(MultiSet set) { MultiSet result = new MultiSet(); for (A a : set.asSet()) { result.add(a, set.get(a)); } return result; } public boolean isEmpty() { return map.isEmpty(); } public String toString() { return str(map); } public void clear() { map.clear(); } Map asMap() { return cloneMap(map); } } static ThreadLocal DynamicObject_loading = new ThreadLocal(); static class DynamicObject { String className; // just the name, without the "main$" LinkedHashMap fieldValues = new LinkedHashMap(); DynamicObject() {} // className = just the name, without the "main$" DynamicObject(String className) { this.className = className;} } static class CompilerBot { static boolean verbose; static File compileSnippet(String snippetID) { return compileSnippet(snippetID, ""); } static Pair compileSnippet2(String snippetID) { return compileSnippet2(snippetID, ""); } // returns jar path static File compileSnippet(String snippetID, String javaTarget) { return compileSnippet2(snippetID, javaTarget).a; } // returns jar path, Java source static Pair compileSnippet2(String snippetID, String javaTarget) { String transpiledSrc = getServerTranspiled2(snippetID); int i = transpiledSrc.indexOf('\n'); String libs = transpiledSrc.substring(0, Math.max(0, i)); if (verbose) print("Compiling snippet: " + snippetID + ". Libs: " + libs); transpiledSrc = transpiledSrc.substring(i+1); return pair(compile(transpiledSrc, libs, javaTarget, snippetID), transpiledSrc); } static File compile(String src) { return compile(src, ""); } static File compile(String src, String libs) { return compile(src, libs, null); } static File compile(String src, String dehlibs, String javaTarget) { return compile(src, dehlibs, javaTarget, null); } static File compile(String src, String dehlibs, String javaTarget, String progID) { if (verbose) print("Compiling " + l(src) + " chars"); // Note: This is different from the calculation in x30 // (might lead to programs being compiled twice) String md5 = md5(dehlibs + "\n" + src + "\n" + progID); File jar = getJarFile(md5); if (jar == null || jar.length() <= 22) { // have to compile boolean canRename = useDummyMainClasses() && isSnippetID(progID) && !tok_classHasModifier(findMainClass(javaTok(src)), "public"); if (verbose) print("useRenaming: " + useDummyMainClasses() + ", canRename: " + canRename + ", progID: " + progID); if (canRename) javaCompileToJar_rename(src, dehlibs, jar, progID); else javaCompileToJar_standard(src, dehlibs, jar); } else { if (verbose) print("Getting classes from cache (" + jar.getAbsolutePath() + ", " + jar.length() + " bytes)"); touchFile(jar); // so we can find the unused ones easier } return jar; } static File getJarFile(String md5) { assertTrue(isMD5(md5)); return new File(getCacheProgramDir("#1002203"), md5 + ".jar"); } } static boolean isTrue(Object o) { if (o instanceof Boolean) return ((Boolean) o).booleanValue(); if (o == null) return false; throw fail(getClassName(o)); } static int varCount; static Map snippetCache = new HashMap(); static boolean useIndexedList = true, opt_javaTok = true; static HashSet included = new HashSet(); static HashSet definitions = new HashSet(); static HashSet shouldNotIncludeFunction = new HashSet(), shouldNotIncludeClass = new HashSet(); public static void main(final String[] args) throws Exception { if (useIndexedList) findCodeTokens_debug = true; javaTok_opt = opt_javaTok; findCodeTokens_indexed = findCodeTokens_unindexed = 0; findCodeTokens_bails = findCodeTokens_nonbails = 0; javaTok_n = javaTok_elements = 0; String in = loadMainJava(); print("759 STARTING " + identityHashCode(main.class)); included.clear(); shouldNotIncludeFunction.clear(); shouldNotIncludeClass.clear(); varCount = 0; //L ts = findTranslators(toLines(join(tok))); //print("Translators in source at start: " + structure(ts)); // "duplicate" statement List lines = toLines(in); call(getJavaX(), "findTranslators", lines); in = fromLines(lines); Matches m = new Matches(); if (match("duplicate *", in, m)) { // actual copying - unused // tok = jtok(loadSnippet(m.get(0))); // reference by include() in = "m { p { callMain(include(" + quote(m.get(0)) + ")); } }"; } List tok = jtok(in); // add m { } if (!hasCodeTokens(tok, "m", "{") && !hasCodeTokens(tok, "main", "{") && !hasCodeTokens(tok, "class", "main")) { //tok = jtok(moveImportsUp("m {\n" + in + "\n}")); replaceTokens_reTok(tok, 1, 2, "m {\n" + tok.get(1)); replaceTokens_reTok(tok, l(tok)-2, l(tok)-1, tok.get(l(tok)-2) + "}"); tok_moveImportsUp(tok); } // standard translate //ts = findTranslators(toLines(join(tok))); //print("Translators in source: " + structure(ts)); tok = jtok(defaultTranslate(join(tok))); //print("end of default translate"); //print(join(tok)); //tok_autoCloseBrackets(tok); tok = processIncludes(tok); // before standard functions processConceptsDot(tok); tok = processIncludes(tok); earlyStuff(tok); int safety = 0; boolean same; do { String before = join(tok); // shortened method declarations BEFORE standardFunctions jreplace(tok, "svoid", "static void"); jreplace(tok, "void {", "$1 $2() {"); jreplace(tok, "String {", "$1 $2() {"); jreplace(tok, "Object {", "$1 $2() {"); jreplace(tok, "List {", "$1 $2() {"); tok_definitions(tok); tok_ifndef(tok); tok_ifdef(tok); tok = standardFunctions(tok); tok = stdstuff(tok); // standard functions and all the keywords String diff; long startTime = now(); //diff = unidiff(before, join(tok)); //print("unidiff: " + (now()-startTime) + " ms"); //same = eq(diff, ""); same = eq(before, join(tok)); // << could be sped up if (!same) { print("Not same " + safety + "."); //print(indent(2, diff)); } if (safety++ >= 10) { //print(unidiff(before, join(tok))); print("----"); print(join(tok)); print("----"); throw fail("safety 10 error!"); } } while (!same); // POST-PROCESSING after stdstuff loop for (String clazz : ll("Pair", "Either")) { jreplace(tok, clazz + "< >", clazz + "<$3, $3>"); jreplace(tok, clazz + "< <> >", clazz + "<$3<$5>, $3<$5>>"); jreplace(tok, clazz + "< <,> >", clazz + "<$3<$5,$7>, $3<$5,$7>>"); } quicknew2(tok); //tok = jtok(quicknew(join(tok))); tok = extendClasses(tok); libs(tok); sourceCodeLine(tok); throwFail(tok); innerClassesVar(tok); tok_ifclass(tok); // Stuff that depends on the list of inner classes (haveClasses) HashSet haveClasses = haveClasses(tok); expandClassReferences(tok, haveClasses); slashCasts(tok, haveClasses); newWithoutNew(tok, haveClasses); tok = autoImports(tok); // faster to do it at the end /*if (useIndexedList) print("Indexed/unindexed lookups: " + findCodeTokens_indexed + "/" + findCodeTokens_unindexed + ", lists made: " + IndexedList2.instances); print("findCodeToken bails: " + findCodeTokens_bails + "/" + findCodeTokens_nonbails); print("javaToks: " + javaTok_n + "/" + javaTok_elements);*/ if (tok.contains("package")) splitJavaFiles(tok); else saveMainJava(tok); } static List noRawFunctionName = ll("break", "continue", "return", "else"); static List localStuff1(List tok) { int safety = 0; boolean same; do { String before = join(tok); tok = multilineStrings(tok); tok_singleQuoteIdentifiersToStringConstants(tok); inStringEvals(tok); listComprehensions(tok); doubleFor_simple(tok); forPing(tok); directSnippetRefs(tok); quicknu(tok); //tok_juxtaposeCalls(tok); jreplace(tok, "synced ", "synchronized $2"); replaceKeywordBlock(tok, "answer", "static S answer(S s) {\nfinal new Matches m;\n", "\nret null;\n}"); replaceKeywordBlock(tok, "static-pcall", "static { pcall {", "}}"); replaceKeywordBlock(tok, "loading", "{ JWindow _loading_window = showLoadingAnimation(); try { /* loading try */ ", "/* loading try */ } finally { disposeWindow(_loading_window); }\n /* loading end */ } /* after loading */ \n"); replaceKeywordBlock(tok, "html", "static O html(S uri, fMap params) ctex " + "{\n", "}"); // "static sync" => static synchronized jreplace(tok, "static sync", "static synchronized"); // "sclass" => static class jreplace(tok, "sclass", "static class"); // "asclass" => abstract static class jreplace(tok, "asclass", "abstract static class"); // "sinterface" => static interface jreplace(tok, "sinterface", "static interface"); // "ssynchronized" => static synchronized jreplace(tok, "ssynchronized", "static synchronized"); jreplace(tok, "ssvoid", "static synchronized void"); jreplace(tok, "sbool", "static bool"); jreplace(tok, "sint", "static int"); jreplace(tok, "snew", "static new"); jreplace(tok, "sv ", "static void $2"); jreplace(tok, "pvoid", "public void"); // "sS" => static S jreplace(tok, "sS", "static S"); // "sO" => static O jreplace(tok, "sO", "static O"); // "sL" => static L jreplace(tok, "sL", "static L"); // "toString {" => "public S toString() {" jreplace(tok, "toString {", "public S toString() {"); jreplace(tok, "Int", "Integer"); jreplace(tok, "Bool", "Boolean"); jreplace(tok, "BigInt", "BigInteger"); jreplace(tok, "Char", "Character"); // I REALLY wanted to avoid this, but eh... jreplace(tok, "SS", "Map"); // "on fail {" => "catch (Throwable _e) { ... rethrow(_e); }" replaceKeywordBlock(tok, "on fail", "catch (Throwable _e) {", "\nthrow rethrow(_e); }"); // "catch {" => "catch (Throwable _e) {" jreplace(tok, "catch {", "catch (Throwable _e) {"); // "catch X e {" => "catch (X e) {" jreplace(tok, "catch {", "catch ($2 $3) {"); // "catch e {" => "catch (Throwable e) {" (if e is lowercase) jreplace(tok, "catch {", "catch (Throwable $2) {", new Object() { boolean get(List tok, int i) { String word = tok.get(i+3); return startsWithLowerCaseOrUnderscore(word); } }); jreplace(tok, "+ +", "+", new Object() { boolean get(List tok, int i) { //printStructure("++: ", subList(tok, i-1, i+6)); if (empty(_get(tok, i+2))) return false; // no space between the pluses if (empty(_get(tok, i)) && eq("+", _get(tok, i-1))) return false; // an actual "++" at the left if (empty(_get(tok, i+4)) && eq("+", _get(tok, i+5))) return false; // an actual "++" at the right //print("doing it"); return true; } }); // some crazy fancy syntax jreplace(tok, "set ;", "$2 = true;"); // [stdEq] -> implementation of equals() and hashCode() jreplace(tok, "[stdEq]", "public bool equals(O o) { ret stdEq2(this, o); }\n" + "public int hashCode() { ret stdHash2(this); }"); // [concepts] "concept.field!" for dereferencing references jreplace(tok, "!", "$1.get()", new Object() { Object get(List tok, int i) { try { if (tok.get(i+2).contains("\n")) return false; // no line break between and ! if (nempty(tok.get(i+4))) return true; // space after = ok String t = _get(tok, i+5); if (t == null) return false; if (isIdentifier(t) || eqOneOf(t, "=", "(")) return false; return true; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (tok.get(i+2).contains(\"\\n\")) false; // no line break between and !\n ..."; }}); int i; // [concepts] "field := value" for defining fields e.g. in "uniq" while ((i = jfind(tok, " :=")) >= 0) { tok.set(i, quote(tok.get(i))); tok.set(i+2, ","); tok.set(i+4, ""); reTok(tok, i, i+5); } // "quoted" := value while ((i = jfind(tok, " :=")) >= 0) { tok.set(i, tok.get(i)); tok.set(i+2, ","); tok.set(i+4, ""); reTok(tok, i, i+5); } // more shortening jreplace(tok, "fS", "final S"); jreplace(tok, "fO", "final O"); jreplace(tok, "fL", "final L"); jreplace(tok, "fMap", "final Map"); jreplace(tok, "fRunnable", "final Runnable"); jreplace(tok, "f int", "final int"); // "continue unless" while ((i = jfind(tok, "continue unless")) >= 0) { int j = scanOverExpression(tok, getBracketMap(tok), i+4, ";"); replaceTokens(tok, i, i+4, "{ if (!("); tok.set(j, ")) continue; }"); reTok(tok, i, j+1); } // "continue if" while ((i = jfind(tok, "continue if")) >= 0) { int j = scanOverExpression(tok, getBracketMap(tok), i+4, ";"); replaceTokens(tok, i, i+4, "{ if ("); tok.set(j, ") continue; }"); reTok(tok, i, j+1); } // "return if" while ((i = jfind(tok, "return if")) >= 0) { int j = scanOverExpression(tok, getBracketMap(tok), i+4, ";"); replaceTokens(tok, i, i+4, "{ if ("); tok.set(j, ") return; }"); reTok(tok, i, j+1); } // while not null () while ((i = jfind(tok, "while not null (")) >= 0) { int closingBracket = findEndOfBracketPart(tok, i+6)-1; replaceTokens(tok, i+2, i+6, "("); tok.set(closingBracket, ") != null)"); reTok(tok, i, closingBracket+1); } // Replace $1 with m.unq(0) etc. - caveat: this blocks identifiers $1, $2, ... for (i = 1; i < l(tok); i += 2) { String s = tok.get(i); if (s.startsWith("$")) { s = substring(s, 1); if (isInteger(s)) { tok.set(i, "m.unq(" + (parseInt(s)-1) + ")"); reTok(tok, i); } } } // instanceof trickery jreplace(tok, "is a ", "instanceof $3"); jreplace(tok, "! instanceof .", "!($2 instanceof $4.$6)"); jreplace(tok, "! instanceof ", "!($2 instanceof $4)"); jreplace(tok, " !instanceof ", "!($1 instanceof $4)"); // map funcname(...) => map(f funcname, ...) jreplace(tok, "map (", "map(f $2,"); // func keyword for lambdas - now automatically quines toString() if enabled while ((i = jfind(tok, "func(")) >= 0) { int argsFrom = i+4, argsTo = findCodeTokens(tok, i, false, ")"); int idx = findCodeTokens(tok, argsTo, false, "{"); int j = findEndOfBracketPart(tok, idx); List contents = subList(tok, idx+1, j-1); String returnType = null; if (eq(tok.get(argsTo+2), "-") && eq(tok.get(argsTo+4), ">")) returnType = join(subList(tok, argsTo+6, idx-1)); String toString = autoQuine ? " public S toString() { ret " + quote(shorten(maxQuineLength, trim(join(contents)))) + "; }" : ""; // TODO: infer argument types /* if (nempty(returnType)) replaceTokens(tok, i, j, "new F1{ " + returnType + " get(" + join(subList(tok, argsFrom, argsTo-1)) + ") ctex { " + tok_addReturn(contents) + " }\n" +*/ replaceTokens(tok, i, j, "new O { O get(" + join(subList(tok, argsFrom, argsTo-1)) + ") ctex { " + tok_addReturn(contents) + " }\n" + toString + "}"); reTok(tok, i, j); } while ((i = jfind(tok, "voidfunc(")) >= 0) { int argsFrom = i+4, argsTo = findCodeTokens(tok, i, false, ")"); int idx = findCodeTokens(tok, argsTo, false, "{"); int j = findEndOfBracketPart(tok, idx); List contents = subList(tok, idx+1, j-1); replaceTokens(tok, i, j, "new O { void get(" + join(subList(tok, argsFrom, argsTo-1)) + ") ct" + "ex { " + tok_addSemicolon(contents) + " }\n" + (autoQuine ? " public S toString() { ret " + quote(shorten(maxQuineLength, trim(join(contents)))) + "; }" : "") + "}"); reTok(tok, i, j); } for (String keyword : ll("f", "func")) { while ((i = jfind(tok, keyword + " {")) >= 0) { int idx = findCodeTokens(tok, i, false, "{"); int j = findEndOfBracketPart(tok, idx+2); // XXX - not idx??? List contents = subList(tok, idx+1, j-1); replaceTokens(tok, i, j, "new O { O get() ct" + "ex { " + tok_addReturn(contents) + " }\n" + (autoQuine ? " public S toString() { ret " + quote(shorten(maxQuineLength, trim(join(contents)))) + "; }" : "") + "}"); reTok(tok, i, j); } while ((i = jfind(tok, keyword + " ->")) >= 0) { int idx = findCodeTokens(tok, i, false, "{"); int j = findEndOfBracketPart(tok, idx); String returnType = join(subList(tok, i+6, idx-1)); List contents = subList(tok, idx+1, j-1); replaceTokens(tok, i, j, "new F0<" + returnType + ">() { " + returnType + " get() ctex { " + tok_addReturn(contents) + " }\n" + (autoQuine ? " public S toString() { ret " + quote(shorten(maxQuineLength, trim(join(contents)))) + "; }" : "") + "}"); reTok(tok, i, j); } } // "ref -> bla" for dereferencing Concept.Ref or ThreadLocal or other //jreplace(tok, " ->", "$1.get()."); jreplace(tok, "->", ".get().", new Object() { Object get(List tok, int i) { try { return empty(tok.get(i+2)) && !(empty(_get(tok, i)) && eq(_get(tok, i-1), "-")) // i-->0 ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "empty(tok.get(i+2))\n && !(empty(_get(tok, i)) && eq(_get(tok, i-1), \"-\"))..."; }}); // shortened subconcept declaration (before star constructors!) shortenedSubconcepts(tok); // "case" as a variable name ( => _case) caseAsVariableName(tok); // "do" as a function name ( => dO) tok_doAsMethodName(tok); // "continue" as a function name ( => _continue) continueAsFunctionName(tok); // Do this BEFORE awt replacement! ("p-awt" contains "awt" token) if (hasCodeTokens(tok, "p", "-")) { jreplace(tok, "p-pretty {", "p-noconsole {"); replaceKeywordBlock(tok, "p-awt-noconsole", "p-awt {", "\nhideConsole(); }"); replaceKeywordBlock(tok, "p-substance-noconsole", "p-substance {", "\nhideConsole(); }"); replaceKeywordBlock(tok, "p-nimbus-noconsole", "p-nimbus {", "\nhideConsole(); }"); replaceKeywordBlock(tok, "p-subst-noconsole", "p-subst {", "\nhideConsole(); }"); replaceKeywordBlock(tok, "p-noconsole", "p-subst {", "\nhideConsole(); }"); replaceKeywordBlock(tok, "p-subst", "p-substance-thread {", "}"); replaceKeywordBlock(tok, "p-substance-thread", "p { substance();", "}"); replaceKeywordBlock(tok, "p-magellan-thread", "p { magellan();", "}"); replaceKeywordBlock(tok, "p-substance", "p-awt { substance();", "}"); replaceKeywordBlock(tok, "p-nimbus", "p-awt { nimbus();", "}"); jreplace(tok, "p-type {", "p-typewriter {"); jreplace(tok, "p-tt {", "p-typewriter {"); replaceKeywordBlock(tok, "p-awt", "p { swing {", "}}"); replaceKeywordBlock(tok, "p-typewriter", "p { typeWriterConsole();", "}"); replaceKeywordBlock(tok, "p-lowprio", "p { lowPriorityThread(r " + "{", "}); }"); tok_p_repeatWithSleep(tok); } replaceKeywordBlock(tok, "awt", "swingLater(r " + "{", // for #711 "});"); replaceKeywordBlock(tok, "swing", "{ swingAndWait(r " + "{", // for #711 "}); }"); unswing(tok); lockBlocks(tok); // crazy stuff jreplace (tok, "for over :", "for (int $2 = 0; $2 < l($4); $2++)"); jreplace (tok, "for , over : {", "for (int $2 = 0; $2 < l($7); $2++) { $4 $5 = $7.get($2);"); jreplace (tok, "for to :", "for (int $2 = 0; $2 < $4; $2++)"); jreplace (tok, "for to :", "for (int $2 = 0; $2 < $4; $2++)"); conceptDeclarations(tok); expandVarCopies(tok); jreplace(tok, "for ( )", "for ($3 $4 : list($3))"); jreplace(tok, "for (final )", "for (final $4 $5 : list($4))"); if (containsToken(tok, "cast")) { String s = join(tok); s = s.replaceAll("(\\w+<[\\w\\s,\\[\\]]+>|\\w+|\\w+\\[\\]|\\w+\\[\\]\\[\\])\\s+(\\w+)\\s*=\\s*cast(\\W[^;]*);", "$1 $2 = ($1) ($3);"); tok = jtok(s); } replaceKeywordBlock(tok, "r-thread", "runnableThread(r " + "{", "})"); rNamedThread(tok); // runnable and r - now also with automatic toString if enabled for (String keyword : ll("runnable", "r")) while ((i = jfind(tok, keyword + " {")) >= 0) { int idx = findCodeTokens(tok, i, false, "{"); int j = findEndOfBracketPart(tok, idx); List contents = subList(tok, idx+1, j-1); //print("r contents: " + structure(contents)); replaceTokens(tok, i, j+1, "new Runnable() { public void run() { try { " + tok_addSemicolon(contents) + "\n} catch (Exception __e) { throw rethrow(__e); } }" + (autoQuine ? " public S toString() { return " + quote(shorten(maxQuineLength, trim(join(contents)))) + "; }" : "") + "}"); reTok(tok, i, j+1); } replaceKeywordBlock(tok, "expectException", "{ bool __ok = false; try {", "} catch { __ok = true; } assertTrue(\"expected exception\", __ok); }"); while ((i = tok.indexOf("tex")) >= 0) { tok.set(i, "throws Exception"); tok = jtok(tok); } // shorter & smarter whiles jreplace(tok, "while true", "while (true)"); jreplace(tok, "while licensed", "while (licensed())"); jreplace(tok, "repeat {", "while (licensed()) {"); tok_repeatWithSleep(tok); // null; => return null; etc. Object cond = new Object() { Object get(List tok, int i) { try { return tok_tokenBeforeLonelyReturnValue(_get(tok, i-1)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "tok_tokenBeforeLonelyReturnValue(_get(tok, i-1))"; }}; jreplace(tok, "null;", "return null;", cond); jreplace(tok, "false;", "return false;", cond); jreplace(tok, "true;", "return true;", cond); jreplace(tok, "this;", "return this;", cond); tok = expandShortTypes(tok); // "myFunction;" instead of "myFunction();" - quite rough cond = new Object() { boolean get(List tok, int i) { String word = tok.get(i+3); //print("single word: " + word); return !noRawFunctionName.contains(word); } }; for (String pre : litlist("}", ";")) jreplace(tok, pre + " ;", "$1 $2();", cond); // shorter match syntax for answer methods jreplace(tok, "if || ", "if (matchOneOf(s, m, $2, $5))"); // "bla..." jreplace(tok, "if ", "if (matchStartX($2, s, m))", new Object() { boolean get(List tok, int i) { return unquote(tok.get(i+3)).endsWith("..."); }}); // "bla * bla | blubb * blubb" jreplace_dyn(tok, "if ", new Object() { Object get(List tok, int cIdx) { try { String s = unquote(tok.get(cIdx+2)); //print("multimatch: " + quote(s)); List l = new ArrayList(); for (String pat : splitAtJavaToken(s, "|")) { //print("multimatch part: " + quote(pat)); if (javaTok(pat).contains("*")) l.add("match(" + quote(trim(pat)) + ", s, m)"); else l.add("match(" + quote(trim(pat)) + ", s)"); } return "if (" + join(" || ", l) + ")"; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "S s = unquote(tok.get(cIdx+2));\n //print(\"multimatch: \" + quote(s));\n ..."; }}, new Object() { Object get(List tok, int i) { try { return javaTokC(unquote(tok.get(i+3))).contains("|") ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "javaTokC(unquote(tok.get(i+3))).contains(\"|\")"; }}); // "bla * bla" jreplace(tok, "if ", "if (match($2, s))", new Object() { boolean get(List tok, int i) { return !javaTokC(unquote(tok.get(i+3))).contains("*"); }}); // "bla" jreplace(tok, "if ", "if (match($2, s, m))"); jreplace(tok, "if match ", "if (match($3, s, m))"); // extra commas ("litlist(1, 2,)") jreplace(tok, ",)", ")"); // additional translations (if necessary) replaceKeywordBlock(tok, "pcall", "try {", "} catch (Throwable __e) { printStackTrace2(__e); }"); replaceKeywordBlock(tok, "pcall-short", "try {", "} catch (Throwable __e) { print(exceptionToStringShort(__e)); }"); replaceKeywordBlock(tok, "pcall-silent", "try {", "} catch (Throwable __e) { silentException(__e); }"); replaceKeywordBlock(tok, "pcall-messagebox", "try {", "} catch __e { messageBox(__e); }"); replaceKeywordBlock(tok, "pcall-infobox", "try {", "} catch __e { infoBox(__e); }"); tok = dialogHandler(tok); replaceKeywordBlock(tok, "exceptionToUser", "try {", "} catch (Throwable __e) { ret exceptionToUser(__e); }"); if (hasCodeTokens(tok, "twice", "{")) replaceKeywordBlock(tok, "twice", "for (int __twice = 0; __twice < 2; __twice++) {", "}"); while ((i = findCodeTokens(tok, "repeat", "*", "{")) >= 0) { String v = makeVar("repeat"); tok.set(i, "for (int " + v + " = 0; " + v + " < " + tok.get(i+2) + "; " + v + "++)"); tok.set(i+2, ""); tok = jtok(tok); } replaceKeywordBlockDyn(tok, "time", new Object() { String[] get() { String var = makeVar("startTime"); return new String[] { "{ long " + var + " = sysNow(); try { ", "} finally { " + var + " = sysNow()-" + var + "; saveTiming(" + var + "); } }"}; }}); // version without { } replaceKeywordBlockDyn(tok, "time2", new Object() { String[] get() { String var = makeVar("startTime"); return new String[] { "long " + var + " = sysNow(); ", " " + var + " = sysNow()-" + var + "; saveTiming(" + var + "); "}; }}); // time "bla" { replaceKeywordPlusQuotedBlock(tok, "time", new Object() { Object get(List tok, int i) { try { String var = makeVar("startTime"); return new String[] { "long " + var + " = sysNow(); ", " done2(" + tok.get(i+2) + ", " + var + "); "}; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "S var = makeVar(\"startTime\");\n ret new S[] {\n \"long \" + var + \" ..."; }}); if (hasCodeTokens(tok, "assertFail", "{")) { String var = makeVar("oops"); replaceKeywordBlock(tok, "assertFail", "boolean " + var + " = false; try {", "\n" + var + " = true; } catch (Exception e) { /* ok */ } assertFalse(" + var + ");"); } replaceKeywordBlock(tok, "yo", "try {", "} catch (Exception " + makeVar("e") + ") { ret false; }"); replaceKeywordBlock(tok, "awtIfNecessary", "swingNowOrLater(r " + "{", "});"); ctex(tok); replaceKeywordBlock(tok, "actionListener", "new java.awt.event.ActionListener() { " + "public void actionPerformed(java.awt.event.ActionEvent _evt) {", "}}"); namedThreads(tok); threads(tok); // try answer while ((i = findCodeTokens(tok, "try", "answer")) >= 0) { int j = findEndOfStatement(tok, i); String v = makeVar("a"); tok.set(i, "{ S " + v); tok.set(i+2, "="); tok.set(j-1, "; if (!empty(" + v + ")) ret " + v + "; }"); tok = jtok(tok); } // return optional (return if not null) while ((i = jfind(tok, "return optional =")) >= 0) { int j = findEndOfStatement(tok, i); String v = tok.get(i+4); clearTokens(tok, i+2, i+4); tok.set(i, "{"); tok.set(j-1, "; if (" + v + " != null) ret " + v + "; }"); tok = jtok(tok); } same = eq(before, join(tok)); // << could be sped up if (!same) { print("Not same " + safety + "."); //print(indent(2, diff)); } if (safety++ >= 10) { //print(unidiff(before, join(tok))); print("----"); print(join(tok)); print("----"); throw fail("safety 10 error!"); } } while (!same); return tok; } // end of localStuff1 static List stdstuff(List tok) { //if (++level >= 10) fail("woot? 10"); print("stdstuff!"); int i; if (jfind(tok, "!") >= 0) { List lines = toLines(join(tok)); List ts = findTranslators(lines); tok = jtok(fromLines(lines)); print("DROPPING TRANSLATORS: " + structure(ts)); } tok = quickmain(tok); tok = processIncludes(tok); processConceptsDot(tok); tok = processIncludes(tok); earlyStuff(tok); tok = localStuff1(tok); // * constructors if (hasCodeTokens(tok, "\\*", "(")) tok = expandStarConstructors(tok); // STANDARD CLASSES & INTERFACES String sc = cacheGet("#1003674"); List lclasses = new ArrayList(); for (String line : toLinesFullTrim(sc)) { line = javaDropComments(line).trim(); int idx = line.indexOf('/'); lclasses.addAll(ll(line.substring(0, idx), line.substring(idx+1))); } final Set haveClasses = addStandardClasses(tok, toStringArray(lclasses)); if (tok.contains("Triple") && !haveClasses.contains("Triple")) jreplace(tok, "Triple", "T3"); // "x << X" or "x >> X" => "x instanceof X" for (String op : ll("<<", ">>")) jreplace(tok, " " + op + " ", "$1 instanceof $4", new Object() { Object get(List tok, int i) { try { return haveClasses.contains(tok.get(i+7)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "haveClasses.contains(tok.get(i+7))"; }}); // concept-related stuff // auto-import concepts boolean _a = tok_hasClassRef2(tok, /*"extends",*/ "Concept") || tok_hasClassRef2(tok, "Concepts"), _b = !haveClasses.contains("Concept"); //print("auto-import: " + _a + ", " + _b); if (_a && _b) { print("Auto-including concepts."); if (shouldNotIncludeClass.contains("Concepts")) { print(join(tok)); throw fail("Unwanted concepts import"); } printStruct(haveClasses); tok = includeInMainLoaded(tok, "concepts."); reTok(tok, l(tok)-1, l(tok)); //processConceptsDot(tok); } // the infamous missing functions (usually caused by class Matches) // maybe not needed anymore? /*if (!hasCodeTokens(tok, "String", "unquote") && containsToken(tok, "unquote")) { print("Adding unquote"); tok = includeInMain(tok, "#1001735"); } if (!hasCodeTokens(tok, "String", "formatSnippetID") && containsToken(tok, "formatSnippetID")) { print("Adding formatSnippetID"); tok = includeInMain(tok, "#1000709"); }*/ functionReferences(tok); tok_moveImportsUp(tok); /*S moved = moveImportsUp2(join(tok)); if (moved != null) tok = jtok(moved);*/ return tok; } // end of stdStuff! static List multilineStrings(List tok) { for (int i = 1; i < tok.size(); i += 2) { String t = tok.get(i); if (isQuoted(t)) if (t.startsWith("[") || t.contains("\r") || t.contains("\n")) tok.set(i, quote(unquote(t))); } return tok; } static void inStringEvals(List tok) { boolean change = false; for (int i = 1; i < tok.size(); i += 2) { String t = tok.get(i); if (!isQuoted(t)) continue; if (t.contains("\\*") && !t.contains("\\\\")) { // << rough tok.set(i, inStringEval(t)); change = true; } } if (change) reTok(tok); } static String inStringEval(String t) { t = dropPrefix("\"", dropSuffix("\"", t)); List l = new ArrayList(); int idx; while ((idx = t.indexOf("\\*")) >= 0) { int j = indexOf(t, idx, "*/"); if (j < 0) break; if (idx > 0) l.add("\"" + substring(t, 0, idx) + "\""); l.add("(" + trim(substring(t, idx+2, j)) + ")"); t = substring(t, j+2); } if (nempty(t)) l.add("\"" + t + "\""); return "(" + join(" + ", l) + ")"; } static List quickmain(List tok) { boolean change = false; int i = findCodeTokens(tok, "main", "{"); if (i < 0) i = findCodeTokens(tok, "m", "{"); if (i >= 0 && !(i-2 > 0 && tok.get(i-2).equals("class"))) { tok.set(i, "class main"); change = true; } i = findCodeTokens(tok, "psvm", "{"); if (i < 0) i = findCodeTokens(tok, "p", "{"); if (i >= 0) { int idx = i+2; int j = findEndOfBracketPart(tok, idx); List contents = subList(tok, idx+1, j-1); tok.set(i, "public static void main(final String[] args) throws Exception"); replaceTokens(tok, idx+1, j-1, tok_addSemicolon(contents)); change = true; } return change ? jtok(tok) : tok; } static String makeVar(String name) { return "_" + name + "_" + varCount++; } static String makeVar() { return makeVar(""); } /*static L standardFunctions(L tok) { ret rtq(tok, "#1002474"); }*/ static List rtq(List tok, String id) { return runTranslatorQuick(tok, id); } static List expandShortTypes(List tok) { // replace with for (int i = 1; i+4 < tok.size(); i += 2) if (tok.get(i).equals("<") && litlist(">", ",").contains(tok.get(i+4))) { String type = tok.get(i+2); if (type.equals("int")) type = "Integer"; else if (type.equals("long")) type = "Long"; tok.set(i+2, type); } // O = Object, S = String, ret = return for (int i = 1; i < tok.size(); i += 2) { String t = tok.get(i); if (t.equals("O")) t = "Object"; if (t.equals("S")) t = "String"; else if (t.equals("L")) t = "List"; //else if (t.equals("F")) t = "Function"; else if (t.equals("ret")) t = "return"; else if (t.equals("bool") && i+2 < tok.size() && neq(tok.get(i+2), "(")) t = "boolean"; // bool -> boolean if it's not a function name tok.set(i, t); } jreplace(tok, "LL< >", "L>"); return tok; } static List autoImports(List tok) { String s = join(tok); List imports = findImports(s); StringBuilder buf = new StringBuilder(); for (String c : standardImports) if (!(imports.contains(c))) buf.append("import " + c + ";\n"); if (buf.length() == 0) return tok; return jtok(buf+s); } static String[] standardImports = { "java.util.*", "java.util.zip.*", "java.util.List", "java.util.regex.*", "java.util.concurrent.*", "java.util.concurrent.atomic.*", "java.util.concurrent.locks.*", "javax.swing.*", "javax.swing.event.*", "javax.swing.text.*", "javax.swing.table.*", "java.io.*", "java.net.*", "java.lang.reflect.*", "java.lang.ref.*", "java.lang.management.*", "java.security.*", "java.security.spec.*", "java.awt.*", "java.awt.event.*", "java.awt.image.*", "javax.imageio.*", "java.math.*" }; static List expandStarConstructors(List tok) { mainLoop: for (int i = 3; i+6 < tok.size(); i += 2) { String t = tok.get(i), l = tok.get(i-2); if (!t.equals("*")) continue; if (!tok.get(i+2).equals("(")) continue; if (!eqOneOf(l, "}", "public", "private", "protected", ";", "{", "endif") && neq(get(tok, i-4), "ifclass")) // is this correct...?? continue; // ok, it seems like a constructor declaration. // Now find class name by going backwards. int j = i, level = 1; while (j > 0 && level > 0) { t = tok.get(j); if (t.equals("}")) ++level; if (t.equals("{")) --level; j -= 2; } while (j > 0) { t = tok.get(j); if (t.equals("class")) { String className = tok.get(j+2); tok.set(i, className); // exchange constructor name! // now for the parameters. // Syntax: *(Learner *learner) { // We will surely add type inference here in time... :) j = i+2; while (!tok.get(j).equals("{")) j += 2; int block = j+1; for (int k = i+2; k < block-1; k += 2) if (tok.get(k).equals("*")) { tok.remove(k); tok.remove(k); block -= 2; String name = tok.get(k); tok.addAll(block, Arrays.asList(new String[] { "\n ", "this", "", ".", "", name, " ", "=", " ", name, "", ";" })); } continue mainLoop; } j -= 2; } } return tok; } static List processIncludes(List tok) { int safety = 0; while (hasCodeTokens(tok, "!", "include") && ++safety < 100) tok = processIncludesSingle(tok); //tok_autoCloseBrackets(tok); return tok; } static List processIncludesSingle(List tok) { int i; while ((i = jfind(tok, "!include #")) >= 0) { String id = tok.get(i+6); included.add(parseLong(id)); replaceTokens(tok, i, i+8, "\n" + loadSnippet(id) + "\n"); reTok_include(tok, i, i+8); } while ((i = jfind(tok, "!include once #")) >= 0) { String id = tok.get(i+8); boolean isNew = included.add(parseLong(id)); replaceTokens(tok, i, i+10, isNew ? "\n" + loadSnippet(id) + "\n" : ""); reTok_include(tok, i, i+10); } return tok; } static List reTok_include(List tok, int i, int j) { return reTok_modify(tok, i, j, "localStuff1"); } static List includeInMainLoaded_reTok(List tok, int i, int j) { return reTok_include(tok, i, j); } static void ctex(List tok) { replaceKeywordBlock(tok, "ctex", "{ try {", "} catch (Exception __e) { throw rethrow(__e); } }"); replaceKeywordBlock(tok, "null on exception", "{ try {", "} catch (Throwable __e) { return null; } }"); replaceKeywordBlock(tok, "false on exception", "{ try {", "} catch (Throwable __e) { return false; } }"); } static List dialogHandler(List tok) { return replaceKeywordBlock(tok, "dialogHandler", "new DialogHandler() {\n" + "public void run(final DialogIO io) {", "}}"); } static void quicknew2(List tok) { jreplace(tok, "new ;", "$2 $3 = new $2;"); jreplace(tok, "new <> ;", "$2<$4> $6 = new $2;"); jreplace(tok, "new <> , ;", "$2<$4> $6 = new $2, $8 = new $2;"); jreplace(tok, "new <,> ;", "$2<$4,$6> $8 = new $2;"); jreplace(tok, "new <<>> ;", "$2 $3 $4 $5 $6 $7 $8 $9 = new $2;"); jreplace(tok, "new < <,> > ;", "$2 $3 $4 $5 $6 $7 $8 $9 $10 $11 = new $2;"); // [abandoned, confusing, looks like a function definition] with arguments - new A a(...); => A a = new A(...); //jreplace(tok, "new (", "$2 $3 = new $2("); jreplace(tok, "for args " + "{", "for (int i = 0; i < args.length; i++) { final String arg = args[i];"); // Constructor calls without parentheses // So you can say something like: predictors.add(new P1); jreplace1(tok, "new ", "new $2()", new Object() { Object get(List tok, int i) { try { return eqOneOf(_get(tok, i+5), "{", ",", ")", ";", ":") ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "eqOneOf(_get(tok, i+5), \"{\", \",\", \")\", \";\", \":\")"; }}); jreplace(tok, "new List(", "new ArrayList("); jreplace(tok, "new Map(", "new HashMap("); jreplace(tok, "new Set(", "new HashSet("); jreplace(tok, "\\*[] ;", "$2[] $6 = new $2[$4];"); // X x = new(...) => X x = new X(...) // X x = new => X x = new jreplace(tok, " = new", "$1 $2 = new $1", new Object() { Object get(List tok, int i) { try { return eqOneOf(_get(tok, i+9), "(", ";", ",", ")") ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "eqOneOf(_get(tok, i+9), \"(\", \";\", \",\", \")\")"; }}); jreplace(tok, " = new \\*", "$1 $2 = new $1"); jreplace(tok, "\\* = new ", "$5 $2 = new $5"); jreplace(tok, "<> = new", "$1 $2 $3 $4 $5 = new $1", new Object() { Object get(List tok, int i) { try { return eqOneOf(_get(tok, i+9+3*2), "(", ";", ",", ")") ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "eqOneOf(_get(tok, i+9+3*2), \"(\", \";\", \",\", \")\")"; }}); jreplace(tok, "<,> = new", "$1 $2 $3 $4 $5 $6 $7 = new $1", new Object() { Object get(List tok, int i) { try { return eqOneOf(_get(tok, i+9+5*2), "(", ";", ",", ")") ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "eqOneOf(_get(tok, i+9+5*2), \"(\", \";\", \",\", \")\")"; }}); } static List extendClasses(List tok) { int i; while ((i = jfind(tok, "extend {")) >= 0) { String className = tok.get(i+2); int idx = findCodeTokens(tok, i, false, "{"); int j = findEndOfBracketPart(tok, idx+2); String content = join(subList(tok, idx+1, j-1)); List c = findInnerClassOfMain(tok, className); //print("Extending class " + className + " ==> " + join(c)); clearTokens(tok.subList(i, j+1)); if (c == null) { print("Warning: Can't extend class " + className + ", not found"); continue; } int startOfClass = indexOfSubList(tok, c); // magicIndexOfSubList is broken int endOfClass = startOfClass + l(c)-1; //print("Extending class " + className + " ==> " + join(subList(tok, startOfClass, endOfClass))); while (neq(tok.get(endOfClass), "}")) --endOfClass; //print("Extending class " + className + " ==> " + join(subList(tok, startOfClass, endOfClass))); tok.set(endOfClass, content + "\n" + tok.get(endOfClass)); reTok(tok); // changed in 2 places, let's retok it all } return tok; } static void listComprehensions(List tok) { int i; for (String op : ll(":", "in")) while ((i = jfind(tok, "[ " + op)) >= 0) { Map bracketMap = getBracketMap(tok); // XXX - optimize String type = tok.get(i+2), id = tok.get(i+4); int j = scanOverExpression(tok, bracketMap, i+8, "|"); String exp = join(tok.subList(i+8, j)); j += 2; int k = scanOverExpression(tok, bracketMap, j, "]"); String where = join(tok.subList(j, k)); ++k; String code = "filter(" + exp + ", func(" + type + " " + id + ") { " + where + " })"; replaceTokens(tok, i, k, code); reTok(tok, i, k); } } /* complicated version - TODO */ // for (S key, S value : map) /*static void doubleFor(L tok) { int i; while ((i = jfind(tok, "for ( , :")) >= 0) { int iComma = indexOf(tok, ",", i); S valType = tok.get(iComma+2); S valName = tok.get(iComma+4); int expStart = indexOf(tok, ":", i)+2; int expEnd = findEndOfBracketPart(expStart-2); assertEquals(")", tok.get(expEnd)); assertEquals("{", tok.get(expEnd+2)); replaceTokens(tok, iComma, expStart, ": keys("); tok.set(expEnd, "))"); tok.set(expEnd+2, "{ " + valType + " " + valName + " = " + S type = tok.get(i+2), id = tok.get(i+4); int j = scanOverExpression(tok, bracketMap, i+8, "|"); S exp = join(tok.subList(i+8, j)); j += 2; int k = scanOverExpression(tok, bracketMap, j, "]"); S where = join(tok.subList(j, k)); ++k; S code = "filter(" + exp + ", func(" + type + " " + id + ") { " + where + " })"; replaceTokens(tok, i, k, code); reTok(tok, i, k); } }*/ static void doubleFor_simple(List tok) { jreplace(tok, "for ( , : ) {", "for ($3 $4 : keys($9)) { $6 $7 = $9.get($4);"); jreplace(tok, "for ( , : ) {", "for ($3 $4 : keys($8)) { $3 $6 = $8.get($4);"); } static void forPing(List tok) { int i, safety = 0; while ((i = jfind(tok, "for ping (")) >= 0) { if (++safety >= 10) throw fail("safety"); int bracketEnd = findEndOfBracketPart(tok, i+4)-1; if (neq("{", tok.get(bracketEnd+2))) throw fail("Please use { with 'for ping' : " + joinSubList(tok, i+4, bracketEnd+3)); tokSet(tok, bracketEnd+2, "{ ping();"); tokSet(tok, i+2, ""); } } // lib 123 => !123 static void libs(List tok) { TreeSet libs = new TreeSet(); int i; while ((i = jfind(tok, "lib ")) >= 0) { String id = tok.get(i+2); print("lib " + id); if (!libs.contains(id)) { libs.add(id); tok.set(i, "!"); tok.set(i+1, ""); } else { print("...ignoring (duplicate)"); clearAllTokens(tok, i, i+3); reTok(tok, i, i+3); } } } // sourceCodeLine() => 1234 static void sourceCodeLine(List tok) { int i ; while ((i = jfind(tok, "sourceCodeLine()")) >= 0) { replaceTokens(tok, i, i+5, str(countChar(join(subList(tok, 0, i)), '\n')+1)); reTok(tok, i, i+5); } } // done before any other processing static void earlyStuff(List tok) { int i; tok_autosemi(tok); tok_autoCloseBrackets(tok); jreplace(tok, "°", "()"); // Note: this makes the word "quine" a special operator // (unusable as a function name) while ((i = jfind(tok, "quine(")) >= 0) { int idx = findCodeTokens(tok, i, false, "("); int j = findEndOfBracketPart(tok, idx+2); tok.set(i, "new Quine"); tok.set(idx, "(" + quote(join(subList(tok, idx+1, j-1))) + ", "); reTok(tok, i, idx+1); } } static void throwFail(List tok) { boolean anyChange = false; for (int i = 1; i+2 < l(tok); i += 2) if (eq(get(tok, i+2), "fail") && !eqOneOf(get(tok, i), "throw", "RuntimeException", "return")) { tok.set(i+2, "throw fail"); anyChange = true; } if (anyChange) reTok(tok); } static void namedThreads(List tok) { for (int i = 0; i < 100; i++) { int idx = findCodeTokens(tok, "thread", "", "{"); if (idx < 0) idx = findCodeTokens(tok, "thread", "", "{"); if (idx < 0) break; int j = findEndOfBracketPart(tok, idx+4); String tName = tok.get(idx+2); String var = "_t_" + i; String pre = "{ Thread " + var + " = new Thread(" + tName + ") {\n" + "public void run() { pcall {\n"; String post = "} }\n};\n" + "startThread(" + var + "); }"; tok.set(idx, pre); tok.set(idx+2, ""); tok.set(idx+4, ""); tok.set(j-1, post); //print(join(subList(tok, idx, j))); reTok(tok, idx, j); } } static void rNamedThread(List tok) { for (int i = 0; i < 100; i++) { int idx = findCodeTokens(tok, "r", "-", "thread", "", "{"); if (idx < 0) idx = findCodeTokens(tok, "r", "-", "thread", "", "{"); if (idx < 0) break; int j = findEndOfBracketPart(tok, idx+8); String tName = tok.get(idx+6); String pre = "r { thread " + tName + " {"; String post = "}}"; replaceTokens(tok, idx, idx+9, pre); tok.set(j-1, post); reTok(tok, idx, j); } } static void threads(List tok) { for (boolean daemon : litlist(false, true)) for (int i = 0; i < 100; i++) { int idx = findCodeTokens(tok, daemon ? "daemon" : "thread", "{"); if (idx < 0) break; int j = findEndOfBracketPart(tok, idx+2); String var = "_t_" + i; String pre = "{ Thread " + var + " = new Thread() {\n" + "public void run() { pcall\n"; String post = "} }\n};\n" + (daemon ? var + ".setDaemon(true);\n" : "") + "startThread(" + var + "); }"; tok.set(idx, pre); tok.set(j-1, post); reTok(tok, idx, j); } } static Map sf; static Boolean _761ok; static List standardFunctions(List tok) { if (sf == null) { String _761 = cacheGet("#761"); if (_761ok == null) _761ok = isBracketHygienic(_761); assertTrue("Whoa! #761 truncated?", _761ok); List standardFunctions = concatLists( (List) loadVariableDefinition(_761, "standardFunctions"), (List) loadVariableDefinition(cacheGet("#1006654"), "standardFunctions")); 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(tok)); int j; while ((j = jfind(tok, "should not include function *.")) >= 0) { String fname = tok.get(j+8); shouldNotIncludeFunction.add(fname); clearAllTokens(tok.subList(j, j+12)); } // changes tok Set invocations = findFunctionInvocations(tok, sf); invocations.addAll(functionsToAlwaysInclude); //print("Functions invoked: " + structure(invocations)); List needed = diff(invocations, defd); if (needed.isEmpty()) break; print("Adding functions: " + join(" " , needed)); Collection bad = setIntersection(new HashSet(needed), shouldNotIncludeFunction); if (nempty(bad)) { String msg = "INCLUDING BAD FUNCTIONS: " + sfu(bad); print(msg); print(join(tok)); throw fail(msg); } List added = new ArrayList(); StringBuilder buf = new StringBuilder(); List preload = new ArrayList(); for (String x : needed) if (sf.containsKey(x)) preload.add(sf.get(x)); cachePreload(preload); for (String x : cloneList(needed)) { if (defd.contains(x)) continue; String id = sf.get(x); if (id == null) { print("Standard function " + x + " not found."); needed.remove(x); continue; } //print("Adding function: " + x + " (" + id + ")"); String function = cacheGet(id); if (("\n" + function).contains("\n!")) print("Warning: " + id + " contains translators."); buf.append(function).append("\n"); added.add(x); defd.addAll(findFunctionDefs(javaTok(function))); } tok = includeInMainLoaded(tok, str(buf)); // defd = new HashSet(findFunctions(tok)); //print("Functions added: " + structure(added)); for (String x : needed) if (!defd.contains(x)) { print(join(tok)); throw fail("Function not defined properly: " + x); } //print("Iteration " + (i+2)); tok_definitions(tok); tok_ifndef(tok); tok_ifdef(tok); if (i >= 1000) throw fail("Too many iterations"); } return tok; } static List findFunctions(List tok) { //ret findFunctionDefinitions(join(findMainClass(tok))); return findFunctionDefs(findMainClass(tok)); } static String cacheGet(String snippetID) { snippetID = formatSnippetID(snippetID); String text = snippetCache.get(snippetID); if (text == null) snippetCache.put(snippetID, text = loadSnippet(snippetID)); return text; } static void cachePreload(List ids) { List needed = new ArrayList(); for (String id : ids) if (!snippetCache.containsKey(formatSnippetID(id))) needed.add(formatSnippetID(id)); if (l(needed) > 1) { List texts = loadSnippets(needed); for (int i = 0; i < l(needed); i++) if (texts.get(i) != null) snippetCache.put(needed.get(i), texts.get(i)); } } static List jtok(List tok) { return jtok(join(tok)); } static List jtok(String s) { List l = javaTok(s); return useIndexedList ? new IndexedList2(l) : l; } static HashSet haveClasses(List tok) { HashSet have = new HashSet(); for (List c : innerClassesOfMain(tok)) have.add(getClassDeclarationName(c)); have.addAll(tok_importedClassNames(tok)); have.add("String"); return have; } // works on Java level (no "sclass" etc) // returns list of classes we have (useful for other processing) static Set addStandardClasses(List tok, String... data) { HashSet have = haveClasses(tok); int j; while ((j = jfind(tok, "should not include class *.")) >= 0) { String cname = tok.get(j+8); shouldNotIncludeClass.add(cname); clearAllTokens(tok.subList(j, j+12)); } //L idx = IndexedList2.ensureIndexed(tok); Set idx = tokenIndexWithoutIfclass(tok); for (int i = 0; i+1 < l(data); i++) { String className = data[i], snippetID = data[i+1]; if (idx.contains(className) && !have.contains(className)) { if (shouldNotIncludeClass.contains(className)) { String msg = "INCLUDING BAD CLASS: " + className; print(msg); print(join(tok)); throw fail(msg); } print("Adding class " + className + " / " + snippetID); snippetID = formatSnippetID(snippetID); String text = cacheGet(snippetID); includeInMainLoaded(tok, text); List ct = javaTok(text); shortenedSubconcepts(ct); conceptDeclarations(ct); jreplace(ct, "sclass", "static class"); jreplace(ct, "sinterface", "static interface"); //tok_autoCloseBrackets(ct); for (List c : allClasses(ct)) have.add(getClassDeclarationName(c)); if (!have.contains(className)) throw fail("Wrongly defined class: " + className + " / " + snippetID); } } return have; } static Set expandableClassNames = lithashset("BigInteger"); // magically append ".class" to class name references static void expandClassReferences(List tok, Set classNames) { boolean change = false; for (int i = 3; i+2 < l(tok); i += 2) { String t = tok.get(i); if (classNames.contains(t) || expandableClassNames.contains(t)) { String s = tok.get(i-2); t = tok.get(i+2); if (eqOneOf(s, "instanceof", "new", ".", "<", ">", "implements", "throws", "extends", "/", "nu")) continue; if (isIdentifier(s) || isInteger(s)) continue; if (eq(s, ",") && isIdentifier(get(tok, i-4)) && eqGet(tok, i-6, "<")) continue; // e.g. T3 if (eq(s, ",") && eqOneOf(_get(tok, i-6), "implements", "throws")) continue; // TODO: longer lists // check for cast if (eq(s, "(") && eq(t, ")") && i >= 5) { if (!eqOneOf(get(tok, i+4), "{", ";")) { String x = tok.get(i-4); if (!isIdentifier(x)) continue; if (eqOneOf(x, "ret", "return")) continue; } } if (eqOneOf(t, ",", ")", ";", ":")) { tok.set(i, tok.get(i) + ".class"); change = true; } } } if (change) reTok(tok); } // "/" => "((ClassName) )" static void slashCasts(List tok, final Set classNames) { jreplace(tok, "/", "(($3) $1)", new Object() { Object get(List tok, int i) { return classNames.contains(tok.get(i+5)); } }); } // experimental - "(...)" => "new (...)" // doesn't work at beginning of statement as we can't easily // distinguish it from a constructor declaration. static void newWithoutNew(List tok, final Set classNames) { jreplace(tok, "(", "new $1(", new Object() { Object get(List tok, int i) { try { if (!classNames.contains(tok.get(i+1))) return false; boolean ok = neqOneOf(_get(tok, i-1), "new", ";", "}", "{", "public", "protected", "private", "."); //print("newWithoutNew: checking " + struct(subList(tok, i-1, i+2)) + " => " + ok); return ok; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (!classNames.contains(tok.get(i+1))) false;\n bool ok = neqOneOf(_get(tok, ..."; }}); } // +var => "var", +var static void expandVarCopies(List tok) { boolean change = false; for (int i = 3; i+2 < l(tok); i += 2) { if (!eq(tok.get(i), "+")) continue; if (!eqOneOf(tok.get(i-2), "(", ",", "{")) continue; String s = tok.get(i+2); if (!isIdentifier(s)) continue; tok.set(i, quote(s) + ", "); change = true; } if (change) reTok(tok); } static void processConceptsDot(List tok) { boolean change; do { change = false; for (int i : jfindAll(tok, "concepts.")) if (contains(get(tok, i+3), "\n")) { replaceTokens(tok, i, i+3, "!" + "include #1004863 // Dynamic Concepts"); reTok(tok, i, i+3); change = true; break; } } while (change); } static void addFieldOrder(List tok, int i) { int idx = findCodeTokens(tok, i, false, "{"); if (idx < 0) return; int j = findEndOfBracketPart(tok, idx); List vars = allVarNames(subList(tok, idx+1, j-1)); print("addFieldOrder " + struct(vars)); if (!vars.contains("_fieldOrder") && !isSortedList(vars)) { print("Adding field order"); tok.set(idx+2, "static String _fieldOrder = " + quote(join(" ", vars)) + ";\n " + tok.get(idx+2)); // reTok has to be done by caller } } static void caseAsVariableName(List tok) { if (!tok.contains("case")) return; for (int i = 1; i+2 < l(tok); i += 2) { String t = tok.get(i+2); if (tok.get(i).equals("case") && !(t.startsWith("'") || isInteger(t) || isIdentifier(t))) tok.set(i, "_case"); } } static void continueAsFunctionName(List tok) { jreplace(tok, "continue(", "_continue("); } // f bla => "bla" - and "please include function bla." static void functionReferences(List tok) { int i; String keyword = "f"; while ((i = jfind(tok, keyword + " ", new Object() { Object get(List tok, int i) { return !eq(tok.get(i+3), "instanceof"); } })) >= 0) { String f = tok.get(i+2); clearTokens(tok, i, i+2); tok.set(i+2, quote(f)); reTok(tok, i, i+2); tok.set(l(tok)-1, last(tok) + "\nplease include function " + f + "."); reTok(tok, l(tok)-1, l(tok)); } } // # 123 => "#123" static void directSnippetRefs(List tok) { int i; while ((i = jfind(tok, "#", new Object() { boolean get(List tok, int i) { return neq(_get(tok, i-1), "include"); } })) >= 0) { String id = tok.get(i+2); clearTokens(tok, i+1, i+3); tok.set(i, quote("#" + id)); reTok(tok, i, i+3); } } static void quicknu(List tok) { jreplace(tok, "nu (", "nu($2.class, "); jreplace(tok, "nu ", "new $2"); } // fill variable innerClasses_list static void innerClassesVar(List tok) { if (!tok.contains("myInnerClasses_list")) return; List have = classDeclarationNames(innerClassesOfMain(tok)); int i = jfind(tok, ">myInnerClasses_list;"); if (i < 0) return; tok.set(i+4, "=litlist(\n" + joinQuoted(", ", have) + ");"); reTok(tok, i+4, i+5); } // process ifclass x ... endif blocks static void tok_ifclass(List tok) { if (!tok.contains("ifclass")) return; List have = classDeclarationNames(innerClassesOfMain(tok)); int i; while ((i = rjfind(tok, "ifclass ")) >= 0) { int j = jfind(tok, i+4, "endif"); if (j < 0) j = l(tok)-1; boolean has = have.contains(tok.get(i+2)); clearTokens(tok, i, i+3); clearTokens(tok, j, j+1); if (!has) clearTokens(tok, i+3, j); reTok(tok, i, j+1); } } // set flag *. static void tok_definitions(List tok) { int i; while ((i = jfind(tok, "set flag .")) >= 0) { String fname = tok.get(i+4); definitions.add(fname); clearAllTokens(tok.subList(i, i+8)); } } // process ifndef x ... endifndef blocks static void tok_ifndef(List tok) { if (!tok.contains("ifndef")) return; int i; while ((i = rjfind(tok, "ifndef ")) >= 0) { int j = jfind(tok, i+4, "endifndef"); if (j < 0) j = l(tok)-1; String fname = tok.get(i+2); boolean has = !definitions.contains(fname); print("ifndef " + fname + " => " + has); clearTokens(tok, i, i+3); clearTokens(tok, j, j+1); if (!has) clearTokens(tok, i+3, j); reTok(tok, i, j+1); } } // process ifdef x ... endifdef blocks static void tok_ifdef(List tok) { if (!tok.contains("ifdef")) return; int i; while ((i = rjfind(tok, "ifdef ")) >= 0) { int j = jfind(tok, i+4, "endifdef"); if (j < 0) j = l(tok)-1; String fname = tok.get(i+2); boolean has = definitions.contains(fname); print("ifdef " + fname + " => " + has); clearTokens(tok, i, i+3); clearTokens(tok, j, j+1); if (!has) clearTokens(tok, i+3, j); reTok(tok, i, j+1); } } static void conceptDeclarations(List tok) { for (String kw : ll("concept", "sconcept")) { Object cond = new Object() { Object get(List tok, int i) { addFieldOrder(tok, i+1); return true; } }; boolean re = false; if (jreplace(tok, kw + " {", "static class $2 extends Concept {", cond)) re = true; if (jreplace(tok, kw + " implements", "static class $2 extends Concept implements", cond)) re = true; if (jreplace(tok, kw + " ", "static class $2", cond)) re = true; if (re) reTok(tok); } } static void shortenedSubconcepts(List tok) { jreplace(tok, " > {", "concept $3 extends $1 {", new Object() { Object get(List tok, int i) { boolean b = (i == 0 || tok.get(i).contains("\n")) || eq(_get(tok, i-1), "abstract"); // only at beginning of line or after "abstract" //print("subconcept " + b + ": " + structure(subList(tok, i-1, i+5))); return b; }}); } // -slightly experimental // -do calculation in another thread, then return to AWT thread // -must be placed in a block // -transforms rest of block static void unswing(List tok) { int i; while ((i = jfind(tok, "unswing {")) >= 0) { int idx = i+2; int closingBracket = findEndOfBracketPart(tok, idx)-1; int endOfOuterBlock = findEndOfBracketPart(tok, closingBracket)-1; tok.set(i, "thread"); tok.set(closingBracket, " awt {"); tok.set(endOfOuterBlock, "}}}"); reTok(tok, closingBracket-1, endOfOuterBlock+1); } } // -slightly experimental // -Syntax: lock theLock; // -lock a lock, unlock at end of current block with finally static void lockBlocks(List tok) { int i; while ((i = jfind(tok, "lock ", new Object() { Object get(List tok, int i) { try { return neq(tok.get(i+3), "instanceof") ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "neq(tok.get(i+3), \"instanceof\")"; }})) >= 0) { int semicolon = findEndOfStatement(tok, i)-1; String var = makeVar("lock"); int endOfOuterBlock = findEndOfBracketPart(tok, semicolon)-1; replaceTokens(tok, i, semicolon+1, "Lock " + var + " = " + joinSubList(tok, i+2, semicolon-1) + "; lock(" + var + "); try {"); tok.set(endOfOuterBlock, "} finally { " + var + ".unlock(); } }"); reTok(tok, i, endOfOuterBlock+1); } } static String struct(Object o) { return structure(o); } static String struct(Object o, structure_Data data) { return structure(o, data); } // map: index of opening bracket -> index of closing bracket static Map getBracketMap(List tok) { Map map = new HashMap(); List stack = new ArrayList(); for (int i = 1; i < l(tok); i+= 2) { if (litlist("{", "(").contains(tok.get(i))) stack.add(i); else if (litlist("}", ")").contains(tok.get(i))) { if (!empty(stack)) map.put(liftLast(stack), i); } } return map; } static List> innerClassesOfMain(List tok) { return innerClasses(findMainClass(tok)); } static String joinQuoted(String sep, Collection c) { List l = new ArrayList(); for (String s : c) l.add(quote(s)); return join(sep, l); } static List classDeclarationNames(List> decls) { List have = new ArrayList(); for (List c : decls) have.add(getClassDeclarationName(c)); return have; } static int shorten_default = 100; static String shorten(String s) { return shorten(s, shorten_default); } static String shorten(String s, int max) { if (s == null) return ""; if (max < 0) return s; return s.length() <= max ? s : s.substring(0, Math.min(s.length(), max)) + "..."; } static String shorten(int max, String s) { return shorten(s, max); } static int identityHashCode(Object o) { return System.identityHashCode(o); } static String quote(Object o) { if (o == null) return "null"; return quote(str(o)); } static String quote(String s) { if (s == null) return "null"; StringBuilder out = new StringBuilder((int) (l(s)*1.5+2)); quote_impl(s, out); return out.toString(); } static void quote_impl(String s, StringBuilder out) { out.append('"'); int l = s.length(); for (int i = 0; i < l; i++) { char c = s.charAt(i); if (c == '\\' || c == '"') out.append('\\').append(c); else if (c == '\r') out.append("\\r"); else if (c == '\n') out.append("\\n"); else out.append(c); } out.append('"'); } static String trim(String s) { return s == null ? null : s.trim(); } static String trim(StringBuilder buf) { return buf.toString().trim(); } static String trim(StringBuffer buf) { return buf.toString().trim(); } public static String join(String glue, Iterable strings) { if (strings == null) return ""; 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)); } static String join(Iterable strings) { return join("", strings); } static String join(Iterable strings, String glue) { return join(glue, strings); } public static String join(String[] strings) { return join("", strings); } static String dropPrefix(String prefix, String s) { return s == null ? null : s.startsWith(prefix) ? s.substring(l(prefix)) : s; } static List jfindAll(List tok, String pat) { List tokin = javaTok(pat); jfind_preprocess(tokin); String[] toks = toStringArray(codeTokensOnly(tokin)); int i = -1; List l = new ArrayList(); while ((i = findCodeTokens(tok, i+1, false, toks)) >= 0) l.add(i); return l; } static boolean useDummyMainClasses() { return eq("1", trim(loadTextFile(getProgramFile("#1008755", "use-dummy-main-classes")))); } static List findInnerClassOfMain(List tok, String className) { for (List c : innerClassesOfMain(tok)) { String name = getClassDeclarationName(c); if (eq(name, className)) return c; } return null; } static String getClassDeclarationName(List c) { if (c != null) for (int i = 1; i+2 < c.size(); i += 2) if (eqOneOf(c.get(i), "class", "interface", "enum")) return c.get(i+2); return null; } static List tok_importedClassNames(List tok) { List names = new ArrayList(); for (int i = 1; i < l(tok); i += 2) if (eq(tok.get(i), "import")) { int j = findEndOfStatement(tok, i); // index of ;+1 String s = get(tok, j-3); if (isIdentifier (s)) names.add(s); i = j-1; } return names; } static void tok_autosemi(List tok) { int i; while ( (i = jfind(tok, "autosemi {")) >= 0 || (i = jfind(tok, "semiauto {")) >= 0) { int closing = findEndOfBlock(tok, i+2)-1; tok.set(i, ""); // remove autosemi keyword tok.set(i+1, ""); for (int j = i+5; j < closing; j += 2) if (containsNewLine(tok.get(j)) && neqOneOf(tok.get(j-1), "{", "}", ";")) tok.set(j-1, tok.get(j-1) + ";"); reTok(tok, i, closing); } } static int jfind(List tok, String in) { return jfind(tok, 1, in); } static int jfind(List tok, int startIdx, String in) { return jfind(tok, startIdx, in, null); } static int jfind(List tok, String in, Object condition) { return jfind(tok, 1, in, condition); } static int jfind(List tok, int startIdx, String in, Object condition) { List tokin = javaTok(in); jfind_preprocess(tokin); return jfind(tok, startIdx, tokin, condition); } // assumes you preprocessed tokin static int jfind(List tok, List tokin) { return jfind(tok, 1, tokin); } static int jfind(List tok, int startIdx, List tokin) { return jfind(tok, startIdx, tokin, null); } static int jfind(List tok, int startIdx, List tokin, Object condition) { return findCodeTokens(tok, startIdx, false, toStringArray(codeTokensOnly(tokin)), condition); } static void jfind_preprocess(List tok) { for (String type : litlist("quoted", "id", "int")) replaceSublist(tok, ll("<", "", type, "", ">"), ll("<" + type + ">")); replaceSublist(tok, ll("\\", "", "*"), ll("\\*")); } static List cloneList(Collection l) { if (l == null) return new ArrayList(); // assume collection's mutex is equal to collection, which will be true unless you explicitly pass a mutex to synchronizedList() which no one ever does. synchronized(l) { return new ArrayList(l); } } static List diff(Collection a, Collection b) { Set set = asSet(b); List l = new ArrayList(); for (String s : a) if (!set.contains(s)) l.add(s); return l; } static String str(Object o) { return o == null ? "null" : o.toString(); } static String str(char[] c) { return new String(c); } static RuntimeException fail() { throw new RuntimeException("fail"); } static RuntimeException fail(Throwable e) { throw asRuntimeException(e); } 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, Throwable innerException) { throw new RuntimeException(msg, innerException); } // It's rough but should work if you don't make anonymous classes inside the statement or something... // Return value is index of semicolon+1 static int findEndOfStatement(List cnc, int i) { int j = indexOf(cnc, ";", i); return j < 0 ? l(cnc)-1 : j+1; } static boolean contains(Collection c, Object o) { return c != null && c.contains(o); } static boolean contains(Object[] x, Object o) { if (x != null) for (Object a : x) if (eq(a, o)) return true; return false; } static boolean contains(String s, char c) { return s != null && s.indexOf(c) >= 0; } static boolean contains(String s, String b) { return s != null && s.indexOf(b) >= 0; } // finds " {" // func: tok, C token index of keyword -> S[] {beg, end} static List replaceKeywordPlusQuotedBlock(List tok, String keyword, Object func) { for (int i = 0; i < 1000; i++) { int idx = jfind(tok, keyword + " {"); if (idx < 0) break; int j = findEndOfBracketPart(tok, idx+4); String[] be = (String[]) callF(func, tok, idx); tok.set(idx, be[0]); clearTokens(tok, idx+1, idx+5); tok.set(j-1, be[1]); reTok(tok, idx, j); } return tok; } static List toLinesFullTrim(String s) { List l = toLines(s); for (ListIterator i = l.listIterator(); i.hasNext(); ) { String line = i.next().trim(); if (line.length() == 0) i.remove(); else i.set(line); } return l; } static List toLinesFullTrim(File f) { return toLinesFullTrim(loadTextFile(f)); } static boolean tok_repeatWithSleep_verbose; static List tok_repeatWithSleep(List tok) { int i; while ((i = jfind(tok, "repeat with sleep * {")) >= 0) { String seconds = tok.get(i+6); // int or id int idx = findCodeTokens(tok, i, false, "{"); int j = findEndOfBracketPart(tok, idx); if (tok_repeatWithSleep_verbose) print("idx=" + idx + ", j=" + j); clearTokens(tok, i, idx+1); tok.set(i, "repeat { pcall {"); tok.set(j-1, "} sleepSeconds(" + seconds + "); }"); reTok(tok, j-1, j); reTok(tok, i, idx+1); } while ((i = jfind(tok, "repeat with ms sleep * {")) >= 0) { String ms = tok.get(i+8); // int or id int idx = findCodeTokens(tok, i, false, "{"); int j = findEndOfBracketPart(tok, idx); if (tok_repeatWithSleep_verbose) print("idx=" + idx + ", j=" + j); clearTokens(tok, i, idx+1); tok.set(i, "repeat { pcall {"); tok.set(j-1, "} sleep(" + ms + "); }"); reTok(tok, j-1, j); reTok(tok, i, idx+1); } return tok; } static String fsi(String id) { return formatSnippetID(id); } // returns index of endToken static int scanOverExpression(List tok, Map bracketMap, int i, String endToken) { while (i < l(tok)) { if (eq(endToken, tok.get(i))) return i; Integer j = bracketMap.get(i); if (j != null) i = j+1; else i++; } return i; } static String joinSubList(List l, int i, int j) { return join(subList(l, i, j)); } static String joinSubList(List l, int i) { return join(subList(l, i)); } static Set tokenIndexWithoutIfclass(List tok) { HashSet set = new HashSet(); int n = l(tok); int level = 0; for (int i = 1; i < n; i += 2) { String t = tok.get(i); if (eqOneOf(t, "ifclass", "ifndef")) ++level; else if (eqOneOf(t, "endif", "endifndef")) --level; else if (level <= 0) set.add(t); } return set; } static boolean allVarNames_debug; static List allVarNames(List tok) { boolean debug = allVarNames_debug; Map bracketMap = getBracketMap(tok); List varNames = new ArrayList(); for (int i = 3; i < tok.size(); i += 2) { String t = tok.get(i); if (eqOneOf(t, "=", ";", ",")) { String v = tok.get(i-2); if (isIdentifier(v)) varNames.add(v); if (eq(t, "=")) { i = scanToEndOfInitializer(tok, bracketMap, i)+1; assertTrue(odd(i)); } } else if (eq(t, "<")) { i = findEndOfTypeArgs(tok, i)+1; if (debug) print("Skipped type args: " + struct(subList(tok, i))); } else if (eq(t, "{")) i = findEndOfBlock(tok, i)+1; // skip function/class bodies } return varNames; } static List allVarNames(String text) { return allVarNames(javaTok(text)); } // replacement for class JavaTok // maybe incomplete, might want to add floating point numbers // todo also: extended multi-line strings static int javaTok_n, javaTok_elements; static boolean javaTok_opt; static List javaTok(String s) { return javaTok(s, null); } static List javaTok(String s, List existing) { ++javaTok_n; int nExisting = javaTok_opt && existing != null ? existing.size() : 0; ArrayList tok = existing != null ? new ArrayList(nExisting) : new ArrayList(); int l = s.length(); int i = 0, n = 0; while (i < l) { int j = i; char c, d; // scan for whitespace while (j < l) { c = s.charAt(j); d = j+1 >= l ? '\0' : s.charAt(j+1); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (c == '/' && d == '*') { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (c == '/' && d == '/') { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } if (n < nExisting && javaTok_isCopyable(existing.get(n), s, i, j)) tok.add(existing.get(n)); else tok.add(quickSubstring(s, i, j)); ++n; i = j; if (i >= l) break; c = s.charAt(i); d = i+1 >= l ? '\0' : s.charAt(i+1); // scan for non-whitespace // Special JavaX syntax: 'identifier if (c == '\'' && Character.isJavaIdentifierStart(d) && i+2 < l && "'\\".indexOf(s.charAt(i+2)) < 0) { j += 2; while (j < l && Character.isJavaIdentifierPart(s.charAt(j))) ++j; } else if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { if (s.charAt(j) == opener /*|| s.charAt(j) == '\n'*/) { // allow multi-line strings ++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 (c == '[' && d == '[') { do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]")); j = Math.min(j+2, l); } else if (c == '[' && d == '=' && 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; if (n < nExisting && javaTok_isCopyable(existing.get(n), s, i, j)) tok.add(existing.get(n)); else tok.add(quickSubstring(s, i, j)); ++n; i = j; } if ((tok.size() % 2) == 0) tok.add(""); javaTok_elements += tok.size(); return tok; } static List javaTok(List tok) { return javaTok(join(tok), tok); } static boolean javaTok_isCopyable(String t, String s, int i, int j) { return t.length() == j-i && s.regionMatches(i, t, 0, j-i); // << could be left out, but that's brave } static File getCacheProgramDir() { return getCacheProgramDir(getProgramID()); } static File getCacheProgramDir(String snippetID) { return new File(userHome(), "JavaX-Caches/" + formatSnippetIDOpt(snippetID)); } public static boolean isSnippetID(String s) { try { parseSnippetID(s); return true; } catch (RuntimeException e) { return false; } } static Map runTranslatorQuick_cache = new HashMap(); static synchronized String runTranslatorQuick(String text, String translatorID) { translatorID = formatSnippetID(translatorID); Class c = runTranslatorQuick_cache.get(translatorID); print("runTranslatorQuick " + programID() + " " + identityHashCode(main.class) + " CACHE " + identityHashCode(runTranslatorQuick_cache) + " " + structure(runTranslatorQuick_cache.keySet()) + " " + (c != null ? "CACHED" : "LOAD") + ": " + translatorID); if (c == null) { //printStackTrace(); c = hotwire(translatorID); print("runTranslatorQuick " + programID() + " " + identityHashCode(main.class) + " CACHE " + identityHashCode(runTranslatorQuick_cache) + " " + structure(runTranslatorQuick_cache.keySet()) + " STORE: " + translatorID + " " + identityHashCode(c)); runTranslatorQuick_cache.put(translatorID, c); } set(c, "mainJava", text); callMain(c); return (String) get(c, "mainJava"); } static List runTranslatorQuick(List tok, String translatorID) { return javaTok(runTranslatorQuick(join(tok), translatorID)); } static Set setIntersection(Set a, Set b) { Set set = similarEmptySet(a); for (A x : a) if (b.contains(x)) set.add(x); return set; } static void assertTrue(Object o) { assertEquals(true, o); } static boolean assertTrue(String msg, boolean b) { if (!b) throw fail(msg); return b; } static boolean assertTrue(boolean b) { if (!b) throw fail("oops"); return b; } static ArrayList litlist(A... a) { return new ArrayList(Arrays.asList(a)); } // get purpose 1: access a list/array/map (safer version of x.get(y)) static A get(List l, int idx) { return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null; } // seems to conflict with other signatures /*static B get(Map map, A key) { ret map != null ? map.get(key) : null; }*/ static A get(A[] l, int idx) { return idx >= 0 && idx < l(l) ? l[idx] : null; } // default to false static boolean get(boolean[] l, int idx) { return idx >= 0 && idx < l(l) ? l[idx] : false; } // get purpose 2: access a field by reflection or a map static Object get(Object o, String field) { try { if (o instanceof Class) return get((Class) o, field); if (o instanceof Map) return ((Map) o).get(field); Field f = getOpt_findField(o.getClass(), field); if (f != null) { f.setAccessible(true); return f.get(o); } if (o instanceof DynamicObject) return ((DynamicObject) o).fieldValues.get(field); } catch (Exception e) { throw asRuntimeException(e); } throw new RuntimeException("Field '" + field + "' not found in " + o.getClass().getName()); } 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()); } static List toLines(File f) { return toLines(loadTextFile(f)); } public static List toLines(String s) { List lines = new ArrayList(); if (s == null) return lines; 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 void tok_p_repeatWithSleep(List tok) { int i; while ((i = jfind(tok, "p-repeat with sleep * {")) >= 0) { String seconds = tok.get(i+6); // int or id int idx = findCodeTokens(tok, i, false, "{"); int j = findEndOfBracketPart(tok, idx); tok.set(i+2, " { "); tok.set(j-1, "} }"); reTok(tok, j-1); reTok(tok, i+2); } } static String tok_addSemicolon(List tok) { String lastToken = get(tok, l(tok)-2); if (eqOneOf(lastToken, "}", ";")) return join(tok); return join(tok) + ";"; } static String tok_addSemicolon(String s) { return tok_addSemicolon(javaTok(s)); } // func returns S[] {beg, end} static List replaceKeywordBlockDyn(List tok, String keyword, Object func) { for (int i = 0; i < 1000; i++) { int idx = findCodeTokens(tok, keyword, "{"); if (idx < 0) break; int j = findEndOfBracketPart(tok, idx+2); String[] be = (String[]) callF(func); tok.set(idx, be[0]); tok.set(idx+1, " "); tok.set(idx+2, ""); tok.set(j-1, be[1]); reTok(tok, idx, j); } return tok; } static Set asSet(Object[] array) { HashSet set = new HashSet(); for (Object o : array) if (o != null) set.add(o); return set; } static Set asSet(String[] array) { TreeSet set = new TreeSet(); for (String o : array) if (o != null) set.add(o); return set; } static Set asSet(Collection l) { if (l instanceof Set) return (Set) l; HashSet set = new HashSet(); for (A o : l) if (o != null) set.add(o); return set; } static List tok_moveImportsUp(List tok) { StringBuilder buf = new StringBuilder(); // scan imports on top int i; for (i = 1; i < l(tok); i += 2) if (eq(tok.get(i), "import")) { int j = indexOf(tok, i+2, ";"); if (j >= 0) i = j; else break; } else break; // scan imports in text for (; i < l(tok); i += 2) if (eq(tok.get(i), "import")) { int j = indexOf(tok, i+2, ";"); if (j < 0) continue; // huh? buf.append(joinSubList(tok, i, j+1)).append("\n"); replaceTokens_reTok(tok, i, j+1, ""); } if (nempty(buf)) { if (l(tok) == 1) addAll(tok, "", ""); tok.set(1, str(buf)+tok.get(1)); reTok(tok, 1, 2); } return tok; } static List reversedList(Collection l) { List x = cloneList(l); Collections.reverse(x); return x; } static Class __javax; static Class getJavaX() { return __javax; } 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_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 set_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()); } static Map cloneMap(Map map) { if (map == null) return litmap(); // assume mutex is equal to collection synchronized(map) { return map instanceof TreeMap ? new TreeMap(map) : map instanceof LinkedHashMap ? new LinkedHashMap(map) : new HashMap(map); } } static List javaTokC(String s) { int l = s.length(); ArrayList tok = new ArrayList(); int i = 0; while (i < l) { int j = i; char c, d; // scan for whitespace while (j < l) { c = s.charAt(j); d = j+1 >= l ? '\0' : s.charAt(j+1); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (c == '/' && d == '*') { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (c == '/' && d == '/') { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } i = j; if (i >= l) break; c = s.charAt(i); d = i+1 >= l ? '\0' : s.charAt(i+1); // 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 (c == '[' && d == '[') { do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]")); j = Math.min(j+2, l); } else if (c == '[' && d == '=' && 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(quickSubstring(s, i, j)); i = j; } return tok; } static void change() { //mainConcepts.allChanged(); // safe version for now cause function is sometimes included unnecessarily (e.g. by EGDiff) callOpt(getOptMC("mainConcepts"), "allChanged"); } static Map _registerThread_threads = Collections.synchronizedMap(new WeakHashMap()); static Thread _registerThread(Thread t) { _registerThread_threads.put(t, true); return t; } static void _registerThread() { _registerThread(Thread.currentThread()); } static List reTok(List tok) { replaceCollection(tok, javaTok(tok)); return tok; } static List reTok(List tok, int i) { return reTok(tok, i, i+1); } static List reTok(List tok, int i, int j) { // extend i to an "N" token // and j to "C" (so j-1 is an "N" token) i = i & ~1; j = j | 1; List t = javaTok(join(subList(tok, i, j))); replaceListPart(tok, i, j, t); // fallback to safety // reTok(tok); return tok; } static boolean isSortedList(List l) { for (int i = 0; i < l(l)-1; i++) if (cmp(l.get(i), l.get(i+1)) > 0) return false; return true; } static int l(Object[] a) { return a == null ? 0 : a.length; } static int l(boolean[] a) { return a == null ? 0 : a.length; } static int l(byte[] a) { return a == null ? 0 : a.length; } static int l(int[] a) { return a == null ? 0 : a.length; } static int l(float[] a) { return a == null ? 0 : a.length; } static int l(char[] a) { return a == null ? 0 : a.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(CharSequence s) { return s == null ? 0 : s.length(); } static long l(File f) { return f == null ? 0 : f.length(); } static int l(Object o) { return o instanceof String ? l((String) o) : o instanceof Map ? l((Map) o) : l((Collection) o); // incomplete } static int l(MultiSet ms) { return ms == null ? 0 : ms.size(); } static String[] toStringArray(Collection c) { String[] a = new String[l(c)]; Iterator it = c.iterator(); for (int i = 0; i < l(a); i++) a[i] = it.next(); return a; } static String[] toStringArray(Object o) { if (o instanceof String[]) return (String[]) o; else if (o instanceof Collection) return toStringArray((Collection) o); else throw fail("Not a collection or array: " + getClassName(o)); } 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 String getServerTranspiled2(String id) { String transpiled = loadCachedTranspilation(id); String md5 = null; if (isOfflineMode()) return transpiled; if (transpiled != null) md5 = md5(transpiled); String transpiledSrc = getServerTranspiled(formatSnippetID(id), md5); if (eq(transpiledSrc, "SAME")) { if (!isTrue(loadPage_silent.get())) print("SAME"); return transpiled; } return transpiledSrc; } static String javaDropComments(String s) { List tok = javaTok(s); replaceLastElement(tok, ""); return join(tok); } static Object call(Object o) { return callFunction(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 String jreplace(String s, String in, String out) { return jreplace(s, in, out, null); } static String jreplace(String s, String in, String out, Object condition) { List tok = javaTok(s); jreplace(tok, in, out, condition); return join(tok); } // 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, null); } static boolean jreplace(List tok, String in, String out, Object condition) { return jreplace(tok, in, out, false, true, condition); } static boolean jreplace(List tok, String in, String out, boolean ignoreCase, boolean reTok, Object condition) { List tokin = javaTok(in); jfind_preprocess(tokin); String[] toks = toStringArray(codeTokensOnly(tokin)); boolean anyChange = false; for (int n = 0; n < 10000; n++) { int i = findCodeTokens(tok, 1, ignoreCase, toks, condition); if (i < 0) return anyChange; List subList = tok.subList(i-1, i+l(tokin)-1); // N to N String expansion = jreplaceExpandRefs(out, subList); int end = i+l(tokin)-2; clearAllTokens(tok, i, end); // C to C tok.set(i, expansion); if (reTok) // would this ever be false?? reTok(tok, i, end); anyChange = true; } throw fail("woot? 10000! " + quote(in) + " => " + quote(out)); } static boolean jreplace_debug; static void addAll(Collection c, Collection b) { if (c != null && b != null) c.addAll(b); } static void addAll(Collection c, A... b) { if (c != null) c.addAll(Arrays.asList(b)); } // returns actual CNC static List findMainClass(List tok) { for (List c : reversedList(allClasses(tok))) { String name = getClassDeclarationName(c); if (eq(name, "main") || name.startsWith("x")) return c; } return findBlock("m {", tok); } static long parseLong(String s) { if (s == null) return 0; return Long.parseLong(dropSuffix("L", s)); } static long parseLong(Object s) { return Long.parseLong((String) s); } static boolean includeInMainLoaded_safe; static List includeInMainLoaded(List tok, String text) { List main = findMainClass(tok); if (main == null) { print(join(tok)); throw fail("no main class"); } int i = main.lastIndexOf("}"); main.set(i, "\n" + text + "\n}"); i += magicIndexOfSubList(tok, main); return includeInMainLoaded_safe ? includeInMainLoaded_reTok(tok, 0, l(tok)) : includeInMainLoaded_reTok(tok, i, i+1); } static String mainJava; static String loadMainJava() throws IOException { if (mainJava != null) return mainJava; return loadTextFile("input/main.java", ""); } 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 (Exception __e) { throw rethrow(__e); } } static String md5(byte[] data) { if (data == null) return "-"; return bytesToHex(md5_impl(data)); } static MessageDigest md5_md; /*static byte[] md5_impl(byte[] data) ctex { if (md5_md == null) md5_md = MessageDigest.getInstance("MD5"); return ((MessageDigest) md5_md.clone()).digest(data); }*/ static byte[] md5_impl(byte[] data) { try { return MessageDigest.getInstance("MD5").digest(data); } catch (Exception __e) { throw rethrow(__e); } } static String md5(File file) { try { return md5(loadBinaryFile(file)); } catch (Exception __e) { throw rethrow(__e); } } static int rjfind(List tok, String in) { return rjfind(tok, 1, in); } static int rjfind(List tok, int startIdx, String in) { return rjfind(tok, startIdx, in, null); } static int rjfind(List tok, String in, Object condition) { return rjfind(tok, 1, in, condition); } static int rjfind(List tok, int startIdx, String in, Object condition) { List tokin = javaTok(in); jfind_preprocess(tokin); return rjfind(tok, startIdx, tokin, condition); } // assumes you preprocessed tokin static int rjfind(List tok, List tokin) { return rjfind(tok, 1, tokin); } static int rjfind(List tok, int startIdx, List tokin) { return rjfind(tok, startIdx, tokin, null); } static int rjfind(List tok, int startIdx, List tokin, Object condition) { return rfindCodeTokens(tok, startIdx, false, toStringArray(codeTokensOnly(tokin)), condition); } static List ll(A... a) { return litlist(a); } static boolean isIdentifier(String s) { return isJavaIdentifier(s); } // i must point at the (possibly imaginary) opening bracket (any of the 2 types, not type parameters) // index returned is index of closing bracket + 1 static int findEndOfBracketPart(List cnc, int i) { int j = i+2, level = 1; while (j < cnc.size()) { if (litlist("{", "(").contains(cnc.get(j))) ++level; else if (litlist("}", ")").contains(cnc.get(j))) --level; if (level == 0) return j+1; ++j; } return cnc.size(); } 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 String sfu(Object o) { return structureForUser(o); } static int indexOfSubList(List x, List y) { return indexOfSubList(x, y, 0); } 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 int indexOfSubList(List x, A[] 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[j])) continue outer; return i; } return -1; } // lists returned are actual CNC (N/C/N/.../C/N) - and connected to // original list // only returns the top level classes static List> allClasses(List tok) { List> l = new ArrayList(); for (int i = 1; i < tok.size(); i += 2) { if (eq(tok.get(i), "{")) // skip functions i = findEndOfBlock(tok, i)-1; else if (eqOneOf(tok.get(i), "class", "interface", "enum") && (i == 1 || !tok.get(i-2).equals("."))) { int j = i; while (j < tok.size() && !tok.get(j).equals("{")) j += 2; j = findEndOfBlock(tok, j)+1; i = leftScanModifiers(tok, i); l.add(tok.subList(i-1, Math.min(tok.size(), j))); i = j-2; } } return l; } static List> allClasses(String text) { return allClasses(javaTok(text)); } static boolean empty(Collection c) { return c == null || c.isEmpty(); } static boolean empty(String s) { return s == null || s.length() == 0; } static boolean empty(Map map) { return map == null || map.isEmpty(); } static boolean empty(Object[] o) { return o == null || o.length == 0; } static boolean empty(Object o) { if (o instanceof Collection) return empty((Collection) o); if (o instanceof String) return empty((String) o); if (o instanceof Map) return empty((Map) o); if (o instanceof Object[]) return empty((Object[]) o); throw fail("unknown type for 'empty': " + getType(o)); } static boolean empty(float[] a) { return a == null || a.length == 0; } static boolean empty(int[] a) { return a == null || a.length == 0; } static boolean empty(long[] a) { return a == null || a.length == 0; } static int countChar(String s, char c) { int n = 0, l = l(s); for (int i = 0; i < l; i++) if (s.charAt(i) == c) ++n; return n; } static boolean tok_tokenBeforeLonelyReturnValue(String t) { return l(t) == 1 && "{};)".contains(t) || eq(t, "else"); } static boolean tok_hasClassRef2(List tok, String className) { int n = l(tok); int level = 0; for (int i = 1; i < n; i += 2) { String t = tok.get(i); if (eqOneOf(t, "ifclass", "ifndef")) ++level; else if (eqOneOf(t, "endif", "endifndef")) --level; else if (level <= 0 && eq(t, className)) return true; } return false; } static boolean isInteger(String s) { if (s == null) return false; int n = l(s); if (n == 0) return false; int i = 0; if (s.charAt(0) == '-') if (++i >= n) return false; while (i < n) { char c = s.charAt(i); if (c < '0' || c > '9') return false; ++i; } return true; } static boolean neqOneOf(Object o, Object... l) { for (Object x : l) if (eq(o, x)) return false; return true; } // supports the usual quotings (', ", variable length double brackets) static boolean isQuoted(String s) { if (startsWith(s, "'") || startsWith(s, "\"")) return true; return isMultilineQuoted(s); } static void replaceTokens(List tok, int i, int j, String s) { clearAllTokens(subList(tok, i, j)); tok.set(i, s); } static Set findFunctionDefs_keywords = new HashSet(splitAtSpace("static svoid ssvoid ssynchronized sbool sS sO sL")); static List findFunctionDefs(List tok) { int n = l(tok); List functions = new ArrayList(); for (int i = 1; i < n; i += 2) { String t = tok.get(i); if (findFunctionDefs_keywords.contains(t)) { int j = i+2; while (j < n && !eqOneOf(tok.get(j), ";", "=", "(", "{")) j += 2; if (eq(get(tok, j), "(") && isIdentifier(tok.get(j-2))) functions.add(tok.get(j-2)); } } return functions; } static String formatSnippetID(String id) { return "#" + parseSnippetID(id); } static String formatSnippetID(long id) { return "#" + id; } 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) { return findCodeTokens(tok, startIdx, ignoreCase, tokens, null); } static List findCodeTokens_specials = litlist("*", "", "", "", "\\*"); static boolean findCodeTokens_debug; static int findCodeTokens_indexed, findCodeTokens_unindexed; static int findCodeTokens_bails, findCodeTokens_nonbails; static int findCodeTokens(List tok, int startIdx, boolean ignoreCase, String[] tokens, Object condition) { if (findCodeTokens_debug) { if (eq(getClassName(tok), "main$IndexedList2")) findCodeTokens_indexed++; else findCodeTokens_unindexed++; } // bail out early if first token not found (works great with IndexedList) if (!findCodeTokens_specials.contains(tokens[0]) && !tok.contains(tokens[0] /*, startIdx << no signature in List for this, unfortunately */)) { ++findCodeTokens_bails; return -1; } ++findCodeTokens_nonbails; 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 if (eq(p, "")) match = isInteger(t); else if (eq(p, "\\*")) match = eq("*", t); else match = ignoreCase ? eqic(p, t) : eq(p, t); if (!match) continue outer; } if (condition == null || checkCondition(condition, tok, i-1)) // pass N index return i; } return -1; } static boolean neq(Object a, Object b) { return !eq(a, b); } static boolean containsToken(List tok, String token) { return tok.contains(token); } static int indexOf(List l, A a, int startIndex) { if (l == null) return -1; for (int i = startIndex; i < l(l); i++) if (eq(l.get(i), a)) return i; return -1; } static int indexOf(List l, int startIndex, A a) { return indexOf(l, a, startIndex); } static int indexOf(List l, A a) { if (l == null) return -1; return l.indexOf(a); } static int indexOf(String a, String b) { return a == null || b == null ? -1 : a.indexOf(b); } static int indexOf(String a, String b, int i) { return a == null || b == null ? -1 : a.indexOf(b, i); } static int indexOf(String a, char b) { return a == null ? -1 : a.indexOf(b); } static int indexOf(String a, char b, int i) { return a == null ? -1 : a.indexOf(b, i); } static int indexOf(String a, int i, String b) { return a == null || b == null ? -1 : a.indexOf(b, i); } static int indexOf(A[] x, A a) { if (x == null) return -1; for (int i = 0; i < l(x); i++) if (eq(x[i], a)) return i; return -1; } static boolean eqOneOf(Object o, Object... l) { for (Object x : l) if (eq(o, x)) return true; return false; } static boolean findFunctionInvocations_debug; // these prefix tokens mark a non-invocation static Set findFunctionInvocations_pre = new HashSet(litlist(".", "void", "S", "String", "int")); // sf = set of functions to search for or null for any function static Set findFunctionInvocations(List tok, Map sf) { int i; Set l = new HashSet(); while ((i = jfind(tok, "please include function *.")) >= 0) { String fname = tok.get(i+6); l.add(fname); clearAllTokens(tok.subList(i, i+10)); } boolean result = false; for (i = 1; i+2 < tok.size(); i += 2) { String f = tok.get(i); if (!isIdentifier(f)) continue; if (findFunctionInvocations_debug) System.out.println("Testing identifier " + f); if (!tok.get(i+2).equals("(")) continue; if (i == 1 || !findFunctionInvocations_pre.contains(tok.get(i-2)) || eq(get(tok, i-2), ".") && eq(get(tok, i-4), "main")) { boolean inSF = sf == null || sf.containsKey(f); if (findFunctionInvocations_debug) System.out.println("Possible invocation: " + f + ", inSF: " + inSF); if (inSF) l.add(f); } } return l; } static A last(List l) { return l.isEmpty() ? null : l.get(l.size()-1); } static char last(String s) { return empty(s) ? '#' : s.charAt(l(s)-1); } static int last(int[] a) { return l(a) != 0 ? a[l(a)-1] : 0; } static boolean isBracketHygienic(String s) { return testBracketHygiene(s); } static boolean isBracketHygienic(String s, String op, String close, Var msg) { return testBracketHygiene(s, op, close, msg); } static boolean isBracketHygienic(String s, String op, String close) { return testBracketHygiene(s, op, close, null); } // keyword can comprise multiple tokens now (like "p-awt"} static List replaceKeywordBlock(List tok, String keyword, String beg, String end) { return replaceKeywordBlock(tok, keyword, beg, end, false); } static List replaceKeywordBlock(List tok, String keyword, String beg, String end, boolean debug) { for (int n = 0; n < 1000; n++) { int i = jfind(tok, keyword + " {"); if (i < 0) break; int idx = findCodeTokens(tok, i, false, "{"); int j = findEndOfBracketPart(tok, idx); if (debug) { print(toUpper(keyword) + " BEFORE\n" + join(subList(tok, i, j))); print(" THEN " + join(subList(tok, j, j+10))); } //assertEquals("}", tok.get(j-1)); tok.set(j-1, end); replaceTokens(tok, i, idx+1, beg); reTok(tok, i, j); if (debug) print(toUpper(keyword) + "\n" + join(subList(tok, i, j)) + "\n"); } return tok; } static boolean eq(Object a, Object b) { return a == null ? b == null : a == b || a.equals(b); } static void clearTokens(List tok) { clearAllTokens(tok); } static void clearTokens(List tok, int i, int j) { clearAllTokens(tok, i, j); } static void tokSet(List tok, int idx, String token) { tok.set(idx, token); reTok(tok, idx); } static List splitAtJavaToken(String s, String splitToken) { return splitByJavaToken(s, splitToken); } static HashSet lithashset(A... items) { HashSet set = new HashSet(); for (A a : items) set.add(a); return set; } static List concatLists(Collection... lists) { List l = new ArrayList(); for (Collection list : lists) if (list != null) l.addAll(list); return l; } static List concatLists(Collection> lists) { List l = new ArrayList(); for (List list : lists) if (list != null) l.addAll(list); return l; } static String defaultTranslate(String text) { Class javax = getJavaX(); File x = makeTempDir(); saveTextFile(new File(x, "main.java"), text); List libraries_out = new ArrayList(); File y = (File) call(javax, "defaultTranslate", x, libraries_out); if (y == null) return null; return loadTextFile(new File(y, "main.java")); } static boolean hasCodeTokens(List tok, String... tokens) { return findCodeTokens(tok, tokens) >= 0; } static String getClassName(Object o) { return o == null ? "null" : o.getClass().getName(); } 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)); } static A _get(List l, int idx) { return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null; } static Object _get(Object o, String field) { return get(o, field); } static A _get(A[] l, int idx) { return idx >= 0 && idx < l(l) ? l[idx] : null; } static void splitJavaFiles(List tok) { try { List indexes = jfindAll(tok, "package"); if (empty(indexes) || indexes.get(0) != 1) indexes.add(0, 1); for (int i = 0; i < l(indexes); i++) { int from = indexes.get(i); int to = i+1 < l(indexes) ? indexes.get(i+1) : l(tok); List subtok = cncSubList(tok, from, to); String src = join(subtok); //print(shorten(src, 80)); String pack = tok_packageName(subtok); print("Package: " + quote(pack)); List> classes = allClasses(subtok); for (List c : classes) { //print(" Class: " + shorten(join(c), 80)); print(" Class: " + quote(getClassDeclarationName(c))); } if (empty(classes)) print("No classes?? " + quote(src)); else if (l(classes) == 1) { String fileName = addSlash(pack.replace('.', '/')) + getClassDeclarationName(first(classes)) + ".java"; print("File name: " + fileName); saveTextFile("output/" + fileName, join(subtok)); } else throw fail("Not supported"); print(); } } catch (Exception __e) { throw rethrow(__e); } } static boolean startsWithLowerCaseOrUnderscore(String s) { return nempty(s) && (s.startsWith("_") || Character.isLowerCase(s.charAt(0))); } static A printStruct(String prefix, A a) { printStructure(prefix, a); return a; } static A printStruct(A a) { printStructure(a); return a; } // optionally convert expression to return statement static String tok_addReturn(List tok) { String lastToken = get(tok, l(tok)-2); //print("addReturn: " + structure(tok) + ", lastToken: " + quote(lastToken)); if (eq(lastToken, "}") || eq(lastToken, ";")) return join(tok); return "ret " + join(tok) + ";"; } static String tok_addReturn(String s) { return tok_addReturn(javaTok(s)); } static void replaceTokens_reTok(List tok, int i, int j, String s) { replaceTokens(tok, i, j, s); reTok(tok, i, j); } static String jreplace1(String s, String in, String out) { return jreplace1(s, in, out, null); } static String jreplace1(String s, String in, String out, Object condition) { List tok = javaTok(s); jreplace1(tok, in, out, condition); return join(tok); } // leaves tok properly tokenized // returns true iff anything was replaced static boolean jreplace1(List tok, String in, String out) { return jreplace1(tok, in, out, false, true, null); } static boolean jreplace1(List tok, String in, String out, Object condition) { return jreplace1(tok, in, out, false, true, condition); } static boolean jreplace1(List tok, String in, String out, boolean ignoreCase, boolean reTok, Object condition) { List tokin = javaTok(in); jfind_preprocess(tokin); boolean anyChange = false; int i = 1; while ((i = findCodeTokens(tok, i, ignoreCase, toStringArray(codeTokensOnly(tokin)), condition)) >= 0) { List subList = tok.subList(i-1, i+l(tokin)-1); // N to N String expansion = jreplaceExpandRefs(out, subList); int end = i+l(tokin)-2; clearAllTokens(tok, i, end); // C to C tok.set(i, expansion); if (reTok) // would this ever be false?? reTok(tok, i, end); i = end; anyChange = true; } return anyChange; } static boolean jreplace1_debug; static boolean eqGet(List l, int i, Object o) { return eq(get(l, i), o); } // usually L static String fromLines(Collection lines) { StringBuilder buf = new StringBuilder(); if (lines != null) for (Object line : lines) buf.append(str(line)).append('\n'); return buf.toString(); } static String fromLines(String... lines) { return fromLines(asList(lines)); } static void tok_singleQuoteIdentifiersToStringConstants(List tok) { for (int i = 1; i < l(tok); i += 2) { String t = tok.get(i); if (isSingleQuoteIdentifier(t)) tok.set(i, quote(substring(t, 1))); } } static void remove(List l, int i) { if (l != null && i >= 0 && i < l(l)) l.remove(i); } static void remove(Collection l, A a) { if (l != null) l.remove(a); } static String substring(String s, int x) { return substring(s, x, l(s)); } static String substring(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 tok_autoCloseBrackets(String s) { return join(tok_autoCloseBrackets(javaTok(s))); } // modifies tok static List tok_autoCloseBrackets(List tok) { bigloop: for (int i = 1; i < l(tok); i += 2) { if (eq(tok.get(i), ";")) { int j, level = 0; for (j = i-2; j >= 0; j -= 2) { String t = tok.get(j); if (eqOneOf(t, ";", "{")) break; if (eq(t, "}")) break; // TODO: skip over until other end of bracket else if (eq(t, ")")) --level; else if (eq(t, "(")) { if (eq(get(tok, j-2), "for")) break; if (eq(get(tok, j-4), "for")) break; // for ping ++level; } } while (level-- > 0) { tok.add(i++, ")"); tok.add(i++, ""); } } } return tok; } static List findImports(String src) { List imports = new ArrayList(); for (String line : toLines(src)) { Matcher matcher = Pattern.compile("^\\s*import\\s+(.*?)\\s*;").matcher(line); if (matcher.find()) imports.add(matcher.group(1)); } return imports; } static long now_virtualTime; static long now() { return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis(); } static List loadSnippets(String... ids) { return loadSnippets(asList(ids)); } static List loadSnippets(List ids) { try { List texts = new ArrayList(); Map map = jsonDecodeMap(doPost( "ids=" + urlencode(join(" ", parseSnippetIDs(ids))), tb_mainServer() + "/get-multi.php")); for (String id : ids) texts.add(lookupSnippetID(map, id)); return texts; } catch (Exception __e) { throw rethrow(__e); } } static boolean preferCached = false; static boolean loadSnippet_debug = false; static ThreadLocal loadSnippet_silent = new ThreadLocal(); static String loadSnippet(String snippetID) { try { return loadSnippet(parseSnippetID(snippetID), preferCached); } catch (IOException e) { throw new RuntimeException(e); } } static String loadSnippet(String snippetID, boolean preferCached) throws IOException { return loadSnippet(parseSnippetID(snippetID), preferCached); } public static String loadSnippet(long snippetID) { try { return loadSnippet(snippetID, preferCached); } catch (IOException e) { throw new RuntimeException(e); } } public static String loadSnippet(long snippetID, boolean preferCached) throws IOException { String text; // boss bot disabled for now for shorter transpilations /*text = getSnippetFromBossBot(snippetID); if (text != null) return text;*/ initSnippetCache(); text = DiskSnippetCache_get(snippetID); if (preferCached && text != null) return text; try { if (loadSnippet_debug && text != null) System.err.println("md5: " + md5(text)); String url = tb_mainServer() + "/getraw.php?id=" + snippetID + "&utf8=1"; if (nempty(text)) url += "&md5=" + md5(text); url += standardCredentials(); String text2 = isTrue(loadSnippet_silent.get()) ? loadPageSilently(url) : loadPage(url); boolean same = eq(text2, "==*#*=="); if (loadSnippet_debug) print("loadSnippet: same=" + same); if (!same) text = text2; } 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(getGlobalCache()); } static Object loadVariableDefinition(String varName) { return loadVariableDefinition(getProgramID(), varName); } // currently only works with string lists ("= litlist(...)") // and strings. static Object loadVariableDefinition(String progIDOrSrc, String varName) { if (isSnippetID(progIDOrSrc)) progIDOrSrc = loadSnippet(progIDOrSrc); List tok = javaTok(progIDOrSrc); int i = findCodeTokens(tok, varName, "="); if (i < 0) return null; i += 4; if (isQuoted(tok.get(i))) return unquote(tok.get(i)); if (eq(get(tok, i), "litlist") && eq(get(tok, i+2), "(")) { int opening = i+2; int closing = findEndOfBracketPart(tok, opening)-1; List l = new ArrayList(); for (i = opening+2; i < closing; i += 4) l.add(unquote(tok.get(i))); return l; } throw fail("Unknown variable type or no definition in source: " + shorten(progIDOrSrc, 100) + "/" + varName); } static String unquote(String s) { if (s == null) return null; 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.startsWith("\'")) && s.length() > 1) { int l = s.endsWith(substring(s, 0, 1)) ? s.length()-1 : s.length(); StringBuilder sb = new StringBuilder(l-1); for (int i = 1; i < l; i++) { char ch = s.charAt(i); if (ch == '\\') { char nextChar = (i == l - 1) ? '\\' : s.charAt(i + 1); // Octal escape? if (nextChar >= '0' && nextChar <= '7') { String code = "" + nextChar; i++; if ((i < l - 1) && s.charAt(i + 1) >= '0' && s.charAt(i + 1) <= '7') { code += s.charAt(i + 1); i++; if ((i < l - 1) && s.charAt(i + 1) >= '0' && s.charAt(i + 1) <= '7') { code += s.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 >= l - 5) { ch = 'u'; break; } int code = Integer.parseInt( "" + s.charAt(i + 2) + s.charAt(i + 3) + s.charAt(i + 4) + s.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(); } return s; // not quoted - return original } static boolean isEmpty(Collection c) { return c == null || c.isEmpty(); } static boolean isEmpty(CharSequence s) { return s == null || s.length() == 0; } static boolean isEmpty(Object[] a) { return a == null || a.length == 0; } static boolean isEmpty(Map map) { return map == null || map.isEmpty(); } 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 nempty(Collection c) { return !isEmpty(c); } static boolean nempty(CharSequence s) { return !isEmpty(s); } static boolean nempty(Object[] o) { return !isEmpty(o); } static boolean nempty(Map m) { return !isEmpty(m); } static boolean nempty(Iterator i) { return i != null && i.hasNext(); } static String dropSuffix(String suffix, String s) { return s.endsWith(suffix) ? s.substring(0, l(s)-l(suffix)) : s; } // f: func(L) -> L static List reTok_modify(List tok, int i, int j, Object f) { // extend i to an "N" token // and j to "C" (so j-1 is an "N" token) i = i & ~1; j = j | 1; List t = javaTok(join(subList(tok, i, j))); if (f != null) t = (List) callF(f, t); replaceListPart(tok, i, j, t); return tok; } static boolean isMD5(String s) { return l(s) == 32 && isLowerHexString(s); } static List findTranslators(List lines) { List translators = new ArrayList(); Pattern pattern = Pattern.compile("^!([0-9# \t]+)"); Pattern pArgs = Pattern.compile("^\\s*\\((.*)\\)"); for (ListIterator iterator = lines.listIterator(); iterator.hasNext(); ) { String line = iterator.next(); line = line.trim(); Matcher matcher = pattern.matcher(line); if (matcher.find()) { String[] t = matcher.group(1).split("[ \t]+"); String rest = line.substring(matcher.end()); String arg = null; if (t.length == 1) { Matcher mArgs = pArgs.matcher(rest); if (mArgs.find()) arg = mArgs.group(1); } for (String transi : t) translators.add(new String[]{transi, arg}); iterator.remove(); } } return translators; } static File javaCompileToJar_rename(String src, File destJar, String progID) { return javaCompileToJar_rename(src, "", destJar, progID); } // returns path to jar static synchronized File javaCompileToJar_rename(String src, String dehlibs, File destJar, String progID) { String javaTarget = null; // use default target print("Compiling " + l(src) + " chars"); String dummyClass = dummyMainClassName(progID); src += "\nclass " + dummyClass + "{}"; String md5 = md5(src); File jar = destJar; Class j = getJavaX(); if (javaTarget != null) setOpt(j, "javaTarget", javaTarget); //setOpt(j, "verbose", true); File srcDir = (File) ( call(j, "TempDirMaker_make")); String className = "main"; String fileName = dummyClass + ".java"; File mainJava = new File(srcDir, fileName); //print("main java: " + mainJava.getAbsolutePath()); saveTextFile(mainJava, src); File classesDir = (File) call(j, "TempDirMaker_make"); List libraries = new ArrayList(); Matcher m = Pattern.compile("\\d+").matcher(dehlibs); while (m.find()) { String libID = m.group(); //print("libID=" + quote(libID)); assertTrue(isSnippetID(libID)); libraries.add(loadLibrary(libID)); } String compilerOutput; try { compilerOutput = (String) call(j, "compileJava", srcDir, libraries, classesDir); } catch (Throwable e) { compilerOutput = (String) get(getJavaX(), "javaCompilerOutput"); throw fail("Compile Error. " + compilerOutput + " " + e); } if (nempty(compilerOutput)) { print("Compiler said: " + compilerOutput); //fail("Compile Error. " + compilerOutput); } // sanity test if (!new File(classesDir, className + ".class").exists()) throw fail("No class generated (" + className + ")"); // add sources to .jar saveTextFile(new File(classesDir, "main.java"), src); // add information about libraries to jar if (nempty(dehlibs)) saveTextFile(new File(classesDir, "libraries"), dehlibs); //print("Zipping: " + classesDir.getAbsolutePath() + " to " + jar.getAbsolutePath()); dir2zip_recurse_verbose = false; int n = dir2zip_recurse(classesDir, jar); // cache on success only //print("Files zipped: " + n); return jar; } static File javaCompileToJar_standard(String src, File destJar) { return javaCompileToJar_standard(src, "", destJar); } // returns path to jar static synchronized File javaCompileToJar_standard(String src, String dehlibs, File destJar) { String javaTarget = null; // use default target print("Compiling " + l(src) + " chars"); String md5 = md5(src); File jar = destJar; Class j = getJavaX(); if (javaTarget != null) setOpt(j, "javaTarget", javaTarget); //setOpt(j, "verbose", true); File srcDir = (File) ( call(j, "TempDirMaker_make")); String className = "main"; String fileName = className + ".java"; File mainJava = new File(srcDir, fileName); //print("main java: " + mainJava.getAbsolutePath()); saveTextFile(mainJava, src); File classesDir = (File) call(j, "TempDirMaker_make"); List libraries = new ArrayList(); Matcher m = Pattern.compile("\\d+").matcher(dehlibs); while (m.find()) { String libID = m.group(); //print("libID=" + quote(libID)); assertTrue(isSnippetID(libID)); libraries.add(loadLibrary(libID)); } String compilerOutput; try { compilerOutput = (String) call(j, "compileJava", srcDir, libraries, classesDir); } catch (Throwable e) { compilerOutput = (String) get(getJavaX(), "javaCompilerOutput"); throw fail("Compile Error. " + compilerOutput + " " + e); } if (nempty(compilerOutput)) { print("Compiler said: " + compilerOutput); //fail("Compile Error. " + compilerOutput); } // sanity test if (!new File(classesDir, className + ".class").exists()) throw fail("No class generated (" + className + ")"); // add sources to .jar saveTextFile(new File(classesDir, "main.java"), src); // add information about libraries to jar if (nempty(dehlibs)) saveTextFile(new File(classesDir, "libraries"), dehlibs); //print("Zipping: " + classesDir.getAbsolutePath() + " to " + jar.getAbsolutePath()); dir2zip_recurse_verbose = false; int n = dir2zip_recurse(classesDir, jar); // cache on success only //print("Files zipped: " + n); return jar; } static volatile boolean readLine_noReadLine; static String readLine() { if (readLine_noReadLine) return null; return (String) call(getJavaX(), "readLine"); } static Pair pair(A a, B b) { return new Pair(a, b); } static boolean structure_showTiming, structure_checkTokenCount; static String structure(Object o) { return structure(o, new structure_Data()); } static String structure(Object o, structure_Data d) { StringWriter sw = new StringWriter(); d.out = new PrintWriter(sw); structure_go(o, d); String s = str(sw); if (structure_checkTokenCount) { print("token count=" + d.n); assertEquals("token count", l(javaTokC(s)), d.n); } return s; } static void structure_go(Object o, structure_Data d) { structure_1(o, d); while (nempty(d.stack)) popLast(d.stack).run(); } static void structureToPrintWriter(Object o, PrintWriter out) { structure_Data d = new structure_Data(); d.out = out; structure_go(o, d); } // leave to false, unless unstructure() breaks static boolean structure_allowShortening = false; static class structure_Data { PrintWriter out; int stringSizeLimit; int shareStringsLongerThan = 20; boolean noStringSharing; IdentityHashMap seen = new IdentityHashMap(); //new BitSet refd; HashMap strings = new HashMap(); HashSet concepts = new HashSet(); HashMap> fieldsByClass = new HashMap(); int n; // token count List stack = new ArrayList(); // append single token structure_Data append(String token) { out.print(token); ++n; return this; } structure_Data append(int i) { out.print(i); ++n; return this; } // append multiple tokens structure_Data append(String token, int tokCount) { out.print(token); n += tokCount; return this; } // extend last token structure_Data app(String token) { out.print(token); return this; } structure_Data app(int i) { out.print(i); return this; } } static void structure_1(final Object o, final structure_Data d) { if (o == null) { d.append("null"); return; } Class c = o.getClass(); boolean concept = false; concept = o instanceof Concept; List lFields = d.fieldsByClass.get(c); if (lFields == null) { // these are never back-referenced (for readability) if (o instanceof Number) { PrintWriter out = d.out; if (o instanceof Integer) { int i = ((Integer) o).intValue(); out.print(i); d.n += i < 0 ? 2 : 1; return; } if (o instanceof Long) { long l = ((Long) o).longValue(); out.print(l); out.print("L"); d.n += l < 0 ? 2 : 1; return; } if (o instanceof Float) { d.append("fl ", 2); quoteToPrintWriter(str(o), out); return; } if (o instanceof Double) { d.append("d(", 3); quoteToPrintWriter(str(o), out); d.append(")"); return; } if (o instanceof BigInteger) { out.print("bigint("); out.print(o); out.print(")"); d.n += ((BigInteger) o).signum() < 0 ? 5 : 4; return; } } if (o instanceof Boolean) { d.append(((Boolean) o).booleanValue() ? "t" : "f"); return; } if (o instanceof Character) { d.append(quoteCharacter((Character) o)); return; } if (o instanceof File) { d.append("File ").append(quote(((File) o).getPath())); return; } // referencable objects follow Integer ref = d.seen.get(o); if (o instanceof String && ref == null) ref = d.strings.get((String) o); if (ref != null) { /*d.refd.set(ref);*/ d.append("t").app(ref); return; } if (!(o instanceof String)) d.seen.put(o, d.n); // record token number else { String s = d.stringSizeLimit != 0 ? shorten((String) o, d.stringSizeLimit) : (String) o; if (!d.noStringSharing) { if (d.shareStringsLongerThan == Integer.MAX_VALUE) d.seen.put(o, d.n); if (l(s) >= d.shareStringsLongerThan) d.strings.put(s, d.n); } quoteToPrintWriter(s, d.out); d.n++; return; } if (o instanceof HashSet) { d.append("hashset "); structure_1(new ArrayList((Set) o), d); return; } if (o instanceof TreeSet) { d.append("treeset "); structure_1(new ArrayList((Set) o), d); return; } String name = c.getName(); if (o instanceof Collection && !startsWith(name, "main$") /* && neq(name, "main$Concept$RefL") */) { d.append("["); final int l = d.n; final Iterator it = ((Collection) o).iterator(); d.stack.add(new Runnable() { public void run() { try { if (!it.hasNext()) d.append("]"); else { d.stack.add(this); if (d.n != l) d.append(", "); structure_1(it.next(), d); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (!it.hasNext())\r\n d.append(\"]\");\r\n else {\r\n d.stack...."; }}); return; } if (o instanceof Map && !startsWith(name, "main$")) { if (o instanceof LinkedHashMap) d.append("lhm"); else if (o instanceof HashMap) d.append("hm"); d.append("{"); final int l = d.n; final Iterator it = ((Map) o).entrySet().iterator(); d.stack.add(new Runnable() { boolean v; Map.Entry e; public void run() { if (v) { d.append("="); v = false; d.stack.add(this); structure_1(e.getValue(), d); } else { if (!it.hasNext()) d.append("}"); else { e = (Map.Entry) it.next(); v = true; d.stack.add(this); if (d.n != l) d.append(", "); structure_1(e.getKey(), d); } } } }); return; } if (c.isArray()) { if (o instanceof byte[]) { d.append("ba ").append(quote(bytesToHex((byte[]) o))); return; } final int n = Array.getLength(o); if (o instanceof boolean[]) { String hex = boolArrayToHex((boolean[]) o); int i = l(hex); while (i > 0 && hex.charAt(i-1) == '0' && hex.charAt(i-2) == '0') i -= 2; d.append("boolarray ").append(n).app(" ").append(quote(substring(hex, 0, i))); return; } String atype = "array", sep = ", "; if (o instanceof int[]) { //ret "intarray " + quote(intArrayToHex((int[]) o)); atype = "intarray"; sep = " "; } d.append(atype).append("{"); d.stack.add(new Runnable() { int i; public void run() { if (i >= n) d.append("}"); else { d.stack.add(this); if (i > 0) d.append(", "); structure_1(Array.get(o, i++), d); } } }); return; } if (o instanceof Class) { d.append("class(", 2).append(quote(((Class) o).getName())).append(")"); return; } if (o instanceof Throwable) { d.append("exception(", 2).append(quote(((Throwable) o).getMessage())).append(")"); return; } if (o instanceof BitSet) { BitSet bs = (BitSet) o; d.append("bitset{", 2); int l = d.n; for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) { if (d.n != l) d.append(", "); d.append(i); } d.append("}"); return; } // Need more cases? This should cover all library classes... if (name.startsWith("java.") || name.startsWith("javax.")) { d.append("j ").append(quote(str(o))); return; // Hm. this is not unstructure-able } /*if (name.equals("main$Lisp")) { fail("lisp not supported right now"); }*/ String dynName = shortDynamicClassName(o); if (concept && !d.concepts.contains(dynName)) { d.concepts.add(dynName); d.append("c "); } // serialize an object with fields. // first, collect all fields and values in fv. TreeSet fields = new TreeSet(new Comparator() { public int compare(Field a, Field b) { return stdcompare(a.getName(), b.getName()); } }); Class cc = c; while (cc != Object.class) { for (Field field : getDeclaredFields_cached(cc)) { if ((field.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) != 0) continue; String fieldName = field.getName(); fields.add(field); // put special cases here... } cc = cc.getSuperclass(); } lFields = asList(fields); // Render this$1 first because unstructure needs it for constructor call. for (int i = 0; i < l(lFields); i++) { Field f = lFields.get(i); if (f.getName().equals("this$1")) { lFields.remove(i); lFields.add(0, f); break; } } d.fieldsByClass.put(c, lFields); } // << if (lFields == null) else { // ref handling for lFields != null Integer ref = d.seen.get(o); if (ref != null) { /*d.refd.set(ref);*/ d.append("t").app(ref); return; } d.seen.put(o, d.n); // record token number } LinkedHashMap fv = new LinkedHashMap(); for (Field f : lFields) { Object value; try { value = f.get(o); } catch (Exception e) { value = "?"; } if (value != null) fv.put(f.getName(), value); } String name = c.getName(); String shortName = dropPrefix("main$", name); // Now we have fields & values. Process fieldValues if it's a DynamicObject. // omit field "className" if equal to class's name if (concept && eq(fv.get("className"), shortName)) fv.remove("className"); if (o instanceof DynamicObject) { fv.putAll((Map) fv.get("fieldValues")); fv.remove("fieldValues"); shortName = shortDynamicClassName(o); fv.remove("className"); } String singleField = fv.size() == 1 ? first(fv.keySet()) : null; d.append(shortName); final int l = d.n; final Iterator it = fv.entrySet().iterator(); d.stack.add(new Runnable() { public void run() { try { if (!it.hasNext()) { if (d.n != l) d.append(")"); } else { Map.Entry e = (Map.Entry) it.next(); d.append(d.n == l ? "(" : ", "); d.append((String) e.getKey()).append("="); d.stack.add(this); structure_1(e.getValue(), d); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (!it.hasNext()) {\r\n if (d.n != l)\r\n d.append(\")\");\r\n } else {\r..."; }}); } static int parseInt(String s) { return empty(s) ? 0 : Integer.parseInt(s); } 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 int print_maxLineLength = 0; // 0 = unset static boolean print_silent; // total mute if set static Object print_byThread_lock = new Object(); static volatile ThreadLocal> print_byThread; // special handling by thread static void print() { print(""); } // slightly overblown signature to return original object... static A print(A o) { ping(); if (print_silent) return o; String s = String.valueOf(o) + "\n"; print_noNewLine(s); return o; } static void print_noNewLine(String s) { if (print_byThread != null) { F1 f = print_byThread.get(); if (f != null) if (isFalse(f.get(s))) return; } print_raw(s); } static void print_raw(String s) { s = fixNewLines(s); // TODO if (print_maxLineLength != 0) 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); } static void print(long l) { print(String.valueOf(l)); } static void print(char c) { print(String.valueOf(c)); } 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); } } } // will create the file or update its last modified timestamp static void touchFile(File file) { try { closeRandomAccessFile(newRandomAccessFile(mkdirsForFile(file), "rw")); } catch (Exception __e) { throw rethrow(__e); } } // out: func(L tok, int cIndex) -> S // condition: func(L tok, int nIndex) -> S [yeah it's inconsistent] static String jreplace_dyn(String s, String in, Object out) { return jreplace_dyn(s, in, out, null); } static String jreplace_dyn(String s, String in, Object out, Object condition) { List tok = javaTok(s); jreplace_dyn(tok, in, out, condition); return join(tok); } // leaves tok properly tokenized // returns true iff anything was replaced static boolean jreplace_dyn(List tok, String in, Object out) { return jreplace_dyn(tok, in, out, false, true, null); } static boolean jreplace_dyn(List tok, String in, Object out, Object condition) { return jreplace_dyn(tok, in, out, false, true, condition); } static boolean jreplace_dyn(List tok, String in, Object out, boolean ignoreCase, boolean reTok, Object condition) { List tokin = javaTok(in); jfind_preprocess(tokin); String[] toks = toStringArray(codeTokensOnly(tokin)); boolean anyChange = false; for (int n = 0; n < 10000; n++) { int i = findCodeTokens(tok, 1, ignoreCase, toks, condition); if (i < 0) return anyChange; String expansion = (String) ( callF(out, tok, i)); int end = i+l(tokin)-2; clearAllTokens(tok, i, end); // C to C tok.set(i, expansion); if (reTok) // would this ever be false?? reTok(tok, i, end); anyChange = true; } throw fail("woot? 10000! " + quote(in) + " => " + quote(out)); } static boolean tok_classHasModifier(List classDecl, String modifier) { int i = classDecl.indexOf("class"); return subList(classDecl, 0, i).contains(modifier); } static boolean tok_doAsMethodName_strict = false; // Strict mode: // Sometimes you have to make sure to omit the space after "do" // do(...) // as opposed to // do (bla+bla).something(); while ...; << very unusual anyway // Non-strict mode: // do (bla+bla).something(); while ...; // is not possible anymore (but who does this?) // You can use spaces however you like static void tok_doAsMethodName(List tok) { if (!tok.contains("do")) return; for (int i = 1; i+2 < l(tok); i += 2) { String next = tok.get(i+2), prev = get(tok, i-2); if (tok.get(i).equals("do") && eq(next, "(") && (!tok_doAsMethodName_strict || eq(prev, ".") || empty(tok.get(i+1)))) tok.set(i, "dO"); } } static WeakHashMap> callF_cache = new WeakHashMap(); static Object callF(Object f, Object... args) { try { if (f instanceof String) return callMC((String) f, args); if (f instanceof Runnable) { ((Runnable) f).run(); return null; } if (f == null) return null; Class c = f.getClass(); ArrayList methods; synchronized(callF_cache) { methods = callF_cache.get(c); if (methods == null) methods = callF_makeCache(c); } int n = l(methods); if (n == 0) throw fail("No get method in " + getClassName(c)); if (n == 1) return methods.get(0).invoke(f, args); for (int i = 0; i < n; i++) { Method m = methods.get(i); if (call_checkArgs(m, args, false)) return m.invoke(f, args); } throw fail("No matching get method in " + getClassName(c)); } catch (Exception __e) { throw rethrow(__e); } } // used internally static ArrayList callF_makeCache(Class c) { ArrayList l = new ArrayList(); Class _c = c; do { for (Method m : _c.getDeclaredMethods()) if (m.getName().equals("get")) { m.setAccessible(true); l.add(m); } if (!l.isEmpty()) break; _c = _c.getSuperclass(); } while (_c != null); callF_cache.put(c, l); return l; } static Object callFunction(Object f, Object... args) { return callF(f, args); } 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(Iterable s) { if (s instanceof ArrayList) return (ArrayList) s; ArrayList l = new ArrayList(); if (s != null) for (A a : s) l.add(a); return l; } static ArrayList asList(Enumeration e) { ArrayList l = new ArrayList(); if (e != null) while (e.hasMoreElements()) l.add(e.nextElement()); return l; } static void replaceListPart(List l, int i, int j, List l2) { l.subList(i, j).clear(); l.addAll(i, l2); } static String quoteCharacter(char c) { if (c == '\'') return "'\\''"; if (c == '\\') return "'\\\\'"; if (c == '\r') return "'\\r'"; if (c == '\n') return "'\\n'"; if (c == '\t') return "'\\t'"; return "'" + c + "'"; } // "$1" is first code token, "$2" second code token etc. static String jreplaceExpandRefs(String s, List tokref) { List tok = javaTok(s); for (int i = 1; i < l(tok); 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 boolean isLowerHexString(String s) { for (int i = 0; i < l(s); i++) { char c = s.charAt(i); if (c >= '0' && c <= '9' || c >= 'a' && c <= 'f') { // ok } else return false; } return true; } static String shortDynamicClassName(Object o) { if (o instanceof DynamicObject && ((DynamicObject) o).className != null) return ((DynamicObject) o).className; return shortClassName(o); } static void replaceCollection(Collection dest, Collection src) { dest.clear(); dest.addAll(src); } static String standardCredentials() { String user = standardCredentialsUser(); String pass = standardCredentialsPass(); if (nempty(user) && nempty(pass)) return "&_user=" + urlencode(user) + "&_pass=" + urlencode(pass); return ""; } static String getType(Object o) { return getClassName(o); } static String quickSubstring(String s, int i, int j) { if (i == j) return ""; return s.substring(i, j); } static String fixNewLines(String s) { return s.replace("\r\n", "\n").replace("\r", "\n"); } 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 String programID; static String getProgramID() { return nempty(programID) ? formatSnippetIDOpt(programID) : "?"; } // TODO: ask JavaX instead static String getProgramID(Class c) { String id = (String) getOpt(c, "programID"); if (nempty(id)) return formatSnippetID(id); return "?"; } static String getProgramID(Object o) { return getProgramID(getMainClass(o)); } static String unnull(String s) { return s == null ? "" : s; } static List unnull(List l) { return l == null ? emptyList() : l; } static Iterable unnull(Iterable i) { return i == null ? emptyList() : i; } static Object[] unnull(Object[] a) { return a == null ? new Object[0] : a; } static BitSet unnull(BitSet b) { return b == null ? new BitSet() : b; } static List findBlock(String pat, List tok) { List tokpat = javaTok(pat); int i = findCodeTokens(tok, toStringArray(codeTokensOnly(tokpat))); //print("index of block " + quote(pat) + ": " + i); if (i < 0) return null; int bracketIdx = i+tokpat.size()-3; assertEquals("{", tok.get(bracketIdx)); int endIdx = findEndOfBlock(tok, bracketIdx); return subList(tok, i-1, endIdx+1); // make it actual CNC } static int min(int a, int b) { return Math.min(a, b); } static long min(long a, long b) { return Math.min(a, b); } static float min(float a, float b) { return Math.min(a, b); } static float min(float a, float b, float c) { return min(min(a, b), c); } static double min(double a, double 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 float min(float[] c) { float x = Float.MAX_VALUE; for (float d : c) x = Math.min(x, d); return x; } static byte min(byte[] c) { byte x = 127; for (byte d : c) if (d < x) x = d; return x; } static short min(short[] c) { short x = 0x7FFF; for (short d : c) if (d < x) x = d; return x; } static boolean dir2zip_recurse_verbose; static int dir2zip_recurse(File inDir, File zip) { return dir2zip_recurse(inDir, zip, ""); } // TODO: the zero files case? static int dir2zip_recurse(File inDir, File zip, String outPrefix) { try { mkdirsForFile(zip); FileOutputStream fout = newFileOutputStream(zip); ZipOutputStream outZip = new ZipOutputStream(fout); try { return dir2zip_recurse(inDir, outZip, outPrefix, 0); } finally { outZip.close(); } } catch (Exception __e) { throw rethrow(__e); } } static int dir2zip_recurse(File inDir, ZipOutputStream outZip, String outPrefix, int level) { try { if (++level >= 20) throw fail("woot? 20 levels in zip?"); List files = new ArrayList(); for (File f : listFiles(inDir)) files.add(f); int n = 0; sortFilesByName(files); for (File f : files) { if (f.isDirectory()) { print("dir2zip_recurse: Scanning " + f.getAbsolutePath()); n += dir2zip_recurse(f, outZip, outPrefix + f.getName() + "/", level); } else { if (dir2zip_recurse_verbose) print("Copying " + f.getName()); outZip.putNextEntry(new ZipEntry(outPrefix + f.getName())); InputStream fin = new FileInputStream(f); copyStream(fin, outZip); fin.close(); ++n; } } return n; } catch (Exception __e) { throw rethrow(__e); } } static void closeRandomAccessFile(RandomAccessFile f) { if (f != null) try { f.close(); callJavaX("dropIO", f); } catch (Throwable e) { printStackTrace(e); } } static HashMap getDeclaredFields_cache = new HashMap(); static Field[] getDeclaredFields_cached(Class c) { Field[] fields; synchronized(getDeclaredFields_cache) { fields = getDeclaredFields_cache.get(c); if (fields == null) { getDeclaredFields_cache.put(c, fields = c.getDeclaredFields()); for (Field f : fields) f.setAccessible(true); } } return fields; } static File getGlobalCache() { File file = new File(javaxCachesDir(), "Binary Snippets"); file.mkdirs(); return file; } static List splitByJavaToken(String s, String splitToken) { List tok = javaTok(s); List l = new ArrayList(); int i = 1; while (i < l(tok)) { int j = smartIndexOf(tok, splitToken, i); l.add(join(subList(tok, i, j-1))); i = j+2; } return l; } static String toUpper(String s) { return s == null ? null : s.toUpperCase(); } static String boolArrayToHex(boolean[] a) { return bytesToHex(boolArrayToBytes(a)); } 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_cached(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; if (matches != null) matches.m = m; return true; } static A callMain(A c, String... args) { callOpt(c, "main", new Object[] {args}); return c; } static void callMain() { callMain(mc()); } 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 String getServerTranspiled(String snippetID) { return getServerTranspiled(snippetID, null); } // returns "SAME" if md5 matches static String getServerTranspiled(String snippetID, String expectedMD5) { try { long id = parseSnippetID(snippetID); /*S t = getTranspilationFromBossBot(id); if (t != null) return t;*/ String text = loadPage_utf8(tb_mainServer() + "/tb-int/get-transpiled.php?raw=1&withlibs=1&id=" + id + "&utf8=1" + (l(expectedMD5) > 1 ? "&md5=" + urlencode(expectedMD5) : "") + standardCredentials()); if (nempty(text) && neq(text, "SAME")) saveTranspiledCode(snippetID, text); return text; } catch (Exception __e) { throw rethrow(__e); } } // Finds out the index of a sublist in the original list // Works with nested (grand-parent) sublists. // Does not work with lists not made with subList() static int magicIndexOfSubList(List list, List sublist) { Integer o1 = (Integer) ( getOpt(list, "offset")); int o2 = (int) ( get(sublist, "offset")); return o2-(o1 != null ? o1 : 0); } static boolean isNonNegativeInteger(String s) { return s != null && Pattern.matches("\\d+", s); } static boolean checkCondition(Object condition, Object... args) { return isTrue(call(condition, "get", args)); } static boolean isFalse(Object o) { return eq(false, o); } /** writes safely (to temp file, then rename) */ static File saveTextFile(String fileName, String contents) throws IOException { CriticalAction action = beginCriticalAction("Saving file " + fileName + " (" + l(contents) + " chars)"); try { File file = new File(fileName); File parentFile = file.getParentFile(); if (parentFile != null) parentFile.mkdirs(); String tempFileName = fileName + "_temp"; File tempFile = new File(tempFileName); if (contents != null) { if (tempFile.exists()) try { String saveName = tempFileName + ".saved." + now(); copyFile(tempFile, new File(saveName)); } catch (Throwable e) { printStackTrace(e); } FileOutputStream fileOutputStream = newFileOutputStream(tempFile.getPath()); 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 (!tempFile.renameTo(file)) throw new IOException("Can't rename " + tempFile + " to " + file); return file; } finally { action.done(); } } static File saveTextFile(File fileName, String contents) { try { saveTextFile(fileName.getPath(), contents); return fileName; } catch (Exception __e) { throw rethrow(__e); } } // 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; } public static File mkdirsForFile(File file) { File dir = file.getParentFile(); if (dir != null) // is null if file is in current dir dir.mkdirs(); return file; } static String loadCachedTranspilation(String id) { return loadTextFile(new File(getCodeProgramDir(id), "Transpilation")); } static RandomAccessFile newRandomAccessFile(File path, String mode) throws IOException { RandomAccessFile f = new RandomAccessFile(path, mode); callJavaX("registerIO", f, path, mode.indexOf('w') >= 0); return f; } static int scanToEndOfInitializer(List tok, Map bracketMap, int i) { while (i < l(tok)) { if (litlist(";", ",", ")", "}").contains(tok.get(i))) return i-1; Integer j = bracketMap.get(i); if (j != null) i = j+1; else i++; } return i; } static String programID() { return getProgramID(); } static File getProgramFile(String progID, String fileName) { if (new File(fileName).isAbsolute()) return new File(fileName); return new File(getProgramDir(progID), fileName); } static File getProgramFile(String fileName) { return getProgramFile(getProgramID(), fileName); } static boolean isOfflineMode() { return eq("1", trim(loadProgramTextFile("#1005806", "offline-mode"))); } static boolean eqic(String a, String b) { if ((a == null) != (b == null)) return false; if (a == null) return true; return a.equalsIgnoreCase(b); } static int lastIndexOf(String a, String b) { return a == null || b == null ? -1 : a.lastIndexOf(b); } static Field setOpt_findField(Class c, String field) { HashMap map; synchronized(getOpt_cache) { map = getOpt_cache.get(c); if (map == null) map = getOpt_makeCache(c); } return map.get(field); } static void setOpt(Object o, String field, Object value) { try { if (o == null) return; Class c = o.getClass(); HashMap map; synchronized(getOpt_cache) { map = getOpt_cache.get(c); if (map == null) map = getOpt_makeCache(c); } if (map == getOpt_special) { if (o instanceof Class) { setOpt((Class) o, field, value); return; } // It's probably a subclass of Map. Use raw method setOpt_raw(o, field, value); return; } Field f = map.get(field); if (f != null) smartSet(f, o, value); // possible improvement: skip setAccessible } catch (Exception __e) { throw rethrow(__e); } } static void setOpt(Class c, String field, Object value) { if (c == null) return; try { Field f = setOpt_findStaticField(c, field); if (f != null) smartSet(f, null, value); } catch (Exception e) { throw new RuntimeException(e); } } static Field setOpt_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; } public static byte[] loadBinaryFile(String fileName) { try { 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(); } catch (IOException e) { throw new RuntimeException(e); } } public static byte[] loadBinaryFile(File file) { return loadBinaryFile(file.getPath()); } static String addSlash(String s) { return empty(s) || s.endsWith("/") ? s : s + "/"; } static A popLast(List l) { return liftLast(l); } static String urlencode(String x) { try { return URLEncoder.encode(unnull(x), "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } static List splitAtSpace(String s) { return asList(s.split("\\s+")); } static int leftScanModifiers(List tok, int i) { List mod = getJavaModifiers(); while (i > 1 && mod.contains(tok.get(i-2))) i -= 2; return i; } public static List parseSnippetIDs(Collection snippetIDs) { List l = new ArrayList(); for (String id : snippetIDs) l.add(str(parseSnippetID(id))); return l; } static List replaceSublist(List l, List x, List y) { if (x == null) return l; int i = 0; while (true) { i = indexOfSubList(l, x, i); if (i < 0) break; // It's inefficient :D for (int j = 0; j < l(x); j++) l.remove(i); l.addAll(i, y); i += l(y); } return l; } static List replaceSublist(List l, int fromIndex, int toIndex, List y) { // inefficient while (toIndex > fromIndex) l.remove(--toIndex); l.addAll(fromIndex, y); return l; } 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 Map litmap(Object... x) { TreeMap map = new TreeMap(); litmap_impl(map, x); return map; } static void litmap_impl(Map map, Object... x) { for (int i = 0; i < x.length-1; i += 2) if (x[i+1] != null) map.put(x[i], x[i+1]); } static String doPost(Map urlParameters, String url) { return doPost(makePostData(urlParameters), url); } static String doPost(String urlParameters, String url) { try { URL _url = new URL(url); return doPost(urlParameters, _url.openConnection(), _url); } catch (Exception __e) { throw rethrow(__e); } } static String doPost(String urlParameters, URLConnection conn, URL url) { try { setHeaders(conn); print("Sending POST request: " + url); // connect and do POST conn.setDoOutput(true); OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream()); writer.write(urlParameters); writer.flush(); String contents = loadPage(conn, url); writer.close(); return contents; } catch (Exception __e) { throw rethrow(__e); } } static boolean odd(int i) { return (i & 1) != 0; } static Object getOptMC(String field) { return getOpt(mc(), field); } static List cncSubList(List tok, int from, int to) { from &= ~1; to |= 1; return subList(tok, from, to); } static boolean isJavaIdentifier(String s) { if (empty(s) || !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 volatile boolean ping_pauseAll; static int ping_sleep = 100; // poll pauseAll flag every 100 static volatile boolean ping_anyActions; static Map ping_actions = synchroMap(new WeakHashMap()); // always returns true static boolean ping() { if (ping_pauseAll || ping_anyActions) ping_impl(); return true; } // returns true when it slept static boolean ping_impl() { try { if (ping_pauseAll && !isAWTThread()) { do Thread.sleep(ping_sleep); while (ping_pauseAll); return true; } if (ping_anyActions) { Object action; synchronized(ping_actions) { action = ping_actions.get(currentThread()); if (action instanceof Runnable) ping_actions.remove(currentThread()); if (ping_actions.isEmpty()) ping_anyActions = false; } if (action instanceof Runnable) ((Runnable) action).run(); else if (eq(action, "cancelled")) throw fail("Thread cancelled."); } return false; } catch (Exception __e) { throw rethrow(__e); } } static int cmp(Number a, Number b) { return a == null ? b == null ? 0 : -1 : cmp(a.doubleValue(), b.doubleValue()); } static int cmp(double a, double b) { return a < b ? -1 : a == b ? 0 : 1; } static int cmp(String a, String b) { return a == null ? b == null ? 0 : -1 : a.compareTo(b); } static int cmp(Object a, Object b) { if (a == null) return b == null ? 0 : -1; if (b == null) return 1; return ((Comparable) a).compareTo(b); } static boolean isMultilineQuoted(String s) { if (!startsWith(s, "[")) return false; int i = 1; while (i < s.length() && s.charAt(i) == '=') ++i; return i < s.length() && s.charAt(i) == '['; } static String dummyMainClassName(String progID) { return "m" + psI(progID); } static ThreadLocal loadPage_charset = new ThreadLocal(); static boolean loadPage_allowGzip = true, loadPage_debug; static boolean loadPage_anonymous; // don't send computer ID static int loadPage_verboseness = 100000; static int loadPage_retries = 1; //60; // seconds static ThreadLocal loadPage_silent = new ThreadLocal(); public static String loadPageSilently(String url) { try { return loadPageSilently(new URL(loadPage_preprocess(url))); } catch (Exception __e) { throw rethrow(__e); } } public static String loadPageSilently(URL url) { try { IOException e = null; for (int tries = 0; tries < loadPage_retries; tries++) try { URLConnection con = openConnection(url); return loadPage(con, url); } catch (IOException _e) { e = _e; if (loadPageThroughProxy_enabled) { print("Trying proxy because of: " + e); try { return loadPageThroughProxy(str(url)); } catch (Throwable e2) { print(" " + exceptionToStringShort(e2)); } } else if (loadPage_debug) print(e); sleepSeconds(1); } throw e; } catch (Exception __e) { throw rethrow(__e); } } static String loadPage_preprocess(String url) { if (url.startsWith("tb/")) // don't think we use this anymore url = tb_mainServer() + "/" + url; if (url.indexOf("://") < 0) url = "http://" + url; return url; } public static String loadPage(String url) { try { url = loadPage_preprocess(url); if (!isTrue(loadPage_silent.get())) print("Loading: " + hideCredentials(url)); return loadPageSilently(new URL(url)); } catch (Exception __e) { throw rethrow(__e); } } public static String loadPage(URL url) { print("Loading: " + hideCredentials(url.toExternalForm())); return loadPageSilently(url); } public static String loadPage(URLConnection con, URL url) throws IOException { try { if (!loadPage_anonymous) setHeaders(con); if (loadPage_allowGzip) con.setRequestProperty("Accept-Encoding", "gzip"); con.setRequestProperty("X-No-Cookies", "1"); } catch (Throwable e) {} // fails if within doPost String contentType = con.getContentType(); if (contentType == null) throw new IOException("Page could not be read: " + url); //print("Content-Type: " + contentType); String charset = loadPage_charset == null ? null : loadPage_charset.get(); if (charset == null) charset = loadPage_guessCharset(contentType); InputStream in = con.getInputStream(); try { if ("gzip".equals(con.getContentEncoding())) { if (loadPage_debug) print("loadPage: Using gzip."); in = new GZIPInputStream(in); } Reader r = new InputStreamReader(in, charset); StringBuilder buf = new StringBuilder(); int n = 0; while (true) { int ch = r.read(); if (ch < 0) break; buf.append((char) ch); ++n; if ((n % loadPage_verboseness) == 0) print(" " + n + " chars read"); } return buf.toString(); } finally { in.close(); } } static String loadPage_guessCharset(String contentType) { Pattern p = Pattern.compile("text/[a-z]+;\\s*charset=([^\\s]+)\\s*"); Matcher m = p.matcher(contentType); String match = m.matches() ? m.group(1) : null; if (loadPage_debug) print("loadPage: contentType=" + contentType + ", match: " + match); /* If Content-Type doesn't match this pre-conception, choose default and hope for the best. */ return or(match, "ISO-8859-1"); } 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 File userHome(String path) { return new File(userDir(), path); } static int max(int a, int b) { return Math.max(a, b); } static int max(int a, int b, int c) { return max(max(a, b), c); } 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 float max(float a, float b) { return Math.max(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; } static float max(float[] c) { if (c.length == 0) return Float.MAX_VALUE; float x = c[0]; for (int i = 1; i < c.length; i++) x = Math.max(x, c[i]); return x; } static byte max(byte[] c) { byte x = -128; for (byte d : c) if (d > x) x = d; return x; } static short max(short[] c) { short x = -0x8000; for (short d : c) if (d > x) x = d; return x; } static A assertEquals(Object x, A y) { return assertEquals(null, x, y); } static A assertEquals(String msg, Object x, A y) { if (!(x == null ? y == null : x.equals(y))) throw fail((msg != null ? msg + ": " : "") + y + " != " + x); return y; } static Map jsonDecodeMap(String s) { Object o = jsonDecode(s); if (o instanceof List && empty((List) o)) return new HashMap(); if (o instanceof Map) return (Map) o; else throw fail("Not a JSON map: " + s); } 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; } static Object first(Object list) { return empty((List) list) ? null : ((List) list).get(0); } static A first(List list) { return empty(list) ? null : list.get(0); } static A first(A[] bla) { return bla == null || bla.length == 0 ? null : bla[0]; } static A first(Iterable i) { if (i == null) return null; Iterator it = i.iterator(); return it.hasNext() ? it.next() : null; } static Character first(String s) { return empty(s) ? null : s.charAt(0); } static A first(Pair p) { return p == null ? null : p.a; } static Set similarEmptySet(Set m) { if (m instanceof TreeSet) return new TreeSet(); if (m instanceof LinkedHashSet) return new LinkedHashSet(); return new HashSet(); } static RuntimeException asRuntimeException(Throwable t) { return t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t); } // not including <> as they are ambiguous (< is also a comparison operator) static String testBracketHygiene_op = "([{"; static String testBracketHygiene_close = ")]}"; static boolean testBracketHygiene(String s) { return testBracketHygiene(s, testBracketHygiene_op, testBracketHygiene_close, null); } static boolean testBracketHygiene(String s, Var msg) { return testBracketHygiene(s, testBracketHygiene_op, testBracketHygiene_close, msg); } static boolean testBracketHygiene(String s, String op, String close, Var msg) { List tok = javaTok(s); Map map = new HashMap(); List stack = getBracketMap2(tok, map, op, close); if (nempty(stack)) { if (msg != null) msg.set("Bad hygiene - " + n(l(stack), "bracket") + " not closed"); return false; } if (map.containsKey(0)) { if (msg != null) msg.set("Bad hygiene - bracket not opened (" + quote(tok.get(map.get(0))) + ")"); return false; } for (int i : keys(map)) { int j = map.get(i); String a = tok.get(i), b = tok.get(j); int ai = op.indexOf(a), bi = close.indexOf(b); if (ai != bi) { if (msg != null) msg.set("Bad hygiene - brackets don't match (" + quote(a) + " vs " + quote(b) + ")"); return false; } } if (msg != null) msg.set("Hygiene OK!"); return true; } static void quoteToPrintWriter(String s, PrintWriter out) { if (s == null) { out.print("null"); return; } out.print('"'); int l = s.length(); for (int i = 0; i < l; i++) { char c = s.charAt(i); if (c == '\\' || c == '"') { out.print('\\'); out.print(c); } else if (c == '\r') out.print("\\r"); else if (c == '\n') out.print("\\n"); else out.print(c); } out.print('"'); } static void printStructure(String prefix, Object o) { if (endsWithLetter(prefix)) prefix += ": "; print(prefix + structureForUser(o)); } static void printStructure(Object o) { print(structureForUser(o)); } // 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 File loadLibrary(String snippetID) { return loadBinarySnippet(snippetID); } static int rfindCodeTokens(List tok, String... tokens) { return rfindCodeTokens(tok, 1, false, tokens); } static int rfindCodeTokens(List tok, boolean ignoreCase, String... tokens) { return rfindCodeTokens(tok, 1, ignoreCase, tokens); } static int rfindCodeTokens(List tok, int startIdx, boolean ignoreCase, String... tokens) { return rfindCodeTokens(tok, startIdx, ignoreCase, tokens, null); } static List rfindCodeTokens_specials = litlist("*", "", "", "", "\\*"); static boolean rfindCodeTokens_debug; static int rfindCodeTokens_indexed, rfindCodeTokens_unindexed; static int rfindCodeTokens_bails, rfindCodeTokens_nonbails; static int rfindCodeTokens(List tok, int startIdx, boolean ignoreCase, String[] tokens, Object condition) { if (rfindCodeTokens_debug) { if (eq(getClassName(tok), "main$IndexedList2")) rfindCodeTokens_indexed++; else rfindCodeTokens_unindexed++; } // bail out early if first token not found (works great with IndexedList) if (!rfindCodeTokens_specials.contains(tokens[0]) && !tok.contains(tokens[0] /*, startIdx << no signature in List for this, unfortunately */)) { ++rfindCodeTokens_bails; return -1; } ++rfindCodeTokens_nonbails; outer: for (int i = tok.size()-tokens.length*2; i >= startIdx; 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 if (eq(p, "")) match = isInteger(t); else if (eq(p, "\\*")) match = eq("*", t); else match = ignoreCase ? eqic(p, t) : eq(p, t); if (!match) continue outer; } if (condition == null || checkCondition(condition, tok, i-1)) // pass N index return i; } return -1; } static boolean containsNewLine(String s) { return contains(s, '\n'); // screw \r, nobody needs it } static Object callOpt(Object o) { if (o == null) return null; return callF(o); } 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) { //fail(e.getMessage() + " | Method: " + method + ", receiver: " + className(o) + ", args: (" + join(", ", map(f className, args) + ")"); 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; } public static long parseSnippetID(String snippetID) { long id = Long.parseLong(shortenSnippetID(snippetID)); if (id == 0) throw fail("0 is not a snippet ID"); return id; } static B lookupSnippetID(Map map, String id) { B b = map.get(formatSnippetID(id)); if (b != null) return b; return map.get("" + parseSnippetID(id)); } static String structureForUser(Object o) { return struct_noStringSharing(o); } static String formatSnippetIDOpt(String s) { return isSnippetID(s) ? formatSnippetID(s) : s; } static Class hotwire(String src) { assertFalse(_inCore()); Class j = getJavaX(); if (isAndroid()) { synchronized(j) { // hopefully this goes well... List libraries = new ArrayList(); File srcDir = (File) call(j, "transpileMain", src, libraries); if (srcDir == null) throw fail("transpileMain returned null (src=" + quote(src) + ")"); Object androidContext = get(j, "androidContext"); return (Class) call(j, "loadx2android", srcDir, src); } } else { // ret hotwire_overInternalBot(src); Class c = (Class) ( call(j, "hotwire", src)); hotwire_copyOver(c); return c; } } public static String loadTextFile(String fileName) { return loadTextFile(fileName, null); } public static String loadTextFile(File fileName, String defaultContents) { try { if (fileName == null || !fileName.exists()) return defaultContents; FileInputStream fileInputStream = new FileInputStream(fileName); InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8"); return loadTextFile(inputStreamReader); } catch (IOException e) { throw new RuntimeException(e); } } public static String loadTextFile(File fileName) { return loadTextFile(fileName, null); } public static String loadTextFile(String fileName, String defaultContents) { return fileName == null ? defaultContents : loadTextFile(newFile(fileName), defaultContents); } 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(); } // i must point at the opening bracket ("<") // index returned is index of closing bracket + 1 static int findEndOfTypeArgs(List cnc, int i) { int j = i+2, level = 1; while (j < cnc.size()) { if (cnc.get(j).equals("<")) ++level; else if (cnc.get(j).equals(">")) --level; if (level == 0) return j+1; ++j; } return cnc.size(); } 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(); try { f.set(o, value); } catch (Exception e) { try { if (f.getType() == Concept.Ref.class) { f.set(o, ((Concept) o).new Ref((Concept) value)); return; } if (o instanceof Concept.Ref) { f.set(o, ((Concept.Ref) o).get()); return; } } catch (Throwable _e) {} throw e; } } // i must point at the (possibly imaginary) opening bracket ("{") // index returned is index of closing bracket + 1 static int findEndOfBlock(List cnc, int i) { int j = i+2, level = 1; while (j < cnc.size()) { if (cnc.get(j).equals("{")) ++level; else if (cnc.get(j).equals("}")) --level; if (level == 0) return j+1; ++j; } return cnc.size(); } static String tb_mainServer_default = "http://tinybrain.de:8080"; static Object tb_mainServer_override; // func -> S static String tb_mainServer() { if (tb_mainServer_override != null) return (String) callF(tb_mainServer_override); return trim(loadTextFile(tb_mainServer_file(), tb_mainServer_default)); } static File tb_mainServer_file() { return getProgramFile("#1001638", "mainserver.txt"); } static boolean tb_mainServer_isDefault() { return eq(tb_mainServer(), tb_mainServer_default); } static void replaceLastElement(List l, A a) { if (nempty(l)) l.set(l(l)-1, a); } static String tok_packageName(List tok) { int i = jfind(tok, "package"); if (i < 0) return ""; i += 2; int j = jfind(tok, i, ";"); if (j < 0) return ""; return join(codeTokensOnly(subList(tok, i-1, j))); } static File makeTempDir() { return (File) call(getJavaX(), "TempDirMaker_make"); } static boolean isSingleQuoteIdentifier(String s) { if (l(s) < 2 || s.charAt(0) != '\'' || !Character.isJavaIdentifierStart(s.charAt(1))) return false; for (int i = 2; i < l(s); i++) if (!Character.isJavaIdentifierPart(s.charAt(i))) return false; return true; } static int stdcompare(Number a, Number b) { return cmp(a, b); } static int stdcompare(String a, String b) { return cmp(a, b); } static int stdcompare(long a, long b) { return a < b ? -1 : a > b ? 1 : 0; } static int stdcompare(Object a, Object b) { return cmp(a, b); } static List> innerClasses(List tok) { if (tok == null) return emptyList(); int i = 1; while (i < tok.size() && !tok.get(i).equals("{")) i += 2; return allClasses(tok.subList(i+1, tok.size())); } static boolean startsWith(String a, String b) { return a != null && a.startsWith(b); } static boolean startsWith(String a, String b, Matches m) { if (!startsWith(a, b)) return false; m.m = new String[] {substring(a, l(b))}; return true; } static boolean startsWith(List a, List b) { if (a == null || l(b) > l(a)) return false; for (int i = 0; i < l(b); i++) if (neq(a.get(i), b.get(i))) return false; return true; } static List beginCriticalAction_inFlight = synchroList(); static class CriticalAction { String description; CriticalAction() {} CriticalAction(String description) { this.description = description;} void done() { beginCriticalAction_inFlight.remove(this); } } static CriticalAction beginCriticalAction(String description) { ping(); CriticalAction c = new CriticalAction(description); beginCriticalAction_inFlight.add(c); return c; } static void cleanMeUp_beginCriticalAction() { int n = 0; while (nempty(beginCriticalAction_inFlight)) { int m = l(beginCriticalAction_inFlight); if (m != n) { n = m; try { print("Waiting for " + n(n, "critical actions") + ": " + join(", ", collect(beginCriticalAction_inFlight, "description"))); } catch (Throwable __e) { printStackTrace2(__e); } } sleepInCleanUp(10); } } static final HashMap> callMC_cache = new HashMap(); static String callMC_key; static Method callMC_value; // varargs assignment fixer for a single string array argument static Object callMC(String method, String[] arg) { return callMC(method, new Object[] {arg}); } static Object callMC(String method, Object... args) { try { Method me; synchronized(callMC_cache) { me = method == callMC_key ? callMC_value : null; } if (me != null) return callMC_value.invoke(null, args); List m; synchronized(callMC_cache) { m = callMC_cache.get(method); } if (m == null) { if (callMC_cache.isEmpty()) { callMC_makeCache(); m = callMC_cache.get(method); } if (m == null) throw fail("Method named " + method + " not found in main"); } int n = m.size(); if (n == 1) { me = m.get(0); synchronized(callMC_cache) { callMC_key = method; callMC_value = me; } return me.invoke(null, args); } for (int i = 0; i < n; i++) { me = m.get(i); if (call_checkArgs(me, args, false)) return me.invoke(null, args); } throw fail("No method called " + method + " with matching arguments found in main"); } catch (Exception __e) { throw rethrow(__e); } } static void callMC_makeCache() { synchronized(callMC_cache) { callMC_cache.clear(); Class _c = (Class) mc(), c = _c; while (c != null) { for (Method m : c.getDeclaredMethods()) if ((m.getModifiers() & Modifier.STATIC) != 0) { m.setAccessible(true); multiMapPut(callMC_cache, m.getName(), m); } c = c.getSuperclass(); } } } static String loadPage_utf8(URL url) { return loadPage_utf8(url.toString()); } static String loadPage_utf8(String url) { loadPage_charset.set("UTF-8"); try { return loadPage(url); } finally { loadPage_charset.set(null); } } // TODO: test if android complains about this static boolean isAWTThread() { if (isAndroid()) return false; if (isHeadless()) return false; return isAWTThread_awt(); } static boolean isAWTThread_awt() { return SwingUtilities.isEventDispatchThread(); } 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 Thread currentThread() { return Thread.currentThread(); } static Throwable printStackTrace(Throwable e) { // we go to system.out now - system.err is nonsense print(getStackTrace(e)); return e; } static void printStackTrace() { printStackTrace(new Throwable()); } static void printStackTrace(String msg) { printStackTrace(new Throwable(msg)); } /*static void printStackTrace(S indent, Throwable e) { if (endsWithLetter(indent)) indent += " "; printIndent(indent, getStackTrace(e)); }*/ static File loadBinarySnippet(String snippetID) { try { long id = parseSnippetID(snippetID); File f = DiskSnippetCache_getLibrary(id); if (fileSize(f) == 0) f = loadDataSnippetToFile(snippetID); return f; } catch (Exception __e) { throw rethrow(__e); } } static String hideCredentials(URL url) { return url == null ? null : hideCredentials(str(url)); } static String hideCredentials(String url) { return url.replaceAll("([&?])_pass=[^&]*", "$1_pass="); } static Class getMainClass() { return main.class; } static Class getMainClass(Object o) { try { return (o instanceof Class ? (Class) o : o.getClass()).getClassLoader().loadClass("main"); } catch (Exception __e) { throw rethrow(__e); } } static void setHeaders(URLConnection con) throws IOException { String computerID = getComputerID_quick(); if (computerID != null) try { con.setRequestProperty("X-ComputerID", computerID); con.setRequestProperty("X-OS", System.getProperty("os.name") + " " + System.getProperty("os.version")); } catch (Throwable e) { //printShortException(e); } } static String exceptionToStringShort(Throwable e) { e = getInnerException(e); String msg = unnull(e.getMessage()); if (msg.indexOf("Error") < 0 && msg.indexOf("Exception") < 0) return baseClassName(e) + ": " + msg; else return msg; } static String n(long l, String name) { return l + " " + trim(l == 1 ? singular(name) : getPlural(name)); } static String n(Collection l, String name) { return n(l(l), name); } static String n(Map m, String name) { return n(l(m), name); } static String n(Object[] a, String name) { return n(l(a), name); } static String n(MultiSet ms, String name) { return n(l(ms), name); } static void saveTranspiledCode(String progID, String code) { saveTextFile(new File(getCodeProgramDir(progID), "Transpilation"), code); } // 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()) { return null; } for (int i = 1; i < pat.size(); i += 2) { String p = pat.get(i), t = tok.get(i); 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 Object jsonDecode(final String text) { final List tok = jsonTok(text); class Y { 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("{")) return parseMap(); if (t.equals("[")) return parseList(); if (t.equals("null")) { i += 2; return null; } if (t.equals("false")) { i += 2; return false; } if (t.equals("true")) { i += 2; return true; } boolean minus = false; if (t.equals("-")) { minus = true; i += 2; t = get(tok, i); } if (isInteger(t)) { i += 2; if (eq(get(tok, i), ".")) { String x = t + "." + get(tok, i+2); i += 4; double d = parseDouble(x); if (minus) d = -d; return d; } else { long l = parseLong(t); if (minus) l = -l; return l != (int) l ? new Long(l) : new Integer((int) l); } } throw new RuntimeException("Unknown token " + (i+1) + ": " + t + ": " + text); } 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 parseMap() { consume("{"); Map map = new TreeMap(); while (!tok.get(i).equals("}")) { String key = unquote(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()))); throw fail(quote(s) + " expected: " + prevToken + " " + nextTokens + " (" + i + "/" + tok.size() + ")"); } i += 2; } } return new Y().parse(); } static File getCodeProgramDir() { return getCodeProgramDir(getProgramID()); } static File getCodeProgramDir(String snippetID) { return new File(javaxCodeDir(), formatSnippetID(snippetID)); } static File getCodeProgramDir(long snippetID) { return getCodeProgramDir(formatSnippetID(snippetID)); } static URLConnection openConnection(URL url) { try { ping(); callOpt(javax(), "recordOpenURLConnection", str(url)); return url.openConnection(); } catch (Exception __e) { throw rethrow(__e); } } static long psI(String snippetID) { return parseSnippetID(snippetID); } static File newFile(File base, String... names) { for (String name : names) base = new File(base, name); return base; } static File newFile(String name) { return name == null ? null : new File(name); } static Object callJavaX(String method, Object... args) { return callOpt(getJavaX(), method, args); } static String loadProgramTextFile(String name) { return loadTextFile(getProgramFile(name)); } static String loadProgramTextFile(String progID, String name) { return loadTextFile(getProgramFile(progID, name)); } static String loadProgramTextFile(String progID, String name, String defaultText) { return loadTextFile(getProgramFile(progID, name), defaultText); } static List emptyList() { return new ArrayList(); //ret Collections.emptyList(); } static FileOutputStream newFileOutputStream(File path) throws IOException { return newFileOutputStream(path.getPath()); } static FileOutputStream newFileOutputStream(String path) throws IOException { return newFileOutputStream(path, false); } static FileOutputStream newFileOutputStream(String path, boolean append) throws IOException { FileOutputStream f = new // Line break for ancient translator FileOutputStream(path, append); callJavaX("registerIO", f, path, true); return f; } static String shortClassName(Object o) { if (o == null) return null; Class c = o instanceof Class ? (Class) o : o.getClass(); String name = c.getName(); return shortenClassName(name); } static List getBracketMap2(List tok, Map map, String openingBrackets, String closingBrackets) { map.clear(); List stack = new ArrayList(); for (int i = 1; i < l(tok); i+= 2) { String t = tok.get(i); if (l(t) == 1) if (openingBrackets.contains(t)) stack.add(i); else if (closingBrackets.contains(tok.get(i))) map.put(empty(stack) ? 0 : liftLast(stack), i); } return stack; } static Object getOpt(Object o, String field) { return getOpt_cached(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 { if (c == null) return null; 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 void copyStream(InputStream in, OutputStream out) { try { byte[] buf = new byte[65536]; while (true) { int n = in.read(buf); if (n <= 0) return; out.write(buf, 0, n); } } catch (Exception __e) { throw rethrow(__e); } } static String struct_noStringSharing(Object o) { structure_Data d = new structure_Data(); d.noStringSharing = true; return structure(o, d); } static void setOpt_raw(Object o, String field, Object value) { if (o == null) return; if (o instanceof Class) setOpt_raw((Class) o, field, value); else try { Field f = setOpt_raw_findField(o.getClass(), field); if (f != null) smartSet(f, o, value); } catch (Exception e) { throw new RuntimeException(e); } } static void setOpt_raw(Class c, String field, Object value) { if (c == null) return; try { Field f = setOpt_raw_findStaticField(c, field); if (f != null) smartSet(f, null, value); } catch (Exception e) { throw new RuntimeException(e); } } static Field setOpt_raw_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 setOpt_raw_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; } static byte[] boolArrayToBytes(boolean[] a) { byte[] b = new byte[(l(a)+7)/8]; for (int i = 0; i < l(a); i++) if (a[i]) b[i/8] |= 1 << (i & 7); return b; } static String standardCredentialsUser() { return trim(loadTextFile(new File(userHome(), ".tinybrain/username"))); } public static void copyFile(File src, File dest) { try { mkdirsForFile(dest); FileInputStream inputStream = new FileInputStream(src.getPath()); FileOutputStream outputStream = newFileOutputStream(dest.getPath()); try { copyStream(inputStream, outputStream); inputStream.close(); } finally { outputStream.close(); } } catch (Exception __e) { throw rethrow(__e); } } static boolean _inCore() { return false; } static List parse3(String s) { return dropPunctuation(javaTokPlusPeriod(s)); } static File javaxCachesDir_dir; // can be set to work on different base dir static File javaxCachesDir() { return javaxCachesDir_dir != null ? javaxCachesDir_dir : new File(userHome(), "JavaX-Caches"); } static String parse3_cached_s; static List parse3_cached_l; static synchronized List parse3_cached(String s) { if (neq(s, parse3_cached_s)) parse3_cached_l = parse3(parse3_cached_s = s); return parse3_cached_l; } static boolean endsWithLetter(String s) { return nempty(s) && isLetter(last(s)); } static File getProgramDir() { return programDir(); } static File getProgramDir(String snippetID) { return programDir(snippetID); } static final boolean loadPageThroughProxy_enabled = false; static String loadPageThroughProxy(String url) { return null; } static void hotwire_copyOver(Class c) { synchronized(StringBuffer.class) { for (String field : litlist("print_log", "print_silent", "androidContext")) { Object o = getOpt(mc(), field); if (o != null) setOpt(c, field, o); } Object mainBot = getMainBot(); if (mainBot != null) setOpt(c, "mainBot", mainBot); setOpt(c, "creator_class", new WeakReference(mc())); } } static void sortFilesByName(List l) { sort(l, new Comparator() { public int compare(File a, File b) { return stdcompare(a.getName(), b.getName()); } }); } static List getJavaModifiers_list = litlist("static", "abstract", "public", "private", "protected", "final", "native", "volatile", "synchronized", "transient"); static List getJavaModifiers() { return getJavaModifiers_list; } static Class mc() { return main.class; } static String makePostData(Map map) { List l = new ArrayList(); for (Map.Entry e : map.entrySet()) { String key = (String) ( e.getKey()); Object val = e.getValue(); if (val != null) { String value = str(val); //structureOrText(val); l.add(urlencode(key) + "=" + urlencode(escapeMultichars(value))); } } return join("&", l); } static String makePostData(Object... params) { return makePostData(litorderedmap(params)); } static Map synchroMap() { return synchroHashMap(); } static Map synchroMap(Map map) { return Collections.synchronizedMap(map); } static void sleepSeconds(double s) { if (s > 0) sleep(round(s*1000)); } static Set keys(Map map) { return map.keySet(); } static Set keys(Object map) { return keys((Map) map); } static Set keys(MultiSet ms) { return ms.keySet(); } static void assertFalse(Object o) { assertEquals(false, o); } static boolean assertFalse(boolean b) { if (b) throw fail("oops"); return b; } static boolean assertFalse(String msg, boolean b) { if (b) throw fail(msg); return b; } // returns l(s) if not found static int smartIndexOf(String s, String sub, int i) { if (s == null) return 0; i = s.indexOf(sub, min(i, l(s))); return i >= 0 ? i : l(s); } static int smartIndexOf(String s, char c, int i) { if (s == null) return 0; i = s.indexOf(c, min(i, l(s))); return i >= 0 ? i : l(s); } static int smartIndexOf(String s, String sub) { return smartIndexOf(s, sub, 0); } static int smartIndexOf(String s, char c) { return smartIndexOf(s, c, 0); } static int smartIndexOf(List l, A sub) { return smartIndexOf(l, sub, 0); } static int smartIndexOf(List l, A sub, int start) { int i = indexOf(l, sub, start); return i < 0 ? l(l) : i; } static final WeakHashMap> getOpt_cache = new WeakHashMap(); static final HashMap getOpt_special = new HashMap(); // just a marker static { getOpt_cache.put(Class.class, getOpt_special); getOpt_cache.put(String.class, getOpt_special); } static Object getOpt_cached(Object o, String field) { try { if (o == null) return null; Class c = o.getClass(); HashMap map; synchronized(getOpt_cache) { map = getOpt_cache.get(c); if (map == null) map = getOpt_makeCache(c); } if (map == getOpt_special) { if (o instanceof Class) return getOpt((Class) o, field); /*if (o instanceof S) ret getOpt(getBot((S) o), field);*/ if (o instanceof Map) return ((Map) o).get(field); } Field f = map.get(field); if (f != null) return f.get(o); if (o instanceof DynamicObject) return ((DynamicObject) o).fieldValues.get(field); return null; } catch (Exception __e) { throw rethrow(__e); } } // used internally - we are in synchronized block static HashMap getOpt_makeCache(Class c) { HashMap map; if (isSubtypeOf(c, Map.class)) map = getOpt_special; else { map = new HashMap(); Class _c = c; do { for (Field f : _c.getDeclaredFields()) { f.setAccessible(true); String name = f.getName(); if (!map.containsKey(name)) map.put(name, f); } _c = _c.getSuperclass(); } while (_c != null); } getOpt_cache.put(c, map); return map; } static File[] listFiles(File dir) { File[] files = dir.listFiles(); return files == null ? new File[0] : files; } static File[] listFiles(String dir) { return listFiles(new File(dir)); } static A or(A a, A b) { return a != null ? a : b; } static String standardCredentialsPass() { return trim(loadTextFile(new File(userHome(), ".tinybrain/userpass"))); } static File userDir() { return new File(userHome()); } static File userDir(String path) { return new File(userHome(), path); } static int isAndroid_flag; static boolean isAndroid() { if (isAndroid_flag == 0) isAndroid_flag = System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0 ? 1 : -1; return isAndroid_flag > 0; } static boolean equalsIgnoreCase(String a, String b) { return a == null ? b == null : a.equalsIgnoreCase(b); } static boolean equalsIgnoreCase(char a, char b) { if (a == b) return true; char u1 = Character.toUpperCase(a); char u2 = Character.toUpperCase(b); if (u1 == u2) return true; return Character.toLowerCase(u1) == Character.toLowerCase(u2); } static List getPlural_specials = ll("sheep", "fish"); static String getPlural(String s) { if (containsIgnoreCase(getPlural_specials, s)) return s; if (ewic(s, "y")) return dropSuffixIgnoreCase("y", s) + "ies"; if (ewic(s, "ss")) return s + "es"; if (ewic(s, "s")) return s; return s + "s"; } static Map singular_specials = litmap( "children", "child", "images", "image", "chess", "chess"); static Set singular_specials2 = litset("time", "machine"); static String singular(String s) { if (s == null) return null; { String _a_1 = singular_specials.get(s); if (!empty(_a_1)) return _a_1; } if (singular_specials2.contains(dropSuffix("s", s))) return dropSuffix("s", s); if (s.endsWith("ness")) return s; if (s.endsWith("ges")) return dropSuffix("s", s); s = dropSuffix("es", s); s = dropSuffix("s", s); return s; } static Object mainBot; static Object getMainBot() { return mainBot; } static volatile boolean sleep_noSleep; static void sleep(long ms) { ping(); if (ms < 0) return; // allow spin locks if (isAWTThread() && ms > 100) throw fail("Should not sleep on AWT thread"); try { Thread.sleep(ms); } catch (Exception e) { throw new RuntimeException(e); } } static void sleep() { try { if (sleep_noSleep) throw fail("nosleep"); print("Sleeping."); sleepQuietly(); } catch (Exception __e) { throw rethrow(__e); } } static String escapeMultichars(String s) { StringBuilder buf = new StringBuilder(); for (int i = 0; i < l(s); i++) if (isExtendedUnicodeCharacter(s, i)) { buf.append("[xchar " + intToHex(s.codePointAt(i)) + "]"); ++i; } else buf.append(s.charAt(i)); return str(buf); } static Throwable getInnerException(Throwable e) { while (e.getCause() != null) e = e.getCause(); return e; } static Boolean isHeadless_cache; static boolean isHeadless() { if (isHeadless_cache != null) return isHeadless_cache; if (GraphicsEnvironment.isHeadless()) return isHeadless_cache = true; // Also check if AWT actually works. // If DISPLAY variable is set but no X server up, this will notice. try { SwingUtilities.isEventDispatchThread(); return isHeadless_cache = false; } catch (Throwable e) { return isHeadless_cache = true; } } static String getStackTrace(Throwable throwable) { StringWriter writer = new StringWriter(); throwable.printStackTrace(new PrintWriter(writer)); return writer.toString(); } static Class javax() { return getJavaX(); } static File programDir_mine; // set this to relocate program's data static File programDir() { return programDir(getProgramID()); } static File programDir(String snippetID) { if (programDir_mine != null && sameSnippetID(snippetID, programID())) return programDir_mine; File dir = new File(javaxDataDir(), formatSnippetID(snippetID)); String c = caseID(); if (nempty(c)) dir = newFile(dir, c); return dir; } static List jsonTok(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 // scan for non-whitespace (json strings, "null" identifier, numbers. everything else automatically becomes a one character token.) 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.isLetter(c)) do ++j; while (j < l && Character.isLetter(s.charAt(j))); else if (Character.isDigit(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 File loadDataSnippetToFile(String snippetID) { try { File f = DiskSnippetCache_file(parseSnippetID(snippetID)); try { URL url = new URL(dataSnippetLink(snippetID)); System.err.println("Loading library: " + hideCredentials(url)); try { loadBinaryPageToFile(openConnection(url), f); if (fileSize(f) == 0) throw fail(); } catch (Throwable _e) { url = new URL("http://data.tinybrain.de/blobs/" + parseSnippetID(snippetID)); loadBinaryPageToFile(openConnection(url), f); } // TODO: check if we hit the "LOADING" message if (fileSize(f) == 0) throw fail(); System.err.println("Bytes loaded: " + fileSize(f)); } catch (Throwable e) { printStackTrace(e); throw fail("Binary snippet #" + snippetID + " not found or not public"); } return f; } catch (Exception __e) { throw rethrow(__e); } } static List synchroList() { return Collections.synchronizedList(new ArrayList()); } static List synchroList(List l) { return Collections.synchronizedList(l); } static Map synchroHashMap() { return Collections.synchronizedMap(new HashMap()); } static File DiskSnippetCache_file(long snippetID) { return new File(getGlobalCache(), "data_" + snippetID + ".jar"); } // Data files are immutable, use centralized cache public static File DiskSnippetCache_getLibrary(long snippetID) throws IOException { File file = DiskSnippetCache_file(snippetID); return file.exists() ? file : null; } public static void DiskSnippetCache_putLibrary(long snippetID, byte[] data) throws IOException { saveBinaryFile(DiskSnippetCache_file(snippetID), 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 (RuntimeException 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; } static String getComputerID_quick() { return computerID(); } static File javaxCodeDir_dir; // can be set to work on different base dir static File javaxCodeDir() { return javaxCodeDir_dir != null ? javaxCodeDir_dir : new File(userHome(), "JavaX-Code"); } static void sleepInCleanUp(long ms) { try { if (ms < 0) return; Thread.sleep(ms); } catch (Exception __e) { throw rethrow(__e); } } static String shortenClassName(String name) { if (name == null) return null; int i = lastIndexOf(name, "$"); if (i < 0) i = lastIndexOf(name, "."); return i < 0 ? name : substring(name, i+1); } static boolean isSubtypeOf(Class a, Class b) { return b.isAssignableFrom(a); // << always hated that method, let's replace it! } static String baseClassName(String className) { return substring(className, className.lastIndexOf('.')+1); } static String baseClassName(Object o) { return baseClassName(getClassName(o)); } static LinkedHashMap litorderedmap(Object... x) { LinkedHashMap map = new LinkedHashMap(); litmap_impl(map, x); return map; } static void multiMapPut(Map> map, A a, B b) { List l = map.get(a); if (l == null) map.put(a, l = new ArrayList()); l.add(b); } static void sort(T[] a, Comparator c) { Arrays.sort(a, c); } static void sort(T[] a) { Arrays.sort(a); } static void sort(List a, Comparator c) { Collections.sort(a, c); } static void sort(List a) { Collections.sort(a); } static long fileSize(String path) { return getFileSize(path); } static long fileSize(File f) { return getFileSize(f); } static List collect(Collection c, String field) { return collectField(c, field); } static List collect(String field, Collection c) { return collectField(c, field); } static List collect(Class c, String field) { return collect(list(c), field); } static boolean isLetter(char c) { return Character.isLetter(c); } static double parseDouble(String s) { return Double.parseDouble(s); } 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 long round(double d) { return Math.round(d); } static HashSet litset(A... items) { return lithashset(items); } static String caseID_caseID; static String caseID() { return caseID_caseID; } static String dropSuffixIgnoreCase(String suffix, String s) { return ewic(s, suffix) ? s.substring(0, l(s)-l(suffix)) : s; } static List collectField(Collection c, String field) { List l = new ArrayList(); for (Object a : c) l.add(getOpt(a, field)); return l; } static String _computerID; public static String computerID() { try { if (_computerID == null) { File file = new File(userHome(), ".tinybrain/computer-id"); _computerID = loadTextFile(file.getPath(), null); if (_computerID == null) { _computerID = makeRandomID(12); saveTextFile(file.getPath(), _computerID); } } return _computerID; } catch (Exception __e) { throw rethrow(__e); } } static ArrayList list(A[] a) { return asList(a); } static ArrayList list(int[] a) { return asList(a); } static ArrayList list(Set s) { return asList(s); } static boolean isExtendedUnicodeCharacter(String s, int idx) { return Character.charCount(s.codePointAt(idx)) > 1; } /** 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 = newFileOutputStream(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); } } 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 boolean containsIgnoreCase(String s, char c) { return indexOfIgnoreCase(s, String.valueOf(c)) >= 0; } static boolean containsIgnoreCase(String a, String b) { return indexOfIgnoreCase(a, b) >= 0; } static List nlTok(String s) { return javaTokPlusPeriod(s); } static String dataSnippetLink(String snippetID) { long id = parseSnippetID(snippetID); if (id >= 1200000 && id < 1300000) { String pw = muricaPassword(); if (empty(pw)) throw fail("Please set 'murica password by running #1008829"); return "http://ai1.lol/1008823/raw/" + id + "?_pass=" + pw; // XXX, although it typically gets hidden when printing } else return "http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_" + id + "&contentType=application/binary"; } static String intToHex(int i) { return bytesToHex(intToBytes(i)); } static long getFileSize(String path) { return path == null ? 0 : new File(path).length(); } static long getFileSize(File f) { return f == null ? 0 : f.length(); } static boolean ewic(String a, String b) { return endsWithIgnoreCase(a, b); } static boolean sameSnippetID(String a, String b) { return a != null && b != null && parseSnippetID(a) == parseSnippetID(b); } static void loadBinaryPageToFile(String url, File file) { try { print("Loading " + url); loadBinaryPageToFile(openConnection(new URL(url)), file); } catch (Exception __e) { throw rethrow(__e); } } static void loadBinaryPageToFile(URLConnection con, File file) { try { setHeaders(con); loadBinaryPageToFile_noHeaders(con, file); } catch (Exception __e) { throw rethrow(__e); } } static void loadBinaryPageToFile_noHeaders(URLConnection con, File file) { try { File ftemp = new File(f2s(file) + "_temp"); FileOutputStream buf = newFileOutputStream(mkdirsFor(ftemp)); InputStream inputStream = con.getInputStream(); try { long len = 0; try { len = con.getContentLengthLong(); } catch (Throwable e) { printStackTrace(e); } int n = 0; while (true) { int ch = inputStream.read(); if (ch < 0) break; buf.write(ch); if (++n % 100000 == 0) println(" " + n + (len != 0 ? "/" + len : "") + " bytes loaded."); } buf.close(); buf = null; file.delete(); ftemp.renameTo(file); inputStream.close(); } finally { if (buf != null) buf.close(); } } catch (Exception __e) { throw rethrow(__e); } } static File javaxDataDir_dir; // can be set to work on different base dir static File javaxDataDir() { return javaxDataDir_dir != null ? javaxDataDir_dir : new File(userHome(), "JavaX-Data"); } static Object sleepQuietly_monitor = new Object(); static void sleepQuietly() { try { assertFalse(isAWTThread()); synchronized(sleepQuietly_monitor) { sleepQuietly_monitor.wait(); } } catch (Exception __e) { throw rethrow(__e); } } static byte[] loadBinaryPage(String url) { try { print("Loading " + url); return loadBinaryPage(new URL(url).openConnection()); } catch (Exception __e) { throw rethrow(__e); } } static byte[] loadBinaryPage(URLConnection con) { try { setHeaders(con); return loadBinaryPage_noHeaders(con); } catch (Exception __e) { throw rethrow(__e); } } static byte[] loadBinaryPage_noHeaders(URLConnection con) { try { ByteArrayOutputStream buf = new ByteArrayOutputStream(); InputStream inputStream = con.getInputStream(); long len = 0; try { len = con.getContentLengthLong(); } catch (Throwable e) { printStackTrace(e); } int n = 0; while (true) { int ch = inputStream.read(); if (ch < 0) break; buf.write(ch); if (++n % 100000 == 0) println(" " + n + (len != 0 ? "/" + len : "") + " bytes loaded."); } inputStream.close(); return buf.toByteArray(); } catch (Exception __e) { throw rethrow(__e); } } static String muricaPassword() { return trim(loadTextFile(muricaPasswordFile())); } static String makeRandomID(int length) { Random random = new Random(); char[] id = new char[length]; for (int i = 0; i < id.length; i++) id[i] = (char) ((int) 'a' + random.nextInt(26)); return new String(id); } static boolean endsWithIgnoreCase(String a, String b) { return a != null && l(a) >= l(b) && a.regionMatches(true, l(a)-l(b), b, 0, l(b)); } static String f2s(File f) { return f == null ? null : f.getAbsolutePath(); } // 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 byte[] intToBytes(int i) { return new byte[] { (byte) (i >>> 24), (byte) (i >>> 16), (byte) (i >>> 8), (byte) i}; } static A println(A a) { return print(a); } public static File mkdirsFor(File file) { return mkdirsForFile(file); } static File muricaPasswordFile() { return new File(javaxSecretDir(), "murica/muricaPasswordFile"); } static File javaxSecretDir_dir; // can be set to work on different base dir static File javaxSecretDir() { return javaxSecretDir_dir != null ? javaxSecretDir_dir : new File(userHome(), "JavaX-Secret"); } static abstract class F1 { abstract B get(A a); } static class Pair { A a; B b; Pair() {} Pair(A a, B b) { this.b = b; this.a = a;} public int hashCode() { return hashCodeFor(a) + 2*hashCodeFor(b); } public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Pair)) return false; Pair t = (Pair) o; return eq(a, t.a) && eq(b, t.b); } public String toString() { return "<" + a + ", " + b + ">"; } } static class Var implements IVar { A v; // you can access this directly if you use one thread Var() {} Var(A v) { this.v = v;} public synchronized void set(A a) { if (v != a) { v = a; notifyAll(); } } public synchronized A get() { return v; } public synchronized boolean has() { return v != null; } public synchronized void clear() { v = null; } } // A concept should be an object, not just a string. // Functions that should always be there for child processes: static int concepts_internStringsLongerThan = 10; static ThreadLocal concepts_unlisted = new ThreadLocal(); static interface Derefable { Concept get(); } static interface IConceptIndex { void update(Concept c); // also for adding void remove(Concept c); } static interface IFieldIndex { List getAll(Val val); } static class Concepts { Map concepts = synchroTreeMap(); HashMap perClassData = new HashMap(); String programID; // set to "-" for non-persistent (possibly not implemented) long idCounter; volatile long changes = 1, changesWritten; volatile java.util.Timer autoSaver; volatile boolean savingConcepts, dontSave; int autoSaveInterval = -1000; // 1 second + wait logic boolean useGZIP = true, quietSave; ReentrantLock lock = new ReentrantLock(true); ReentrantLock saverLock = new ReentrantLock(true); long lastSaveTook, lastSaveWas; float maxAutoSavePercentage = 10; List conceptIndices; Map, Map> fieldIndices; Concepts() {} Concepts(String programID) { this.programID = programID;} synchronized long internalID() { do { ++idCounter; } while (hasConcept(idCounter)); return idCounter; } void initProgramID() { if (programID == null) programID = getDBProgramID(); } // Now tries to load from bot first, then go to disk. Concepts load() { return load(false); } Concepts safeLoad() { return load(true); } Concepts load(boolean allDynamic) { initProgramID(); if (tryToGrab(allDynamic)) return this; return loadFromDisk(allDynamic); } Concepts loadFromDisk() { return loadFromDisk(false); } Concepts loadFromDisk(boolean allDynamic) { clearConcepts(); DynamicObject_loading.set(true); try { long time = now(); Map _concepts = concepts; // empty map readLocally2_allDynamic.set(allDynamic); readLocally2(this, programID, "concepts"); Map __concepts = concepts; concepts = _concepts; concepts.putAll(__concepts); int l = readLocally_stringLength; int tokrefs = unstructure_tokrefs; assignConceptsToUs(); done("Loaded " + n(l(concepts), "concepts"), time); if (fileSize(getProgramFile(programID, "idCounter.structure")) != 0) readLocally2(this, programID, "idCounter"); else calcIdCounter(); } finally { DynamicObject_loading.set(null); } allChanged(); return this; } Concepts loadConcepts() { return load(); } boolean tryToGrab(boolean allDynamic) { if (sameSnippetID(programID, getDBProgramID())) return false; RemoteDB db = connectToDBOpt(programID); try { if (db != null) { loadGrab(db.fullgrab(), allDynamic); return true; } } finally { if (db != null) db.close(); } return false; } Concepts load(String grab) { return loadGrab(grab, false); } Concepts safeLoad(String grab) { return loadGrab(grab, true); } Concepts loadGrab(String grab, boolean allDynamic) { clearConcepts(); DynamicObject_loading.set(true); try { Map map = (Map) (allDynamic ? safeUnstructure(grab) : unstructure(grab)); concepts.putAll(map); assignConceptsToUs(); for (long l : map.keySet()) idCounter = max(idCounter, l); } finally { DynamicObject_loading.set(null); } allChanged(); return this; } void assignConceptsToUs() { for (Concept c : values(concepts)) { c._concepts = this; callOpt_noArgs(c, "_doneLoading2"); } } String progID() { return programID == null ? getDBProgramID() : programID; } Concept getConcept(String id) { return empty(id) ? null : getConcept(parseLong(id)); } Concept getConcept(long id) { return (Concept) concepts.get((long) id); } Concept getConcept(RC ref) { return ref == null ? null : getConcept(ref.longID()); } boolean hasConcept(long id) { return concepts.containsKey((long) id); } void deleteConcept(long id) { Concept c = getConcept(id); if (c == null) print("Concept " + id + " not found"); else c.delete(); } void calcIdCounter() { long id_ = 0; for (long id : keys(concepts)) id_ = max(id_, id); idCounter = id_+1; saveLocally2(this, programID, "idCounter"); } void saveConceptsIfDirty() { saveConcepts(); } void save() { saveConcepts(); } void saveConcepts() { if (dontSave) return; initProgramID(); saverLock.lock(); savingConcepts = true; long start = now(), time; try { String s = null; //synchronized(main.class) { long _changes = changes; if (_changes == changesWritten) return; File f = getProgramFile(programID, useGZIP ? "concepts.structure.gz" : "concepts.structure"); lock.lock(); long fullTime = now(); try { saveLocally2(this, programID, "idCounter"); if (useGZIP) { saveGZStructureToFile(f, cloneMap(concepts)); getProgramFile(programID, "concepts.structure").delete(); } else s = structure(cloneMap(concepts)); } finally { lock.unlock(); } changesWritten = _changes; // only update when structure didn't fail if (!useGZIP) { time = now()-start; if (!quietSave) print("Saving " + toM(l(s)) + "M chars (" /*+ changesWritten + ", "*/ + time + " ms)"); start = now(); saveTextFile(f, javaTokWordWrap(s)); getProgramFile(programID, "concepts.structure.gz").delete(); } copyFile(f, getProgramFile(programID, "concepts.structure" + (useGZIP ? ".gz" : "") + ".backup" + ymd() + "-" + formatInt(hours(), 2))); time = now()-start; if (!quietSave) print(programID + ": Saved " + toK(f.length()) + " K, " + n(concepts, "concepts") + " (" + time + " ms)"); lastSaveWas = fullTime; lastSaveTook = now()-fullTime; } finally { savingConcepts = false; saverLock.unlock(); } } void _autoSaveConcepts() { if (autoSaveInterval < 0 && maxAutoSavePercentage != 0) { long pivotTime = Math.round(lastSaveWas+lastSaveTook*100.0/maxAutoSavePercentage); if (now() < pivotTime) { //print("Skipping auto-save (last save took " + lastSaveTook + ")"); return; } } saveConcepts(); } void clearConcepts() { concepts.clear(); allChanged(); } synchronized void allChanged() { ++changes; } // auto-save every second if dirty synchronized void autoSaveConcepts() { if (autoSaver == null) { if (isTransient()) throw fail("Can't persist transient database"); autoSaver = doEvery_daemon(abs(autoSaveInterval), new Runnable() { public void run() { try { _autoSaveConcepts() ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "_autoSaveConcepts()"; }}); // print("Installed auto-saver (" + autoSaveInterval + " ms, " + progID() + ")"); } } void cleanMeUp() { if (autoSaver != null) { autoSaver.cancel(); autoSaver = null; } while (savingConcepts) sleepInCleanUp(10); saveConceptsIfDirty(); } Map getIDsAndNames() { Map map = new HashMap(); Map cloned = cloneMap(concepts); for (long id : keys(cloned)) map.put(id, cloned.get(id).className); return map; } void deleteConcepts(List l) { for (Object o : l) if (o instanceof Long) concepts.remove((Long) o); else if (o instanceof Concept) ((Concept) o).delete(); else warn("Can't delete " + getClassName(o)); } A conceptOfType(Class type) { return firstOfType(allConcepts(), type); } List conceptsOfType(Class type) { return filterByType(allConcepts(), type); } List listConcepts(Class type) { return conceptsOfType(type); } List list(Class type) { return conceptsOfType(type); } List list(String type) { return conceptsOfType(type); } List conceptsOfType(String type) { return filterByDynamicType(allConcepts(), "main$" + type); } boolean hasConceptOfType(Class type) { return hasType(allConcepts(), type); } void persistConcepts() { loadConcepts(); autoSaveConcepts(); } // We love synonyms void conceptPersistence() { persistConcepts(); } Concepts persist() { persistConcepts(); return this; } void persist(int interval) { autoSaveInterval = interval; persist(); } // Runs r if there is no concept of that type A ensureHas(Class c, Runnable r) { A a = conceptOfType(c); if (a == null) { r.run(); a = conceptOfType(c); if (a == null) throw fail("Concept not made by " + r + ": " + shortClassName(c)); } return a; } // Ensures that every concept of type c1 is ref'd by a concept of // type c2. // Type of func: voidfunc(concept) void ensureHas(Class c1, Class c2, Object func) { for (Concept a : conceptsOfType(c1)) { Concept b = findBackRef(a, c2); if (b == null) { callF(func, a); b = findBackRef(a, c2); if (b == null) throw fail("Concept not made by " + func + ": " + shortClassName(c2)); } } } // Type of func: voidfunc(concept) void forEvery(Class type, Object func) { for (Concept c : conceptsOfType(type)) callF(func, c); } int deleteAll(Class type) { List l = (List) conceptsOfType(type); for (Concept c : l) c.delete(); return l(l); } Collection allConcepts() { synchronized(concepts) { return new ArrayList(values(concepts)); } } int countConcepts(Class c, Object... params) { int n = 0; for (A x : list(c)) if (checkConceptFields(x, params)) ++n; return n; } int countConcepts(String c, Object... params) { int n = 0; for (Concept x : list(c)) if (checkConceptFields(x, params)) ++n; return n; } int countConcepts() { return l(concepts); } synchronized void addConceptIndex(IConceptIndex index) { if (conceptIndices == null) conceptIndices = new ArrayList(); conceptIndices.add(index); } synchronized void addFieldIndex(Class c, String field, IFieldIndex index) { if (fieldIndices == null) fieldIndices = new HashMap(); Map map = fieldIndices.get(c); if (map == null) fieldIndices.put(c, map = new HashMap()); map.put(field, index); } synchronized IFieldIndex getFieldIndex(Class c, String field) { if (fieldIndices == null) return null; Map map = fieldIndices.get(c); return map == null ? null : map.get(field); } // inter-process methods RC xnew(String name, Object... values) { return new RC(cnew(name, values)); } void xset(long id, String field, Object value) { xset(new RC(id), field, value); } void xset(RC c, String field, Object value) { if (value instanceof RC) value = getConcept((RC) value); cset(getConcept(c), field, value); } Object xget(long id, String field) { return xget(new RC(id), field); } Object xget(RC c, String field) { return xgetPost(cget(getConcept(c), field)); } Object xgetPost(Object o) { o = deref(o); if (o instanceof Concept) return new RC((Concept) o); return o; } void xdelete(long id) { xdelete(new RC(id)); } void xdelete(RC c) { getConcept(c).delete(); } void xdelete(List l) { for (RC c : l) xdelete(c); } List xlist() { return map("toPassRef", allConcepts()); } List xlist(String className) { return map("toPassRef", conceptsOfType(className)); } boolean isTransient() { return eq(programID, "-"); } String xfullgrab() { lock.lock(); try { if (changes == changesWritten && !isTransient()) return loadConceptsStructure(programID); return structure(cloneMap(concepts)); } finally { lock.unlock(); } } void xshutdown() { // Killing whole VM if someone wants this DB to shut down cleanKillVM(); } long xchangeCount() { return changes; } int xcount() { return countConcepts(); } void register(Concept c) { if (c._concepts == this) return; if (c._concepts != null) throw fail("Can't re-register"); c._concepts = this; c.id = internalID(); c.created = now(); concepts.put((long) c.id, c); c.change(); } } // class Concepts static volatile Concepts mainConcepts = new Concepts(); // Where we create new concepts static class Concept extends DynamicObject { transient Concepts _concepts; // Where we belong long id; //O madeBy; //double energy; //bool defunct; long created; // used only internally (cnew) Concept(String className) { super(className); _created(); } Concept() { if (!_loading()) { //className = shortClassName(this); // XXX - necessary? //print("New concept of type " + className); _created(); } } List refs; List backRefs; static boolean loading() { return _loading(); } static boolean _loading() { return isTrue(DynamicObject_loading.get()); } void _created() { if (!isTrue(concepts_unlisted.get())) mainConcepts.register(this); } /*void put(S field, O value) { fieldValues.put(field, value); change(); } O get(S field) { ret fieldValues.get(field); }*/ class Ref { A value; Ref() { if (!isTrue(DynamicObject_loading.get())) refs = addDyn(refs, this); } Ref(A value) { this.value = value; refs = addDyn(refs, this); index(); } // get owning concept (source) Concept concept() { return Concept.this; } // get target A get() { return value; } boolean has() { return value != null; } void set(A a) { if (a == value) return; unindex(); value = a; index(); } void set(Ref ref) { set(ref.get()); } void clear() { set((A) null); } void index() { if (value != null) value.backRefs = addDyn(value.backRefs, this); change(); } void unindex() { if (value != null) value.backRefs = removeDyn(value.backRefs, this); } void change() { Concept.this.change(); } } class RefL extends AbstractList { List < Ref < A > > l = new ArrayList(); public A set(int i, A o) { A prev = l.get(i).get(); l.get(i).set(o); return prev; } public void add(int i, A o) { l.add(i, new Ref(o)); } public A get(int i) { return l.get(i).get(); } public A remove(int i) { return l.remove(i).get(); } public int size() { return l.size(); } public boolean contains(Object o) { if (o instanceof Concept) for (Ref r : l) if (eq(r.get(), o)) return true; return super.contains(o); } } void delete() { //name = "[defunct " + name + "]"; //defunct = true; //energy = 0; for (Ref r : unnull(refs)) r.unindex(); refs = null; for (Ref r : cloneList(backRefs)) r.set((Concept) null); backRefs = null; if (_concepts != null) { _concepts.concepts.remove((long) id); _concepts.allChanged(); if (_concepts.conceptIndices != null) for (IConceptIndex index : _concepts.conceptIndices) index.remove(this); _concepts = null; } id = 0; } BaseXRef export() { return new BaseXRef(_concepts.progID(), id); } // notice system of a change in this object void change() { if (_concepts != null) { _concepts.allChanged(); if (_concepts.conceptIndices != null) for (IConceptIndex index : _concepts.conceptIndices) index.update(this); } } String _programID() { return _concepts == null ? getDBProgramID() : _concepts.progID(); } } // class Concept // remote reference (for inter-process communication or // external databases). Formerly "PassRef". // prepared for string ids if we do them later static class RC { transient Object owner; String id; RC() {} // make serialisation happy RC(long id) { this.id = str(id); } RC(Object owner, long id) { this.id = str(id); this.owner = owner; } RC(Concept c) { this(c.id); } long longID() { return parseLong(id); } public String toString() { return id; } transient RemoteDB db; String getString(String field) { return db.xS(this, field); } Object get(String field) { return db.xget(this, field); } void set(String field, Object value) { db.xset(this, field, value); } } // Reference to a concept in another program static class BaseXRef { String programID; long id; BaseXRef() {} BaseXRef(String programID, long id) { this.id = id; this.programID = programID;} public boolean equals(Object o) { if (!(o instanceof BaseXRef)) return false; BaseXRef r = (BaseXRef) ( o); return eq(programID, r.programID) && eq(id, r.id); } public int hashCode() { return programID.hashCode() + (int) id; } } // BaseXRef as a concept static class XRef extends Concept { BaseXRef ref; XRef() {} XRef(BaseXRef ref) { this.ref = ref; _doneLoading2(); } // after we have been added to concepts void _doneLoading2() { getIndex().put(ref, this); } HashMap getIndex() { return getXRefIndex(_concepts); } } static synchronized HashMap getXRefIndex(Concepts concepts) { HashMap cache = (HashMap) concepts.perClassData.get(XRef.class); if (cache == null) concepts.perClassData.put(XRef.class, cache = new HashMap()); return cache; } // uses mainConcepts static XRef lookupOrCreateXRef(BaseXRef ref) { XRef xref = getXRefIndex(mainConcepts).get(ref); if (xref == null) xref = new XRef(ref); return xref; } // define standard concept functions to use main concepts static List list(Class type) { return mainConcepts.list(type); } static List list(Concepts concepts, Class type) { return concepts.list(type); } static List list(String type) { return mainConcepts.list(type); } static List list(Concepts concepts, String type) { return concepts.list(type); } static int csetAll(Concept c, Object... values) { return cset(c, values); } static void cleanMeUp_concepts() { mainConcepts.cleanMeUp(); } static void loadAndAutoSaveConcepts() { mainConcepts.persist(); } static void loadAndAutoSaveConcepts(int interval) { mainConcepts.persist(interval); } static void loadConceptsFrom(String progID) { mainConcepts.programID = progID; mainConcepts.load(); } static List conceptsOfType(String type) { return mainConcepts.conceptsOfType(type); } static long changeCount() { return mainConcepts.changes; } static List exposedDBMethods = ll("xlist", "xnew", "xset", "xdelete", "xget", "xclass", "xfullgrab", "xshutdown", "xchangeCount", "xcount"); static RC toPassRef(Concept c) { return new RC(c); } // so we can instantiate the program to run as a bare DB bot static Throwable printStackTrace2(Throwable e) { // we go to system.out now - system.err is nonsense print(getStackTrace2(e)); return e; } static void printStackTrace2() { printStackTrace2(new Throwable()); } static void printStackTrace2(String msg) { printStackTrace2(new Throwable(msg)); } /*static void printStackTrace2(S indent, Throwable e) { if (endsWithLetter(indent)) indent += " "; printIndent(indent, getStackTrace2(e)); }*/ static int hashCodeFor(Object a) { return a == null ? 0 : a.hashCode(); } static boolean equals(Object a, Object b) { return a == null ? b == null : a.equals(b); } static RuntimeException rethrow(Throwable e) { throw asRuntimeException(e); } static String getStackTrace2(Throwable throwable) { return getStackTrace(throwable) + replacePrefix("java.lang.RuntimeException: ", "FAIL: ", str(getInnerException(throwable))) + "\n"; } static String replacePrefix(String prefix, String replacement, String s) { if (!startsWith(s, prefix)) return s; return replacement + substring(s, l(prefix)); } static interface IVar { void set(A a); A get(); boolean has(); void clear(); } static class RemoteDB { DialogIO db; String name; // s = bot name or snippet ID RemoteDB(String s) { this(s, false); } RemoteDB(String s, boolean autoStart) { name = s; if (isSnippetID(s)) name = dbBotName(s); db = findBot(name); if (db == null) if (autoStart) { nohupJavax(fsI(s)); waitForBotStartUp(name); assertNotNull("Weird problem", db = findBot(s)); } else throw fail("DB " + s + " not running"); } boolean functional() { return db != null; } // now always true List list() { return adopt((List) rpc(db, "xlist")); } List list(String className) { return adopt((List) rpc(db, "xlist", className)); } List xlist() { return list(); } List xlist(String className) { return list(className); } // adopt is an internal method List adopt(List l) { if (l != null) for (RC rc : l) adopt(rc); return l; } RC adopt(RC rc) { if (rc != null) rc.db = this; return rc; } Object adopt(Object o) { if (o instanceof RC) return adopt((RC) o); return o; } String xclass(RC o) { return (String) rpc(db, "xclass", o); } Object xget(RC o, String field) { return adopt(rpc(db, "xget", o, field)); } String xS(RC o, String field) { return (String) xget(o, field); } RC xgetref(RC o, String field) { return adopt((RC) xget(o, field)); } void xset(RC o, String field, Object value) { rpc(db, "xset", o, field, value); } RC uniq(String className) { RC ref = first(list(className)); if (ref == null) ref = xnew(className); return ref; } RC xuniq(String className) { return uniq(className); } RC xnew(String className, Object... values) { return adopt((RC) rpc(db, "xnew", className, values)); } void xdelete(RC o) { rpc(db, "xdelete", o); } void xdelete(List l) { rpc(db, "xdelete", l); } void close() { if (db != null) db.close(); } String fullgrab() { return (String) rpc(db, "xfullgrab"); } String xfullgrab() { return fullgrab(); } void xshutdown() { rpc(db, "xshutdown"); } long xchangeCount() { return (long) rpc(db, "xchangeCount"); } int xcount() { return (int) rpc(db, "xcount"); } void reconnect() { close(); db = findBot(name); } RC rc(long id) { return new RC(this, id); } } static Object cget(Object c, String field) { Object o = getOpt(c, field); if (o instanceof Concept.Ref) return ((Concept.Ref) o).get(); return o; } static Concept getConcept(long id) { return mainConcepts.getConcept(id); } static A getConcept(Class cc, long id) { return getConcept(mainConcepts, cc, id); } static A getConcept(Concepts concepts, Class cc, long id) { Concept c = concepts.getConcept(id); if (c == null) return null; if (!isInstance(cc, c)) throw fail("Can't convert concept: " + getClassName(c) + " -> " + getClassName(cc) + " (" + id + ")"); return (A) c; } static Collection allConcepts() { return mainConcepts.allConcepts(); } static Collection allConcepts(Concepts concepts) { return concepts.allConcepts(); } // firstDelay = delay static java.util.Timer doEvery_daemon(int delay, final Object r) { return doEvery_daemon(delay, delay, r); } static java.util.Timer doEvery_daemon(int delay, int firstDelay, final Object r) { final java.util.Timer timer = new java.util.Timer(true); timer.scheduleAtFixedRate(smartTimerTask(r, timer, delay), firstDelay, delay); return timer; } static Map findBot_cache = synchroHashMap(); 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(sendToLocalBotQuietly(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 (Throwable __e) { print(exceptionToStringShort(__e)); } } return null; } static float abs(float f) { return Math.abs(f); } static int abs(int i) { return Math.abs(i); } static double abs(double d) { return Math.abs(d); } static String loadConceptsStructure(String progID) { return loadTextFilePossiblyGZipped(getProgramFile(progID, "concepts.structure")); } static String loadConceptsStructure() { return loadConceptsStructure(dbProgramID()); } static String dbBotName(String progID) { return fsI(progID) + " Concepts"; } static void saveGZStructureToFile(File file, Object o) { try { File parentFile = file.getParentFile(); if (parentFile != null) parentFile.mkdirs(); File tempFile = tempFileFor(file); if (tempFile.exists()) try { String saveName = tempFile.getPath() + ".saved." + now(); copyFile(tempFile, new File(saveName)); } catch (Throwable e) { printStackTrace(e); } FileOutputStream fileOutputStream = newFileOutputStream(tempFile.getPath()); GZIPOutputStream gos = new GZIPOutputStream(fileOutputStream); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(gos, "UTF-8"); PrintWriter printWriter = new PrintWriter(outputStreamWriter); structureToPrintWriter(o, printWriter); printWriter.close(); gos.close(); fileOutputStream.close(); if (file.exists() && !file.delete()) throw new IOException("Can't delete " + file.getPath()); if (!tempFile.renameTo(file)) throw new IOException("Can't rename " + tempFile + " to " + file); } catch (Exception __e) { throw rethrow(__e); } } static Object load(String varName) { readLocally(varName); return get(mc(), varName); } static Object load(String progID, String varName) { readLocally(progID, varName); return get(mc(), varName); } static boolean checkConceptFields(Concept x, Object... data) { for (int i = 0; i < l(data); i += 2) if (neq(cget(x, (String) data[i]), deref(data[i+1]))) return false; return true; } static void saveLocally(String variableName) { saveLocally(programID(), variableName); } static void saveLocally(String progID, String variableName) { saveLocally2(mc(), progID, variableName); } static void saveLocally2(Object obj, String variableName) { saveLocally2(obj, programID(), variableName); } static void saveLocally2(Object obj, String progID, String variableName) { Lock _lock_3 = saveLock(); lock(_lock_3); try { File textFile = new File(programDir(progID), variableName + ".text"); File structureFile = new File(programDir(progID), variableName + ".structure"); Object x = get(obj, variableName); if (x == null) { textFile.delete(); structureFile.delete(); } else if (x instanceof String) { saveTextFile(textFile, (String) x); structureFile.delete(); } else { saveTextFile(structureFile, javaTokWordWrap(structure(x))); textFile.delete(); } } finally { _lock_3.unlock(); } } static RemoteDB connectToDBOpt(String dbNameOrID) { try { return new RemoteDB(dbNameOrID); } catch (Throwable __e) { return null; } } static String fsI(String id) { return formatSnippetID(id); } static String fsI(long id) { return formatSnippetID(id); } static Object safeUnstructure(String s) { return unstructure(s, true); } static List addDyn(List l, A a) { if (l == null) l = new ArrayList(); l.add(a); return l; } static long toK(long l) { return (l+1023)/1024; } // returns number of changes static int cset(Concept c, Object... values) { try { int changes = 0; values = expandParams(c.getClass(), values); warnIfOddCount(values); for (int i = 0; i+1 < l(values); i += 2) { String field = (String) values[i]; Object value = values[i+1]; Field f = setOpt_findField(c.getClass(), field); //print("cset: " + c.id + " " + field + " " + struct(value) + " " + f); if (value instanceof RC) value = c._concepts.getConcept((RC) value); value = deref(value); if (value instanceof String && l((String) value) >= concepts_internStringsLongerThan) value = ((String) value).intern(); if (f == null) { // TODO: keep ref if it exists mapPut2(c.fieldValues, field, value instanceof Concept ? c.new Ref((Concept) value) : value); c.change(); } else if (isSubtypeOf(f.getType(), Concept.Ref.class)) { ((Concept.Ref) f.get(c)).set((Concept) derefRef(value)); c.change(); ++changes; } else { Object old = f.get(c); if (neq(value, old)) { f.set(c, value); c.change(); ++changes; } } } return changes; } catch (Exception __e) { throw rethrow(__e); } } static long toM(long l) { return (l+1024*1024-1)/(1024*1024); } static String toM(long l, int digits) { return formatDouble(toM_double(l), digits); } static void clearConcepts() { mainConcepts.clearConcepts(); } static void clearConcepts(Concepts concepts) { concepts.clearConcepts(); } static SortedMap synchroTreeMap() { return Collections.synchronizedSortedMap(new TreeMap()); } static void nohupJavax(final String javaxargs) { { Thread _t_0 = new Thread() { public void run() { try { call(hotwireOnce("#1008562"), "nohupJavax", javaxargs); } catch (Throwable __e) { printStackTrace2(__e); } } }; startThread(_t_0); } } static void nohupJavax(final String javaxargs, final String vmArgs) { { Thread _t_1 = new Thread() { public void run() { try { call(hotwireOnce("#1008562"), "nohupJavax", javaxargs, vmArgs); } catch (Throwable __e) { printStackTrace2(__e); } } }; startThread(_t_1); } } static List map(Iterable l, Object f) { return map(f, l); } static List map(Object f, Iterable l) { List x = new ArrayList(); for (Object o : unnull(l)) x.add(callF(f, o)); return x; } static List map(Object f, Object[] l) { return map(f, asList(l)); } static List map(Object[] l, Object f) { return map(f, l); } static List map(Object f, Map map) { return map(map, f); } static List map(Map map, Object f) { List x = new ArrayList(); for (Object _e : map.entrySet()) { Map.Entry e = (Map.Entry) _e; x.add(callF(f, e.getKey(), e.getValue())); } return x; } static String getDBProgramID_id; static String getDBProgramID() { return nempty(getDBProgramID_id) ? getDBProgramID_id : programID(); } static boolean hasType(Collection c, Class type) { for (Object x : c) if (isInstanceX(type, x)) return true; return false; } static void cleanKillVM() { ping(); cleanKillVM_noSleep(); sleep(); } static void cleanKillVM_noSleep() { call(getJavaX(), "cleanKill"); } static A uniq(Class c, Object... params) { return uniqueConcept(c, params); } static A uniq(Concepts cc, Class c, Object... params) { return uniqueConcept(cc, c, params); } static String javaTokWordWrap(String s) { int cols = 120, col = 0; List tok = javaTok(s); for (int i = 0; i < l(tok); i++) { String t = tok.get(i); if (odd(i) && col >= cols && !containsNewLine(t)) tok.set(i, t += "\n"); int idx = t.lastIndexOf('\n'); if (idx >= 0) col = l(t)-(idx+1); else col += l(t); } return join(tok); } static Object deref(Object o) { if (o instanceof Derefable) o = ((Derefable) o).get(); return o; } static Object rpc(String botName, String method, Object... args) { return unstructure_debug(matchOK2OrFail( sendToLocalBot(botName, rpc_makeCall(method, args)))); } static Object rpc(DialogIO bot, String method, Object... args) { return unstructure_debug(matchOK2OrFail( bot.ask(rpc_makeCall(method, args)))); } static String rpc_makeCall(String method, Object... args) { if (empty(args)) return "call " + method; return format("call *", concatLists((List) ll(method), asList(args))); } static A assertNotNull(A a) { assertTrue(a != null); return a; } static A assertNotNull(String msg, A a) { assertTrue(msg, a != null); return a; } static List filterByDynamicType(Collection c, String type) { List l = new ArrayList(); for (A x : c) if (eq(dynamicClassName(x), type)) l.add(x); return l; } 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 A firstOfType(Collection c, Class type) { for (Object x : c) if (isInstanceX(type, x)) return (A) x; return null; } static Object unstructure(String text) { return unstructure(text, false); } static Object unstructure(String text, final boolean allDynamic) { return unstructure(text, allDynamic, null); } static int structure_internStringsLongerThan = 50; static int unstructure_tokrefs; // stats abstract static class unstructure_Receiver { abstract void set(Object o); } // classFinder: func(name) -> class (optional) static Object unstructure(String text, boolean allDynamic, Object classFinder) { if (text == null) return null; return unstructure_tok(javaTokC_noMLS_iterator(text), allDynamic, classFinder); } static Object unstructure_reader(BufferedReader reader) { return unstructure_tok(javaTokC_noMLS_onReader(reader), false, null); } static Object unstructure_tok(final Producer tok, final boolean allDynamic, final Object classFinder) { final boolean debug = unstructure_debug; final class X { int i = -1; HashMap refs = new HashMap(); HashMap tokrefs = new HashMap(); HashSet concepts = new HashSet(); HashMap classesMap = new HashMap(); List stack = new ArrayList(); String curT; // look at current token String t() { return curT; } // get current token, move to next String tpp() { String t = curT; consume(); return t; } void parse(final unstructure_Receiver out) { String t = t(); int refID = 0; if (structure_isMarker(t, 0, l(t))) { refID = parseInt(t.substring(1)); consume(); } final int _refID = refID; // if (debug) print("parse: " + quote(t)); final int tokIndex = i; parse_inner(refID, tokIndex, new unstructure_Receiver() { void set(Object o) { if (_refID != 0) refs.put(_refID, o); if (o != null) tokrefs.put(tokIndex, o); out.set(o); } }); } void parse_inner(int refID, int tokIndex, final unstructure_Receiver out) { String t = t(); // if (debug) print("parse_inner: " + quote(t)); Class c = classesMap.get(t); if (c == null) { if (t.startsWith("\"")) { String s = internIfLongerThan(unquote(tpp()), structure_internStringsLongerThan); out.set(s); return; } if (t.startsWith("'")) { out.set(unquoteCharacter(tpp())); return; } if (t.equals("bigint")) { out.set(parseBigInt()); return; } if (t.equals("d")) { out.set(parseDouble()); return; } if (t.equals("fl")) { out.set(parseFloat()); return; } if (t.equals("false") || t.equals("f")) { consume(); out.set(false); return; } if (t.equals("true") || t.equals("t")) { consume(); out.set(true); return; } if (t.equals("-")) { consume(); t = tpp(); out.set(isLongConstant(t) ? (Object) (-parseLong(t)) : (Object) (-parseInt(t))); return; } if (isInteger(t) || isLongConstant(t)) { consume(); //if (debug) print("isLongConstant " + quote(t) + " => " + isLongConstant(t)); if (isLongConstant(t)) { out.set(parseLong(t)); return; } long l = parseLong(t); boolean isInt = l == (int) l; if (debug) print("l=" + l + ", isInt: " + isInt); out.set(isInt ? (Object) new Integer((int) l) : (Object) new Long(l)); return; } if (t.equals("File")) { consume(); File f = new File(unquote(tpp())); out.set(f); return; } if (t.startsWith("r") && isInteger(t.substring(1))) { consume(); int ref = Integer.parseInt(t.substring(1)); Object o = refs.get(ref); if (o == null) print("Warning: unsatisfied back reference " + ref); out.set(o); return; } if (t.startsWith("t") && isInteger(t.substring(1))) { consume(); int ref = Integer.parseInt(t.substring(1)); Object o = tokrefs.get(ref); if (o == null) print("Warning: unsatisfied token reference " + ref); out.set(o); return; } if (t.equals("hashset")) { parseHashSet(out); return; } if (t.equals("treeset")) { parseTreeSet(out); return; } if (eqOneOf(t, "hashmap", "hm")) { consume(); parseMap(new HashMap(), out); return; } if (t.equals("lhm")) { consume(); parseMap(new LinkedHashMap(), out); return; } if (t.equals("{")) { parseMap(out); return; } if (t.equals("[")) { parseList(out); return; } if (t.equals("bitset")) { parseBitSet(out); return; } if (t.equals("array") || t.equals("intarray")) { parseArray(out); return; } if (t.equals("ba")) { consume(); String hex = unquote(tpp()); out.set(hexToBytes(hex)); return; } if (t.equals("boolarray")) { consume(); int n = parseInt(tpp()); String hex = unquote(tpp()); out.set(boolArrayFromBytes(hexToBytes(hex), n)); return; } if (t.equals("class")) { out.set(parseClass()); return; } if (t.equals("l")) { parseLisp(out); return; } if (t.equals("null")) { consume(); out.set(null); return; } if (eq(t, "c")) { consume("c"); t = t(); assertTrue(isJavaIdentifier(t)); concepts.add(t); } } if (eq(t, "j")) { consume("j"); out.set(parseJava()); return; } if (c == null && !isJavaIdentifier(t)) throw new RuntimeException("Unknown token " + (i+1) + ": " + t); // any other class name if (c == null) { // First, find class if (allDynamic) c = null; else c = classFinder != null ? (Class) callF(classFinder, t) : findClass(t); if (c != null) classesMap.put(t, c); } // Check if it has an outer reference consume(); boolean hasBracket = eq(t(), "("); if (hasBracket) consume(); boolean hasOuter = hasBracket && eq(t(), "this$1"); DynamicObject dO = null; Object o = null; if (c != null) { o = hasOuter ? nuStubInnerObject(c) : nuEmptyObject(c); if (o instanceof DynamicObject) dO = (DynamicObject) o; } else { if (concepts.contains(t) && (c = findClass("Concept")) != null) o = dO = (DynamicObject) nuEmptyObject(c); else dO = new DynamicObject(); dO.className = t; if (debug) print("Made dynamic object " + t + " " + shortClassName(dO)); } // Save in references list early because contents of object // might link back to main object if (refID != 0) refs.put(refID, o != null ? o : dO); tokrefs.put(tokIndex, o != null ? o : dO); // NOW parse the fields! final LinkedHashMap fields = new LinkedHashMap(); // preserve order final Object _o = o; final DynamicObject _dO = dO; if (hasBracket) { stack.add(new Runnable() { public void run() { try { if (eq(t(), ")")) { consume(")"); objRead(_o, _dO, fields); out.set(_o != null ? _o : _dO); } else { final String key = unquote(tpp()); consume("="); stack.add(this); parse(new unstructure_Receiver() { void set(Object value) { fields.put(key, value); if (eq(t(), ",")) consume(); } }); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (eq(t(), \")\")) {\r\n consume(\")\");\r\n objRead(_o, _dO, fie..."; }}); } else { objRead(o, dO, fields); out.set(o != null ? o : dO); } } void objRead(Object o, DynamicObject dO, Map fields) { if (o != null) if (dO != null) { if (debug) printStructure("setOptAllDyn", fields); setOptAllDyn(dO, fields); } else { setOptAll(o, fields); } else for (String field : keys(fields)) dO.fieldValues.put(field.intern(), fields.get(field)); if (o != null) pcallOpt_noArgs(o, "_doneLoading"); } void parseSet(final Set set, final unstructure_Receiver out) { parseList(new unstructure_Receiver() { void set(Object o) { set.addAll((List) o); out.set(set); } }); } void parseLisp(final unstructure_Receiver out) { consume("l"); consume("("); final ArrayList list = new ArrayList(); stack.add(new Runnable() { public void run() { try { if (eq(t(), ")")) { consume(")"); out.set(newObject("main$Lisp", (String) list.get(0), subList(list, 1))); } else { stack.add(this); parse(new unstructure_Receiver() { void set(Object o) { list.add(o); if (eq(t(), ",")) consume(); } }); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (eq(t(), \")\")) {\r\n consume(\")\");\r\n out.set(newObject(\"main$..."; }}); } void parseBitSet(final unstructure_Receiver out) { consume("bitset"); consume("{"); final BitSet bs = new BitSet(); stack.add(new Runnable() { public void run() { try { if (eq(t(), "}")) { consume("}"); out.set(bs); } else { stack.add(this); parse(new unstructure_Receiver() { void set(Object o) { bs.set((Integer) o); if (eq(t(), ",")) consume(); } }); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (eq(t(), \"}\")) {\r\n consume(\"}\");\r\n out.set(bs);\r\n } ..."; }}); } void parseList(final unstructure_Receiver out) { consume("["); final ArrayList list = new ArrayList(); stack.add(new Runnable() { public void run() { try { if (eq(t(), "]")) { consume("]"); out.set(list); } else { stack.add(this); parse(new unstructure_Receiver() { void set(Object o) { //if (debug) print("List element type: " + getClassName(o)); list.add(o); if (eq(t(), ",")) consume(); } }); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (eq(t(), \"]\")) {\r\n consume(\"]\");\r\n out.set(list);\r\n ..."; }}); } void parseArray(final unstructure_Receiver out) { final String type = tpp(); consume("{"); final List list = new ArrayList(); stack.add(new Runnable() { public void run() { try { if (eq(t(), "}")) { consume("}"); out.set(type.equals("intarray") ? toIntArray(list) : list.toArray()); } else { stack.add(this); parse(new unstructure_Receiver() { void set(Object o) { list.add(o); if (eq(t(), ",")) consume(); } }); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "if (eq(t(), \"}\")) {\r\n consume(\"}\");\r\n out.set(type.equals(\"int..."; }}); } Object parseClass() { consume("class"); consume("("); String name = unquote(tpp()); consume(")"); name = dropPrefix("main$", name); Class c = allDynamic ? null : classFinder != null ? (Class) callF(classFinder, name) : 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 = tpp(); if (eq(val, "-")) val = "-" + tpp(); consume(")"); return new BigInteger(val); } Object parseDouble() { consume("d"); consume("("); String val = unquote(tpp()); consume(")"); return Double.parseDouble(val); } Object parseFloat() { consume("fl"); String val; if (eq(t(), "(")) { consume("("); val = unquote(tpp()); consume(")"); } else { val = unquote(tpp()); } return Float.parseFloat(val); } void parseHashSet(unstructure_Receiver out) { consume("hashset"); parseSet(new HashSet(), out); } void parseTreeSet(unstructure_Receiver out) { consume("treeset"); parseSet(new TreeSet(), out); } void parseMap(unstructure_Receiver out) { parseMap(new TreeMap(), out); } Object parseJava() { String j = unquote(tpp()); Matches m = new Matches(); if (jmatch("java.awt.Color[r=*,g=*,b=*]", j, m)) return nuObject("java.awt.Color", parseInt(m.unq(0)), parseInt(m.unq(1)), parseInt(m.unq(2))); else { warn("Unknown Java object: " + j); return null; } } void parseMap(final Map map, final unstructure_Receiver out) { consume("{"); stack.add(new Runnable() { boolean v; Object key; public void run() { if (v) { v = false; stack.add(this); consume("="); parse(new unstructure_Receiver() { void set(Object value) { map.put(key, value); if (debug) print("parseMap: Got value " + getClassName(value) + ", next token: " + quote(t())); if (eq(t(), ",")) consume(); } }); } else { if (eq(t(), "}")) { consume("}"); out.set(map); } else { v = true; stack.add(this); parse(new unstructure_Receiver() { void set(Object o) { key = o; } }); } } // if v else } // run() }); } /*void parseSub(unstructure_Receiver out) { int n = l(stack); parse(out); while (l(stack) > n) stack }*/ void consume() { curT = tok.next(); ++i; } void consume(String s) { if (!eq(t(), s)) { /*S prevToken = i-1 >= 0 ? tok.get(i-1) : ""; S nextTokens = join(tok.subList(i, Math.min(i+2, tok.size()))); fail(quote(s) + " expected: " + prevToken + " " + nextTokens + " (" + i + "/" + tok.size() + ")");*/ throw fail(quote(s) + " expected, got " + quote(t())); } consume(); } void parse_x(unstructure_Receiver out) { consume(); // get first token parse(out); while (nempty(stack)) popLast(stack).run(); } } final Var v = new Var(); X x = new X(); x.parse_x(new unstructure_Receiver() { void set(Object o) { v.set(o); } }); unstructure_tokrefs = x.tokrefs.size(); return v.get(); } static boolean unstructure_debug; static Concept cnew(String name, Object... values) { Class cc = findClass(name); Concept c = cc != null ? nuObject(cc) : new Concept(name); csetAll(c, values); return c; } static Concept cnew(Concepts concepts, String name, Object... values) { Class cc = findClass(name); concepts_unlisted.set(true); Concept c; try { c = cc != null ? nuObject(cc) : new Concept(name); } finally { concepts_unlisted.set(null); } concepts.register(c); csetAll(c, values); return c; } static A cnew(Class cc, Object... values) { A c = nuObject(cc); csetAll(c, values); return c; } static A cnew(Concepts concepts, Class cc, Object... values) { concepts_unlisted.set(true); A c; try { c = nuObject(cc); } finally { concepts_unlisted.set(null); } concepts.register(c); csetAll(c, values); return c; } static boolean bareDBMode_on; static void bareDBMode() { bareDBMode(null); // default autoSaveInterval } static void bareDBMode(Integer autoSaveInterval) { bareDBMode_on = true; conceptsAndBot(autoSaveInterval); } static boolean warn_on = true; static void warn(String s) { if (warn_on) print("Warning: " + s); } static void warn(String s, List warnings) { warn(s); if (warnings != null) warnings.add(s); } static Str concept(String name) { for (Str s : list(Str.class)) if (eqic(s.name, name) || containsIgnoreCase(s.otherNames, name)) return s; return new Str(name); } static int hours() { return hours(Calendar.getInstance()); } static int hours(Calendar c) { return c.get(Calendar.HOUR_OF_DAY); } static Lock dbLock() { return mainConcepts.lock; } static A findBackRef(Concept c, Class type) { for (Concept.Ref r : c.backRefs) if (instanceOf(r.concept(), type)) return (A) r.concept(); return null; } static A findBackRef(Class type, Concept c) { return findBackRef(c, type); } static List removeDyn(List l, A a) { if (l == null) return null; l.remove(a); return empty(l) ? null : l; } static String formatInt(int i, int digits) { return padLeft(str(i), '0', digits); } static WeakHashMap> callOpt_noArgs_cache = new WeakHashMap(); static Object callOpt_noArgs(Object o, String method) { try { if (o == null) return null; if (o instanceof Class) return callOpt(o, method); // not optimized Class c = o.getClass(); HashMap map; synchronized(callOpt_noArgs_cache) { map = callOpt_noArgs_cache.get(c); if (map == null) map = callOpt_noArgs_makeCache(c); } Method m = map.get(method); return m != null ? m.invoke(o) : null; } catch (Exception __e) { throw rethrow(__e); } } // used internally - we are in synchronized block static HashMap callOpt_noArgs_makeCache(Class c) { HashMap map = new HashMap(); Class _c = c; do { for (Method m : c.getDeclaredMethods()) if (m.getParameterTypes().length == 0) { m.setAccessible(true); String name = m.getName(); if (!map.containsKey(name)) map.put(name, m); } _c = _c.getSuperclass(); } while (_c != null); callOpt_noArgs_cache.put(c, map); return map; } static List filterByType(Collection c, Class type) { List l = new ArrayList(); for (Object x : c) if (isInstanceX(type, x)) l.add((A) x); return l; } static List filterByType(Object[] c, Class type) { return filterByType(asList(c), type); } static int done_minPrint = 10; static long done(long startTime, String desc) { long time = now()-startTime; if (time >= done_minPrint) print(desc + " [" + time + " ms]"); return time; } static long done(String desc, long startTime) { return done(startTime, desc); } static long done(long startTime) { return done(startTime, ""); } static void readLocally(String progID, String varNames) { readLocally2(mc(), progID, varNames); } static void readLocally(String varNames) { readLocally2(mc(), programID(), varNames); } static void readLocally2(Object obj, String varNames) { readLocally2(obj, programID(), varNames); } static int readLocally_stringLength; static ThreadLocal readLocally2_allDynamic = new ThreadLocal(); // read a string variable from standard storage // does not overwrite variable contents if there is no file static void readLocally2(Object obj, String progID, String varNames) { try { boolean allDynamic = isTrue(getAndClearThreadLocal(readLocally2_allDynamic)); for (String variableName : javaTokC(varNames)) { File textFile = new File(programDir(progID), variableName + ".text"); String value = loadTextFile(textFile); if (value != null) set(main.class, variableName, value); else { File structureFile = new File(programDir(progID), variableName + ".structure"); value = loadTextFile(structureFile); if (value == null) { File structureGZFile = new File(programDir(progID), variableName + ".structure.gz"); if (!structureGZFile.isFile()) return; //value = loadGZTextFile(structureGZFile); InputStream fis = new FileInputStream(structureGZFile); try { GZIPInputStream gis = new GZIPInputStream(fis); InputStreamReader reader = new InputStreamReader(gis, "UTF-8"); BufferedReader bufferedReader = new BufferedReader(reader); //O o = unstructure_reader(bufferedReader); Object o = unstructure_tok(javaTokC_noMLS_onReader(bufferedReader), allDynamic, null); readLocally_set(obj, variableName, o); } finally { fis.close(); } return; } readLocally_stringLength = l(value); if (nempty(value)) readLocally_set(obj, variableName, allDynamic ? safeUnstructure(value) : unstructure(value)); } } } catch (Exception __e) { throw rethrow(__e); } } static void readLocally_set(Object c, String varName, Object value) { Object oldValue = get(c, varName); if (oldValue instanceof List && !(oldValue instanceof ArrayList) && value != null) { // Assume it's a synchroList. value = synchroList((List) value); } set(c, varName, value); } static boolean hasConcept(Class c, Object... params) { return findConceptWhere(c, params) != null; } static Collection values(Map map) { return map == null ? emptyList() : map.values(); } static String ymd() { return year() + formatInt(month(), 2) + formatInt(dayOfMonth(), 2); } static int countConcepts(Class c, Object... params) { return mainConcepts.countConcepts(c, params); } static int countConcepts() { return mainConcepts.countConcepts(); } static int countConcepts(String className) { return mainConcepts.countConcepts(className); } static int countConcepts(Concepts concepts, String className) { return concepts.countConcepts(className); } static int year() { return Calendar.getInstance().get(Calendar.YEAR); } static Object unstructure_debug(String s) { try { return unstructure(s); } catch (Throwable _e) { print("Was unstructuring: " + s); throw rethrow(_e); } } static A uniqueConcept(Class c, Object... params) { return uniqueConcept(mainConcepts, c, params); } static A uniqueConcept(Concepts cc, Class c, Object... params) { params = expandParams(c, params); A x = findConceptWhere(cc, c, params); if (x == null) { x = nuObject(c); csetAll(x, params); } return x; } static byte[] hexToBytes(String s) { int n = l(s) / 2; byte[] bytes = new byte[n]; for (int i = 0; i < n; i++) { String hex = substring(s, i*2, i*2+2); try { bytes[i] = (byte) parseHexByte(hex); } catch (Throwable _e) { throw fail("Bad hex byte: " + quote(hex) + " at " + i*2 + "/" + l(s)); } } return bytes; } static HashMap nuEmptyObject_cache = new HashMap(); static A nuEmptyObject(Class c) { try { Constructor ctr; synchronized(nuEmptyObject_cache) { ctr = nuEmptyObject_cache.get(c); if (ctr == null) { nuEmptyObject_cache.put(c, ctr = nuEmptyObject_findConstructor(c)); ctr.setAccessible(true); } } return (A) ctr.newInstance(); } catch (Exception __e) { throw rethrow(__e); } } static Constructor nuEmptyObject_findConstructor(Class c) { for (Constructor m : c.getDeclaredConstructors()) if (m.getParameterTypes().length == 0) return m; throw fail("No default constructor declared in " + c.getName()); } static Lock saveLock_lock = fairLock(); static Lock saveLock() { return saveLock_lock; } static boolean structure_isMarker(String s, int i, int j) { if (i >= j) return false; if (s.charAt(i) != 'm') return false; ++i; while (i < j) { char c = s.charAt(i); if (c < '0' || c > '9') return false; ++i; } return true; } static Object newObject(Class c, Object... args) { return nuObject(c, args); } static Object newObject(String className, Object... args) { return nuObject(className, args); } static A findConceptWhere(Class c, Object... params) { return findConceptWhere(mainConcepts, c, params); } static A findConceptWhere(Concepts concepts, Class c, Object... params) { params = expandParams(c, params); // indexed if (concepts.fieldIndices != null) for (int i = 0; i < l(params); i += 2) { IFieldIndex index = concepts.getFieldIndex(c, (String) params[i]); if (index != null) for (A x : index.getAll(params[i+1])) if (checkConceptFields(x, params)) return x; } // table scan for (A x : concepts.list(c)) if (checkConceptFields(x, params)) return x; return null; } static Concept findConceptWhere(Concepts concepts, String c, Object... params) { for (Concept x : concepts.list(c)) if (checkConceptFields(x, params)) return x; return null; } static boolean startsWithIgnoreCase(String a, String b) { return a != null && a.regionMatches(true, 0, b, 0, b.length()); } static A getAndClearThreadLocal(ThreadLocal tl) { A a = tl.get(); tl.set(null); return a; } static boolean instanceOf(Object o, String className) { if (o == null) return false; String c = o.getClass().getName(); return eq(c, className) || eq(c, "main$" + className); } static boolean instanceOf(Object o, Class c) { if (c == null) return false; return c.isInstance(o); } static boolean jmatch(String pat, String s) { return jmatch(pat, s, null); } static boolean jmatch(String pat, String s, Matches matches) { if (s == null) return false; return jmatch(pat, javaTok(s), matches); } static boolean jmatch(String pat, List toks) { return jmatch(pat, toks, null); } static boolean jmatch(String pat, List toks, Matches matches) { List tokpat = javaTok(pat); 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 int month() { return Calendar.getInstance().get(Calendar.MONTH)+1; } volatile static boolean conceptsAndBot_running; static void conceptsAndBot() { conceptsAndBot(null); } static void conceptsAndBot(Integer autoSaveInterval) { if (conceptsAndBot_running) return; conceptsAndBot_running = true; try { ensureBotIsNotAlreadyRunning(dbBotStandardName()); } catch (Throwable _e) { mainConcepts.dontSave = true; // SAFETY throw rethrow(_e); } if (autoSaveInterval != null) loadAndAutoSaveConcepts(autoSaveInterval); else loadAndAutoSaveConcepts(); dbBot(); } static String sendToLocalBotQuietly(String bot, String text, Object... args) { text = format3(text, args); DialogIO channel = newFindBot2(bot); if (channel == null) throw fail(quote(bot) + " not found"); try { channel.readLine(); channel.sendLine(text); String s = channel.readLine(); return s; } catch (Throwable e) { e.printStackTrace(); return null; } finally { channel.close(); } } static String sendToLocalBotQuietly(int port, String text, Object... args) { text = format3(text, args); DialogIO channel = talkTo(port); try { channel.readLine(); channel.sendLine(text); String s = channel.readLine(); return s; } catch (Throwable e) { e.printStackTrace(); return null; } finally { if (channel != null) channel.close(); } } static String getBotAddress(String bot) { List l = fullBotScan(bot); return empty(l) ? null : first(l).address; } static void warnIfOddCount(Object... list) { if (odd(l(list))) printStackTrace("Odd list size: " + list); } static String padLeft(String s, char c, int n) { return rep(c, n-l(s)) + s; } // default to space static String padLeft(String s, int n) { return padLeft(s, ' ', n); } static void setOptAll(Object o, Map fields) { if (fields == null) return; for (String field : keys(fields)) setOpt(o, field, fields.get(field)); } static void setOptAll(Object o, Object... values) { //values = expandParams(c.getClass(), values); warnIfOddCount(values); for (int i = 0; i+1 < l(values); i += 2) { String field = (String) values[i]; Object value = values[i+1]; setOpt(o, field, value); } } static String dbProgramID() { return getDBProgramID(); } static String matchOK2OrFail(String s) { Matches m = new Matches(); if (swic(s, "ok ")) return substring(s, 3); else if (eqic(s, "ok")) return "ok"; else throw fail(s); } static String dynamicClassName(Object o) { if (o instanceof DynamicObject && ((DynamicObject) o).className != null) return "main$" + ((DynamicObject) o).className; return className(o); } static Producer javaTokC_noMLS_onReader(final BufferedReader r) { final class X implements Producer { StringBuilder buf = new StringBuilder(); // stores from "i" char c, d, e = 'x'; // just not '\0' X() { // fill c, d and e nc(); nc(); nc(); } // get next character(s) into c, d and e void nc() { try { c = d; d = e; if (e == '\0') return; int i = r.read(); e = i < 0 ? '\0' : (char) i; } catch (Exception __e) { throw rethrow(__e); } } void ncSave() { if (c != '\0') { buf.append(c); nc(); } } public String next() { // scan for whitespace while (c != '\0') { if (c == ' ' || c == '\t' || c == '\r' || c == '\n') nc(); else if (c == '/' && d == '*') { do nc(); while (c != '\0' && !(c == '*' && d == '/')); nc(); nc(); } else if (c == '/' && d == '/') { do nc(); while (c != '\0' && "\r\n".indexOf(c) < 0); } else break; } if (c == '\0') return null; // scan for non-whitespace if (c == '\'' || c == '"') { char opener = c; ncSave(); while (c != '\0') { if (c == opener || c == '\n') { // end at \n to not propagate unclosed string literal errors ncSave(); break; } else if (c == '\\') { ncSave(); ncSave(); } else ncSave(); } } else if (Character.isJavaIdentifierStart(c)) do ncSave(); while (Character.isJavaIdentifierPart(c) || c == '\''); // for stuff like "don't" else if (Character.isDigit(c)) { do ncSave(); while (Character.isDigit(c)); if (c == 'L') ncSave(); // Long constants like 1L } else ncSave(); String t = buf.toString(); buf.setLength(0); return t; } } return new X(); } static boolean isLongConstant(String s) { if (!s.endsWith("L")) return false; s = s.substring(0, l(s)-1); return isInteger(s); } static String sendToLocalBot(String bot, String text, Object... args) { text = format3(text, args); DialogIO channel = findBot(bot); if (channel == null) throw 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(); } } static int[] toIntArray(List l) { int[] a = new int[l(l)]; for (int i = 0; i < a.length; i++) a[i] = l.get(i); return a; } static Object derefRef(Object o) { if (o instanceof Concept.Ref) o = ((Concept.Ref) o).get(); return o; } static Object[] expandParams(Class c, Object[] params) { if (l(params) == 1) params = new Object[] { singleFieldName(c), params[0] }; else warnIfOddCount(params); return params; } static boolean isInstance(Class type, Object arg) { return type.isInstance(arg); } static int dayOfMonth() { return days(); } static boolean setOptAllDyn_debug; static void setOptAllDyn(DynamicObject o, Map fields) { if (fields == null) return; for (String field : keys(fields)) { Object val = fields.get(field); boolean has = hasField(o, field); if (has) setOpt(o, field, val); else { o.fieldValues.put(field.intern(), val); if (setOptAllDyn_debug) print("setOptAllDyn added dyn " + field + " to " + o + " [value: " + val + ", fieldValues = " + systemHashCode(o.fieldValues) + ", " + struct(keys(o.fieldValues)) + "]"); } } } 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 talkToSubBot_IO(subBot, io); } static class talkToSubBot_IO extends DialogIO { String subBot; DialogIO io; talkToSubBot_IO(String subBot, DialogIO io) { this.io = io; this.subBot = subBot;} // 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)); } } static String formatDouble(double d, int digits) { String format = "0." + rep(digits, '#'); return new java.text.DecimalFormat(format, new java.text.DecimalFormatSymbols(Locale.ENGLISH)).format(d); } static String firstPartOfHelloString(String s) { int i = s.lastIndexOf('/'); return i < 0 ? s : rtrim(s.substring(0, i)); } static Producer javaTokC_noMLS_iterator(final String s) { return new Producer() { final int l = s.length(); int i = 0; public String next() { if (i >= l) return null; int j = i; char c, d; // scan for whitespace while (j < l) { c = s.charAt(j); d = j+1 >= l ? '\0' : s.charAt(j+1); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (c == '/' && d == '*') { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (c == '/' && d == '/') { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } i = j; if (i >= l) return null; c = s.charAt(i); d = i+1 >= l ? '\0' : s.charAt(i+1); // 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 ++j; String t = quickSubstring(s, i, j); i = j; return t; } }; } 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; } // r may return false to cancel timer static TimerTask smartTimerTask(final Object r, final java.util.Timer timer, final int delay) { return new TimerTask() { long lastRun; public void run() { if (!licensed()) timer.cancel(); else { lastRun = fixTimestamp(lastRun); long now = now(); if (now >= lastRun + delay*0.9) { lastRun = now; if (eq(false, pcallF(r))) timer.cancel(); } } } }; } static char unquoteCharacter(String s) { assertTrue(s.startsWith("'") && s.length() > 1); return unquote("\"" + s.substring(1, s.endsWith("'") ? s.length()-1 : s.length()) + "\"").charAt(0); } static A nuStubInnerObject(Class c) { try { Class outerType = getOuterClass(c); Constructor m = c.getDeclaredConstructor(outerType); m.setAccessible(true); return (A) m.newInstance(new Object[] {null}); } catch (Exception __e) { throw rethrow(__e); } } static boolean[] boolArrayFromBytes(byte[] a, int n) { boolean[] b = new boolean[n]; int m = min(n, l(a)*8); for (int i = 0; i < m; i++) b[i] = (a[i/8] & 1 << (i & 7)) != 0; return b; } static String loadTextFilePossiblyGZipped(String fileName) { return loadTextFilePossiblyGZipped(fileName, null); } static String loadTextFilePossiblyGZipped(String fileName, String defaultContents) { File gz = new File(fileName + ".gz"); return gz.exists() ? loadGZTextFile(gz) : loadTextFile(fileName, defaultContents); } static String loadTextFilePossiblyGZipped(File fileName) { return loadTextFilePossiblyGZipped(fileName, null); } static String loadTextFilePossiblyGZipped(File fileName, String defaultContents) { return loadTextFilePossiblyGZipped(fileName.getPath(), defaultContents); } static HashMap findClass_cache = new HashMap(); // 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) { synchronized(findClass_cache) { if (findClass_cache.containsKey(name)) return findClass_cache.get(name); if (!isJavaIdentifier(name)) return null; Class c; try { c = Class.forName("main$" + name); } catch (ClassNotFoundException e) { c = null; } findClass_cache.put(name, c); return c; } } static DialogIO talkTo(int port) { return talkTo("localhost", port); } static int talkTo_defaultTimeout = 10000; // This is the CONNECT timeout static int talkTo_timeoutForReads = 0; // Timeout waiting for answers (0 = no timeout) static ThreadLocal> talkTo_byThread = new ThreadLocal(); static DialogIO talkTo(String ip, int port) { try { String full = ip + ":" + port; Map map = talkTo_byThread.get(); if (map != null && map.containsKey(full)) return map.get(full); if (isLocalhost(ip) && port == vmPort()) return talkToThisVM(); return new talkTo_IO(ip, port); } catch (Exception __e) { throw rethrow(__e); } } static class talkTo_IO extends DialogIO { String ip; int port; Socket s; Writer w; BufferedReader in; talkTo_IO(String ip, int port) { try { this.port = port; this.ip = ip; s = new Socket(); try { if (talkTo_timeoutForReads != 0) s.setSoTimeout(talkTo_timeoutForReads); s.connect(new InetSocketAddress(ip, port), talkTo_defaultTimeout); } catch (Throwable e) { throw fail("Tried talking to " + ip + ":" + port, e); } w = new OutputStreamWriter(s.getOutputStream(), "UTF-8"); in = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8")); } catch (Exception __e) { throw rethrow(__e); } } boolean isLocalConnection() { return s.getInetAddress().isLoopbackAddress(); } boolean isStillConnected() { return !(eos || s.isClosed()); } void sendLine(String line) { try { w.write(line + "\n"); w.flush(); } catch (Exception __e) { throw rethrow(__e); } } String readLineImpl() { try { return in.readLine(); } catch (Exception __e) { throw rethrow(__e); } } void close() { try { if (!noClose) s.close(); } catch (IOException e) { // whatever } } Socket getSocket() { return s; } } static float parseFloat(String s) { return Float.parseFloat(s); } static String internIfLongerThan(String s, int l) { return s == null ? null : l(s) >= l ? s.intern() : s; } static void pcallOpt_noArgs(Object o, String method) { try { callOpt_noArgs(o, method); } catch (Throwable __e) { printStackTrace2(__e); } } static double toM_double(long l) { return l/(1024*1024.0); } static void mapPut2(Map map, A key, B value) { if (map != null && key != null) if (value != null) map.put(key, value); else map.remove(key); } static String format(String pat, Object... args) { return format3(pat, args); } static Object nuObject(String className, Object... args) { try { return nuObject(Class.forName(className), args); } catch (Exception __e) { throw rethrow(__e); } } // too ambiguous - maybe need to fix some callers /*static O nuObject(O realm, S className, O... args) { ret nuObject(_getClass(realm, className), args); }*/ static A nuObject(Class c, Object... args) { try { Constructor m = nuObject_findConstructor(c, args); m.setAccessible(true); return (A) m.newInstance(args); } catch (Exception __e) { throw rethrow(__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 fail("Constructor " + c.getName() + getClasses(args) + " not found" + (args.length == 0 && (c.getModifiers() & Modifier.STATIC) == 0 ? " - hint: it's a non-static class!" : "")); } 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 File tempFileFor(File f) { return new File(f.getPath() + "_temp"); } static Class hotwireOnce(String programID) { return hotwireCached(programID, false); } static Map newFindBot2_cache = synchroHashMap(); static boolean newFindBot2_verbose; static DialogIO newFindBot2(String name) { Integer port = newFindBot2_cache.get(name); if (port != null) { if (newFindBot2_verbose) print("newFindBot2: testing " + name + " => " + port); DialogIO io = talkTo(port); String q = format("has bot *", name); String s = io.ask(q); if (match("yes", s)) { io = talkToSubBot(name, io); call(io, "pushback", "?"); // put some hello string in (yes, this should be improved.) return io; } // bot not there anymore - remove cache entry newFindBot2_cache.remove(name); if (newFindBot2_verbose) print("newFindBot2: dropping " + name + " => " + port); } DialogIO io = findBot(name); if (io != null) { newFindBot2_cache.put(name, io.getPort()); if (newFindBot2_verbose) print("newFindBot2: remembering " + name + " => " + port); } return io; } static TreeMap hotwireCached_cache = new TreeMap(); static Class hotwireCached(String programID) { return hotwireCached(programID, true); } static synchronized Class hotwireCached(String programID, boolean runMain) { programID = formatSnippetID(programID); Class c = hotwireCached_cache.get(programID); if (c == null) { c = hotwire(programID); if (runMain) callMain(c); hotwireCached_cache.put(programID, c); } return c; } static int vmPort() { return myVMPort(); } static volatile boolean licensed_yes = true; static boolean licensed() { ping(); return licensed_yes; } static void licensed_off() { licensed_yes = false; } static int days() { return Calendar.getInstance().get(Calendar.DAY_OF_MONTH); } static boolean hasField(Object o, String field) { return findField2(o, field) != null; } static class ScannedBot { String helloString; String address; ScannedBot(String helloString, String address) { this.address = address; this.helloString = helloString;} ScannedBot() {} } static List fullBotScan() { return fullBotScan(""); } static List fullBotScan(String searchPattern) { List bots = new ArrayList(); for (ProgramScan.Program p : quickBotScan()) { String botName = firstPartOfHelloString(p.helloString); boolean isVM = startsWithIgnoreCase(p.helloString, "This is a JavaX VM."); boolean shouldRecurse = swic(botName, "Multi-Port") || isVM; if (swic(botName, searchPattern)) bots.add(new ScannedBot(botName, "" + p.port)); if (shouldRecurse) try { Map subBots = (Map) unstructure(sendToLocalBotQuietly(p.port, "list bots")); for (Number vport : subBots.keySet()) { botName = subBots.get(vport); if (swic(botName, searchPattern)) bots.add(new ScannedBot(botName, p.port + "/" + vport)); } } catch (Exception e) { e.printStackTrace(); } } return bots; } static String rep(int n, char c) { return repeat(c, n); } static String rep(char c, int n) { return repeat(c, n); } static List rep(A a, int n) { return repeat(a, n); } static List rep(int n, A a) { return repeat(n, a); } static Class getOuterClass(Class c) { try { String s = c.getName(); int i = s.lastIndexOf('$'); return Class.forName(substring(s, 0, i)); } catch (Exception __e) { throw rethrow(__e); } } static DialogIO talkToThisVM() { return new talkToThisVM_IO(); } static class talkToThisVM_IO extends DialogIO { List answers = ll(thisVMGreeting()); boolean isLocalConnection() { return true; } boolean isStillConnected() { return true; } int getPort() { return vmPort(); } void sendLine(String line) { answers.add(or2(sendToThisVM_newThread(line), "?")); } String readLineImpl() { try { return popFirst(answers); } catch (Exception __e) { throw rethrow(__e); } } void close() {} Socket getSocket() { return null; } } static String singleFieldName(Class c) { Set l = listFields(c); if (l(l) != 1) throw fail("No single field found in " + c + " (have " + n(l(l), "fields") + ")"); return first(l); } static volatile Android3 dbBot_instance; static Android3 dbBot() { return dbBot(dbBotStandardName()); } static Android3 dbBot(String name) { ensureBotIsNotAlreadyRunning(name); assertNotNull(mainConcepts); return dbBot_instance = methodsBot2(name, mainConcepts, exposedDBMethods, mainConcepts.lock); } static String dbBotStandardName() { return dbBotName(getDBProgramID()) + "."; } 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; } static void ensureBotIsNotAlreadyRunning(String name) { if (hasBot(name)) throw fail("Bot already running [" + name + "]"); } static boolean swic(String a, String b) { return startsWithIgnoreCase(a, b); } static boolean swic(String a, String b, Matches m) { if (!swic(a, b)) return false; m.m = new String[] {substring(a, l(b))}; return true; } static int systemHashCode(Object o) { return identityHashCode(o); } static ReentrantLock fairLock() { return new ReentrantLock(true); } static long fixTimestamp(long timestamp) { return timestamp > now() ? 0 : timestamp; } static String className(Object o) { return getClassName(o); } static List getClasses(Object[] array) { List l = new ArrayList(); for (Object o : array) l.add(_getClass(o)); return l; } static Object pcallF(Object f, Object... args) { return pcallFunction(f, args); } static B pcallF(F1 f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { return null; } } static String loadGZTextFile(File file) { try { if (!file.isFile()) return null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); InputStream fis = new FileInputStream(file); GZIPInputStream gis = new GZIPInputStream(fis); try { byte[] buffer = new byte[1024]; int len; while((len = gis.read(buffer)) != -1){ baos.write(buffer, 0, len); } } finally { fis.close(); } baos.close(); return fromUtf8(baos.toByteArray()); // TODO: use a Reader } catch (Exception __e) { throw rethrow(__e); } } static int parseHexByte(String s) { return Integer.parseInt(s, 16); } static boolean isLocalhost(String ip) { return isLoopbackIP(ip) || eqic(ip, "localhost"); } static String thisVMGreeting() { List record_list = (List) ( get(getJavaX(), "record_list")); Object android = first(record_list); // Should be of class Android3 return getString(android, "greeting"); } static Class _getClass(String name) { try { return Class.forName(name); } catch (ClassNotFoundException e) { return null; // could optimize this } } static Class _getClass(Object o) { return o == null ? null : o instanceof Class ? (Class) o : o.getClass(); } static Class _getClass(Object realm, String name) { try { return getClass(realm).getClassLoader().loadClass(classNameToVM(name)); } catch (Exception __e) { throw rethrow(__e); } } static String repeat(char c, int n) { n = Math.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(n); for (int i = 0; i < n; i++) l.add(a); return l; } static List repeat(int n, A a) { return repeat(a, n); } // This is for main classes that are all static. // (We don't go to base classes.) static Set listFields(Object c) { TreeSet fields = new TreeSet(); for (Field f : _getClass(c).getDeclaredFields()) fields.add(f.getName()); return fields; } static int myVMPort() { List records = (List) ( get(getJavaX(), "record_list")); Object android = records.get(records.size()-1); return (Integer) get(android, "port"); } static boolean isLoopbackIP(String ip) { return eq(ip, "127.0.0.1"); } static String or2(String a, String b) { return nempty(a) ? a : b; } static Field findField2(Object o, String field) { if (o instanceof Class) return findField2_findStaticField((Class) o, field); return findField2_findField(o.getClass(), field); } static Field findField2_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 findField2_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; } static String sendToThisVM_newThread(String s, Object... args) { final String _s = format(s, args); try { return (String) evalInNewThread(new Object() { Object get() { try { return callStaticAnswerMethod(getJavaX(), _s) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callStaticAnswerMethod(getJavaX(), _s)"; }}); } catch (Throwable e) { e = getInnerException(e); printStackTrace(e); return str(e); } } static A popFirst(List l) { if (empty(l)) return null; A a = first(l); l.remove(0); return a; } static Object pcallFunction(Object f, Object... args) { try { return callFunction(f, args); } catch (Throwable __e) { printStackTrace2(__e); } return null; } static boolean hasBot(String searchPattern) { DialogIO io = findBot(searchPattern); if (io != null) { io.close(); return true; } else return false; } static Android3 methodsBot2(String name, final Object receiver, final List exposedMethods) { return methodsBot2(name, receiver, exposedMethods, null); } static Android3 methodsBot2(String name, final Object receiver, final List exposedMethods, final Lock lock) { Android3 android = new Android3(); android.greeting = name; android.console = false; android.responder = new Responder() { String answer(String s, List history) { return exposeMethods2(receiver, s, exposedMethods, lock); } }; return makeBot(android); } static String fromUtf8(byte[] bytes) { try { return new String(bytes, "UTF-8"); } catch (Exception __e) { throw rethrow(__e); } } 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) getOpt(o, (String) key); throw fail("Not a string key: " + getClassName(key)); } static int makeBot(String greeting) { return makeAndroid3(greeting).port; } static Android3 makeBot(Android3 a) { makeAndroid3(a); return a; } static Android3 makeBot(String greeting, Object responder) { Android3 a = new Android3(greeting); a.responder = makeResponder(responder); makeBot(a); return a; } static Android3 makeBot() { return makeAndroid3(getProgramTitle() + "."); } static Object evalInNewThread(final Object f) { final Flag flag = new Flag(); final Var var = new Var(); final Var exception = new Var(); { Thread _t_2 = new Thread() { public void run() { try { try { var.set(callF(f)); } catch (Throwable e) { exception.set(e); } flag.raise(); } catch (Throwable __e) { printStackTrace2(__e); } } }; startThread(_t_2); } flag.waitUntilUp(); if (exception.has()) throw rethrow(exception.get()); return var.get(); } static String callStaticAnswerMethod(List bots, String s) { for (Object c : bots) try { String answer = callStaticAnswerMethod(c, s); if (!empty(answer)) return answer; } catch (Throwable e) { print("Error calling " + getProgramID(c)); e.printStackTrace(); } return null; } static String callStaticAnswerMethod(Object c, String s) { String answer = (String) callOpt(c, "answer", s, litlist(s)); if (answer == null) answer = (String) callOpt(c, "answer", s); return emptyToNull(answer); } static String callStaticAnswerMethod(String s) { return callStaticAnswerMethod(mc(), s); } static String callStaticAnswerMethod(String s, List history) { return callStaticAnswerMethod(mc(), s, history); } static String callStaticAnswerMethod(Object c, String s, List history) { String answer = (String) callOpt(c, "answer", s, history); if (answer == null) answer = (String) callOpt(c, "answer", s); return emptyToNull(answer); } static boolean exposeMethods2_debug; static String exposeMethods2(Object receiver, String s, List methodNames) { return exposeMethods2(receiver, s, methodNames, null); } static String exposeMethods2(Object receiver, String s, List methodNames, Lock lock) { Matches m = new Matches(); if (exposeMethods2_debug) print("Received: " + s); if (match("call *", s, m)) { List l; if (isIdentifier(m.unq(0))) l = ll(m.unq(0)); else l = (List) unstructure(m.unq(0)); // we used to have safeUnstructure here String method = getString(l, 0); if (!contains(methodNames, method)) throw fail("Method not allowed: " + method); if (lock != null) lock.lock(); try { if (exposeMethods2_debug) print("Calling: " + method); Object o = call(receiver, method, asObjectArray(subList(l, 1))); if (exposeMethods2_debug) print("Got: " + getClassName(o)); return ok2(structure(o)); } finally { if (lock != null) lock.unlock(); } } if (match("list methods", s)) return ok2(structure(methodNames)); return null; } static String classNameToVM(String name) { return name.replace(".", "$"); } 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 { try { return getClass(realm).getClassLoader().loadClass(classNameToVM(name)); } catch (ClassNotFoundException e) { return null; } } catch (Exception __e) { throw rethrow(__e); } } // An "Android" is a program that accepts text questions (on console or TCP) and outputs one response text per question static boolean makeAndroid3_disable; // disable all android making static class Android3 { String greeting; boolean publicOverride; // optionally set this in client int startPort = 5000; // optionally set this in client Responder responder; boolean console = true; boolean quiet; // no messages on console boolean daemon = false; boolean incomingSilent = false; int incomingPrintLimit = 200; boolean useMultiPort = true; boolean recordHistory; boolean verbose; int answerPrintLimit = 500; boolean newLineAboveAnswer, newLineBelowAnswer; // set by system int port; long vport; DialogHandler handler; ServerSocket server; Android3(String greeting) { this.greeting = greeting;} Android3() {} synchronized void dispose() { if (server != null) { try { server.close(); } catch (IOException e) { print("[internal] " + e); } server = null; } if (vport != 0) try { print("Disposing " + this); removeFromMultiPort(vport); vport = 0; } catch (Throwable __e) { printStackTrace2(__e); } } public String toString() { return "Bot: " + greeting + " [vport " + vport + "]"; } } static abstract class Responder { abstract String answer(String s, List history); } static Android3 makeAndroid3(final String greeting) { return makeAndroid3(new Android3(greeting)); } static Android3 makeAndroid3(final String greeting, Responder responder) { Android3 android = new Android3(greeting); android.responder = responder; return makeAndroid3(android); } static Android3 makeAndroid3(final Android3 a) { if (makeAndroid3_disable) return a; if (a.responder == null) a.responder = new Responder() { String answer(String s, List history) { return callStaticAnswerMethod(s, history); } }; if (!a.quiet) print("[bot] " + a.greeting); if (a.console && makeAndroid3_consoleInUse() && !readLine_noReadLine) a.console = false; record(a); if (a.useMultiPort) a.vport = addToMultiPort(a.greeting, makeAndroid3_verboseResponder(a)); if (a.console) makeAndroid3_handleConsole(a); if (a.useMultiPort) return a; a.handler = makeAndroid3_makeDialogHandler(a); if (a.quiet) startDialogServer_quiet.set(true); try { a.port = a.daemon ? startDialogServerOnPortAboveDaemon(a.startPort, a.handler) : startDialogServerOnPortAbove(a.startPort, a.handler); } finally { startDialogServer_quiet.set(null); } a.server = startDialogServer_serverSocket; return a; } static void makeAndroid3_handleConsole(final Android3 a) { // Console handling stuff if (!a.quiet) print("You may also type on this console."); { Thread _t_3 = new Thread() { public void run() { try { List history = new ArrayList(); while (licensed()) { String line; try { line = readLine(); } catch (Throwable e) { print(getInnerMessage(e)); break; } if (line == null) break; /*if (eq(line, "bye")) { print("> bye stranger"); history = new ArrayList(); } else*/ { history.add(line); history.add(makeAndroid3_getAnswer(line, history, a)); // prints answer on console too } } } catch (Throwable __e) { printStackTrace2(__e); } } }; startThread(_t_3); } } static DialogHandler makeAndroid3_makeDialogHandler(final Android3 a) { return new DialogHandler() { public void run(final DialogIO io) { if (!a.publicOverride && !(publicCommOn() || io.isLocalConnection())) { io.sendLine("Sorry, not allowed"); return; } String dialogID = randomID(8); io.sendLine(a.greeting + " / Your ID: " + dialogID); List history = new ArrayList(); while (io.isStillConnected()) { if (io.waitForLine()) { final String line = io.readLineNoBlock(); String s = dialogID + " at " + now() + ": " + quote(line); if (!a.incomingSilent) print(shorten(s, a.incomingPrintLimit)); if (eq(line, "bye")) { io.sendLine("bye stranger"); return; } Matches m = new Matches(); if (a.recordHistory) history.add(line); String answer; if (match3("this is a continuation of talk *", s, m) || match3("hello bot! this is a continuation of talk *", s, m)) { dialogID = unquote(m.m[0]); answer = "ok"; } else try { makeAndroid3_io.set(io); answer = makeAndroid3_getAnswer(line, history, a); } finally { makeAndroid3_io.set(null); } if (a.recordHistory) history.add(answer); io.sendLine(answer); //appendToLog(logFile, s); } } }}; } static String makeAndroid3_getAnswer(String line, List history, Android3 a) { String answer, originalAnswer; try { originalAnswer = a.responder.answer(line, history); answer = makeAndroid3_fallback(line, history, originalAnswer); } catch (Throwable e) { e = getInnerException(e); printStackTrace(e); originalAnswer = answer = e.toString(); } if (!a.incomingSilent) { if (originalAnswer == null) originalAnswer = "?"; if (a.newLineAboveAnswer) print(); print(">" + dropFirst(indentx(2, shorten(rtrim(originalAnswer), a.answerPrintLimit)))); if (a.newLineBelowAnswer) print(); } return answer; } static String makeAndroid3_fallback(String s, List history, String answer) { // Now we only do the safe thing instead of VM inspection - give out our process ID if (answer == null && match3("what is your pid", s)) return getPID(); if (answer == null && match3("what is your program id", s)) // should be fairly safe, right? return getProgramID(); if (match3("get injection id", s)) return getInjectionID(); if (answer == null) answer = "?"; if (answer.indexOf('\n') >= 0 || answer.indexOf('\r') >= 0) answer = quote(answer); return answer; } static boolean makeAndroid3_consoleInUse() { for (Object o : record_list) if (o instanceof Android3 && ((Android3) o).console) return true; return false; } static Responder makeAndroid3_verboseResponder(final Android3 a) { return new Responder() { String answer(String s, List history) { if (a.verbose) print("> " + shorten(s, a.incomingPrintLimit)); String answer = a.responder.answer(s, history); if (a.verbose) print("< " + shorten(answer, a.incomingPrintLimit)); return answer; } }; } static ThreadLocal makeAndroid3_io = new ThreadLocal(); static Android3 makeAndroid3() { return makeAndroid3(getProgramTitle() + "."); } static String getProgramTitle() { return getProgramName(); } static Object[] asObjectArray(List l) { return toObjectArray(l); } static String emptyToNull(String s) { return eq(s, "") ? null : s; } static String ok2(String s) { return "ok " + s; } static String makeResponder_callAnswerMethod(Object bot, String s, List history) { String answer = (String) callOpt(bot, "answer", s, history); if (answer == null) answer = (String) callOpt(bot, "answer", s); return answer; } static Responder makeResponder(final Object bot) { if (bot instanceof Responder) return (Responder) bot; if (bot instanceof String) { String f = (String) ( bot); return new Responder() { String answer(String s, List history) { String answer = (String) callOptMC((String) bot, s, history); if (answer == null) answer = (String) callOptMC((String) bot, s); return answer; } }; } return new Responder() { String answer(String s, List history) { return makeResponder_callAnswerMethod(bot, s, history); } }; } static List record_list = synchroList(); static void record(Object o) { record_list.add(o); } static AtomicInteger dialogServer_clients = new AtomicInteger(); static boolean dialogServer_printConnects; static ThreadLocal startDialogServer_quiet = new ThreadLocal(); static Set dialogServer_knownClients = synchroTreeSet(); static int startDialogServerOnPortAbove(int port, DialogHandler handler) { while (!forbiddenPort(port) && !startDialogServerIfPortAvailable(port, handler)) ++port; return port; } static int startDialogServerOnPortAboveDaemon(int port, DialogHandler handler) { while (!forbiddenPort(port) && !startDialogServerIfPortAvailable(port, handler, true)) ++port; return port; } static void startDialogServer(int port, DialogHandler handler) { if (!startDialogServerIfPortAvailable(port, handler)) throw fail("Can't start dialog server on port " + port); } static boolean startDialogServerIfPortAvailable(int port, final DialogHandler handler) { return startDialogServerIfPortAvailable(port, handler, false); } static ServerSocket startDialogServer_serverSocket; static boolean startDialogServerIfPortAvailable(int port, final DialogHandler handler, boolean daemon) { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(port); } catch (IOException e) { // probably the port number is used - let's assume there already is a chat server. return false; } final ServerSocket _serverSocket = serverSocket; startDialogServer_serverSocket = serverSocket; Thread thread = new Thread("Socket accept port " + port) { public void run() { try { while (true) { try { final Socket s = _serverSocket.accept(); String client = s.getInetAddress().toString(); if (!dialogServer_knownClients.contains(client) && neq(client, "/127.0.0.1")) { print("connect from " + client + " - clients: " + dialogServer_clients.incrementAndGet()); dialogServer_knownClients.add(client); } String threadName = "Handling client " + s.getInetAddress(); Thread t2 = new Thread(threadName) { public void run() { try { final Writer w = new OutputStreamWriter(s.getOutputStream(), "UTF-8"); final BufferedReader in = new BufferedReader( new InputStreamReader(s.getInputStream(), "UTF-8")); DialogIO io = new DialogIO() { // This should be the same as #1001076 (talkTo) boolean isLocalConnection() { return s.getInetAddress().isLoopbackAddress(); } boolean isStillConnected() { return !(eos || s.isClosed()); } void sendLine(String line) { try { w.write(line + "\n"); w.flush(); } catch (Exception __e) { throw rethrow(__e); } } String readLineImpl() { try { return in.readLine(); } catch (Exception __e) { throw rethrow(__e); } } void close() { try { s.close(); } catch (IOException e) { // whatever } } Socket getSocket() { return s; } }; try { handler.run(io); } finally { s.close(); } } catch (IOException e) { print("[internal] " + e); } finally { //print("client disconnect - " + dialogServer_clients.decrementAndGet() + " remaining"); } } }; // Thread t2 t2.setDaemon(true); // ? t2.start(); } catch (SocketTimeoutException e) { } } } catch (IOException e) { print("[internal] " + e); } }}; if (daemon) thread.setDaemon(true); thread.start(); if (!isTrue(getAndClearThreadLocal(startDialogServer_quiet))) print("Dialog server on port " + port + " started."); return true; } static String myJavaSource_code; static String myJavaSource() { return myJavaSource_code; } static boolean publicCommOn() { return "1".equals(loadTextFile(new File(userHome(), ".javax/public-communication"))); } static void removeFromMultiPort(long vport) { if (vport == 0) return; for (Object port : getMultiPorts()) call(port, "removePort", vport); } static Object addToMultiPort_responder; static long addToMultiPort(final String botName) { return addToMultiPort(botName, new Object() { public String answer(String s, List history) { String answer = (String) ( callOpt(getMainClass(), "answer", s, history)); if (answer != null) return answer; answer = (String) callOpt(getMainClass(), "answer", s); if (answer != null) return answer; if (match3("get injection id", s)) return getInjectionID(); return null; } }); } static long addToMultiPort(final String botName, final Object responder) { //print(botName); addToMultiPort_responder = responder; startMultiPort(); List ports = getMultiPorts(); if (ports == null) return 0; if (ports.isEmpty()) throw fail("No multiports!"); if (ports.size() > 1) print("Multiple multi-ports. Using last one."); Object port = last(ports); Object responder2 = new Object() { public String answer(String s, List history) { if (match3("get injection id", s)) return getInjectionID(); if (match3("your name", s)) return botName; return (String) call(responder, "answer", s, history); } }; record(responder2); return (Long) call(port, "addResponder", botName, responder2); } static String indentx(String s) { return indentx(indent_default, s); } static String indentx(int n, String s) { return dropSuffix(repeat(' ', n), indent(n, s)); } static String indentx(String indent, String s) { return dropSuffix(indent, indent(indent, s)); } static String getInjectionID() { return (String) call(getJavaX(), "getInjectionID", getMainClass()); } static String[] dropFirst(int n, String[] a) { return drop(n, a); } static String[] dropFirst(String[] a) { return drop(1, a); } static Object[] dropFirst(Object[] a) { return drop(1, a); } static List dropFirst(List l) { return dropFirst(1, l); } static List dropFirst(Iterable i) { return dropFirst(toList(i)); } static List dropFirst(int n, List l) { return n <= 0 ? l : new ArrayList(l.subList(Math.min(n, l.size()), l.size())); } static List dropFirst(List l, int n) { return dropFirst(n, l); } static String dropFirst(int n, String s) { return substring(s, n); } static String dropFirst(String s) { return substring(s, 1); } static Object callOptMC(String method, Object... args) { return callOpt(mc(), method, args); } static String getInnerMessage(Throwable e) { if (e == null) return null; return getInnerException(e).getMessage(); } static Object[] toObjectArray(Collection c) { List l = asList(c); return l.toArray(new Object[l.size()]); } static String processID_cached; // try to get our current process ID static String getPID() { if (processID_cached == null) { String name = ManagementFactory.getRuntimeMXBean().getName(); processID_cached = name.replaceAll("@.*", ""); } return processID_cached; } static int randomID_defaultLength = 12; static String randomID(int length) { return makeRandomID(length); } static String randomID() { return randomID(randomID_defaultLength); } static String getProgramName_cache; static synchronized String getProgramName() { if (getProgramName_cache == null) getProgramName_cache = getSnippetTitleOpt(programID()); return getProgramName_cache; } static Set synchroTreeSet() { return Collections.synchronizedSet(new TreeSet()); } static boolean forbiddenPort(int port) { return port == 5037; // adb } static ArrayList toList(A[] a) { return asList(a); } static ArrayList toList(int[] a) { return asList(a); } static ArrayList toList(Set s) { return asList(s); } static ArrayList toList(Iterable s) { return asList(s); } static List getMultiPorts() { return (List) callOpt(getJavaX(), "getMultiPorts"); } static int indent_default = 2; 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 String indent(String s) { return indent(indent_default, s); } static List indent(String indent, List lines) { List l = new ArrayList(); for (String s : lines) l.add(indent + s); return l; } // start multi-port if none exists in current VM. static void startMultiPort() { List mp = getMultiPorts(); if (mp != null && mp.isEmpty()) { nohupJavax("#1001639"); throw fail("Upgrading JavaX, please restart this program afterwards."); //callMain(hotwire("#1001672")); } } static String getSnippetTitleOpt(String s) { return isSnippetID(s) ? getSnippetTitle(s) : s; } static String[] drop(int n, String[] a) { n = Math.min(n, a.length); String[] b = new String[a.length-n]; System.arraycopy(a, n, b, 0, b.length); return b; } static Object[] drop(int n, Object[] a) { n = Math.min(n, a.length); Object[] b = new Object[a.length-n]; System.arraycopy(a, n, b, 0, b.length); return b; } static String getSnippetTitle(String id) { try { if (id == null) return null; if (!isSnippetID(id)) return "?"; long parsedID = parseSnippetID(id); String url; if (isImageServerSnippet(parsedID)) url = "http://ai1.lol/images/raw/title/" + parsedID; else url = tb_mainServer() + "/tb-int/getfield.php?id=" + parsedID + "&field=title" + standardCredentials(); return trim(loadPageSilently(url)); } catch (Exception __e) { throw rethrow(__e); } } static String getSnippetTitle(long id) { return getSnippetTitle(fsI(id)); } static boolean isImageServerSnippet(long id) { return id >= 1100000 && id < 1200000; } static interface Producer { public A next(); } static class Str extends Concept { String name; List otherNames = new ArrayList(); Str() {} Str(String name) { this.name = name;} public String toString() { return name; } } /** this class is fully thread-safe */ static class Flag { private boolean up; /** returns true if flag was down before */ public synchronized boolean raise() { if (!up) { up = true; notifyAll(); return true; } else return false; } public synchronized void waitUntilUp() { while (!up) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void waitUntilUp(long timeout) { if (!up) { try { wait(timeout); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized boolean isUp() { return up; } public String toString() { return isUp() ? "up" : "down"; } // currently does a semi-active wait with latency = 50 ms public void waitForThisOr(Flag otherFlag) { try { while (!isUp() && !otherFlag.isUp()) Thread.sleep(50); } catch (Exception __e) { throw rethrow(__e); } } } static class ProgramScan { static int threads = isWindows() ? 500 : 10; static int timeout = 5000; // hmm... static String ip = "127.0.0.1"; // This range is not used anymore anyway static int quickScanFrom = 10000, quickScanTo = 10999; static int maxNumberOfVMs_android = 4; // Android will always only have one if we don't screw up static int maxNumberOfVMs_nonAndroid = 50; // 100; static int maxNumberOfVMs; static boolean verbose; static class Program { int port; String helloString; Program(int port, String helloString) { this.helloString = helloString; this.port = port;} } static List scan() { try { return scan(1, 65535); } catch (Exception __e) { throw rethrow(__e); } } static List scan(int fromPort, int toPort) { return scan(fromPort, toPort, new int[0]); } static List scan(int fromPort, int toPort, int[] preferredPorts) { try { Set preferredPortsSet = new HashSet(asList(preferredPorts)); int scanSize = toPort-fromPort+1; String name = toPort < 10000 ? "bot" : "program"; int threads = isWindows() ? min(500, scanSize) : min(scanSize, 10); final ExecutorService es = Executors.newFixedThreadPool(threads); if (verbose) print(firstToUpper(name) + "-scanning " + ip + " with timeout " + timeout + " ms in " + threads + " threads."); startTiming(); List < Future < Program > > futures = new ArrayList(); List ports = new ArrayList(); for (int port : preferredPorts) { futures.add(checkPort(es, ip, port, timeout)); ports.add(port); } for (int port = fromPort; port <= toPort; port++) if (!preferredPortsSet.contains(port) && !forbiddenPort(port)) { futures.add(checkPort(es, ip, port, timeout)); ports.add(port); } es.shutdown(); List programs = new ArrayList(); long time = now(); int i = 0; for (final Future f : futures) { if (verbose) print("Waiting for port " + get(ports, i++) + " at time " + (now()-time)); Program p = f.get(); if (p != null) programs.add(p); } //stopTiming("Port Scan " + scanSize + ", " + n(threads, "threads") + ": ", 250); if (verbose) print("Found " + programs.size() + " " + name + "(s) on " + ip); return programs; } catch (Exception __e) { throw rethrow(__e); } } static Future checkPort(final ExecutorService es, final String ip, final int port, final int timeout) { return es.submit(new Callable() { @Override public Program call() { try { Socket socket = new Socket(); try { socket.setSoTimeout(timeout); socket.connect(new InetSocketAddress(ip, port), timeout); //if (verbose) print("Connected to " + ip + ":" + port); BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream(), "UTF-8")); String hello = or(in.readLine(), "?"); return new Program(port, hello); } finally { socket.close(); } } catch (Exception ex) { return null; } } }); } static List quickScan() { return scan(quickScanFrom, quickScanTo); } static List quickBotScan() { return quickBotScan(new int[0]); } static List quickBotScan(int[] preferredPorts) { if (maxNumberOfVMs == 0) maxNumberOfVMs = isAndroid() ? maxNumberOfVMs_android : maxNumberOfVMs_nonAndroid; return scan(4999, 5000+maxNumberOfVMs-1, preferredPorts); } } public static boolean isWindows() { return System.getProperty("os.name").contains("Windows"); } static Thread startThread(Object runnable) { return startThread(defaultThreadName(), runnable); } static Thread startThread(String name, Object runnable) { return startThread(newThread(toRunnable(runnable), name)); } static Thread startThread(Thread t) { _registerThread(t); t.start(); return t; } static void lock(Lock lock) { try { lock.lockInterruptibly(); } catch (Exception __e) { throw rethrow(__e); } } static void lock(Lock lock, String msg) { print("Locking: " + msg); lock(lock); } static void lock(Lock lock, String msg, long timeout) { print("Locking: " + msg); lockOrFail(lock, timeout); } static ReentrantLock lock() { return fairLock(); } static long stopTiming_defaultMin = 10; static long startTiming_startTime; static void startTiming() { startTiming_startTime = now(); } static void stopTiming() { stopTiming(null); } static void stopTiming(String text) { stopTiming(text, stopTiming_defaultMin); } static void stopTiming(String text, long minToPrint) { long time = now()-startTiming_startTime; if (time >= minToPrint) { text = or2(text, "Time: "); print(text + time + " ms"); } } static String firstToUpper(String s) { if (empty(s)) return s; return Character.toUpperCase(s.charAt(0)) + s.substring(1); } // runnable = Runnable or String (method name) static Thread newThread(Object runnable) { return new Thread(_topLevelErrorHandling(toRunnable(runnable))); } static Thread newThread(Object runnable, String name) { return new Thread(_topLevelErrorHandling(toRunnable(runnable)), name); } static void lockOrFail(Lock lock, long timeout) { try { if (!lock.tryLock(timeout, TimeUnit.MILLISECONDS)) { String s = "Couldn't acquire lock after " + timeout + " ms."; if (lock instanceof ReentrantLock) { ReentrantLock l = (ReentrantLock) ( lock); s += " Hold count: " + l.getHoldCount() + ", owner: " + call(l, "getOwner"); } throw fail(s); } } catch (Exception __e) { throw rethrow(__e); } } static Runnable toRunnable(final Object o) { if (o instanceof Runnable) return (Runnable) o; return new Runnable() { public void run() { try { callF(o) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(o)"; }}; } static String defaultThreadName_name; static String defaultThreadName() { if (defaultThreadName_name == null) defaultThreadName_name = "A thread by " + programID(); return defaultThreadName_name; } static Runnable _topLevelErrorHandling(final Runnable runnable) { return new Runnable() { public void run() { try { try { runnable.run(); } catch (Throwable __e) { printStackTrace2(__e); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "pcall { runnable.run(); }"; }}; } }