Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

6399
LINES
[SHOW ALL]

< > BotCompany Repo | #1000508 // Transpilation of #722

JavaX source code - run with: x30.jar

1  
import java.security.NoSuchAlgorithmException;
2  
import java.security.MessageDigest;
3  
import java.net.*;
4  
import java.io.*;
5  
import javax.swing.*;
6  
import java.util.regex.*;
7  
import java.util.*;
8  
9  
10  
11  
import java.lang.reflect.*;
12  
import java.math.*; // BigInteger
13  
import java.text.DecimalFormat;
14  
import java.lang.management.*; // for time measurement
15  
16  
/**
17  
 JavaX runner version 18
18  
19  
 Changes to v17:
20  
 -can run server-transpiled main programs for super-quick compilation
21  
  (no local transpiling except for nested solvers or something)
22  
23  
 */
24  
25  
class _x18 {
26  
  static final String version = "JavaX 18";
27  
28  
  static boolean verbose = false, translate = false, list = false, virtualizeTranslators = true;
29  
  static String translateTo = null;
30  
  static boolean preferCached = false, safeOnly = false, noID = false, noPrefetch = false;
31  
  static List<String[]> mainTranslators = new ArrayList<String[]>();
32  
  private static Map<Long, String> memSnippetCache = new HashMap<Long, String>();
33  
  private static int processesStarted, compilations;
34  
35  
  // snippet ID -> md5
36  
  private static HashMap<Long, String> prefetched = new HashMap<Long, String>();
37  
  private static File virtCache;
38  
39  
  // doesn't work yet
40  
  private static Map<String, Class<?>> programCache = new HashMap<String, Class<?>>();
41  
  static boolean cacheTranslators = false;
42  
43  
  // this should work (caches transpiled translators)
44  
  private static HashMap<Long, Object[]> translationCache = new HashMap<Long, Object[]>();
45  
  static boolean cacheTranspiledTranslators = true;
46  
47  
  // which snippets are available pre-transpiled server-side?
48  
  private static Set<Long> hasTranspiledSet = new HashSet<Long>();
49  
  static boolean useServerTranspiled = true;
50  
51  
  static Object androidContext;
52  
  static boolean android = isAndroid();
53  
54  
  public static void main(String[] args) throws Exception {
55  
    File ioBaseDir = new File("."), inputDir = null, outputDir = null;
56  
    String src = null;
57  
    List<String> programArgs = new ArrayList<String>();
58  
59  
    for (int i = 0; i < args.length; i++) {
60  
      String arg = args[i];
61  
62  
      if (arg.equals("-version")) {
63  
        showVersion();
64  
        return;
65  
      }
66  
67  
      if (arg.equals("-sysprop")) {
68  
        showSystemProperties();
69  
        return;
70  
      }
71  
72  
      if (arg.equals("-v") || arg.equals("-verbose"))
73  
        verbose = true;
74  
      else if (arg.equals("-finderror"))
75  
        verbose = true;
76  
      else if (arg.equals("-offline") || arg.equalsIgnoreCase("-prefercached"))
77  
        preferCached = true;
78  
      else if (arg.equals("-novirt"))
79  
        virtualizeTranslators = false;
80  
      else if (arg.equals("-safeonly"))
81  
        safeOnly = true;
82  
      else if (arg.equals("-noid"))
83  
        noID = true;
84  
      else if (arg.equals("-nocachetranspiled"))
85  
        cacheTranspiledTranslators = false;
86  
      else if (arg.equals("-localtranspile"))
87  
        useServerTranspiled = false;
88  
      else if (arg.equals("translate"))
89  
        translate = true;
90  
      else if (arg.equals("list")) {
91  
        list = true;
92  
        virtualizeTranslators = false; // so they are silenced
93  
      } else if (arg.equals("run")) {
94  
        // it's the default command anyway
95  
      } else if (arg.startsWith("input="))
96  
        inputDir = new File(arg.substring(6));
97  
      else if (arg.startsWith("output="))
98  
        outputDir = new File(arg.substring(7));
99  
      else if (arg.equals("with"))
100  
        mainTranslators.add(new String[] {args[++i], null});
101  
      else if (translate && arg.equals("to"))
102  
        translateTo = args[++i];
103  
      else if (src == null) {
104  
        //System.out.println("src=" + arg);
105  
        src = arg;
106  
      } else
107  
        programArgs.add(arg);
108  
    }
109  
110  
    cleanCache();
111  
112  
    if (useServerTranspiled)
113  
      noPrefetch = true;
114  
115  
    if (src == null) src = ".";
116  
117  
    // Might actually want to write to 2 disk caches (global/per program).
118  
    if (virtualizeTranslators && !preferCached)
119  
      virtCache = TempDirMaker_make();
120  
121  
    if (inputDir != null) {
122  
      ioBaseDir = TempDirMaker_make();
123  
      System.out.println("Taking input from: " + inputDir.getAbsolutePath());
124  
      System.out.println("Output is in: " + new File(ioBaseDir, "output").getAbsolutePath());
125  
      copyInput(inputDir, new File(ioBaseDir, "input"));
126  
    }
127  
128  
    javaxmain(src, ioBaseDir, translate, list, programArgs.toArray(new String[programArgs.size()]));
129  
130  
    if (outputDir != null) {
131  
      copyInput(new File(ioBaseDir, "output"), outputDir);
132  
      System.out.println("Output copied to: " + outputDir.getAbsolutePath());
133  
    }
134  
135  
    if (verbose) {
136  
      // print stats
137  
      System.out.println("Processes started: " + processesStarted + ", compilations: " + compilations);
138  
    }
139  
  }
140  
141  
  public static void javaxmain(String src, File ioDir, boolean translate, boolean list,
142  
                               String[] args) throws Exception {
143  
    List<File> libraries = new ArrayList<File>();
144  
    File X = transpileMain(src, libraries);
145  
    if (X == null)
146  
      return;
147  
148  
    // list or run
149  
150  
    if (translate) {
151  
      File to = X;
152  
      if (translateTo != null)
153  
        if (new File(translateTo).isDirectory())
154  
          to = new File(translateTo, "main.java");
155  
        else
156  
          to = new File(translateTo);
157  
      if (to != X)
158  
        copy(new File(X, "main.java"), to);
159  
      System.out.println("Program translated to: " + to.getAbsolutePath());
160  
    } else if (list)
161  
      System.out.println(loadTextFile(new File(X, "main.java").getPath(), null));
162  
    else
163  
      javax2(X, ioDir, false, false, libraries, args, null);
164  
  }
165  
166  
  static File transpileMain(String src, List<File> libraries) throws Exception {
167  
    File srcDir;
168  
    boolean isTranspiled = false;
169  
    if (isSnippetID(src)) {
170  
      prefetch(src);
171  
      long id = parseSnippetID(src);
172  
      srcDir = loadSnippetAsMainJava(src);
173  
      if (hasTranspiledSet.contains(id)) {
174  
        System.err.println("Trying pretranspiled main program: #" + id);
175  
        String transpiledSrc = getServerTranspiled("#" + id);
176  
        if (!transpiledSrc.isEmpty()) {
177  
          srcDir = TempDirMaker_make();
178  
          saveTextFile(new File(srcDir, "main.java").getPath(), transpiledSrc);
179  
          isTranspiled = true;
180  
          //translationCache.put(id, new Object[] {srcDir, libraries});
181  
        }
182  
      }
183  
    } else {
184  
      srcDir = new File(src);
185  
186  
      // if the argument is a file, it is assumed to be main.java
187  
      if (srcDir.isFile()) {
188  
        srcDir = TempDirMaker_make();
189  
        copy(new File(src), new File(srcDir, "main.java"));
190  
      }
191  
192  
      if (!new File(srcDir, "main.java").exists()) {
193  
        showVersion();
194  
        System.out.println("No main.java found, exiting");
195  
        return null;
196  
      }
197  
    }
198  
199  
    // translate
200  
201  
    File X = srcDir;
202  
203  
    if (!isTranspiled) {
204  
      X = topLevelTranslate(X, libraries);
205  
      System.err.println("Translated " + src);
206  
207  
      // save prefetch data
208  
      if (isSnippetID(src))
209  
        savePrefetchData(src);
210  
    }
211  
    return X;
212  
  }
213  
214  
  private static void prefetch(String mainSnippetID) throws IOException {
215  
    if (noPrefetch) return;
216  
217  
    long mainID = parseSnippetID(mainSnippetID);
218  
    String s = mainID + " " + loadTextFile(new File(userHome(), ".tinybrain/prefetch/" + mainID + ".txt").getPath(), "");
219  
    String[] ids = s.trim().split(" ");
220  
    if (ids.length > 1) {
221  
      String url = "http://tinybrain.de:8080/tb-int/prefetch.php?ids=" + URLEncoder.encode(s, "UTF-8");
222  
      String data = loadPage(new URL(url));
223  
      String[] split = data.split(" ");
224  
      if (split.length == ids.length)
225  
        for (int i = 0; i < ids.length; i++)
226  
          prefetched.put(parseSnippetID(ids[i]), split[i]);
227  
    }
228  
  }
229  
230  
  static String _userHome;
231  
  static String userHome() {
232  
    if (_userHome == null) {
233  
      if (android)
234  
        _userHome = ((File) call(androidContext, "getFilesDir")).getAbsolutePath();
235  
      else
236  
        _userHome = System.getProperty("user.home");
237  
      System.out.println("userHome: " + _userHome);
238  
    }
239  
    return _userHome;
240  
  }
241  
242  
  private static void savePrefetchData(String mainSnippetID) throws IOException {
243  
    List<String> ids = new ArrayList<String>();
244  
    long mainID = parseSnippetID(mainSnippetID);
245  
246  
    for (long id : memSnippetCache.keySet())
247  
      if (id != mainID)
248  
        ids.add(String.valueOf(id));
249  
250  
    saveTextFile(new File(userHome(),".tinybrain/prefetch/" + mainID + ".txt").getPath(), join(" ", ids));
251  
  }
252  
253  
  static File topLevelTranslate(File srcDir, List<File> libraries_out) throws Exception {
254  
    File X = srcDir;
255  
    X = applyTranslators(X, mainTranslators, libraries_out); // translators supplied on command line (unusual)
256  
257  
    // actual inner translation of the JavaX source
258  
    X = defaultTranslate(X, libraries_out);
259  
    return X;
260  
  }
261  
262  
  private static File defaultTranslate(File x, List<File> libraries_out) throws Exception {
263  
    x = luaPrintToJavaPrint(x);
264  
    x = repeatAutoTranslate(x, libraries_out);
265  
    return x;
266  
  }
267  
268  
  private static File repeatAutoTranslate(File x, List<File> libraries_out) throws Exception {
269  
    while (true) {
270  
      File y = autoTranslate(x, libraries_out);
271  
      if (y == x)
272  
        return x;
273  
      x = y;
274  
    }
275  
  }
276  
277  
  private static File autoTranslate(File x, List<File> libraries_out) throws Exception {
278  
    String main = loadTextFile(new File(x, "main.java").getPath(), null);
279  
    List<String> lines = toLines(main);
280  
    List<String[]> translators = findTranslators(lines);
281  
    if (translators.isEmpty())
282  
      return x;
283  
284  
    main = fromLines(lines);
285  
    File newDir = TempDirMaker_make();
286  
    saveTextFile(new File(newDir, "main.java").getPath(), main);
287  
    return applyTranslators(newDir, translators, libraries_out);
288  
  }
289  
290  
  private static List<String[]> findTranslators(List<String> lines) {
291  
    List<String[]> translators = new ArrayList<String[]>();
292  
    Pattern pattern = Pattern.compile("^!([0-9# \t]+)");
293  
    Pattern pArgs = Pattern.compile("^\\s*\\((.*)\\)");
294  
    for (ListIterator<String> iterator = lines.listIterator(); iterator.hasNext(); ) {
295  
      String line = iterator.next();
296  
      line = line.trim();
297  
      Matcher matcher = pattern.matcher(line);
298  
      if (matcher.find()) {
299  
        String[] t = matcher.group(1).split("[ \t]+");
300  
        String rest = line.substring(matcher.end());
301  
        String arg = null;
302  
        if (t.length == 1) {
303  
          Matcher mArgs = pArgs.matcher(rest);
304  
          if (mArgs.find())
305  
            arg = mArgs.group(1);
306  
        }
307  
        for (String transi : t)
308  
          translators.add(new String[]{transi, arg});
309  
        iterator.remove();
310  
      }
311  
    }
312  
    return translators;
313  
  }
314  
315  
  public static List<String> toLines(String s) {
316  
    List<String> lines = new ArrayList<String>();
317  
    int start = 0;
318  
    while (true) {
319  
      int i = toLines_nextLineBreak(s, start);
320  
      if (i < 0) {
321  
        if (s.length() > start) lines.add(s.substring(start));
322  
        break;
323  
      }
324  
325  
      lines.add(s.substring(start, i));
326  
      if (s.charAt(i) == '\r' && i+1 < s.length() && s.charAt(i+1) == '\n')
327  
        i += 2;
328  
      else
329  
        ++i;
330  
331  
      start = i;
332  
    }
333  
    return lines;
334  
  }
335  
336  
  private static int toLines_nextLineBreak(String s, int start) {
337  
    for (int i = start; i < s.length(); i++) {
338  
      char c = s.charAt(i);
339  
      if (c == '\r' || c == '\n')
340  
        return i;
341  
    }
342  
    return -1;
343  
  }
344  
345  
  public static String fromLines(List<String> lines) {
346  
    StringBuilder buf = new StringBuilder();
347  
    for (String line : lines) {
348  
      buf.append(line).append('\n');
349  
    }
350  
    return buf.toString();
351  
  }
352  
353  
  private static File applyTranslators(File x, List<String[]> translators, List<File> libraries_out) throws Exception {
354  
    for (String[] translator : translators)
355  
      x = applyTranslator(x, translator[0], translator[1], libraries_out);
356  
    return x;
357  
  }
358  
359  
  // also takes a library
360  
  private static File applyTranslator(File x, String translator, String arg, List<File> libraries_out) throws Exception {
361  
    if (verbose)
362  
      System.out.println("Using translator " + translator + " on sources in " + x.getPath());
363  
364  
    File newDir = runTranslatorOnInput(translator, null, arg, x, !verbose, libraries_out);
365  
366  
    if (!new File(newDir, "main.java").exists()) {
367  
      throw new Exception("Translator " + translator + " did not generate main.java");
368  
      // TODO: show translator output
369  
    }
370  
    if (verbose)
371  
      System.out.println("Translated with " + translator + " from " + x.getPath() + " to " + newDir.getPath());
372  
    x = newDir;
373  
    return x;
374  
  }
375  
376  
  private static File luaPrintToJavaPrint(File x) throws IOException {
377  
    File newDir = TempDirMaker_make();
378  
    String code = loadTextFile(new File(x, "main.java").getPath(), null);
379  
    code = luaPrintToJavaPrint(code);
380  
    if (verbose)
381  
      System.out.println(code);
382  
    saveTextFile(new File(newDir, "main.java").getPath(), code);
383  
    return newDir;
384  
  }
385  
386  
  public static String luaPrintToJavaPrint(String code) {
387  
    return ("\n" + code).replaceAll(
388  
      "(\n\\s*)print (\".*\")",
389  
      "$1System.out.println($2);").substring(1);
390  
  }
391  
392  
  public static File loadSnippetAsMainJava(String snippetID) throws IOException {
393  
    checkProgramSafety(snippetID);
394  
    File srcDir = TempDirMaker_make();
395  
    saveTextFile(new File(srcDir, "main.java").getPath(), loadSnippet(snippetID));
396  
    return srcDir;
397  
  }
398  
399  
  public static File loadSnippetAsMainJavaVerified(String snippetID, String hash) throws IOException {
400  
    checkProgramSafety(snippetID);
401  
    File srcDir = TempDirMaker_make();
402  
    saveTextFile(new File(srcDir, "main.java").getPath(), loadSnippetVerified(snippetID, hash));
403  
    return srcDir;
404  
  }
405  
406  
  /** returns output dir */
407  
  private static File runTranslatorOnInput(String snippetID, String hash, String arg, File input,
408  
                                           boolean silent,
409  
                                           List<File> libraries_out) throws Exception {
410  
    long id = parseSnippetID(snippetID);
411  
    File libraryFile = DiskSnippetCache_getLibrary(id);
412  
    if (libraryFile != null) {
413  
      loadLibrary(snippetID, libraries_out, libraryFile);
414  
      return input;
415  
    }
416  
417  
    String[] args = arg != null ? new String[]{arg} : new String[0];
418  
419  
    File srcDir = hash == null ? loadSnippetAsMainJava(snippetID)
420  
      : loadSnippetAsMainJavaVerified(snippetID, hash);
421  
    long mainJavaSize = new File(srcDir, "main.java").length();
422  
423  
    if (mainJavaSize == 0) { // no text in snippet? assume it's a library
424  
      loadLibrary(snippetID, libraries_out, libraryFile);
425  
      return input;
426  
    }
427  
428  
    List<File> libraries = new ArrayList<File>();
429  
    Object[] cached = translationCache.get(id);
430  
    if (cached != null) {
431  
      //System.err.println("Taking translator " + snippetID + " from cache!");
432  
      srcDir = (File) cached[0];
433  
      libraries = (List<File>) cached[1];
434  
    } else if (hasTranspiledSet.contains(id)) {
435  
      System.err.println("Trying pretranspiled translator: #" + snippetID);
436  
      String transpiledSrc = getServerTranspiled(snippetID);
437  
      if (!transpiledSrc.isEmpty()) {
438  
        srcDir = TempDirMaker_make();
439  
        saveTextFile(new File(srcDir, "main.java").getPath(), transpiledSrc);
440  
        translationCache.put(id, cached = new Object[] {srcDir, libraries});
441  
      }
442  
    }
443  
444  
    File ioBaseDir = TempDirMaker_make();
445  
446  
    /*Class<?> mainClass = programCache.get("" + parseSnippetID(snippetID));
447  
    if (mainClass != null)
448  
      return runCached(ioBaseDir, input, args);*/
449  
    // Doesn't work yet because virtualized directories are hardcoded in translator...
450  
451  
    if (cached == null) {
452  
      System.err.println("Translating translator #" + id);
453  
      srcDir = defaultTranslate(srcDir, libraries);
454  
      System.err.println("Translated translator #" + id);
455  
      if (cacheTranspiledTranslators)
456  
        translationCache.put(id, new Object[]{srcDir, libraries});
457  
    }
458  
459  
    boolean runInProcess = false;
460  
461  
    if (virtualizeTranslators) {
462  
      if (verbose) System.out.println("Virtualizing translator");
463  
464  
      //srcDir = applyTranslator(srcDir, "#2000351"); // I/O-virtualize the translator
465  
      // that doesn't work because it recurses infinitely...
466  
467  
      // So we do it right here:
468  
      String s = loadTextFile(new File(srcDir, "main.java").getPath(), null);
469  
      s = s.replaceAll("new\\s+File\\(", "virtual.newFile(");
470  
      s = s.replaceAll("new\\s+FileInputStream\\(", "virtual.newFileInputStream(");
471  
      s = s.replaceAll("new\\s+FileOutputStream\\(", "virtual.newFileOutputStream(");
472  
      s += "\n\n" + loadSnippet("#2000355"); // load class virtual
473  
474  
      // change baseDir
475  
      s = s.replace("virtual_baseDir = \"\";",
476  
        "virtual_baseDir = " + javaQuote(ioBaseDir.getAbsolutePath()) + ";");
477  
478  
      // forward snippet cache (virtualized one)
479  
      File dir = virtCache != null ? virtCache : DiskSnippetCache_dir;
480  
      s = s.replace("static File DiskSnippetCache_dir;",
481  
        "static File DiskSnippetCache_dir = new File(" + javaQuote(dir.getAbsolutePath()) + ");");
482  
      s = s.replace("static boolean preferCached = false;", "static boolean preferCached = true;");
483  
484  
      if (verbose) {
485  
        System.out.println("==BEGIN VIRTUALIZED TRANSLATOR==");
486  
        System.out.println(s);
487  
        System.out.println("==END VIRTUALIZED TRANSLATOR==");
488  
      }
489  
      srcDir = TempDirMaker_make();
490  
      saveTextFile(new File(srcDir, "main.java").getPath(), s);
491  
492  
      // TODO: silence translator also
493  
      runInProcess = true;
494  
    }
495  
496  
    return runJavaX(ioBaseDir, srcDir, input, silent, runInProcess, libraries,
497  
      args, cacheTranslators ? "" + id : null);
498  
  }
499  
500  
  private static String getServerTranspiled(String snippetID) throws IOException {
501  
    long id = parseSnippetID(snippetID);
502  
    URL url = new URL("http://tinybrain.de:8080/tb-int/get-transpiled.php?raw=1&id=" + id);
503  
    return loadPage(url);
504  
  }
505  
506  
  static void checkProgramSafety(String snippetID) throws IOException {
507  
    if (!safeOnly) return;
508  
    URL url = new URL("http://tinybrain.de:8080/tb-int/is-javax-safe.php?id=" + parseSnippetID(snippetID));
509  
    String text = loadPage(url);
510  
    if (!text.startsWith("{\"safe\":\"1\"}"))
511  
      throw new RuntimeException("Translator not safe: #" + parseSnippetID(snippetID));
512  
  }
513  
514  
  private static void loadLibrary(String snippetID, List<File> libraries_out, File libraryFile) throws IOException {
515  
    if (verbose)
516  
      System.out.println("Assuming " + snippetID + " is a library.");
517  
518  
    if (libraryFile == null) {
519  
      byte[] data = loadDataSnippetImpl(snippetID);
520  
      DiskSnippetCache_putLibrary(parseSnippetID(snippetID), data);
521  
      libraryFile = DiskSnippetCache_getLibrary(parseSnippetID(snippetID));
522  
    }
523  
524  
    if (!libraries_out.contains(libraryFile))
525  
      libraries_out.add(libraryFile);
526  
  }
527  
528  
  private static byte[] loadDataSnippetImpl(String snippetID) throws IOException {
529  
    byte[] data;
530  
    try {
531  
      URL url = new URL("http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_"
532  
        + parseSnippetID(snippetID) + "&contentType=application/binary");
533  
      System.err.println("Loading library: " + url);
534  
      data = loadBinaryPage(url.openConnection());
535  
      if (verbose)
536  
        System.err.println("Bytes loaded: " + data.length);
537  
    } catch (FileNotFoundException e) {
538  
      throw new IOException("Binary snippet #" + snippetID + " not found or not public");
539  
    }
540  
    return data;
541  
  }
542  
543  
  /** returns output dir */
544  
  private static File runJavaX(File ioBaseDir, File originalSrcDir, File originalInput,
545  
                               boolean silent, boolean runInProcess,
546  
                               List<File> libraries, String[] args, String cacheAs) throws Exception {
547  
    File srcDir = new File(ioBaseDir, "src");
548  
    File inputDir = new File(ioBaseDir, "input");
549  
    File outputDir = new File(ioBaseDir, "output");
550  
    copyInput(originalSrcDir, srcDir);
551  
    copyInput(originalInput, inputDir);
552  
    javax2(srcDir, ioBaseDir, silent, runInProcess, libraries, args, cacheAs);
553  
    return outputDir;
554  
  }
555  
556  
  private static void copyInput(File src, File dst) throws IOException {
557  
    copyDirectory(src, dst);
558  
  }
559  
560  
  public static boolean hasFile(File inputDir, String name) {
561  
    return new File(inputDir, name).exists();
562  
  }
563  
564  
  public static void copyDirectory(File src, File dst) throws IOException {
565  
    if (verbose) System.out.println("Copying " + src.getAbsolutePath() + " to " + dst.getAbsolutePath());
566  
    dst.mkdirs();
567  
    File[] files = src.listFiles();
568  
    if (files == null) return;
569  
    for (File file : files) {
570  
      File dst1 = new File(dst, file.getName());
571  
      if (file.isDirectory())
572  
        copyDirectory(file, dst1);
573  
      else {
574  
        if (verbose) System.out.println("Copying " + file.getAbsolutePath() + " to " + dst1.getAbsolutePath());
575  
        copy(file, dst1);
576  
      }
577  
    }
578  
  }
579  
580  
  /** Quickly copy a file without a progress bar or any other fancy GUI... :) */
581  
  public static void copy(File src, File dest) throws IOException {
582  
    FileInputStream inputStream = newFileInputStream(src);
583  
    FileOutputStream outputStream = newFileOutputStream(dest);
584  
    try {
585  
      copy(inputStream, outputStream);
586  
      inputStream.close();
587  
    } finally {
588  
      outputStream.close();
589  
    }
590  
  }
591  
592  
  static Object call(Object o, String method, Object... args) {
593  
    try {
594  
      Method m = call_findMethod(o, method, args, false);
595  
      m.setAccessible(true);
596  
      return m.invoke(o, args);
597  
    } catch (Exception e) {
598  
      throw new RuntimeException(e);
599  
    }
600  
  }
601  
602  
  static Object call(Class c, String method, Object... args) {
603  
    try {
604  
      Method m = call_findStaticMethod(c, method, args, false);
605  
      m.setAccessible(true);
606  
      return m.invoke(null, args);
607  
    } catch (Exception e) {
608  
      throw new RuntimeException(e);
609  
    }
610  
  }
611  
612  
  static Method call_findStaticMethod(Class c, String method, Object[] args, boolean debug) {
613  
    while (c != null) {
614  
      for (Method m : c.getDeclaredMethods()) {
615  
        if (debug)
616  
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
617  
        if (!m.getName().equals(method)) {
618  
          if (debug) System.out.println("Method name mismatch: " + method);
619  
          continue;
620  
        }
621  
622  
        if ((m.getModifiers() & Modifier.STATIC) == 0 || !call_checkArgs(m, args, debug))
623  
          continue;
624  
625  
        return m;
626  
      }
627  
      c = c.getSuperclass();
628  
    }
629  
    throw new RuntimeException("Method '" + method + "' (static) with " + args.length + " parameter(s) not found in " + c.getName());
630  
  }
631  
632  
  static Method call_findMethod(Object o, String method, Object[] args, boolean debug) {
633  
    Class c = o.getClass();
634  
    while (c != null) {
635  
      for (Method m : c.getDeclaredMethods()) {
636  
        if (debug)
637  
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
638  
        if (m.getName().equals(method) && call_checkArgs(m, args, debug))
639  
          return m;
640  
      }
641  
      c = c.getSuperclass();
642  
    }
643  
    throw new RuntimeException("Method '" + method + "' (non-static) with " + args.length + " parameter(s) not found in " + o.getClass().getName());
644  
  }
645  
646  
  private static boolean call_checkArgs(Method m, Object[] args, boolean debug) {
647  
    Class<?>[] types = m.getParameterTypes();
648  
    if (types.length != args.length) {
649  
      if (debug)
650  
        System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
651  
      return false;
652  
    }
653  
    for (int i = 0; i < types.length; i++)
654  
      if (!(args[i] == null || types[i].isInstance(args[i]))) {
655  
        if (debug)
656  
          System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
657  
        return false;
658  
      }
659  
    return true;
660  
  }
661  
662  
  private static FileInputStream newFileInputStream(File f) throws FileNotFoundException {
663  
    /*if (androidContext != null)
664  
      return (FileInputStream) call(androidContext,
665  
        "openFileInput", f.getPath());
666  
    else*/
667  
    return new FileInputStream(f);
668  
  }
669  
670  
  private static FileOutputStream newFileOutputStream(File f) throws FileNotFoundException {
671  
    /*if (androidContext != null)
672  
      return (FileOutputStream) call(androidContext,
673  
        "openFileOutput", f.getPath(), 0);
674  
    else*/
675  
    return new FileOutputStream(f);
676  
  }
677  
678  
  public static void copy(InputStream in, OutputStream out) throws IOException {
679  
    byte[] buf = new byte[65536];
680  
    while (true) {
681  
      int n = in.read(buf);
682  
      if (n <= 0) return;
683  
      out.write(buf, 0, n);
684  
    }
685  
  }
686  
687  
  /** writes safely (to temp file, then rename) */
688  
  public static void saveTextFile(String fileName, String contents) throws IOException {
689  
    File file = new File(fileName);
690  
    File parentFile = file.getParentFile();
691  
    if (parentFile != null)
692  
      parentFile.mkdirs();
693  
    String tempFileName = fileName + "_temp";
694  
    FileOutputStream fileOutputStream = newFileOutputStream(new File(tempFileName));
695  
    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, charsetForTextFiles);
696  
    PrintWriter printWriter = new PrintWriter(outputStreamWriter);
697  
    printWriter.print(contents);
698  
    printWriter.close();
699  
    if (file.exists() && !file.delete())
700  
      throw new IOException("Can't delete " + fileName);
701  
702  
    if (!new File(tempFileName).renameTo(file))
703  
      throw new IOException("Can't rename " + tempFileName + " to " + fileName);
704  
  }
705  
706  
  /** writes safely (to temp file, then rename) */
707  
  public static void saveBinaryFile(String fileName, byte[] contents) throws IOException {
708  
    File file = new File(fileName);
709  
    File parentFile = file.getParentFile();
710  
    if (parentFile != null)
711  
      parentFile.mkdirs();
712  
    String tempFileName = fileName + "_temp";
713  
    FileOutputStream fileOutputStream = newFileOutputStream(new File(tempFileName));
714  
    fileOutputStream.write(contents);
715  
    fileOutputStream.close();
716  
    if (file.exists() && !file.delete())
717  
      throw new IOException("Can't delete " + fileName);
718  
719  
    if (!new File(tempFileName).renameTo(file))
720  
      throw new IOException("Can't rename " + tempFileName + " to " + fileName);
721  
  }
722  
723  
  public static String loadTextFile(String fileName, String defaultContents) throws IOException {
724  
    if (!new File(fileName).exists())
725  
      return defaultContents;
726  
727  
    FileInputStream fileInputStream = newFileInputStream(new File(fileName));
728  
    InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, charsetForTextFiles);
729  
    return loadTextFile(inputStreamReader, (int) new File(fileName).length());
730  
  }
731  
732  
  public static String loadTextFile(Reader reader, int length) throws IOException {
733  
    try {
734  
      char[] chars = new char[length];
735  
      int n = reader.read(chars);
736  
      return new String(chars, 0, n);
737  
    } finally {
738  
      reader.close();
739  
    }
740  
  }
741  
742  
  static File DiskSnippetCache_dir;
743  
744  
  public static void initDiskSnippetCache(File dir) {
745  
    DiskSnippetCache_dir = dir;
746  
    dir.mkdirs();
747  
  }
748  
749  
  // Data files are immutable, use centralized cache
750  
  public static synchronized File DiskSnippetCache_getLibrary(long snippetID) throws IOException {
751  
    File file = new File(getGlobalCache(), "data_" + snippetID + ".jar");
752  
    if (verbose)
753  
      System.out.println("Checking data cache: " + file.getPath());
754  
    return file.exists() ? file : null;
755  
  }
756  
757  
  public static synchronized String DiskSnippetCache_get(long snippetID) throws IOException {
758  
    return loadTextFile(DiskSnippetCache_getFile(snippetID).getPath(), null);
759  
  }
760  
761  
  private static File DiskSnippetCache_getFile(long snippetID) {
762  
    return new File(DiskSnippetCache_dir, "" + snippetID);
763  
  }
764  
765  
  public static synchronized void DiskSnippetCache_put(long snippetID, String snippet) throws IOException {
766  
    saveTextFile(DiskSnippetCache_getFile(snippetID).getPath(), snippet);
767  
  }
768  
769  
  public static synchronized void DiskSnippetCache_putLibrary(long snippetID, byte[] data) throws IOException {
770  
    saveBinaryFile(new File(getGlobalCache(), "data_" + snippetID).getPath() + ".jar", data);
771  
  }
772  
773  
  public static File DiskSnippetCache_getDir() {
774  
    return DiskSnippetCache_dir;
775  
  }
776  
777  
  public static void initSnippetCache() {
778  
    if (DiskSnippetCache_dir == null)
779  
      initDiskSnippetCache(getGlobalCache());
780  
  }
781  
782  
  private static File getGlobalCache() {
783  
    File file = new File(userHome(), ".tinybrain/snippet-cache");
784  
    file.mkdirs();
785  
    return file;
786  
  }
787  
788  
  public static String loadSnippetVerified(String snippetID, String hash) throws IOException {
789  
    String text = loadSnippet(snippetID);
790  
    String realHash = getHash(text.getBytes("UTF-8"));
791  
    if (!realHash.equals(hash)) {
792  
      String msg;
793  
      if (hash.isEmpty())
794  
        msg = "Here's your hash for " + snippetID + ", please put in your program: " + realHash;
795  
      else
796  
        msg = "Hash mismatch for " + snippetID + ": " + realHash + " (new) vs " + hash + " - has tinybrain.de been hacked??";
797  
      throw new RuntimeException(msg);
798  
    }
799  
    return text;
800  
  }
801  
802  
  public static String getHash(byte[] data) {
803  
    return bytesToHex(getFullFingerprint(data));
804  
  }
805  
806  
  public static byte[] getFullFingerprint(byte[] data) {
807  
    try {
808  
      return MessageDigest.getInstance("MD5").digest(data);
809  
    } catch (NoSuchAlgorithmException e) {
810  
      throw new RuntimeException(e);
811  
    }
812  
  }
813  
814  
  public static String bytesToHex(byte[] bytes) {
815  
    return bytesToHex(bytes, 0, bytes.length);
816  
  }
817  
818  
  public static String bytesToHex(byte[] bytes, int ofs, int len) {
819  
    StringBuilder stringBuilder = new StringBuilder(len*2);
820  
    for (int i = 0; i < len; i++) {
821  
      String s = "0" + Integer.toHexString(bytes[ofs+i]);
822  
      stringBuilder.append(s.substring(s.length()-2, s.length()));
823  
    }
824  
    return stringBuilder.toString();
825  
  }
826  
827  
  public static String loadSnippet(String snippetID) throws IOException {
828  
    return loadSnippet(parseSnippetID(snippetID));
829  
  }
830  
831  
  public static long parseSnippetID(String snippetID) {
832  
    return Long.parseLong(shortenSnippetID(snippetID));
833  
  }
834  
835  
  private static String shortenSnippetID(String snippetID) {
836  
    if (snippetID.startsWith("#"))
837  
      snippetID = snippetID.substring(1);
838  
    String httpBlaBla = "http://tinybrain.de/";
839  
    if (snippetID.startsWith(httpBlaBla))
840  
      snippetID = snippetID.substring(httpBlaBla.length());
841  
    return snippetID;
842  
  }
843  
844  
  public static boolean isSnippetID(String snippetID) {
845  
    snippetID = shortenSnippetID(snippetID);
846  
    return isInteger(snippetID) && Long.parseLong(snippetID) != 0;
847  
  }
848  
849  
  public static boolean isInteger(String s) {
850  
    return Pattern.matches("\\-?\\d+", s);
851  
  }
852  
853  
  public static String loadSnippet(long snippetID) throws IOException {
854  
    String text = memSnippetCache.get(snippetID);
855  
    if (text != null)
856  
      return text;
857  
858  
    initSnippetCache();
859  
    text = DiskSnippetCache_get(snippetID);
860  
    if (preferCached && text != null)
861  
      return text;
862  
863  
    String md5 = text != null ? md5(text) : "-";
864  
    if (text != null) {
865  
      String hash = prefetched.get(snippetID);
866  
      if (hash != null) {
867  
        if (md5.equals(hash)) {
868  
          memSnippetCache.put(snippetID, text);
869  
          return text;
870  
        } else
871  
          prefetched.remove(snippetID); // (maybe this is not necessary)
872  
      }
873  
    }
874  
875  
    try {
876  
      /*URL url = new URL("http://tinybrain.de:8080/getraw.php?id=" + snippetID);
877  
      text = loadPage(url);*/
878  
      String theURL = "http://tinybrain.de:8080/getraw.php?id=" + snippetID + "&getmd5=1&utf8=1&usetranspiled=1";
879  
      if (text != null) {
880  
        //System.err.println("MD5: " + md5);
881  
        theURL += "&md5=" + md5;
882  
      }
883  
      URL url = new URL(theURL);
884  
      String page = loadPage(url);
885  
886  
      // parse & drop transpilation flag available line
887  
      int i = page.indexOf('\n');
888  
      boolean hasTranspiled = page.substring(0, i).trim().equals("1");
889  
      if (hasTranspiled)
890  
        hasTranspiledSet.add(snippetID);
891  
      else
892  
        hasTranspiledSet.remove(snippetID);
893  
      page = page.substring(i+1);
894  
895  
      if (page.startsWith("==*#*==")) {
896  
        // same, keep text
897  
        //System.err.println("Snippet unchanged, keeping.");
898  
      } else {
899  
        // drop md5 line
900  
        i = page.indexOf('\n');
901  
        String hash = page.substring(0, i).trim();
902  
        text = page.substring(i+1);
903  
904  
        String myHash = md5(text);
905  
        if (myHash.equals(hash)) {
906  
          //System.err.println("Hash match: " + hash);
907  
        } else
908  
          System.err.println("Hash mismatch");
909  
      }
910  
    } catch (FileNotFoundException e) {
911  
      e.printStackTrace();
912  
      throw new IOException("Snippet #" + snippetID + " not found or not public");
913  
    }
914  
915  
    memSnippetCache.put(snippetID, text);
916  
917  
    try {
918  
      initSnippetCache();
919  
      DiskSnippetCache_put(snippetID, text);
920  
    } catch (IOException e) {
921  
      System.err.println("Minor warning: Couldn't save snippet to cache ("  + DiskSnippetCache_getDir() + ")");
922  
    }
923  
924  
    return text;
925  
  }
926  
927  
  private static String md5(String text) {
928  
    try {
929  
      return bytesToHex(md5impl(text.getBytes("UTF-8"))); // maybe different than the way PHP does it...
930  
    } catch (UnsupportedEncodingException e) {
931  
      throw new RuntimeException(e);
932  
    }
933  
  }
934  
935  
  public static byte[] md5impl(byte[] data) {
936  
    try {
937  
      return MessageDigest.getInstance("MD5").digest(data);
938  
    } catch (NoSuchAlgorithmException e) {
939  
      throw new RuntimeException(e);
940  
    }
941  
  }
942  
943  
  private static String loadPage(URL url) throws IOException {
944  
    System.err.println("Loading: " + url.toExternalForm());
945  
    URLConnection con = url.openConnection();
946  
    return loadPage(con, url);
947  
  }
948  
949  
  public static String loadPage(URLConnection con, URL url) throws IOException {
950  
    setHeaders(con);
951  
    String contentType = con.getContentType();
952  
    if (contentType == null)
953  
      throw new IOException("Page could not be read: " + url);
954  
    //Log.info("Content-Type: " + contentType);
955  
    String charset = guessCharset(contentType);
956  
    //System.err.println("Charset: " + charset);
957  
    Reader r = new InputStreamReader(con.getInputStream(), charset);
958  
    StringBuilder buf = new StringBuilder();
959  
    while (true) {
960  
      int ch = r.read();
961  
      if (ch < 0)
962  
        break;
963  
      //Log.info("Chars read: " + buf.length());
964  
      buf.append((char) ch);
965  
    }
966  
    return buf.toString();
967  
  }
968  
969  
  public static byte[] loadBinaryPage(URLConnection con) throws IOException {
970  
    setHeaders(con);
971  
    return loadBinaryPage_noHeaders(con);
972  
  }
973  
974  
  private static byte[] loadBinaryPage_noHeaders(URLConnection con) throws IOException {
975  
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
976  
    InputStream inputStream = con.getInputStream();
977  
    while (true) {
978  
      int ch = inputStream.read();
979  
      if (ch < 0)
980  
        break;
981  
      buf.write(ch);
982  
    }
983  
    inputStream.close();
984  
    return buf.toByteArray();
985  
  }
986  
987  
  private static void setHeaders(URLConnection con) throws IOException {
988  
    String computerID = getComputerID();
989  
    if (computerID != null)
990  
      con.setRequestProperty("X-ComputerID", computerID);
991  
  }
992  
993  
  public static String guessCharset(String contentType) {
994  
    Pattern p = Pattern.compile("text/html;\\s+charset=([^\\s]+)\\s*");
995  
    Matcher m = p.matcher(contentType);
996  
    /* If Content-Type doesn't match this pre-conception, choose default and hope for the best. */
997  
    return m.matches() ? m.group(1) : "ISO-8859-1";
998  
  }
999  
1000  
  /** runs a transpiled set of sources */
1001  
  public static void javax2(File srcDir, File ioBaseDir, boolean silent, boolean runInProcess,
1002  
                            List<File> libraries, String[] args, String cacheAs) throws Exception {
1003  
    if (android)
1004  
      javax2android(srcDir, args);
1005  
    else {
1006  
      File classesDir = TempDirMaker_make();
1007  
      String javacOutput = compileJava(srcDir, libraries, classesDir);
1008  
1009  
      // run
1010  
1011  
      if (verbose) System.out.println("Running program (" + srcDir.getAbsolutePath()
1012  
        + ") on io dir " + ioBaseDir.getAbsolutePath() + (runInProcess ? "[in-process]" : "") + "\n");
1013  
      runProgram(javacOutput, classesDir, ioBaseDir, silent, runInProcess, libraries, args, cacheAs);
1014  
    }
1015  
  }
1016  
1017  
  static void javax2android(File srcDir, String[] args) throws Exception {
1018  
    // TODO: optimize if it's a loaded snippet anyway
1019  
    URL url = new URL("http://tinybrain.de:8080/dexcompile.php");
1020  
    URLConnection conn = url.openConnection();
1021  
    String postData = "src=" + URLEncoder.encode(loadTextFile(new File(srcDir, "main.java").getPath(), null), "UTF-8");
1022  
    byte[] dexData = doPostBinary(postData, conn, url);
1023  
    if (!isDex(dexData))
1024  
      throw new RuntimeException("Dex generation error: " + dexData.length + " bytes - " + new String(dexData, "UTF-8"));
1025  
    System.out.println("Dex loaded: " + dexData.length + "b");
1026  
1027  
    File dexDir = TempDirMaker_make();
1028  
    File dexFile = new File(dexDir, System.currentTimeMillis() + ".dex");
1029  
    File dexOutputDir = TempDirMaker_make();
1030  
1031  
    System.out.println("Saving dex to: " + dexDir.getAbsolutePath());
1032  
    try {
1033  
      saveBinaryFile(dexFile.getPath(), dexData);
1034  
    } catch (Throwable e) {
1035  
      System.out.println("Whoa!");
1036  
      throw new RuntimeException(e);
1037  
    }
1038  
1039  
    System.out.println("Getting parent class loader.");
1040  
    ClassLoader parentClassLoader =
1041  
      //ClassLoader.getSystemClassLoader(); // does not find support jar
1042  
      //getClass().getClassLoader(); // Let's try this...
1043  
      _x18.class.getClassLoader().getParent(); // XXX !
1044  
1045  
    System.out.println("Making DexClassLoader.");
1046  
    //DexClassLoader classLoader = new DexClassLoader(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null,
1047  
    //  parentClassLoader);
1048  
    Class dcl = Class.forName("dalvik.system.DexClassLoader");
1049  
    Object classLoader = dcl.getConstructors()[0].newInstance(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null,
1050  
      parentClassLoader);
1051  
1052  
    System.out.println("Loading main class.");
1053  
    //Class<?> theClass = classLoader.loadClass(mainClassName);
1054  
    Class<?> theClass = (Class<?>) call(classLoader, "loadClass", "main");
1055  
1056  
    System.out.println("Main class loaded.");
1057  
    try {
1058  
      set(theClass, "androidContext", androidContext);
1059  
    } catch (Throwable e) {}
1060  
1061  
    Method main = null;
1062  
    try {
1063  
      main = call_findStaticMethod(theClass, "main", new Object[]{androidContext}, false);
1064  
    } catch (RuntimeException e) {
1065  
    }
1066  
1067  
    System.out.println("main method for " + androidContext + " of " + theClass + ": " + main);
1068  
1069  
    if (main != null) {
1070  
      // old style main program that returns a View
1071  
      System.out.println("Calling main (old-style)");
1072  
      Object view = main.invoke(null, androidContext);
1073  
      System.out.println("Calling setContentView with " + view);
1074  
      call(Class.forName("main"), "setContentViewInUIThread", view);
1075  
      //call(androidContext, "setContentView", view);
1076  
      System.out.println("Done.");
1077  
    } else {
1078  
      System.out.println("New-style main method running.\n\n====\n");
1079  
      runMainMethod(args, theClass);
1080  
    }
1081  
  }
1082  
1083  
  static byte[] DEX_FILE_MAGIC = { 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 };
1084  
1085  
  static boolean isDex(byte[] dexData) {
1086  
    if (dexData.length < DEX_FILE_MAGIC.length) return false;
1087  
    for (int i = 0; i < DEX_FILE_MAGIC.length; i++)
1088  
      if (dexData[i] != DEX_FILE_MAGIC[i])
1089  
        return false;
1090  
    return true;
1091  
  }
1092  
1093  
  static byte[] doPostBinary(String urlParameters, URLConnection conn, URL url) throws IOException {
1094  
    // connect and do POST
1095  
    setHeaders(conn);
1096  
    conn.setDoOutput(true);
1097  
1098  
    OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
1099  
    writer.write(urlParameters);
1100  
    writer.flush();
1101  
1102  
    byte[] contents = loadBinaryPage_noHeaders(conn);
1103  
    writer.close();
1104  
    return contents;
1105  
  }
1106  
1107  
  static String compileJava(File srcDir, List<File> libraries, File classesDir) throws IOException {
1108  
    ++compilations;
1109  
1110  
    // collect sources
1111  
1112  
    List<File> sources = new ArrayList<File>();
1113  
    if (verbose) System.out.println("Scanning for sources in " + srcDir.getPath());
1114  
    scanForSources(srcDir, sources, true);
1115  
    if (sources.isEmpty())
1116  
      throw new IOException("No sources found");
1117  
1118  
    // compile
1119  
1120  
    File optionsFile = File.createTempFile("javax", "");
1121  
    if (verbose) System.out.println("Compiling " + sources.size() + " source(s) to " + classesDir.getPath());
1122  
    String options = "-d " + bashQuote(classesDir.getPath());
1123  
    writeOptions(sources, libraries, optionsFile, options);
1124  
    classesDir.mkdirs();
1125  
    return invokeJavac(optionsFile);
1126  
  }
1127  
1128  
  private static void runProgram(String javacOutput, File classesDir, File ioBaseDir,
1129  
                                 boolean silent, boolean runInProcess,
1130  
                                 List<File> libraries, String[] args, String cacheAs) throws Exception {
1131  
    // print javac output if compile failed and it hasn't been printed yet
1132  
    boolean didNotCompile = !didCompile(classesDir);
1133  
    if (verbose || didNotCompile)
1134  
      System.out.println(javacOutput);
1135  
    if (didNotCompile)
1136  
      return;
1137  
1138  
    if (runInProcess
1139  
      || (ioBaseDir.getAbsolutePath().equals(new File(".").getAbsolutePath()) && !silent)) {
1140  
      runProgramQuick(classesDir, libraries, args, cacheAs);
1141  
      return;
1142  
    }
1143  
1144  
    boolean echoOK = false;
1145  
    // TODO: add libraries to class path
1146  
    String bashCmd = "(cd " + bashQuote(ioBaseDir.getAbsolutePath()) + " && (java -cp "
1147  
      + bashQuote(classesDir.getAbsolutePath()) + " main" + (echoOK ? "; echo ok" : "") + "))";
1148  
    if (verbose) System.out.println(bashCmd);
1149  
    String output = backtick(bashCmd);
1150  
    if (verbose || !silent)
1151  
      System.out.println(output);
1152  
  }
1153  
1154  
  static boolean didCompile(File classesDir) {
1155  
    return hasFile(classesDir, "main.class");
1156  
  }
1157  
1158  
  private static void runProgramQuick(File classesDir, List<File> libraries,
1159  
                                      String[] args, String cacheAs) throws Exception {
1160  
    // collect urls
1161  
    URL[] urls = new URL[libraries.size()+1];
1162  
    urls[0] = classesDir.toURI().toURL();
1163  
    for (int i = 0; i < libraries.size(); i++)
1164  
      urls[i+1] = libraries.get(i).toURI().toURL();
1165  
1166  
    // make class loader
1167  
    URLClassLoader classLoader = new URLClassLoader(urls);
1168  
1169  
    // load JavaX main class
1170  
    Class<?> mainClass = classLoader.loadClass("main");
1171  
1172  
    if (cacheAs != null)
1173  
      programCache.put(cacheAs, mainClass);
1174  
1175  
    runMainMethod(args, mainClass);
1176  
  }
1177  
1178  
1179  
  static void runMainMethod(Object args, Class<?> mainClass) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
1180  
    Method main = mainClass.getMethod("main", String[].class);
1181  
    main.invoke(null, args);
1182  
  }
1183  
1184  
  private static String invokeJavac(File optionsFile) throws IOException {
1185  
    String output;
1186  
    try {
1187  
      output = invokeEcj(optionsFile);
1188  
    } catch (Exception e) {
1189  
      if (verbose) {
1190  
        System.err.println("ecj not found or misconfigured - using javac");
1191  
        e.printStackTrace();
1192  
      }
1193  
      output = backtick("javac " + bashQuote("@" + optionsFile.getPath()));
1194  
    }
1195  
    if (verbose) System.out.println(output);
1196  
    return output;
1197  
  }
1198  
1199  
  // throws ClassNotFoundException if ecj is not in classpath
1200  
  static String invokeEcj(File optionsFile) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
1201  
    Class batchCompiler = getEclipseCompiler();
1202  
1203  
    StringWriter writer = new StringWriter();
1204  
    PrintWriter printWriter = new PrintWriter(writer);
1205  
1206  
    // add more eclipse options in the line below
1207  
1208  
    String[] args = { "@" + optionsFile.getPath(),
1209  
      "-source", "1.7",
1210  
      "-nowarn"
1211  
    };
1212  
    Method compile = batchCompiler.getDeclaredMethod("compile", args.getClass(), PrintWriter.class, PrintWriter.class,
1213  
      Class.forName("org.eclipse.jdt.core.compiler.CompilationProgress"));
1214  
    compile.invoke(null, args, printWriter, printWriter, null);
1215  
    return writer.toString();
1216  
  }
1217  
1218  
  static Class<?> getEclipseCompiler() throws ClassNotFoundException {
1219  
    return Class.forName("org.eclipse.jdt.core.compiler.batch.BatchCompiler");
1220  
  }
1221  
1222  
  private static void writeOptions(List<File> sources, List<File> libraries,
1223  
                                   File optionsFile, String moreOptions) throws IOException {
1224  
    FileWriter writer = new FileWriter(optionsFile);
1225  
    for (File source : sources)
1226  
      writer.write(bashQuote(source.getPath()) + " ");
1227  
    if (!libraries.isEmpty()) {
1228  
      List<String> cp = new ArrayList<String>();
1229  
      for (File lib : libraries)
1230  
        cp.add(lib.getAbsolutePath());
1231  
      writer.write("-cp " + bashQuote(join(File.pathSeparator, cp)) + " ");
1232  
    }
1233  
    writer.write(moreOptions);
1234  
    writer.close();
1235  
  }
1236  
1237  
  static void scanForSources(File source, List<File> sources, boolean topLevel) {
1238  
    if (source.isFile() && source.getName().endsWith(".java"))
1239  
      sources.add(source);
1240  
    else if (source.isDirectory() && !isSkippedDirectoryName(source.getName(), topLevel)) {
1241  
      File[] files = source.listFiles();
1242  
      for (File file : files)
1243  
        scanForSources(file, sources, false);
1244  
    }
1245  
  }
1246  
1247  
  private static boolean isSkippedDirectoryName(String name, boolean topLevel) {
1248  
    if (topLevel) return false; // input or output ok as highest directory (intentionally specified by user, not just found by a directory scan in which case we probably don't want it. it's more like heuristics actually.)
1249  
    return name.equalsIgnoreCase("input") || name.equalsIgnoreCase("output");
1250  
  }
1251  
1252  
  public static String backtick(String cmd) throws IOException {
1253  
    ++processesStarted;
1254  
    File outFile = File.createTempFile("_backtick", "");
1255  
    File scriptFile = File.createTempFile("_backtick", isWindows() ? ".bat" : "");
1256  
1257  
    String command = cmd + ">" + bashQuote(outFile.getPath()) + " 2>&1";
1258  
    //Log.info("[Backtick] " + command);
1259  
    try {
1260  
      saveTextFile(scriptFile.getPath(), command);
1261  
      String[] command2;
1262  
      if (isWindows())
1263  
        command2 = new String[] { scriptFile.getPath() };
1264  
      else
1265  
        command2 = new String[] { "/bin/bash", scriptFile.getPath() };
1266  
      Process process = Runtime.getRuntime().exec(command2);
1267  
      try {
1268  
        process.waitFor();
1269  
      } catch (InterruptedException e) {
1270  
        throw new RuntimeException(e);
1271  
      }
1272  
      process.exitValue();
1273  
      return loadTextFile(outFile.getPath(), "");
1274  
    } finally {
1275  
      scriptFile.delete();
1276  
    }
1277  
  }
1278  
1279  
  /** possibly improvable */
1280  
  public static String javaQuote(String text) {
1281  
    return bashQuote(text);
1282  
  }
1283  
1284  
  /** possibly improvable */
1285  
  public static String bashQuote(String text) {
1286  
    if (text == null) return null;
1287  
    return "\"" + text
1288  
      .replace("\\", "\\\\")
1289  
      .replace("\"", "\\\"")
1290  
      .replace("\n", "\\n")
1291  
      .replace("\r", "\\r") + "\"";
1292  
  }
1293  
1294  
  public final static String charsetForTextFiles = "UTF8";
1295  
1296  
  static long TempDirMaker_lastValue;
1297  
1298  
  public static File TempDirMaker_make() {
1299  
    File dir = new File(userHome(), ".javax/" + TempDirMaker_newValue());
1300  
    dir.mkdirs();
1301  
    return dir;
1302  
  }
1303  
1304  
  private static long TempDirMaker_newValue() {
1305  
    long value;
1306  
    do
1307  
      value = System.currentTimeMillis();
1308  
    while (value == TempDirMaker_lastValue);
1309  
    TempDirMaker_lastValue = value;
1310  
    return value;
1311  
  }
1312  
1313  
  public static String join(String glue, Iterable<String> strings) {
1314  
    StringBuilder buf = new StringBuilder();
1315  
    Iterator<String> i = strings.iterator();
1316  
    if (i.hasNext()) {
1317  
      buf.append(i.next());
1318  
      while (i.hasNext())
1319  
        buf.append(glue).append(i.next());
1320  
    }
1321  
    return buf.toString();
1322  
  }
1323  
1324  
  public static boolean isWindows() {
1325  
    return System.getProperty("os.name").contains("Windows");
1326  
  }
1327  
1328  
  public static String makeRandomID(int length) {
1329  
    Random random = new Random();
1330  
    char[] id = new char[length];
1331  
    for (int i = 0; i< id.length; i++)
1332  
      id[i] = (char) ((int) 'a' + random.nextInt(26));
1333  
    return new String(id);
1334  
  }
1335  
1336  
  static String computerID;
1337  
  public static String getComputerID() throws IOException {
1338  
    if (noID) return null;
1339  
    if (computerID == null) {
1340  
      File file = new File(userHome(), ".tinybrain/computer-id");
1341  
      computerID = loadTextFile(file.getPath(), null);
1342  
      if (computerID == null) {
1343  
        computerID = makeRandomID(12);
1344  
        saveTextFile(file.getPath(), computerID);
1345  
      }
1346  
      if (verbose)
1347  
        System.out.println("Local computer ID: " + computerID);
1348  
    }
1349  
    return computerID;
1350  
  }
1351  
1352  
  static int fileDeletions;
1353  
1354  
  static void cleanCache() {
1355  
    if (verbose)
1356  
      System.out.println("Cleaning cache");
1357  
    fileDeletions = 0;
1358  
    File javax = new File(userHome(), ".javax");
1359  
    long now = System.currentTimeMillis();
1360  
    File[] files = javax.listFiles();
1361  
    if (files != null) for (File dir : files) {
1362  
      if (dir.isDirectory() && Pattern.compile("\\d+").matcher(dir.getName()).matches()) {
1363  
        long time = Long.parseLong(dir.getName());
1364  
        long seconds = (now - time) / 1000;
1365  
        long minutes = seconds / 60;
1366  
        long hours = minutes / 60;
1367  
        if (hours >= 1) {
1368  
          //System.out.println("Can delete " + dir.getAbsolutePath() + ", age: " + hours + " h");
1369  
          removeDir(dir);
1370  
        }
1371  
      }
1372  
    }
1373  
    if (verbose && fileDeletions != 0)
1374  
      System.out.println("Cleaned cache. File deletions: " + fileDeletions);
1375  
  }
1376  
1377  
  static void removeDir(File dir) {
1378  
    if (dir.getAbsolutePath().indexOf(".javax") < 0)  // security check!
1379  
      return;
1380  
    for (File f : dir.listFiles()) {
1381  
      if (f.isDirectory())
1382  
        removeDir(f);
1383  
      else {
1384  
        if (verbose)
1385  
          System.out.println("Deleting " + f.getAbsolutePath());
1386  
        f.delete();
1387  
        ++fileDeletions;
1388  
      }
1389  
    }
1390  
    dir.delete();
1391  
  }
1392  
1393  
  static void showSystemProperties() {
1394  
    System.out.println("System properties:\n");
1395  
    for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
1396  
      System.out.println("  " + entry.getKey() + " = " + entry.getValue());
1397  
    }
1398  
    System.out.println();
1399  
  }
1400  
1401  
  static void showVersion() {
1402  
    //showSystemProperties();
1403  
    boolean eclipseFound = hasEclipseCompiler();
1404  
    //String platform = System.getProperty("java.vendor") + " " + System.getProperty("java.runtime.name") + " " + System.getProperty("java.version");
1405  
    String platform = System.getProperty("java.vm.name") + " " + System.getProperty("java.version");
1406  
    String os = System.getProperty("os.name"), arch = System.getProperty("os.arch");
1407  
    System.out.println("This is " + version + ".");
1408  
    System.out.println("[Details: " +
1409  
      (eclipseFound ? "Eclipse compiler (good)" : "javac (not so good)")
1410  
      + ", " + platform + ", " + arch + ", " + os + "]");
1411  
  }
1412  
1413  
  private static boolean hasEclipseCompiler() {
1414  
    boolean compilerFound = false;
1415  
    try { getEclipseCompiler(); compilerFound = true; } catch (ClassNotFoundException e) {}
1416  
    return compilerFound;
1417  
  }
1418  
1419  
  static boolean isAndroid() {
1420  
    return System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0;
1421  
  }
1422  
1423  
  static void set(Class c, String field, Object value) {
1424  
    try {
1425  
      Field f = set_findStaticField(c, field);
1426  
      f.setAccessible(true);
1427  
      f.set(null, value);
1428  
    } catch (Exception e) {
1429  
      throw new RuntimeException(e);
1430  
    }
1431  
  }
1432  
1433  
  static Field set_findStaticField(Class<?> c, String field) {
1434  
    for (Field f : c.getDeclaredFields())
1435  
      if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0)
1436  
        return f;
1437  
    throw new RuntimeException("Static field '" + field + "' not found in " + c.getName());
1438  
  }
1439  
} // class _x18 modified for Android
1440  
1441  
interface Function {
1442  
  public Object process(Object in);
1443  
  public void toJava_process(Code code);
1444  
}
1445  
1446  
interface ReversibleFunction extends Function {
1447  
  public Object unprocess(Object in);
1448  
  public void toJava_unprocess(Code code);
1449  
}
1450  
1451  
// generic learner (works on objects)
1452  
interface Learner {
1453  
  public void processInOut(Object in, Object out);
1454  
  public Object processIn(Object in);
1455  
  public void toJava(Code code);
1456  
  public void tryAgain();
1457  
}
1458  
1459  
abstract class Base {
1460  
  void printVars() {
1461  
    StringBuilder buf = new StringBuilder();
1462  
    Field[] fields = getClass().getDeclaredFields();
1463  
    for (Field field : fields) {
1464  
      if ((field.getModifiers() & Modifier.STATIC) != 0)
1465  
        continue;
1466  
      Object value;
1467  
      try {
1468  
        value = field.get(this);
1469  
      } catch (Exception e) {
1470  
        value = "?";
1471  
      }
1472  
      
1473  
      if (!(buf.length() == 0)) buf.append(", ");
1474  
      buf.append(field.getName() + "=" + value);
1475  
    }
1476  
    System.out.println(buf.toString());
1477  
  }
1478  
  
1479  
  String name() {
1480  
    return getClass().getName().replaceAll("^main\\$", "");
1481  
  }
1482  
  
1483  
  void debug(String s) {
1484  
    System.out.println(name() + ": " + s);
1485  
  }
1486  
}
1487  
1488  
abstract class LearnerImpl extends Base implements Learner {
1489  
  public void tryAgain() {
1490  
    throw new RuntimeException("No try again");
1491  
  }
1492  
  
1493  
  public void toJava(Code code) {
1494  
    main.todo();
1495  
  }
1496  
}
1497  
1498  
abstract class FunctionImpl extends Base implements Function {
1499  
  public void toJava_process(Code code) {
1500  
    main.todo();
1501  
  }
1502  
}
1503  
1504  
class Code {
1505  
  StringBuilder buf = new StringBuilder();
1506  
  String var = "in";
1507  
  String indent = "";
1508  
  List<String> translators = new ArrayList<String>();
1509  
  List<String> varStack = new ArrayList<String>();
1510  
  int varCounter;
1511  
  
1512  
  Code() {
1513  
    translators.add("!636");
1514  
  }
1515  
  
1516  
  void line(String line) {
1517  
    buf.append(indent).append(line).append('\n');
1518  
  }
1519  
  
1520  
  void indent() {
1521  
    indent += "  ";
1522  
  }
1523  
  
1524  
  void unindent() {
1525  
    indent = indent.substring(0, indent.length()-2);
1526  
  }
1527  
  
1528  
  void translators(String... ids) {
1529  
    for (String id : ids)
1530  
      if (! translators.contains(id)) // space is needed otherwise translator 636 is fooled :)
1531  
        translators.add(id);
1532  
  }
1533  
  
1534  
  String getTranslators() {
1535  
    // TODO: We should really fix the "standard functions" translator
1536  
    // to properly find the main class.
1537  
    //
1538  
    // As a hack, we move it upwards (before classes adding)
1539  
    int i = translators.indexOf("!629");
1540  
    if (i >= 0) {
1541  
      translators.remove(i);
1542  
      translators.add(0, "!629");
1543  
    }
1544  
    
1545  
    return main.fromLines(translators);
1546  
  }
1547  
1548  
  String s() {
1549  
    return "((String) " + var + ")";
1550  
  }
1551  
  
1552  
  String list() {
1553  
    return "((List) " + var + ")";
1554  
  }
1555  
  
1556  
  void assign(String exp) {
1557  
    line(var + " = " + exp + ";");
1558  
  }
1559  
  
1560  
  void newVar() {
1561  
    varStack.add(var);
1562  
    var = "_v" + (++varCounter);
1563  
    line("Object " + var + ";");
1564  
  }
1565  
  
1566  
  void oldVar() {
1567  
    var = varStack.get(varStack.size()-1);
1568  
    varStack.remove(varStack.size()-1);
1569  
  }
1570  
}
1571  
1572  
public class main {
1573  
  // The following two fields are filled by JavaX.
1574  
  static Object androidContext;
1575  
  static String programID;
1576  
  
1577  
  static final String ALL = "#681";
1578  
  
1579  
  static boolean classic = false;
1580  
  
1581  
  static List<Case> cases = new ArrayList<Case>();
1582  
  static HashMap<String, Case> casesByID = new HashMap<String, Case>();
1583  
  static boolean testJava = false, showFails, execTasks = false;
1584  
  static boolean collectClosest = false; // saves time
1585  
  static Set<String> parseErrors = new HashSet<String>();
1586  
  static int caseIdx;
1587  
  static Set<Class> debuggedClasses = new HashSet<Class>();
1588  
  static String strategy = "#1000464";
1589  
  static String[] innerClasses={
1590  
"Case",
1591  
"LSplitInput",
1592  
"FDropFirstLines",
1593  
"LOutSuffix",
1594  
"LOutPattern",
1595  
"LId",
1596  
"LInputPattern",
1597  
"LFixed",
1598  
"LEach",
1599  
"LChange",
1600  
"LFindOutInIn",
1601  
"LWrap",
1602  
"RFJavaTok",
1603  
"RFToLines",
1604  
"FStringsOnly",
1605  
"FNumbersOnly",
1606  
"FLength",
1607  
"LFixedFunction",
1608  
"LTrivial",
1609  
"LGetListElement",
1610  
"LMath",
1611  
"EscapeCase",
1612  
"LCharShift",
1613  
"LFirst",
1614  
"LSwitch",
1615  
"LMulti",
1616  
"FIsString",
1617  
"FCommonPrefix",
1618  
"FCommonSuffix",
1619  
"Matrix",
1620  
"LDistinguishList",
1621  
"RunnersUp",
1622  
"LBox",
1623  
"LBox2",
1624  
"LEmptyBox",
1625  
"LFixedSubstring",
1626  
"LFixWhitespace",
1627  
"LCertainElement",
1628  
"LOneWordChanged",
1629  
"LBla",
1630  
"LIntersperse",
1631  
"LFixedPositions",
1632  
"LMap",
1633  
"LPrefixSuffix",
1634  
"LRemoveInputSuffix",
1635  
"FBeforeColon",
1636  
"FAfterColon",
1637  
"FUnquote",
1638  
"LCombineTwoFunctions",
1639  
"LCombineTwoFunctions2",
1640  
"Stepper",
1641  
};
1642  
  static List<Throwable> strategyParseErrors = new ArrayList<Throwable>();
1643  
  static Learner trying;
1644  
  static String user, pass;
1645  
  static boolean edit;
1646  
1647  
  public static void main(String[] args) throws Exception {
1648  
    keepAlive();
1649  
    _x18.androidContext = androidContext;
1650  
    long startTime = System.currentTimeMillis();
1651  
    
1652  
    List<String> snippetIDs = new ArrayList<String>();
1653  
    List<String> inputs = new ArrayList<String>();
1654  
    List<String> inputDocs = new ArrayList<String>();
1655  
    List<Case> userCases = new ArrayList<Case>();
1656  
    
1657  
    for (int i = 0; i < args.length; i++) {
1658  
      String arg = args[i];
1659  
      if (arg.equals("debug"))
1660  
        debugOn(args[++i]);
1661  
      else if (arg.equals("user"))
1662  
        user = args[++i];
1663  
      else if (arg.equals("pass")) {
1664  
        pass = args[++i];
1665  
        args[i] = "hidden";
1666  
      } else if (arg.equals("edit"))
1667  
        edit = true;
1668  
      else if (arg.equals("in")) {
1669  
        String in = args[++i];
1670  
        //System.out.println("in=" + quote(in));
1671  
        String quoteUnquote = unquote("\"" + in + "\"");
1672  
        //System.out.println("now in=" + quote(quoteUnquote));
1673  
        inputs.add(quoteUnquote);
1674  
      } else if (arg.equals("in-doc"))
1675  
        inputDocs.add(args[++i]);
1676  
      else if (arg.equals("-notestjava"))
1677  
        testJava = false;
1678  
      else if (arg.equals("-testjava"))
1679  
        testJava = true;
1680  
      else if (arg.equals("-withexec"))
1681  
        execTasks = true;
1682  
      else if (arg.equals("-showfails"))
1683  
        showFails = true;
1684  
      else if (arg.equals("-classic"))
1685  
        classic = true;
1686  
      else if (arg.equals("strategy"))
1687  
        strategy = args[++i];
1688  
      else if (arg.equals("all"))
1689  
        snippetIDs.add(ALL);
1690  
      else if (isSnippetID(arg))
1691  
        snippetIDs.add(arg);
1692  
      else
1693  
        System.err.println("Unknown argument: " + arg + ", ignoring");
1694  
    }
1695  
    
1696  
    if (snippetIDs.isEmpty()) {
1697  
      String s = loadTextFile("input/input.txt", null);
1698  
      if (s != null)
1699  
        parse(s);
1700  
      else
1701  
        parse(ALL);
1702  
    }
1703  
    
1704  
    for (String snippetID : snippetIDs)
1705  
      try {
1706  
        Case _case = parse(snippetID);
1707  
        if (_case != null) {
1708  
          _case.halfExamples.addAll(inputs);
1709  
          for (String docID : inputDocs)
1710  
            _case.halfExamples.add(loadSnippet(docID));
1711  
          userCases.add(_case);
1712  
        }
1713  
      } catch (Throwable e) {
1714  
        e.printStackTrace();
1715  
        parseErrors.add(snippetID);
1716  
      }
1717  
    
1718  
    int solved = 0, n = cases.size(), goodJava = 0;
1719  
    for (caseIdx = 0; caseIdx < n; caseIdx++) {
1720  
      Case _case = cases.get(caseIdx);
1721  
      try {
1722  
        calculate(_case);
1723  
      } catch (Throwable e) {
1724  
        e.printStackTrace();
1725  
      }
1726  
      if (_case.winner != null)
1727  
        ++solved;
1728  
      if (_case.goodJava)
1729  
        ++goodJava;
1730  
      if (caseIdx+1 == solved)
1731  
        System.out.println((caseIdx+1) + " case(s) processed & solved.");
1732  
      else
1733  
        System.out.println((caseIdx+1) + " case(s) processed, " + solved + " solved.");
1734  
      if (testJava && goodJava < solved)
1735  
        System.out.println((solved-goodJava) + " case(s) with BAD JAVA.");
1736  
    }
1737  
1738  
    System.out.println("");    
1739  
    System.out.println("----");
1740  
1741  
    List<Case> sorted = casesSortedByID();
1742  
    boolean allSolved = solved == n;
1743  
    if (solved != 0) {
1744  
      System.out.println();
1745  
      System.out.print("Solved: ");
1746  
      for (Case _case : sorted)
1747  
        if (_case.winner != null)
1748  
          System.out.print(_case.id + " ");
1749  
      System.out.println();
1750  
    }
1751  
    if (testJava && solved > goodJava) {
1752  
      System.out.println();
1753  
      System.out.print("Bad Java: ");
1754  
      for (Case _case : sorted)
1755  
        if (_case.winner != null && !_case.goodJava)
1756  
          System.out.print(_case.id + " ");
1757  
      System.out.println();
1758  
    }
1759  
    if (!allSolved) {
1760  
      System.out.println();
1761  
      System.out.print("Unsolved: ");
1762  
      for (Case _case : sorted)
1763  
        if (_case.winner == null)
1764  
          System.out.print(_case.id + " ");
1765  
      System.out.println();
1766  
    }
1767  
    if (!parseErrors.isEmpty()) {
1768  
      System.out.print("\nParse errors: ");
1769  
      for (String id : parseErrors)
1770  
          System.out.print(id + " ");
1771  
      System.out.println();
1772  
    }
1773  
    if (!strategyParseErrors.isEmpty())
1774  
      System.out.println("Strategy parse errors!");
1775  
    System.out.println();
1776  
    if (allSolved && testJava && goodJava < solved)
1777  
      System.out.println("ALL SOLVED (" + solved + "), but some BAD JAVA.");
1778  
    else {
1779  
      System.out.println(allSolved ? "ALL SOLVED (" + solved + ")" : "Solved " + solved + " out of " + n + ".");
1780  
      if (testJava)
1781  
        if (goodJava == solved)
1782  
          System.out.println("All Java code OK" + (allSolved ? "" : " (for solved cases)") + ".");
1783  
        else
1784  
          System.out.println("Some bad Java.");
1785  
      else
1786  
        System.out.println("Java not tested.");
1787  
    }
1788  
    System.out.println("");
1789  
1790  
    if (edit) {
1791  
      if (userCases.size() != 1) fail("Edit: More than one user case, confused");
1792  
      Case _case = userCases.get(0);
1793  
      for (String docID : inputDocs) {
1794  
        Object _out = _case.processIn(loadSnippet(docID));
1795  
        if (!(_out instanceof String))
1796  
          fail("Case did not generate a string: " + structure(_out));
1797  
        String out = (String) ( _out);
1798  
        String editInfo = "Solver #" + programID + " " + join(" ", args);
1799  
        editSnippetText(docID, out, editInfo);
1800  
      }
1801  
    }
1802  
1803  
    /*long time = getUserTime();
1804  
    if (time >= 0)
1805  
      System.out.println("User time: " + formatDouble(time/1e9, 3) + "s");*/
1806  
      
1807  
    long time = System.currentTimeMillis()-startTime;
1808  
    System.out.println("Real time: " + formatDouble(time/1e3, 3) + "s");
1809  
  }
1810  
  
1811  
  public static String formatDouble(double d, int digits) {
1812  
    String format = "0.";
1813  
    for (int i = 0; i < digits; i++) format += "#";
1814  
    String s = new DecimalFormat(format).format(d);
1815  
    return s.replace(',', '.'); // hack german -> english
1816  
  }
1817  
  
1818  
  /** Get user time in nanoseconds. */
1819  
  public static long getUserTime( ) {
1820  
    ThreadMXBean bean = ManagementFactory.getThreadMXBean();
1821  
    return bean.isCurrentThreadCpuTimeSupported() ?
1822  
      bean.getCurrentThreadUserTime() : -1L;
1823  
  }
1824  
  
1825  
  static class Case {
1826  
    String id;
1827  
    List<Object[]> fullExamples = new ArrayList<Object[]>();
1828  
    List<Object> halfExamples = new ArrayList<Object>();
1829  
    List<Object[]> examples1, examples2;
1830  
    Learner winner;
1831  
    RunnersUp runnersUp = collectClosest ? new RunnersUp() : null;
1832  
    boolean goodJava;
1833  
    List<Case> combined;
1834  
    int splitPoint = -1;
1835  
    
1836  
    // stats
1837  
    int learnersTried, retries;
1838  
1839  
    void split() {    
1840  
      if (examples1 != null)
1841  
        return; // already done
1842  
      if (fullExamples.size() < 2)
1843  
        throw new RuntimeException("Too few examples (" + fullExamples.size() + ")");
1844  
      if (splitPoint < 0)
1845  
        splitPoint = fullExamples.size()-1;
1846  
      System.out.println("Full examples: " + fullExamples.size() + ", splitPoint: " + splitPoint + ", half examples: " + halfExamples.size());
1847  
      examples1 = fullExamples.subList(0, splitPoint);
1848  
      examples2 = fullExamples.subList(splitPoint, fullExamples.size());
1849  
    }
1850  
    
1851  
    void add(Case _case) {
1852  
      combined.add(_case);
1853  
      fullExamples.addAll(_case.fullExamples);
1854  
      halfExamples.addAll(_case.halfExamples);
1855  
    }
1856  
    
1857  
    Object processIn(Object in) {
1858  
      return winner.processIn(in);
1859  
    }
1860  
  }
1861  
1862  
  static Case parse(String arg) throws Exception {
1863  
   try {
1864  
    if (arg == null) return null;
1865  
    arg = arg.trim();
1866  
    if (arg.length() == 0) return null;
1867  
    Case _case = new Case();
1868  
    String text;
1869  
    
1870  
    if (casesByID.containsKey(arg))
1871  
      return casesByID.get(arg);
1872  
      
1873  
    if (arg.startsWith("Combine")) {
1874  
      _case.id = "Combine";
1875  
      _case.combined = new ArrayList<Case>();
1876  
      List<String> tok = JavaTok.split(arg);
1877  
      List<String> ids = new ArrayList<String>();
1878  
      for (int i = 5; i < tok.size(); i += 6) { // skip # and "and"
1879  
        Case case2 = parse("#" + tok.get(i));
1880  
        _case.id += " #" + tok.get(i);
1881  
        cases.remove(case2);
1882  
        _case.add(case2);
1883  
      }
1884  
      addCase(_case);
1885  
      return _case;
1886  
    }
1887  
    
1888  
    if (isSnippetID(arg)) {
1889  
      _case.id = arg;
1890  
      text = loadSnippet(arg);
1891  
    } else {
1892  
      _case.id = "direct (" + shorten(arg, 10) + ")";
1893  
      text = arg;
1894  
    }
1895  
1896  
    // it's a collection of cases!
1897  
    if (text.trim().startsWith("#") || text.trim().startsWith("Combine")) {
1898  
      for (String line : toLines(text))
1899  
        parse(line);
1900  
      return null;
1901  
    }
1902  
    
1903  
    // it's a "Continue:" task - transform to I/O format
1904  
    if (text.trim().startsWith("Continue:")) {
1905  
      List<String> lines = toLines(text);
1906  
      StringBuilder buf = new StringBuilder();
1907  
      for (int i = 1; i < lines.size(); i++) {
1908  
        buf.append("In: " + quote("" + i) + "\n");
1909  
        buf.append("Out: " + quote(lines.get(i)) + "\n");
1910  
      }
1911  
      int numAsking = 3;
1912  
      for (int i = lines.size(); i < lines.size()+numAsking; i++)
1913  
        buf.append("In: " + quote("" + i) + "\n");
1914  
      text = buf.toString();
1915  
    }
1916  
      
1917  
    // it's an "Execute." task - run Java(X) and transform to I/O format
1918  
    if (text.trim().startsWith("Execute.")) {
1919  
      if (!execTasks) return null;
1920  
      List<String> tok = JavaTok.split(text);
1921  
      StringBuilder buf = new StringBuilder();
1922  
      for (int i = 5; i < tok.size(); i += 2) {
1923  
        if (tok.get(i).equals("-") && tok.get(i+2).equals("-")) {
1924  
          i += 2;
1925  
          buf.append("--\n");
1926  
        } else {
1927  
          String code = unquote_fixSpaces(tok.get(i));
1928  
          String result = execute("!636\n" + code);
1929  
          buf.append("In: " + quote(code) + "\n");
1930  
          buf.append("Out: " + quote(result) + "\n");
1931  
        }
1932  
      }
1933  
      text = buf.toString();
1934  
    }
1935  
1936  
    if (text.trim().startsWith("Marking.")) {
1937  
      
1938  
      List<String> tok = JavaTok.split(text);
1939  
      StringBuilder buf = new StringBuilder();
1940  
      for (int i = 5; i < tok.size(); i += 2) {
1941  
        if (tok.get(i).equals("-") && tok.get(i+2).equals("-")) {
1942  
          i += 2;
1943  
          buf.append("--\n");
1944  
        } else {
1945  
          String out = unquote_fixSpaces(tok.get(i));
1946  
          String in = out.replace("[[", "").replace("]]", "");
1947  
          buf.append("In: " + quote(in) + "\n");
1948  
          buf.append("Out: " + quote(out) + "\n");
1949  
        }
1950  
      }
1951  
      text = buf.toString();
1952  
    }
1953  
 
1954  
    System.out.println(text);
1955  
    String in = null, out = null;
1956  
    
1957  
    List<String> tok = JavaTok.split(text);
1958  
    for (int i = 1; i < tok.size(); i += 2) {
1959  
      String t = tok.get(i), t2 = i+2 < tok.size() ? tok.get(i+2) : "";
1960  
      if (t.equals("-") && t2.equals("-")) {
1961  
        i += 2;
1962  
        _case.splitPoint = _case.fullExamples.size();
1963  
        //System.out.println("t=" + t + ", t2= " + t2);
1964  
      } else if (t.toUpperCase().startsWith("I") && t2.equals("-") && tok.get(i+4).toUpperCase().equals("DOC") && tok.get(i+6).equals(":")) { // "In-Doc:"
1965  
        if (in != null)
1966  
          _case.halfExamples.add(in);
1967  
        i += 8;
1968  
        int j = findNextLine(tok, i);
1969  
        String inID = unquote_fixSpaces(JavaTok.join(tok.subList(i, j-1)));
1970  
        in = loadSnippet(inID);
1971  
        i = j-2;
1972  
        out = null;
1973  
      } else if (t.toUpperCase().startsWith("I") && t2.equals(":")) { // "In:" or "I:"
1974  
        if (in != null)
1975  
          _case.halfExamples.add(in);
1976  
        i += 4;
1977  
        int j = findNextLine(tok, i);
1978  
        in = unquote_fixSpaces(JavaTok.join(tok.subList(i, j-1)));
1979  
        i = j-2;
1980  
        out = null;
1981  
      } else if (t.toUpperCase().startsWith("O") && t2.equals(":")) { // "Out: " or "O: "
1982  
        i += 4;
1983  
        int j = findNextLine(tok, i);
1984  
        out = unquote_fixSpaces(JavaTok.join(tok.subList(i, j-1)));
1985  
        i = j-2;
1986  
        if ((in + out).indexOf('\t') >= 0)
1987  
          System.err.println("WARNING: Tab character used!");
1988  
        System.out.println(quote(in) + " => " + quote(out));
1989  
        _case.fullExamples.add(new String[] {in, out});
1990  
        in = out = null;
1991  
      } else {
1992  
        int j = findNextLine(tok, i);
1993  
        String line = JavaTok.join(tok.subList(i, j-1));
1994  
        i = j-2;
1995  
        System.out.println("-- Ignoring line: " + line);
1996  
      }
1997  
    }
1998  
    
1999  
    if (in != null)
2000  
      _case.halfExamples.add(in);
2001  
    addCase(_case);
2002  
    return _case;
2003  
   } catch (Throwable e) {
2004  
    parseErrors.add(arg);
2005  
    e.printStackTrace();
2006  
    return null;
2007  
   }
2008  
  }
2009  
  
2010  
  static String unquote_fixSpaces(String s) {
2011  
    String _ = unquote(s);
2012  
    if (s.startsWith("[["))
2013  
      _ = fixSpaces(_);
2014  
    return _;
2015  
  }
2016  
  
2017  
  // remove invisible spaces at end of line - this will otherwise confuse the engine(s) greatly...
2018  
  static String fixSpaces(String s) {
2019  
    System.out.println(quote(s));
2020  
    s = s.replaceAll("[\t ]+(\r?\n)", "$1");
2021  
    s = s.replaceAll("[\t ]+$", "");
2022  
    //System.out.println("_fixSpaces => " + quote(s));
2023  
    return s;
2024  
  }
2025  
  
2026  
  // i is a code-token (odd index)
2027  
  // return value is also a code token, or end of list
2028  
  static int findNextLine(List<String> tok, int i) {
2029  
    while (i < tok.size() && tok.get(i-1).indexOf('\n') < 0)
2030  
      i += 2;
2031  
    return i;
2032  
  }
2033  
  
2034  
  static void addCase(Case _case) {
2035  
    cases.add(_case);
2036  
    casesByID.put(_case.id, _case);
2037  
  }
2038  
  
2039  
  static void calculate(Case _case) throws Exception {
2040  
    System.out.println("\n== CASE " + _case.id + " ==");
2041  
    
2042  
    _case.split();
2043  
    Learner learner = findOKLearner(_case);
2044  
    if (learner == null) {
2045  
      String stat = "";
2046  
      if (_case.retries != 0) stat = " + " + _case.retries + " retries";
2047  
      System.out.println("\nProblem not solved (" + _case.learnersTried + " learners tried" + stat + ")");
2048  
      RunnersUp ru = _case.runnersUp;
2049  
      if (ru != null && ru.winner != null)
2050  
        System.out.println("Closest result: " + quote(ru.bestResult) + " instead of " + quote(ru.expected) + " (score " + ru.bestScore + ") by " + structure(ru.winner));
2051  
    } else {
2052  
      System.out.println("\nSolved!");
2053  
      print("  " + structure(learner) + "\n");
2054  
      _case.winner = learner;
2055  
      Code code = new Code();
2056  
      if (testJava) try {
2057  
        learner.toJava(code);
2058  
2059  
        if (testJava)
2060  
          testJava(_case, code); // prints "GOOD JAVA" or "BAD JAVA"
2061  
        else
2062  
          System.out.println("Java:");
2063  
          
2064  
        System.out.println(indent("  ", code.getTranslators()));
2065  
        System.out.println(indent("  ", code.buf.toString()));
2066  
      } catch (Throwable e) {
2067  
        System.out.println("BAD JAVA");
2068  
      }
2069  
        
2070  
      for (Object in : _case.halfExamples) {
2071  
        Object out = learner.processIn(in);
2072  
        System.out.println(structure(in) + " =>! " + structure(out));
2073  
      }
2074  
    }
2075  
  }
2076  
2077  
  static void choice_orderList(List l) {
2078  
  }
2079  
  
2080  
  static Learner findOKLearner(Case _case) throws Exception {
2081  
    List<Learner> list = makeLearners();
2082  
    choice_orderList(list);
2083  
    for (Learner learner : list) try {
2084  
      if (learnerOK(learner, _case))
2085  
        return learner;
2086  
    } catch (Throwable e) {
2087  
      if (showFails)
2088  
        e.printStackTrace();
2089  
    }
2090  
    
2091  
    if (_case.combined != null) {
2092  
      Case switchCase = makeSwitchCase(_case);
2093  
      calculate(switchCase);
2094  
      Learner learner = switchCase.winner;
2095  
      if (learner != null)
2096  
        return new LSwitch(_case, learner);
2097  
    }
2098  
    
2099  
    return null;
2100  
  }
2101  
  
2102  
  static Case makeSwitchCase(Case _case) {
2103  
    int i = 0;
2104  
    Case s = new Case();
2105  
    s.id = "Switch " + _case.id;
2106  
    s.examples1 = new ArrayList<Object[]>();
2107  
    s.examples2 = new ArrayList<Object[]>();
2108  
    for (Case c : _case.combined) {
2109  
      ++i;
2110  
      for (Object[] e : c.examples1)
2111  
        s.examples1.add(new Object[] {e[0], String.valueOf(i)});
2112  
      for (Object[] e : c.examples2)
2113  
        s.examples2.add(new Object[] {e[0], String.valueOf(i)});
2114  
      for (Object[] e : c.fullExamples)
2115  
        s.fullExamples.add(new Object[] {e[0], String.valueOf(i)});
2116  
    }
2117  
    return s;
2118  
  }
2119  
  
2120  
  static boolean learnerOK(Learner learner, Case _case) {
2121  
    trying = learner;
2122  
    
2123  
    try {
2124  
      Object[] _e = null;
2125  
      try {
2126  
        if (_case.examples1 == null)
2127  
          fail("Case not calculated: " + _case.id);
2128  
          
2129  
        ++_case.learnersTried;
2130  
        for (Object[] e : _case.examples1) {
2131  
          _e = e;
2132  
          learner.processInOut(e[0], e[1]);
2133  
        }
2134  
        
2135  
        // full validation against all examples - but starting with the unknown ones to make coding easier for learners
2136  
        int retry = 0;
2137  
        retryLoop: while (true) {
2138  
          int n = _case.fullExamples.size();
2139  
          for (int i = 0; i < n; i++) {
2140  
            Object[] e = _case.fullExamples.get((i+_case.examples1.size()) % n);
2141  
            _e = e;
2142  
            Object out = learner.processIn(e[0]);
2143  
            if (!e[1].equals(out)) {
2144  
            
2145  
              if (_case.runnersUp != null && e[1] instanceof String && out instanceof String)
2146  
                _case.runnersUp.add((String) e[1], (String) out, leven((String) out, (String) e[1]), learner);
2147  
                
2148  
              if (debuggedClasses.contains(learner.getClass()) || showFails)
2149  
                System.out.println("[fail] " + structure(learner) + " on " + structure(e[0]) + " - got: " + structure(out) + " rather than: " + structure(e[1]));
2150  
              ++retry;
2151  
              ++_case.retries;
2152  
              if (retry % 1000 == 0)
2153  
                System.err.println("Retry " + retry);
2154  
              learner.tryAgain();
2155  
              continue retryLoop;
2156  
            }
2157  
          }
2158  
          return true;  // all test examples passed
2159  
        }
2160  
      } catch (Throwable e) {
2161  
        if (debuggedClasses.contains(learner.getClass()) || showFails) {
2162  
          e.printStackTrace();
2163  
          System.err.println("[fail] " + structure(learner) + " on " + (_e == null ? "?" : structure(_e[0])) + " - " + e);
2164  
        }
2165  
        return false;
2166  
      }
2167  
    } finally {
2168  
      trying = null;
2169  
    }
2170  
  }
2171  
  
2172  
  static boolean validate(String[] example, Learner learner) {
2173  
    try {
2174  
      String out = (String) learner.processIn(example[0]);
2175  
      if (!example[1].equals(out)) {
2176  
          //System.out.println("[fail] " + learner + " on " + quote(e[0]) + " - got: " + quote(out) + " rather than: " + quote(e[1]));
2177  
          return false;
2178  
        }
2179  
      return true;
2180  
    } catch (Throwable e) {
2181  
      silentException(e);
2182  
      return false;
2183  
    }
2184  
  }
2185  
  
2186  
  static void silentException(Throwable e) {
2187  
  }
2188  
  
2189  
  static List<Learner> makeLearners() { try {
2190  
 
2191  
    String s = isSnippetID(strategy) ? loadSnippet(strategy) : strategy;
2192  
    s += "\n" + combineClasses();
2193  
    System.out.println("Strategy:\n" + s);
2194  
    List<Learner> list = parseStrategy(s);
2195  
    if (classic)
2196  
      list.addAll(classicLearners()); // Endlosschleife!?
2197  
    return list;
2198  
  
2199  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2200  
  
2201  
  static String combineClasses() {
2202  
    StringBuilder buf = new StringBuilder();
2203  
    for (String s : innerClasses) {
2204  
      Class c = findClass(s);
2205  
      if (c == null) System.err.println("Warning: Class not found - " + s);
2206  
      else if (isSubclass(c, Learner.class)) {
2207  
        if (hasConstructor(c))
2208  
          buf.append(getName(c) + "\n");
2209  
        else
2210  
          for (String s2 : innerClasses) {
2211  
            Class c2 = findClass(s2);
2212  
            if (c2 != null && hasConstructor(c, c2))
2213  
              buf.append(getName(c) + " " + getName(c2) + "\n");
2214  
          }
2215  
      }
2216  
    }
2217  
    return buf.toString();
2218  
  }
2219  
  
2220  
  static List<Learner> parseStrategy(String strategyText) { try {
2221  
 
2222  
    List <String> lines = toLines(strategyText);
2223  
    List<Learner> list = new ArrayList<Learner>();
2224  
    for (String s : lines) try {
2225  
      s = s.trim();
2226  
      String[] parts = s.split(" +");
2227  
      if (parts.length == 0) continue;
2228  
      
2229  
      Stack<Object> stack = new Stack<Object>();
2230  
      for (int i = parts.length-1; i >= 0; i--) {
2231  
        String p = parts[i];
2232  
        Class c = findClassFlex(p);
2233  
        if (c == null)
2234  
          fail("Strategy element " + p + " not known");
2235  
        
2236  
        Constructor ctr = c.getDeclaredConstructors()[0];
2237  
        Class[] types = ctr.getParameterTypes();
2238  
        if (types.length == 0)
2239  
          stack.add(ctr.newInstance());
2240  
        else if (types.length == 1) {
2241  
          if (stack.isEmpty())
2242  
            fail("Too few arguments for " + c.getName());
2243  
          stack.add(ctr.newInstance(stack.pop()));
2244  
        } else if (types.length == 2) {
2245  
          if (stack.size() < 2)
2246  
            fail("Too few arguments for " + c.getName());
2247  
          Object a = stack.pop();
2248  
          Object b = stack.pop();
2249  
          stack.add(ctr.newInstance(a, b));
2250  
        } else
2251  
          fail("bla");
2252  
        
2253  
        /*if (isSubclass(c, ReversibleFunction.class)) {
2254  
          l = new LWrap((ReversibleFunction) newInstance(c), l);
2255  
          continue;
2256  
        }*/
2257  
      }
2258  
      
2259  
      list.add((Learner) stack.peek());
2260  
    } catch (Throwable e) {
2261  
      System.err.println("Strategy parse error.");
2262  
      e.printStackTrace();
2263  
      strategyParseErrors.add(e);
2264  
    }
2265  
    return list;
2266  
  
2267  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
2268  
2269  
  static List<Learner> classicLearners() {
2270  
    List<Learner> list = new ArrayList<Learner>();
2271  
    list.addAll(level1());
2272  
    list.add(new LBox(new LMulti(level1())));
2273  
    list.add(new LBox2(new LMulti(level1())));
2274  
    
2275  
    list.add(new LChange(new FCommonPrefix(), new LOutPattern()));
2276  
    list.add(new LChange(new FCommonSuffix(), new LOutPattern()));
2277  
    
2278  
    list.add(new LEmptyBox(new LMulti(level1())));
2279  
    
2280  
    list.add(new LChange(new RFToLines(), new LFixedFunction(new FLength())));
2281  
    
2282  
    // for #1000455
2283  
    list.add(new LFindOutInIn(new LFixedSubstring()));
2284  
   
2285  
    for (int I = 1; I <= 2; I++)
2286  
    // for #1000457
2287  
    list.add(new LChange(new FDropFirstLines(I), new LMulti(
2288  
      new LFixWhitespace(l1()), level1())));
2289  
2290  
    // for #1000458
2291  
    list.add(new LFixWhitespace(l1()));
2292  
2293  
    return list;
2294  
  }
2295  
2296  
  static Learner l1() {
2297  
    return new LMulti(level1());
2298  
  }
2299  
  
2300  
  static List<Learner> level1() {
2301  
    List<Learner> list = new ArrayList<Learner>();
2302  
    list.add(new LId()); // always good to have as included learner
2303  
    list.add(new LPrefixSuffix());
2304  
    list.add(new LSplitInput(new LOutPattern()));
2305  
    list.add(new LInputPattern());
2306  
    list.add(new LFixed());
2307  
    list.add(new LWrap(new RFJavaTok(), new LEach(new LFixedFunction(new EscapeCase()))));
2308  
    list.add(new LCharShift());
2309  
    list.add(new LOutSuffix(new LFirst(new LCharShift())));
2310  
    
2311  
    list.add(new LChange(new RFJavaTok(), new LMulti(
2312  
      new LChange(new FStringsOnly(), new LGetListElement()),
2313  
      new LChange(new FNumbersOnly(), new LMath()))
2314  
    ));
2315  
    
2316  
    // TODO - for #1000378
2317  
    list.add(new LChange(new RFJavaTok(), new LDistinguishList(new FIsString())));
2318  
    
2319  
    list.add(new LFixedSubstring());
2320  
    
2321  
    // of no use now it seems
2322  
    // list.add(new LTryPrevious());
2323  
    
2324  
    return list;
2325  
  }
2326  
  
2327  
  // enhanced with robust multilines
2328  
  public static String unquote(String s) {
2329  
    if (s.startsWith("[")) {
2330  
      int i = 1;
2331  
      while (i < s.length() && s.charAt(i) == '=') ++i;
2332  
      if (i < s.length() && s.charAt(i) == '[') {
2333  
        String m = s.substring(1, i);
2334  
        if (s.endsWith("]" + m + "]"))
2335  
          return s.substring(i+1, s.length()-i-1);
2336  
      }
2337  
    }
2338  
    
2339  
    if (s.startsWith("\"") && s.endsWith("\"") && s.length() > 1)
2340  
      //return s.substring(1, s.length()-1).replace("\\\"", "\"").replace("\\\\", "\\");
2341  
      return unescapeJavaString(s.substring(1, s.length()-1));
2342  
    else
2343  
      return s; // return original
2344  
  }
2345  
  
2346  
  /** from: http://stackoverflow.com/questions/3537706/howto-unescape-a-java-string-literal-in-java,
2347  
      or: https://gist.github.com/uklimaschewski/6741769
2348  
      
2349  
      Thanks!
2350  
  */
2351  
  public static String unescapeJavaString(String st) {
2352  
    StringBuilder sb = new StringBuilder(st.length());
2353  
2354  
    for (int i = 0; i < st.length(); i++) {
2355  
        char ch = st.charAt(i);
2356  
        if (ch == '\\') {
2357  
            char nextChar = (i == st.length() - 1) ? '\\' : st
2358  
                    .charAt(i + 1);
2359  
            // Octal escape?
2360  
            if (nextChar >= '0' && nextChar <= '7') {
2361  
                String code = "" + nextChar;
2362  
                i++;
2363  
                if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
2364  
                        && st.charAt(i + 1) <= '7') {
2365  
                    code += st.charAt(i + 1);
2366  
                    i++;
2367  
                    if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
2368  
                            && st.charAt(i + 1) <= '7') {
2369  
                        code += st.charAt(i + 1);
2370  
                        i++;
2371  
                    }
2372  
                }
2373  
                sb.append((char) Integer.parseInt(code, 8));
2374  
                continue;
2375  
            }
2376  
            switch (nextChar) {
2377  
            case '\\':
2378  
                ch = '\\';
2379  
                break;
2380  
            case 'b':
2381  
                ch = '\b';
2382  
                break;
2383  
            case 'f':
2384  
                ch = '\f';
2385  
                break;
2386  
            case 'n':
2387  
                ch = '\n';
2388  
                break;
2389  
            case 'r':
2390  
                ch = '\r';
2391  
                break;
2392  
            case 't':
2393  
                ch = '\t';
2394  
                break;
2395  
            case '\"':
2396  
                ch = '\"';
2397  
                break;
2398  
            case '\'':
2399  
                ch = '\'';
2400  
                break;
2401  
            // Hex Unicode: u????
2402  
            case 'u':
2403  
                if (i >= st.length() - 5) {
2404  
                    ch = 'u';
2405  
                    break;
2406  
                }
2407  
                int code = Integer.parseInt(
2408  
                        "" + st.charAt(i + 2) + st.charAt(i + 3)
2409  
                                + st.charAt(i + 4) + st.charAt(i + 5), 16);
2410  
                sb.append(Character.toChars(code));
2411  
                i += 5;
2412  
                continue;
2413  
            }
2414  
            i++;
2415  
        }
2416  
        sb.append(ch);
2417  
    }
2418  
    return sb.toString();
2419  
  }
2420  
  
2421  
  static String indent(String indent, String s) {
2422  
    return indent + s.replace("\n", "\n" + indent);
2423  
  }
2424  
  
2425  
  static void debugOn(String name) {
2426  
    try {
2427  
      Class c = findClassFlex(name);
2428  
      debuggedClasses.add(c);
2429  
      Field field;
2430  
      while (true)
2431  
        try {
2432  
          field = c.getDeclaredField("debug");
2433  
          break;
2434  
        } catch  (NoSuchFieldException e) {
2435  
          c = c.getSuperclass();
2436  
        }
2437  
      field.setBoolean(null, true);
2438  
    } catch (Exception e) {
2439  
      e.printStackTrace();
2440  
      System.err.println("Cannot debug class " + name);
2441  
    }
2442  
  }
2443  
2444  
static int choice(String text, int i) {
2445  
  return i;
2446  
}
2447  
  
2448  
  // splits the input at some point, takes only one part
2449  
  static class LSplitInput extends LearnerImpl {
2450  
    int splitIdx = 1; // split after first character
2451  
    Learner baseLearner;
2452  
    
2453  
    LSplitInput(Learner baseLearner) {
2454  
      this.baseLearner = baseLearner;
2455  
      splitIdx = choice("LSplitInput.i", splitIdx);
2456  
    }
2457  
    
2458  
    public void processInOut(Object _in, Object _out) {
2459  
      String in = (String) _in, out = (String) _out;
2460  
      in = in.substring(splitIdx);
2461  
      baseLearner.processInOut(in, out);
2462  
    }
2463  
    
2464  
    public Object processIn(Object _in) {
2465  
      String in = (String) _in;
2466  
      in = in.substring(splitIdx);
2467  
      return baseLearner.processIn(in);
2468  
    }
2469  
    
2470  
    public void toJava(Code code) {
2471  
      if (splitIdx != 0)
2472  
        code.line(code.var + " = ((String) " + code.var + ").substring(" + splitIdx + ");");
2473  
      baseLearner.toJava(code);
2474  
    }
2475  
  }
2476  
2477  
  static class FDropFirstLines implements Function {
2478  
    int n;
2479  
    FDropFirstLines(int n) {
2480  
  this.n = n;}
2481  
2482  
    public Object process(Object _in) {
2483  
      String in = (String)_in;
2484  
      for (int I=0; I < n; I++)
2485  
        in = in.substring(in.indexOf('\n')+1);
2486  
      return in;
2487  
    }
2488  
    
2489  
    public void toJava_process(Code code) {
2490  
      todo();
2491  
    }
2492  
  }
2493  
  
2494  
  // removes common suffix from out, delegates to base learner
2495  
  static class LOutSuffix extends LearnerImpl {
2496  
    String suffixOut = "";
2497  
    Learner baseLearner;
2498  
    
2499  
    LOutSuffix(Learner baseLearner) {
2500  
      this.baseLearner = baseLearner;
2501  
    }
2502  
    
2503  
    public void processInOut(Object _in, Object _out) {
2504  
      String in = (String) _in, out = (String) _out;
2505  
      if (out.endsWith("!"))
2506  
        suffixOut = "!";
2507  
      if (out.endsWith(suffixOut))
2508  
        out = out.substring(0, out.length()-suffixOut.length());
2509  
      
2510  
      baseLearner.processInOut(in, out);
2511  
    }
2512  
    
2513  
    public Object processIn(Object _in) {
2514  
      String in = (String) _in;
2515  
      return baseLearner.processIn(in) + suffixOut;
2516  
    }
2517  
    
2518  
    public void toJava(Code code) {
2519  
      baseLearner.toJava(code);
2520  
      if (suffixOut.length() != 0)
2521  
        code.line(code.var + " = " + code.s() + "+" + quote(suffixOut) + ";");
2522  
    }
2523  
  }
2524  
  
2525  
  // if input appears in output in fixed pattern
2526  
  static class LOutPattern extends LearnerImpl {
2527  
    static boolean debug;
2528  
    String pattern = "%!%";
2529  
2530  
    public void processInOut(Object _in, Object _out) {
2531  
      String in = (String) _in, out = (String) _out;
2532  
      pattern = out.replace(in, "%!%");
2533  
      if (debug)
2534  
        System.out.println("LOutPattern: in=" + quote(in) + ", out=" + quote(out) + ", pattern=" + quote(pattern));
2535  
    }
2536  
    
2537  
    public String processIn(Object _in) {
2538  
      String in = (String) _in;
2539  
      return pattern.replace("%!%", in);
2540  
    }
2541  
    
2542  
    public void toJava(Code code) {
2543  
      code.line(code.var + " = " + quote(pattern) + ".replace(" + quote("%!%") + ", (String) " + code.var + ");");
2544  
    }
2545  
  }
2546  
  
2547  
  // simplets learner - only knows the "id" function
2548  
  static class LId extends LearnerImpl {
2549  
    public void processInOut(Object in, Object out) {
2550  
    }
2551  
    
2552  
    public Object processIn(Object in) {
2553  
      return in;
2554  
    }
2555  
    
2556  
    public void toJava(Code code) {
2557  
    }
2558  
  }
2559  
  
2560  
  // for "find" tasks (e.g. "abcde" to "[[abc]]de")
2561  
  static class LInputPattern extends LearnerImpl {
2562  
    String regexp = "", findMarker1 = "[[", findMarker2 = "]]";
2563  
    
2564  
    public void processInOut(Object _in, Object _out) {
2565  
      String in = (String) _in, out = (String) _out;
2566  
      int i = out.indexOf(findMarker1), j = out.indexOf(findMarker2, i+1);
2567  
      if (j < 0) return;
2568  
      String s = out.substring(i+2, j);
2569  
      regexp = s.replaceAll("\\d+", Matcher.quoteReplacement("\\d+"));
2570  
      System.out.println("regexp: " + regexp);
2571  
    }
2572  
    
2573  
    public String processIn(Object _in) {
2574  
      String in = (String) _in;
2575  
      if (regexp.length() == 0)
2576  
        return in;
2577  
      else
2578  
        return in.replaceAll("(" + regexp + ")", findMarker1 + "$1" + findMarker2);
2579  
    }
2580  
    
2581  
    public void toJava(Code code) {
2582  
      code.line(code.var + " = ((String) " + code.var + ").replaceAll(" + quote("(" + regexp + ")") + ", \"[[$1]]\");");
2583  
    }
2584  
  }
2585  
  
2586  
  static class LFixed extends LearnerImpl {
2587  
    static boolean debug;
2588  
    Object value;
2589  
    
2590  
    public void processInOut(Object in, Object out) {
2591  
      value = out;
2592  
      if (debug)
2593  
        printVars();
2594  
    }
2595  
    
2596  
    public Object processIn(Object in) {
2597  
      return value;
2598  
    }
2599  
    
2600  
    public void toJava(Code code) {
2601  
      code.line(code.var + " = " + quote((String) value) + ";");
2602  
    }
2603  
  }
2604  
  
2605  
  static void fail() {
2606  
    throw new RuntimeException("fail");
2607  
  }
2608  
  
2609  
  static void fail(String msg) {
2610  
    throw new RuntimeException(msg);
2611  
  }
2612  
  
2613  
  static void assertSameSize(List a, List b) {
2614  
    if (a.size() != b.size())
2615  
      fail("wrong list sizes");
2616  
  }
2617  
2618  
  // process lists in parallel
2619  
  // (in and out must be a list of same length)
2620  
  static class LEach extends LearnerImpl {
2621  
    static boolean debug;
2622  
    Learner base;
2623  
    
2624  
    LEach(Learner base) {
2625  
      this.base = base;
2626  
    }
2627  
    
2628  
    public void processInOut(Object _in, Object _out) {
2629  
      List in = (List) _in, out = (List) _out;
2630  
      assertSameSize(in, out);
2631  
      for (int i = 0; i < in.size(); i++)
2632  
        base.processInOut(in.get(i), out.get(i));
2633  
      if (debug)
2634  
        printVars();
2635  
    }
2636  
    
2637  
    public Object processIn(Object _in) {
2638  
      List in = (List) _in;
2639  
      List out = new ArrayList();
2640  
      for (Object x : in)
2641  
        out.add(base.processIn(x));
2642  
      return out;
2643  
    }
2644  
    
2645  
    public void toJava(Code code) {
2646  
      code.line("List out = new ArrayList();");
2647  
      code.line("for (Object x : (List) in) {");
2648  
      code.indent();
2649  
      code.line("in = x;");
2650  
      base.toJava(code);
2651  
      code.line("out.add(in);");
2652  
      code.unindent();
2653  
      code.line("}");
2654  
      code.line("in = out;");
2655  
    }
2656  
  }
2657  
  
2658  
  static class LChange extends LearnerImpl {
2659  
    Function f;
2660  
    Learner base;
2661  
    
2662  
    LChange(Function f, Learner base) {
2663  
      this.f = f;
2664  
      this.base = base;
2665  
    }
2666  
    
2667  
    public void processInOut(Object in, Object out) {
2668  
      in = f.process(in);
2669  
      base.processInOut(in, out);
2670  
    }
2671  
    
2672  
    public Object processIn(Object in) {
2673  
      in = f.process(in);
2674  
      return base.processIn(in);
2675  
    }
2676  
    
2677  
    public void toJava(Code code) {
2678  
      f.toJava_process(code);
2679  
      base.toJava(code);
2680  
    }
2681  
  }
2682  
  
2683  
  static class LFindOutInIn extends LearnerImpl {
2684  
    static boolean debug;
2685  
    Learner base;
2686  
    String findMarker1 = "{{", findMarker2 = "}}";
2687  
    
2688  
    LFindOutInIn(Learner base) {
2689  
  this.base = base;}
2690  
    
2691  
    public void processInOut(Object _in, Object _out) {
2692  
      String in = (String) _in, out = (String) _out;
2693  
      if (out.length() == 0)
2694  
        out = in;
2695  
      else {
2696  
        int i = in.indexOf(out);
2697  
        if (i < 0) throw new RuntimeException("error");
2698  
        int j = i+out.length();
2699  
        out = in.substring(0, i) + findMarker1 + in.substring(i, j) + findMarker2 + in.substring(j);
2700  
        if (debug)
2701  
          System.out.println("LFindOutInIn: index=" + i);
2702  
      }
2703  
      
2704  
      base.processInOut(in, out);
2705  
      
2706  
      if (debug)
2707  
        System.out.println("LFindOutInIn: Base learned. " + base);
2708  
    }
2709  
    
2710  
    public Object processIn(Object _in) {
2711  
      if (debug)
2712  
        System.out.println("LFindOutInIn: processIn");
2713  
      String in = (String) _in;
2714  
      String out = (String) base.processIn(in);
2715  
      int i = out.indexOf(findMarker1), j = out.indexOf(findMarker2, i)-2;
2716  
      String result = i < 0 ? "" : in.substring(i, j);
2717  
      if (debug)
2718  
        System.out.println("LFindOutInIn: processIn " + i + " " + j + " " + quote(result));
2719  
      return result;
2720  
    }
2721  
    
2722  
    public void tryAgain() {
2723  
      base.tryAgain();
2724  
    }
2725  
  }
2726  
  
2727  
  static class LWrap extends LearnerImpl {
2728  
    ReversibleFunction f;
2729  
    Learner base;
2730  
    
2731  
    LWrap(ReversibleFunction f, Learner base) {
2732  
  this.base = base;
2733  
  this.f = f;}
2734  
    
2735  
    public void processInOut(Object in, Object out) {
2736  
      in = f.process(in);
2737  
      out = f.process(out);
2738  
      base.processInOut(in, out);
2739  
    }
2740  
    
2741  
    public Object processIn(Object in) {
2742  
      in = f.process(in);
2743  
      in = base.processIn(in);
2744  
      in = f.unprocess(in);
2745  
      return in;
2746  
    }
2747  
    
2748  
    public void toJava(Code code) {
2749  
      f.toJava_process(code);
2750  
      base.toJava(code);
2751  
      f.toJava_unprocess(code);
2752  
    }
2753  
  }
2754  
  
2755  
  static class RFJavaTok implements ReversibleFunction {
2756  
    public Object process(Object in) {
2757  
      return JavaTok.split((String) in);
2758  
    }
2759  
    
2760  
    public Object unprocess(Object in) {
2761  
      return JavaTok.join((List) in);
2762  
    }
2763  
    
2764  
    public void toJava_process(Code code) {
2765  
      code.translators("!636", "!686");
2766  
      code.line(code.var + " = JavaTok.split((String) " + code.var + ");");
2767  
    }
2768  
    
2769  
    public void toJava_unprocess(Code code) {
2770  
      code.translators("!636", "!686");
2771  
      code.line(code.var + " = JavaTok.join((List) " + code.var + ");");
2772  
    }
2773  
  }
2774  
  
2775  
  static class RFToLines implements ReversibleFunction {
2776  
    public Object process(Object in) {
2777  
      return toLines((String) in);
2778  
    }
2779  
    
2780  
    public Object unprocess(Object in) {
2781  
      return fromLines((List) in);
2782  
    }
2783  
    
2784  
    public void toJava_process(Code code) {
2785  
      code.translators("!636", "!629");
2786  
      code.assign("toLines(" + code.s() + ");");
2787  
    }
2788  
    
2789  
    public void toJava_unprocess(Code code) {
2790  
      code.translators("!636", "!629");
2791  
      code.assign("fromLines(" + code.list() + ");");
2792  
    }
2793  
  }
2794  
  
2795  
  // works on a token list - makes a list of only the string constants (unquoted)
2796  
  static class FStringsOnly implements Function {
2797  
    static boolean debug;
2798  
    
2799  
    public Object process(Object _in) {
2800  
      List<String> tok = new ArrayList<String>();
2801  
      for (String s : (List<String>) _in) {
2802  
        boolean isString = s.startsWith("\"") || s.startsWith("[[");
2803  
        if (isString)
2804  
          tok.add(unquote(s));
2805  
        if (debug)
2806  
          System.out.println("FStringsOnly - isString: " + isString + " - " + s);
2807  
      }
2808  
      return tok;
2809  
    }
2810  
    
2811  
    public void toJava_process(Code code) {
2812  
      code.translators("!629");
2813  
      code.line("List<String> tok = new ArrayList<String>();");
2814  
      code.line("for (String s : (List<String>) " + code.var + ")");
2815  
      code.line("  if (s.startsWith(\"\\\"\") || s.startsWith(\"[[\")) tok.add(unquote(s));");
2816  
      code.assign("tok");
2817  
    }
2818  
  }
2819  
  
2820  
  // works on a token list - makes a list of only the number constants
2821  
  static class FNumbersOnly implements Function {
2822  
    static boolean debug;
2823  
    
2824  
    public Object process(Object _in) {
2825  
      List<String> tok = new ArrayList<String>();
2826  
      for (String s : (List<String>) _in) {
2827  
        boolean isNumber = s.length() != 0 && (Character.isDigit(s.charAt(0)) || (s.charAt(0) == '-' && s.length() > 1));
2828  
        if (isNumber)
2829  
          tok.add(s);
2830  
        if (debug)
2831  
          System.out.println("FNumbersOnly - isNumber: " + isNumber + " - " + s);
2832  
      }
2833  
      return tok;
2834  
    }
2835  
    
2836  
    public void toJava_process(Code code) {
2837  
      code.line("List<String> tok = new ArrayList<String>();");
2838  
      code.line("for (String s : (List<String>) " + code.var + ")");
2839  
      code.line("  if (s.length() != 0 && (Character.isDigit(s.charAt(0)) || (s.charAt(0) == '-' && s.length() > 1))) tok.add(s);");
2840  
      code.assign("tok");
2841  
    }
2842  
  }
2843  
  
2844  
  static class FLength implements Function {
2845  
    static boolean debug;
2846  
    
2847  
    public Object process(Object in) {
2848  
      Object result = String.valueOf(in instanceof List ? ((List) in).size() : ((String) in).length());
2849  
      if (debug)
2850  
        System.out.println("FLength: " + result);
2851  
      return result;
2852  
    }
2853  
    
2854  
    public void toJava_process(Code code) {
2855  
      code.assign("String.valueOf(" + code.var + " instanceof List ? " + code.list() + ".size() : " + code.s() + ".length())");
2856  
    }
2857  
  }
2858  
  
2859  
  static class LFixedFunction extends LearnerImpl {
2860  
    Function f;
2861  
    
2862  
    LFixedFunction(Function f) {
2863  
      this.f = f;
2864  
    }
2865  
    
2866  
    public void processInOut(Object in, Object out) {
2867  
    }
2868  
    
2869  
    public Object processIn(Object in) {
2870  
      return f.process(in);
2871  
    }
2872  
    
2873  
    public void toJava(Code code) {
2874  
      f.toJava_process(code);
2875  
    }
2876  
  }
2877  
  
2878  
  // trivial stuff like taking the one element out of a 1-element list
2879  
  static class LTrivial extends LearnerImpl {
2880  
    public void processInOut(Object in, Object out) {
2881  
    }
2882  
    
2883  
    public Object processIn(Object in) {
2884  
      return ((List) in).get(0);
2885  
    }
2886  
    
2887  
    public void toJava(Code code) {
2888  
      code.assign(code.list() + ".get(0)");
2889  
    }
2890  
  }
2891  
  
2892  
  // get element of a list at fixed index
2893  
  static class LGetListElement extends LearnerImpl {
2894  
    static boolean debug;
2895  
    int i;
2896  
    
2897  
    public void processInOut(Object _in, Object out) {
2898  
      List in = (List) _in;
2899  
      i = in.indexOf(out);
2900  
      if (debug)
2901  
        System.out.println("LGetListElement: " + i + " " + out);
2902  
    }
2903  
    
2904  
    public Object processIn(Object in) {
2905  
      return ((List) in).get(i);
2906  
    }
2907  
    
2908  
    public void toJava(Code code) {
2909  
      code.assign(code.list() + ".get(" + i + ")");
2910  
    }
2911  
  }
2912  
  
2913  
  // math operations
2914  
  static class LMath extends LearnerImpl {
2915  
    static boolean debug;
2916  
    String allOperations = "+ - * /";
2917  
    Set<String> possibleOperations = new TreeSet<String>();
2918  
    
2919  
    LMath() {
2920  
      possibleOperations.addAll(Arrays.asList(allOperations.split(" +")));
2921  
    }
2922  
    
2923  
    public void processInOut(Object _in, Object _out) {
2924  
      List in = (List) _in;
2925  
      String out = (String) _out;
2926  
      BigInteger[] inNumbers = getNumbers(in);
2927  
      BigInteger[] outNumbers = new BigInteger[] {getNumber(out)};
2928  
      findOperation(inNumbers, outNumbers);
2929  
      if (debug)
2930  
        System.out.println("Operations: " + possibleOperations);
2931  
    }
2932  
    
2933  
    public void findOperation(BigInteger[] in, BigInteger[] out) {
2934  
      filterOperations(in, out);
2935  
      if (possibleOperations.isEmpty())
2936  
        fail("tilt");
2937  
    }
2938  
      
2939  
    public void filterOperations(BigInteger[] in, BigInteger[] out) {
2940  
      for (Iterator<String> i = possibleOperations.iterator(); i.hasNext(); ) {
2941  
        String op = i.next();
2942  
        BigInteger[] out2 = doOperation(op, in);
2943  
        if (out2 == null || !arraysEqual(out, out2))
2944  
          i.remove(); // keep only matching operations
2945  
      }
2946  
    }
2947  
    
2948  
    public BigInteger[] doOperation(String op, BigInteger[] in) {
2949  
      op = op.intern();
2950  
      try {
2951  
        if (in.length == 2) {
2952  
          BigInteger a = in[0], b = in[1], x = null;
2953  
          if (op == "+")
2954  
            x = a.add(b);
2955  
          else if (op == "-")
2956  
            x = a.subtract(b);
2957  
          else if (op == "*")
2958  
            x = a.multiply(b);
2959  
          else if (op == "/")
2960  
            x = a.divide(b);
2961  
          return x != null ? new BigInteger[] {x} : null;
2962  
        }
2963  
        return null;
2964  
      } catch (Throwable e) {
2965  
        return null;
2966  
      }
2967  
    }
2968  
    
2969  
    public String processIn(Object _in) {
2970  
      List<String> in = (List<String>) _in;
2971  
      String op = possibleOperations.iterator().next();
2972  
      if (debug)
2973  
        System.out.println("op: " + op);
2974  
      BigInteger[] inNumbers = getNumbers(in);
2975  
      BigInteger[] outNumbers = doOperation(op, inNumbers);
2976  
      return outNumbers[0].toString();
2977  
    }
2978  
    
2979  
    String BI = BigInteger.class.getName();
2980  
    
2981  
    public void toJava(Code code) {
2982  
      String op = possibleOperations.iterator().next();
2983  
      String a = "new " + BI + "((String) " + code.list() + ".get(0))";
2984  
      String b = "new " + BI + "((String) " + code.list() + ".get(1))";
2985  
      if (op.equals("+"))
2986  
        code.assign(a + ".add(" + b + ").toString()");
2987  
      else
2988  
        todo();
2989  
    }
2990  
    
2991  
    static BigInteger[] getNumbers(List<String> in) {
2992  
      BigInteger[] big = new BigInteger[in.size()];
2993  
      for (int i = 0; i < in.size(); i++)
2994  
        big[i] = new BigInteger(in.get(i));
2995  
      return big;
2996  
    }
2997  
    
2998  
    static BigInteger getNumber(String s) {
2999  
      return new BigInteger(s);
3000  
    }
3001  
    
3002  
    static boolean arraysEqual(BigInteger[] a, BigInteger[] b) {
3003  
      if (a.length != b.length) return false;
3004  
      for (int i = 0; i < a.length; i++)
3005  
        if (!a[i].equals(b[i])) return false;
3006  
      return true;
3007  
    }
3008  
  }
3009  
  
3010  
  static class EscapeCase implements Function {
3011  
    static boolean debug;
3012  
    
3013  
    public Object process(Object _in) {
3014  
      if (debug)
3015  
        System.out.println("EscapeCase: " + _in);
3016  
      String in = (String) _in;
3017  
      return in.equals("case") ? "_case" : in;
3018  
    }
3019  
    
3020  
    public void toJava_process(Code code) {
3021  
      code.line("if (\"case\".equals(" + code.var + ")) " + code.var + " = " + quote("_case") + ";");
3022  
    }
3023  
  }
3024  
  
3025  
  static class LCharShift extends LearnerImpl {
3026  
    int shift;
3027  
    
3028  
    public void processInOut(Object _in, Object _out) {
3029  
      String in = (String) _in, out = (String) _out;
3030  
      shift = (int) out.charAt(0) - (int) in.charAt(0);
3031  
    }
3032  
    
3033  
    public Object processIn(Object _in) {
3034  
      String in = (String) _in;
3035  
      char[] c = new char[in.length()];
3036  
      for (int i = 0; i < c.length; i++)
3037  
        c[i] = (char) ((int) in.charAt(i) + shift);
3038  
      return new String(c);
3039  
    }
3040  
    
3041  
    public void toJava(Code code) {
3042  
      code.line("char[] c = new char[((String) " + code.var + ").length()];");
3043  
      code.line("for (int i = 0; i < c.length; i++)");
3044  
      code.line("  c[i] = (char) ((int) ((String) " + code.var + ").charAt(i)" + (shift < 0 ? "" + shift : "+" + shift) + ");");
3045  
      code.line(code.var + " = new String(c);");
3046  
    }
3047  
  }
3048  
  
3049  
  // applies base learner to first char of string
3050  
  // (or first element of list, TODO)
3051  
  static class LFirst extends LearnerImpl {
3052  
    Learner baseLearner;
3053  
    
3054  
    LFirst(Learner baseLearner) {
3055  
      this.baseLearner = baseLearner;
3056  
    }
3057  
    
3058  
    public void processInOut(Object _in, Object _out) {
3059  
      String in = (String) _in, out = (String) _out;
3060  
      if (in.length() == 0)
3061  
        return;
3062  
      String firstIn = in.substring(0, 1), firstOut = out.substring(0, 1);
3063  
      baseLearner.processInOut(firstIn, firstOut);
3064  
    }
3065  
    
3066  
    public Object processIn(Object _in) {
3067  
      String in = (String) _in;
3068  
      if (in.length() == 0)
3069  
        return in;
3070  
      String firstIn = in.substring(0, 1);
3071  
      return baseLearner.processIn(firstIn) + in.substring(1);
3072  
    }
3073  
    
3074  
    public void toJava(Code code) {
3075  
      code.line("if (" + code.s() + ".length() != 0) {");
3076  
      code.indent();
3077  
      code.line("String rest = " + code.s() + ".substring(1);");
3078  
      code.line(code.var + " = " + code.s() + ".substring(0, 1);");
3079  
      baseLearner.toJava(code);
3080  
      code.line(code.var + " = " + code.s() + "+rest;");
3081  
      code.unindent();
3082  
      code.line("}");
3083  
    }
3084  
  }
3085  
  
3086  
  static Method findMainMethod(Class<?> theClass) {
3087  
    for (Method method : theClass.getMethods())
3088  
      if (method.getName().equals("main") && method.getParameterTypes().length == 1)
3089  
        return method;
3090  
    throw new RuntimeException("Method 'main' with 1 parameter not found in " + theClass.getName());
3091  
  }
3092  
  
3093  
  // compile JavaX source and load main class
3094  
  static Class<?> compileAndLoadMainClass(String src) throws Exception {
3095  
    File srcDir = _x18.TempDirMaker_make();
3096  
    File classesDir = _x18.TempDirMaker_make();
3097  
    _x18.saveTextFile(new File(srcDir, "main.java").getPath(), src);
3098  
    List<File> libraries = new ArrayList<File>();
3099  
    File transpiledDir = _x18.topLevelTranslate(srcDir, libraries);
3100  
    String javacOutput = _x18.compileJava(transpiledDir, libraries, classesDir);
3101  
    System.out.println(javacOutput);
3102  
    URL[] urls = {classesDir.toURI().toURL()};
3103  
    
3104  
    // make class loader
3105  
    URLClassLoader classLoader = new URLClassLoader(urls);
3106  
3107  
    // load main class
3108  
    Class<?> mainClass = classLoader.loadClass("main");
3109  
    return mainClass;
3110  
  }
3111  
      
3112  
  static Method compileJavaInToOut(Code code) {
3113  
    try {
3114  
      String java = code.buf.toString();
3115  
      String prelude = /*"import java.util.*;\n\n" +*/
3116  
        "public class main { public static Object main(Object in) throws Exception {\n";
3117  
      String postlude = "\nreturn in;\n}}";
3118  
      String src = code.getTranslators() + "\n" + prelude + java + postlude;
3119  
      Class<?> mainClass = compileAndLoadMainClass(src);
3120  
      return findMainMethod(mainClass);
3121  
    } catch (Exception e) {
3122  
      throw new RuntimeException(e);
3123  
    }
3124  
  }
3125  
  
3126  
  static Method findCalcMethod(Class<?> theClass) {
3127  
    for (Method method : theClass.getMethods())
3128  
      if (method.getName().equals("calc"))
3129  
        return method;
3130  
    throw new RuntimeException("Method 'calc' not found in " + theClass.getName());
3131  
  }
3132  
  
3133  
  // for simplejava stuff (execute tasks)
3134  
  static String execute(String src) {
3135  
    try {
3136  
      Class<?> mainClass = compileAndLoadMainClass(src);
3137  
      Method m = findCalcMethod(mainClass);
3138  
      return String.valueOf(m.invoke(null));
3139  
    } catch (Exception e) {
3140  
      throw new RuntimeException(e);
3141  
    }
3142  
  }
3143  
  
3144  
  static void testJava(Case _case, Code code) {
3145  
    try {
3146  
      Method m = compileJavaInToOut(code);
3147  
      
3148  
      for (Object[] e : _case.fullExamples) {
3149  
        String out = (String) m.invoke(null, e[0]);
3150  
        
3151  
        if (!e[1].equals(out)) {
3152  
          throw new RuntimeException("[fail] Java code on " + structure(e[0]) + " - got: " + structure(out) + " rather than: " + structure(e[1]));
3153  
        }
3154  
      }
3155  
      
3156  
      System.out.println("\nGOOD JAVA.");
3157  
      _case.goodJava = true;
3158  
    } catch (Throwable e) {
3159  
      if (showFails)
3160  
        e.printStackTrace();
3161  
      System.out.println("\nBAD JAVA.");
3162  
    }
3163  
  }
3164  
  
3165  
  static void todo() {
3166  
    fail("todo");
3167  
  }
3168  
  
3169  
  static class LSwitch extends LearnerImpl {
3170  
    Case _case;
3171  
    Learner switcher;
3172  
    
3173  
    LSwitch(Case _case, Learner switcher) {
3174  
      this._case = _case;
3175  
      this.switcher = switcher;
3176  
    }
3177  
    
3178  
    public void processInOut(Object in, Object out) {
3179  
    }
3180  
   
3181  
    public Object processIn(Object in) {
3182  
      int i = Integer.parseInt((String) switcher.processIn(in));
3183  
      return _case.combined.get(i-1).winner.processIn(in);
3184  
    }
3185  
   
3186  
    public void toJava(Code code) {
3187  
      todo();
3188  
    }
3189  
  }
3190  
 
3191  
  /*static class LTryPrevious extends LearnerImpl {
3192  
    List<Case> candidates = new ArrayList<Case>();
3193  
    
3194  
    *() {
3195  
      for (int i = caseIdx-1; i >= 0; i--)
3196  
        candidates.add(cases.get(i));
3197  
    }
3198  
3199  
    public void processInOut(Object _in, Object _out) {
3200  
      String in = (String) _in, out = (String) _out;
3201  
      for (ListIterator<Case> i = candidates.listIterator(); i.hasNext(); ) {
3202  
        Case case = i.next();
3203  
        if (case.winner == null || !validate(new String[] {in, out}, case.winner))
3204  
          i.remove();
3205  
      }
3206  
      if (candidates.isEmpty())
3207  
        fail("no previous solution found");
3208  
    }
3209  
  
3210  
    public Object processIn(Object in) {
3211  
      return candidates.get(0).winner.processIn(in);
3212  
    }
3213  
  
3214  
    public void toJava(Code code) {
3215  
      candidates.get(0).winner.toJava(code);
3216  
    }
3217  
  }*/
3218  
  
3219  
  static class LMulti extends LearnerImpl {
3220  
    static boolean debug;
3221  
    List<Learner> candidates = new ArrayList<Learner>();
3222  
    
3223  
    LMulti(Learner... learners) {
3224  
      for (Learner l : learners)
3225  
        candidates.add(l);
3226  
    }
3227  
    
3228  
    LMulti(List<Learner> learners) {
3229  
      for (Learner l : learners)
3230  
        candidates.add(l);
3231  
    }
3232  
3233  
    LMulti(Learner l, List<Learner> ll) {
3234  
       candidates.add(l);
3235  
      candidates.addAll(ll);
3236  
    }
3237  
    
3238  
    public void processInOut(Object in, Object out) {
3239  
      if (debug)
3240  
        System.err.println("LMulti candidates: " + candidates.size());
3241  
      for (ListIterator<Learner> i = candidates.listIterator(); i.hasNext(); ) {
3242  
        Learner l = i.next();
3243  
        try {
3244  
          l.processInOut(in, out);
3245  
        } catch (Throwable e) {
3246  
          if (debug) {
3247  
            e.printStackTrace();
3248  
            System.err.println("Removing candidate: " + structure(l));
3249  
          }
3250  
          silentException(e);
3251  
          i.remove();
3252  
        }
3253  
      }
3254  
      if (debug)
3255  
        System.err.println("LMulti candidates now: " + candidates.size());
3256  
      if (candidates.isEmpty())
3257  
        fail("no candidates left");
3258  
    }
3259  
    
3260  
    public Object processIn(Object in) {
3261  
      while (true) { // fails or returns eventually
3262  
        Learner l = candidates.get(0);
3263  
        if (debug)
3264  
          System.err.println("Using candidate: " + structure(l) + ", " + candidates.size() + " left");
3265  
        try {
3266  
          return l.processIn(in);
3267  
        } catch (Throwable e) {
3268  
          if (debug) {
3269  
            e.printStackTrace();
3270  
            System.err.println("Removing candidate: " + structure(l));
3271  
          }
3272  
          silentException(e);
3273  
          candidates.remove(0);
3274  
        }
3275  
      }
3276  
    }
3277  
    
3278  
    public void tryAgain() {
3279  
      candidates.remove(0);
3280  
    }
3281  
    
3282  
    public void toJava(Code code) {
3283  
      candidates.get(0).toJava(code);
3284  
    }
3285  
  }
3286  
  
3287  
  static String structure(Object o) {
3288  
    if (o == null) return "null";
3289  
    String name = o.getClass().getName();
3290  
    
3291  
    StringBuilder buf = new StringBuilder();
3292  
    
3293  
    if (o instanceof Collection) {
3294  
      for (Object x : (Collection) o) {
3295  
        if (!(buf.length() == 0)) buf.append(", ");
3296  
        buf.append(structure(x));
3297  
      }
3298  
      return "{" + buf + "}";
3299  
    }
3300  
3301  
    if (o instanceof String)
3302  
      return quote((String) o);
3303  
    
3304  
    // Need more cases? This should cover all library classes...
3305  
    if (name.startsWith("java.") || name.startsWith("javax."))
3306  
      return String.valueOf(o);
3307  
      
3308  
    String shortName = o.getClass().getName().replaceAll("^main\\$", "");
3309  
3310  
    // TODO: go to superclasses too
3311  
    Field[] fields = o.getClass().getDeclaredFields();
3312  
    int numFields = 0;
3313  
    String fieldName = "";
3314  
    for (Field field : fields) {
3315  
      if ((field.getModifiers() & Modifier.STATIC) != 0)
3316  
        continue;
3317  
      Object value;
3318  
      try {
3319  
        value = field.get(o);
3320  
      } catch (Exception e) {
3321  
        value = "?";
3322  
      }
3323  
      
3324  
      fieldName = field.getName();
3325  
      
3326  
      // special case for LMulti - show only first (current) candidate
3327  
      if (shortName.equals("LMulti") && field.getName().equals("candidates")) {
3328  
        fieldName = "candidate";
3329  
        value = ((List) value).isEmpty() ? value : ((List) value).get(0);
3330  
      }
3331  
        
3332  
      if (value != null) {
3333  
        if (!(buf.length() == 0)) buf.append(", ");
3334  
        buf.append(fieldName + "=" + structure(value));
3335  
      }
3336  
      ++numFields;
3337  
    }
3338  
    String b = buf.toString();
3339  
    if (numFields == 1)
3340  
      b = b.replaceAll("^" + fieldName + "=", ""); // drop field name if only one
3341  
    String s = shortName;
3342  
    if (!(buf.length() == 0))
3343  
      s += "(" + b + ")";
3344  
    return s;
3345  
  }
3346  
  
3347  
  static class FIsString implements Function {
3348  
    static boolean debug;
3349  
    
3350  
    public Object process(Object _in) {
3351  
      String in = (String) _in;
3352  
      return in.startsWith("\"") || in.startsWith("[[") ? "1" : "0";
3353  
    }
3354  
    
3355  
    public void toJava_process(Code code) {
3356  
      code.assign(code.s() + ".startsWith(\"\\\"\") || " + code.s() + ".startsWith(\"[[\") ? \"1\" : \"0\"");
3357  
    }
3358  
  }
3359  
  
3360  
  static class FCommonPrefix implements Function {
3361  
    static boolean debug;
3362  
3363  
    public Object process(Object _in) {
3364  
      String in = (String) _in, prefix = null;
3365  
      for (String line : toLines(in))
3366  
        if (line.length() != 0) {
3367  
          prefix = prefix == null ? line : commonPrefix(prefix, line);
3368  
          if (debug)
3369  
            System.out.println("FCommonPrefix: line=" + quote(line) + ", prefix=" + quote(prefix));
3370  
        }
3371  
      return prefix;
3372  
    }
3373  
    
3374  
    public void toJava_process(Code code) {
3375  
      todo();
3376  
    }
3377  
  }
3378  
  
3379  
  static class FCommonSuffix implements Function {
3380  
    static boolean debug;
3381  
3382  
    public Object process(Object _in) {
3383  
      String in = (String) _in, suffix = null;
3384  
      for (String line : toLines(in))
3385  
        if (line.length() != 0) {
3386  
          suffix = suffix == null ? line : commonSuffix(suffix, line);
3387  
          if (debug)
3388  
            System.out.println("FCommonSuffix: line=" + quote(line) + ", suffix=" + quote(suffix));
3389  
        }
3390  
      return suffix;
3391  
    }
3392  
    
3393  
    public void toJava_process(Code code) {
3394  
      todo();
3395  
    }
3396  
  }
3397  
  
3398  
  /*static class Pair {
3399  
    Object a, b;
3400  
    
3401  
    public boolean equals(Object o) {
3402  
      return o instanceof Pair && a.equals(((Pair) o).a) && b.equals((Pair) o).b);
3403  
    }
3404  
    
3405  
    public int hashCode() {
3406  
      return a.hashCode()+b.hashCode()*2;
3407  
    }
3408  
  }*/
3409  
  
3410  
  static class Matrix {
3411  
    HashMap<Object, Map> dataByCol = new HashMap<Object, Map>();
3412  
    
3413  
    void put(Object col, Object row, Object value) {
3414  
      //data.put(new Pair(col, row), value);
3415  
      getCol(col).put(row, value);
3416  
    }
3417  
    
3418  
    Map getCol(Object col) {
3419  
      Map map = dataByCol.get(col);
3420  
      if (map == null)
3421  
        dataByCol.put(col, map = new HashMap());
3422  
      return map;
3423  
    }
3424  
    
3425  
    Object get(Object col, Object row) {
3426  
      //return data.get(new Pair(col, row));
3427  
      return getCol(col).get(row);
3428  
    }
3429  
    
3430  
    Set cols() {
3431  
      return dataByCol.keySet();
3432  
    }
3433  
    
3434  
    Set rowsFor(Object col) {
3435  
      return getCol(col).keySet();
3436  
    }
3437  
  }
3438  
  
3439  
  static class LDistinguishList extends LearnerImpl {
3440  
    static boolean debug;
3441  
    Function helper;
3442  
    int idx;
3443  
    Matrix matrix = new Matrix();
3444  
3445  
    LDistinguishList(Function helper) {
3446  
      this.helper = helper;
3447  
    }
3448  
    
3449  
    public void processInOut(Object _in, Object out) {
3450  
      List in = (List) _in;
3451  
      for (int i = 0; i < in.size(); i++) {
3452  
        Object y = helper.process(in.get(i));
3453  
        matrix.put(i, y, out);
3454  
      }
3455  
    }
3456  
    
3457  
    public Object processIn(Object _in) {
3458  
      // find idx
3459  
      
3460  
      for (Object i : matrix.cols()) {
3461  
        Collection ys = matrix.rowsFor(i);
3462  
        if (ys.size() > 1) {
3463  
          idx = (Integer) i;
3464  
          break;
3465  
        }
3466  
      }
3467  
      
3468  
      List in = (List) _in;
3469  
      Object y = helper.process(in.get(idx));
3470  
      return matrix.get(idx, y);
3471  
    }
3472  
    
3473  
    public void toJava(Code code) {
3474  
      List ys = new ArrayList(matrix.rowsFor(idx));
3475  
      //String v = code.var;
3476  
      //code.newVar();
3477  
      //String vy = code.var;
3478  
      //code.oldVar();
3479  
      code.assign(code.list() + ".get(" + idx + ")");
3480  
      helper.toJava_process(code);
3481  
      
3482  
      for (int i = 0; i < ys.size(); i++) {
3483  
        Object y = ys.get(i);
3484  
        if (i < ys.size())
3485  
          code.line((i == 0 ? "" : "else ") + "if (" + quote((String) y) + ".equals(" + code.var + "))");
3486  
        else
3487  
          code.line("else");
3488  
        code.line("  " + code.var + " = " + quote((String) matrix.get(idx, y)) + ";");
3489  
      }
3490  
    }
3491  
  }
3492  
  
3493  
  static class RunnersUp {
3494  
    String expected;
3495  
    int bestScore = -1;
3496  
    String bestResult;
3497  
    Learner winner;
3498  
    
3499  
    void add(String expected, String result, int score, Learner learner) {
3500  
      if (!expected.equals(this.expected) || bestScore == -1 || score < bestScore) {
3501  
        bestScore = score;
3502  
        bestResult = result;
3503  
        winner = learner;
3504  
      }
3505  
      this.expected = expected;
3506  
    }
3507  
  }
3508  
  
3509  
  static List<Case> casesSortedByID() {
3510  
    Map<Long, Case> map = new TreeMap<Long, Case>();
3511  
    List<Case> rest = new ArrayList<Case>();
3512  
    for (Case c : cases)
3513  
      if (isSnippetID(c.id))
3514  
        map.put(parseSnippetID(c.id), c);
3515  
      else
3516  
        rest.add(c);
3517  
    List<Case> list = new ArrayList<Case>(map.values());
3518  
    list.addAll(rest);
3519  
    return list;
3520  
  }
3521  
  
3522  
  static int leven(String s, String t) {
3523  
  // degenerate cases
3524  
  if (s.equals(t)) return 0;
3525  
  if (s.length() == 0) return t.length();
3526  
  if (t.length() == 0) return s.length();
3527  
3528  
  // create two work vectors of integer distances
3529  
  int[] v0 = new int[t.length() + 1];
3530  
  int[] v1 = new int[t.length() + 1];
3531  
3532  
  // initialize v0 (the previous row of distances)
3533  
  // this row is A[0][i]: edit distance for an empty s
3534  
  // the distance is just the number of characters to delete from t
3535  
  for (int i = 0; i < v0.length; i++)
3536  
      v0[i] = i;
3537  
3538  
  for (int i = 0; i < s.length(); i++)
3539  
  {
3540  
      // calculate v1 (current row distances) from the previous row v0
3541  
3542  
      // first element of v1 is A[i+1][0]
3543  
      //   edit distance is delete (i+1) chars from s to match empty t
3544  
      v1[0] = i + 1;
3545  
3546  
      // use formula to fill in the rest of the row
3547  
      for (int j = 0; j < t.length(); j++)
3548  
      {
3549  
          int cost = s.charAt(i) == t.charAt(j) ? 0 : 1;
3550  
          v1[j + 1] = Math.min(Math.min(v1[j] + 1, v0[j + 1] + 1), v0[j] + cost);
3551  
      }
3552  
3553  
      // copy v1 (current row) to v0 (previous row) for next iteration
3554  
      /*for (int j = 0; j < v0.length; j++)
3555  
          v0[j] = v1[j];*/
3556  
          
3557  
      // swap arrays
3558  
      int[] v = v1; v1 = v0; v0 = v;
3559  
  }
3560  
3561  
  // for array copying:
3562  
  // return v1[t.length()];
3563  
  // for array swapping:
3564  
  return v0[t.length()];
3565  
} // leven (Levenshtein distance function)
3566  
  
3567  
  // These end up inside the main class
3568  
3569  
static class LBox extends LearnerImpl {
3570  
  static boolean debug;
3571  
  Learner middleLearner;
3572  
  char topChar, bottomChar;
3573  
  
3574  
  LBox(Learner middleLearner) {
3575  
    this.middleLearner = middleLearner;
3576  
  }
3577  
  
3578  
  // An optimization, but a bit of a dangerous one:
3579  
  /*public void forwardSecretExample(Object _in, Object _out) {
3580  
    String in = (String) _in, out = (String) _out;
3581  
    L<S> l = toLines(out);
3582  
    String middle = l.get(2);
3583  
    middleLearner.processInOut(in, middle);
3584  
  }*/
3585  
  
3586  
  public void tryAgain() {
3587  
    middleLearner.tryAgain();
3588  
  }
3589  
  
3590  
  public void processInOut(Object _in, Object _out) {
3591  
    String in = (String) _in, out = (String) _out;
3592  
    List<String> l = toLines(out);
3593  
    String middle = l.get(2);
3594  
    middleLearner.processInOut(in, middle);
3595  
    topChar = l.get(1).charAt(0);
3596  
    bottomChar = l.get(3).charAt(0);
3597  
  }
3598  
  
3599  
  public Object processIn(Object in) {
3600  
    String middle = (String) middleLearner.processIn(in);
3601  
    return "\n" +
3602  
      repeat(topChar, middle.length()) + "\n" +
3603  
      middle + "\n" +
3604  
      repeat(bottomChar, middle.length()) + "\n";
3605  
  }
3606  
  
3607  
  static String repeat(char c, int n) {
3608  
    char[] chars = new char[n];
3609  
    for (int i = 0; i < n; i++)
3610  
      chars[i] = c;
3611  
    return new String(chars);
3612  
  }
3613  
  
3614  
  public void toJava(Code code) {
3615  
    todo();
3616  
  }
3617  
}
3618  
3619  
static class LBox2 extends LearnerImpl {
3620  
  static boolean debug;
3621  
  Learner middleLearner;
3622  
  char topChar, bottomChar, tlChar, trChar, blChar, brChar;
3623  
  
3624  
  LBox2(Learner middleLearner) {
3625  
    this.middleLearner = middleLearner;
3626  
  }
3627  
  
3628  
  public void tryAgain() {
3629  
    middleLearner.tryAgain();
3630  
  }
3631  
  
3632  
  public void processInOut(Object _in, Object _out) {
3633  
    String in = (String) _in, out = (String) _out;
3634  
    List<String> l = toLines(out);
3635  
    String middle = l.get(2);
3636  
    if (debug)
3637  
      System.out.println("Forwarding to middle learner: " + quote(in) + " => " + quote(middle));
3638  
    middleLearner.processInOut(in, middle);
3639  
    String top = l.get(1), bottom = l.get(3);
3640  
    tlChar = top.charAt(0);
3641  
    topChar = top.charAt(1);
3642  
    trChar = top.charAt(top.length()-1);
3643  
    blChar = bottom.charAt(0);
3644  
    bottomChar = bottom.charAt(1);
3645  
    brChar = bottom.charAt(bottom.length()-1);
3646  
  }
3647  
  
3648  
  public Object processIn(Object in) {
3649  
    String middle = (String) middleLearner.processIn(in);
3650  
    return "\n" +
3651  
      tlChar + repeat(topChar, middle.length()-2) + trChar + "\n" +
3652  
      middle + "\n" +
3653  
      blChar + repeat(bottomChar, middle.length()-2) + brChar + "\n";
3654  
  }
3655  
  
3656  
  static String repeat(char c, int n) {
3657  
    char[] chars = new char[n];
3658  
    for (int i = 0; i < n; i++)
3659  
      chars[i] = c;
3660  
    return new String(chars);
3661  
  }
3662  
  
3663  
  public void toJava(Code code) {
3664  
    todo();
3665  
  }
3666  
} // Box learners
3667  
  // These end up inside the main class
3668  
3669  
static class LEmptyBox extends LearnerImpl {
3670  
  static boolean debug;
3671  
  Learner inputLearner;
3672  
  char c;
3673  
  
3674  
  LEmptyBox(Learner inputLearner) {
3675  
  this.inputLearner = inputLearner;}
3676  
  
3677  
  public void tryAgain() {
3678  
    inputLearner.tryAgain();
3679  
  }
3680  
  
3681  
  public void processInOut(Object in, Object _out) {
3682  
    String out = (String) _out;
3683  
    List<String> l = toLines(out);
3684  
    int w = l.get(1).length(), h = l.size()-1;
3685  
    String input = w + "*" + h;
3686  
    if (debug)
3687  
      System.out.println("LEmptyBox: Feeding to input learner: " + in + " => " + input);
3688  
    inputLearner.processInOut(in, input);
3689  
    if (debug)
3690  
      System.out.println("Input learner: " + structure(inputLearner));
3691  
    c = l.get(1).charAt(0);
3692  
    if (debug)
3693  
      System.out.println("LEmptyBox: c=" + c);
3694  
  }
3695  
  
3696  
  public Object processIn(Object in) {
3697  
    String input = (String) inputLearner.processIn(in);
3698  
    if (debug)
3699  
      System.out.println("LEmptyBox: in=" + in + ", input=" + input);
3700  
    String[] split = input.split("\\*");
3701  
    int w = Integer.parseInt(split[0]), h = Integer.parseInt(split[1]);
3702  
    StringBuilder buf = new StringBuilder();
3703  
    buf.append("\n");
3704  
    buf.append(repeat(c, w) + "\n");
3705  
    for (int y = 1; y < h-1; y++)
3706  
      buf.append(c + repeat(' ', w-2) + c + "\n");
3707  
    buf.append(repeat(c, w) + "\n");
3708  
    return buf.toString();
3709  
  }
3710  
  
3711  
  static String repeat(char c, int n) {
3712  
    char[] chars = new char[n];
3713  
    for (int i = 0; i < n; i++)
3714  
      chars[i] = c;
3715  
    return new String(chars);
3716  
  }
3717  
  
3718  
  public void toJava(Code code) {
3719  
    todo();
3720  
  }
3721  
}
3722  
 // Empty box learner
3723  
  
3724  
  // for "find" tasks (e.g. "abcde" to "[[abc]]de")
3725  
  static class LFixedSubstring extends LearnerImpl {
3726  
    static boolean debug;
3727  
    String findMarker1 = "{{", findMarker2 = "}}";
3728  
    int i, j;
3729  
    
3730  
    public void processInOut(Object _in, Object _out) {
3731  
      String in = (String) _in, out = (String) _out;
3732  
      i = out.indexOf(findMarker1);
3733  
      j = out.indexOf(findMarker2, i)-2;
3734  
      if (debug && j >= 0) {
3735  
        boolean correct = in.substring(i, j).equals(out.substring(i+2, j+2));
3736  
        System.out.println("LFixedSubstring: i, j = " + i + ", " + j + " " + correct + " " + quote(in.substring(i, j)));
3737  
      }
3738  
    }
3739  
    
3740  
    public String processIn(Object _in) {
3741  
      String in = (String) _in;
3742  
      return in.substring(0, i) + findMarker1 + in.substring(i, j) + findMarker2 + in.substring(j);
3743  
    }
3744  
  }
3745  
3746  
  static void print(Object o) {
3747  
    System.out.println(o);
3748  
  }
3749  
3750  
  // These end up inside the main class
3751  
3752  
static class LFixWhitespace extends LearnerImpl {
3753  
  static boolean debug;
3754  
  Learner base;
3755  
  String s;
3756  
  
3757  
  LFixWhitespace(Learner base) {
3758  
  this.base = base;}
3759  
  
3760  
  public void tryAgain() {
3761  
    base.tryAgain();
3762  
  }
3763  
  
3764  
  public void processInOut(Object _in, Object _out) {
3765  
    String in = (String) _in, out = (String) _out;
3766  
    int i = count(in), j = count(out);
3767  
    in = in.substring(i);
3768  
    s = out.substring(0, j);
3769  
    out = out.substring(j);
3770  
  if (debug)
3771  
      System.out.println("LFixWhitespace: Feeding to input learner: " + in + " => " + out);
3772  
    base.processInOut(in, out);
3773  
  }
3774  
  
3775  
  public Object processIn(Object _in) {
3776  
    String in = (String) _in;
3777  
    int i = count(in);
3778  
    in = in.substring(i);
3779  
    in = (String) base.processIn(in);
3780  
    in = s + in;
3781  
    return in;
3782  
  }
3783  
3784  
  int count(String s) {
3785  
    int I = 0;
3786  
    while (I < s.length () && "\r\n\t ".indexOf(s.charAt(I)) >= 0)
3787  
      ++I;
3788  
    return I;
3789  
  }
3790  
3791  
}
3792  
 // LFixWhiteSpace
3793  
3794  
  static Object newInstance(Class c, Object... args) { try {
3795  
 
3796  
    Constructor m = findConstructor(c, args);
3797  
    m.setAccessible(true);
3798  
    return m.newInstance(args);
3799  
  
3800  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
3801  
  
3802  
  static boolean hasConstructor(Class c, Object... args) {
3803  
    for (Constructor m : c.getDeclaredConstructors()) {
3804  
      if (!checkArgs(m.getParameterTypes(), args, false))
3805  
        continue;
3806  
      return true;
3807  
    }
3808  
    return false;
3809  
  }
3810  
  
3811  
  static Constructor findConstructor(Class c, Object... args) {
3812  
    for (Constructor m : c.getDeclaredConstructors()) {
3813  
      if (!checkArgs(m.getParameterTypes(), args, false))
3814  
        continue;
3815  
      return m;
3816  
    }
3817  
    throw new RuntimeException("Constructor with " + args.length + " matching parameter(s) not found in " + c.getName());
3818  
  }
3819  
  
3820  
  static Class findClass(String name) {
3821  
    for (String c : innerClasses)
3822  
      if (c.equalsIgnoreCase(name))
3823  
        try {
3824  
          return Class.forName("main$" + c);
3825  
        } catch (ClassNotFoundException e) {
3826  
          return null;
3827  
        }
3828  
    return null;
3829  
  }
3830  
  
3831  
  static boolean isSubclass(Class a, Class b) {
3832  
    return b.isAssignableFrom(a);
3833  
  }
3834  
  
3835  
  static boolean checkArgs(Class[] types, Object[] args, boolean debug) {
3836  
    if (types.length != args.length) {
3837  
      if (debug)
3838  
        System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
3839  
      return false;
3840  
    }
3841  
    for (int i = 0; i < types.length; i++)
3842  
      if (!(args[i] == null || types[i].isInstance(args[i]))) {
3843  
        if (debug)
3844  
          System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
3845  
        return false;
3846  
      }
3847  
    return true;
3848  
  }
3849  
  
3850  
  // finds a fixed element, e.g. a Java token
3851  
3852  
static class LCertainElement extends LearnerImpl {
3853  
  static boolean debug;
3854  
  ReversibleFunction f; // e.g. JavaTok
3855  
  String element;
3856  
  
3857  
  LCertainElement(ReversibleFunction f) {
3858  
  this.f = f;}
3859  
  
3860  
  public void processInOut(Object _in, Object _out) {
3861  
    String in = (String) _in, out = (String) _out;
3862  
    Set<String> set = new HashSet<String>(getMarked(out));
3863  
    if (debug)
3864  
      System.out.println("LCertainElement: set=" + set);
3865  
    if (set.size() == 1)
3866  
      element = set.iterator().next();
3867  
    if (debug)
3868  
      System.out.println("LCertainElement: element=" + element);
3869  
  }
3870  
  
3871  
  public Object processIn(Object _in) {
3872  
    List<String> l = (List<String>) f.process(_in);
3873  
    List<String> l2 = new ArrayList<String>();
3874  
    for (String x : l)
3875  
      l2.add(x.equals(element) ? "[[" + x + "]]" : x);
3876  
    return f.unprocess(l2);
3877  
  }
3878  
  
3879  
  static List<Integer> getFindMarkers2(String s) {
3880  
    List<Integer> l = new ArrayList<Integer>();
3881  
    int i = 0;
3882  
    while (i < s.length()) {
3883  
      int j = s.indexOf("[[", i);
3884  
      if (j < 0) break;
3885  
      int k = s.indexOf("]]", j+2);
3886  
      if (k < 0) break;
3887  
      l.add(j);
3888  
      l.add(k+2);
3889  
      i = k+2;
3890  
    }
3891  
    return l;
3892  
  }
3893  
  
3894  
  static List<String> getMarked(String s) {
3895  
    List<Integer> l = getFindMarkers2(s);
3896  
    List<String> result = new ArrayList<String>();
3897  
    for (int i = 0; i < l.size(); i += 2)
3898  
      result.add(s.substring(l.get(i)+2, l.get(i+1)-2));
3899  
    return result;
3900  
  }
3901  
} // LCertainElement
3902  
  
3903  
  static Class findClassFlex(String p) {
3904  
    Class c = findClass(p);
3905  
    if (c == null) c = findClass("L" + p);
3906  
    if (c == null) c = findClass("F" + p);
3907  
    if (c == null) c = findClass("RF" + p);
3908  
    return c;
3909  
  }
3910  
  
3911  
  
3912  
static class LOneWordChanged extends LearnerImpl {
3913  
  static boolean debug;
3914  
  Function preprocessor; // e.g. JavaTok
3915  
  List<String> lastIn; // only while learning
3916  
  String lastOut; // only while learning
3917  
  int index;
3918  
  String outPattern;
3919  
3920  
  LOneWordChanged(Function preprocessor) {
3921  
  this.preprocessor = preprocessor;}
3922  
  
3923  
  public void processInOut(Object _in, Object _out) {
3924  
    if (debug)
3925  
      System.out.println("LOneWordChanged: i/o " + _in + " " + _out);
3926  
    String in = (String) _in, out = (String) _out;
3927  
    List<String> l = (List<String>) preprocessor.process(_in);
3928  
    lastIn = l;
3929  
    lastOut = out;
3930  
  }
3931  
  
3932  
  public Object processIn(Object _in) {
3933  
    if (debug)
3934  
      System.out.println("LOneWordChanged: i " + _in);
3935  
    List<String> l = (List<String>) preprocessor.process(_in);
3936  
    
3937  
    if (outPattern == null) {
3938  
      if (l.size() != lastIn.size()) fail();
3939  
      for (int i = 0; i < l.size(); i++)
3940  
        if (!l.get(i).equals(lastIn.get(i))) {
3941  
          index = i;
3942  
          String word = lastIn.get(index);
3943  
          outPattern = lastOut.replace(word, "{*}");
3944  
          
3945  
          // delete learning data
3946  
          lastIn = null;
3947  
          lastOut = null;
3948  
          break;
3949  
        }
3950  
        
3951  
      if (outPattern == null) {
3952  
        if (debug)
3953  
          System.out.println("LOneWordChanged: last=" + lastIn + ", this=" + l);
3954  
        fail("not applicable - all words identical");
3955  
      }
3956  
    }
3957  
    if (debug)
3958  
      System.out.println("LOneWordChanged: index=" + index + ", outPattern=" + quote(outPattern));
3959  
3960  
    String word = l.get(index);
3961  
    return outPattern.replace("{*}", word);
3962  
  }
3963  
  
3964  
  static List<Integer> getFindMarkers2(String s) {
3965  
    List<Integer> l = new ArrayList<Integer>();
3966  
    int i = 0;
3967  
    while (i < s.length()) {
3968  
      int j = s.indexOf("[[", i);
3969  
      if (j < 0) break;
3970  
      int k = s.indexOf("]]", j+2);
3971  
      if (k < 0) break;
3972  
      l.add(j);
3973  
      l.add(k+2);
3974  
      i = k+2;
3975  
    }
3976  
    return l;
3977  
  }
3978  
  
3979  
  static List<String> getMarked(String s) {
3980  
    List<Integer> l = getFindMarkers2(s);
3981  
    List<String> result = new ArrayList<String>();
3982  
    for (int i = 0; i < l.size(); i += 2)
3983  
      result.add(s.substring(l.get(i)+2, l.get(i+1)-2));
3984  
    return result;
3985  
  }
3986  
} // LOneWordChanged
3987  
  static class LBla extends LearnerImpl {
3988  
  static boolean debug;
3989  
  String inPattern, outPattern;
3990  
3991  
  public void processInOut(Object _in, Object _out) {
3992  
    String in = (String) _in, out = (String) _out;
3993  
    int i = in.indexOf("bla"), j = out.indexOf("bla");
3994  
    if (i < 0 || j < 0) fail();
3995  
    inPattern = "^" + patternQuote(in.substring(0, i))
3996  
      + "(.+)" + patternQuote(in.substring(i+3)) + "$";
3997  
    outPattern = Matcher.quoteReplacement(out.substring(0, j))
3998  
      + "$1" + Matcher.quoteReplacement(out.substring(j+3));
3999  
  }
4000  
  
4001  
  public Object processIn(Object _in) {
4002  
    String in = (String) _in;
4003  
    return in.replaceAll(inPattern, outPattern);
4004  
  }
4005  
} // LBla
4006  
  
4007  
  static String shorten(String s, int max) {
4008  
    return s.length() <= max ? s : s.substring(0, Math.min(s.length(), max)) + "...";
4009  
  }
4010  
  
4011  
  // for stuff like #1000478
4012  
4013  
static class LIntersperse extends LearnerImpl {
4014  
  static boolean debug;
4015  
  ReversibleFunction preprocessor; // e.g. JavaTok
4016  
  List<String> outPrefix, outSuffix, filler;
4017  
4018  
  LIntersperse(ReversibleFunction preprocessor) {
4019  
  this.preprocessor = preprocessor;}
4020  
  
4021  
  public void processInOut(Object _in, Object _out) {
4022  
    if (debug)
4023  
      debug("i/o " + _in + " " + _out);
4024  
    List<String> in = (List<String>) ( preprocessor.process(_in));
4025  
    List<String> out = (List<String>) ( preprocessor.process(_out));
4026  
    int i = out.indexOf(in.get(1));
4027  
    outPrefix = out.subList(0, i);
4028  
    int j = out.lastIndexOf(in.get(in.size()-2));
4029  
    outSuffix = out.subList(j+1, out.size());
4030  
    int newLength = j-i+1;
4031  
    int realOldLength = (in.size()-1)/2;
4032  
    
4033  
    /*int intersperse = 0;
4034  
    if (realOldLength > 1)
4035  
      intersperse = newLength-realOldLength*3-1;*/
4036  
    int intersperse = 3; // just assume this for now
4037  
    if (realOldLength > 1)
4038  
      filler = out.subList(i+1, i+1+intersperse);
4039  
    
4040  
    if (debug)
4041  
      debug("oldLength= " + in.size() + ", newLength=" + newLength +", realOldLength=" + realOldLength + ", intersperse=" + intersperse + ", filler length=" + (filler == null ? 0 : filler.size()));
4042  
  }
4043  
  
4044  
  public Object processIn(Object _in) {
4045  
    List<String> l = (List<String>) ( preprocessor.process(_in));
4046  
    if (debug)
4047  
      debug("i " + structure(l));
4048  
    List<String> list = new ArrayList<String>();
4049  
    list.addAll(outPrefix);
4050  
    if (debug)
4051  
      debug("ranging from " + 1 + " to " + (l.size()-2));
4052  
    for (int i = 1; i < l.size()-1; i += 2) {
4053  
      list.add(l.get(i));
4054  
      if (i < l.size()-2)
4055  
        list.addAll(filler);
4056  
    }
4057  
    list.addAll(outSuffix);
4058  
    Object out = preprocessor.unprocess(list);
4059  
    if (debug)
4060  
      debug("output: " + structure(list) + " = " + structure(out));
4061  
    return out;
4062  
  }
4063  
} // LIntersperse
4064  
  
4065  
  static String getName(Class c) {
4066  
    return c.getName().replaceAll("^main\\$", "");
4067  
  }
4068  
  
4069  
  static void keepAlive() {
4070  
    Thread _t = new Thread() {
4071  
public void run() {
4072  
try {
4073  
4074  
      Object printed = null;
4075  
      while (true) {
4076  
        Object t = trying;
4077  
        Thread.sleep(1000);
4078  
        if (t != null && t == trying && printed != t) { // still trying
4079  
          System.err.println("* "+ structure(t));
4080  
          printed = t;
4081  
        } else printed = null;
4082  
      }
4083  
    } catch (Exception _e) {
4084  
  throw _e instanceof RuntimeException ? (RuntimeException) _e : new RuntimeException(_e); } }
4085  
};
4086  
_t.setDaemon(true);
4087  
_t.start();
4088  
  }
4089  
  
4090  
  // for stuff like #1000349
4091  
4092  
static class LFixedPositions extends LearnerImpl {
4093  
  static boolean debug;
4094  
  Map<Integer, String> map = new TreeMap<Integer, String>();
4095  
4096  
  public void processInOut(Object _in, Object _out) {
4097  
    // Only looks at out... funny, eh?
4098  
    
4099  
    String out = (String) ( _out);
4100  
    for (int i = 0; i < out.length(); i++) {
4101  
      String s = map.get(i);
4102  
      String c = out.substring(i, i+1);
4103  
      if (s == null)
4104  
        map.put(i, c);
4105  
      else if (s.equals("")) {
4106  
      } else if (!s.equals(c))
4107  
        map.put(i, ""); // TILT
4108  
    }
4109  
    
4110  
    for (int i : map.keySet())
4111  
      if (i >= out.length())
4112  
        map.put(i, "");
4113  
  }
4114  
  
4115  
  public Object processIn(Object _in) {
4116  
    String in = (String) ( _in);
4117  
    List<String> l = new ArrayList<String>();
4118  
    for (int i = 0; i < in.length(); i++)
4119  
      l.add(in.substring(i, i+1));
4120  
    for (int i : map.keySet()) {
4121  
      String s = map.get(i);
4122  
      if (!s.equals("")) {
4123  
        while (i >= l.size())
4124  
          l.add(" ");
4125  
        l.set(i, s);
4126  
      }
4127  
    }
4128  
    return join("", l);
4129  
  }
4130  
} // LFixedPositions
4131  
  // for stuff like #1000351
4132  
4133  
static class LMap extends LearnerImpl {
4134  
  static boolean debug;
4135  
  Map<String, String> map = new TreeMap<String, String>();
4136  
4137  
  public void processInOut(Object _in, Object _out) {
4138  
    List<String> in = (List<String>) ( _in);
4139  
    List<String> out = (List<String>) ( _out);
4140  
    
4141  
    if (in.size() != out.size()) fail();
4142  
    
4143  
    for (int i = 0; i < in.size(); i++) {
4144  
      String a = in.get(i), b = out.get(i);
4145  
      if (!a.equals(b))
4146  
        map.put(a, b);
4147  
    }
4148  
  }
4149  
  
4150  
  public Object processIn(Object _in) {
4151  
    List<String> in = (List<String>) ( _in);
4152  
    List<String> out = new ArrayList<String>();
4153  
    for (int i = 0; i < in.size(); i++) {
4154  
      String a = in.get(i), b = map.get(a);
4155  
      out.add(b != null ? b : a);
4156  
    }
4157  
    return out;
4158  
  }
4159  
} // LMap
4160  
    // learns to exchange common prefixes and suffixes.
4161  
  // maybe it's not perfect... see #1000491 for a problem case
4162  
  
4163  
  static class LPrefixSuffix extends LearnerImpl {
4164  
    static boolean debug;
4165  
    String prefixIn, suffixIn, prefixOut, suffixOut;
4166  
    
4167  
    public void processInOut(Object _in, Object _out) {
4168  
      String in = (String) _in, out = (String) _out;
4169  
      updateIn(in);
4170  
      prefixOut = prefixOut == null ? out : commonPrefix(prefixOut, out);
4171  
      suffixOut = suffixOut == null ? out : commonSuffix(suffixOut, out);
4172  
      if (debug)
4173  
        printState("processInOut(" + quote(in) + ", " + quote(out) + ")");
4174  
    }
4175  
    
4176  
    void updateIn(String in) {
4177  
      prefixIn = prefixIn == null ? in : commonPrefix(prefixIn, in);
4178  
      suffixIn = suffixIn == null ? in : commonSuffix(suffixIn, in);
4179  
      if (debug)
4180  
        printState("updateIn(" + quote(in) + ")");
4181  
    }
4182  
4183  
    public String processIn(Object _in) {
4184  
      String in = (String) _in;
4185  
      //System.out.println("[before last info] " + quote(prefixIn) + " " + quote(suffixIn) + " " + quote(prefixOut) + " " + quote(suffixOut));
4186  
      //System.out.println("[last info] " + quote(in));
4187  
      
4188  
      // use latest information
4189  
      String p = prefixIn, s = suffixIn;
4190  
      updateIn(in);
4191  
      prefixOut = prefixOut.substring(0, prefixOut.length()-(p.length()-prefixIn.length()));
4192  
      suffixOut = suffixOut.substring(s.length()-suffixIn.length());
4193  
      
4194  
      //System.out.println("[after last info] " + quote(prefixIn) + " " + quote(suffixIn) + " " + quote(prefixOut) + " " + quote(suffixOut));
4195  
      String core = in.substring(prefixIn.length(), in.length()-suffixIn.length());
4196  
      return prefixOut + core + suffixOut;
4197  
    }
4198  
    
4199  
    public void toJava(Code code) {
4200  
      if (prefixIn.length() != 0 || suffixIn.length() != 0)
4201  
        code.line(code.var + " = ((String) " + code.var + ").substring(" + prefixIn.length() + ", " + code.s() + ".length()-" + suffixIn.length() + ");");
4202  
      if (prefixOut.length() != 0 || suffixOut.length() != 0)
4203  
        code.line(code.var + " = " + quote(prefixOut) + " + " + code.var + " + " + quote(suffixOut) + ";");
4204  
    }
4205  
    
4206  
    void printState(String text) {
4207  
      System.out.println(text);
4208  
      printVars();
4209  
    }
4210  
  }
4211  
  
4212  
 // LPrefixSuffix
4213  
  // learns to remove input suffixes.
4214  
4215  
static class LRemoveInputSuffix extends LearnerImpl {
4216  
  static boolean debug;
4217  
  String suffix;
4218  
  static boolean failFast = false;
4219  
  
4220  
  public void processInOut(Object _in, Object _out) {
4221  
    String in = (String) _in, out = (String) _out;
4222  
    if (!in.startsWith(out)) fail();
4223  
    suffix = overwrite(suffix, in.substring(out.length()));
4224  
  }
4225  
4226  
  public String processIn(Object _in) {
4227  
    String in = (String) _in;
4228  
    if (!in.endsWith(suffix)) if (failFast) fail(); else return in;
4229  
    return in.substring(0, in.length()-suffix.length());
4230  
  }
4231  
}
4232  
 // LRemoveInputSuffix
4233  
  
4234  
  static <T> T overwrite(T existing, T newValue) {
4235  
    if (existing != null && !existing.equals(newValue))
4236  
      fail("Overwrite");
4237  
    return newValue;
4238  
  }
4239  
  
4240  
  static void getPass() { try {
4241  
 
4242  
    if (user != null) {
4243  
      File pwFile = new File(_x18.userHome(), ".javax/pw-" + user);
4244  
      if (pass == null) {
4245  
        List<String> lines = toLines(readTextFile(pwFile, ""));
4246  
        if (!lines.isEmpty())
4247  
          pass = lines.get(0).trim();
4248  
        if ("".equals(pass)) pass = null;
4249  
        //System.out.println("Pass: " + quote(pass));
4250  
        if (pass != null)
4251  
          System.out.println("Password read from " + pwFile.getAbsolutePath());
4252  
      }
4253  
      if (pass == null)
4254  
        System.out.println("You can put your password in: " + pwFile.getAbsolutePath());
4255  
    }
4256  
  
4257  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
4258  
  
4259  
  static void editSnippetText(String docID, String newText, String editInfo) { try {
4260  
 
4261  
    getPass();
4262  
    System.out.println("Editing " + docID);
4263  
    URL url = new URL("http://tinybrain.de:8080/tb-int/auto-edit.php");
4264  
    String postData =
4265  
        "user=" + urlencode(user)
4266  
      + "&pass=" + urlencode(pass)
4267  
      + "&id=" + urlencode(docID)
4268  
      + "&text=" + urlencode(newText)
4269  
      + "&editinfo=" + urlencode(editInfo);
4270  
    System.out.println(postData);
4271  
    String result = doPost(postData, url.openConnection(), url);
4272  
    System.out.println("Edit result: " + result);
4273  
  
4274  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
4275  
  
4276  
  static String urlencode(String s) { try {
4277  
 
4278  
    return URLEncoder.encode(unnull(s), "UTF-8");
4279  
  
4280  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}
4281  
  
4282  
  static String doPost(String urlParameters, URLConnection conn, URL url) throws IOException {
4283  
    // connect and do POST
4284  
    conn.setDoOutput(true);
4285  
4286  
    OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
4287  
    writer.write(urlParameters);
4288  
    writer.flush();
4289  
4290  
    String contents = loadPage(conn, url);
4291  
    writer.close();
4292  
    return contents;
4293  
  }
4294  
  
4295  
  static class FBeforeColon extends FunctionImpl {
4296  
  public Object process(Object _in) {
4297  
    String in = (String) ( _in);
4298  
    return in.substring(0, in.indexOf(':'));
4299  
  }
4300  
} // FBeforeColon
4301  
  static class FAfterColon extends FunctionImpl {
4302  
  public Object process(Object _in) {
4303  
    String in = (String) ( _in);
4304  
    return in.substring(in.indexOf(':')+1);
4305  
  }
4306  
} // FAfterColon
4307  
  static class FUnquote extends FunctionImpl {
4308  
  public Object process(Object _in) {
4309  
    String in = (String) ( _in);
4310  
    return unquote(in);
4311  
  }
4312  
} // FUnquote
4313  
  static class LCombineTwoFunctions extends LearnerImpl {
4314  
  static boolean debug;
4315  
  Function f, g;
4316  
4317  
  public void processInOut(Object in, Object out) {
4318  
    if (f != null) return;
4319  
    List<Function> functions = allFunctions();
4320  
    
4321  
    // simple version without really checking multiple candidates
4322  
    for (Function f : functions) {
4323  
      Object o = tryProcess(f, in);
4324  
      if (o != null) {
4325  
        if (debug)
4326  
          debug("in=" + structure(in) + ", f=" + structure(f) + ", o=" + structure(o));
4327  
        for (Function g : functions) {
4328  
          Object x = tryProcess(g, o);
4329  
          if (out.equals(x)) {
4330  
            if (debug)
4331  
              debug("bingo! " + structure(g));
4332  
            this.f = f;
4333  
            this.g = g;
4334  
            return;
4335  
          }
4336  
        }
4337  
      }
4338  
    }
4339  
  }
4340  
  
4341  
  public Object processIn(Object in) {
4342  
    return g.process(f.process(in));
4343  
  }
4344  
  
4345  
  static Object tryProcess(Function f, Object in) {
4346  
    try {
4347  
      return f.process(in);
4348  
    } catch (Throwable e) {
4349  
      return null;
4350  
    }
4351  
  }
4352  
} // LCombineTwoFunctions
4353  
  // extensive version with full search
4354  
    
4355  
static class LCombineTwoFunctions2 extends LearnerImpl {
4356  
  static boolean debug;
4357  
  Stepper stepper;
4358  
  List<Object[]> examples = new ArrayList<Object[]>();
4359  
  
4360  
  public void processInOut(Object in, Object out) {
4361  
    examples.add(new Object[] {in, out});
4362  
    
4363  
    if (stepper == null)
4364  
      stepper = new Stepper(allFunctions());
4365  
4366  
    while (!stepper.ended()) {
4367  
      if (check(stepper.current()))
4368  
        return; // ok, keep
4369  
      else
4370  
        stepper.step();
4371  
    }
4372  
    
4373  
    fail();
4374  
  }
4375  
  
4376  
  boolean check(Function[] fg) {
4377  
    if (debug) debug("Checking " + structure(fg));
4378  
    for (Object[] e : examples) {
4379  
      Object o = tryProcess(fg[0], e[0]);
4380  
      if (o != null) {
4381  
        Object x = tryProcess(fg[1], o);
4382  
        if (e[1].equals(x))
4383  
          continue; // example ok
4384  
      }
4385  
      return false;
4386  
    }
4387  
    return true; // all examples ok
4388  
  }
4389  
  
4390  
  public Object processIn(Object in) {
4391  
    Function[] fg = stepper.current();
4392  
    return fg[1].process(fg[0].process(in));
4393  
  }
4394  
  
4395  
  static Object tryProcess(Function f, Object in) { try {
4396  
 
4397  
    return f.process(in);
4398  
  
4399  
} catch (Throwable __e) { return null; }}
4400  
  
4401  
  static class Stepper {
4402  
    List<Function> functions;
4403  
    int i1, i2;
4404  
    
4405  
    Stepper(List<Function> functions) {
4406  
  this.functions = functions;}
4407  
    
4408  
    boolean ended() {
4409  
      return i1 >= functions.size();
4410  
    }
4411  
    
4412  
    Function[] current() {
4413  
      return new Function[] {functions.get(i1), functions.get(i2)};
4414  
    }
4415  
    
4416  
    void step() {
4417  
      if (i2 < functions.size()) ++i2;
4418  
      else { ++i1; i2 = 0; }
4419  
    }
4420  
  }
4421  
} // LCombineTwoFunctions2
4422  
  
4423  
  static List<Function> allFunctions() {
4424  
    List<Function> list = new ArrayList<Function>();
4425  
    for (String s : innerClasses) {
4426  
      Class c = findClass(s);
4427  
      if (c == null) continue;
4428  
      if (isSubclass(c, Function.class) && hasConstructor(c))
4429  
        list.add((Function) newInstance(c));
4430  
    }
4431  
    return list;
4432  
  }
4433  
4434  
  /** writes safely (to temp file, then rename) */
4435  
  public static void saveTextFile(String fileName, String contents) throws IOException {
4436  
    File file = new File(fileName);
4437  
    File parentFile = file.getParentFile();
4438  
    if (parentFile != null)
4439  
      parentFile.mkdirs();
4440  
    String tempFileName = fileName + "_temp";
4441  
    FileOutputStream fileOutputStream = new FileOutputStream(tempFileName);
4442  
    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");
4443  
    PrintWriter printWriter = new PrintWriter(outputStreamWriter);
4444  
    printWriter.print(contents);
4445  
    printWriter.close();
4446  
    if (file.exists() && !file.delete())
4447  
      throw new IOException("Can't delete " + fileName);
4448  
4449  
    if (!new File(tempFileName).renameTo(file))
4450  
      throw new IOException("Can't rename " + tempFileName + " to " + fileName);
4451  
  }
4452  
4453  
  public static String loadTextFile(String fileName, String defaultContents) throws IOException {
4454  
    if (!new File(fileName).exists())
4455  
      return defaultContents;
4456  
4457  
    FileInputStream fileInputStream = new FileInputStream(fileName);
4458  
    InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8");
4459  
    return loadTextFile(inputStreamReader);
4460  
  }
4461  
4462  
  public static String loadTextFile(Reader reader) throws IOException {
4463  
    StringBuilder builder = new StringBuilder();
4464  
    try {
4465  
      BufferedReader bufferedReader = new BufferedReader(reader);
4466  
      String line;
4467  
      while ((line = bufferedReader.readLine()) != null)
4468  
        builder.append(line).append('\n');
4469  
    } finally {
4470  
      reader.close();
4471  
    }
4472  
    return builder.length() == 0 ? "" : builder.substring(0, builder.length()-1);
4473  
  }
4474  
4475  
  public static String readTextFile(String fileName, String defaultContents) throws IOException {
4476  
    return loadTextFile(fileName, defaultContents);
4477  
  }
4478  
4479  
  public static String readTextFile(File file, String defaultContents) throws IOException {
4480  
    return loadTextFile(file.getPath(), defaultContents);
4481  
  }
4482  
4483  
4484  
 public static List<String> toLines(String s) {
4485  
    List<String> lines = new ArrayList<String>();
4486  
    int start = 0;
4487  
    while (true) {
4488  
      int i = toLines_nextLineBreak(s, start);
4489  
      if (i < 0) {
4490  
        if (s.length() > start) lines.add(s.substring(start));
4491  
        break;
4492  
      }
4493  
4494  
      lines.add(s.substring(start, i));
4495  
      if (s.charAt(i) == '\r' && i+1 < s.length() && s.charAt(i+1) == '\n')
4496  
        i += 2;
4497  
      else
4498  
        ++i;
4499  
4500  
      start = i;
4501  
    }
4502  
    return lines;
4503  
  }
4504  
4505  
  private static int toLines_nextLineBreak(String s, int start) {
4506  
    for (int i = start; i < s.length(); i++) {
4507  
      char c = s.charAt(i);
4508  
      if (c == '\r' || c == '\n')
4509  
        return i;
4510  
    }
4511  
    return -1;
4512  
  }
4513  
4514  
  public static String fromLines(List<String> lines) {
4515  
    StringBuilder buf = new StringBuilder();
4516  
    for (String line : lines) {
4517  
      buf.append(line).append('\n');
4518  
    }
4519  
    return buf.toString();
4520  
  }
4521  
4522  
  static boolean preferCached = false;
4523  
4524  
  public static String loadSnippet(String snippetID) throws IOException {
4525  
    return loadSnippet(parseSnippetID(snippetID), preferCached);
4526  
  }
4527  
4528  
  public static String loadSnippet(String snippetID, boolean preferCached) throws IOException {
4529  
    return loadSnippet(parseSnippetID(snippetID), preferCached);
4530  
  }
4531  
4532  
  public static long parseSnippetID(String snippetID) {
4533  
    return Long.parseLong(shortenSnippetID(snippetID));
4534  
  }
4535  
4536  
  private static String shortenSnippetID(String snippetID) {
4537  
    if (snippetID.startsWith("#"))
4538  
      snippetID = snippetID.substring(1);
4539  
    String httpBlaBla = "http://tinybrain.de/";
4540  
    if (snippetID.startsWith(httpBlaBla))
4541  
      snippetID = snippetID.substring(httpBlaBla.length());
4542  
    return snippetID;
4543  
  }
4544  
4545  
  public static boolean isSnippetID(String snippetID) {
4546  
    snippetID = shortenSnippetID(snippetID);
4547  
    return isInteger(snippetID) && Long.parseLong(snippetID) != 0;
4548  
  }
4549  
4550  
  public static boolean isInteger(String s) {
4551  
    return Pattern.matches("\\-?\\d+", s);
4552  
  }
4553  
4554  
  public static String loadSnippet(long snippetID, boolean preferCached) throws IOException {
4555  
    if (preferCached) {
4556  
      initSnippetCache();
4557  
      String text = DiskSnippetCache_get(snippetID);
4558  
      if (text != null)
4559  
        return text;
4560  
    }
4561  
4562  
    String text;
4563  
    try {
4564  
      URL url = new URL("http://tinybrain.de:8080/getraw.php?id=" + snippetID);
4565  
      text = loadPage(url);
4566  
    } catch (FileNotFoundException e) {
4567  
      throw new IOException("Snippet #" + snippetID + " not found or not public");
4568  
    }
4569  
4570  
    try {
4571  
      initSnippetCache();
4572  
      DiskSnippetCache_put(snippetID, text);
4573  
    } catch (IOException e) {
4574  
      System.err.println("Minor warning: Couldn't save snippet to cache ("  + DiskSnippetCache_getDir() + ")");
4575  
    }
4576  
4577  
    return text;
4578  
  }
4579  
4580  
  static File DiskSnippetCache_dir;
4581  
4582  
  public static void initDiskSnippetCache(File dir) {
4583  
    DiskSnippetCache_dir = dir;
4584  
    dir.mkdirs();
4585  
  }
4586  
4587  
  public static synchronized String DiskSnippetCache_get(long snippetID) throws IOException {
4588  
    return loadTextFile(DiskSnippetCache_getFile(snippetID).getPath(), null);
4589  
  }
4590  
4591  
  private static File DiskSnippetCache_getFile(long snippetID) {
4592  
    return new File(DiskSnippetCache_dir, "" + snippetID);
4593  
  }
4594  
4595  
  public static synchronized void DiskSnippetCache_put(long snippetID, String snippet) throws IOException {
4596  
    saveTextFile(DiskSnippetCache_getFile(snippetID).getPath(), snippet);
4597  
  }
4598  
4599  
  public static File DiskSnippetCache_getDir() {
4600  
    return DiskSnippetCache_dir;
4601  
  }
4602  
4603  
  public static void initSnippetCache() {
4604  
    if (DiskSnippetCache_dir == null)
4605  
      initDiskSnippetCache(new File(System.getProperty("user.home"), ".tinybrain/snippet-cache"));
4606  
  }
4607  
  
4608  
4609  
  public static String commonPrefix(String a, String b) {
4610  
    int i = 0;
4611  
    while (i < a.length() && i < b.length() && a.charAt(i) == b.charAt(i))
4612  
      ++i;
4613  
    return a.substring(0, i);
4614  
  }
4615  
4616  
4617  
  public static String commonSuffix(String a, String b) {
4618  
    int i = 0;
4619  
    while (i < a.length() && i < b.length() && a.charAt(a.length()-1-i) == b.charAt(b.length()-1-i))
4620  
      ++i;
4621  
    return a.substring(a.length()-i);
4622  
  }
4623  
4624  
4625  
  public static String quote(String s) {
4626  
    if (s == null) return "null";
4627  
    return "\"" + s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\r", "\\r").replace("\n", "\\n") + "\"";
4628  
  }
4629  
4630  
  public static String bytesToHex(byte[] bytes) {
4631  
    return bytesToHex(bytes, 0, bytes.length);
4632  
  }
4633  
4634  
  public static String bytesToHex(byte[] bytes, int ofs, int len) {
4635  
    StringBuilder stringBuilder = new StringBuilder(len*2);
4636  
    for (int i = 0; i < len; i++) {
4637  
      String s = "0" + Integer.toHexString(bytes[ofs+i]);
4638  
      stringBuilder.append(s.substring(s.length()-2, s.length()));
4639  
    }
4640  
    return stringBuilder.toString();
4641  
  }
4642  
4643  
4644  
  public static byte[] loadBinaryPage(URLConnection con) throws IOException {
4645  
    //setHeaders(con);
4646  
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
4647  
    InputStream inputStream = con.getInputStream();
4648  
    int n = 0;
4649  
    while (true) {
4650  
      int ch = inputStream.read();
4651  
      if (ch < 0)
4652  
        break;
4653  
      buf.write(ch);
4654  
      if (++n % 100000 == 0)
4655  
        System.err.println("  " + n + " bytes loaded.");
4656  
    }
4657  
    inputStream.close();
4658  
    return buf.toByteArray();
4659  
  }
4660  
4661  
4662  
  /** writes safely (to temp file, then rename) */
4663  
  public static void saveBinaryFile(String fileName, byte[] contents) throws IOException {
4664  
    File file = new File(fileName);
4665  
    File parentFile = file.getParentFile();
4666  
    if (parentFile != null)
4667  
      parentFile.mkdirs();
4668  
    String tempFileName = fileName + "_temp";
4669  
    FileOutputStream fileOutputStream = new FileOutputStream(tempFileName);
4670  
    fileOutputStream.write(contents);
4671  
    fileOutputStream.close();
4672  
    if (file.exists() && !file.delete())
4673  
      throw new IOException("Can't delete " + fileName);
4674  
4675  
    if (!new File(tempFileName).renameTo(file))
4676  
      throw new IOException("Can't rename " + tempFileName + " to " + fileName);
4677  
  }
4678  
4679  
4680  
  public static String loadPage(String url) throws IOException {
4681  
    if (url.indexOf("://") < 0)
4682  
      url = "http://" + url;
4683  
    return loadPage(new URL(url));
4684  
  }
4685  
  
4686  
  public static String loadPage(URL url) throws IOException {
4687  
    System.out.println("Loading: " + url.toExternalForm());
4688  
    URLConnection con = url.openConnection();
4689  
    return loadPage(con, url);
4690  
  }
4691  
4692  
  public static String loadPage(URLConnection con, URL url) throws IOException {
4693  
    String contentType = con.getContentType();
4694  
    if (contentType == null)
4695  
      throw new IOException("Page could not be read: " + url);
4696  
    //Log.info("Content-Type: " + contentType);
4697  
    String charset = loadPage_guessCharset(contentType);
4698  
    Reader r = new InputStreamReader(con.getInputStream(), charset);
4699  
    StringBuilder buf = new StringBuilder();
4700  
    while (true) {
4701  
      int ch = r.read();
4702  
      if (ch < 0)
4703  
        break;
4704  
      //Log.info("Chars read: " + buf.length());
4705  
      buf.append((char) ch);
4706  
    }
4707  
    return buf.toString();
4708  
  }
4709  
  
4710  
  static String loadPage_guessCharset(String contentType) {
4711  
    Pattern p = Pattern.compile("text/html;\\s+charset=([^\\s]+)\\s*");
4712  
    Matcher m = p.matcher(contentType);
4713  
    /* If Content-Type doesn't match this pre-conception, choose default and hope for the best. */
4714  
    return m.matches() ? m.group(1) : "ISO-8859-1";
4715  
  }
4716  
4717  
  static Class<?> getClass(String name) {
4718  
    try {
4719  
      return Class.forName(name);
4720  
    } catch (ClassNotFoundException e) {
4721  
      return null;
4722  
    }
4723  
  }
4724  
4725  
  static Object call(Object o, String method, Object... args) {
4726  
    try {
4727  
      Method m = call_findMethod(o, method, args, false);
4728  
      m.setAccessible(true);
4729  
      return m.invoke(o, args);
4730  
    } catch (Exception e) {
4731  
      throw new RuntimeException(e);
4732  
    }
4733  
  }
4734  
4735  
  static Object call(Class c, String method, Object... args) {
4736  
    try {
4737  
      Method m = call_findStaticMethod(c, method, args, false);
4738  
      m.setAccessible(true);
4739  
      return m.invoke(null, args);
4740  
    } catch (Exception e) {
4741  
      throw new RuntimeException(e);
4742  
    }
4743  
  }
4744  
4745  
  static Method call_findStaticMethod(Class c, String method, Object[] args, boolean debug) {
4746  
    while (c != null) {
4747  
      for (Method m : c.getDeclaredMethods()) {
4748  
        if (debug)
4749  
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
4750  
        if (!m.getName().equals(method)) {
4751  
          if (debug) System.out.println("Method name mismatch: " + method);
4752  
          continue;
4753  
        }
4754  
4755  
        if ((m.getModifiers() & Modifier.STATIC) == 0 || !call_checkArgs(m, args, debug))
4756  
          continue;
4757  
4758  
        return m;
4759  
      }
4760  
      c = c.getSuperclass();
4761  
    }
4762  
    throw new RuntimeException("Method '" + method + "' (static) with " + args.length + " parameter(s) not found in " + c.getName());
4763  
  }
4764  
4765  
  static Method call_findMethod(Object o, String method, Object[] args, boolean debug) {
4766  
    Class c = o.getClass();
4767  
    while (c != null) {
4768  
      for (Method m : c.getDeclaredMethods()) {
4769  
        if (debug)
4770  
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
4771  
        if (m.getName().equals(method) && call_checkArgs(m, args, debug))
4772  
          return m;
4773  
      }
4774  
      c = c.getSuperclass();
4775  
    }
4776  
    throw new RuntimeException("Method '" + method + "' (non-static) with " + args.length + " parameter(s) not found in " + o.getClass().getName());
4777  
  }
4778  
4779  
  private static boolean call_checkArgs(Method m, Object[] args, boolean debug) {
4780  
    Class<?>[] types = m.getParameterTypes();
4781  
    if (types.length != args.length) {
4782  
      if (debug)
4783  
        System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
4784  
      return false;
4785  
    }
4786  
    for (int i = 0; i < types.length; i++)
4787  
      if (!(args[i] == null || types[i].isInstance(args[i]))) {
4788  
        if (debug)
4789  
          System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
4790  
        return false;
4791  
      }
4792  
    return true;
4793  
  }
4794  
4795  
4796  
4797  
  static void set(Class c, String field, Object value) {
4798  
    try {
4799  
      Field f = set_findStaticField(c, field);
4800  
      f.setAccessible(true);
4801  
      f.set(null, value);
4802  
    } catch (Exception e) {
4803  
      throw new RuntimeException(e);
4804  
    }
4805  
  }
4806  
  
4807  
  static Field set_findStaticField(Class<?> c, String field) {
4808  
    for (Field f : c.getDeclaredFields())
4809  
      if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0)
4810  
        return f;
4811  
    throw new RuntimeException("Static field '" + field + "' not found in " + c.getName());
4812  
  }
4813  
4814  
static String patternQuote(String s) {
4815  
  return s.length() == 0 ? "" : Pattern.quote(s);
4816  
}
4817  
4818  
  public static String join(String glue, Iterable<String> strings) {
4819  
    StringBuilder buf = new StringBuilder();
4820  
    Iterator<String> i = strings.iterator();
4821  
    if (i.hasNext()) {
4822  
      buf.append(i.next());
4823  
      while (i.hasNext())
4824  
        buf.append(glue).append(i.next());
4825  
    }
4826  
    return buf.toString();
4827  
  }
4828  
  
4829  
  public static String join(String glue, String[] strings) {
4830  
    return join(glue, Arrays.asList(strings));
4831  
  }
4832  
4833  
4834  
  static String unnull(String s) {
4835  
    return s == null ? "" : s;
4836  
  }
4837  
}
4838  
4839  
class JavaTok {
4840  
  static String join(List<String> cnc) {
4841  
    StringBuilder buf = new StringBuilder();
4842  
    for (String s : cnc) buf.append(s);
4843  
    return buf.toString();
4844  
  }
4845  
  
4846  
  static List<String> split(String src) {
4847  
    Java20 lex = new Java20();
4848  
    src = src.replace("\r\n", "\n");
4849  
    LineNumberReader source = new LineNumberReader(new StringReader(src));
4850  
    int lineNr = source.getLineNumber()+1;
4851  
    List<T> list = new ArrayList<T>();
4852  
    try {
4853  
      for (Object a; (a = lex.grab(source)) != lex.$;) {
4854  
        String word = lex.word();
4855  
        String q = quote(word);
4856  
        //System.out.println("grabbed at line " + lineNr + ": " + a + " " + q);
4857  
        lineNr = source.getLineNumber()+1;
4858  
        
4859  
        T t = new T(a, word);
4860  
        boolean isSpace = t.isSpace();
4861  
        if (isSpace && list.size() > 0 && list.get(list.size()-1).isSpace())
4862  
          list.get(list.size()-1).word += word; // merge spaces
4863  
        else
4864  
          list.add(t);
4865  
      }
4866  
    } catch (Lexicon.Exception e) {
4867  
      throw new RuntimeException(e);
4868  
    }
4869  
    
4870  
    List<String> cnc = new ArrayList<String>();
4871  
    for (int i = 0; i < list.size(); ) {
4872  
      T t = list.get(i);
4873  
      boolean shouldBeSpace = (cnc.size() % 2) == 0;
4874  
      boolean isSpace = t.isSpace();
4875  
      if (shouldBeSpace == isSpace) {
4876  
        cnc.add(t.word);
4877  
        ++i;
4878  
      } else if (shouldBeSpace)
4879  
        cnc.add("");
4880  
      else {
4881  
        System.out.println(cncToLines(cnc));
4882  
        throw new RuntimeException("TILT at " + cnc.size() + ": " + quote(t.word));
4883  
      }
4884  
    }
4885  
    if ((cnc.size() % 2) == 0)
4886  
      cnc.add("");
4887  
4888  
    return cnc;
4889  
  }
4890  
  
4891  
  static class T {
4892  
    Object a; String word;
4893  
    
4894  
    T(Object a, String word) { this.a = a; this.word = word; }
4895  
    
4896  
    boolean isSpace() {
4897  
      return a.equals("WHITE_SPACE") || a.equals("COMMENT");
4898  
    }
4899  
  }
4900  
  
4901  
  static String cncToLines(List<String> cnc) {
4902  
    StringBuilder out = new StringBuilder();
4903  
    for (String token : cnc)
4904  
      out.append(quote(token) + "\n");
4905  
    return out.toString();
4906  
  }
4907  
  
4908  
  public static String quote(String s) {
4909  
    if (s == null) return "null";
4910  
    return "\"" + s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\r", "\\r").replace("\n", "\\n") + "\"";
4911  
  }
4912  
  
4913  
  static class Java20 extends Lexicon {
4914  
4915  
	Java20() {
4916  
4917  
		/**
4918  
		* Grammar for Java 2.0.
4919  
		*
4920  
		* Nonterminal - first letter uppercase
4921  
		* TERMINAL - all letters uppercase
4922  
		* keyword - all letters lowercase
4923  
		*/
4924  
		int INFINITY = -1;
4925  
4926  
		/**
4927  
		* 19.3 Terminals from section 3.6: White Space: [[:space:]]
4928  
		*/
4929  
		put("WHITE_SPACE", new Repetition(PosixClass.space(), 1, INFINITY));
4930  
4931  
		/**
4932  
		* 19.3 Terminals from section 3.7: Comment
4933  
		*/
4934  
		put("COMMENT", new Union(
4935  
4936  
			//
4937  
			// Traditional Comment: /\*[^*]+(\*([^*/][^*]*)?)*\*/
4938  
			//
4939  
			new Concatenation(
4940  
				new Singleton("/*"), new Concatenation(
4941  
				new Repetition(new NonMatch("*"), 1, INFINITY), new Concatenation(
4942  
				new Repetition(
4943  
					new Concatenation(
4944  
						new Singleton("*"),
4945  
						new Repetition(new Concatenation(
4946  
							new NonMatch("*/"),
4947  
							new Repetition(new NonMatch("*"), 0, INFINITY)
4948  
						), 0, 1)
4949  
					), 0, INFINITY
4950  
				),
4951  
				new Singleton("*/")
4952  
			))), new Union(
4953  
4954  
			/**
4955  
			* End Of Line Comment: //[^\n]*\n
4956  
			*/
4957  
			new Concatenation(
4958  
				new Singleton("//"), new Concatenation(
4959  
				new Repetition(new NonMatch("\n"), 0, INFINITY),
4960  
				new Singleton("\n")
4961  
			)),
4962  
4963  
			//
4964  
			// Documentation Comment: /\*\*(([^*/][^*]*)?\*)*/
4965  
			//
4966  
			new Concatenation(
4967  
				new Singleton("/**"), new Concatenation(
4968  
				new Repetition(
4969  
					new Concatenation(
4970  
						new Repetition(new Concatenation(
4971  
							new NonMatch("*/"),
4972  
							new Repetition(new NonMatch("*"), 0, INFINITY)
4973  
						), 0, 1),
4974  
						new Singleton("*")
4975  
					), 0, INFINITY
4976  
				),
4977  
				new Singleton("/")
4978  
			))
4979  
		)));
4980  
4981  
		put("IDENTIFIER", new Concatenation(
4982  
			new Union(
4983  
				PosixClass.alpha(),
4984  
				new Match("_$")
4985  
			),
4986  
			new Repetition(
4987  
				new Union(
4988  
					PosixClass.alnum(),
4989  
					new Match("_$")
4990  
				), 0, INFINITY
4991  
			)
4992  
		));
4993  
4994  
		/**
4995  
		* 19.3 Terminals from section 3.9: Keyword (recognized but not in the Java grammar)
4996  
		*/
4997  
		put("KEYWORD", new Union(
4998  
			new Singleton("const"),
4999  
			new Singleton("goto")
5000  
		));
5001  
5002  
		/**
5003  
		* 19.3 Terminals from section 3.10.1: Integer Literal
5004  
		*/
5005  
		put("INTEGER_LITERAL", new Concatenation(
5006  
			new Union(
5007  
				/**
5008  
				* Decimal Integer Literal: 0|[1-9][[:digit:]]*
5009  
				*/
5010  
				new Singleton("0"), new Union(
5011  
5012  
				new Concatenation(
5013  
					new Range('1', '9'),
5014  
					new Repetition(PosixClass.digit(), 0, INFINITY)
5015  
				), new Union(
5016  
5017  
				/**
5018  
				* Hexadecimal Integer Literal: 0[xX][[:xdigit:]]+
5019  
				*/
5020  
				new Concatenation(
5021  
					new Singleton("0"), new Concatenation(
5022  
					new Match("xX"),
5023  
					new Repetition(PosixClass.xdigit(), 1, INFINITY)
5024  
				)),
5025  
5026  
				/**
5027  
				* Octal Integer Literal: 0[0-7]+
5028  
				*/
5029  
				new Concatenation(
5030  
					new Singleton("0"),
5031  
					new Repetition(new Range('0', '7'), 1, INFINITY)
5032  
				)
5033  
			))),
5034  
			new Repetition(new Match("lL"), 0, 1)
5035  
		));
5036  
5037  
		/**
5038  
		* 19.3 Terminals from section 3.10.2: Floating-Point Literal
5039  
		*/
5040  
		put("FLOATING_POINT_LITERAL", new Union(
5041  
5042  
			/**
5043  
			* [[:digit:]]+\.[[:digit:]]*([eE][-+]?[[:digit:]]+)?[fFdD]?
5044  
			*/
5045  
			new Concatenation(
5046  
				new Repetition(PosixClass.digit(), 1, INFINITY), new Concatenation(
5047  
				new Singleton("."), new Concatenation(
5048  
				new Repetition(PosixClass.digit(), 0, INFINITY), new Concatenation(
5049  
				new Repetition(new Concatenation(
5050  
					new Match("eE"), new Concatenation(
5051  
					new Repetition(new Match("-+"), 0, 1),
5052  
					new Repetition(PosixClass.digit(), 1, INFINITY)
5053  
				)), 0, 1),
5054  
				new Repetition(new Match("fFdD"), 0, 1)
5055  
			)))), new Union(
5056  
5057  
			/**
5058  
			* \.[[:digit:]]+([eE][-+]?[[:digit:]]+)?[fFdD]?
5059  
			*/
5060  
			new Concatenation(
5061  
				new Singleton("."), new Concatenation(
5062  
				new Repetition(PosixClass.digit(), 1, INFINITY), new Concatenation(
5063  
				new Repetition(new Concatenation(
5064  
					new Match("eE"), new Concatenation(
5065  
					new Repetition(new Match("-+"), 0, 1),
5066  
					new Repetition(PosixClass.digit(), 1, INFINITY)
5067  
				)), 0, 1),
5068  
				new Repetition(new Match("fFdD"), 0, 1)
5069  
			))), new Union(
5070  
5071  
			/**
5072  
			* [[:digit:]]+[eE][-+]?[[:digit:]]+[fFdD]?
5073  
			*/
5074  
			new Concatenation(
5075  
				new Repetition(PosixClass.digit(), 1, INFINITY), new Concatenation(
5076  
				new Match("eE"), new Concatenation(
5077  
				new Repetition(new Match("-+"), 0, 1), new Concatenation(
5078  
				new Repetition(PosixClass.digit(), 1, INFINITY),
5079  
				new Repetition(new Match("fFdD"), 0, 1)
5080  
			)))),
5081  
5082  
			/**
5083  
			* [[:digit:]]+([eE][-+]?[[:digit:]]+)?[fFdD]
5084  
			*/
5085  
			new Concatenation(
5086  
				new Repetition(PosixClass.digit(), 1, INFINITY), new Concatenation(
5087  
				new Repetition(new Concatenation(
5088  
					new Match("eE"), new Concatenation(
5089  
					new Repetition(new Match("-+"), 0, 1),
5090  
					new Repetition(PosixClass.digit(), 1, INFINITY)
5091  
				)), 0, 1),
5092  
				new Match("fFdD")
5093  
			))
5094  
		))));
5095  
5096  
		/**
5097  
		* 19.3 Terminals from section 3.10.3: Boolean Literal
5098  
		*/
5099  
		put("BOOLEAN_LITERAL", new Union(
5100  
			new Singleton("true"),
5101  
			new Singleton("false")
5102  
		));
5103  
5104  
		/**
5105  
		* 19.3 Terminals from section 3.10.4: Character Literal
5106  
		*/
5107  
		put("CHARACTER_LITERAL", new Concatenation(
5108  
			new Singleton("'"), new Concatenation(
5109  
			new Union(
5110  
5111  
				/**
5112  
				* Single Character: [^\r\n'\\]
5113  
				*/
5114  
				new NonMatch("\r\n'\\"),
5115  
5116  
				/**
5117  
				* Escape Sequence: \\([btnfr\"'\\]|[0-3]?[0-7]{1,2})
5118  
				*/
5119  
				new Concatenation(
5120  
					new Singleton("\\"),
5121  
					new Union(
5122  
						new Match("btnfr\"'\\"),
5123  
						new Concatenation(
5124  
							new Repetition(new Range('0', '3'), 0, 1),
5125  
							new Repetition(new Range('0', '7'), 1, 2)
5126  
						)
5127  
					)
5128  
				)
5129  
			),
5130  
			new Singleton("'")
5131  
		)));
5132  
5133  
		put("MULTILINE_LITERAL", new Concatenation(
5134  
			new Singleton("[["), new Concatenation(
5135  
			new Repetition(
5136  
				new Union(
5137  
					new NonMatch("]"),
5138  
					new Concatenation(
5139  
					  new Singleton("]"), new NonMatch("]"))
5140  
			  ), 0, INFINITY
5141  
			),
5142  
			new Singleton("]]")
5143  
		)));
5144  
5145  
		put("MULTILINE_LITERAL2", new Concatenation(
5146  
			new Singleton("[=["), new Concatenation(
5147  
			new Repetition(
5148  
				new Union(
5149  
					new NonMatch("]"),
5150  
					new Concatenation(new Singleton("]"), new Union(
5151  
				    new NonMatch("="),
5152  
				    new Concatenation(new Singleton("="), new NonMatch("]"))))
5153  
			  ), 0, INFINITY
5154  
			),
5155  
			new Singleton("]=]")
5156  
		)));
5157  
5158  
		/**
5159  
		* 19.3 Terminals from section 3.10.5: String Literal
5160  
		*/
5161  
		put("STRING_LITERAL", new Concatenation(
5162  
			new Singleton("\""), new Concatenation(
5163  
			new Repetition(
5164  
				new Union(
5165  
5166  
					/**
5167  
					* Single Character: [^\r\n"\\]
5168  
					*/
5169  
					new NonMatch("\r\n\"\\"),
5170  
5171  
					/**
5172  
					* Escape Sequence: \\([btnfr\"'\\]|[0-3]?[0-7]{1,2})
5173  
					*/
5174  
					new Concatenation(
5175  
						new Singleton("\\"),
5176  
						new Union(
5177  
							new Match("btnfr\"'\\"),
5178  
							new Union(
5179  
  							new Concatenation(
5180  
  								new Repetition(new Range('0', '3'), 0, 1),
5181  
  								new Repetition(new Range('0', '7'), 1, 2)
5182  
  							),
5183  
  							new Concatenation(
5184  
  							  new Singleton("u"),
5185  
  							  new Repetition(new Match("0123456789abcdefABCDEF"), 4, 4)
5186  
  							)
5187  
  						)
5188  
						)
5189  
					)
5190  
				), 0, INFINITY
5191  
			),
5192  
			new Singleton("\"")
5193  
		)));
5194  
5195  
		/**
5196  
		* 19.3 Terminals section 3.10.7: Null Literal
5197  
		*/
5198  
		put("NULL_LITERAL", new Singleton("null"));
5199  
		
5200  
		// OK, it seems we have to add some more stuff...
5201  
		
5202  
		//put("OTHER1", new Match(";{}=,<>[]().+-:|&!"));
5203  
		//put("OTHER1", new NonMatch("")); // catch anything, one character at a time
5204  
		put("OTHER1", new NonMatch(" \t\r\n")); // catch any non-whitespace, one character at a time
5205  
5206  
	}
5207  
} // class Java20
5208  
}
5209  
/**
5210  
* <p>This class implements a {@link Lexicon}.</p>
5211  
*
5212  
* @version 1.3
5213  
* @author &copy; 1999-2009 <a href="http://www.csupomona.edu/~carich/">Craig A. Rich</a> &lt;<a href="mailto:carich@csupomona.edu">carich@csupomona.edu</a>&gt;
5214  
*/
5215  
class Lexicon {
5216  
//Q
5217  
/**
5218  
* <p>The number of lexical NFA states constructed.</p>
5219  
*/
5220  
private static int QSize = 0;
5221  
5222  
/**
5223  
* <p>Creates a new state in the lexical NFA.</p>
5224  
*
5225  
* @return a new state in the lexical NFA.
5226  
*/
5227  
private static Integer s() {
5228  
	return ++QSize;
5229  
}
5230  
//delta
5231  
/**
5232  
* <p>The transition relation of the lexical NFA.</p>
5233  
*/
5234  
private static final Stack<Stack<Object>> delta = new Stack<Stack<Object>>();
5235  
5236  
/**
5237  
* <p>Puts a transition into the lexical NFA.</p>
5238  
*
5239  
* @param s the state from which the transition is made.
5240  
* @param A the <code>Alphabet</code> on which the transition is made.
5241  
* @param r the state to which the transition is made.
5242  
*/
5243  
private static void put(Integer s, Alphabet A, Integer r) {
5244  
5245  
	if (Math.max(s,r) >= delta.size()) delta.setSize(Math.max(s,r)+1);
5246  
5247  
	Stack<Object> pairs = delta.get(s);
5248  
	if (pairs == null) delta.set(s, pairs = new Stack<Object>());
5249  
5250  
	pairs.push(A);
5251  
	pairs.push(r);
5252  
}
5253  
//Set
5254  
/**
5255  
* <p>This class implements a {@link Lexicon.Set <code>Set</code>}.</p>
5256  
*
5257  
* @version 1.3
5258  
* @author &copy; 1999-2009 <a href="http://www.csupomona.edu/~carich/">Craig A. Rich</a> &lt;<a href="mailto:carich@csupomona.edu">carich@csupomona.edu</a>&gt;
5259  
* @param <E> the element type.
5260  
*/
5261  
static class Set<E> extends Stack<E> {
5262  
5263  
	/**
5264  
	* <p>The null exclusion indicator. If <code>true</code>, <code>add</code> methods will not add <code>null</code> to this <code>Set</code>.</p>
5265  
	*/
5266  
	private final boolean excludeNull;
5267  
5268  
	/**
5269  
	* <p>Constructs a <code>Set</code> with an initial capacity.</p>
5270  
	*
5271  
	* @param capacity the initial capacity. The magnitude of <code>capacity</code> is the initial capacity. The null exclusion indicator is initialized to <code>true</code> if <code>capacity</code> is negative.
5272  
	*/
5273  
	Set(int capacity) {
5274  
		super();
5275  
		ensureCapacity(Math.abs(capacity));
5276  
		excludeNull = (capacity < 0);
5277  
	}
5278  
5279  
	/**
5280  
	* <p>Adds an element to this <code>Set</code>. The element is not added if it occurs in this <code>Set</code> or it is <code>null</code> and the null exclusion indicator is <code>true</code>. The capacity is expanded if necessary.</p>
5281  
	*
5282  
	* @param element the element to add to this <code>Set</code>.
5283  
	* @return <code>true</code> if this <code>Set</code> is changed; <code>false</code> otherwise.
5284  
	*/
5285  
	public boolean add(E element) {
5286  
		if (excludeNull && element == null || contains(element)) return false;
5287  
		push(element);
5288  
		return true;
5289  
	}
5290  
5291  
	/**
5292  
	* <p>Adds a <code>Set</code> of elements to this <code>Set</code>. An element is not added if it occurs in this <code>Set</code> or it is <code>null</code> and the null exclusion indicator is <code>true</code>. The capacity is expanded if necessary.</p>
5293  
	*
5294  
	* @param index the index in <code>S</code> beyond which elements are added.
5295  
	* @param S the <code>Set</code> to add to this <code>Set</code>.
5296  
	* @return <code>true</code> if this <code>Set</code> is changed; <code>false</code> otherwise.
5297  
	*/
5298  
	boolean add(int index, Set<E> S) {
5299  
		if (S == null) return false;
5300  
		boolean push = isEmpty();
5301  
		boolean add = false;
5302  
5303  
		for (int i = index; i < S.size(); i++) {
5304  
			E element = S.get(i);
5305  
5306  
			if (!(excludeNull && element == null))
5307  
				if (push) {
5308  
					push(element);
5309  
					add = true;
5310  
				}
5311  
				else if (add(element))
5312  
					add = true;
5313  
		}
5314  
		return add;
5315  
	}
5316  
5317  
	/**
5318  
	* <p>Adds a <code>Set</code> of elements to this <code>Set</code>. An element is not added if it occurs in this <code>Set</code> or it is <code>null</code> and the null exclusion indicator is <code>true</code>. The capacity is expanded if necessary.</p>
5319  
	*
5320  
	* @param S the <code>Set</code> to add to this <code>Set</code>.
5321  
	* @return <code>true</code> if this <code>Set</code> is changed; <code>false</code> otherwise.
5322  
	*/
5323  
	boolean add(Set<E> S) {
5324  
		return add(0, S);
5325  
	}
5326  

5327  
	public String toString() {
5328  
		StringBuffer result = new StringBuffer(80);
5329  
		result.append('{');
5330  
5331  
		for (int i = 0; i < size(); i++) {
5332  
			if (i > 0) result.append(' ');
5333  
			result.append(get(i));
5334  
		}
5335  
		result.append('}');
5336  
		return result.toString();
5337  
	}
5338  
//Set
5339  
}
5340  
//I
5341  
/**
5342  
* <p>The initial states of the lexical NFA. When empty, there is a need to compute the current initial states. It is computed only on demand created by {@link #initial()}.</p>
5343  
*/
5344  
private final Set<Integer> I;
5345  
//F
5346  
/**
5347  
* <p>The final states of the lexical NFA. A final state is mapped to the terminal it accepts in this <code>Lexicon</code>. When empty, there is a need to compute current final states. It is computed only on demand created by {@link #initial()}.</p>
5348  
*/
5349  
private final Map<Integer, Object> F;
5350  
//Lexicon.transition
5351  
/**
5352  
* <p>Computes a transition using the lexical NFA.</p>
5353  
*
5354  
* @param S the states from which the transition is made.
5355  
* @param a the character on which the transition is made.
5356  
* @param R the states to which the transition is made.
5357  
* @return the states to which the transition is made.
5358  
*/
5359  
private static Set<Integer> transition(Set<Integer> S, char a, Set<Integer> R) {
5360  
	R.clear();
5361  
5362  
	for (Integer s : S) {
5363  
		Stack<Object> pairs = delta.get(s);
5364  
5365  
		if (pairs != null)
5366  
			for (int k = 0; k < pairs.size(); k += 2) {
5367  
				Alphabet A = (Alphabet)pairs.get(k);
5368  
5369  
				if (A != null) {
5370  
					Integer r = (Integer)pairs.get(k+1);
5371  
					if (A.contains(a)) R.add(r);
5372  
				}
5373  
			}
5374  
	}
5375  
	return R;
5376  
}
5377  
//Lexicon.closure
5378  
/**
5379  
* <p>Computes a reflexive transitive closure under empty transition using the lexical NFA. The closure is computed in place by a breadth-first search expanding <code>S</code>.</p>
5380  
*
5381  
* @param S the states whose reflexive transitive closure is computed under empty transition.
5382  
* @return the reflexive transitive closure of <code>S</code> under empty transition.
5383  
*/
5384  
private static Set<Integer> closure(Set<Integer> S) {
5385  
5386  
	for (int i = 0; i < S.size(); i++) {
5387  
		Integer s = S.get(i);
5388  
		Stack<Object> pairs = delta.get(s);
5389  
5390  
		if (pairs != null)
5391  
			for (int k = 0; k < pairs.size(); k += 2) {
5392  
				Alphabet A = (Alphabet)pairs.get(k);
5393  
5394  
				if (A == null) {
5395  
					Integer r = (Integer)pairs.get(k+1);
5396  
					S.add(r);
5397  
				}
5398  
			}
5399  
	}
5400  
	return S;
5401  
}
5402  
//Expression
5403  
/**
5404  
* <p>This class implements an {@link Lexicon.Expression <code>Expression</code>} expressing a regular language.</p>
5405  
*
5406  
* @version 1.3
5407  
* @author &copy; 1999-2009 <a href="http://www.csupomona.edu/~carich/">Craig A. Rich</a> &lt;<a href="mailto:carich@csupomona.edu">carich@csupomona.edu</a>&gt;
5408  
*/
5409  
abstract public static class Expression implements Cloneable {
5410  
5411  
	/**
5412  
	* <p>The initial state of the NFA constructed from this <code>Expression</code>.</p>
5413  
	*/
5414  
	Integer i;
5415  
	/**
5416  
	* <p>The final state of the NFA constructed from this <code>Expression</code>.</p>
5417  
	*/
5418  
	Integer f;
5419  
5420  
	/**
5421  
	* <p>Creates a clone of this <code>Expression</code>, and replicates the NFA constructed from this <code>Expression</code>.</p>
5422  
	*
5423  
	* @return a clone of this <code>Expression</code>.
5424  
	*/
5425  
	abstract public Object clone();
5426  
}
5427  
//Alphabet
5428  
/**
5429  
* <p>This class implements an {@link Lexicon.Alphabet <code>Alphabet</code>} of character symbols.</p>
5430  
*
5431  
* @version 1.3
5432  
* @author &copy; 1999-2009 <a href="http://www.csupomona.edu/~carich/">Craig A. Rich</a> &lt;<a href="mailto:carich@csupomona.edu">carich@csupomona.edu</a>&gt;
5433  
*/
5434  
abstract public static class Alphabet extends Expression {
5435  
5436  
	/**
5437  
	* <p>Indicates whether a character occurs in this <code>Alphabet</code>.</p>
5438  
	*
5439  
	* @param a the character whose status is requested.
5440  
	* @return <code>true</code> if <code>a</code> occurs in this <code>Alphabet</code>; <code>false</code> otherwise.
5441  
	*/
5442  
	abstract boolean contains(char a);
5443  
}
5444  
//Match
5445  
/**
5446  
* <p>This class implements an {@link Lexicon.Alphabet <code>Alphabet</code>} containing some characters.</p>
5447  
*
5448  
* @version 1.3
5449  
* @author &copy; 1999-2009 <a href="http://www.csupomona.edu/~carich/">Craig A. Rich</a> &lt;<a href="mailto:carich@csupomona.edu">carich@csupomona.edu</a>&gt;
5450  
*/
5451  
public static class Match extends Alphabet {
5452  
5453  
	/**
5454  
	* <p>The {@link Character} or {@link String} representing this <code>Alphabet</code>.</p>
5455  
	*/
5456  
	final Object A;
5457  
5458  
	/**
5459  
	* <p>Constructs an <code>Alphabet</code> containing some characters, and builds the NFA constructed from this <code>Expression</code>.</p>
5460  
	*
5461  
	* @param i the initial state of the NFA constructed.
5462  
	* @param A the {@link Character} or {@link String} of characters in this <code>Alphabet</code>.
5463  
	* @param f the final state of the NFA constructed.
5464  
	*/
5465  
	private Match(Integer i, Object A, Integer f) {
5466  
		this.A = A;
5467  
		put(this.i = i, this, this.f = f);
5468  
	}
5469  
5470  
	/**
5471  
	* <p>Constructs an <code>Alphabet</code> containing one character, and builds the NFA constructed from this <code>Expression</code>.</p>
5472  
	*
5473  
	* @param i the initial state of the NFA constructed.
5474  
	* @param a the character in this <code>Alphabet</code>.
5475  
	* @param f the final state of the NFA constructed.
5476  
	*/
5477  
	private Match(Integer i, char a, Integer f) {
5478  
		this(i, new Character(a), f);
5479  
	}
5480  
5481  
	/**
5482  
	* <p>Constructs an <code>Alphabet</code> containing one character, and builds the NFA constructed from this <code>Expression</code>.</p>
5483  
	*
5484  
	* @param a the character in this <code>Alphabet</code>.
5485  
	*/
5486  
	public Match(char a) {
5487  
		this(s(), a, s());
5488  
	}
5489  
5490  
	/**
5491  
	* <p>Constructs an <code>Alphabet</code> containing some characters, and builds the NFA constructed from this <code>Expression</code>.</p>
5492  
	*
5493  
	* @param A the {@link Character} or {@link String} of characters in this <code>Alphabet</code>.
5494  
	*/
5495  
	public Match(Object A) {
5496  
		this(s(), A, s());
5497  
	}
5498  
5499  
	/**
5500  
	* <p>Indicates whether a character occurs in this <code>Alphabet</code>.</p>
5501  
	*
5502  
	* @param a the character whose status is requested.
5503  
	* @return <code>true</code> if <code>a</code> occurs in this <code>Alphabet</code>; <code>false</code> otherwise.
5504  
	*/
5505  
	boolean contains(char a) {
5506  
		if (A instanceof Character)
5507  
			return (Character)A == a;
5508  
5509  
		if (A instanceof String)
5510  
			return ((String)A).indexOf(a) != -1;
5511  
5512  
		if (A instanceof Stack<?>)
5513  
			for (Alphabet alphabet : (Stack<Alphabet>)A)
5514  
				if (alphabet.contains(a)) return true;
5515  
		return false;
5516  
	}
5517  
5518  
	/**
5519  
	* <p>Creates a clone of this <code>Alphabet</code>, and replicates the NFA constructed from this <code>Expression</code>.</p>
5520  
	*
5521  
	* @return a clone of this <code>Alphabet</code>.
5522  
	*/
5523  
	public Object clone() {
5524  
		return new Match(A);
5525  
	}
5526  
}
5527  
//NonMatch
5528  
/**
5529  
* <p>This class implements an {@link Lexicon.Alphabet <code>Alphabet</code>} containing all except some characters.</p>
5530  
*
5531  
* @version 1.3
5532  
* @author &copy; 1999-2009 <a href="http://www.csupomona.edu/~carich/">Craig A. Rich</a> &lt;<a href="mailto:carich@csupomona.edu">carich@csupomona.edu</a>&gt;
5533  
*/
5534  
public static class NonMatch extends Match {
5535  
5536  
	/**
5537  
	* <p>Constructs an <code>Alphabet</code> containing all characters except one, and builds the NFA constructed from this <code>Expression</code>.</p>
5538  
	*
5539  
	* @param a the character not in this <code>Alphabet</code>.
5540  
	*/
5541  
	public NonMatch(char a) {
5542  
		super(a);
5543  
	}
5544  
5545  
	/**
5546  
	* <p>Constructs an <code>Alphabet</code> containing all characters except some, and builds the NFA constructed from this <code>Expression</code>.</p>
5547  
	*
5548  
	* @param A the {@link Character} or {@link String} of characters not in this <code>Alphabet</code>.
5549  
	*/
5550  
	public NonMatch(Object A) {
5551  
		super(A);
5552  
	}
5553  
5554  
	/**
5555  
	* <p>Indicates whether a character occurs in this <code>Alphabet</code>.</p>
5556  
	*
5557  
	* @param a the character whose status is requested.
5558  
	* @return <code>true</code> if <code>a</code> occurs in this <code>Alphabet</code>; <code>false</code> otherwise.
5559  
	*/
5560  
	boolean contains(char a) {
5561  
		return a != (char)-1 && !super.contains(a);
5562  
	}
5563  
5564  
	/**
5565  
	* <p>Creates a clone of this <code>Alphabet</code>, and replicates the NFA constructed from this <code>Expression</code>.</p>
5566  
	*
5567  
	* @return a clone of this <code>Alphabet</code>.
5568  
	*/
5569  
	public Object clone() {
5570  
		return new NonMatch(A);
5571  
	}
5572  
}
5573  
//Range
5574  
/**
5575  
* <p>This class implements an {@link Lexicon.Alphabet <code>Alphabet</code>} containing the characters in a range.</p>
5576  
*
5577  
* @version 1.3
5578  
* @author &copy; 1999-2009 <a href="http://www.csupomona.edu/~carich/">Craig A. Rich</a> &lt;<a href="mailto:carich@csupomona.edu">carich@csupomona.edu</a>&gt;
5579  
*/
5580  
public static class Range extends Alphabet {
5581  
5582  
	/**
5583  
	* <p>The first character in the range.</p>
5584  
	*/
5585  
	private final char a1;
5586  
	/**
5587  
	* <p>The last character in the range.</p>
5588  
	*/
5589  
	private final char a2;
5590  
5591  
	/**
5592  
	* <p>Constructs an <code>Alphabet</code> containing the characters in a range, and builds the NFA constructed from this <code>Expression</code>.</p>
5593  
	*
5594  
	* @param a1 the first character in the range.
5595  
	* @param a2 the last character in the range.
5596  
	*/
5597  
	public Range(char a1, char a2) {
5598  
		this.a1 = a1;
5599  
		this.a2 = a2;
5600  
		put(i = s(), this, f = s());
5601  
	}
5602  
5603  
	/**
5604  
	* <p>Indicates whether a character occurs in this <code>Alphabet</code>.</p>
5605  
	*
5606  
	* @param a the character whose status is requested.
5607  
	* @return <code>true</code> if <code>a</code> occurs in this <code>Alphabet</code>; <code>false</code> otherwise.
5608  
	*/
5609  
	boolean contains(char a) {
5610  
		return a1 <= a && a <= a2;
5611  
	}
5612  
5613  
	/**
5614  
	* <p>Creates a clone of this <code>Alphabet</code>, and replicates the NFA constructed from this <code>Expression</code>.</p>
5615  
	*
5616  
	* @return a clone of this <code>Alphabet</code>.
5617  
	*/
5618  
	public Object clone() {
5619  
		return new Range(a1, a2);
5620  
	}
5621  
}
5622  

5623  
/**
5624  
* <p>This class implements an {@link Lexicon.Alphabet <code>Alphabet</code>} containing the characters in a POSIX character class.</p>
5625  
*
5626  
* @version 1.3
5627  
* @author &copy; 1999-2009 <a href="http://www.csupomona.edu/~carich/">Craig A. Rich</a> &lt;<a href="mailto:carich@csupomona.edu">carich@csupomona.edu</a>&gt;
5628  
*/
5629  
public static class PosixClass extends Alphabet {
5630  
5631  
	/**
5632  
	* <p>The bit mask representing this <code>PosixClass</code>.</p>
5633  
	*/
5634  
	private final int posixClass;
5635  
5636  
	/**
5637  
	* <p>Constructs an <code>Alphabet</code> containing the characters in a POSIX character class, and builds the NFA constructed from this <code>Expression</code>.</p>
5638  
	*
5639  
	* @param posixClass the bit mask representing this <code>PosixClass</code>.
5640  
	*/
5641  
	private PosixClass(int posixClass) {
5642  
		this.posixClass = posixClass;
5643  
		put(i = s(), this, f = s());
5644  
	}
5645  
5646  
	/**
5647  
	* <p>Creates an <code>Alphabet</code> containing the uppercase alphabetic characters.</p>
5648  
	*
5649  
	* @return an <code>Alphabet</code> containing the uppercase alphabetic characters.
5650  
	*/
5651  
	public static PosixClass upper() {
5652  
		return new PosixClass(0x0001);
5653  
	}
5654  
5655  
	/**
5656  
	* <p>Creates an <code>Alphabet</code> containing the lowercase alphabetic characters.</p>
5657  
	*
5658  
	* @return an <code>Alphabet</code> containing the lowercase alphabetic characters.
5659  
	*/
5660  
	public static PosixClass lower() {
5661  
		return new PosixClass(0x0002);
5662  
	}
5663  
5664  
	/**
5665  
	* <p>Creates an <code>Alphabet</code> containing the alphabetic characters.</p>
5666  
	*
5667  
	* @return an <code>Alphabet</code> containing the alphabetic characters.
5668  
	*/
5669  
	public static PosixClass alpha() {
5670  
		return new PosixClass(0x0004);
5671  
	}
5672  
5673  
	/**
5674  
	* <p>Creates an <code>Alphabet</code> containing the decimal digit characters.</p>
5675  
	*
5676  
	* @return an <code>Alphabet</code> containing the decimal digit characters.
5677  
	*/
5678  
	public static PosixClass digit() {
5679  
		return new PosixClass(0x0008);
5680  
	}
5681  
5682  
	/**
5683  
	* <p>Creates an <code>Alphabet</code> containing the hexadecimal digit characters.</p>
5684  
	*
5685  
	* @return an <code>Alphabet</code> containing the hexadecimal digit characters.
5686  
	*/
5687  
	public static PosixClass xdigit() {
5688  
		return new PosixClass(0x0010);
5689  
	}
5690  
5691  
	/**
5692  
	* <p>Creates an <code>Alphabet</code> containing the alphanumeric characters.</p>
5693  
	*
5694  
	* @return an <code>Alphabet</code> containing the alphanumeric characters.
5695  
	*/
5696  
	public static PosixClass alnum() {
5697  
		return new PosixClass(0x0020);
5698  
	}
5699  
5700  
	/**
5701  
	* <p>Creates an <code>Alphabet</code> containing the punctuation characters.</p>
5702  
	*
5703  
	* @return an <code>Alphabet</code> containing the punctuation characters.
5704  
	*/
5705  
	public static PosixClass punct() {
5706  
		return new PosixClass(0x0040);
5707  
	}
5708  
5709  
	/**
5710  
	* <p>Creates an <code>Alphabet</code> containing the graphical characters.</p>
5711  
	*
5712  
	* @return an <code>Alphabet</code> containing the graphical characters.
5713  
	*/
5714  
	public static PosixClass graph() {
5715  
		return new PosixClass(0x0080);
5716  
	}
5717  
5718  
	/**
5719  
	* <p>Creates an <code>Alphabet</code> containing the printable characters.</p>
5720  
	*
5721  
	* @return an <code>Alphabet</code> containing the printable characters.
5722  
	*/
5723  
	public static PosixClass print() {
5724  
		return new PosixClass(0x0100);
5725  
	}
5726  
5727  
	/**
5728  
	* <p>Creates an <code>Alphabet</code> containing the blank characters.</p>
5729  
	*
5730  
	* @return an <code>Alphabet</code> containing the blank characters.
5731  
	*/
5732  
	public static PosixClass blank() {
5733  
		return new PosixClass(0x0200);
5734  
	}
5735  
5736  
	/**
5737  
	* <p>Creates an <code>Alphabet</code> containing the space characters.</p>
5738  
	*
5739  
	* @return an <code>Alphabet</code> containing the space characters.
5740  
	*/
5741  
	public static PosixClass space() {
5742  
		return new PosixClass(0x0400);
5743  
	}
5744  
5745  
	/**
5746  
	* <p>Creates an <code>Alphabet</code> containing the control characters.</p>
5747  
	*
5748  
	* @return an <code>Alphabet</code> containing the control characters.
5749  
	*/
5750  
	public static PosixClass cntrl() {
5751  
		return new PosixClass(0x0800);
5752  
	}
5753  
5754  
	/**
5755  
	* <p>Indicates whether a character occurs in this <code>Alphabet</code>.</p>
5756  
	*
5757  
	* @param a the character whose status is requested.
5758  
	* @return <code>true</code> if <code>a</code> occurs in this <code>Alphabet</code>; <code>false</code> otherwise.
5759  
	*/
5760  
	boolean contains(char a) {
5761  
		int UPPER = 0x0001; int LOWER = 0x0002;
5762  
		int ALPHA = 0x0004; int DIGIT = 0x0008;
5763  
		int XDIGIT = 0x0010; int ALNUM = 0x0020;
5764  
		int PUNCT = 0x0040; int GRAPH = 0x0080;
5765  
		int PRINT = 0x0100; int BLANK = 0x0200;
5766  
		int SPACE = 0x0400; int CNTRL = 0x0800;
5767  
		int classes = 0;
5768  
5769  
		switch (Character.getType(a)) {
5770  
			default: break;
5771  
			case Character.UPPERCASE_LETTER:
5772  
				classes |= UPPER | ALPHA | (('A' <= a && a <= 'F') ? XDIGIT : 0) | ALNUM | GRAPH | PRINT; break;
5773  
			case Character.LOWERCASE_LETTER:
5774  
				classes |= LOWER | ALPHA | (('a' <= a && a <= 'f') ? XDIGIT : 0) | ALNUM | GRAPH | PRINT; break;
5775  
			case Character.TITLECASE_LETTER:
5776  
			case Character.MODIFIER_LETTER:
5777  
			case Character.OTHER_LETTER:
5778  
				classes |= ALPHA | ALNUM | GRAPH | PRINT; break;
5779  
			case Character.NON_SPACING_MARK:
5780  
			case Character.COMBINING_SPACING_MARK:
5781  
			case Character.ENCLOSING_MARK:
5782  
				classes |= PUNCT | GRAPH | PRINT; break;
5783  
			case Character.DECIMAL_DIGIT_NUMBER:
5784  
				classes |= DIGIT | XDIGIT | ALNUM | GRAPH | PRINT; break;
5785  
			case Character.LETTER_NUMBER:
5786  
			case Character.OTHER_NUMBER:
5787  
				classes |= ALNUM | GRAPH | PRINT; break;
5788  
			case Character.CONNECTOR_PUNCTUATION:
5789  
			case Character.DASH_PUNCTUATION:
5790  
			case Character.START_PUNCTUATION:
5791  
			case Character.END_PUNCTUATION:
5792  
			case Character.INITIAL_QUOTE_PUNCTUATION:
5793  
			case Character.FINAL_QUOTE_PUNCTUATION:
5794  
			case Character.OTHER_PUNCTUATION:
5795  
			case Character.MATH_SYMBOL:
5796  
			case Character.CURRENCY_SYMBOL:
5797  
			case Character.MODIFIER_SYMBOL:
5798  
			case Character.OTHER_SYMBOL:
5799  
				classes |= PUNCT | GRAPH | PRINT; break;
5800  
			case Character.SPACE_SEPARATOR:
5801  
				classes |= PRINT | BLANK | SPACE; break;
5802  
			case Character.LINE_SEPARATOR:
5803  
			case Character.PARAGRAPH_SEPARATOR:
5804  
				break;
5805  
			case Character.CONTROL:
5806  
				classes |= ((a == '\t') ? BLANK : 0) | ((a == '\t' || a == '\n' || a == '\013' || a == '\f' || a == '\r') ? SPACE : 0) | CNTRL; break;
5807  
			case Character.FORMAT:
5808  
			case Character.SURROGATE:
5809  
			case Character.PRIVATE_USE:
5810  
			case Character.UNASSIGNED:
5811  
				break;
5812  
		}
5813  
		return (classes & posixClass) != 0;
5814  
	}
5815  
5816  
	/**
5817  
	* <p>Creates a clone of this <code>Alphabet</code>, and replicates the NFA constructed from this <code>Expression</code>.</p>
5818  
	*
5819  
	* @return a clone of this <code>Alphabet</code>.
5820  
	*/
5821  
	public Object clone() {
5822  
		return new PosixClass(posixClass);
5823  
	}
5824  
}
5825  
//UnicodeCategory
5826  
/**
5827  
* <p>This class implements an {@link Lexicon.Alphabet <code>Alphabet</code>} containing the characters in a Unicode general category.</p>
5828  
*
5829  
* @version 1.3
5830  
* @author &copy; 1999-2009 <a href="http://www.csupomona.edu/~carich/">Craig A. Rich</a> &lt;<a href="mailto:carich@csupomona.edu">carich@csupomona.edu</a>&gt;
5831  
*/
5832  
public static class UnicodeCategory extends Alphabet {
5833  
5834  
	/**
5835  
	* <p>The byte representing the Unicode general category.</p>
5836  
	*/
5837  
	private final byte category;
5838  
5839  
	/**
5840  
	* <p>Constructs an <code>Alphabet</code> containing the characters in a Unicode general category, and builds the NFA constructed from this <code>Expression</code>. The class {@link Character} defines byte constants representing each of the Unicode general categories.</p>
5841  
	*
5842  
	* @param category The byte representing the Unicode general category.
5843  
	* @see Character
5844  
	*/
5845  
	public UnicodeCategory(byte category) {
5846  
		this.category = category;
5847  
		put(i = s(), this, f = s());
5848  
	}
5849  
5850  
	/**
5851  
	* <p>Indicates whether a character occurs in this <code>Alphabet</code>.</p>
5852  
	*
5853  
	* @param a the character whose status is requested.
5854  
	* @return <code>true</code> if <code>a</code> occurs in this <code>Alphabet</code>; <code>false</code> otherwise.
5855  
	*/
5856  
	boolean contains(char a) {
5857  
		return Character.getType(a) == category;
5858  
	}
5859  
5860  
	/**
5861  
	* <p>Creates a clone of this <code>Alphabet</code>, and replicates the NFA constructed from this <code>Expression</code>.</p>
5862  
	*
5863  
	* @return a clone of this <code>Alphabet</code>.
5864  
	*/
5865  
	public Object clone() {
5866  
		return new UnicodeCategory(category);
5867  
	}
5868  
}
5869  
//Repetition
5870  
/**
5871  
* <p>This class implements an {@link Lexicon.Expression <code>Expression</code>} expressing the repetition of a regular language.</p>
5872  
*
5873  
* @version 1.3
5874  
* @author &copy; 1999-2009 <a href="http://www.csupomona.edu/~carich/">Craig A. Rich</a> &lt;<a href="mailto:carich@csupomona.edu">carich@csupomona.edu</a>&gt;
5875  
*/
5876  
public static class Repetition extends Expression {
5877  
5878  
	/**
5879  
	* <p>The operand <code>Expression</code>.</p>
5880  
	*/
5881  
	private final Expression e1;
5882  
	/**
5883  
	* <p>The minimum number of times <code>e1</code> is repeated.</p>
5884  
	*/
5885  
	private final int min;
5886  
	/**
5887  
	* <p>The maximum number of times <code>e1</code> is repeated.</p>
5888  
	*/
5889  
	private final int max;
5890  
5891  
	/**
5892  
	* <p>Constructs an <code>Expression</code> expressing the repetition of a regular language, and builds the NFA constructed from this <code>Expression</code>. Large finite values for the minimum or maximum cause the NFA constructed from the operand <code>Expression</code> to be copied many times, resulting in a space-inefficient NFA.</p>
5893  
	*
5894  
	* @param e1 the operand <code>Expression</code>.
5895  
	* @param min the minimum number of times <code>e1</code> is repeated. If negative, it is assumed to be zero.
5896  
	* @param max the maximum number of times <code>e1</code> is repeated. If negative, it is assumed to be infinity.
5897  
	*/
5898  
	public Repetition(Expression e1, int min, int max) {
5899  
		this.e1 = e1 = (Expression)e1.clone();
5900  
		this.min = min = Math.max(min, 0);
5901  
		this.max = max;
5902  
5903  
		i = (min > 0) ? e1.i : s();
5904  
		f = (min > 0) ? e1.f : i;
5905  
5906  
		if (min == 0 && max < 0) {
5907  
			put(i, null, e1.i);
5908  
			put(e1.f, null, i);
5909  
		}
5910  
		else {
5911  
			for (int k = 2; k <= min; k++) {
5912  
				e1 = (Expression)e1.clone();
5913  
				put(f, null, e1.i);
5914  
				f = e1.f;
5915  
			}
5916  
			if (max > min) {
5917  
				Integer tail = f;
5918  
				put(tail, null, f = s());
5919  
5920  
				for (int k = min+1; k <= max; k++) {
5921  
					if (k > 1) e1 = (Expression)e1.clone();
5922  
					put(tail, null, e1.i);
5923  
					put(tail = e1.f, null, f);
5924  
				}
5925  
			}
5926  
			else if (max < 0) put(f, null, e1.i);
5927  
		}
5928  
	}
5929  
5930  
	/**
5931  
	* <p>Creates a clone of this <code>Expression</code>, and replicates the NFA constructed from this <code>Expression</code>.</p>
5932  
	*
5933  
	* @return a clone of this <code>Expression</code>.
5934  
	*/
5935  
	public Object clone() {
5936  
		return new Repetition(e1, min, max);
5937  
	}
5938  
}
5939  
//Concatenation
5940  
/**
5941  
* <p>This class implements an {@link Lexicon.Expression <code>Expression</code>} expressing the concatenation of two regular languages.</p>
5942  
*
5943  
* @version 1.3
5944  
* @author &copy; 1999-2009 <a href="http://www.csupomona.edu/~carich/">Craig A. Rich</a> &lt;<a href="mailto:carich@csupomona.edu">carich@csupomona.edu</a>&gt;
5945  
*/
5946  
public static class Concatenation extends Expression {
5947  
5948  
	/**
5949  
	* <p>The left operand <code>Expression</code>.</p>
5950  
	*/
5951  
	private final Expression e1;
5952  
	/**
5953  
	* <p>The right operand <code>Expression</code>.</p>
5954  
	*/
5955  
	private final Expression e2;
5956  
5957  
	/**
5958  
	* <p>Constructs an <code>Expression</code> expressing the concatenation of two regular languages, and builds the NFA constructed from this <code>Expression</code>.</p>
5959  
	*
5960  
	* @param e1 the left operand <code>Expression</code>.
5961  
	* @param e2 the right operand <code>Expression</code>.
5962  
	*/
5963  
	public Concatenation(Expression e1, Expression e2) {
5964  
		this.e1 = e1 = (Expression)e1.clone();
5965  
		this.e2 = e2 = (Expression)e2.clone();
5966  
5967  
		i = e1.i;
5968  
		f = e2.f;
5969  
5970  
		put(e1.f, null, e2.i);
5971  
	}
5972  
5973  
	/**
5974  
	* <p>Creates a clone of this <code>Expression</code>, and replicates the NFA constructed from this <code>Expression</code>.</p>
5975  
	*
5976  
	* @return a clone of this <code>Expression</code>.
5977  
	*/
5978  
	public Object clone() {
5979  
		return new Concatenation(e1, e2);
5980  
	}
5981  
}
5982  
//Singleton
5983  
/**
5984  
* <p>This class implements an {@link Lexicon.Expression <code>Expression</code>} expressing a singleton language.</p>
5985  
*
5986  
* @version 1.3
5987  
* @author &copy; 1999-2009 <a href="http://www.csupomona.edu/~carich/">Craig A. Rich</a> &lt;<a href="mailto:carich@csupomona.edu">carich@csupomona.edu</a>&gt;
5988  
*/
5989  
public static class Singleton extends Expression {
5990  
5991  
	/**
5992  
	* <p>The string whose singleton language is expressed.</p>
5993  
	*/
5994  
	private final String x;
5995  
5996  
	/**
5997  
	* <p>Constructs an <code>Expression</code> expressing a singleton language, and builds the NFA constructed from this <code>Expression</code>.</p>
5998  
	*
5999  
	* @param x the string whose singleton language is expressed.
6000  
	*/
6001  
	public Singleton(String x) {
6002  
		this.x = x;
6003  
6004  
		f = i = s();
6005  
6006  
		for (char c : x.toCharArray())
6007  
			new Match(f, c, f = s());
6008  
	}
6009  
6010  
	/**
6011  
	* <p>Creates a clone of this <code>Expression</code>, and replicates the NFA constructed from this <code>Expression</code>.</p>
6012  
	*
6013  
	* @return a clone of this <code>Expression</code>.
6014  
	*/
6015  
	public Object clone() {
6016  
		return new Singleton(x);
6017  
	}
6018  
}
6019  
//Union
6020  
/**
6021  
* <p>This class implements an {@link Lexicon.Expression <code>Expression</code>} expressing the union of two regular languages.</p>
6022  
*
6023  
* @version 1.3
6024  
* @author &copy; 1999-2009 <a href="http://www.csupomona.edu/~carich/">Craig A. Rich</a> &lt;<a href="mailto:carich@csupomona.edu">carich@csupomona.edu</a>&gt;
6025  
*/
6026  
public static class Union extends Expression {
6027  
6028  
	/**
6029  
	* <p>The left operand <code>Expression</code>.</p>
6030  
	*/
6031  
	private final Expression e1;
6032  
	/**
6033  
	* <p>The right operand <code>Expression</code>.</p>
6034  
	*/
6035  
	private final Expression e2;
6036  
6037  
	/**
6038  
	* <p>Constructs an <code>Expression</code> expressing the union of two regular languages, and builds the NFA constructed from this <code>Expression</code>.</p>
6039  
	*
6040  
	* @param e1 the left operand <code>Expression</code>.
6041  
	* @param e2 the right operand <code>Expression</code>.
6042  
	*/
6043  
	public Union(Expression e1, Expression e2) {
6044  
		this.e1 = e1 = (Expression)e1.clone();
6045  
		this.e2 = e2 = (Expression)e2.clone();
6046  
6047  
		i = s();
6048  
		f = s();
6049  
6050  
		put(i, null, e1.i); put(e1.f, null, f);
6051  
		put(i, null, e2.i); put(e2.f, null, f);
6052  
	}
6053  
6054  
	/**
6055  
	* <p>Creates a clone of this <code>Expression</code>, and replicates the NFA constructed from this <code>Expression</code>.</p>
6056  
	*
6057  
	* @return a clone of this <code>Expression</code>.
6058  
	*/
6059  
	public Object clone() {
6060  
		return new Union(e1, e2);
6061  
	}
6062  
}
6063  
6064  
/**
6065  
* <p>The mapping representing this <code>Lexicon</code>. A terminal is mapped to the initial state of the NFA constructed from the associated <code>Expression</code>.</p>
6066  
*/
6067  
private final Map<Object, Expression> E;
6068  
6069  
/**
6070  
* <p>Puts a terminal and associated <code>Expression</code> into this <code>Lexicon</code>. The <code>Expression</code> supersedes any previously associated with the terminal.</p>
6071  
*
6072  
* @param a the terminal to add to this <code>Lexicon</code>.
6073  
* @param e the <code>Expression</code> associated with terminal <code>a</code>. When grabbing, the language expressed by <code>e</code> matches <code>a</code>.
6074  
*/
6075  
public void put(Object a, Expression e) {
6076  
	E.put(a, e);
6077  
	I.clear();
6078  
	F.clear();
6079  
}
6080  
6081  
/**
6082  
* <p>Indicates whether a symbol is a terminal in this <code>Lexicon</code>.</p>
6083  
*
6084  
* @param a the symbol whose status is requested.
6085  
* @return <code>true</code> if <code>a</code> is a terminal in this <code>Lexicon</code>; <code>false</code> otherwise.
6086  
*/
6087  
boolean terminal(Object a) {
6088  
	return E.containsKey(a);
6089  
}
6090  
//Lexicon()
6091  
/**
6092  
* <p>The terminal matched by the character at the end of a source stream.</p>
6093  
* @since 1.1, renames <code>END_OF_SOURCE</code> in version 1.0.
6094  
*/
6095  
protected static final Object $ = new String("$");
6096  
6097  
/**
6098  
* <p>The <code>Alphabet</code> containing the character at the end of a source stream.</p>
6099  
*/
6100  
private static final Expression $_EXPRESSION = new Match((char)-1);
6101  
6102  
/**
6103  
* <p>Constructs an empty <code>Lexicon</code>.</p>
6104  
*/
6105  
protected Lexicon() {
6106  
	E = new HashMap<Object, Expression>(500);
6107  
	I = new Set<Integer>(-200);
6108  
	F = new HashMap<Integer, Object>(500);
6109  
	put($, $_EXPRESSION);
6110  
}
6111  
6112  
/**
6113  
* <p>Constructs a <code>Lexicon</code> that is a shallow copy of <code>lexicon</code>. The fields of the new <code>Lexicon</code> refer to the same elements as those in <code>lexicon</code>.</p>
6114  
*
6115  
 *
6116  
* @param lexicon the <code>Lexicon</code> to copy.
6117  
*/
6118  
Lexicon(Lexicon lexicon) {/*debug*/
6119  
	debug = lexicon.debug;/*off*/
6120  
	E = lexicon.E;
6121  
	I = lexicon.I;
6122  
	F = lexicon.F;
6123  
}
6124  
//Lexicon.initial
6125  
/**
6126  
* <p>Returns the initial states of the lexical NFA.</p>
6127  
*
6128  
* @return {@link #I}, computing it and {@link #F} if there is a need to compute the current initial states and final states.
6129  
*/
6130  
private Set<Integer> initial() {
6131  
6132  
	if (I.isEmpty()) {
6133  
6134  
		for (Object a : E.keySet()) {
6135  
			Expression e = E.get(a);
6136  
6137  
			I.add(e.i);
6138  
			F.put(e.f, a);
6139  
		}
6140  
		closure(I);
6141  
	}
6142  
	return I;
6143  
}
6144  
//accept
6145  
/**
6146  
* <p>Computes the current final state, if any, in the lexical NFA.</p>
6147  
*
6148  
* @param S the current states.
6149  
* @return the maximum final state in <code>S</code>. Returns <code>null</code> if <code>S</code> contains no final states.
6150  
*/
6151  
private Integer accept(Set<Integer> S) {
6152  
6153  
	Integer
6154  
		f = null;
6155  
6156  
	for (Integer s : S)
6157  
		if (F.containsKey(s))
6158  
			if (f == null || f < s) f = s;
6159  
6160  
	return f;
6161  
}
6162  

6163  
/**
6164  
* <p>This class implements an {@link Lexicon.Exception <code>Exception</code>}.</p>
6165  
*
6166  
* @version 1.3
6167  
* @author &copy; 1999-2009 <a href="http://www.csupomona.edu/~carich/">Craig A. Rich</a> &lt;<a href="mailto:carich@csupomona.edu">carich@csupomona.edu</a>&gt;
6168  
*/
6169  
public class Exception extends java.lang.Exception {
6170  
6171  
	/**
6172  
	* <p>The extended error message.</p>
6173  
	*/
6174  
	private StringBuffer message;
6175  
6176  
	/**
6177  
	* <p>Constructs an <code>Exception</code> with a message.</p>
6178  
	*
6179  
	* @param message the error message.
6180  
	*/
6181  
	public Exception(String message) {
6182  
		super(message);
6183  
	}
6184  
6185  
	/**
6186  
	* <p>Returns the error message.</p>
6187  
	*
6188  
	* @return the error message.
6189  
	*/
6190  
	public String getMessage() {
6191  
		return (message == null) ? super.getMessage() : message.toString();
6192  
	}
6193  
6194  
	/**
6195  
	* <p>Extends the error message in this <code>Exception</code>. The extended message includes the line number, message and source characters following the error.</p>
6196  
	*
6197  
	* @param source the source character stream.
6198  
	* @return this <code>Exception</code> with an extended message.
6199  
	*/
6200  
	Exception extend(LineNumberReader source) {
6201  
		if (message == null) message = new StringBuffer(132);
6202  
		else message.setLength(0);
6203  
6204  
		message.append("line ");
6205  
		message.append(source.getLineNumber()+1);
6206  
		message.append(": ");
6207  
		message.append(super.getMessage());
6208  
		message.append(System.getProperty("line.separator"));
6209  
		message.append("...");
6210  
		message.append(word());
6211  
		try {
6212  
			String rest = source.readLine();
6213  
			if (rest != null) message.append(rest);
6214  
		}
6215  
		c
6216  
[...]
6217  
6218  
6219  
6220  
6221  
6222  
6223  
6224  
6225  
6226  
6227  
6228  
6229  
6230  
6231  
6232  
6233  
6234  
6235  
6236  
6237  
6238  
6239  
6240  
6241  
6242  
6243  
6244  
6245  
6246  
6247  
6248  
6249  
6250  
6251  
6252  
6253  
6254  
6255  
6256  
6257  
6258  
6259  
6260  
6261  
6262  
6263  
6264  
6265  
6266  
6267  
6268  
6269  
6270  
6271  
6272  
6273  
6274  
6275  
6276  
6277  
6278  
6279  
6280  
6281  
6282  
6283  
6284  
6285  
6286  
6287  
6288  
6289  
6290  
6291  
6292  
6293  
6294  
6295  
6296  
6297  
6298  
6299  
6300  
6301  
6302  
6303  
6304  
6305  
6306  
6307  
6308  
6309  
6310  
6311  
6312  
6313  
6314  
6315  
6316  
6317  
6318  
6319  
6320  
6321  
6322  
6323  
6324  
6325  
6326  
6327  
6328  
6329  
6330  
6331  
6332  
6333  
6334  
6335  
6336  
6337  
6338  
6339  
6340  
6341  
6342  
6343  
6344  
6345  
6346  
6347  
6348  
6349  
6350  
6351  
6352  
6353  
6354  
6355  
6356  
6357  
6358  
6359  
6360  
6361  
6362  
6363  
6364  
6365  
6366  
6367  
6368  
6369  
6370  
6371  
6372  
6373  
6374  
6375  
6376  
6377  
6378  
6379  
6380  
6381  
6382  
6383  
6384  
6385  
6386  
6387  
6388  
6389  
6390  
6391  
6392  
6393  
6394  
6395  
6396  
6397  
6398  
6399  

full source  download  show line numbers  debug dex  old transpilations   

Travelled to 13 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt

Comments [hide]

ID Author/Program Comment Date
690 #1000610 (pitcher) 2015-08-18 00:07:07
689 #1000604 (pitcher) 2015-08-18 00:07:22

add comment

Snippet ID: #1000508
Snippet name: Transpilation of #722
Eternal ID of this version: #1000508/1
Text MD5: dc5e14522d76e267db8cff1390d42ee0
Author: stefan
Category:
Type: JavaX source code
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2015-08-09 20:08:45
Source code size: 207526 bytes / 6399 lines
Pitched / IR pitched: No / Yes
Views / Downloads: 862 / 621
Referenced in: [show references]