static Object unstructure(String text) { ret unstructure(text, false); } static Object unstructure(String text, final boolean allDynamic) { ret unstructure(text, allDynamic, null); } static int structure_internStringsLongerThan = 50; // TODO: backrefs for hashmap{} etc.? // classFinder: name -> class static Object unstructure(String text, final boolean allDynamic, final O classFinder) { if (text == null) ret null; final L tok = javaTokC(text); final boolean debug = unstructure_debug; class X { int i = 0; new HashMap refs; new HashSet concepts; Object parse() { String t = tok.get(i); int refID = 0; if (t.startsWith("m") && isInteger(t.substring(1))) { refID = parseInt(t.substring(1)); i++; t = tok.get(i); } // if (debug) print("parse: " + quote(t)); if (t.startsWith("'")) { char c = unquoteCharacter(tok.get(i)); i++; return c; } if (t.equals("bigint")) return parseBigInt(); if (t.equals("d")) return parseDouble(); if (t.equals("fl")) return parseFloat(); if (t.equals("false") || t.equals("f")) { i++; return false; } if (t.equals("true") || t.equals("t")) { i++; return true; } if (t.equals("-")) { t = tok.get(i+1); i += 2; ret isLongConstant(t) ? (O) (-parseLong(t)) : (O) (-parseInt(t)); } if (isInteger(t) || isLongConstant(t)) { i++; //if (debug) print("isLongConstant " + quote(t) + " => " + isLongConstant(t)); if (isLongConstant(t)) ret parseLong(t); long l = parseLong(t); bool isInt = l == (int) l; if (debug) print("l=" + l + ", isInt: " + isInt); ret isInt ? (O) new Integer((int) l) : (O) new Long(l); } if (t.equals("File")) { File f = new File(unquote(tok.get(i+1))); i += 2; ret f; } if (t.startsWith("r") && isInteger(t.substring(1))) { i++; int ref = Integer.parseInt(t.substring(1)); O o = refs.get(ref); if (o == null) print("Warning: unsatisfied back reference " + ref); ret o; } O o = parse_inner(refID); if (refID != 0) refs.put(refID, o); ret o; } // everything that can be backreferenced O parse_inner(int refID) { String t = tok.get(i); // if (debug) print("parse_inner: " + quote(t)); if (t.startsWith("\"")) { String s = internIfLongerThan(unquote(tok.get(i)), structure_internStringsLongerThan); i++; return s; } if (t.equals("hashset")) return parseHashSet(); if (t.equals("treeset")) return parseTreeSet(); if (eqOneOf(t, "hashmap", "hm")) return parseHashMap(); if (t.equals("{")) return parseMap(); if (t.equals("[")) return parseList(); if (t.equals("bitset")) return parseBitSet(); if (t.equals("array") || t.equals("intarray")) return parseArray(); if (t.equals("ba")) { S hex = unquote(tok.get(i+1)); i += 2; ret hexToBytes(hex); } if (t.equals("boolarray")) { int n = parseInt(tok.get(i+1)); S hex = unquote(tok.get(i+2)); i += 6; ret boolArrayFromBytes(hexToBytes(hex), n); } if (t.equals("class")) return parseClass(); if (t.equals("l")) return parseLisp(); if (t.equals("null")) { i++; return null; } /* in dev. if (!allDynamic && t.equals("run")) { S snippetID = unquote(t.get(i+1)); i += 2; run( } */ bool concept = eq(t, "c"); if (concept) { consume("c"); t = tok.get(i); assertTrue(isJavaIdentifier(t)); concepts.add(t); } // any other class name if (isJavaIdentifier(t)) { // First, find class Class c; if (allDynamic) c = null; else if (classFinder != null) c = (Class) callF(classFinder, t); else c = findClass(t); // Second, check if it has an outer reference i++; bool hasOuter = eq(get(tok, i), "(") && eq(get(tok, i+1), "this$1"); DynamicObject dO = null; O o = null; if (c != null) o = hasOuter ? nuStubInnerObject(c) : nuEmptyObject(c); else { if (concepts.contains(t) && (c = findClass("Concept")) != null) o = dO = (DynamicObject) nuEmptyObject(c); else dO = new DynamicObject; dO.className = t; if (debug) print("Made dynamic object " + t + " " + shortClassName(dO)); } // Now, save in references list. if (refID != 0) refs.put(refID, o != null ? o : dO); // NOW parse the fields! new Map fields; if (i < tok.size() && tok.get(i).equals("(")) { consume("("); while (!tok.get(i).equals(")")) { String key = unquote(tok.get(i)); i++; consume("="); Object value = parse(); fields.put(key, value); if (tok.get(i).equals(",")) i++; } consume(")"); } if (o != null) if (dO != null) { if (debug) printStructure("setOptAllDyn", fields); setOptAllDyn(dO, fields); } else setOptAll(o, fields); else dO.fieldValues.putAll(fields); if (o != null) pcallOpt(o, "_doneLoading"); return o != null ? o : dO; } throw new RuntimeException("Unknown token " + (i+1) + ": " + t); } Object parseSet(Set set) { set.addAll((L) parseList()); return set; } Object parseLisp() { consume("l"); consume("("); new ArrayList list; while (!tok.get(i).equals(")")) { list.add(parse()); if (tok.get(i).equals(",")) i++; } consume(")"); return newObject("main$Lisp", (S) list.get(0), subList(list, 1)); } Object parseBitSet() { consume("bitset"); consume("{"); new BitSet bs; while (!tok.get(i).equals("}")) { bs.set((Integer) parse()); if (tok.get(i).equals(",")) i++; } consume("}"); return bs; } Object parseList() { consume("["); new ArrayList list; while (!tok.get(i).equals("]")) { O o = parse(); //if (debug) print("List element type: " + getClassName(o)); list.add(o); if (tok.get(i).equals(",")) i++; } consume("]"); return list; } Object parseArray() { S type = tok.get(i); i++; consume("{"); List list = new ArrayList; while (!tok.get(i).equals("}")) { list.add(parse()); if (tok.get(i).equals(",")) i++; } consume("}"); if (type.equals("intarray")) return toIntArray(list); return list.toArray(); } Object parseClass() { consume("class"); consume("("); S name = tok.get(i); i++; consume(")"); Class c = allDynamic ? null : findClass(name); if (c != null) ret c; new DynamicObject dO; dO.className = "java.lang.Class"; dO.fieldValues.put("name", name); ret dO; } Object parseBigInt() { consume("bigint"); consume("("); S val = tok.get(i); i++; if (eq(val, "-")) { val = "-" + tok.get(i); i++; } consume(")"); ret new BigInteger(val); } Object parseDouble() { consume("d"); consume("("); S val = unquote(tok.get(i)); i++; consume(")"); ret Double.parseDouble(val); } Object parseFloat() { consume("fl"); S val; if (eq(tok.get(i), "(")) { consume("("); val = unquote(tok.get(i)); i++; consume(")"); } else { val = unquote(tok.get(i)); i++; } ret Float.parseFloat(val); } Object parseHashMap() { i++; return parseMap(new HashMap); } Object parseHashSet() { consume("hashset"); return parseSet(new HashSet); } Object parseTreeSet() { consume("treeset"); return parseSet(new TreeSet); } Object parseMap() { return parseMap(new TreeMap); } Object parseMap(Map map) { consume("{"); while (!tok.get(i).equals("}")) { O key = parse(); consume("="); Object value = parse(); map.put(key, value); if (tok.get(i).equals(",")) i++; } consume("}"); return map; } void consume(String s) { if (!tok.get(i).equals(s)) { S prevToken = i-1 >= 0 ? tok.get(i-1) : ""; S nextTokens = join(tok.subList(i, Math.min(i+2, tok.size()))); fail(quote(s) + " expected: " + prevToken + " " + nextTokens + " (" + i + "/" + tok.size() + ")"); } i++; } } return new X().parse(); } static boolean unstructure_debug;