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

28369
LINES
[SHOW ALL]

< > BotCompany Repo | #1032684 // #759 transpilation backup

JavaX source code (desktop) - run with: x30.jar

Download Jar.

import java.util.*;
import java.util.zip.*;
import java.util.List;
import java.util.regex.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.table.*;
import java.io.*;
import java.net.*;
import java.lang.reflect.*;
import java.lang.ref.*;
import java.lang.management.*;
import java.security.*;
import java.security.spec.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.imageio.*;
import java.math.*;

// We don't use #1006722 anymore
//   (but it's still listed in #7!?)

// TODO: deprecate (remove) the "f <id>" syntax

// TODO: skip ifclass blocks in findFunctionInvocations.
//       after main loop completes (including standard functions+
//       classes), run tok_ifclass and if any change, run main loop
//       again.
//       This will allow clean handling of functions like "unnull"
//       which right now _always_ include Symbol (because unnull
//       references emptySymbol inside an ifclass Symbol block).

// Note: Before editing this, transpile #7
// Use 'Very fresh' (R-transpile doesn't seem to work) or Q/M if you haven't screwed yourself currently
// by making your transpiler unable to transpile itself... or
// something

// Note: Don't try hijackPrint, it doesn't seem to work in here
// (because of the way it is called by e.g. transpileForServer_returnPair?)

/* functions to possibly edit when creating/changing JavaX constructs:
  tok_pingify
*/

// new JavaParser includes
import com.github.javaparser.*;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.*;
import com.github.javaparser.ast.visitor.*;
import com.github.javaparser.Problem;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.EnumDeclaration;
import com.github.javaparser.ast.stmt.LocalClassDeclarationStmt;
import com.github.javaparser.printer.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.text.NumberFormat;
import java.nio.charset.Charset;
import javax.imageio.metadata.*;
import javax.imageio.stream.*;
import static x30_pkg.x30_util.DynamicObject;
import java.nio.file.Files;
import java.nio.file.Path;

class main {




 // JavaParser







static CompilationUnit javaParseCompilationUnit(String java) { return javaParseCompilationUnit(java, false); }
static CompilationUnit javaParseCompilationUnit(String java, boolean withComments) {
  JavaParser p = new JavaParser();
  p.getParserConfiguration().setAttributeComments(withComments);
  ParseResult<CompilationUnit> r = p.parse(java);
  if (!r.isSuccessful())
    throw fail(str(r));
  return r.getResult().get();
}







static String javaParser_makeAllPublic(String src, Object... __) {
  CompilationUnit cu = javaParseCompilationUnit(src);
  PrettyPrinterConfiguration ppconf = new PrettyPrinterConfiguration();
  ppconf.setIndentSize(2);
  ppconf.setIndentType(PrettyPrinterConfiguration.IndentType.SPACES);
  ppconf.setPrintComments(false);
  javaParser_makeAllPublic_Visitor visitor = new javaParser_makeAllPublic_Visitor();
  visitor.notTopLevelClassDecl = boolOptPar("notTopLevelClassDecl", __);
  visitor.visit(cu, null);
  return cu.toString(ppconf);
}

static class javaParser_makeAllPublic_Visitor extends VoidVisitorAdapter {
  boolean notTopLevelClassDecl = false; // don't make top-level class declaration public
  
  void makePublic(NodeList<com.github.javaparser.ast.Modifier> modifiers) {
    // XXX: does this work?
    modifiers.remove(com.github.javaparser.ast.Modifier.privateModifier());
    modifiers.remove(com.github.javaparser.ast.Modifier.protectedModifier());
    if (!modifiers.contains(com.github.javaparser.ast.Modifier.publicModifier()))
      modifiers.add(com.github.javaparser.ast.Modifier.publicModifier());
  }

  public void visit(ClassOrInterfaceDeclaration n, Object arg) {
    Node parent = n.getParentNode().get();
    if (!(parent instanceof LocalClassDeclarationStmt
      || parent instanceof CompilationUnit && neq(n.getName().asString(), "main")))
      if (notTopLevelClassDecl)
        notTopLevelClassDecl = false;
      else
        makePublic(n.getModifiers());
    super.visit(n, arg);
  }
  
  public void visit(FieldDeclaration n, Object arg) {
    makePublic(n.getModifiers());
    super.visit(n, arg);
  }
  
  public void visit(ConstructorDeclaration n, Object arg) {
    Node parent = n.getParentNode().get();
    if (!(parent instanceof EnumDeclaration))
      makePublic(n.getModifiers());
    super.visit(n, arg);
  }
  
  public void visit(MethodDeclaration n, Object arg) {
    NodeList<com.github.javaparser.ast.Modifier> m = n.getModifiers();
    //print("Method found: " + n.getName() + " with modifiers: " + m + ", position: " + n.getRange()->begin);
    if (m.contains(com.github.javaparser.ast.Modifier.privateModifier()) && !m.contains(com.github.javaparser.ast.Modifier.staticModifier()) && !m.contains(com.github.javaparser.ast.Modifier.finalModifier()))
      m.add(com.github.javaparser.ast.Modifier.finalModifier());
    makePublic(m);
    super.visit(n, arg);
  }
}
static String javaParser_reparse_keepComments(String src) {
  CompilationUnit cu = javaParseCompilationUnit(src, true);
  PrettyPrinterConfiguration ppconf = new PrettyPrinterConfiguration();
  ppconf.setPrintComments(true);
  ppconf.setIndentSize(2);
  ppconf.setIndentType(PrettyPrinterConfiguration.IndentType.SPACES);
  return cu.toString(ppconf);
}

static String javaParser_makeAllPublic_keepComments(String src) {
  CompilationUnit cu = javaParseCompilationUnit(src);
  PrettyPrinterConfiguration ppconf = new PrettyPrinterConfiguration();
  ppconf.setPrintComments(true);
  ppconf.setIndentSize(2);
  ppconf.setIndentType(PrettyPrinterConfiguration.IndentType.SPACES);
  new javaParser_makeAllPublic_Visitor().visit(cu, null);
  return cu.toString(ppconf);
}

static int findCodeTokens(List<String> tok, String... tokens) {
  return findCodeTokens(tok, 1, false, tokens);
}

static int findCodeTokens(List<String> tok, boolean ignoreCase, String... tokens) {
  return findCodeTokens(tok, 1, ignoreCase, tokens);
}

static int findCodeTokens(List<String> tok, int startIdx, boolean ignoreCase, String... tokens) {
  return findCodeTokens(tok, startIdx, ignoreCase, tokens, null);
}

static HashSet<String> findCodeTokens_specials = lithashset("*", "<quoted>", "<id>", "<int>", "\\*");
static int findCodeTokens_bails, findCodeTokens_nonbails;

static interface findCodeTokens_Matcher {
  boolean get(String token);
}

static int findCodeTokens(List<String> tok, int startIdx, boolean ignoreCase, String[] tokens, Object condition) {
  int end = tok.size()-tokens.length*2+2, nTokens = tokens.length;
  int i = startIdx | 1;

  findCodeTokens_Matcher[] matchers = new findCodeTokens_Matcher[nTokens];
  IContentsIndexedList2 indexedList = tok instanceof IContentsIndexedList2 ? (IContentsIndexedList2) tok : null;
  
  
  TreeSet<HasIndex> indices = null;
  int indicesOfs = 0;

  for (int j = 0; j < nTokens; j++) {
    String p = tokens[j];
    findCodeTokens_Matcher matcher;
    if (p.equals("*"))
      matcher = t -> true;
    else if (p.equals("<quoted>"))
      matcher = t -> isQuoted(t);
    else if (p.equals("<id>"))
      matcher = t -> isIdentifier(t);
    else if (p.equals("<int>"))
      matcher = t -> isInteger(t);
    else if (p.equals("\\*"))
      matcher = t -> t.equals("*");
    else if (ignoreCase)
      matcher = t -> eqic(p, t);
    else {
      matcher = t -> t.equals(p);
      if (indexedList != null) {
        TreeSet<HasIndex> indices2 = indexedList.indicesOf_treeSetOfHasIndex(p);
        
        if (indices2 == null) return -1;
        if (indices == null || indices2.size() < indices.size()) {
          // found shorter list
          indices = indices2;
          indicesOfs = j;
        }
      }
    }
    matchers[j] = matcher;
  }
  
  // go over shortest index
  if (indices != null) {
    int min = i+indicesOfs*2;
    SortedSet<HasIndex> tailSet = min == 1 ? indices : indices.tailSet(new HasIndex(min));
    
    outer: for (HasIndex h : tailSet) {
      int idx = h.idx-indicesOfs*2;
      if (idx >= end) break;
      
      for (int j = 0; j < nTokens; j++) try {
        if (!matchers[j].get(tok.get(idx+j*2)))
          continue outer;
      } catch (IndexOutOfBoundsException e) {
        print("fct indicesOfs=" + indicesOfs + ", h=" + h + ", idx=" + idx);
        throw e;
      }

      if (condition == null || checkTokCondition(condition, tok, idx-1))
        return idx;
    }
    return -1;
  }
 
  outer: for (; i < end; i += 2) {
    for (int j = 0; j < nTokens; j++)
      if (!matchers[j].get(tok.get(i+j*2)))
        continue outer;

    if (condition == null || checkTokCondition(condition, tok, i-1)) // pass N index
      return i;
  }
  return -1;
}
static boolean autoQuine = true;
static int maxQuineLength = 80;
static boolean assumeTriple = true;
static boolean quickInstanceOfEnabled = false; // interferes with things
static boolean debug_jreplace = false;

// _registerThread usually costs nothing because we need
// the registerWeakHashMap mechanism anyway for ping().
// Anyway - no forced functions for now :)
static List<String> functionsToAlwaysInclude = ll(
  //"_registerThread",
  //"asRuntimeException"
);

// classes with two type parameters that can written with just one
// e.g. Pair<S> => Pair<S, S>
static Set<String> pairClasses = lithashset("Pair", "Either", "Map", "AbstractMap", "HashMap", "TreeMap", "LinkedHashMap", "MultiMap", "CompactHashMap", "WrappedMap", "F1", "IF1", "AllOnAll", "AllOnAllWithUpdates", "MultiSetMap", "AutoMap", "ValueOnDemandMap", "NavigableMap", "SortedMap");

// classes with 3 type parameters that can written with just one
// e.g. T3<S> => T3<S, S, S>
static Set<String> tripleClasses = lithashset("T3", "IF2", "F2", "IVF3", "VF3");

static String transpilingSnippetID;
//static new AtomicInteger varCount;
static ThreadLocal<AtomicInteger> varCountByThread = new ThreadLocal();
static Map<String, String> snippetCache = syncMap();
static boolean useIndexedList2 = false, useTokenIndexedList = true;
static boolean opt_javaTok = true;
static boolean cacheStdFunctions = true, cacheStdClasses = true;
static HashMap<Long, CachedInclude> cachedIncludes = new HashMap();
static ExecutorService executor;
static List lclasses;
static long startTime, lastPrint;

// These variables have to be cleared manually for each transpilation

static HashSet<Long> included = new HashSet();
static NavigableSet<String> definitions = ciSet();
static HashMap<String, String> rewrites = new HashMap();
static HashSet<String> shouldNotIncludeFunction = new HashSet(); // this verifies after the fact that the function was not included
static HashSet<String> shouldNotIncludeClass = new HashSet();
static HashSet<String> doNotIncludeFunction = new HashSet(); // this actually prevents the function from being included
static Map<String, String> functionToPackage = new HashMap();
static HashSet<String> doNotIncludeClass = new HashSet();
static HashSet<String> needLatest = new HashSet(); // this forces a function to be included
static HashSet<String> addedFunctions = new HashSet();
static HashSet<String> addedClasses = new HashSet();
static HashSet<String> hardFunctionReferences = new HashSet();
static HashSet<String> mapLikeFunctions = new HashSet();
static HashSet<String> lambdaMapLikeFunctions = new HashSet();
static HashSet<String> curry1LikeFunctions = new HashSet();
static HashSet<String> mapMethodLikeFunctions = new HashSet();
static HashSet<String> nuLikeFunctions = new HashSet();
static HashSet<String> getLikeFunctions = new HashSet(); // name is slightly misleading. this turns a pre-argument into a fieldLambda
static HashSet<String> lambdaMethod0LikeFunctions = new HashSet(); // we're getting better again with the names (maybe)
static HashSet<String> lambda0LikeFunctions = new HashSet();
static Map<String, String> extraStandardFunctions;
static boolean quickmainDone1, quickmainDone2;
static TreeSet<String> libs = new TreeSet();
static String mainBaseClass, mainPackage, mainClassName;
static boolean localStuffOnly = false; // for transpiling a fragment
static boolean asInclude = false; // for transpiling an include (auto-close scopes, enable all standard class ifclass [todo])
static boolean allowMetaCode = false; // run any embedded meta code
static List<String> metaPostBlocks, metaTransformers;
static boolean dontPrintSource = false;
static boolean dontLoadCachedIncludesFromVM = false; // for benchmarking
static HashSet<String> haveClasses = new HashSet();

static class CachedInclude {
  String javax;
  Future<String> java;
  String realJava;
  long snippetID;
  
  CachedInclude() {}
  CachedInclude(long snippetID) {
  this.snippetID = snippetID;}
  
  String java() {
    return realJava != null ? realJava : getFuture(java);
  }
  
  Future<String> javaFuture() {
    return realJava != null ? nowFuture(realJava) : java;
  }
  
  void clean() {
    if (java != null) {
      realJava = getFuture(java);
      java = null;
    }
  }
}

public static void main(final String[] args) throws Exception {
  startTime = lastPrint = sysNow();
  try {
    if (!dontLoadCachedIncludesFromVM)
      vmKeepWithProgramMD5_get("cachedIncludes");
  } catch (Throwable __e) { _handleException(__e); }
  executor = Executors.newFixedThreadPool(numberOfCores());
  transpilingSnippetID = or(getThreadLocal((ThreadLocal<String>) getOpt(javax(), "transpilingSnippetID")), transpilingSnippetID);
  //print(+transpilingSnippetID);
  transpileRaw_dontCopyFromCreator = true;
  
  final Object oldPrint = or(print_byThread().get(), "print_raw");
   AutoCloseable __17 = tempInterceptPrint(new F1<String, Boolean>() {
    Boolean get(String s) {
      long now = sysNow();
      long time = now-lastPrint; // -startTime;
      lastPrint = now;
      callF(oldPrint, "[" + formatInt(time/1000, 2) + ":" + formatInt(time % 1000, 3) + "] " + s);
      return false;
    }
  }); try {
  
  try {
    _main();
  } finally {
    interceptPrintInThisThread(oldPrint);
    if (executor != null) executor.shutdown();
    executor = null;
    localStuffOnly = false;
    asInclude = false;
  }
} finally { _close(__17); }}

static void _main() { try {
  if (sameSnippetID(programID(), defaultJavaXTranslatorID())) setDefaultJavaXTranslatorID("#7");
  
  //reTok_modify_check = true;
  //if (useIndexedList) findCodeTokens_debug = true;
  javaTok_opt = opt_javaTok;
  //findCodeTokens_indexed = findCodeTokens_unindexed = 0;
  findCodeTokens_bails = findCodeTokens_nonbails = 0;
  javaTok_n = javaTok_elements = 0;
  String in = loadMainJava();
  
  print("759 STARTING " + identityHashCode(main.class));
  // clear things
  includeInMainLoaded_magicComment = null;
  included.clear();
  definitions.clear();
  rewrites.clear();
  // XXX definitions.add("SymbolAsString");
  shouldNotIncludeFunction.clear();
  shouldNotIncludeClass.clear();
  doNotIncludeFunction.clear();
  functionToPackage.clear();
  needLatest.clear();
  doNotIncludeClass.clear();
  addedFunctions.clear();
  addedClasses.clear();
  hardFunctionReferences.clear();
  mapLikeFunctions = cloneHashSet(tok_mapLikeFunctions());
  lambdaMapLikeFunctions = new HashSet();
  curry1LikeFunctions = new HashSet();
  mapMethodLikeFunctions = cloneHashSet(tok_mapMethodLikeFunctions());
  nuLikeFunctions.clear();
  getLikeFunctions.clear();
  lambdaMethod0LikeFunctions.clear();
  lambda0LikeFunctions.clear();
  extraStandardFunctions = new HashMap();
  libs.clear();
  mainBaseClass = mainPackage = mainClassName = null;
  varCountByThread.set(null);
  quickmainDone1 = quickmainDone2 = false;
  metaPostBlocks = new ArrayList();
  metaTransformers = new ArrayList();
  dontPrintSource = false;
  defaultMaxQuineLength_value = defaultMaxQuineLength_defaultValue;
  debug_jreplace = false;
  haveClasses.clear();
  
  //L ts = findTranslators(toLines(join(tok)));
  //print("Translators in source at start: " + structure(ts));
  
  List<String> tok = jtok(in);
  
  try {
    tok_definitions(tok);
    
    // add m { }
    
    if (!localStuffOnly && !hasCodeTokens(tok, "m", "{") && !hasCodeTokens(tok, "main", "{") && !hasCodeTokens(tok, "class", "main")) {
      if (l(tok) == 1) tok = singlePlusList(first(tok), dropFirst(javaTok("m {}")));
      else {
        replaceTokens_reTok(tok, 1, 2, "m {\n\n" + unnull(get(tok, 1)));
        replaceTokens_reTok(tok, l(tok)-2, l(tok)-1, unnull(get(tok, l(tok)-2)) + "}");
      }
      tok_moveImportsUp(tok);
    }
    
    // standard translate
    
    //ts = findTranslators(toLines(join(tok)));
    //print("Translators in source: " + structure(ts));
    
    if (tok_hasTranslators(tok))
      tok = jtok(defaultTranslate(join(tok)));
    
    //print("end of default translate");
    //print(join(tok));
  
    //tok_autoCloseBrackets(tok);    
    
    tok_metaTransformNow(tok);
    
    tok_processEarlyIncludes(tok);
      
    tok_earlyGeneralStuff(tok);
    
    tok = tok_processIncludes(tok); // before standard functions
    if (processConceptsDot(tok))
      tok = tok_processIncludes(tok);
    tok = localStuff1(tok);
    
   if (!localStuffOnly) {
    int safety = 0;
    boolean same = false;
    do { // BIG LOOP
      ping();
      List<String> before = cloneList(tok);
      
      // do the non-local stuff (flags and rewrites, things that correlate across includes like tok_selfType)
      
      // to allow crazy stuff like "nonStatic !include #someStaticClass"
      tok_processEarlyIncludes(tok);
      
      jreplace(tok, "nonStatic static", "");
      
      tok_selfType(tok);
      tok_mainClassNameAndPackage(tok);
      tok_definitions(tok);
      tok_ifndef(tok);
      tok_ifdef(tok);
      defineMapLikes(tok);
      defineLambdaMapLikes(tok);
      defineCurry1Likes(tok);
      defineMapMethodLikes(tok);
      defineNuLikes(tok);
      defineXLikes(tok, "getLike", getLikeFunctions);
      defineXLikes(tok, "lambdaMethod0Like", lambdaMethod0LikeFunctions);
      defineXLikes(tok, "lambda0Like", lambda0LikeFunctions);
      if (tok_applyAllXLikeFunctions(tok)) {
        functionReferences(tok);
        lambdaReferences(tok);
      }
      
      tok_dropExtraCommas(tok); // from e.g. tok_applyMapMethodLikeFunctions
      tok_delegateTo(tok);
      tok_replaceWith(tok);
      tok_findAndClearRewrites(tok);
      tok_processRewrites(tok);
      
      tok_multiTypeArguments_v2(tok);
      
      // main bla(...) => mainClassName.bla(...)
      // or main ClassName => main.ClassName
      //jreplace(tok, "main <id>(", mainClassName() + ".$2(");
      grabImportedStaticFunctions(tok);
      jreplace_dyn_allowNull(tok, "main <id>", (_tok, cIdx, end) -> {
        String fname = _tok.get(cIdx+2);
        boolean isClass = haveClasses.contains(fname);
        printVars("expandMainRef", "fname", fname, "isClass", isClass);
        if (isClass || eqGet(_tok, cIdx+4, "(")) {
          String pkg = functionToPackage.get(fname);
          printVars("expandMainRef", "pkg", pkg);
          String call = "." + fname;
          return or(pkg, mainClassName()) + call;
        }
        return null;
      });
      
      try {
        if (safety == 0) tok = quickmain(tok);
      } catch (Throwable e) {
        printSources(tok);
        rethrow(e);
      }
      tok_collectMetaPostBlocks(tok, metaPostBlocks);
      tok_collectTransformers(tok, metaTransformers);
      tok_metaTransformNow(tok);
      
      // Hack to allow DynModule to reimplement _registerThread
      /*if (tok.contains("DynModule") && !addedClasses.contains("DynModule"))
        addStandardClasses_v2(tok);*/
      
      defineExtraSF(tok);
      tok = standardFunctions(tok);
      tok = stdstuff(tok); // all the keywords, standard
      String diff;
      long startTime = now();
      //diff = unidiff(before, join(tok));
      //print("unidiff: " + (now()-startTime) + " ms");
      //same = eq(diff, "");
      same = eq(tok, before);
      if (!same) {
        print("Not same " + safety + ".");
        //print(indent(2, diff));
      }
      if (safety++ >= 10) {
        //print(unidiff(before, join(tok)));
        printSources(tok);
        throw fail("safety 10 error!");
      }
    } while (!same); // END OF BIG LOOP
    
    print("Post.");
    
    if (mainBaseClass != null) {
      jreplace1(tok, "class main", "class main extends " + mainBaseClass);
      mainBaseClass = null;
    }
    
    print("moveImportsUp"); tok_moveImportsUp(tok);
    
    print("Indexing"); tok = indexTokenList(tok);
    
    // POST-PROCESSING after stdstuff loop
    
    if (transpilingSnippetID != null)
      jreplace_dyn(tok, "class whatever", 
        new F2<List<String>, Integer, String>() { public String get(List<String> tok, Integer cIndex) { try { 
          try { return "class " + stringToLegalIdentifier(getSnippetTitle(transpilingSnippetID)); } catch (Throwable __e) { _handleException(__e); }
          return "class Whatever";
         } catch (Exception __e) { throw rethrow(__e); } }
  public String toString() { return "pcall { ret \"class \" + stringToLegalIdentifier(getSnippetTitle(transpilingSni..."; }});
    
    //print("Type<A, A>"); // Type<A> to Type<A, A>
    
    print("extendClasses"); tok = extendClasses(tok);
    print("libs"); libs(tok);
    print("sourceCodeLine"); sourceCodeLine(tok);
    
    tok_overridableFunctionDefs(tok, null);
    
    // escaping for lambdas
    //jreplace(tok, "-=>", "->");
  
    // Stuff that depends on the list of inner classes (haveClasses)
    haveClasses = haveClasses_actual(tok);
    print("innerClassesVar"); innerClassesVar(tok, haveClasses);
    fillVar_transpilationDate(tok);
    haveClasses_addImported(tok, haveClasses);
    print("ifclass"); tok_ifclass(tok, haveClasses);
    
    print("slashCasts"); slashCasts(tok, haveClasses);
    print("newWithoutNew"); newWithoutNew(tok, haveClasses);
    
    if (!assumeTriple) {
      print("Triple");
      if (tok.contains("Triple") && !haveClasses.contains("Triple")) {
        jreplace(tok, "Triple", "T3");
        haveClasses.remove("Triple");
        haveClasses.add("T3");
        slashCasts(tok, lithashset("T3"));
        tok_quickInstanceOf(tok, lithashset("T3"));
      }
    }
    
    if (hasDef("SymbolAsString"))
      jreplace(tok, "Symbol", "String");
  
    // using non-lazy version now for cleanImports
    print("classReferences"); expandClassReferences/*_lazy*/(tok, haveClasses);
    
    if (metaCodeAllowed()) runMetaPostBlocks(tok);

    // Error-checking
    print("Error-checking"); 
    Set<String> functions = new HashSet(findFunctions(tok));
    for (String f : hardFunctionReferences)
      if (!functions.contains(f))
        throw fail("Function " + f + " requested, but not supplied");
  
    print("autoImports"); tok = autoImports(tok); // faster to do it at the end
    
    if (hasDef("cleanImports"))
      tok_cleanImports(tok);
    
    if (includeInMainLoaded_magicComment != null) {
      int i = lastIndexOfStartingWith(tok, includeInMainLoaded_magicComment);
      if (i >= 0) tok.set(i, dropPrefix(includeInMainLoaded_magicComment, tok.get(i)));
    }

    print("definitions=" + sfu(definitions));
    if (containsOneOf(definitions, "allpublic", "reparse", "PublicExceptTopClass")) {
      // Fire up the Java parser & pretty printer!
      print(containsIC(definitions, "allpublic")? "Making all public." : "Reparsing.");
      //try {
        String src = join(tok);
        try {
          if (containsIC(definitions, "allpublic"))
            src = javaParser_makeAllPublic(src);
          else if (containsIC(definitions, "PublicExceptTopClass"))
            src = javaParser_makeAllPublic(src, "notTopLevelClassDecl" , true);
          else if (containsIC(definitions, "keepComments"))
            src = javaParser_makeAllPublic_keepComments(src);
          else {
            print("javaParser_reparse_keepComments. size=" + l(src));
            src = javaParser_reparse_keepComments(src);
            print("size=" + l(src));
          }
        } catch (Throwable e) {
          String _src = src;
          String errorContext = extractAndPrintJavaParseError(_src, e);
          File f = transpilerErrorSourceFile();
          saveTextFileVerbose(f, src);
          dontPrintSource = true;
          throw fail("Java parsing error:" + errorContext + innerMessage(e)); // drop the long nested parser stacktraces
        }
        tok = jtok(src);
      /*} catch e {
        S src = join(tok);
        if (!dontPrintSource)
          print(src);
        print(f2s(saveProgramTextFile("error.java", src)));
        throw rethrow(e);
      }*/
    }
    
    // Do this after JavaParser (because it doesn't like package after class)
    
    if (mainPackage != null) {
      print("mainPackage");
      tokPrepend(tok, 1, "package " + mainPackage + ";\n");
      reTok(tok, 1, 2);
    }
    
    if (mainClassName != null) {
      print("mainClassName");
      jreplace(tok, "class main", "class " + mainClassName);
      jreplace(tok, "main.class", mainClassName + ".class");
      //tokPrepend(tok, 1, "class main {}\n"); // so main.class is generated and compiler sanity checks succeed. we can later skip it in the JavaXClassLoader
    }
    
    if (nempty(libs)) {
      print("Adding libs: " + libs);
      tok.add(concatMap_strings(new F1<String, String>() { public String get(String s) { try {  return "\n!" + s;  } catch (Exception __e) { throw rethrow(__e); } }
  public String toString() { return "\"\\n!\" + s"; }}, libs));
    }
   } // if (!localStuffOnly)
  } catch (Throwable e) {
    if (!dontPrintSource)
      printSources(tok);
    String src = join(tok);
    print(f2s(saveProgramTextFile("error.java", src)));
    throw rethrow(e);
  }
  
  /*if (useIndexedList)
    print("Indexed/unindexed lookups: " + findCodeTokens_indexed + "/" + findCodeTokens_unindexed + ", lists made: " + IndexedList2.instances);
  print("findCodeToken bails: " + findCodeTokens_bails + "/" + findCodeTokens_nonbails);
  print("javaToks: " + javaTok_n + "/" + javaTok_elements);*/
  
  print("Saving.");
  
  // for dexcompile.php
  if (mainClassName != null)
    tokPrepend(tok, 0, "//FILENAME: " 
      + (mainPackage != null ? mainPackage.replace(".", "/") + "/" : "") + mainClassName + ".java\n");
    
  if (mainJava != null)
    mainJava = join(tok);
  else if (tok.contains("package"))
    splitJavaFiles(tok);
  else
    saveMainJava(tok);
} catch (Exception __e) { throw rethrow(__e); } }

static List<String> localStuff1(List<String> tok) {
  int safety = 0, i;
  boolean same = false;
  tok = indexTokenList(tok);
  
  tok_scopes(tok, "autoCloseScopes" , true);
    
  do {
    ping();
    List<String> before = cloneList(tok);
    
    //print("localStuff loop " + safety);
    
    earlyStuff(tok);
    
    // EARLY local stuff goes here
    
    tok_dropMetaComments(tok);
    
    tok_earlyGeneralStuff(tok);
    
    conceptDeclarations(tok);
    tok_recordDecls(tok);
    
    tok = multilineStrings(tok);
    tok_singleQuoteIdentifiersToStringConstants(tok);
    tok_inStringEvals(tok);
    tok_listComprehensions(tok);
    
    tok_eventFunctions(tok);
    
    tok_for_single(tok);
    
    tok_for_unpair(tok); // Do this...
    //tok_doubleFor_v2(tok);
    tok_doubleFor_v3(tok); // ...before this
    
    tok_forUnnull(tok);
    tok_ifCast(tok);
    forPing(tok);
    tok_directSnippetRefs(tok);
    quicknu(tok);
    //tok_juxtaposeCalls(tok);
    
    jreplace(tok, "LLS", "L<LS>");
    jreplace(tok, "LS", "L<S>");
    jreplace(tok, "ES", "Ext<S>");
    jreplace(tok, "ExtS", "Ext<S>");
    
    jreplace(tok, "WeakRef", "WeakReference");
    
    jreplace(tok, "dispose <id>;", "{ cleanUp($2); $2 = null; }");
  
    tok_doPing(tok);
      
    replaceKeywordBlock(tok,
      "swing",
      "{ swing(r {",
      "}); }");
      
    replaceKeywordBlock(tok,
      "androidUI",
      "{ androidUI(r {",
      "}); }");
      
    replaceKeywordBlock(tok,
      "withDBLock",
      "{ withDBLock(r {",
      "}); }");
      
    replaceKeywordBlock(tok, "afterwards", "temp tempAfterwards(r {", "});");
      
    for (String keyword : ll("tokcondition", "tokCondition"))
      replaceKeywordBlock(tok,
        keyword,
        "new TokCondition { public bool get(final L<S> tok, final int i) {",
        "}}");
      
    jreplace(tok, "synced <id>", "synchronized $2");
    jreplace(tok, "sync <id>", "synchronized $2");
    
    jreplace(tok, "synchronized {", "synchronized(this) {");
    
    replaceKeywordBlock(tok, "answer",
      "static S answer(S s) {\nfinal new Matches m;\n",
      "\nret null;\n}");
      
    replaceKeywordBlock(tok, "static-pcall",
      "static { pcall {",
      "}}");
      
    replaceKeywordBlock(tok, "loading",
      "{ temp tempShowLoadingAnimation(); ",
      "}");
  
    replaceKeywordPlusQuotedBlock(tok, "loading",
      new Object() { String[] get(List<String> tok, int i) {
        String text = tok.get(i+2);
        return new String[] {
          "{ temp tempShowLoadingAnimation(" + text + "); ",
          "}" };
      }});
      
    while ((i = jfind(tok, "visualize as")) >= 0) {
      int j = tok_findEndOfStatement(tok, i); // expression, rather
      tok.set(i+2, "{ ret");
      tok.set(j-1, "; }");
      reTok(tok, i, j);
    }
      
    for (String pat : ll("visual {", "visualize {"))
      jreplace(tok, pat, "public JComponent visualize() {", tokCondition_beginningOfMethodDeclaration());
  
    jreplace(tok, "visualize2 {", "JComponent visualize2() {", tokCondition_beginningOfMethodDeclaration());
    
    replaceKeywordBlock(tok, "start-thread-printDone", "start-thread {", "printDone(); }");

    replaceKeywordBlock(tok, "start-thread", "start { thread \"Start\" { temp enter(); pcall {", "}}}");
    
    jreplace(tok, "start {", "void start() ctex { super.start();", tokCondition_beginningOfMethodDeclaration());
    
    var notVoidMethod = new TokCondition() { public boolean get(final List<String> tok, final int i) {
      return neqGet(tok, i-1, "void");
    }};
    
    // run { ... } => public void run() { ... }
    // same with close
    jreplace(tok, "run {", "public void run() {", notVoidMethod);
    jreplace(tok, "close {", "public void close() {", notVoidMethod);
    
    replaceKeywordBlock(tok, "html",
      "static O html(S uri, fMap<S, S> params) ctex " + "{\n", "}");
    
    replaceKeywordBlock(tok, "afterVisualize",
      "visualize { JComponent _c = super.visualize();",
      "ret _c; }");
    
    replaceKeywordBlock(tok, "enhanceFrame",
      "void enhanceFrame(Container f) { super.enhanceFrame(f);",
      "}");
    
    if (assumeTriple)
      jreplace(tok, "Triple", "T3");

    tok_shortFinals(tok);
    
    tok_moduleClassDecls(tok);

    jreplace(tok, "static sync", "static synchronized");
    jreplace(tok, "sclass", "static class");
    jreplace(tok, "fclass", "final class");
    jreplace(tok, "fsclass", "final static class");
    jreplace(tok, "srecord", "static record");
    jreplace(tok, "strecord", "static transformable record");
    jreplace(tok, "record noeq", "noeq record");
    jreplace(tok, "asclass", "abstract static class");
    jreplace(tok, "sinterface", "static interface");
    jreplace(tok, "ssynchronized", "static synchronized");
  
    jreplace(tok, "ssvoid", "static synchronized void");
    jreplace(tok, "sbool", "static bool");
    jreplace(tok, "fbool", "final bool");
    jreplace(tok, "sint", "static int");
    jreplace(tok, "snew", "static new");
    jreplace(tok, "sv <id>", "static void $2");
    jreplace(tok, "pvoid", "public void");
  
    // "sS" => static S
    jreplace(tok, "sS", "static S");
  
    // "sO" => static O
    jreplace(tok, "sO", "static O");
  
    // "sL" => static L
    jreplace(tok, "sL", "static L");
  
    // "toString {" => "public S toString() {"
    jreplace(tok, "toString {", "public S toString() {");
    
    jreplace(tok, "Int", "Integer");
    jreplace(tok, "Bool", "Boolean");
    jreplace(tok, "BigInt", "BigInteger");
    jreplace(tok, "Char", "Character");
    
    jreplace(tok, "Sym", "Symbol");
    jreplace(tok, "SymSym", "SymbolSymbol");
    
    jreplace(tok, "SS", "Map<S>");
    jreplace(tok, "SymbolSymbol", "Map<Symbol>");
    
    jreplace(tok, "MapSO", "Map<S, O>");
    
    jreplace(tok, "ByName<", "IF1<S, ");
    
    jreplace(tok, "ITransform<<id>>", "IF1<$3>");
    jreplace(tok, "ITransform", "IF1");
    
    jreplace(tok, "PairS", "Pair<S>");
    jreplace(tok, "LPairS", "L<Pair<S>>");
    
    jreplace(tok, "T3S", "T3<S>");
    jreplace(tok, "F1S", "F1<S>");
    
    jreplace(tok, "ItIt", "IterableIterator");
    jreplace(tok, "CloseableItIt", "CloseableIterableIterator");

    jreplace(tok, "class <id> > <id>", "class $2 extends $4", new TokCondition() { public boolean get(final List<String> tok, final int i) {
      String t = _get(tok, i+1+4*2);
      return eq(t, "{") || isIdentifier(t);
    }});
    jreplace(tok, "class <id> > <id><<id>> {", "class $2 extends $4  $5 $6 $7 {");
    
    jreplace(tok, "ISegmenter", "IF1<BufferedImage, L<Rect>>");
    
    // IPred<A, B> => IF2<A, B, Bool>
    jreplace(tok, "IPred<<id>, <id>>", "IF2<$3, $5, Bool>");
    
    // IPred<A> => IF1<A, Bool>
    jreplace(tok, "IPred<<id>>", "IF1<$3, Bool>");
    jreplace(tok, "IPred<<id>[]>", "IF1<$3[], Bool>");
    
    // Proposition => WithReasoning<S> (should we really define this?)
    jreplace(tok, "Proposition", "WithReasoning<S>");
    
    replaceKeywordBlock(tok, "print exceptions",
      "{ try {",
      "} on fail __e { printStackTrace(__e); } }");
    
    // "on fail {" => "catch (Throwable _e) { ... rethrow(_e); }"
    replaceKeywordBlock(tok, "on fail",
      "catch (Throwable _e) {",
      "\nthrow rethrow(_e); }");
  
    // "on fail e {" => "catch (Throwable e) { ... rethrow(e); }"
    replaceKeywordBlock_dyn2_legacy(tok, "on fail <id>", new Object() {
      String[] get(List<String> tok, int iOpening, int iClosing) {
        String var = tok.get(iOpening-2);
        return new String[] {
          "catch (Throwable " + var + ") {",
          "\nthrow rethrow(" + var + "); }"
        };
      }
    });

    // "on fail Type e {" => "catch (Type e) { ... rethrow(e); }"
    replaceKeywordBlock_dyn2_legacy(tok, "on fail <id> <id>", new Object() {
      String[] get(List<String> tok, int iOpening, int iClosing) {
        String type = tok.get(iOpening-4);
        String var = tok.get(iOpening-2);
        return new String[] {
          "catch (" + type + " " + var + ") {",
          "\nthrow " + var + "; }"
        };
      }
    });

    // "catch {" => "catch (Throwable _e) {"
    jreplace(tok, "catch {", "catch (Throwable _e) {");
  
    // "catch print e {" => "catch e { _handleException(e); "
    jreplace(tok, "catch print <id> {", "catch $3 { _handleException($3);");
  
    // "catch print short e {" => "catch e { printExceptionShort(e); "
    jreplace(tok, "catch print short <id> {", "catch $4 { printExceptionShort($4);");
    
    // "catch X e {" => "catch (X e) {"
    jreplace(tok, "catch <id> <id> {", "catch ($2 $3) {");
  
    // "catch e {" => "catch (Throwable e) {" (if e is lowercase)
    jreplace(tok, "catch <id> {", "catch (Throwable $2) {", new TokCondition() { public boolean get(final List<String> tok, final int i) {
      String word = tok.get(i+3);
      return startsWithLowerCaseOrUnderscore(word);
    }});
    
    jreplace(tok, "+ +", "+", new TokCondition() { public boolean get(final List<String> tok, final int i) {
      //printStructure("++: ", subList(tok, i-1, i+6));
      if (empty(_get(tok, i+2))) return false; // no space between the pluses
      if (empty(_get(tok, i)) && eq("+", _get(tok, i-1))) return false;  // an actual "++" at the left
      if (empty(_get(tok, i+4)) && eq("+", _get(tok, i+5))) return false;  // an actual "++" at the right
      //print("doing it");
      return true;
    }});
  
    // single underscore (not allowed in Java anymore) to double underscore
    jreplace(tok, "_", "__");
    
    // [stdEq] -> implementation of equals() and hashCode()
    jreplace(tok, "[stdEq]",
      "public bool equals(O o) { ret stdEq2(this, o); }\n" +
      "public int hashCode() { ret stdHash2(this); }");
    
    // [stdToString] -> toString { ret stdToString(this); }
    jreplace(tok, "[stdToString]",
      "toString { ret stdToString(this); }");
    
    // [concepts] "concept.field!" for dereferencing references
    
    // "<id>!", "?!" etc.
    jreplace(tok, "*!", "$1.get()", new TokCondition() { public boolean get(final List<String> tok, final int i) {
      String l = tok.get(i+1);
      if (!(isIdentifier(l) || eqOneOf(l, ")", "?"))) return false;
      if (tok.get(i+2).contains("\n")) return false; // no line break between <id> and !
      if (nempty(tok.get(i+4))) return true; // space after = ok
      String t = _get(tok, i+5);
      if (t == null) return false;
      if (isIdentifier(t) || eqOneOf(t, "=", "(")) return false;
      return true;
    }});
    
    jreplace(tok, "for (<id> :", "for (var $3 :");
    
    jreplace(tok, "for (<id> <id>)", "for ($3 $4 : list($3))");
    jreplace(tok, "for (final <id> <id>)", "for (final $4 $5 : list($4))");

    jreplace(tok, "ret", "return", new TokCondition() { public boolean get(final List<String> tok, final int i) {
      if (eqGetOneOf(tok, i-1, "int", "return")) return false;
      String next = _get(tok, i+3);
      if (eq(next, ".")) return isInteger(_get(tok, i+5)); // e.g. "ret .1", but not "ret.bla"
      return !eqOneOf(next, "=", ")");
    }});
    
    // "continue unless", "break unless"
    
    for (String phrase : ll("continue unless", "break unless"))
      while ((i = jfind(tok, phrase)) >= 0) {
        String keyword = tok.get(i);
        int j = scanOverExpression(tok, getBracketMap(tok), i+4, ";");
        replaceTokens(tok, i, i+4, "{ if (!(");
        tok.set(j, ")) " + keyword + "; }");
        reTok(tok, i, j+1);
      }
    
    // S s = bla(), return if null; => S s = bla(); if (s == null) return;
    // same with continue, break
    while ((i = jfind(tok, ", <id> if null;", new TokCondition() { public boolean get(final List<String> tok, final int i) {
      return eqOneOf(tok.get(i+3), "return", "ret", "continue", "break");
    }})) >= 0) {
      String cmd = tok.get(i+2);
      int j = tok_findBeginningOfStatement(tok, i);
      print("Found statement " + j + "/" + i + " - " + joinSubList(tok, j-1, i+5*2-1));
      String var = getVarDeclarationName(subList(tok, j-1, i));
      replaceTokens_reTok(tok, i, i+5*2-1, "; if (" + var + " == null) " + cmd + ";");
    }
    
    // S s = bla(), return false if null; => S s = bla(); if (s == null) return false;
    // (with false being any identifier)
    while ((i = jfind(tok, ", return <id> if null;")) >= 0) {
      String returnValue = tok.get(i+4);
      int j = tok_findBeginningOfStatement(tok, i);
      String var = getVarDeclarationName(subList(tok, j-1, i));
      replaceTokens_reTok(tok, i, i+6*2-1, "; if (" + var + " == null) return " + returnValue + ";");
    }
    
    // "continue if", "break if"
    
    for (String phrase : ll("continue if", "break if"))
      while ((i = jfind(tok, phrase)) >= 0) {
        String keyword = tok.get(i);
        int j = scanOverExpression(tok, getBracketMap(tok), i+4, ";");
        replaceTokens(tok, i, i+4, "{ if (");
        tok.set(j, ") " + keyword + "; }");
        reTok(tok, i, j+1);
      }
    
    // return unless set <var>; => { if (var) return; set var; }
    jreplace(tok, "return unless set <id>;", "{ if ($4) return; set $4; }");

    // set x; => x = true;
    // yeah it doesn't save many characters, but I like it
    jreplace(tok, "set <id>;", "$2 = true;", new TokCondition() { public boolean get(final List<String> tok, final int i) {
      return !eqGet(tok, i-1, "unless");
    }});
    
    // set !x; => x = false; (did this one day and thought yes it makes sense in a way)
    jreplace(tok, "set !<id>;", "$3 = false;");
    
    // unset x; => x = false;
    jreplace(tok, "unset <id>;", "$2 = false;");
    
    // "return if"
    
    while ((i = jfind(tok, "return if")) >= 0) {
      int j = scanOverExpression(tok, getBracketMap(tok), i+4, ";");
      replaceTokens(tok, i, i+4, "{ if (");
      tok.set(j, ") return; }");
      reTok(tok, i, j+1);
    }
    
    // "return unless"
    
    while ((i = jfind(tok, "return unless")) >= 0) {
      int j = scanOverExpression(tok, getBracketMap(tok), i+4, ";");
      replaceTokens(tok, i, i+4, "{ if (!(");
      tok.set(j, ")) return; }");
      reTok(tok, i, j+1);
    }
    
    tok_ifNullAssign(tok);

    // "return <id> if"
    
    // "return with <statement>" / "continue with <statement>" / "break with <statement>"
    
    while ((i = jfind(tok, "<id> with", new TokCondition() { public boolean get(final List<String> tok, final int i) { return eqOneOf(tok.get(i+1), "return", "continue", "break"); }})) >= 0) {
      // XXX int j = scanOverExpression(tok, getBracketMap(tok), i+4, ";");
      int j = tok_findEndOfStatement(tok, i+4)-1;
      //print("Found statement: " + joinSubList(tok, i+4, j+1));
      tok.set(j, "; " + tok.get(i) + "; }");
      replaceTokens(tok, i, i+3, "{");
      reTok(tok, i, j+1);
    }
    
    while ((i = jfind(tok, "return <id> if")) >= 0) {
      int j = scanOverExpression(tok, getBracketMap(tok), i+4, ";");
      tok.set(j, ") return " + tok.get(i+2) + "; }");
      replaceTokens(tok, i, i+6, "{ if (");
      reTok(tok, i, j+1);
    }
    
    // return "bla" with <statement>
    
    while ((i = jfindOneOf(tok,
      "return <quoted> with", "return <id> with")) >= 0) {
      String result = tok.get(i+2);
      int j = scanOverExpression(tok, getBracketMap(tok), i+6, ";");
      replaceTokens(tok, i, i+5, "{");
      tok.set(j, "; return " + result + "; }");
      reTok(tok, i, j+1);
    }
    
    tok_debugStatements(tok);

    // while not null (...) / if not null (...)
    
    while ((i = jfind_check("not", tok, "<id> not null (", new TokCondition() { public boolean get(final List<String> tok, final int i) {
      return eqOneOf(_get(tok, i+1), "if", "while");
    }})) >= 0) {
      int closingBracket = findEndOfBracketPart(tok, i+6)-1;
      replaceTokens(tok, i+2, i+6, "(");
      tok.set(closingBracket, ") != null)");
      reTok(tok, i, closingBracket+1);
    }
    
    // while null (...) / if null (...)
    
    while ((i = jfind_check("null", tok, "<id> null (", new TokCondition() { public boolean get(final List<String> tok, final int i) {
      return eqOneOf(_get(tok, i+1), "if", "while");
    }})) >= 0) {
      int closingBracket = findEndOfBracketPart(tok, i+4)-1;
      replaceTokens(tok, i+2, i+4, "(");
      tok.set(closingBracket, ") == null)");
      reTok(tok, i, closingBracket+1);
    }
    
    // Replace $1 with m.unq(0) etc. - caveat: this blocks identifiers $1, $2, ...
    for (i = 1; i < l(tok); i += 2) {
      String s = tok.get(i);
      if (s.startsWith("$")) {
        s = substring(s, 1);
        if (isInteger(s)) {
          tok.set(i, "m.unq(" + (parseInt(s)-1) + ")");
          reTok(tok, i);
        }
      }
    }
  
    // instanceof trickery
    
    jreplace(tok, "is a <id>", "instanceof $3");
    jreplace(tok, "!<id> instanceof <id>.<id>", "!($2 instanceof $4.$6)");
    jreplace(tok, "!<id> instanceof <id>", "!($2 instanceof $4)");
    jreplace(tok, "<id> !instanceof <id>", "!($1 instanceof $4)");
    
    // map func1 func2 func3(...) => mapFGH(f func1, f func2, f func3, ...)
    jreplace(tok, "map <id> <id> <id>(", "mapFGH(f $2, f $3, f $4,");

    // map func1 func2(...) => mapFG(f func1, f func2, ...)
    jreplace(tok, "map <id> <id>(", "mapFG(f $2, f $3,");

    // "ref->bla" for dereferencing Concept.Ref or ThreadLocal or other
    // For lambdas, use SPACES on the left or right of the arrow!
    //jreplace(tok, "<id> ->", "$1.get().");
    jreplace(tok, "->", ".get().", new TokCondition() { public boolean get(final List<String> tok, final int i) {
      return empty(tok.get(i)) // no space on left of arrow
        && empty(tok.get(i+2)) // no space inside of arrow
        && empty(tok.get(i+4)) // no space on right of arrow
        && !eq(_get(tok, i-1), "-"); // i-->0;
    }});
    
    // shortened subconcept declaration (before star constructors!)
    shortenedSubconcepts(tok);
    
    tok_beaConceptDecls(tok);
    
    // "case" as a variable name ( => _case)
    
    caseAsVariableName(tok);
    
    // "do" as a function name ( => dO)
    
    tok_doAsMethodName(tok);
    
    // "continue" as a function name ( => _continue)
    continueAsFunctionName(tok);
    
    tok_extend(tok);
    
    jreplace(tok, "pn {", "p-noconsole {");
      
    // Do these BEFORE awt replacement! ("p-awt" contains "awt" token)
    replaceKeywordBlock(tok, "r-awt", "r { awt {", "}}");
    if (hasCodeTokens(tok, "p", "-")) tok_p_old(tok);

    replaceKeywordBlock(tok, "awt-messagebox", "awt { pcall-messagebox {", "}}");
    replaceKeywordBlock(tok, "awt", "swingLater(r {", "});");
  
    jreplace(tok, "p-android {", "set flag Android. p {");
      
    unswing(tok);
    lockBlocks(tok);
    tok_switchTo(tok);
    
    // trim x;
    
    jreplace(tok, "trim <id>;", "$2 = trim($2);");
  
    // iterate with index
  
    jreplace (tok, "for <id> over <id>:", "for (int $2 = 0; $2 < l($4); $2++)");
    jreplace (tok, "for <id> backwards over <id>:", "for (int $2 = l($5)-1; $2 >= 0; $2--)");
    jreplace (tok, "for <id>, <id> <id> over <id>: {", "for (int $2 = 0; $2 < l($7); $2++) { $4 $5 = $7.get($2);");
    jreplace (tok, "for <id>, <id> <id> backwards over <id>: {", "for (int $2 = l($8)-1; $2 >= 0; $2--) { $4 $5 = $8.get($2);");
    jreplace (tok, "for <id> to <id>:", "for (int $2 = 0; $2 < $4; $2++)");
    jreplace (tok, "for <id> to <int>:", "for (int $2 = 0; $2 < $4; $2++)");
    
    tok = expandShortTypes(tok);
      
    tok_equalsCast(tok);
    tok_equalsOptCast(tok);

    replaceKeywordBlock(tok, "r-thread-messagebox", "r-thread { pcall-messagebox {", "}}");
    
    replaceKeywordBlock(tok, "thread-messagebox", "thread { pcall-messagebox {", "}}");
    
    jreplace(tok, "rThread {", "r-thread {");
    jreplace(tok, "rEnterThread {", "rThreadEnter {");
    jreplace(tok, "rThreadEnter {", "r-thread { temp enter(); ");
  
    replaceKeywordBlock(tok, "r-thread", "runnableThread(r {", "})");
    rNamedThread(tok);
    
    // only works in the scope of a DynModule
    jreplace(tok, "rEnter {", "r { temp enter(); ");
  
    replaceKeywordBlock(tok, "r-pcall", "r { pcall {", "}}");
  
    replaceKeywordBlock(tok, "r-messagebox", "r { pcall-messagebox {", "}}");
    
    jreplace(tok, "r <id> + r <id>", "r { $2(); $5(); }");
    
    // runnable and r - now also with automatic toString if enabled
    for (String keyword : ll("runnable", "r")) {
      while ((i = jfind(tok, keyword + " {")) >= 0) {
        int idx = findCodeTokens(tok, i, false, "{");
        int j = findEndOfBracketPart(tok, idx);
        List<String> contents = subList(tok, idx+1, j-1);
        replaceTokens(tok, i, j+1, "new Runnable {"
          + "  public void run() ctex { " + tok_addSemicolon(contents) + "\n}"
          + (autoQuine ? tok_autoQuineFunc(contents) : "")
          + "}");
        reTok(tok, i, j+1);
      }
      
      while ((i = jfind(tok, keyword + " <quoted> {")) >= 0) {
        int idx = findCodeTokens(tok, i, false, "{");
        int j = findEndOfBracketPart(tok, idx);
        List<String> contents = subList(tok, idx+1, j-1);
        replaceTokens(tok, i, j+1, "new Runnable {"
          + "  public void run() ctex { " + tok_addSemicolon(contents) + "\n}"
          + "  toString { ret " + tok.get(i+2) + "; }"
          + (autoQuine ? tok_autoQuineFunc(contents, "_shortenedSourceCode") : "")
          + "}");
        reTok(tok, i, j+1);
      }
    }
    
    replaceKeywordBlock(tok,
      "expectException",
      "{ bool __ok = false; try {",
      "} catch { __ok = true; } assertTrue(\"expected exception\", __ok); }");
      
    while ((i = tok.indexOf("tex")) >= 0) {
      tok.set(i, "throws Exception");
      tok = jtok(tok);
    }
    
    // shorter & smarter whiles
    
    jreplace(tok, "while true", "while (true)");
    jreplace(tok, "while licensed", "while (licensed())");
    jreplace(tok, "repeat {", "while (licensed()) {");
    tok_repeatWithSleep(tok);
    
    // null; => return null; etc.
  
    Object cond = new TokCondition() { public boolean get(final List<String> tok, final int i) {
      return tok_tokenBeforeLonelyReturnValue(tok, i-1);
    }};
    jreplace(tok, "null;", "return null;", cond);
    jreplace(tok, "false;", "return false;", cond);
    jreplace(tok, "true;", "return true;", cond);
    jreplace(tok, "this;", "return this;", cond);
    
    // ok <cmd> => ret "OK" with <cmd>
    jreplace(tok, "ok <id>", "return \"OK\" with $2");
    replaceKeywordBlock(tok, "ok", "{", " return \"OK\"; }",
      new F2<List<String>, Integer, Boolean>() { public Boolean get(List<String> _tok, Integer nIdx) { try {  return !eqGetOneOf(_tok, nIdx-1, "void", "svoid");  } catch (Exception __e) { throw rethrow(__e); } }
  public String toString() { return "!eqGetOneOf(_tok, nIdx-1, \"void\", \"svoid\")"; }});
  
    // "myFunction;" instead of "myFunction();" - quite rough
    // (isolated identifier as function call)
    cond = new TokCondition() {
      public boolean get(List<String> tok, int i) {
        String word = tok.get(i+3);
        //print("single word: " + word);
        return !eqOneOf(word, "break", "continue", "return", "else", "endifdef", "endif");
      }
    };
    for (String pre : litlist("}", ";"))
      jreplace(tok, pre + " <id>;", "$1 $2();", cond);
  
    // shorter match syntax for answer methods
    
    tok_expandIfQuoted(tok);
    
    jreplace(tok, "if <id> eq <quoted>", "if (eq($2, $4))");
  
    tok_dropExtraCommas(tok);
    
    // additional translations (if necessary)
    
    jreplace(tok, "pcall ping {", "pcall { ping();");
    
    replaceKeywordBlock(tok, ") pcall",
      ") { pcall {",
      "}}");
    
    //jreplace(tok, "void <id> pcall {", "$1 $2() pcall {");
    
    replaceKeywordBlock(tok,
      "pcall",
      "try {",
      "} catch (Throwable __e) { _handleException(__e); }");
  
    replaceKeywordBlock(tok,
      "pcall-print",
      "try {",
      "} catch (Throwable __e) { printStackTrace(__e); }");
  
    replaceKeywordBlock(tok,
      "pcall-short",
      "try {",
      "} catch (Throwable __e) { print(exceptionToStringShort(__e)); }");
  
    replaceKeywordBlock(tok,
      "pcall-silent",
      "try {",
      "} catch (Throwable __e) { silentException(__e); }");
  
    replaceKeywordBlock(tok,
      "pcall-messagebox",
      "try {",
      "} catch __e { messageBox(__e); }");
  
    replaceKeywordBlock(tok,
      "pcall-infobox",
      "try {",
      "} catch __e { infoBox(__e); }");
  
    tok = dialogHandler(tok);
    
    replaceKeywordBlock(tok, "exceptionToUser",
      "try {",
      "} catch (Throwable __e) { ret exceptionToUser(__e); }"); 
  
    if (hasCodeTokens(tok, "twice", "{"))
      replaceKeywordBlock(tok, "twice",
        "for (int __twice = 0; __twice < 2; __twice++) {",
        "}"); 
  
    while ((i = findCodeTokens(tok, "bench", "*", "{")) >= 0) {
      int j = findEndOfBracketPart(tok, i+4)-1;
      String time = makeVar("time");
      String v = makeVar("bench");
      String n = tok.get(i+2);
      tok.set(i, "{ long " + time + " = sysNow(); for (int " + v + " = 0; " + v + " < " + n + "; " + v + "++)");
      tok.set(i+2, "");
      tok.set(j, "} printBenchResult(sysNow()-" + time + ", " + n + "); }");
      reTok(tok, i, j+1);
    }
  
    replaceKeywordBlockDyn(tok,
      "time",
      new Object() { String[] get() {
        String var = makeVar("startTime");
        return new String[] {
          "{ long " + var + " = sysNow(); try { ",
          "} finally { " + var + " = sysNow()-" + var + "; saveTiming(" + var + "); } }"};
      }});
    
    // version without { }
    replaceKeywordBlockDyn(tok,
      "time2",
      new Object() { String[] get() {
        String var = makeVar("startTime");
        return new String[] {
          "long " + var + " = sysNow(); ",
          " " + var + " = sysNow()-" + var + "; saveTiming(" + var + "); "};
      }});
    
    // time "bla" {
    // time msg {
    replaceKeywordPlusQuotedOrIDBlock(tok,
      "time",
      new Object() { String[] get(List<String> tok, int i) {
        String var = makeVar("startTime");
        return new String[] {
          "long " + var + " = sysNow(); ",
          " done2_always(" + tok.get(i+2) + ", " + var + "); "};
      }});
    
    if (hasCodeTokens(tok, "assertFail", "{")) {
      String var = makeVar("oops");
      
      replaceKeywordBlock(tok,
        "assertFail",
        "boolean " + var + " = false; try {",
        "\n" + var + " = true; } catch (Exception e) { /* ok */ } assertFalse(" + var + ");"); 
    }
    
    replaceKeywordBlock(tok,
      "yo",
      "try {",
      "} catch (Exception " + makeVar("e") + ") { ret false; }",
      new TokCondition() { public boolean get(final List<String> tok, final int i) {
        return neqOneOf(_get(tok, i-1), "svoid", "void");
      }});
  
    replaceKeywordBlock(tok,
      "awtIfNecessary",
      "swingNowOrLater(r " + "{",
      "});");
      
    ctex(tok);
      
    replaceKeywordBlock(tok,
      "actionListener",
      "new java.awt.event.ActionListener() { " +
        "public void actionPerformed(java.awt.event.ActionEvent _evt) { pcall-messagebox {",
      "}}}");
      
    for (String keyword : ll("autocloseable", "autoCloseable"))
      /*replaceKeywordBlock(tok,
        keyword,
        "new AutoCloseable() { public void close() throws Exception {",
        "}}");*/
        replaceKeywordBlock_dyn2_legacy(tok, keyword, new Object() {
          String[] get(List<String> tok, int iOpening, int iClosing) {
            List<String> contents = subList(tok, iOpening+1, iClosing);
            return new String[] {
              "new AutoCloseable() { toString { ret " + quote(shorten(defaultMaxQuineLength(), trimJoin(contents))) + "; } public void close() throws Exception {",
              "}}"
            };
          }
        });
      
    // try answer (string, test with nempty)
    while ((i = findCodeTokens(tok, "try", "answer")) >= 0) {
      int j = findEndOfStatement(tok, i);
      String v = makeVar();
      boolean needCurly = !eqGet(tok, i-2, "{");
      tok.set(i, (needCurly ? "{" : "") + " S " + v);
      tok.set(i+2, "=");
      tok.set(j-1, "; if (!empty(" + v + ")) ret " + v + "; " + (needCurly ? "}" : ""));
      reTok(tok, i, j);
    }
  
    // try bool[ean] (try answer with Bool type)
    while ((i = findCodeTokens(tok, "try", "boolean")) >= 0) {
      int j = findEndOfStatement(tok, i);
      String v = makeVar();
      tok.set(i, "{ Bool " + v);
      tok.set(i+2, "=");
      tok.set(j-1, "; if (" + v + " != null) ret " + v + "; }");
      reTok(tok, i, j);
    }
    
    // <statement>, print "..."; => { <statement>; print("..."); }
    while ((i = jfind(tok, ", print <quoted>;")) >= 0) {
      int j = tok_findBeginningOfStatement(tok, i);
      replaceTokens_reTok(tok, i, i+4*2-1, "; print(" + tok.get(i+4) + "); }");
      tokPrepend_reTok(tok, j, "{ ");
    }
    
    // return if null <expression> => if (<expression> == null) return;
    while ((i = jfind(tok, "return if null")) >= 0) {
      int j = findEndOfStatement(tok, i);
      clearTokens(tok, i, i+2);
      tok.set(i+4, "((");
      tok.set(j-1, ") == null) ret;");
      reTok(tok, i, j);
    }
    
    // return optional (return if not null)
    while ((i = jfind_check("optional", tok, "return optional <id> =")) >= 0) {
      int j = findEndOfStatement(tok, i);
      String v = tok.get(i+4);
      clearTokens(tok, i+2, i+4);
      tok.set(i, "{");
      tok.set(j-1, "; if (" + v + " != null) ret " + v + "; }");
      reTok(tok, i, j);
    }
    
    // try object (return if not null)
    
    jreplace_dyn(tok, "try object (<id>)", (_tok, cIdx) -> {
      String type = _tok.get(cIdx+6);
      return "try object " + type + " " + makeVar() + " = (" + type + ")";
    });
    
    while ((i = jfind_check("object", tok, "try object")) >= 0) {
      int j = findEndOfStatement(tok, i);
      clearTokens(tok, i, i+3);
      boolean isDecl = isIdentifier(get(tok, i+4)) && isIdentifier(get(tok, i+6)) && eqGet(tok, i+8, "=");
      if (isDecl) {
        String v = get(tok, i+6);
        tok.set(i, "{");
        tok.set(j-1, "; if (" + v + " != null) ret " + v + "; }");
      } else {
        String v = makeVar();
        tok.set(i, "{ O " + v + "=");
        tok.set(j-1, "; if (" + v + " != null) ret " + v + "; }");
      }
      reTok(tok, i, j);
    }
    
    // try Int i = ...; (return if not null, shorter version of "try object")
    /*while ((i = jfind(tok, "try <id> <id> =")) >= 0) {
      int j = findEndOfStatement(tok, i);
      S type = tok.get(i+2), v = tok.get(i+4);
      tok.set(i, "{");
      tok.set(j-1, "; if (" + v + " != null) ret " + v + "; }");
      reTok(tok, i, j);
    }*/
    while ((i = jfind(tok, "try <id>")) >= 0) {
      int iType = i+2, iVar = tok_findEndOfType(tok, iType);
      String v = tok.get(iVar);
      assertEquals("try object", "=", get(tok, iVar+2));
      int j = findEndOfStatement(tok, iVar);
      tok.set(i, "{");
      tok.set(j-1, "; if (" + v + " != null) ret " + v + "; }");
      reTok(tok, i, j);
    }
    
    // debug print ...; => if (debug) print(...);
    while ((i = jfind(tok, "debug print")) >= 0) {
      int j = findEndOfStatement(tok, i);
      replaceTokens(tok, i, i+4, "if (debug) print(");
      tok.set(j-1, ");");
      reTok(tok, i, j);
    }
    
    functionReferences(tok);
    tok_expandLPair(tok);
    tok_expandPairL(tok);
    tok_expandLT3(tok);
    tok_quicknew2(tok);
    
    // before temp blocks
    tok_varNamedLikeFunction(tok);
    
    tempBlocks(tok); // after quicknew2 for stuff like "temp new X x;"

    // X x = nu(+...)  =>  X x = nu X(+...)
    jreplace(tok, "<id> <id> = nu(+", "$1 $2 = nu $1(+");
    // X x = nu(a := ...)  =>  X x = nu X(a := ...)
    jreplace(tok, "<id> <id> = nu(<id> :=", "$1 $2 = nu $1($6 :=");
    
    tok_expandVarCopies(tok); // AFTER the lines just above
    tok_replaceColonEqualsSyntax(tok); // ditto

    tok_unpair(tok);
    tok_cachedFunctions(tok);
    tok_simplyCachedFunctions(tok);
    tok_timedCachedFunctions(tok);
    
    tok_optPar(tok);
    
    throwFailEtc(tok);
    tok_typeAA(tok, pairClasses);
    tok_typeAAA(tok, tripleClasses);
    tok_typeAO(tok, litset("WithTrail"));

    // before star constructors so we can define star constructors in a macro
    tok_localMacro(tok);

    // do this after expanding sclass etc.
    tok = tok_expandStarConstructors(tok);
    
    tok_kiloConstants(tok);
    //tok_colonMessages(tok);
    
    while ((i = jfind(tok, "shit:")) >= 0) {
      int j = tok_findEndOfStatement(tok, i);
      tok.set(i+2, "(");
      tok.set(j-1, ");");
      reTok(tok, i, j);
    }
      
    jreplace(tok, "shit(", "ret with print(");
    
    tok_virtualTypes(tok);
    tok_autoLongConstants(tok);
    
    // common misordering of type arguments
    jreplace(tok, "boolean <A>", "<A> boolean");
    
    tok_unimplementedMethods(tok);
    tok_switchableFields(tok);
    tok_autoDisposeFields(tok);
    tok_shortVisualize(tok);
    tok_whileGreaterThan(tok);
    tok_ifThenEnd(tok);
    
    tok_autoInitVars(tok);
    
    tok_fixBadTypeParameterOrder(tok);
    
    // shortened method declarations BEFORE standardFunctions
    tok_svoidEtc(tok);
    jreplace(tok, "void <id> {", "$1 $2() {");
    jreplace(tok, "void <id> thread {", "$1 $2() thread {");
    jreplace(tok, "String <id> {", "$1 $2() {");
    jreplace(tok, "Object <id> {", "$1 $2() {");
    jreplace(tok, "List <id> {", "$1 $2() {");

    namedThreads(tok);
    threads(tok);
    
    //tok_maxEquals(tok);
    
    tok_questionDot(tok);
    
    jreplace(tok, "if (<id>?!)", "if ($3 != null && $3!)");
    
    tok_embeddedFunctions(tok);
    
    // quicknew for arrays
    
    jreplace(tok, "<id>[] <id> = new[", "$1[] $4 = new $1[");
    jreplace(tok, "<id>[] <id> = new {", "$1[] $4 = {");
    jreplace(tok, "<id><<id>>[] <id> = new[", "$1 $2 $3 $4[] $7 = new $1[");
    
    jreplace(tok, "<id> ifNull =", "if ($1 == null) $1 =");
    
    jreplace(tok, "class <id> extends <id><.<id>>", "class $2 extends $4<$2.$7>");
    
    tok_once(tok); // must come before next line so you can combine them
    tok_ifRecordMatch(tok);
    
    jreplace(tok, "<id> ||=", "$1 = $1 ||");
    
    // magicValue followed by a numerical constant
    jreplace(tok, "magicValue", "", (_tok, _i) -> {
      String t = _get(_tok, _i+3);
      return eqOneOf(t, ".", "-") || isInteger(t);
    });
    
    lambdaReferences(tok);
    
    tok_returnSelf(tok);
    
    // Lua-like print statement
    jreplace(tok, "print <quoted>", "print($2);");
    
    tok_tildeCalls(tok);
    
    tok_svoidEtc(tok);
    tok_swappableFunctions(tok);
    
    tok_optParLambda(tok);
    
    tok_runnableClasses(tok);
    
    tok_swapStatement(tok);
    
    tok_akaFunctionNames(tok);
    
    tok_defaultArguments(tok);
    
    tok_persistableClasses(tok);
    
    tok_transientClasses(tok);
    
    tok_shortMethodReferences(tok);
    
    jreplace(tok, "== null ?:", "== null ? null :");
    jreplace(tok, "?:", "== null ? null :");
    
    tok_orCase(tok);
    
    tok_beforeMethods(tok);
    tok_afterMethods(tok);
    
    tok_returnAsFunctionName(tok);
    
    tok_transpileGetSet(tok);
    
    tok_pcallPrefix(tok);
    
    tok_numberFunctionNames(tok);
    
    tok_castToStatements(tok);
    
    tok_optionalFields(tok);
    
    tok_getFromMap(tok);
    
    jreplace(tok, "for <id> : <id> {", "for (var $2 : $4) {");
    
    tok_shortLambdas(tok);
    
    // end of local stuff
    
    tok_processMetaBlocks(tok, metaCodeAllowed());
    if (metaCodeAllowed()) runMetaTransformers(tok);

    same = eq(tok, before);
    /*if (!same)
      print("local not same " + safety + " (" + l(tok) + " tokens)");*/
    if (safety++ >= 10) {
      printSources(tok);
      throw fail("safety 10 error!");
    }
  } while (!same);
  
  return tok;
}

static List<String> reTok_include(List<String> tok, int i, int j) {
  return reTok_modify(tok, i, j, "localStuff1");
}

static List<String> includeInMainLoaded_reTok(List<String> tok, int i, int j) {
  return reTok_include(tok, i, j);
}

static List<String> stdstuff(List<String> tok) {
  //if (++level >= 10) fail("woot? 10");
  
  print("stdstuff!");
  int i;
  
  List<String> ts = new ArrayList();
  tok_findTranslators(tok, ts);
  if (nempty(ts))
    print("DROPPING TRANSLATORS: " + structure(ts));

  print("quickmain"); tok = quickmain(tok);
  print("includes"); tok = tok_processIncludes(tok);
  print("conceptsDot"); if (processConceptsDot(tok))
  tok = tok_processIncludes(tok);
  
  List<String> dontImports = tok_processDontImports(tok, definitions);
  for (String s : dontImports)
    doNotIncludeFunction.remove(afterLastDot(s));
  
  //print('starConstructors); tok = tok_expandStarConstructors(tok);
    
  // drop Java 8 annotations since we're compiling for Java 7
  jreplace(tok, "@Nullable", "");

  // STANDARD CLASSES & INTERFACES
  
  print("standard classes");
  haveClasses = addStandardClasses_v2(tok);
  
  tok_quickInstanceOf(tok, haveClasses);

  // concept-related stuff
  
  // auto-import concepts
  /*bool _a = tok_hasClassRef2(tok, "Concept") || tok_hasClassRef2(tok, "Concepts"), _b = !haveClasses.contains("Concept");
  //print("auto-import: " + _a + ", " + _b);
  if (_a && _b) {
    print("Auto-including concepts.");
    if (shouldNotIncludeClass.contains("Concepts")) {
      print(join(tok));
      fail("Unwanted concepts import");
    }
    printStruct(haveClasses);
    tok = includeInMainLoaded(tok, "concepts.");
    reTok(tok, l(tok)-1, l(tok));
    //processConceptsDot(tok);
  }*/
  
  return tok;
} // end of stdStuff!

static List<String> multilineStrings(List<String> tok) {
  for (int i = 1; i < tok.size(); i += 2) {
    String t = tok.get(i);
    if (isQuoted(t))
      if (t.startsWith("[") || t.contains("\r") || t.contains("\n"))
        tok.set(i, quote(unquote(t)));
  }
  return tok;
}

static List<String> quickmain(List<String> tok) {
  if (quickmainDone1 && quickmainDone2) return tok;

  int i = findCodeTokens(tok, "main", "{");
  if (i < 0) i = findCodeTokens(tok, "m", "{");
  if (i >= 0 && !(i-2 > 0 && tok.get(i-2).equals("class"))) {
    tokSet_reTok(tok, i, (definitions.contains("mainClassPublic") ? "public " : "") + "class main");
    quickmainDone1 = true;
  }
    
  i = findCodeTokens(tok, "psvm", "{");
  if (i < 0) i = findCodeTokens(tok, "p", "{");
  if (i >= 0) {
    int idx = i+2;
    int j = findEndOfBracketPart(tok, idx)-1;
    List<String> contents = subList(tok, idx+1, j);
    //print("contents: " + sfu(contents));
    tok.set(i, "public static void main(final String[] args) throws Exception");
    replaceTokens(tok, idx+1, j, tok_addSemicolon(contents));
    reTok(tok, i, j);
    quickmainDone2 = true;
  }
    
  return tok;
}

static String makeVar(String name) {
  AtomicInteger counter = varCountByThread.get();
  if (counter == null)
    varCountByThread.set(counter = new AtomicInteger());
  return "_" + name + "_" + getAndInc(counter);
}

static String makeVar() { return makeVar(""); }

static List<String> rtq(List<String> tok, String id) {
  return runTranslatorQuick(tok, id);
}

static List<String> expandShortTypes(List<String> tok) {
  // replace <int> with <Integer>
  for (int i = 1; i+4 < tok.size(); i += 2)
    if (tok.get(i).equals("<")
      && litlist(">", ",").contains(tok.get(i+4))) {
      String type = tok.get(i+2);
      if (type.equals("int")) type = "Integer";
      else if (type.equals("long")) type = "Long";
      tok.set(i+2, type);
    }
    
  jreplace(tok, "O", "Object");
  jreplace(tok, "S", "String");
  jreplace(tok, "L", "List");
  jreplace(tok, "Cl", "Collection");
  
  // bool -> boolean if it's not a function name
  jreplace(tok, "bool", "boolean", new TokCondition() { public boolean get(final List<String> tok, final int i) {
    return neqGetOneOf(tok, i+3, "(", null);
  }});
  
  jreplace(tok, "AtomicBool", "AtomicBoolean");
  jreplace(tok, "AtomicInt", "AtomicInteger");

  jreplace(tok, "LL< <id> >", "L<L<$3>>");
  jreplace(tok, "LL< <id><<id>> >", "L<L<$3<$5>>>");
  jreplace(tok, "LL<?>", "L<L<?>>");
  jreplace(tok, "LL", "L<L>");
  jreplace(tok, "LPt", "L<Pt>");
  
  jreplace(tok, "Clusters< <id> >", "Map<$3, Collection<$3>>");
  
  return tok;
}

static List<String> autoImports(List<String> tok) {
  HashSet<String> imports = new HashSet(tok_findImports(tok));
  StringBuilder buf = new StringBuilder();
  for (String c : standardImports())
    if (!(imports.contains(c)))
      buf.append("import " + c + ";\n");
  if (buf.length() == 0) return tok;
  tok.set(0, buf+tok.get(0));
  return reTok(tok, 0, 1);
}

static List<String> tok_processIncludes(List<String> tok) {
  int safety = 0;
  while (hasCodeTokens(tok, "!", "include") && ++safety < 100)
    tok = tok_processIncludesSingle(tok);
  
  //tok_autoCloseBrackets(tok);
  return tok;
}

static void tok_processEarlyIncludes(List<String> tok) {
  int i;
  while ((i = jfind_check("include", tok, "!include early #<int>")) >= 0) {
    String id = tok.get(i+8);
    included.add(parseLong(id));
    replaceTokens_reTok(tok, i, i+10, "\n" + cacheGet(id) + "\n");
  }
}

static List<String> tok_processIncludesSingle(List<String> tok) {
  int i;
  while ((i = jfind_check("include", tok, "!include #<int>")) >= 0) {
    String id = tok.get(i+6);
    included.add(parseLong(id));
    replaceTokens(tok, i, i+8, "\n" + cacheGet(id) + "\n");
    reTok_include(tok, i, i+8);
  }
  while ((i = jfind_check("include", tok, "!include once #<int>")) >= 0) {
    String id = tok.get(i+8);
    boolean isNew = included.add(parseLong(id));
    replaceTokens(tok, i, i+10, 
      isNew ? "\n" + cacheGet(id) + "\n" : "");
    reTok_include(tok, i, i+10);
  }
  return tok;
}

// ctex and various other error-related keyword blocks
static void ctex(List<String> tok) {
  replaceKeywordBlock(tok, "ctex",
    "{ try {",
    "} catch (Exception __e) { throw rethrow(__e); } }");
  for (String keyword : ll("null on exception", "null on error"))
    replaceKeywordBlock(tok, keyword,
      "{ try {",
      "} catch (Throwable __e) { return null; } }");
  replaceKeywordBlock(tok, "false on exception",
    "{ try {",
    "} catch (Throwable __e) { return false; } }");
  replaceKeywordBlock(tok, "try-OrError",
    "{ try {",
    "} catch (Throwable __e) { return OrError.error(__e); } }");
}
  
static List<String> dialogHandler(List<String> tok) {
  return replaceKeywordBlock(tok,
    "dialogHandler",
    "new DialogHandler() {\n" +
      "public void run(final DialogIO io) {",
    "}}");
}

static List<String> extendClasses(List<String> tok) {
  int i;
  while ((i = jfind(tok, "extend <id> {")) >= 0) {
    String className = tok.get(i+2);
    int idx = findCodeTokens(tok, i, false, "{");
    int j = findEndOfBracketPart(tok, idx+2);
    String content = joinSubList(tok, idx+1, j-1);
    List<String> c = findInnerClassOfMain(tok, className);
    print("Extending class " + className);
    clearTokens(subList(tok, i, j+1));
    if (c == null) {
      print("Warning: Can't extend class " + className + ", not found");
      continue;
    }
    int startOfClass = indexOfSubList(tok, c);
    int endOfClass = startOfClass + l(c)-1;
    //print("Extending class " + className + " ==> " + join(subList(tok, startOfClass, endOfClass)));
    while (neq(tok.get(endOfClass), "}")) --endOfClass;
    //print("Extending class " + className + " ==> " + join(subList(tok, startOfClass, endOfClass)));
    tok.set(endOfClass, content + "\n" + tok.get(endOfClass));
    
    doubleReTok(tok, i, j+1, endOfClass, endOfClass+1);
  }
  return tok;
}

// for ping / while ping
static void forPing(List<String> tok) {
  int i;
  for (String keyword : ll("for", "fOr", "while"))
    while ((i = jfind(tok, keyword + " ping (")) >= 0) {
      int bracketEnd = findEndOfBracketPart(tok, i+4)-1;
      int iStatement = bracketEnd+2;
      int iEnd = findEndOfStatement(tok, iStatement);
      tok.set(i+2, "");
      
      // turn into block
      if (!eq(get(tok, iStatement), "{")) {
        tok.set(iStatement, "{ " + tok.get(iStatement));
        tok.set(iEnd-1, tok.get(iEnd-1) + " }");
      }
        
      // add ping
      tok.set(iStatement, "{ ping(); " + dropPrefixTrim("{", tok.get(iStatement)));
      reTok(tok, i+2, iEnd);
    }
}

// lib 123 => !123
static void libs(List<String> tok) {
  int i;
  while ((i = jfind(tok, "lib <int>")) >= 0) {
    String id = tok.get(i+2);
    print("lib " + id);
    if (!libs.contains(id)) {
      libs.add(id);
      clearAllTokens(tok, i, i+3);
      /*tok.set(i, "!");
      tok.set(i+1, "");*/
    } else {
      print("...ignoring (duplicate)");
      clearAllTokens(tok, i, i+3);
      reTok(tok, i, i+3);
    }
  }
  print("libs found: " + libs);
}

// sourceCodeLine() => 1234
static void sourceCodeLine(List<String> tok) {
  int i ;
  while ((i = jfind(tok, "sourceCodeLine()")) >= 0) {
    replaceTokens(tok, i, i+5, str(countChar(joinSubList(tok, 0, i), '\n')+1));
    reTok(tok, i, i+5);
  }
}

// done before any other processing
static void earlyStuff(List<String> tok) {
  int i;
  
  tok_processEarlyIncludes(tok);
      
  tok_scopes(tok, "autoCloseScopes" , asInclude);
  tok_autosemi(tok);
  tok_autoCloseBrackets(tok);
  jreplace(tok, "°", "()");
  
  // Note: this makes the word "quine" a special operator
  // (unusable as a function name)
  
  while ((i = jfind(tok, "quine(")) >= 0) {
    int idx = findCodeTokens(tok, i, false, "(");
    int j = findEndOfBracketPart(tok, idx+2);
    tok.set(i, "new Quine");
    tok.set(idx, "(" + quote(joinSubList(tok, idx+1, j-1)) + ", ");
    reTok(tok, i, idx+1);
  }
  
  jreplace_check("after", tok, "void <id> after super {", "void $2 { super.$2();");
  
  replaceKeywordBlock(tok, "r-enter", "r { enter {", "}}");
    
  // do this before func & voidfunc because otherwise they swallow it
  jreplace(tok, "enter {", "{ temp enter();");
  
  tok_mutatorMethods(tok);
  
  // func keyword for lambdas - now automatically quines toString() if enabled
  
  replaceKeywordBlock(tok,
    "null",
    "{",
    "null; }");
    
  // do func & voidfunc early to preserve original code as toString
  
  while ((i = jfind(tok, "func(")) >= 0) {
    int argsFrom = i+4, argsTo = findCodeTokens(tok, i, false, ")");
    int idx = findCodeTokens(tok, argsTo, false, "{");
    int j = findEndOfBracketPart(tok, idx);
    List<String> contents = subList(tok, idx+1, j-1);
    
    String returnType = "O";
    if (eq(tok.get(argsTo+2), "-") && eq(tok.get(argsTo+4), ">"))
      returnType = tok_toNonPrimitiveTypes(joinSubList(tok, argsTo+6, idx-1));
      
    String toString = autoQuine ? "  public S toString() { ret " + quote(shorten(defaultMaxQuineLength(), trimJoin(contents))) + "; }" : "";
    
    List<String> args = cloneSubList(tok, argsFrom-1, argsTo);
    tok_shortFinals(args);
    tok_toNonPrimitiveTypes(args);
    
    List<String> types = tok_typesOfParams(args);
    Object type = "O";
    if (l(types) <= 3)
      type = "F" + l(types) + "<" + joinWithComma(types) + ", " + returnType + ">";
    String body = tok_addReturn(contents);
    
    replaceTokens_reTok(tok, i, j,
      "new " + type + "() { public "
        + returnType + " get(" + trimJoin(args) + ") ctex { "
          + body
        + " }\n" + toString + "}");
  }
  
  while ((i = jfind(tok, "voidfunc(")) >= 0) {
    int argsFrom = i+4, argsTo = findCodeTokens(tok, i, false, ")");
    int idx = findCodeTokens(tok, argsTo, false, "{");
    int j = findEndOfBracketPart(tok, idx);
    List<String> contents = subList(tok, idx+1, j-1);
    
    if (jcontains(subList(tok, argsTo+1, idx), "->"))
      throw fail("voidfunc with return type: " + joinSubList(tok, i, j+1));
    
    List<String> args = cloneSubList(tok, argsFrom-1, argsTo);
    tok_shortFinals(args);
    tok_toNonPrimitiveTypes(args);
    
    List<String> types = tok_typesOfParams(args);
    Object type = "O";
    if (l(types) <= 4)
      type = "VF" + l(types) + "<" + joinWithComma(types) + ">";

    replaceTokens(tok, i, j, "new " + type + "() { public void get(" + trimJoin(args) + ") ctex { " + tok_addSemicolon(contents) + " }\n" +
    (autoQuine ? "  public S toString() { ret " + quote(shorten(defaultMaxQuineLength(), trim(join(contents)))) + "; }" : "") + "}");
    reTok(tok, i, j);
  }
  
  jreplace(tok, "func {", "func -> O {");
  jreplace(tok, "f {", "f -> O {");
  
  // swing -> S { ... } => swing(func -> S { ... })
  while ((i = jfind(tok, "swing ->")) >= 0) {
    int bracket = findCodeTokens(tok, i, false, "{");
    int j = findEndOfBracketPart(tok, bracket);
    tok.set(i, "swing(func");
    tok.set(j-1, "})");
    reTok(tok, i, j);
  }
  
  tok_qFunctions(tok);
  
  jreplace(tok, "func fullToString {", "func -> O fullToString {");
  
  for (String keyword : ll(/*"f",*/ "func")) {
    while ((i = jfind(tok, keyword + " ->", new TokCondition() { public boolean get(final List<String> tok, final int i) {
      return isIdentifier(_get(tok, i+7)); // avoid lambda declaration like: f -> { ... }
    }})) >= 0) {
      // I think there is a bug here for something like func -> x { new x { } }
      int idx = findCodeTokens(tok, i, false, "{");
      int j = findEndOfBracketPart(tok, idx);
      String returnType = tok_toNonPrimitiveTypes(joinSubList(tok, i+6, idx-1));
      int quineLength = defaultMaxQuineLength();
      if (eq(lastJavaToken(returnType), "fullToString")) {
        quineLength = Integer.MAX_VALUE;
        returnType = dropLastJavaTokenAndSpacing(returnType);
      }
      List<String> contents = subList(tok, idx+1, j-1);
      replaceTokens(tok, i, j, "new F0<" + returnType + ">() { public " + returnType + " get() ctex { " + tok_addReturn(contents) + " }\n" +
        (autoQuine ? "  public S toString() { ret " + quote(shorten(quineLength, trimJoin(contents))) + "; }" : "") + "}");
      reTok(tok, i, j);
    }
  }
    
  while ((i = jfind(tok, "time repeat * {")) >= 0) {
    int j = findEndOfBlock(tok, i+6)-1;
    tok.set(i, "time {");
    tok.set(j, "}}");
    reTok(tok, i, j+1);
  }

  // do this before "m {" stuff because "repeat m" [doesn't seem to work yet though)
  while ((i = findCodeTokens(tok, "repeat", "*", "{")) >= 0) {
    String v = makeVar("repeat");
    tok.set(i, "for (int " + v + " = 0; " + v + " < " + tok.get(i+2) + "; " + v + "++)");
    tok.set(i+2, "");
    reTok(tok, i, i+3);
  }
}

static void throwFailEtc(List<String> tok) {
  boolean anyChange = false;
  for (int i = 1; i+4 < l(tok); i += 2)
    if (eqOneOf(get(tok, i+2), "fail", "todo")
      && eq(get(tok, i+4), "(")
      && !eqOneOf(get(tok, i), "throw", "RuntimeException", "return", "f", "function", ".", "void", "main", "Throwable")) {
      tok.set(i+2, "throw " + tok.get(i+2));
      anyChange = true;
    }
  if (anyChange)
    reTok(tok);
}

static void namedThreads(List<String> tok) {
  for (int i = 0; i < 100; i++) {
    int idx = findCodeTokens(tok, "thread", "<quoted>", "{");
    if (idx < 0) idx = findCodeTokens(tok, "thread", "<id>", "{");
    if (idx < 0)
      break;
    int j = findEndOfBracketPart(tok, idx+4);
    String tName = tok.get(idx+2);
    
    String pre = "startThread(" + tName + ", r { ";
    String post = "})";
    if (!tok_tokenLeftOfExpression(tok, idx-2)) post += ";";

    tok.set(idx, pre);
    tok.set(idx+2, "");
    tok.set(idx+4, "");
    tok.set(j-1, post);
    //print(join(subList(tok, idx, j)));
    reTok(tok, idx, j);
  }
}

static void rNamedThread(List<String> tok) {
  for (int i = 0; i < 100; i++) {
    int idx = findCodeTokens(tok, "r", "-", "thread", "<quoted>", "{");
    if (idx < 0) idx = findCodeTokens(tok, "r", "-", "thread", "<id>", "{");
    if (idx < 0)
      break;
    int j = findEndOfBracketPart(tok, idx+8);
    String tName = tok.get(idx+6);
    
    String pre = "r { thread " + tName + " {";
    String post = "}}";

    replaceTokens(tok, idx, idx+9, pre);
    tok.set(j-1, post);
    reTok(tok, idx, j);
  }
}

static void threads(List<String> tok) {
  replaceKeywordBlock(tok, "daemon", "startDaemonThread(r {", "});");
  //replaceKeywordBlock(tok, "thread", "startThread(r {", "});");
  
  // now enclose in { } to allow using like "void bla() thread { ... }" - if it's not used as an expression
  
  replaceKeywordBlock_dyn2_legacy(tok, "thread", new Object() { // don't use func here, it can't be transpiled
    String[] get (List<String> tok, int iOpening, int iClosing) {
      // is the thread clause used as an expression?
      boolean isExpression = tok_tokenLeftOfExpression(tok, iOpening-4);
      return new String[] {
        (isExpression ? "" : "{ ") + "startThread(r {",
        "})" +
        (isExpression ? "" : "; }")
      };
    }
  });
}

static Map<String, String> sf; // standard standard functions (haha)
static Boolean _761ok;

static List<String> standardFunctions(List<String> tok) {
  if (sf == null) {
    String _761 = cacheGet("#761");
    if (_761ok == null)
      _761ok = isBracketHygienic(_761);
    assertTrue("Whoa! #761 truncated?", _761ok);
    List<String> standardFunctions = concatLists(
      (List) loadVariableDefinition(_761, "standardFunctions"),
      (List) loadVariableDefinition(cacheGet("#1006654"), "standardFunctions"));

    sf = new HashMap();
    for (String x : standardFunctions) {
      String[] f = x.split("/");
      sf.put(f[1], f[0]);
    }
  }
  
  Map<String, String> sfMap = combinedMap(extraStandardFunctions, sf);
    
  for (int i = 0; ; i++) {
    print("Looking for required functions");
    Set<String> defd = new HashSet(findFunctions(tok));
    
    int j;
    while ((j = jfind(tok, "should not include function *.")) >= 0) {
      String fname = tok.get(j+8);
      shouldNotIncludeFunction.add(fname);
      clearAllTokens(subList(tok, j, j+12));
    }

    while ((j = jfind(tok, "do not include function *.")) >= 0) {
      String fname = tok.get(j+8);
      doNotIncludeFunction.add(fname);
      clearAllTokens(subList(tok, j, j+12));
    }
    
    while ((j = jfind(tok, "need latest <id>.")) >= 0) {
      String t = tok.get(j+4);
      needLatest.add(t);
      doNotIncludeClass.remove(t);
      clearAllTokens(subList(tok, j, j+8));
    }
    
    grabImportedStaticFunctions(tok);
    doNotIncludeFunction.removeAll(needLatest);
    
    // changes tok
    Set<String> invocations = findFunctionInvocations(tok, sfMap, hardFunctionReferences, defd, true, mainClassName());
    /*if (invocations.contains("str"))
      print("==STR==" + join(tok) + "==STR==");*/
    if (!cic(definitions, "leanMode"))
      invocations.addAll(functionsToAlwaysInclude);

    //print("Functions invoked: " + structure(invocations));
    List<String> needed = diff(invocations, defd);
    for (String f : doNotIncludeFunction) needed.remove(f);
    if (needed.isEmpty())
      break;
    print("Adding functions: " + join(" " , needed));
    
    HashSet neededSet = new HashSet(needed);
    Collection<String> bad = setIntersection(neededSet, shouldNotIncludeFunction);
    if (nempty(bad)) {
      String msg = "INCLUDING BAD FUNCTIONS: " + sfu(bad);
      print(msg);
      print(join(tok));
      throw fail(msg);
    }
      
    List<String> added = new ArrayList();
    List<Future<String>> parts = new ArrayList();
    List<String> preload = new ArrayList();
    
    for (String x : needed)
      if (sfMap.containsKey(x))
        preload.add(sfMap.get(x));
    print("Preloading: " + preload);
    cachePreload(preload);
    
    for (String x : cloneList(needed)) {
      if (defd.contains(x)) continue;
      
      String id = sfMap.get(x);
      if (id == null) {
        print("Standard function " + x + " not found.");
        needed.remove(x);
        continue;
      }
      //print("Adding function: " + x + " (" + id + ")");
       
      String function = cacheGet(id) + "\n";
      //if (("\n" + function).contains("\n!")) print("Warning: " + id + " contains translators.");
      
      if (cacheStdFunctions) {
        long _id = psI(id);
        CachedInclude ci = cachedIncludes.get(_id);
        if (ci == null) {
          cachedIncludes.put(_id, ci = new CachedInclude(_id));
          //println("Caching include " + _id + ", l=" + l(cachedIncludes));
        }
        function += "\n";
        final String _function = function;
        if (neq(ci.javax, function)) {
          print("Compiling function: " + x);
          ci.javax = function;
          ci.java = executor.submit(new Callable<String>() {
            public String call() {
              // We assume that variables made with makeVar() will always be local to some block
              varCountByThread.set(null);
              return join(localStuff1(jtok(_function)));
            }
          });
          ci.realJava = null;
        }
        parts.add(ci.javaFuture());
      } else
        parts.add(nowFuture(function));
        
      added.add(x);
      Collection<String> newFunctions = new HashSet(findFunctionDefs(javaTok(function)));
      defd.addAll(newFunctions);
      for (String f : newFunctions)
        if (!addedFunctions.add(f)) {
          printSources(tok);
          throw fail("Trying to add function " + f + " again - main class syntax broken!");
        }
    }
    
    print("Getting " + nParts(parts));
    String text = lines(getAllFutures(parts));
    print("Including " + nParts(parts));
    tok = includeInMainLoaded(tok, text);
      
    // defd = new HashSet(findFunctions(tok));
    //print("Functions added: " + joinWithSpace(added));
    
    for (String x : needed)
      if (!defd.contains(x)) {
        print(join(tok));
        throw fail("Function not defined properly: " + x);
      }
    //print("Iteration " + (i+2));
    
    print("Processing definitions");
    
    dontPrint("definitions"); tok_definitions(tok);
    dontPrint("ifndef"); tok_ifndef(tok);
    dontPrint("ifdef"); tok_ifdef(tok);
    
    if (i >= 1000) throw fail("Too many iterations");
  }
  
  return tok;
}

// TODO: skip functions defined in inner classes!
static List<String> findFunctions(List<String> tok) {
  //ret findFunctionDefinitions(join(findMainClass(tok)));
  return findFunctionDefs(findMainClass(tok));
}

static String cacheGet(String snippetID) {
  snippetID = formatSnippetID(snippetID);
  String text = snippetCache.get(snippetID);
  if (text == null) {
    text = loadSnippet(snippetID);
    // very early processing/checks for includes
    
    if (hasUnclosedStringLiterals(text))
      throw fail("Unclosed string literals in " + snippetID);
      
    if (regexpContains("\\bscope\\b", text)) {
      List<String> tok = javaTok(text);
      tok_scopes(tok, "autoCloseScopes" , true);
      text = join(tok);
    }
    
    snippetCache.put(snippetID, text);
  }
  return text;
}

static void cachePreload(Collection<String> ids) {
  List<String> needed = new ArrayList();
  for (String id : ids)
    if (!snippetCache.containsKey(formatSnippetID(id)))
      needed.add(formatSnippetID(id));
  if (l(needed) > 1) {
    List<String> texts = loadSnippets(needed);
    for (int i = 0; i < l(needed); i++)
      if (texts.get(i) != null)
        snippetCache.put(needed.get(i), texts.get(i));
  }
}

static List<String> jtok(List<String> tok) {
  return jtok(join(tok));
}

static List<String> jtok(String s) {
  return indexTokenList(javaTok(s));
}

static HashSet<String> haveClasses_actual(List<String> tok) {
  HashSet<String> have = new HashSet();
  for (List<String> c : innerClassesOfMain(tok))
    have.add(getClassDeclarationName(c));
  return have;
}

static HashSet<String> haveClasses_addImported(List<String> tok, HashSet<String> have) { return haveClasses_addImported(tok, have, true); }
static HashSet<String> haveClasses_addImported(List<String> tok, HashSet<String> have, boolean mainLevel) {
  have.addAll(tok_importedClassNames(tok, mainLevel ? (__18, __19) -> onImportFound(__18, __19) : null));
  have.addAll(usualJavaClassNames()); // for S => S.class
  return have;
}

// works on Java level (no "sclass" etc)
// returns list of classes we have (useful for other processing)
static HashSet<String> addStandardClasses_v2(List<String> tok) {
  if (lclasses == null) {
    String sc = cacheGet("#1003674");
    lclasses = new ArrayList();
    for (String line : tlft_j(sc)) {
      int idx = line.indexOf('/');
      lclasses.addAll(ll(line.substring(0, idx), line.substring(idx+1)));
    }
  }
  List<String> definitions = lclasses;

  for (int safety = 0; safety < 10; safety++) {
    HashSet<String> have = haveClasses_actual(tok);
    haveClasses_addImported(tok, have);
    have.addAll(keys(rewrites));

    int j;
    while ((j = jfind(tok, "should not include class *.")) >= 0) {
      String cname = tok.get(j+8);
      shouldNotIncludeClass.add(cname);
      clearAllTokens(subList(tok, j, j+12));
    }
    
    while ((j = jfind(tok, "do not include class *.")) >= 0) {
      String name = tok.get(j+8);
      if (!needLatest.contains(name)) doNotIncludeClass.add(name);
      clearAllTokens(subList(tok, j, j+12));
    }
    
    Map<String, String> need = new HashMap();
    Set<String> snippets = new HashSet();
    Set<String> idx = tokenIndexWithoutIfclass_forStdClasses(tok);
    
    while ((j = jfind(tok, "please include class *.")) >= 0) {
      String cname = tok.get(j+6);
      idx.add(cname);
      clearAllTokens(subList(tok, j, j+10));
    }

    for (int i = 0; i+1 < l(definitions); i += 2) {
      String className = definitions.get(i);
      String snippetID = fsI(definitions.get(i+1));
      if (idx.contains(className) && !have.contains(className) && snippets.add(snippetID))
        need.put(className, snippetID);
    }
    if (hasDef("SymbolAsString")) {
      print("Have SymbolAsString.");
      if (need.containsKey("Symbol")) {
        print("Have Symbol.");
        need.remove("Symbol");
      }
    } else
      print("No SymbolAsString.");
      
    removeAll(need, doNotIncludeClass);
    if (empty(need)) return have;
  
    for (String className : keys(need))
      if (shouldNotIncludeClass.contains(className)) {
        String msg = "INCLUDING BAD CLASS: " + className;
        print(msg);
        print(join(tok));
        throw fail(msg);
      }
      
    cachePreload(values(need));
    
    List<Pair<String, CachedInclude>> parts = new ArrayList(); // class name, Java code
    
    print("Adding classes: " + joinWithSpace(keys(need)));
    for (String className : keys(need)) {
      if (have.contains(className)) continue; // intermittent add
      String snippetID = need.get(className);
      //print("Adding class " + className + " / " + snippetID);
      snippetID = fsI(snippetID);
      String text = cacheGet(snippetID) + "\n";
      
      assertTrue(cacheStdClasses);
      long _id = psI(snippetID);
      CachedInclude ci = cachedIncludes.get(_id);
      if (ci == null) cachedIncludes.put(_id, ci = new CachedInclude(_id));
      if (neq(ci.javax, text)) {
        print("Compiling class: " + className);
        ci.javax = text;
        ci.java = executor.submit(new Callable<String>() {
          public String call() {
            // We assume that variables made with makeVar() will always be local to some block
            varCountByThread.set(null);
            return join(localStuff1(jtok(text)));
          }
        });
        ci.realJava = null;
      }
      parts.add(pair(className, ci));
    }
    
    StringBuilder buf = new StringBuilder();
    for (Pair<String, CachedInclude> p : parts) {
      String className = p.a;
      List<String> ct = javaTok(p.b.java());
      Map<String, String> rewritten = new HashMap();
      tok_findAndClearRewrites(ct, rewritten);
      if (rewritten.containsKey(className))
        have.add(className);
      for (List<String> c : allClasses(ct)) {
        String name = getClassDeclarationName(c);
        have.add(name);
      }
      
      haveClasses_addImported(ct, have, false);
      
      if (!have.contains(className))
        throw fail("Wrongly defined class: " + className + " / " + p.b.snippetID + "\n\n" + p.b.java());
      if (!addedClasses.add(className)) {
        printSources(tok);
        throw fail("Trying to add class " + className + " again - main class syntax broken!");
      }
      buf.append(p.b.java());
    } // for part
    
    tok = includeInMainLoaded(tok, str(buf));
  }
  throw fail("safety 10");
}

static Set<String> expandableClassNames = lithashset("BigInteger");

// no reTok - leaves tok dirty
// magically append ".class" to class name references
static void expandClassReferences_lazy(List<String> tok, Set<String> classNames) { expandClassReferences_lazy(tok, classNames, null); }
static void expandClassReferences_lazy(List<String> tok, Set<String> classNames, List<IntRange> reToks) {
  for (int i = 3; i+2 < l(tok); i += 2) {
    String t = tok.get(i);
    
    // skip implements/extends/throws lists
    if (eqOneOf(t, "implements", "extends", "throws")) {
      i = tok_endOfImplementsList(tok, i);
      continue;
    }
    
    if (classNames.contains(t) || expandableClassNames.contains(t)) {
      String s = tok.get(i-2); t = tok.get(i+2);
      // Now s is token before class name, t is token after class name
      // TODO: This whole logic ain't very good
      // (Hard to distinguish between "Int.class" as an argument
      // and "Int" as a type parameter.)
      if (eqOneOf(s, "instanceof", "new", ".", "<", ">", "/", "nu")) continue;
      if (eq(t, ":")) continue; // e.g. String::concat
      if (isInteger(s)) continue;
      if (isIdentifier(s) && !eqOneOf(s, "ret", "return")) continue;
        
      if (eq(t, ",") && isIdentifier(get(tok, i+4)) && eqGet(tok, i+6, ">")) continue; // e.g. T3<L<S>, S, S>
      if (eq(s, ",") && isIdentifier(get(tok, i-4)) &&
        (eqGet(tok, i-6, "<") || eqGet(tok, i-6, ",")
        && isIdentifier(get(tok, i-8)) && eqGet(tok, i-10, "<"))) continue; // e.g. T3<S, S, S> or IF3<S, S, S, S>
      if (eq(s, ",") && eqOneOf(_get(tok, i-6), "implements", "throws")) continue;
      
      // check for cast
      if (eq(s, "(") && eq(t, ")") && i >= 5) {
        if (!eqOneOf(get(tok, i+4), "{", ";")) {
          String x = tok.get(i-4);
          if (!isIdentifier(x)) continue;
          if (eqOneOf(x, "ret", "return")) continue;
        }
      }
      
      // check following token
      if (eqOneOf(t, ",", ")", ";", ":", "|")) {
        tok.set(i, tok.get(i) + ".class");
        { if (reToks != null) reToks.add(intRange(i, i+1)); }
      }
    }
  }
}

static void expandClassReferences(List<String> tok, Set<String> classNames) {
  List<IntRange> reToks = new ArrayList();
  expandClassReferences_lazy(tok, classNames, reToks);
  reTok_multi(tok, reToks);
}

// "<id>/<ClassName>" => "((ClassName) <id>)"
static void slashCasts(List<String> tok, final Set<String> classNames) {
  /*jreplace(tok, "<id>/<id>", "(($3) $1)", tokcondition {
    ret classNames.contains(tok.get(i+5));
  });*/
  int n = l(tok)-4;
  for (int i = 1; i < n; i += 2)
    if (tok.get(i+2).equals("/") && isIdentifier(tok.get(i))
      && classNames.contains(tok.get(i+4)))
      replaceTokens_reTok(tok, i, i+5, "((" + tok.get(i+4) + ") " + tok.get(i) + ")");
}

// "<ClassName>(...)" => "new <ClassName>(...)"
// doesn't work at beginning of statement as we can't easily
// distinguish it from a constructor declaration.
static void newWithoutNew(List<String> tok, final Set<String> classNames) {
  TokCondition cond = newWithoutNew_condition(classNames);
  jreplace(tok, "<id>(", "new $1(", cond);
  // just two cases with type args for now
  jreplace(tok, "<id><<id>>(", "new $1<$3>(", cond);
  jreplace(tok, "<id><>(", "new $1<>(", cond);
}

static TokCondition newWithoutNew_condition(final Set<String> classNames) {
  return new TokCondition() { public boolean get(final List<String> tok, final int i) {
    if (!classNames.contains(tok.get(i+1))) return false;
    String prev = _get(tok, i-1);
    boolean ok = //!(eqGet(tok, i-3, "ifclass") && isIdentifier(prev))
      nempty(prev) // discarded ifclass
      && (eq(prev, ">") ? eqGet(tok, i-3, "-")
      : neqOneOf(prev, "new", ";", "}", "{", "public", "protected", "private", "."));
    //print("newWithoutNew: checking " + struct(subList(tok, i-3, i+2)) + " => " + ok);
    return ok;
  }};
}

static boolean processConceptsDot(List<String> tok) {
  boolean anyChange = false, change;
  do {
    change = false;
    for (int i : jfindAll(tok, "concepts."))
      if (contains(get(tok, i+3), "\n")) {
        replaceTokens(tok, i, i+3, "!" + "include once #1004863 // Dynamic Concepts");
        reTok(tok, i, i+3);
        change = anyChange = true;
        break;
      }
  } while (change);
  return anyChange;
}

static void caseAsVariableName(List<String> tok) {
  if (!tok.contains("case")) return;
  for (int i = 1; i+2 < l(tok); i += 2) {
    String t = tok.get(i+2);
    if (tok.get(i).equals("case")
      && !(t.startsWith("'") || isInteger(t) || isIdentifier(t) || isQuoted(t)))
      tok.set(i, "_case");
  }
}

static void continueAsFunctionName(List<String> tok) {
  jreplace(tok, "continue(", "_continue(");
}

// f bla => "bla" - and "please include function bla."
static void functionReferences(List<String> tok) {
  int i;
  
  jreplace_dyn(tok, "f-thread <id>", new F2<List<String>, Integer, Object>() { public Object get(List<String> tok, Integer cIdx) { try { 
    return "dynamicCallableMC_thread(" + quote(tok.get(cIdx+6)) + ")";
   } catch (Exception __e) { throw rethrow(__e); } }
  public String toString() { return "\"dynamicCallableMC_thread(\" + quote(tok.get(cIdx+6)) + \")\""; }});
  
  String keyword = "f";
  while ((i = jfind(tok, keyword + " <id>", new TokCondition() { public boolean get(final List<String> tok, final int i) {
    return !eqOneOf(tok.get(i+3), "instanceof", "default");
  }})) >= 0) {
    String f = tok.get(i+2);
    clearTokens(tok, i, i+2);
    tok.set(i+2, quote(f));
    reTok(tok, i, i+2);
    tok.set(l(tok)-1, last(tok) + "\nplease include function " + f + ".");
    reTok(tok, l(tok)-1, l(tok));
  }
  
  // r|rThread|rEnter|rThreadEnter|rEnterThread fname => r|rThread|... { fname() }
  while ((i = jfindOneOf_cond(tok, new TokCondition() { public boolean get(final List<String> tok, final int i) {
    return !eq(tok.get(i+3), "instanceof")
      && !eq(_get(tok, i-1), "cast");
  }}, "r <id>", "rThread <id>", "rEnter <id>", "rThreadEnter <id>",
    "rEnterThread <id>")) >= 0) {
    String f = tok.get(i+2);
    replaceTokens(tok, i, i+3, tok.get(i) + " { " + f + "(); }");
    reTok(tok, i, i+3);
  }
  
  // dm_q fname => r_dm_q(r fname)
  jreplace(tok, "dm_q <id>", "r_dm_q(r $2)");
  
  // vf<S> fname => voidfunc(S s) { fname(s) }
  jreplace(tok, "vf<<id>> <id>", "voidfunc($3 a) { $5(a) }");
  
  // vf<L<S>> fname => voidfunc(L<S> a) { fname(a) }
  jreplace(tok, "vf<<id><<id>>> <id>", "voidfunc($3<$5> a) { $8(a) }");
  
  // construct<S> Entry => func(S s) -> Entry { new Entry(s) }
  jreplace(tok, "construct<<id>> <id>", "func($3 a) -> $5 { new $5(a) }");
  
  // f<S> fname => func -> S { fname() }
  jreplace(tok, "f<<id>> <id>", "func -> $3 { $5() }");
  
  // f<S, S> fname => func(S x) -> S { fname(x) }
  jreplace(tok, "f<<id>, <id>> <id>", "func($3 x) -> $5 { $7(x) }");
  
  // f<S, L<S>> fname => func(S x) -> L<S> { fname(x) }
  jreplace(tok, "f<<id>, <id><<id>>> <id>", "func($3 x) -> $5 $6 $7 $8 { $10(x) }");
  
  // f<L<S>, S> fname => func(L<S> x) -> S { fname(x) }
  jreplace(tok, "f<<id><<id>>, <id>> <id>", "func($3 $4 $5 $6 x) -> $8 { $10(x) }");
  
  // f<S, L<S>, S> fname => func(S x, L<S> y) -> S { fname(x, y) }
  jreplace(tok, "f<<id>, <id><<id>>, <id>> <id>", "func($3 x, $5 $6 $7 $8 y) -> $10 { $12(x, y) }");
  
  // if1 fname => a -> fname(a)
  // ivf1 fname => a -> fname(a)
  for (String _keyword : ll("if1", "ivf1"))
    jreplace_dyn(tok, _keyword + " <id>", new F2<List<String>, Integer, String>() { public String get(List<String> tok, Integer i) { try { 
      String var = makeVar();
      return var + " -> " + tok.get(i+2) + "(" + var + ")";
     } catch (Exception __e) { throw rethrow(__e); } }
  public String toString() { return "S var = makeVar();\r\n      ret var + \" -> \" + tok.get(i+2) + \"(\" + var + \")\";"; }});
}

static void quicknu(List<String> tok) {
  jreplace(tok, "nu <id>(", "nu($2.class, "); // not needed anymore
  jreplace(tok, "nu <id>", "new $2");
}

// fill variable innerClasses_list
static void innerClassesVar(List<String> tok, Set<String> have) {
  int i = jfind_check("myInnerClasses_list", tok, ">myInnerClasses_list;");
  if (i < 0) return;
  tok.set(i+4, "=litlist(\n" + joinQuoted(", ", have) + ");");
  reTok(tok, i+4, i+5);
}

// fill variable innerClasses_list
static void fillVar_transpilationDate(List<String> tok) {
  int i = jfind_check("myTranspilationDate_value", tok, "long myTranspilationDate_value;");
  if (i < 0) return;
  tok.set(i+4, " = " + now() + "L;");
  reTok(tok, i+4, i+5);
}

static boolean ifclass_reTokImmediately = false;
static boolean ifclass_noReTok = true;

// process ifclass x ... endif blocks
static void tok_ifclass(List<String> tok, Set<String> have) {
  tok_conditionals(tok, "ifclass", "endif", id -> contains(have, id), ifclass_reTokImmediately, ifclass_noReTok);
}

// returns number of changes
static int tok_conditionals(List<String> tok, String keyword, String keywordEnd, IF1<String, Boolean> pred, boolean reTokImmediately, boolean noReTok) {
  int changes = 0;
  if (tok instanceof IContentsIndexedList) {
    int[] l = ((IContentsIndexedList) tok).indicesOf(keyword);
    for (int j = l(l)-1; j >= 0; j--) {
      int i = l[j];
      if (isIdentifier(get(tok, i+2))) {
        processConditional(tok, i, keyword, keywordEnd, pred, reTokImmediately && !noReTok);
        ++changes;
      }
    }
  } else {
    if (!tok.contains(keyword)) return changes;
    int i = l(tok);
    while ((i = rjfind(tok, 1, i-1, keyword + " <id>")) >= 0) {
      ++changes;
      processConditional(tok, i, keyword, keywordEnd, pred, reTokImmediately && !noReTok);
    }
  }
  if (changes != 0 && !reTokImmediately && !noReTok) reTok(tok);
  //print(keyword + ": " + nChanges(changes));
  return changes;
}

static void processConditional(List<String> tok, int i, String keyword, String keywordEnd, IF1<String, Boolean> pred, boolean reTokImmediately) {
  int j = jfind(tok, i+4, keywordEnd);
  if (j < 0) j = l(tok)-1;
  String name = tok.get(i+2);
  boolean has = pred.get(name);
  //print(keyword + " " + name + " => " + has);
  if (has) {
    clearTokens_maybeReTok(tok, j, j+1, reTokImmediately);
    clearTokens_maybeReTok(tok, i, i+3, reTokImmediately);
  } else
    clearTokens_maybeReTok(tok, i, j+1, reTokImmediately); // safer than before
}

// set flag *.
static void tok_definitions(List<String> tok) {
  int i;
  while ((i = jfind_check("flag", tok, "set flag <id>.")) >= 0) {
    String fname = tok.get(i+4);
    print("Setting flag " + fname);
    definitions.add(fname);
    if (eqic(fname, "debug_jreplace")) debug_jreplace = true;
    clearAllTokens(subList(tok, i, i+8));
  }
  
  while ((i = jfind_check("flag", tok, "unset flag <id>.")) >= 0) {
    String fname = tok.get(i+4);
    print("Unsetting flag " + fname);
    definitions.remove(fname);
    clearAllTokens(subList(tok, i, i+8));
  }
}

// rewrite <id> [=|with|to] <definition>
// - a global version of "replace <id> with"
// new version - may not work yet
/*svoid tok_findAndClearRewrites(LS tok, SS newlyDefined default null) {
  tok_findRewrites(tok, newlyDefined, f -> {
    print("Have rewrite: " + f.token + " => " + f.replacement());
    clearTokens(f.tok, f.startCIdx(), f.endNIdx());
  });
}*/


// rewrite <id> [=|with|to] <definition>
// - a global version of "replace <id> with"
// old version (works)
static void tok_findAndClearRewrites(List<String> tok) { tok_findAndClearRewrites(tok, null); }
static void tok_findAndClearRewrites(List<String> tok, Map<String, String> newlyDefined) {
  int i;
  while ((i = jfind(tok, "rewrite <id>", (_tok, nIdx) ->
    eqGetOneOf(_tok, nIdx+5, "with", "=", "to"))) >= 0) {
    String token = tok.get(i+2);
    int repStart = i+6;
    int repEnd = smartIndexOf(tok, repStart, ".");
    String replacement = joinSubList(tok, repStart, repEnd-1);
    clearTokens(tok, i, repEnd+1);
    mapPut(newlyDefined, token, replacement);
    rewrites.put(token, replacement);
    print("Have rewrite: " + token + " => " + replacement);
  }
}

static void tok_processRewrites(List<String> tok) {
  for (String token : keys(rewrites))
    jreplace(tok, token, rewrites.get(token));
}

// extend *. (set base class of main class)
static void tok_extend(List<String> tok) {
  int i;
  while ((i = jfind(tok, "extend <id>.")) >= 0) {
    mainBaseClass = tok.get(i+2);
    clearAllTokens(tok, i, i+7);
  }
}

// process ifndef x ... endifndef blocks
static void tok_ifndef(List<String> tok) {
  tok_conditionals(tok, "ifndef", "endifndef", id -> !definitions.contains(id), true, false);
}
  
// process ifdef x ... endifdef blocks
static void tok_ifdef(List<String> tok) {
  tok_conditionals(tok, "ifdef", "endifdef", id -> definitions.contains(id), true, false);
}
  
static void conceptDeclarations(List<String> tok) {
  for (String kw : ll("concept", "sconcept")) {
    Object cond = new TokCondition() { public boolean get(final List<String> tok, final int i) { tok_addFieldOrder(tok, i+1); return true; }};
    boolean re = false;
    if (jreplace(tok, kw + " <id> {", "static class $2 extends Concept {", cond)) re = true;
    if (jreplace(tok, kw + " <id> implements", "static class $2 extends Concept implements", cond)) re = true;
    if (jreplace(tok, kw + " <id>", "static class $2", cond)) re = true;
    if (re) reTok(tok);
  }
}

static void shortenedSubconcepts(List<String> tok) {
  jreplace(tok, "<id> > <id> {", "concept $3 extends $1 {", new TokCondition() { public boolean get(final List<String> tok, final int i) {
    boolean b = (i == 0 || tok.get(i).contains("\n")) || eq(_get(tok, i-1), "abstract"); // only at beginning of line or after "abstract"
    //print("subconcept " + b + ": " + structure(subList(tok, i-1, i+5)));
    return b;
  }});
}

// -slightly experimental
// -do calculation in another thread, then return to AWT thread
// -must be placed in a block
// -transforms rest of block 
static void unswing(List<String> tok) {
  int i;
  while ((i = jfind(tok, "unswing {")) >= 0) {
    int idx = i+2;
    int closingBracket = findEndOfBracketPart(tok, idx)-1;
    int endOfOuterBlock = findEndOfBracketPart(tok, closingBracket)-1;
    tok.set(i, "thread");
    tok.set(closingBracket, " awt {");
    tok.set(endOfOuterBlock, "}}}");
    reTok(tok, closingBracket-1, endOfOuterBlock+1);
  }
}

// -Syntax: lock theLock;
// -lock a lock, unlock at end of current block with finally
static void lockBlocks(List<String> tok) {
  int i;
  while ((i = jfind(tok, "lock <id>", new TokCondition() { public boolean get(final List<String> tok, final int i) { return neq(tok.get(i+3), "instanceof"); }})) >= 0) {
    int semicolon = findEndOfStatement(tok, i)-1;
    String var = makeVar();
    int endOfOuterBlock = findEndOfBracketPart(tok, semicolon)-1;
    replaceTokens(tok, i, semicolon+1,
      "Lock " + var + " = " + joinSubList(tok, i+2, semicolon-1) + "; lock(" + var + "); try {");
    tok.set(endOfOuterBlock, "} finally { unlock(" + var + "); } }");
    reTok(tok, i, endOfOuterBlock+1);
  }
}

// -Syntax: temp Bla bla = bla();
// -expands to try(Bla bla = bla()) { ... } with rest of block inside
static void tempBlocks(List<String> tok) {
  int i;
  jreplace_dyn(tok, "temp (<id>) <id>", (_tok, cIdx) -> {
    String var = makeVar(), type = tok.get(cIdx+4), firstTokenOfExpr = tok.get(cIdx+8);
    return ("temp " + type + " " + var + " = cast " + firstTokenOfExpr);
  });
  
  jreplace(tok, "temp <id> =", "temp var $2 =");
  
  while ((i = jfind(tok, "temp <id>")) >= 0) {
    int semicolon = findEndOfStatement(tok, i)-1;
    int endOfOuterBlock = findEndOfBracketPart(tok, semicolon)-1;
    List<String> sub = subList(tok, i-1, semicolon);
    int eq = subList(sub, 0, smartIndexOfOneOf(sub, "{", "(")).indexOf("=");
    String var;
    if (eq >= 0)
      var = sub.get(eq-2);
    else {
      // no var name, e.g. temp newThoughtSpace();
      var = makeVar();
      tok.set(i+2, "AutoCloseable " + var + " = " + tok.get(i+2));
    }
      
    //tok.set(i, "try (");
    //tok.set(semicolon, ") {";
    //tok.set(endOfOuterBlock, "}}");
    
    tok.set(i, "");
    tok.set(semicolon, "; try {");
    tok.set(endOfOuterBlock, "} finally { _close(" + var + "); }}");
    
    reTok(tok, i, endOfOuterBlock+1);
  }
}

static void forgetCachedIncludes() {
  cachedIncludes.clear();
}

// TODO: when to do this / merge contents if there are multiple transpilers?
static void cleanMeUp() {
  for (CachedInclude ci : values(cachedIncludes))
    ci.clean();
  vmKeepWithProgramMD5_save("cachedIncludes");
}

static void printSources(List<String> tok) {
  String src = join(tok);
  print("----");
  print(src);
  print("----");
  print("Bracket hygiene: " + testBracketHygiene2(src));
}

static void tok_quickInstanceOf(List<String> tok, final Set<String> haveClasses) {
  if (!quickInstanceOfEnabled) return;
  // "x << X" or "x >> X" => "x instanceof X"
  for (String op : ll("<<", ">>"))
    jreplace(tok, "<id> " + op + " <id>", "$1 instanceof $4", new TokCondition() { public boolean get(final List<String> tok, final int i) {
      return haveClasses.contains(tok.get(i+7))
        && !eqOneOf(tok.get(i-1), "<", "extends", "implements");
    }});
}

static boolean hasDef(String s) {
  return definitions.contains(s);
}

static void tok_shortFinals(List<String> tok) {
  jreplace(tok, "fS", "final S");
  jreplace(tok, "fO", "final O");
  jreplace(tok, "fL", "final L");
  jreplace(tok, "fMap", "final Map");
  jreplace(tok, "fRunnable", "final Runnable");
  jreplace(tok, "f int", "final int");
}

static void tok_mainClassNameAndPackage(List<String> tok) {
  int i;
  if ((i = jfind(tok, "mainClassName <id>")) >= 0) {
    mainClassName = tok.get(i+2);
    if (eqGet(tok, i+4, ".") && isIdentifier(get(tok, i+6))) {
      mainPackage = mainClassName;
      mainClassName = tok.get(i+6);
      clearTokensAndReTok(tok, i, i+7);
    } else
      clearTokensAndReTok(tok, i, i+3);
  }
  
  if ((i = jfind(tok, "mainPackage <id>")) >= 0) {
    int j = i+2;
    while (subListEquals(tok, j+1, "", ".", "") && isIdentifier(get(tok, j+4)))
      j += 4;
    mainPackage = joinSubList(tok, i+2, j+1);
    clearTokens_reTok(tok, i, j+1);
  }
}

static void defineMapLikes(List<String> tok) {
  int i;
  while ((i = jfind(tok, "mapLike <id>")) >= 0) {
    mapLikeFunctions.add(printIf(printMapLikes(), "mapLike", tok.get(i+2)));
    clearTokens_reTok(tok, i, i+2);
  }
}

static void defineLambdaMapLikes(List<String> tok) {
  int i;
  while ((i = jfind(tok, "lambdaMapLike <id>")) >= 0) {
    lambdaMapLikeFunctions.add(printIf(printMapLikes(), "lambdaMapLike", tok.get(i+2)));
    clearTokens_reTok(tok, i, i+2);
  }
}

static void defineCurry1Likes(List<String> tok) {
  int i;
  while ((i = jfind(tok, "curry1Like <id>")) >= 0) {
    curry1LikeFunctions.add(printIf(printMapLikes(), "curry1Like", tok.get(i+2)));
    clearTokens_reTok(tok, i, i+2);
  }
}

static void defineMapMethodLikes(List<String> tok) {
  int i;
  while ((i = jfind(tok, "mapMethodLike <id>")) >= 0) {
    mapMethodLikeFunctions.add(printIf(printMapLikes(), "mapMethodLike", tok.get(i+2)));
    clearTokens_reTok(tok, i, i+2);
  }
}

// functions that work like "nu" syntactically (accept a class as "super-first" parameter [left of the bracket])
static void defineNuLikes(List<String> tok) {
  int i;
  while ((i = jfind(tok, "nuLike <id>")) >= 0) {
    nuLikeFunctions.add(printIf(printMapLikes(), "nuLike", tok.get(i+2)));
    clearTokens_reTok(tok, i, i+2);
  }
}

static void defineXLikes(List<String> tok, String keyword, Set<String> xLikeFunctions) {
  int i;
  while ((i = jfind(tok, keyword + " <id>")) >= 0) {
    xLikeFunctions.add(printIf(printMapLikes(), keyword, tok.get(i+2)));
    clearTokens_reTok(tok, i, i+2);
  }
}

static void defineExtraSF(List<String> tok) {
  int i;
  IntRange reTok = null;
  while ((i = jfind(tok, "function <id> is in *.")) >= 0) {
    extraStandardFunctions.put(tok.get(i+2), fsI(unquote(tok.get(i+8))));
    clearTokens(tok, i, i+12);
    reTok = combineIntRanges(reTok, intRange(i, i+12));
  }
  reTok(tok, reTok);
}

static boolean tok_applyAllXLikeFunctions(List<String> tok) {
  List<IntRange> reToks = new ArrayList();
  for (int i : jfindAll(tok, "<id> <id> (")) {
    String f = tok.get(i), arg = tok.get(i+2), replacement = null;
    if (contains(mapLikeFunctions, f))
      replacement = (f + "(f " + arg + ",");
    else if (contains(lambdaMapLikeFunctions, f))
      replacement = (f + "(lambda1 " + arg + ",");
    else if (contains(curry1LikeFunctions, f))
      replacement = (f + "(lambda2 " + arg + ",");
    else if (contains(mapMethodLikeFunctions, f))
      replacement = (f + "(" + (quote(arg)) + ",");
    else if (contains(nuLikeFunctions, f))
      replacement = (f + "(" + arg + ".class,");
    else if (contains(getLikeFunctions, f))
      replacement = (f + "(lambdaField " + arg + ",");
    else if (contains(lambdaMethod0LikeFunctions, f))
      replacement = (f + "(methodLambda0 " + arg + ",");
    else if (contains(lambda0LikeFunctions, f))
      replacement = (f + "(lambda0 " + arg + ",");
    
    if (replacement != null)
      replaceTokens_reTokLater(tok, reToks, i, i+5, replacement + " ");
  }
  reTok_multi(tok, reToks);
  return nempty(reToks);
}

/*sbool tok_applyMapLikeFunctions(LS tok, final Set<S> mapLikeFunctions) {
  // map funcname(...) => map(f funcname, ...)
  // filter funcname(...) => filter(f funcname, ...)
  // ...
  ret jreplace(tok, "<id> <id>(", "$1(f $2,", func(L<S> tok, int i) -> bool {
    contains(mapLikeFunctions, tok.get(i+1))
  });
}*/

/*sbool tok_applyLambdaMapLikeFunctions(LS tok, final Set<S> lambdaMapLikeFunctions) {
  // mapNonNulls funcname(...) => mapNonNulls(lambda1 funcname, ...)
  // mapKeysAndValues funcname(...) => mapKeysAndValues(lambda1 funcname, ...)
  // ...
  ret jreplace(tok, "<id> <id>(", "$1(lambda1 $2,", func(L<S> tok, int i) -> bool {
    contains(lambdaMapLikeFunctions, tok.get(i+1))
  });
}*/

/*sbool tok_applyCurry1LikeFunctions(LS tok, final Set<S> curry1LikeFunctions) {
  // curry1 funcname(...) => curry1(lambda2 funcname, ...)
  ret jreplace(tok, "<id> <id>(", "$1(lambda2 $2,", func(L<S> tok, int i) -> bool {
    contains(curry1LikeFunctions, tok.get(i+1))
  });
}*/

/*sbool tok_applyMapMethodLikeFunctions(LS tok, final Set<S> mapMethodLikeFunctions) {
  // mapMethod funcname(...) => mapMethod('funcname, ...)
  // collect funcname(...) => collect('funcname, ...)
  // ...
  ret jreplace_dyn(tok, "<id> <id>(",
    func(L<S> tok, int cIdx) -> S { tok.get(cIdx) + "(" + quote(tok.get(cIdx+2)) + "," },
    func(L<S> tok, int i) -> bool {
      contains(mapMethodLikeFunctions, tok.get(i+1))
    });
}*/

static void runMetaPostBlocks(List<String> tok) {
  for  (String code : unnull(metaPostBlocks)) { ping(); 
    String snippetID = standardFunctionSnippet(assertIdentifier(code));
    if (empty(snippetID))
      throw fail("meta-post function not found: " + code);
    call(hotwireCached(snippetID), code, tok);
    //callF(codeToFunctionOnArbitraryType(code, "LS", L, "tok"), tok);
  }
}

static void runMetaTransformers(List<String> tok) {
  for  (String code : unnull(metaTransformers))
    { ping(); tok_runMetaTransformer(tok, code); }
}

/*sbool tok_applyNuLikeFunctions(LS tok, final Set<S> nuLikeFunctions) {
  // nu ClassName(...) => nu(ClassName, ...)
  // ...
  ret jreplace_dyn(tok, "<id> <id>(",
    func(L<S> tok, int cIdx) -> S { tok.get(cIdx) + "(" + tok.get(cIdx+2) + ".class," },
    func(L<S> tok, int i) -> bool {
      contains(nuLikeFunctions, tok.get(i+1))
    });
}*/

/*sbool tok_applyGetLikeFunctions(LS tok, Set<S> getLikeFunctions) {
  // get fieldName(...) => get(fieldLambda fieldName, ...)
  // ...
  ret jreplace_dyn(tok, "<id> <id>(",
    func(L<S> tok, int cIdx) -> S { tok.get(cIdx) + "(lambdaField " + tok.get(cIdx+2) + "," },
    func(L<S> tok, int i) -> bool {
      contains(getLikeFunctions, tok.get(i+1))
    });
}*/

static boolean metaCodeAllowed() {
  return allowMetaCode || containsIC(definitions, "allowMetaCode");
}

static List<String> indexTokenList(List<String> tok) {
  if (useIndexedList2) return indexedList2(tok);
  if (useTokenIndexedList) return tokenIndexedList3(tok);
  return tok;
}

static void tok_earlyGeneralStuff(List<String> tok) {
  // self-compile construct (TODO)
  /*jreplace(tok, "self-compile", (tok, iOpening, iClosing) -> S[] {
  
    selfCompiling
  });*/
  
  tok_standardBot1(tok);
  tok_processSimplified(tok);
  tok_compactModules(tok);
  
  tok_metaFor(tok);
  
  // is, short for "implements"
  jreplace(tok, "is <id>", "implements $2", new TokCondition() { public boolean get(final List<String> tok, final int i) {
    return !eqGetOneOf(tok, i+3, "a", "in"); // "is a", "is in" are defined as something else
  }});
}

static void lambdaReferences(List<String> tok) {
  // lambda0 myFunction => () -> myFunction()
  jreplace(tok, "lambda0 <id>", "() -> $2()");
  
  // lambda1 myFunction => var123 -> myFunction(var123)
  jreplace_dyn(tok, "lambda1 <id>", new F2<List<String>, Integer, Object>() { public Object get(List<String> tok, Integer cIdx) { try { 
    String var = makeVar();
    String s = var + " -> " + tok.get(cIdx+2) + "(" + var + ")";
    return eqGet(tok, cIdx-2, ")") ? roundBracket(s) : s;
   } catch (Exception __e) { throw rethrow(__e); } }
  public String toString() { return "S var = makeVar();\r\n    S s = var + \" -> \" + tok.get(cIdx+2) + \"(\" + var + \")..."; }});
  
  // lambda2 myFunction => (a, b) -> myFunction(a, b)
  jreplace_dyn(tok, "lambda2 <id>", new F2<List<String>, Integer, Object>() { public Object get(List<String> tok, Integer cIdx) { try { 
    String a = makeVar();
    String b = makeVar();
    String s = ("(" + a + ", " + b + ") -> " + (tok.get(cIdx+2)) + "(" + a + ", " + b + ")");
    return eqGet(tok, cIdx-2, ")") ? roundBracket(s) : s;
   } catch (Exception __e) { throw rethrow(__e); } }
  public String toString() { return "S a = makeVar();\r\n    S b = makeVar();\r\n    S s = \"(\\*a*/, \\*b*/) -> \\*tok.ge..."; }});
  
  // methodLambda0 methodName => var123 -> var123.methodName()
  jreplace_dyn(tok, "methodLambda0 <id>", new F2<List<String>, Integer, Object>() { public Object get(List<String> tok, Integer cIdx) { try { 
    String var = makeVar();
    return var + " -> " + var + "." + tok.get(cIdx+2) + "()";
   } catch (Exception __e) { throw rethrow(__e); } }
  public String toString() { return "S var = makeVar();\r\n    ret var + \" -> \" + var + \".\" + tok.get(cIdx+2) + \"()\";"; }});
  
  // fieldLambda fieldName => var123 -> var123.fieldName
  jreplace_dyn(tok, "fieldLambda <id>", new F2<List<String>, Integer, Object>() { public Object get(List<String> tok, Integer cIdx) { try { 
    String var = makeVar();
    return var + " -> " + var + "." + tok.get(cIdx+2);
   } catch (Exception __e) { throw rethrow(__e); } }
  public String toString() { return "S var = makeVar();\r\n    ret var + \" -> \" + var + \".\" + tok.get(cIdx+2);"; }});
}

static void clearSnippetCache() {
  snippetCache.clear();
  sf = null;
  lclasses = null;
}

static void mediumRefresh() {
  clearSnippetCache();
  print("Medium-refreshed transpiler " + mc());
}

static void jreplace_performing(List<String> tok, int i, int end, String expansion) {
  if (debug_jreplace)
    print("jreplace: " + quote(joinSubList(tok, i, end)) + " => " + quote(expansion));
}

static String mainClassName() {
  return or(mainClassName, "main");
}

static void grabImportedStaticFunctions(List<String> tok) {
  for (String name : tok_importedStaticFunctionNamesWithPackages(tok)) {
    int idx = lastIndexOf(name, '.');
    String pkg = takeFirst(idx, name);
    String functionName = dropFirst(idx+1, name);
    //print(functionName + " => " + pkg);
    doNotIncludeFunction.add(functionName);
    functionToPackage.put(functionName, pkg);
  }
}

static boolean printMapLikes() {
  return contains(definitions, "printMapLikesEtc");
}

// delete import if marked as "need latest"
static void onImportFound(List<String> tok, IntRange r) {
  String id = get(tok, r.end-3);
  if (needLatest.contains(id)) {
    print("Purging import: " + joinSubList(tok, r));
    clearTokens(tok, r);
  }
}
static RuntimeException fail() { throw new RuntimeException("fail"); }
static RuntimeException fail(Throwable e) { throw asRuntimeException(e); }
static RuntimeException fail(Object msg) { throw new RuntimeException(String.valueOf(msg)); }


static RuntimeException fail(Object... objects) { throw new Fail(objects); }


static RuntimeException fail(String msg) { throw new RuntimeException(msg == null ? "" : msg); }
static RuntimeException fail(String msg, Throwable innerException) { throw new RuntimeException(msg, innerException); }



static String str(Object o) {
  return o == null ? "null" : o.toString();
}

static String str(char[] c) {
  return new String(c);
}


static boolean boolOptPar(ThreadLocal<Boolean> tl) {
  return boolOptParam(tl);
}



// defaults to false
static boolean boolOptPar(Object[] __, String name) {
  return boolOptParam(__, name);
}

static boolean boolOptPar(String name, Object[] __) {
  return boolOptParam(__, name);
}


static boolean neq(Object a, Object b) {
  return !eq(a, b);
}


static <A> HashSet<A> lithashset(A... items) {
  HashSet<A> set = new HashSet();
  for (A a : items) set.add(a);
  return set;
}


// supports the usual quotings (", variable length double brackets) except ' quoting
static boolean isQuoted(String s) {
  
  if (isNormalQuoted_dirty(s)) return true;
  
  
  return isMultilineQuoted(s);
}


static boolean isIdentifier(String s) {
  return isJavaIdentifier(s);
}


static boolean isInteger(String s) {
  int n = l(s);
  if (n == 0) return false;
  int i = 0;
  if (s.charAt(0) == '-')
    if (++i >= n) return false;
  while (i < n) {
    char c = s.charAt(i);
    if (c < '0' || c > '9') return false;
    ++i;
  }
  return true;
}


static boolean eqic(String a, String b) {
  
  
    if ((a == null) != (b == null)) return false;
    if (a == null) return true;
    return a.equalsIgnoreCase(b);
  
}


static boolean eqic(Symbol a, Symbol b) {
  return eq(a, b);
}

static boolean eqic(Symbol a, String b) {
  return eqic(asString(a), b);
}


static boolean eqic(char a, char b) {
  if (a == b) return true;
  
    char u1 = Character.toUpperCase(a);
    char u2 = Character.toUpperCase(b);
    if (u1 == u2) return true;
  
  return Character.toLowerCase(u1) == Character.toLowerCase(u2);
}


static volatile StringBuffer local_log = new StringBuffer(); // not redirected




static volatile Appendable print_log = local_log; // might be redirected, e.g. to main bot

// in bytes - will cut to half that
static volatile int print_log_max = 1024*1024;
static volatile int local_log_max = 100*1024;

static boolean print_silent = false; // total mute if set

static Object print_byThread_lock = new Object();
static volatile ThreadLocal<Object> print_byThread; // special handling by thread - prefers F1<S, Bool>
static volatile Object print_allThreads;
static volatile Object print_preprocess;

static void print() {
  print("");
}

static <A> A print(String s, A o) {
  print(combinePrintParameters(s, o));
  return o;
}

// slightly overblown signature to return original object...
static <A> A print(A o) {
  ping_okInCleanUp();
  if (print_silent) return o;
  String s = o + "\n";
  print_noNewLine(s);
  return o;
}

static void print_noNewLine(String s) {
  
  try {
    Object f = getThreadLocal(print_byThread_dontCreate());
    if (f == null) f = print_allThreads;
      if (f != null)
        // We do need the general callF machinery here as print_byThread is sometimes shared between modules
        if (isFalse(
          
            f instanceof F1 ? ((F1) f).get(s) :
          
          callF(f, s))) return;
  } catch (Throwable e) {
    System.out.println(getStackTrace(e));
  }
  

  print_raw(s);
}

static void print_raw(String s) {
  
  if (print_preprocess != null) s = (String) callF(print_preprocess, s);
  s = fixNewLines(s);
  
  Appendable loc = local_log;
  Appendable buf = print_log;
  int loc_max = print_log_max;
  if (buf != loc && buf != null) {
    print_append(buf, s, print_log_max);
    loc_max = local_log_max;
  }
  if (loc != null) 
    print_append(loc, s, loc_max);
  
  
    System.out.print(s);
  
  vmBus_send("printed", mc(), s);
}

static void print_autoRotate() {
  
}


static boolean checkTokCondition(Object condition, List<String> tok, int i) {
  if (condition instanceof TokCondition)
    return ((TokCondition) condition).get(tok, i);
  return checkCondition(condition, tok, i);
}


static <A> List<A> ll(A... a) {
  ArrayList l = new ArrayList(a.length);
  if (a != null) for (A x : a) l.add(x);
  return l;
}


static List syncMap(Object f, Map map) {
  return syncMap(map, f);
}

// map: func(key, value) -> list element
static List syncMap(Map map, Object f) {
  return map(cloneLinkedHashMap(map), f); // TODO: use a temporary list instead
}

static <A, B> Map<A, B> syncMap() {
  return synchroHashMap();
}

static <A, B> Map<A, B> syncMap(Map map) {
  return synchronizedMap(map);
}


static TreeSet<String> ciSet() {
  return caseInsensitiveSet();
}


static <A> A getFuture(Future<A> f) { try {
  return f == null ? null : f.get();
} catch (Exception __e) { throw rethrow(__e); } }


static <A> NowFuture<A> nowFuture(A a) {
  return new NowFuture(a);
}


static long sysNow() {
  ping();
  return System.nanoTime()/1000000;
}


static void vmKeepWithProgramMD5_get(String varName) {
  String struct =  (String) (callOpt(javax(), "vmKeep_get", programID(), md5OfMyJavaSource(), varName));
  if (struct != null)
    setOpt(mc(), varName, unstructure(struct));
}


static volatile PersistableThrowable _handleException_lastException;
static List _handleException_onException = synchroList(ll((IVF1<Throwable>) (__1 -> printStackTrace2(__1))));
static boolean _handleException_showThreadCancellations = false;

static void _handleException(Throwable e) {
  _handleException_lastException = persistableThrowable(e);
  
  Throwable e2 = innerException(e);
  if (e2.getClass() == RuntimeException.class && eq(e2.getMessage(), "Thread cancelled.") || e2 instanceof InterruptedException) {
    if (_handleException_showThreadCancellations)
      System.out.println(getStackTrace_noRecord(e2));
    return;
  }

  for (Object f : cloneList(_handleException_onException)) try {
    callF(f, e);
  } catch (Throwable e3) {
    try {
      printStackTrace2(e3); // not using pcall here - it could lead to endless loops
    } catch (Throwable e4) {
      System.out.println(getStackTrace(e3));
      System.out.println(getStackTrace(e4));
    }
  }
}


static volatile int numberOfCores_value;

static int numberOfCores() {
  if (numberOfCores_value == 0)
    numberOfCores_value = Runtime.getRuntime().availableProcessors();
  return numberOfCores_value;
}


static <A> A or(A a, A b) {
  return a != null ? a : b;
}


// this syntax should be removed...
static Object getThreadLocal(Object o, String name) {
  ThreadLocal t =  (ThreadLocal) (getOpt(o, name));
  return t != null ? t.get() : null;
}

static <A> A getThreadLocal(ThreadLocal<A> tl) {
  return tl == null ? null : tl.get();
}

static <A> A getThreadLocal(ThreadLocal<A> tl, A defaultValue) {
  return or(getThreadLocal(tl), defaultValue);
}


static Object getOpt(Object o, String field) {
  return getOpt_cached(o, field);
}

static Object getOpt(String field, Object o) {
  return getOpt_cached(o, field);
}

static Object getOpt_raw(Object o, String field) { try {
  Field f = getOpt_findField(o.getClass(), field);
  if (f == null) return null;
  makeAccessible(f);
  return f.get(o);
} catch (Exception __e) { throw rethrow(__e); } }

// access of static fields is not yet optimized
static Object getOpt(Class c, String field) { try {
  if (c == null) return null;
  Field f = getOpt_findStaticField(c, field);
  if (f == null) return null;
  makeAccessible(f);
  return f.get(null);
} catch (Exception __e) { throw rethrow(__e); } }

static Field getOpt_findStaticField(Class<?> c, String field) {
  Class _c = c;
  do {
    for (Field f : _c.getDeclaredFields())
      if (f.getName().equals(field) && (f.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0)
        return f;
    _c = _c.getSuperclass();
  } while (_c != null);
  return null;
}



static Class javax() {
  return getJavaX();
}


static ThreadLocal<Object> print_byThread() {
  synchronized(print_byThread_lock) {
    if (print_byThread == null)
      print_byThread = new ThreadLocal();
  }
  return print_byThread;
}


// f can return false to suppress regular printing
// call print_raw within f to actually print something
static AutoCloseable tempInterceptPrint(F1<String, Boolean> f) {
  return tempSetThreadLocal(print_byThread(), f);
}


// get purpose 1: access a list/array/map (safer version of x.get(y))

static <A> A get(List<A> l, int idx) {
  return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null;
}

// seems to conflict with other signatures
/*static <A, B> B get(Map<A, B> map, A key) {
  ret map != null ? map.get(key) : null;
}*/

static <A> A get(A[] l, int idx) {
  return idx >= 0 && idx < l(l) ? l[idx] : null;
}

// default to false
static boolean get(boolean[] l, int idx) {
  return idx >= 0 && idx < l(l) ? l[idx] : false;
}

// get purpose 2: access a field by reflection or a map

static Object get(Object o, String field) {
  try {
    if (o == null) return null;
    if (o instanceof Class) return get((Class) o, field);
    
    if (o instanceof Map)
      return ((Map) o).get(field);
      
    Field f = getOpt_findField(o.getClass(), field);
    if (f != null) {
      makeAccessible(f);
      return f.get(o);
    }
      
    
      if (o instanceof DynamicObject)
        return getOptDynOnly(((DynamicObject) o), field);
    
  } catch (Exception e) {
    throw asRuntimeException(e);
  }
  throw new RuntimeException("Field '" + field + "' not found in " + o.getClass().getName());
}

static Object get_raw(String field, Object o) {
  return get_raw(o, field);
}

static Object get_raw(Object o, String field) { try {
  if (o == null) return null;
  Field f = get_findField(o.getClass(), field);
  makeAccessible(f);
  return f.get(o);
} catch (Exception __e) { throw rethrow(__e); } }

static Object get(Class c, String field) {
  try {
    Field f = get_findStaticField(c, field);
    makeAccessible(f);
    return f.get(null);
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
}

static Field get_findStaticField(Class<?> c, String field) {
  Class _c = c;
  do {
    for (Field f : _c.getDeclaredFields())
      if (f.getName().equals(field) && (f.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0)
        return f;
    _c = _c.getSuperclass();
  } while (_c != null);
  throw new RuntimeException("Static field '" + field + "' not found in " + c.getName());
}

static Field get_findField(Class<?> c, String field) {
  Class _c = c;
  do {
    for (Field f : _c.getDeclaredFields())
      if (f.getName().equals(field))
        return f;
    _c = _c.getSuperclass();
  } while (_c != null);
  throw new RuntimeException("Field '" + field + "' not found in " + c.getName());
}

static Object get(String field, Object o) {
  return get(o, field);
}

static boolean get(BitSet bs, int idx) {
  return bs != null && bs.get(idx);
}


static Map<Class, ArrayList<Method>> callF_cache = newDangerousWeakHashMap();


  static <A> A callF(F0<A> f) {
    return f == null ? null : f.get();
  }



  static <A, B> B callF(F1<A, B> f, A a) {
    return f == null ? null : f.get(a);
  }



  static <A> A callF(IF0<A> f) {
    return f == null ? null : f.get();
  }



  static <A, B> B callF(IF1<A, B> f, A a) {
    return f == null ? null : f.get(a);
  }



  static <A, B, C> C callF(F2<A, B, C> f, A a, B b) {
    return f == null ? null : f.get(a, b);
  }



  static <A, B, C> C callF(IF2<A, B, C> f, A a, B b) {
    return f == null ? null : f.get(a, b);
  }



  static <A> void callF(VF1<A> f, A a) {
    if (f != null) f.get(a);
  }


static Object callF(Runnable r) { { if (r != null) r.run(); } return null; }

static Object callF(Object f, Object... args) {
  if (f instanceof String)
    return callMCWithVarArgs((String) f, args); // possible SLOWDOWN over callMC
    
  return safeCallF(f, args);
}

static Object safeCallF(Object f, Object... args) {
  if (f instanceof Runnable) {
    ((Runnable) f).run();
    return null;
  }
  if (f == null) return null;
  
  Class c = f.getClass();
  ArrayList<Method> methods;
  synchronized(callF_cache) {
    methods = callF_cache.get(c);
    if (methods == null)
      methods = callF_makeCache(c);
  }
  
  int n = l(methods);
  if (n == 0) {
    
    throw fail("No get method in " + getClassName(c));
  }
  if (n == 1) return invokeMethod(methods.get(0), f, args);
  for (int i = 0; i < n; i++) {
    Method m = methods.get(i);
    if (call_checkArgs(m, args, false))
      return invokeMethod(m, f, args);
  }
  throw fail("No matching get method in " + getClassName(c));
}

// used internally
static ArrayList<Method> callF_makeCache(Class c) {
  ArrayList<Method> l = new ArrayList();
  Class _c = c;
  do {
    for (Method m : _c.getDeclaredMethods())
      if (m.getName().equals("get")) {
        makeAccessible(m);
        l.add(m);
      }
    if (!l.isEmpty()) break;
    _c = _c.getSuperclass();
  } while (_c != null);
  callF_cache.put(c, l);
  return l;
}


static String formatInt(int i, int digits) {
  return padLeft(str(i), '0', digits);
}

static String formatInt(long l, int digits) {
  return padLeft(str(l), '0', digits);
}


// f can return false to suppress regular printing
// call print_raw within f to actually print something
// f preferrably is F1<S, Bool>
static Object interceptPrintInThisThread(Object f) {
  Object old = print_byThread().get();
  print_byThread().set(f);
  return old;
}


static void _close(AutoCloseable c) {
  if (c != null) try {
    c.close();
  } catch (Throwable e) {
    // Some classes stupidly throw an exception on double-closing
    if (c instanceof javax.imageio.stream.ImageOutputStream)
      return;
    else throw rethrow(e);
  }
}


static boolean sameSnippetID(String a, String b) {
  if (!isSnippetID(a) || !isSnippetID(b)) return false;
  return parseSnippetID(a) == parseSnippetID(b);
}


static String programID() {
  return getProgramID();
}

static String programID(Object o) {
  return getProgramID(o);
}


static String defaultJavaXTranslatorID_value = "#759";

static String defaultJavaXTranslatorID() {
  return defaultJavaXTranslatorID_value;
}


static void setDefaultJavaXTranslatorID(String snippetID) {
  defaultJavaXTranslatorID_value = snippetID;
}


  static String mainJava;
  
  static String loadMainJava() throws IOException {
    if (mainJava != null) return mainJava;
    return loadTextFile("input/main.java", "");
  }


static int identityHashCode(Object o) {
  return System.identityHashCode(o);
}


static <A> HashSet<A> cloneHashSet(Collection<A> set) {
  if (set == null) return new HashSet();
  synchronized(collectionMutex(set)) {
    HashSet < A > s = new HashSet<>(l(set));
    s.addAll(set);
    return s;
  }
}


static Set<String> tok_mapLikeFunctions_set = emptySet();
  
static Set<String> tok_mapLikeFunctions() {
  return tok_mapLikeFunctions_set;
}


static Set<String> tok_mapMethodLikeFunctions_set = asHashSet(
  splitAtSpace("mapMethod uniquifyByField indexByField collect"));
  
static Set<String> tok_mapMethodLikeFunctions() {
  return tok_mapMethodLikeFunctions_set;
}


  static boolean hasCodeTokens(List<String> tok, String... tokens) {
    return findCodeTokens(tok, tokens) >= 0;
  }


static int l(Object[] a) { return a == null ? 0 : a.length; }
static int l(boolean[] a) { return a == null ? 0 : a.length; }
static int l(byte[] a) { return a == null ? 0 : a.length; }
static int l(short[] a) { return a == null ? 0 : a.length; }
static int l(long[] a) { return a == null ? 0 : a.length; }
static int l(int[] a) { return a == null ? 0 : a.length; }
static int l(float[] a) { return a == null ? 0 : a.length; }
static int l(double[] a) { return a == null ? 0 : a.length; }
static int l(char[] a) { return a == null ? 0 : a.length; }
static int l(Collection c) { return c == null ? 0 : c.size(); }

static int l(Iterator i) { return iteratorCount_int_close(i); } // consumes the iterator && closes it if possible

static int l(Map m) { return m == null ? 0 : m.size(); }
static int l(CharSequence s) { return s == null ? 0 : s.length(); }
static long l(File f) { return f == null ? 0 : f.length(); }



static int l(Object o) {
  return o == null ? 0
    : o instanceof String ? l((String) o)
    : o instanceof Map ? l((Map) o)
    : o instanceof Collection ? l((Collection) o)
    : o instanceof Object[] ? l((Object[]) o)
    : o instanceof boolean[] ? l((boolean[]) o)
    : o instanceof byte[] ? l((byte[]) o)
    : o instanceof char[] ? l((char[]) o)
    : o instanceof short[] ? l((short[]) o)
    : o instanceof int[] ? l((int[]) o)
    : o instanceof float[] ? l((float[]) o)
    : o instanceof double[] ? l((double[]) o)
    : o instanceof long[] ? l((long[]) o)
    : (Integer) call(o, "size");
}



  static int l(MultiSet ms) { return ms == null ? 0 : ms.size(); }





  static int l(IntRange r) { return r == null ? 0 : r.length(); }












static <A> List<A> singlePlusList(A a, Collection<A> l) {
  return itemPlusList(a, l);
}



static Object first(Object list) {
  return first((Iterable) list);
}


static <A> A first(List<A> list) {
  return empty(list) ? null : list.get(0);
}

static <A> A first(A[] bla) {
  return bla == null || bla.length == 0 ? null : bla[0];
}


static <A> A first(IterableIterator<A> i) {
  return first((Iterator<A>) i);
}


static <A> A first(Iterator<A> i) {
  return i == null || !i.hasNext() ? null : i.next();
}

static <A> A first(Iterable<A> i) {
  if (i == null) return null;
  Iterator<A> it = i.iterator();
  return it.hasNext() ? it.next() : null;
}

static Character first(String s) { return empty(s) ? null : s.charAt(0); }
static Character first(CharSequence s) { return empty(s) ? null : s.charAt(0); }


static <A, B> A first(Pair<A, B> p) {
  return p == null ? null : p.a;
}



static <A, B, C> A first(T3<A, B, C> t) {
  return t == null ? null : t.a;
}


static Byte first(byte[] l) { 
  return empty(l) ? null : l[0];
}





static <A> A first(A[] l, IF1<A, Boolean> pred) {
  return firstThat(l, pred);
}

static <A> A first(Iterable<A> l, IF1<A, Boolean> pred) {
  return firstThat(l, pred);
}

static <A> A first(IF1<A, Boolean> pred, Iterable<A> l) {
  return firstThat(pred, l);
}


static String[] dropFirst(int n, String[] a) {
  return drop(n, a);
}

static String[] dropFirst(String[] a) {
  return drop(1, a);
}

static Object[] dropFirst(Object[] a) {
  return drop(1, a);
}

static <A> List<A> dropFirst(List<A> l) {
  return dropFirst(1, l);
}

static <A> List<A> dropFirst(int n, Iterable<A> i) { return dropFirst(n, toList(i)); }
static <A> List<A> dropFirst(Iterable<A> i) { return dropFirst(toList(i)); }

static <A> List<A> dropFirst(int n, List<A> l) {
  return n <= 0 ? l : new ArrayList(l.subList(Math.min(n, l.size()), l.size()));
}

static <A> List<A> dropFirst(List<A> l, int n) {
  return dropFirst(n, l);
}

static String dropFirst(int n, String s) { return substring(s, n); }
static String dropFirst(String s, int n) { return substring(s, n); }
static String dropFirst(String s) { return substring(s, 1); }




// TODO: extended multi-line strings

static int javaTok_n, javaTok_elements;
static boolean javaTok_opt = false;

static List<String> javaTok(String s) {
  ++javaTok_n;
  ArrayList<String> tok = new ArrayList();
  int l = s == null ? 0 : s.length();
  
  int i = 0;
  while (i < l) {
    int j = i;
    char c, d;
    
        // scan for whitespace
        while (j < l) {
          c = s.charAt(j);
          d = j+1 >= l ? '\0' : s.charAt(j+1);
          if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
            ++j;
          else if (c == '/' && d == '*') {
            do ++j; while (j < l && !regionMatches(s, j, "*/"));
            j = Math.min(j+2, l);
          } else if (c == '/' && d == '/') {
            do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0);
          } else
            break;
        }
        
        tok.add(javaTok_substringN(s, i, j));
        i = j;
        if (i >= l) break;
        c = s.charAt(i);
        d = i+1 >= l ? '\0' : s.charAt(i+1);
    
        // scan for non-whitespace
        
        // Special JavaX syntax: 'identifier
        if (c == '\'' && Character.isJavaIdentifierStart(d) && i+2 < l && "'\\".indexOf(s.charAt(i+2)) < 0) {
          j += 2;
          while (j < l && Character.isJavaIdentifierPart(s.charAt(j)))
            ++j;
        } else if (c == '\'' || c == '"') {
          char opener = c;
          ++j;
          while (j < l) {
            int c2 = s.charAt(j);
            if (c2 == opener || c2 == '\n' && opener == '\'') { // allow multi-line strings, but not for '
              ++j;
              break;
            } else if (c2 == '\\' && j+1 < l)
              j += 2;
            else
              ++j;
          }
        } else if (Character.isJavaIdentifierStart(c))
          do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || s.charAt(j) == '\'')); // for stuff like "don't"
        else if (Character.isDigit(c)) {
          do ++j; while (j < l && Character.isDigit(s.charAt(j)));
          if (j < l && s.charAt(j) == 'L') ++j; // Long constants like 1L
        } else if (c == '[' && d == '[') {
          do ++j; while (j < l && !regionMatches(s, j, "]]"));
          j = Math.min(j+2, l);
        } else if (c == '[' && d == '=' && i+2 < l && s.charAt(i+2) == '[') {
          do ++j; while (j+2 < l && !regionMatches(s, j, "]=]"));
          j = Math.min(j+3, l);
        } else
          ++j;
      
    tok.add(javaTok_substringC(s, i, j));
    i = j;
  }
  
  if ((tok.size() % 2) == 0) tok.add("");
  javaTok_elements += tok.size();
  return tok;
}

static List<String> javaTok(List<String> tok) {
  return javaTokWithExisting(join(tok), tok);
}


static void replaceTokens_reTok(List<String> tok, int i, int j, String s) {
  replaceTokens(tok, i, j, s);
  reTok(tok, i, j);
}


static String unnull(String s) {
  return s == null ? "" : s;
}

static <A> Collection<A> unnull(Collection<A> l) {
  return l == null ? emptyList() : l;
}

static <A> List<A> unnull(List<A> l) { return l == null ? emptyList() : l; }
static int[] unnull(int[] l) { return l == null ? emptyIntArray() : l; }
static char[] unnull(char[] l) { return l == null ? emptyCharArray() : l; }
static double[] unnull(double[] l) { return l == null ? emptyDoubleArray() : l; }

static <A, B> Map<A, B> unnull(Map<A, B> l) {
  return l == null ? emptyMap() : l;
}

static <A> Iterable<A> unnull(Iterable<A> i) {
  return i == null ? emptyList() : i;
}

static <A> A[] unnull(A[] a) {
  return a == null ? (A[]) emptyObjectArray() : a;
}

static BitSet unnull(BitSet b) {
  return b == null ? new BitSet() : b;
}



//ifclass Symbol

static Symbol unnull(Symbol s) {
  return s == null ? emptySymbol() : s;
}
//endif



static <A, B> Pair<A, B> unnull(Pair<A, B> p) {
  return p != null ? p : new Pair(null, null);
}


static int unnull(Integer i) { return i == null ? 0 : i; }
static long unnull(Long l) { return l == null ? 0L : l; }
static double unnull(Double l) { return l == null ? 0.0 : l; }


static List<String> tok_moveImportsUp(List<String> tok) {
  StringBuilder buf = new StringBuilder();
  
  //print("tok_moveImportsUp n=" + l(tok));
  //print("Source begins: " + quote(shorten(join(tok), 200)));
  
  // scan imports on top
  int i;
  Set<String> have = new HashSet();
  for (i = 1; i < l(tok); i += 2)
    if (eq(tok.get(i), "import")) {
      int j = indexOf(tok, i+2, ";");
      if (j < 0) break;
      String s = joinSubList(tok, i, j+1);
      have.add(s);
      i = j;
    } else break;
  
  //print("tok_moveImportsUp have " + l(have) + " after " + i);
  
  List<IntRange> reToks = new ArrayList();
  
  // scan imports in text
  for (; i < l(tok); i += 2)
    if (eq(tok.get(i), "import")) {
      int j = indexOf(tok, i+2, ";");
      if (j < 0) continue; // huh?
      String s = joinSubList(tok, i, j+1);
      //print("Found import at " + i + ": " + s);
      if (!have.contains(s)) {
        buf.append(s).append("\n");
        have.add(s);
      }
      replaceTokens(tok, i, j+1, "");
      
      i -= 2;
    }
    
  if (nempty(buf)) {
    if (l(tok) == 1) addAll(tok, "", "");
    tok.set(1, str(buf)+"\n"+tok.get(1));
    reToks.add(intRange(1, 2));
  }
    
  return reTok_multi(tok, reToks);
}


static boolean tok_hasTranslators(List<String> tok) {
  int n = l(tok)-2;
  for (int i = 1; i < n; i += 2)
    if (tok.get(i).equals("!") && isInteger(tok.get(i+2)))
      return true;
  return false;
}


static String defaultTranslate(String text) {
  Class javax = getJavaX();
  File x = makeTempDir();
  saveTextFile(new File(x, "main.java"), text);
  List<File> libraries_out = new ArrayList();
  List<String> libraries2_out = new ArrayList();
  File y =  (File) (call(javax, "defaultTranslate", x, libraries_out, libraries2_out));
  if (y == null) return null;
  return loadTextFile(new File(y, "main.java"));
}



public static <A> String join(String glue, Iterable<A> strings) {
  if (strings == null) return "";
  if (strings instanceof Collection) {
    if (((Collection) strings).size() == 1) return str(first((Collection) strings));
  }
  StringBuilder buf = new StringBuilder();
  Iterator<A> i = strings.iterator();
  if (i.hasNext()) {
    buf.append(i.next());
    while (i.hasNext())
      buf.append(glue).append(i.next());
  }
  return buf.toString();
}

public static String join(String glue, String... strings) {
  return join(glue, Arrays.asList(strings));
}

static <A> String join(Iterable<A> strings) {
  return join("", strings);
}

static <A> String join(Iterable<A> strings, String glue) {
  return join(glue, strings);
}

public static String join(String[] strings) {
  return join("", strings);
}


static String join(String glue, Pair p) {
  return p == null ? "" : str(p.a) + glue + str(p.b);
}



static void tok_metaTransformNow(List<String> tok) {
  int i = -1;
  while ((i = jfind(tok, i+1, "meta-transformNow {")) >= 0) {
    int iOpening = indexOf(tok, i, "{");
    int iClosing = findEndOfBracketPart(tok, iOpening)-1;
    String code = joinSubList(tok, iOpening+2, iClosing-1);
    clearTokens_reTok(tok, i, iClosing+1);
    tok_runMetaTransformer(tok, code);
  }
}





//sbool ping_actions_shareable = true;
static volatile boolean ping_pauseAll = false;
static int ping_sleep = 100; // poll pauseAll flag every 100
static volatile boolean ping_anyActions = false;
static Map<Thread, Object> ping_actions = newWeakHashMap();
static ThreadLocal<Boolean> ping_isCleanUpThread = new ThreadLocal();

// always returns true
static boolean ping() {
  //ifdef useNewPing
  newPing();
  //endifdef
  if (ping_pauseAll || ping_anyActions) ping_impl(true /* XXX */);
  //ifndef LeanMode ping_impl(); endifndef
  return true;
}

// returns true when it slept
static boolean ping_impl(boolean okInCleanUp) { try {
  if (ping_pauseAll && !isAWTThread()) {
    do
      Thread.sleep(ping_sleep);
    while (ping_pauseAll);
    return true;
  }
  
  if (ping_anyActions) { // don't allow sharing ping_actions
    if (!okInCleanUp && !isTrue(ping_isCleanUpThread.get()))
      failIfUnlicensed();
    Object action = null;
    synchronized(ping_actions) {
      if (!ping_actions.isEmpty()) {
        action = ping_actions.get(currentThread());
        if (action instanceof Runnable)
          ping_actions.remove(currentThread());
        if (ping_actions.isEmpty()) ping_anyActions = false;
      }
    }
    
    if (action instanceof Runnable)
      ((Runnable) action).run();
    else if (eq(action, "cancelled"))
      throw fail("Thread cancelled.");
  }

  return false;
} catch (Exception __e) { throw rethrow(__e); } }



static <A> ArrayList<A> cloneList(Iterable<A> l) {
  return l instanceof Collection ? cloneList((Collection) l) : asList(l);
}

static <A> ArrayList<A> cloneList(Collection<A> l) {
  if (l == null) return new ArrayList();
  synchronized(collectionMutex(l)) {
    return new ArrayList<A>(l);
  }
}




static String jreplace(String s, String in, String out) {
  return jreplace(s, in, out, null);
}

static String jreplace(String s, String in, String out, Object condition) {
  List<String> tok = javaTok(s);
  return jreplace(tok, in, out, condition) ? join(tok) : s;
}

// leaves tok properly tokenized
// returns true iff anything was replaced
static boolean jreplace(List<String> tok, String in, String out) {
  return jreplace(tok, in, out, false, true, null);
}

static boolean jreplace(List<String> tok, String in, String out, Object condition) {
  return jreplace(tok, in, out, false, true, condition);
}

static boolean jreplace(List<String> tok, String in, String out, IF2<List<String>, Integer, Boolean> condition) {
  return jreplace(tok, in, out, (Object) condition);
}

static boolean jreplace(List<String> tok, String in, String out, boolean ignoreCase, boolean reTok, Object condition) {
  String[] toks = javaTokForJFind_array(in);
  int lTokin = toks.length*2+1;

  boolean anyChange = false;
  int i = -1;
  for (int n = 0; n < 10000; n++) { // TODO: don't need this check anymore
    i = findCodeTokens(tok, i+1, ignoreCase, toks, condition);
    if (i < 0)
      return anyChange;
    List<String> subList = tok.subList(i-1, i+lTokin-1); // N to N
    String expansion = jreplaceExpandRefs(out, subList);
    int end = i+lTokin-2;
    
    jreplace_performing(tok, i, end, expansion);
    
    clearAllTokens(tok, i, end); // C to C
    tok.set(i, expansion);
    if (reTok) // would this ever be false??
      reTok(tok, i, end);
    i = end;
    anyChange = true;
  }
  throw fail("woot? 10000! " + quote(in) + " => " + quote(out));
}

static boolean jreplace_debug = false;


static void tok_selfType(List<String> tok) {
  int i;
  mainLoop: while ((i = jfind(tok, "selfType <id>")) >= 0) {
    // Now find class name by going backwards.
    
    int j = i, level = 1;
    while (j > 0 && level > 0) {
      String t = tok.get(j);
      if (t.equals("}")) ++level;
      if (t.equals("{")) --level;
      j -= 2;
    }
    
    // search for class name
    while (j > 0) {
      String t = tok.get(j);
      if (t.equals("class")) {
        String className = tok.get(j+2);
        tok.set(i, className);
        
        continue mainLoop;
      }
      j -= 2;
    }
    tok.set(i, "Object"); // avoid endless loop
  }
}


// extra commas - e.g. ll(1, 2,) => ll(1, 2)
static void tok_dropExtraCommas(List<String> tok) {
  jreplace(tok, ",)", ")");
}


static void tok_delegateTo(List<String> tok) {
  jreplace(tok, "delegate <id> to <id>.", "replace $2 with $4.$2.");
  jreplace(tok, "delegate <id> to <id>().", "replace $2 with $4().$2.");
  // TODO: general expressions in "to" clause
}



static void tok_replaceWith(List<String> tok) {
  int i;
  while  ((i = jfind(tok, "replace <id> with")) >= 0) { ping(); 
    String token = tok.get(i+2);
    int repStart = i+6;
    int repEnd = repStart;
    
    // Find . with space or line break or EOF afterwards
    while  (repEnd < l(tok) && !(
      eqGet(tok, repEnd, ".") && // end when there is a dot
      (nempty(get(tok, repEnd+1)) || repEnd == l(tok)-2))) // ...and it's the end of the text OR there is a space or newline after the dot
      { ping(); repEnd += 2; }
    print("tok_replaceWith: Found " + joinSubList(tok, repStart, repEnd));
    //int repEnd = smartIndexOf(tok, repStart, ".");

    String replacement = joinSubList(tok, repStart, repEnd-1);
    clearTokens(tok, i, repEnd+1);
    print("Replacing " + token + " with " + replacement + ".");
    int end = findEndOfBlock(tok, repEnd)-1;
    for  (int j = repEnd+2; j < end; j += 2)
      { ping(); if (eq(tok.get(j), token)) tok.set(j, replacement); }
    reTok(tok, i, end);
  }
}



// example:
// sS loadTextFile(File/S file) { ... }

static void tok_multiTypeArguments_v2(List<String> tok) {
  int i;
  
  // File/S
  
  while ((i = jfind(tok, "File/<id> <id>", (_tok, nIdx) ->
    eqGetOneOf(_tok, nIdx+5, "S", "String"))) >= 0) {
      
    String argName = tok.get(i+6);
    String mainType = tok.get(i), altType = tok.get(i+4);
    String converter = "newFile";
    String expr = converter + "(" + argName + ")";
    
    tok_expandMultiTypeArgument(tok,
      i, i+8,
      argName,
      mainType, altType, expr);
  }
  
  // Graphics2D etc, IIntegralImage etc, ...
  
  while ((i = jfind(tok, "<id> etc <id>")) >= 0) {
    String mainType = tok.get(i);
    String argName = tok.get(i+4);
    int iArgEnd = i+6;
 
    List<Pair<String, String>> alts = new ArrayList(); // altType, converter
    if (eq(mainType, "Graphics2D"))
      alts.add(pair("BufferedImage", "graphics"));
    else if (eq(mainType, "IIntegralImage")) {
      alts.add(pair("BufferedImage", "IntegralImage"));
      alts.add(pair("MakesBufferedImage", "IntegralImage"));
    } else if (eq(mainType, "BufferedImage"))
      alts.add(pair("MakesBufferedImage", "toBufferedImage"));
    else if (eq(mainType, "Pt")) {
      alts.add(pair(("int " + argName + "_x, int " + argName + "_y"),
        ("pt(" + argName + "_x, " + argName + "_y)")));
      tok_expandMultiTypeArgument_v3(tok,
        i, iArgEnd,
        argName,
        mainType, alts);
      continue;
    }
    
    if (empty(alts))
      throw fail("Unknown multi-type: " + joinSubList(tok, i, iArgEnd));
      
    alts = mapPairB(alts, converter -> converter + "(" + argName + ")");
    
    tok_expandMultiTypeArgument_v2(tok,
      i, iArgEnd,
      argName,
      mainType, alts);
  }  
  
  // Iterable<X> or X[], Iterable<> or X...
  
  IntRange r;
  for (String modifier : ll("[]", "..."))
    while ((r = jfind_range(tok, "Iterable<<id>> or <id>" + modifier + " <id>")) != null) {
      i = r.start+1;
      String mainType = joinSubList(tok, i, i+7);
      String altType = joinSubList(tok, i+10, r.end-3);
      String argName = tok.get(r.end-2);
      String converter = "asList";
      String expr = converter + "(" + argName + ")";
      
      tok_expandMultiTypeArgument(tok, i, r.end, argName, mainType, altType, expr);
    }
    
  // Iterable or O[], Iterable or X..., Cl/O[], etc
    
  for (String modifier : ll("[]", "..."))
    while ((r = jfind_range(tok, "<id> * *" + modifier + " <id>", new F2<List<String>, Integer, Object>() { public Object get(List<String> tok, Integer nIdx) { try { 
      return eqOneOf(tok.get(nIdx+1), "Iterable", "Collection", "Cl", "List", "L")
      && eqGetOneOf(tok, nIdx+3, "or", "/")
      && eqGetOneOf(tok, nIdx+5, "O", "Object");
     } catch (Exception __e) { throw rethrow(__e); } }
  public String toString() { return "eqOneOf(tok.get(nIdx+1), \"Iterable\", \"Collection\", \"Cl\", \"List\", \"L\")\r\n      ..."; }})) != null) {
      i = r.start+1;
      String mainType = tok.get(i);
      String altType = joinSubList(tok, i+4, r.end-3);
      String argName = tok.get(r.end-2);
      String converter = "asList";
      String expr = converter + "(" + argName + ")";
      
      tok_expandMultiTypeArgument(tok, i, r.end, argName, mainType, altType, expr);
    }
}


// goes over input only once (doesn't start again at 1 like jreplace_dyn)

static String jreplace_dyn_allowNull(String s, String in, TokReplacer replacer) { return jreplace_dyn_allowNull(s, in, replacer, null); }
static String jreplace_dyn_allowNull(String s, String in, TokReplacer replacer, ITokCondition condition) {
  List<String> tok = javaTok(s);
  jreplace_dyn_allowNull(tok, in, replacer, condition);
  return join(tok);
}

static boolean jreplace_dyn_allowNull(List<String> tok, String in, TokReplacer replacer) { return jreplace_dyn_allowNull(tok, in, replacer, null); }
static boolean jreplace_dyn_allowNull(List<String> tok, String in, TokReplacer replacer, ITokCondition condition) {
  return jreplace_dyn_allowNull(tok, in, replacer, condition, false, true);
}

static boolean jreplace_dyn_allowNull(List<String> tok, String in, TokReplacer replacer, ITokCondition condition, boolean ignoreCase, boolean reTok) {
  List<String> tokin = javaTok(in);
  jfind_preprocess(tokin);
  String[] toks = toStringArray(codeTokensOnly(tokin));  

  boolean anyChange = false;
  int i = 0;
  for (int safety = 0; safety < 10000; safety++) {
    ping();
    
    i = findCodeTokens(tok, i, ignoreCase, toks, condition);
    if (i < 0) return anyChange;
    int start = i, end = i+l(tokin)-2;
    i = end+2;
    String expansion = replacer.get(tok, start, end);
    if (expansion != null) {
      clearAllTokens(tok, start, end); // C to C
      
      tok.set(start, expansion);
      if (reTok) { // would this ever be false??
        int n = l(tok);
        reTok(tok, start, end);
        i += l(tok)-n; // adjust for replacement
      }
      anyChange = true;
    }
  }
  throw fail("woot? 10000! " + quote(in) + " => " + replacer);
}


// Use like this: printVars(+x, +y);
// Or: printVars("bla", +x);
// Or: printVars bla(, +x);
static void printVars(Object... params) {
  printVars_str(params);
}


static boolean eqGet(List l, int i, Object o) {
  return eq(get(l, i), o);
}

static <A, B> boolean eqGet(Map<A, B> map, A key, Object o) {
  return eq(mapGet(map, key), o);
}


static RuntimeException rethrow(Throwable t) {
  
  if (t instanceof Error)
    _handleError((Error) t);
  
  throw t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t);
}

static RuntimeException rethrow(String msg, Throwable t) {
  throw new RuntimeException(msg, t);
}


static void tok_collectMetaPostBlocks(List<String> tok, List<String> postProcessBlocks_out) {
  int i = -1;
  while ((i = jfind(tok, i+1, "meta-postProcess {")) >= 0) {
    int iOpening = indexOf(tok, i, "{");
    int iClosing = findEndOfBracketPart(tok, iOpening)-1;
    add(postProcessBlocks_out, joinSubList(tok, iOpening+2, iClosing-1));
    clearTokens_reTok(tok, i, iClosing+1);
  }
}


static void tok_collectTransformers(List<String> tok, List<String> out) {
  int i = -1;
  while ((i = jfind(tok, i+1, "meta-transform {")) >= 0) {
    int iOpening = indexOf(tok, i, "{");
    int iClosing = findEndOfBracketPart(tok, iOpening)-1;
    add(out, joinSubList(tok, iOpening+2, iClosing-1));
    clearTokens_reTok(tok, i, iClosing+1);
  }
}


static long now_virtualTime;
static long now() {
  return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis();
}



static boolean eq(Object a, Object b) {
  return a == b || a != null && b != null && a.equals(b);
}


// a little kludge for stuff like eq(symbol, "$X")
static boolean eq(Symbol a, String b) {
  return eq(str(a), b);
}



static String jreplace1(String s, String in, String out) {
  return jreplace1(s, in, out, null);
}

static String jreplace1(String s, String in, String out, Object condition) {
  List<String> tok = javaTok(s);
  return jreplace1(tok, in, out, condition) ? join(tok) : s;
}

// leaves tok properly tokenized
// returns true iff anything was replaced
static boolean jreplace1(List<String> tok, String in, String out) {
  return jreplace1(tok, in, out, false, true, null);
}

static boolean jreplace1(List<String> tok, String in, String out, Object condition) {
  return jreplace1(tok, in, out, false, true, condition);
}

static boolean jreplace1(List<String> tok, String in, String out, boolean ignoreCase, boolean reTok, Object condition) {
  List<String> tokin = javaTok(in);
  jfind_preprocess(tokin);

  boolean anyChange = false;
  int i = 1;
  while ((i = findCodeTokens(tok, i, ignoreCase, toStringArray(codeTokensOnly(tokin)), condition)) >= 0) {
    List<String> subList = tok.subList(i-1, i+l(tokin)-1); // N to N
    String expansion = jreplaceExpandRefs(out, subList);
    int end = i+l(tokin)-2;
    clearAllTokens(tok, i, end); // C to C
    tok.set(i, expansion);
    if (reTok) // would this ever be false??
      reTok(tok, i, end);
    i = end;
    anyChange = true;
  }
  return anyChange;
}

static boolean jreplace1_debug = false;


// out: func(L<S> tok, int cIndex) -> S
// condition: func(L<S> tok, int nIndex) -> S  [yeah it's inconsistent]
static String jreplace_dyn(String s, String in, Object out) {
  return jreplace_dyn(s, in, out, null);
}

static String jreplace_dyn(String s, String in, Object out, Object condition) {
  List<String> tok = javaTok(s);
  jreplace_dyn(tok, in, out, condition);
  return join(tok);
}

// leaves tok properly tokenized
// returns true iff anything was replaced
static boolean jreplace_dyn(List<String> tok, String in, Object out) {
  return jreplace_dyn(tok, in, out, false, true, null);
}

static boolean jreplace_dyn(List<String> tok, String in, IF2<List<String>, Integer, String> out) {
  return jreplace_dyn(tok, in, (Object) out);
}

static boolean jreplace_dyn(List<String> tok, String in, IF2<List<String>, Integer, String> out, IF2<List<String>, Integer, Boolean> condition) {
  return jreplace_dyn(tok, in, (Object) out, (Object) condition);
}

static boolean jreplace_dyn(List<String> tok, String in, Object out, Object condition) {
  return jreplace_dyn(tok, in, out, false, true, condition);
}

static boolean jreplace_dyn(List<String> tok, String in, Object out, boolean ignoreCase, boolean reTok, Object condition) {
  List<String> tokin = javaTok(in);
  jfind_preprocess(tokin);
  
  String[] toks = toStringArray(codeTokensOnly(tokin));

  boolean anyChange = false;
  for (int n = 0; n < 10000; n++) {
    int i = findCodeTokens(tok, 1, ignoreCase, toks, condition);
    if (i < 0)
      return anyChange;
    String expansion =  (String) (callF(out, tok, i));
    int end = i+l(tokin)-2;
    clearAllTokens(tok, i, end); // C to C
    tok.set(i, expansion);
    if (reTok) // would this ever be false??
      reTok(tok, i, end);
    anyChange = true;
  }
  throw fail("woot? 10000! " + quote(in) + " => " + quote(out));
}


static String stringToLegalIdentifier(String s) {
  StringBuilder buf = new StringBuilder();
  s = dropTrailingSquareBracketStuff(s);
  for (int i = 0; i < l(s); i++) {
    char c = s.charAt(i);
    if (empty(buf) ? Character.isJavaIdentifierStart(c) : Character.isJavaIdentifierPart(c))
      buf.append(c);
  }
  if (empty(buf)) throw fail("Can't convert to legal identifier: " + s);
  return str(buf);
}


static String getSnippetTitle(String id) {
  if (id == null) return null;
  if (!isSnippetID(id)) return "?";
  
  
  IResourceLoader rl = vm_getResourceLoader();
  if (rl != null)
    return rl.getSnippetTitle(id);
  
  
  return getSnippetTitle_noResourceLoader(id);
}
  
static String getSnippetTitle_noResourceLoader(String id) { try {
  if (isLocalSnippetID(id)) return localSnippetTitle(id);
  long parsedID = parseSnippetID(id);
  String url;
  if (isImageServerSnippet(parsedID))
    url = imageServerURL() + "title/" + parsedID + muricaCredentialsQuery();
  else if (isGeneralFileServerSnippet(parsedID))
    url = "http://butter.botcompany.de:8080/files/name/" + parsedID;
  else
    url = tb_mainServer() + "/tb-int/getfield.php?id=" + parsedID + "&field=title" + standardCredentials_noCookies();
  String title = trim(loadPageSilently(url));
  if (title != null)
    try { saveTextFileIfChanged(snippetTitle_cacheFile(id), title); } catch (Throwable __e) { print(exceptionToStringShort(__e)); }
  return or(title, "?");
} catch (Exception __e) { throw rethrow(__e); } }

static String getSnippetTitle(long id) {
  return getSnippetTitle(fsI(id));
}



static void tok_overridableFunctionDefs(List<String> tok, Set<String> overriableFunctions_out) {
  int i;
  while ((i = jfind(tok, "static overridable <id>")) >= 0) {
    int bracket = indexOf(tok, "(", i);
    int codeStart = indexOf(tok, "{", bracket);
    String fName = assertIdentifier(tok.get(bracket-2));
    String type = joinSubList(tok, i+4, bracket-3);
    String boxedType = tok_toNonPrimitiveTypes(type);
    String args = joinWithComma(map(__45 -> tok_lastIdentifier(__45), tok_parseArgsDeclList(subList(tok, i))));
    String castIt = eq(type, "Object") ? "" : ("(" + type + ")");

    replaceTokens(tok, i, bracket-1, ("static Object _override_" + fName + ";\n")
      + ("static " + type + " " + fName));
    
    tokAppend(tok, codeStart, (" if (_override_" + fName + " != null) return " + castIt + " callF(_override_" + fName + ", " + args + ");\n"));
    reTok(tok, i, codeStart+1);
    addToCollection(overriableFunctions_out, fName);
  }
}


static List<String> tok_cleanImports(List<String> tok) {
  List<String> index = toContentsIndexedList(tok);
  List<IntRange> imports = tok_findImports_returnRanges(tok);
  BitSet exclude = new BitSet();
  for (IntRange r : imports)
    set(exclude, r.end-4);
    
  List<IntRange> reToks = new ArrayList();
  loop: for (IntRange r : imports) {
    String id = get(tok, r.end-4);
    if (!isIdentifier(id)) continue;
    for (int i : indicesOf(index, id))
      if (!get(exclude, i))
        continue loop; // id is in use
    // import is unused - delete
    clearTokens_addToReToks(tok, r.start+1, r.end, reToks);
  }
  
  reTok_multi(tok, reToks);
  return tok;
}


static int lastIndexOfStartingWith(List<String> l, String s) {
  for (int i = l(l)-1; i >= 0; i--)
    if (startsWith(l.get(i), s))
      return i;
  return -1;
}


static String dropPrefix(String prefix, String s) {
  return s == null ? null : s.startsWith(prefix) ? s.substring(l(prefix)) : s;
}


static String sfu(Object o) { return structureForUser(o); }


// unclear semantics when l is a special set (e.g. ciSet)

static <A> boolean containsOneOf(Collection<A> l, A... x) {
  if (l instanceof Set) {
    if (x != null)
      for (A a : x)
        if (l.contains(a))
          return true;
  } else {
    for (A a : unnull(l))
      if (eqOneOf(a, x))
        return true;
  }
  return false;
}

static <A> boolean containsOneOf(Collection<A> l, Set<A> set) {
  if (set == null) return false;
  for (A a : unnull(l))
    if (set.contains(a))
      return true;
  return false;
}

static boolean containsOneOf(String s, String... x) {
  for (String o : x)
    if (contains(s, o)) return true;
  return false;
}


static boolean containsIC(Collection<String> l, String s) {
  return containsIgnoreCase(l, s);
}

static boolean containsIC(String[] l, String s) {
  return containsIgnoreCase(l, s);
}

static boolean containsIC(String s, char c) {
  return containsIgnoreCase(s, c);
}

static boolean containsIC(String a, String b) {
  return containsIgnoreCase(a, b);
}


static boolean containsIC(Producer<String> p, String a) {
  if (p != null && a != null) while (true) {
    String x = p.next();
    if (x == null) break;
    if (eqic(x, a)) return true;
  }
  return false;
}



static String extractAndPrintJavaParseError(String src, Throwable e) {
  StringBuilder buf = new StringBuilder();
  print_tee(buf);
  String msg = takeFirstLines(2, innerMessage(e));
  print_tee(buf, msg);
  int line = parseIntOpt(regexpFirstGroupIC("line (\\d+)", msg));
  print_tee(buf);
  if (line != 0) {
    List<String> lines = lines(src);
    for (int i = max(1, line-5); i <= min(l(lines), line+5); i++)
      print_tee(buf, (i == line ? "* " : "  ") + "LINE " + i + ": " + lines.get(i-1));
  }
  print_tee(buf);
  return str(buf);
}


static File transpilerErrorSourceFile() {
  return javaxCachesDir("error-source.java");
}


static void saveTextFileVerbose(File f, String text) {
  boolean exists = f.exists();
  saveTextFile(f, text);
  print((!exists ? "Created" : "Updated") + " file " + f2s(f) + " (" + f.length() + " bytes)");
}


static String innerMessage(Throwable e) {
  return getInnerMessage(e);
}


static void tokPrepend(List<String> tok, String s) { tokPrepend(tok, 0, s); }
static void tokPrepend(List<String> tok, int i, String s) {
  tok.set(i, s + tok.get(i));
}


static List<String> reTok(List<String> tok) {
  replaceCollection(tok, javaTok(tok));
  return tok;
}

static List<String> reTok(List<String> tok, int i) {
  return reTok(tok, i, i+1);
}

static List<String> reTok(List<String> tok, int i, int j) {
  // extend i to an "N" token
  // and j to "C" (so j-1 is an "N" token)
  i = max(i & ~1, 0);
  j = min(l(tok), j | 1);
  if (i >= j) return tok;
  
  List<String> t = javaTok(joinSubList(tok, i, j));
  replaceListPart(tok, i, j, t);
  
  // fallback to safety
  // reTok(tok);
  
  return tok;
}


static List<String> reTok(List<String> tok, IntRange r) {
  if (r != null) reTok(tok, r.start, r.end);
  return tok;
}



static boolean nempty(Collection c) {
  return !empty(c);
}

static boolean nempty(CharSequence s) {
  return !empty(s);
}

static boolean nempty(Object[] o) { return !empty(o); }
static boolean nempty(byte[] o) { return !empty(o); }
static boolean nempty(int[] o) { return !empty(o); }

static boolean nempty(Map m) {
  return !empty(m);
}

static boolean nempty(Iterator i) {
  return i != null && i.hasNext();
}


static boolean nempty(Object o) { return !empty(o); }



static boolean nempty(IntRange r) { return !empty(r); }











static String concatMap_strings(Object f, Iterable l) {
  return join((List<String>) map(f, l));
}

static String concatMap_strings(Object f, Object[] l) {
  return join((List<String>) map(f, l));
}

static String concatMap_strings(Iterable l, Object f) {
  return concatMap_strings(f, l);
}

static <A> String concatMap_strings(Iterable<A> l, IF1<A, String> f) {
  return concatMap_strings(f, l);
}

static <A> String concatMap_strings(IF1<A, String> f, Iterable<A> l) {
  return concatMap_strings((Object) f, l);
}


static String f2s(File f) {
  return f == null ? null : f.getAbsolutePath();
}

static String f2s(String s) { return f2s(newFile(s)); }


 static String f2s(java.nio.file.Path p) {
  return p == null ? null : f2s(p.toFile());
}



static File saveProgramTextFile(String name, String contents) {
  return saveTextFile(getProgramFile(name), contents);
}

static File saveProgramTextFile(String progID, String name, String contents) {
  return saveTextFile(getProgramFile(progID, name), contents);
}


static void splitJavaFiles(List<String> tok) { try {
  splitJavaFiles(tok, newFile("output"));
} catch (Exception __e) { throw rethrow(__e); } }

static void splitJavaFiles(List<String> tok, File outDir) { try {
  List<Integer> indexes = jfindAll(tok, "package");
  if (empty(indexes) || indexes.get(0) != 1)
    indexes.add(0, 1);
  for (int i = 0; i < l(indexes); i++) {
    int from = indexes.get(i);
    int to = i+1 < l(indexes) ? indexes.get(i+1) : l(tok);
    List<String> subtok = cncSubList(tok, from, to);
    String src = join(subtok);
    //print(shorten(src, 80));
    String pack = tok_packageName(subtok);
    print("Package: " + quote(pack));
    List<List<String>> classes = allClasses(subtok);
    /*for (L<S> c : classes) {
      //print("  Class: " + shorten(join(c), 80));
      print("  Class: " + quote(getClassDeclarationName(c)));
    }*/
    if (empty(classes))
      print("No classes?? " + quote(src));
    else {
      String className = getNameOfPublicClass(subtok);
      if (className == null) className = getNameOfAnyClass(subtok);

      String fileName = addSlash(pack.replace('.', '/')) + className + ".java";
      print("File name: " + fileName);
      saveTextFile(newFile(outDir, fileName), join(subtok));
    }
    print();
  }
} catch (Exception __e) { throw rethrow(__e); } }


  static void saveMainJava(String s) throws IOException {
    if (mainJava != null)
      mainJava = s;
    else
      saveTextFile("output/main.java", s);
  }
  
  static void saveMainJava(List<String> tok) throws IOException {
    saveMainJava(join(tok));
  }



// process scope x. ... end scope blocks
static void tok_scopes(List<String> tok, Object... __) {
  if (!tok.contains("scope")) return;
  boolean autoCloseScopes = boolPar("autoCloseScopes", __);
  
  // New syntax: scope bla { ... }
  replaceKeywordBlock(tok, "scope <id>",
    "scope $2. ",
    " end scope ");
  
  int i;
  // Old syntax: scope bla ... end scope
  while ((i = rjfind(tok, "scope <id>")) >= 0) {
    String scopeName = tok.get(i+2);
    int start = i+4;
    if (eqGet(tok, start, ".")) start += 2;
    int j = jfind(tok, start, "end scope");
    if (j < 0)
      if (autoCloseScopes)
        j = l(tok);
      else
        throw fail("Scope " + scopeName + " opened but not closed");
    else
      clearTokens(tok, j, j+3);

    HashSet<String> names = new HashSet();
    HashSet<String> functions = new HashSet();
    
    clearTokens(tok, i, start-1);
    List<String> subTok = subList(tok, start-1, j);
    
    // auto-make #lock variable
    if (jfind(subTok, "lock #lock") >= 0
      && jfind(subTok, "Lock #lock") < 0)
        tok.set(i, "static Lock " + scopeName + "_lock = lock();\n");
    
    // first pass (find # declarations)
    for (int k = start; k < j-2; k += 2) {
      String t = get(tok, k+2);
      if (eqGet(tok, k, "#") && isIdentifier(t)) {
        names.add(t);
        if (eqGetOneOf(tok, k+4, "(", "{", "<", "extends", "implements", ">")) // cover class declarations too
          functions.add(t); 
        replaceTokens(tok, k, k+3, scopeName + "_" + t);
      }
    }
    
    // second pass (references to scoped variables)
    for (int k = start; k < j; k += 2) {
      String t = get(tok, k);
      if (isIdentifier(t)) {
        if (names.contains(t)) {
          if (eqGet(tok, k-2, ".")) {}
          else if (eqGet(tok, k+2, "(") && !functions.contains(t)) {}
          // avoid lock ... and map ...
          else if (eqOneOf(t, "lock", "map") && isIdentifier(get(tok, k+2))) {}
          else
            tok.set(k, scopeName + "_" + t);
        } else if (eq(t, "__scope"))
          tok.set(k, quote(scopeName));
      }
    }
    
    reTok(tok, i, j+1);
  }
}


static void tok_dropMetaComments(List<String> tok) {
  int i = -1;
  while ((i = jfind(tok, i+1, "meta-comment {")) >= 0) {
    int iOpening = indexOf(tok, i, "{");
    int iClosing = findEndOfBracketPart(tok, iOpening)-1;
    clearTokens_reTok(tok, i, iClosing+1);
  }
}


// TODO: sometimes equals/hashCode is not generated. See an old version of #1026301

static boolean tok_recordDecls_autoWithToList = true;

static void tok_recordDecls(List<String> tok) {
  int i;
  boolean re = false;
  
  jreplace(tok, "record <id> > <id> {", "record $2 extends $4 {");
  jreplace(tok, "record <id> > <id><<id>> {", "record $2 extends $4  $5 $6 $7 {");
  
  jreplace(tok, "record <id> {", "record $2() {");
  jreplace(tok, "record <id> implements", "record $2() implements");
  jreplace(tok, "record <id> extends", "record $2() extends");
  
  while ((i = jfind_any(tok, "record <id>(", "record <id><")) >= 0) {
    int argsFrom = smartIndexOf(tok, i, "(")+2;
    int argsTo = findCodeTokens(tok, argsFrom, false, ")");
    int idx = findCodeTokens(tok, argsTo, false, "{");
    
    int j = findEndOfBracketPart(tok, idx);
    String name = tok.get(i+2);
    
    int iMod = tok_leftScanCustomModifiers(tok, i, addAllAndReturnCollection(litset("noeq", "flexeq", "noToString", "withToList", "transformable"), getJavaModifiers()));
    Set<String> mods = codeTokensAsSet(subList(tok, iMod-1, i));
    clearTokensExceptFromSet(tok, iMod, i-1, getJavaModifiers());

    boolean withToList = mods.contains("withToList");
    withToList = withToList || tok_recordDecls_autoWithToList;
    
    StringBuilder buf = new StringBuilder();
    
    List<String> tArgs = subList(tok, argsFrom-1, argsTo);
    List<Pair<String, String>> args = tok_typesAndNamesOfParams(tArgs, "typelessMeansObject" , true);
    List<String> contents = subList(tok, idx+1, j);
    boolean hasDefaultConstructor = tok_hasDefaultConstructor(contents, name);
    
    for (Pair<String, String> p : args)
      buf.append("\n  " + jreplace(p.a, "...", "[]") + " " + p.b + ";");
      
    if (nempty(args) && !hasDefaultConstructor) buf.append("\n  *() {}");
    buf.append("\n  *(" + joinWithComma(map(args, new F1<Pair<String, String>, String>() { public String get(Pair<String, String> p) { try { 
      return dropPrefix("new ", p.a) + " *" + p.b;  } catch (Exception __e) { throw rethrow(__e); } }
  public String toString() { return "dropPrefix(\"new \", p.a) + \" *\" + p.b"; }})) + ") {}");
      
    if (!mods.contains("noToString") && !(jfind(contents, "toString {") >= 0 || jfind(contents, "toString()") >= 0))
      buf.append("\n  toString { ret " 
        + "shortClassName_dropNumberPrefix(this) + \"(\" + "
        + join(" + \", \" + ", secondOfPairs(args))
        + " + \")\"; }");
      
    if (!mods.contains("noeq")) {
      //buf.append("\n  [stdEq]");
      boolean flexEq = mods.contains("flexeq"); // fix for records inside of parameterized classes
      buf.append(tok_genEqualsAndHashCode(name, args, "flexEq", flexEq));
    }
    
    if (withToList)
      buf.append(tok_genRecordFieldsToList(args));
      
    boolean transformable = mods.contains("transformable");
    if (transformable)
      buf.append(tok_genRecordTransformable(name, args));
      
    String interfaces = joinNemptiesWithComma(
      withToList ? "IFieldsToList" : "",
      transformable ? "Transformable, Visitable" : "");

    tok.set(idx, (empty(interfaces) ? ""
      : (contains(subList(tok, argsTo, idx), "implements") ? "," : "implements") + " " + interfaces)
      + "{" + buf);
    
    tok.set(i, "class");
    clearTokens(tok, argsFrom-2, argsTo+1);
    reTok(tok, i, idx+1);
    
    // no static fields allowed in non-static classes (for whatever reason)
    if (contains(tok_modifiersLeftOf(tok, i), "static"))
      tok_addFieldOrder(tok, i);

    
    re = true;
  }
  if (re) reTok(tok);
}


static void tok_singleQuoteIdentifiersToStringConstants(List<String> tok) {
  for (int i = 1; i < l(tok); i += 2) {
    String t = tok.get(i);
    if (isSingleQuoteIdentifier(t))
      tok.set(i, quote(substring(t, 1)));
  }
}


static void tok_inStringEvals(List<String> tok) {
  boolean change = false;
  for (int i = 1; i < tok.size(); i += 2) {
    String t = tok.get(i);
    if (!isQuoted(t)) continue;
    if (t.contains("\\*") && !t.contains("\\\\")) { // << rough
      tok.set(i, transpile_inStringEval(t));
      change = true;
    }
  }
  if (change) reTok(tok);
}


static void tok_listComprehensions(List<String> tok) {
  if (!tok.contains("[")) return;
  for (int i = 1; i < l(tok); i += 2) try {
    { if (!(eq(tok.get(i), "["))) continue; }
    int iOp = indexOfAny(tok, i+2, "?", ":", "in", "[", "]");
    if (iOp < 0) return;
    if (!eqOneOf(tok.get(iOp), ":", "in")) continue; // not a list comprehension
    if (eqGet(tok, iOp+2, ".")) continue; // "in." (in is a variable name)
    
    Map<Integer, Integer> bracketMap = getBracketMap(tok); // XXX - optimize
    
    String type = joinSubList(tok, i+2, iOp-3), id = tok.get(iOp-2);
    int j = scanOverExpression(tok, bracketMap, iOp+2, "|");
    String exp = join(subList(tok, iOp+2, j));
    j += 2;
    int k = scanOverExpression(tok, bracketMap, j, "]");
    String where = join(subList(tok, j, k));
    ++k;
      
    String code = "filter(" + exp + ", func(" + type + " " + id + ") -> Bool { " + where + " })";
    replaceTokens(tok, i, k, code);
    reTok(tok, i, k);
  } catch (Throwable _e) {
    print("Was processing list comprehension: " + joinSubList(tok, i, i+30) + "...");
  
throw rethrow(_e); }
}




/* event change; =>
   transient L<Runnable> onChange;
   selfType onChange(Runnable r) { onChange = syncAddOrCreate(onChange, r); this; }
   void change() { pcallFAll(onChange); }
*/
static void tok_eventFunctions(List<String> tok) {
  int i;

  while ((i = jfind(tok, "event <id>")) >= 0) {
    int iSemicolon = i+4;
    List<String> tokArgs = null;
    if (eqGet(tok, i+4, "(")) {
      int argsFrom = i+6;
      int argsTo = findCodeTokens(tok, argsFrom, false, ")");
      tokArgs = subList(tok, argsFrom-1, argsTo);
      iSemicolon = argsTo+2;
    }
    // TODO for next version: parse modifiers in square brackets
    //if (eqGet(tok, iSemicolon, "["))
    
    if (neqGet(tok, iSemicolon, ";"))
      throw fail("Semicolon expected at end: " + joinSubList(tok, i, iSemicolon+1));
    
    String change = tok.get(i+2);
    String onChange = "on" + firstToUpper(change);
    List<Pair<String, String>> args = tok_typesAndNamesOfParams(tokArgs);
    List<String> types = pairsA(args);
    String args1 = join(dropFirstAndLast(tokArgs));
    String args2 = joinWithComma(pairsB(args));
    String typeParams = joinWithComma(map(__46 -> tok_toNonPrimitiveTypes(__46), types));     String listenerType = empty(args) ? "Runnable" : "IVF" + l(args) + "<" + typeParams + ">";
    String r = empty(args) ? "r" : "f";
   
    replaceTokens_reTok(tok, i, iSemicolon+1, 
      ("transient L<" + listenerType + "> " + onChange + ";\n") +
      ("selfType " + onChange + "(" + listenerType + " " + r + ") { " + onChange + " = syncAddOrCreate(" + onChange + ", " + r + "); this; }\n") +
      ("void " + change + "(" + args1 + ") { pcallFAll(" + (joinNemptiesWithComma(onChange, args2)) + "); }")
    );
  }
}


// for single () iterates once when the expression is not null
// and zero times when it is null.
// It was actually used once.
static void tok_for_single(List<String> tok) {
  int i = -1;
  while ((i = jfind(tok, i+1, "for single (")) >= 0) {
    int iColon = indexOf(tok, ":", i);
    int iClosing = findEndOfBracketPart(tok, iColon)-1;
    tok.set(iColon, ": singletonUnlessNull(");
    tok.set(iClosing, "))");
    clearTokens(tok, i+2, i+4);
    reTok(tok, i, iClosing+1);
  }
}


static void tok_for_unpair(List<String> tok) {
  jreplace(tok, "for (unpair <id> <id>, <id> :", "for (unpair $4 $5, $4 $7 :");
  jreplace(tok, "for (<id> <id>, <id> : unpair", "for (unpair $3 $4, $3 $6 :");
  jreplace(tok, "for (<id> <id>, <id> <id> : unpair", "for (unpair $3 $4, $6 $7 :");
  
  int i = -1;
  while ((i = jfind(tok, i+1, "for (unpair <id> <id>, <id> <id> :")) >= 0) {
    String type1 = tok.get(i+6), var1 = tok.get(i+8);
    String type2 = tok.get(i+12), var2 = tok.get(i+14);
    int iColon = indexOf(tok, ":", i);
    int iClosing = findEndOfBracketPart(tok, iColon)-1;
    int iCurly = iClosing+2;
    tok_statementToBlock(tok, iCurly);
    String pairVar = makeVar();
    replaceTokens(tok, i+4, iColon-1, tok_toNonPrimitiveTypes("Pair<" + type1 + ", " + type2 + ">") + " " + pairVar);
    tok.set(iCurly, "{ "
      + type1 + " " + var1 + " = pairA(" + pairVar + "); "
      + type2 + " " + var2 + " = pairB(" + pairVar + "); ");
    reTok(tok, i, iCurly+1);
  }
}


static boolean tok_doubleFor_v3_debug = false;

static void tok_doubleFor_v3(List<String> tok) {
  for (int i : reversed(indexesOf(tok, "for"))) {
    if (neq(get(tok, i+2), "(")) continue;
    int argsFrom = i+4;
    // move loop label to proper place
    if (eqGet(tok, i-2, ":") && isIdentifier(get(tok, i-4))) i -= 4;
    int iColon = indexOfAny(tok, argsFrom, ":", ")");
    if (neq(get(tok, iColon), ":")) continue;
    if (tok_doubleFor_v3_debug) print("have colon");
    tok_typesAndNamesOfParams_keepModifiers.set(true);
    List<Pair<String, String>> args;
    try {
      args = tok_typesAndNamesOfParams(subList(tok, argsFrom-1, iColon-1));
    } catch (Throwable _e) {
      print("tok_doubleFor_v3: Skipping parsing complicated for statement (probably not a double for)");
      continue;
    }
    
    if (l(args) != 2) continue; // simple for or a three-argument for (out of this world!)
    
    // S a, b => S a, S b
    if (eq(second(args).a, "?")) second(args).a = first(args).a;
    
    if (tok_doubleFor_v3_debug) print("have 2 args: " + sfu(args));
    int iClosing = tok_findEndOfForExpression(tok, iColon+2);
    if (iClosing < 0) continue;
    if (tok_doubleFor_v3_debug) print("have closing");
    String exp = trimJoinSubList(tok, iColon+2, iClosing-1);
    if (tok_doubleFor_v3_debug) print("Expression: " + exp);
    int iCurly = iClosing+2;
    tok_statementToBlock(tok, iCurly);
    int iCurlyEnd = tok_findEndOfStatement(tok, iCurly)-1;
    if (iCurlyEnd < 0) continue;
    if (tok_doubleFor_v3_debug) print("have curly end");
    
    tokAppend(tok, iColon, " _entrySet(");
    tokPrepend(tok, iClosing, ")");
    
    String entryVar = makeVar();
    
    replaceTokens(tok, argsFrom, iColon-1,
      "Map.Entry<? extends " + tok_toNonPrimitiveTypes(first(first(args))) + ", ? extends "
        + tok_toNonPrimitiveTypes(first(second(args))) + "> " + entryVar);
      /*"Map.Entry<" + first(first(args)) + ", "
        + first(second(args)) + "> " + entryVar);*/
    
    tokAppend(tok, iCurly, " " + joinPairWithSpace(first(args)) + " = " + entryVar + ".getKey(); "
      + joinPairWithSpace(second(args)) + " = " + entryVar + ".getValue(); ");
    reTok(tok, i, iCurlyEnd+1);
  }
}


static void tok_forUnnull(List<String> tok) {
  jreplace(tok, "fOr (", "for unnull (");
  
  int i = -1;
  while ((i = jfind(tok, i+1, "for unnull (")) >= 0) {
    int argsFrom = i+4;
    int iColon = indexOf(tok, i, ":");
    int iClosing = tok_findEndOfForExpression(tok, iColon+2);
    
    clearTokens(tok, i+2, i+4);
    tokPrepend(tok, iColon+2, "unnullForIteration(");
    tokPrepend(tok, iClosing, ")");
    reTok(tok, i+2, iClosing+1);
    
  }
}


static void tok_ifCast(List<String> tok) {
  int i;
  while ((i = jfind_check("cast", tok, "if (<id> cast <id>")) >= 0) {
    int iEndOfType = indexOfAny(tok, i, ")", "&");
    int iClosing = tok_endOfExpression(tok, i+4)+1;
    String var = tok.get(i+4), type = joinSubList(tok, i+8, iEndOfType-1);
    String rawType = tok_dropTypeParameters(type);
    int start = iClosing+2, end = tok_findEndOfStatement(tok, start);
    replaceTokens(tok, i+6, iEndOfType-1, "instanceof " + rawType);
    
    // replace "var" with "((Type) var)" in enclosed block
    // unless it's another cast expression or an assignment
    
    printVars("iEndOfType", iEndOfType, "iClosing", iClosing, "iClosing" , get(tok, iClosing), "start", start, "start" , get(tok, start), "end", end, "lTok" , l(tok));
    end = min(end, l(tok));
    
    for (int j = iEndOfType; j < end; j += 2)
      if (eq(tok.get(j), var) && neqGet(tok, j-2, ".")
        && neqGetOneOf(tok, j+2, "cast", "(")
        && !tok_isAssignment(tok, j+2))
        tok_insertCast(tok, j, type);
    reTok(tok, i+6, end);
  }
}


// # 123 => "#123"
static void tok_directSnippetRefs(List<String> tok) {
  int i = -1;
  while ((i = jfind(tok, i+1, "#<int>", new TokCondition() { public boolean get(final List<String> tok, final int i) {
    return !eqOneOf(_get(tok, i-1), "include", "once");
  }})) >= 0) {
    String id = tok.get(i+2);
    clearTokens(tok, i+1, i+3);
    tok.set(i, quote("#" + id));
    reTok(tok, i, i+3);
  }
}


static void tok_doPing(List<String> tok) {
  jreplace(tok, "do ping {", "do { ping();");
  
  int i;
  while ((i = jfind(tok, "do ping <id>")) >= 0) {
    int j = tok_findEndOfStatement(tok, i+4);
    tok.set(i+2, "{ ping();");
    tokAppend(tok, j-1, " }");
    reTok(tok, i, j);
  }
}


// keyword can comprise multiple tokens now (like "p-awt"}
static List<String> replaceKeywordBlock(List<String> tok, String keyword, String beg, String end) {
  return replaceKeywordBlock(tok, keyword, beg, end, false, null);
}

static List<String> replaceKeywordBlock(List<String> tok, String keyword, String beg, String end, Object cond) {
  return replaceKeywordBlock(tok, keyword, beg, end, false, cond);
}

static List<String> replaceKeywordBlock(List<String> tok, String keyword, String beg, String end,
  boolean debug, Object cond) {
  for (int n = 0; n < 1000; n++) {
    int i = jfind(tok, keyword + " {", cond);
    if (i < 0)
      break;
    int idx = findCodeTokens(tok, i, false, "{");
    int j = findEndOfBracketPart(tok, idx);
    
    if (debug) {
      print(toUpper(keyword) + " BEFORE\n" + join(subList(tok, i, j)));
      print("  THEN " + join(subList(tok, j, j+10)));
    }
    //assertEquals("}", tok.get(j-1));
    List<String> subList = subList(tok, i-1, idx); // N to somewhere
    tok.set(j-1, jreplaceExpandRefs(end, subList));
    replaceTokens(tok, i, idx+1, jreplaceExpandRefs(beg, subList));
    reTok(tok, i, j);
    if (debug) print(toUpper(keyword) + "\n" + join(subList(tok, i, j)) + "\n");
  }
  return tok;
}


// finds "<keyword> <quoted> {"
// func: tok, C token index of keyword -> S[] {beg, end}
static List<String> replaceKeywordPlusQuotedBlock(List<String> tok, String keyword, Object func) {
  for (int i = 0; i < 1000; i++) {
    int idx = jfind(tok, keyword + " <quoted> {");
    if (idx < 0)
      break;
    int j = findEndOfBracketPart(tok, idx+4);
    
    String[] be = (String[]) callF(func, tok, idx);
    tok.set(idx, be[0]);
    clearTokens(tok, idx+1, idx+5);
    tok.set(j-1, be[1]);
    reTok(tok, idx, j);
  }
  return tok;
}


static int jfind(String s, String in) {
  return jfind(javaTok(s), in);
}

static int jfind(List<String> tok, String in) {
  return jfind(tok, 1, in);
}

static int jfind(List<String> tok, int startIdx, String in) {
  return jfind(tok, startIdx, in, null);
}

static int jfind(List<String> tok, String in, Object condition) {
  return jfind(tok, 1, in, condition);
}

static int jfind(List<String> tok, String in, ITokCondition condition) { return jfind(tok, 1, in, condition); }
static int jfind(List<String> tok, int startIndex, String in, ITokCondition condition) {
  return jfind(tok, startIndex, in, (Object) condition);
}

static int jfind(List<String> tok, int startIdx, String in, Object condition) {
  //LS tokin = jfind_preprocess(javaTok(in));
  return jfind(tok, startIdx, javaTokForJFind_array(in), condition);
}

// assumes you preprocessed tokin
static int jfind(List<String> tok, List<String> tokin) {
  return jfind(tok, 1, tokin);
}

static int jfind(List<String> tok, int startIdx, List<String> tokin) {
  return jfind(tok, startIdx, tokin, null);
}

static int jfind(List<String> tok, int startIdx, String[] tokinC, Object condition) {
  return findCodeTokens(tok, startIdx, false, tokinC, condition);
}

static int jfind(List<String> tok, int startIdx, List<String> tokin, Object condition) {
  return jfind(tok, startIdx, codeTokensAsStringArray(tokin), condition);
}

static List<String> jfind_preprocess(List<String> tok) {
  for (String type : litlist("quoted", "id", "int"))
    replaceSublist(tok, ll("<", "", type, "", ">"), ll("<" + type + ">"));
  replaceSublist(tok, ll("\\", "", "*"), ll("\\*"));
  return tok;
}


// Return value is index of semicolon/curly brace+1
static int tok_findEndOfStatement(List<String> tok, int i) {
  // Is it a block?
  if (eq(get(tok, i), "{"))
    return findEndOfBlock(tok, i);
    
  // It's a regular statement. Special handling of "for" and "if"
  int j = i;
  boolean special = false;
  while (j < l(tok) && neq(tok.get(j), ";")) {
    String t = get(tok, j);
    if (eqOneOf(t, "for", "if")) special = true;
    if (eqOneOf(t, "{", "(")) {
      j = findEndOfBracketPart(tok, j)+1;
      if (special && eq(t, "{")) return j-1;
    } else
      j += 2;
  }
  return j+1;
}


static TokCondition tokCondition_beginningOfMethodDeclaration() {
  return new TokCondition() { public boolean get(final List<String> tok, final int i) {
    return eqOneOf(_get(tok, i-1), "}", ";", "{", null, ""); // "" is there to hopefully handle badly re-toked includes preceding the declaration
  }};
}


static boolean neqGet(List l, int i, Object o) {
  return neq(get(l, i), o);
}


static void tok_moduleClassDecls(List<String> tok) {
  jreplace(tok, "module <id> {", "module $2 extends DynModule {");
  jreplace(tok, "module {", "module " + stefansOS_defaultModuleClassName() + " {");
  jreplace(tok, "module > <id>", "module " + stefansOS_defaultModuleClassName() + " > $3");
  int i = -1;
  while ((i = jfind(tok, i+1, "module <id>")) >= 0) {
    int j = findEndOfBlock(tok, indexOf(tok, "{", i))-1;
    String name = tok.get(i+2);
    tok.set(i, "sclass");
    tokAppend(tok, j, "\nsbool _moduleClass_" + name + " = true;"); // just a marker to quickly find module classes
    reTok(tok, j, j+1);
  }
}



static <A> A _get(List<A> l, int idx) {
  return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null;
}

static Object _get(Object o, String field) {
  return get(o, field);
}

static Object _get(String field, Object o) {
  return get(o, field);
}
static <A> A _get(A[] l, int idx) {
  return idx >= 0 && idx < l(l) ? l[idx] : null;
}


// func : func(LS tok, int iOpening, int iClosing) -> S[] {beg, end}
static List<String> replaceKeywordBlock_dyn2_legacy(List<String> tok, String keyword, Object func) {
  for (int i = 0; i < 1000; i++) {
    //int idx = findCodeTokens(tok, keyword, "{");
    int idx = jfind(tok, keyword + " {");
    if (idx < 0)
      break;
    int idx2 = findCodeTokens(tok, idx, false, "{");
    int j = findEndOfBracketPart(tok, idx2);
    
    String[] be = (String[]) callF(func, tok, idx2, j-1);
    replaceTokens(tok, idx, idx2+2, be[0] + " ");
    tok.set(j-1, be[1]);
    reTok(tok, idx, j);
  }
  return tok;
}


static boolean startsWithLowerCaseOrUnderscore(String s) {
  return nempty(s) && (s.startsWith("_") || Character.isLowerCase(s.charAt(0)));
}


static boolean empty(Collection c) { return c == null || c.isEmpty(); }
static boolean empty(Iterable c) { return c == null || !c.iterator().hasNext(); }
static boolean empty(CharSequence s) { return s == null || s.length() == 0; }
static boolean empty(Map map) { return map == null || map.isEmpty(); }
static boolean empty(Object[] o) { return o == null || o.length == 0; }


static boolean empty(Object o) {
  if (o instanceof Collection) return empty((Collection) o);
  if (o instanceof String) return empty((String) o);
  if (o instanceof Map) return empty((Map) o);
  if (o instanceof Object[]) return empty((Object[]) o);
  if (o instanceof byte[]) return empty((byte[]) o);
  if (o == null) return true;
  throw fail("unknown type for 'empty': " + getType(o));
}


static boolean empty(Iterator i) { return i == null || !i.hasNext(); }

static boolean empty(double[] a) { return a == null || a.length == 0; }
static boolean empty(float[] a) { return a == null || a.length == 0; }
static boolean empty(int[] a) { return a == null || a.length == 0; }
static boolean empty(long[] a) { return a == null || a.length == 0; }
static boolean empty(byte[] a) { return a == null || a.length == 0; }
static boolean empty(short[] a) { return a == null || a.length == 0; }


static boolean empty(MultiSet ms) { return ms == null || ms.isEmpty(); }




static boolean empty(File f) { return getFileSize(f) == 0; }


static boolean empty(IntRange r) { return r == null || r.empty(); }











stat
[...]

Author comment

Began life as a copy of #759

full source  download  show line numbers  debug dex  old transpilations   

Travelled to 2 computer(s): bhatertpkbcr, mqqgnosmbjvj

No comments. add comment

-
Snippet ID: #1032684
Snippet name: #759 transpilation backup
Eternal ID of this version: #1032684/1
Text MD5: 7b7244b5016b2b09008f7304bfefdb62
Author: stefan
Category: javax
Type: JavaX source code (desktop)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-10-01 23:22:41
Source code size: 826505 bytes / 28369 lines
Pitched / IR pitched: No / No
Views / Downloads: 383 / 401
Referenced in: