1 | static Object unstructure(String text) { |
2 | ret unstructure(text, false); |
3 | } |
4 | |
5 | static Object unstructure(String text, final boolean allDynamic) { |
6 | ret unstructure(text, allDynamic, null); |
7 | } |
8 | |
9 | static int structure_internStringsLongerThan = 50; |
10 | |
11 | // classFinder: func(name) -> class (optional) |
12 | static Object unstructure(String text, final boolean allDynamic, |
13 | final O classFinder) { |
14 | if (text == null) ret null; |
15 | final L<S> tok = javaTokC(text); |
16 | final boolean debug = unstructure_debug; |
17 | |
18 | class X { |
19 | int i = 0; |
20 | new HashMap<Integer, O> refs; |
21 | new HashMap<Integer, O> tokrefs; |
22 | new HashSet<S> concepts; |
23 | new HashMap<S, Class> classesMap; |
24 | new L<Runnable> stack; |
25 | |
26 | Object parse() { |
27 | String t = tok.get(i); |
28 | |
29 | int refID = 0; |
30 | if (structure_isMarker(t, 0, l(t))) { |
31 | refID = parseInt(t.substring(1)); |
32 | i++; |
33 | } |
34 | |
35 | // if (debug) print("parse: " + quote(t)); |
36 | |
37 | int tokIndex = i; |
38 | O o = parse_inner(refID, tokIndex); |
39 | if (refID != 0) |
40 | refs.put(refID, o); |
41 | if (o != null) |
42 | tokrefs.put(tokIndex, o); |
43 | ret o; |
44 | } |
45 | |
46 | O parse_inner(int refID, int tokIndex) { |
47 | String t = tok.get(i); |
48 | |
49 | // if (debug) print("parse_inner: " + quote(t)); |
50 | |
51 | Class c = classesMap.get(t); |
52 | if (c == null) { |
53 | if (t.startsWith("\"")) { |
54 | String s = internIfLongerThan(unquote(tok.get(i)), structure_internStringsLongerThan); |
55 | i++; |
56 | return s; |
57 | } |
58 | |
59 | if (t.startsWith("'")) |
60 | ret unquoteCharacter(tok.get(i++)); |
61 | if (t.equals("bigint")) |
62 | return parseBigInt(); |
63 | if (t.equals("d")) |
64 | return parseDouble(); |
65 | if (t.equals("fl")) |
66 | return parseFloat(); |
67 | if (t.equals("false") || t.equals("f")) { |
68 | i++; return false; |
69 | } |
70 | if (t.equals("true") || t.equals("t")) { |
71 | i++; return true; |
72 | } |
73 | if (t.equals("-")) { |
74 | t = tok.get(i+1); |
75 | i += 2; |
76 | ret isLongConstant(t) ? (O) (-parseLong(t)) : (O) (-parseInt(t)); |
77 | } |
78 | if (isInteger(t) || isLongConstant(t)) { |
79 | i++; |
80 | //if (debug) print("isLongConstant " + quote(t) + " => " + isLongConstant(t)); |
81 | if (isLongConstant(t)) ret parseLong(t); |
82 | long l = parseLong(t); |
83 | bool isInt = l == (int) l; |
84 | if (debug) |
85 | print("l=" + l + ", isInt: " + isInt); |
86 | ret isInt ? (O) new Integer((int) l) : (O) new Long(l); |
87 | } |
88 | |
89 | if (t.equals("File")) { |
90 | File f = new File(unquote(tok.get(i+1))); |
91 | i += 2; |
92 | ret f; |
93 | } |
94 | |
95 | if (t.startsWith("r") && isInteger(t.substring(1))) { |
96 | i++; |
97 | int ref = Integer.parseInt(t.substring(1)); |
98 | O o = refs.get(ref); |
99 | if (o == null) |
100 | print("Warning: unsatisfied back reference " + ref); |
101 | ret o; |
102 | } |
103 | |
104 | if (t.startsWith("t") && isInteger(t.substring(1))) { |
105 | i++; |
106 | int ref = Integer.parseInt(t.substring(1)); |
107 | O o = tokrefs.get(ref); |
108 | if (o == null) |
109 | print("Warning: unsatisfied token reference " + ref); |
110 | ret o; |
111 | } |
112 | |
113 | if (t.equals("hashset")) |
114 | return parseHashSet(); |
115 | if (t.equals("treeset")) |
116 | return parseTreeSet(); |
117 | if (eqOneOf(t, "hashmap", "hm")) |
118 | return parseHashMap(); |
119 | if (t.equals("{")) |
120 | return parseMap(); |
121 | if (t.equals("[")) |
122 | return parseList(); |
123 | if (t.equals("bitset")) |
124 | return parseBitSet(); |
125 | if (t.equals("array") || t.equals("intarray")) |
126 | return parseArray(); |
127 | if (t.equals("ba")) { |
128 | S hex = unquote(tok.get(i+1)); |
129 | i += 2; |
130 | ret hexToBytes(hex); |
131 | } |
132 | if (t.equals("boolarray")) { |
133 | int n = parseInt(tok.get(i+1)); |
134 | S hex = unquote(tok.get(i+2)); |
135 | i += 6; |
136 | ret boolArrayFromBytes(hexToBytes(hex), n); |
137 | } |
138 | if (t.equals("class")) |
139 | return parseClass(); |
140 | if (t.equals("l")) |
141 | return parseLisp(); |
142 | if (t.equals("null")) { |
143 | i++; return null; |
144 | } |
145 | |
146 | /* in dev. |
147 | if (!allDynamic && t.equals("run")) { |
148 | S snippetID = unquote(t.get(i+1)); |
149 | i += 2; |
150 | run( |
151 | } |
152 | */ |
153 | |
154 | if (eq(t, "c")) { |
155 | consume("c"); |
156 | t = tok.get(i); |
157 | assertTrue(isJavaIdentifier(t)); |
158 | concepts.add(t); |
159 | } |
160 | } |
161 | |
162 | if (c == null && !isJavaIdentifier(t)) |
163 | throw new RuntimeException("Unknown token " + (i+1) + ": " + t); |
164 | |
165 | // any other class name |
166 | if (c == null) { |
167 | // First, find class |
168 | if (allDynamic) c = null; |
169 | else if (classFinder != null) |
170 | c = (Class) callF(classFinder, t); |
171 | else |
172 | c = findClass(t); |
173 | if (c != null) |
174 | classesMap.put(t, c); |
175 | } |
176 | |
177 | // Check if it has an outer reference |
178 | i++; |
179 | bool hasOuter = eq(get(tok, i), "(") && eq(get(tok, i+1), "this$1"); |
180 | |
181 | DynamicObject dO = null; |
182 | O o = null; |
183 | if (c != null) |
184 | o = hasOuter ? nuStubInnerObject(c) : nuEmptyObject(c); |
185 | else { |
186 | if (concepts.contains(t) && (c = findClass("Concept")) != null) |
187 | o = dO = (DynamicObject) nuEmptyObject(c); |
188 | else |
189 | dO = new DynamicObject; |
190 | dO.className = t; |
191 | if (debug) print("Made dynamic object " + t + " " + shortClassName(dO)); |
192 | } |
193 | |
194 | // Save in references list early because contents of object |
195 | // might link back to main object |
196 | |
197 | if (refID != 0) |
198 | refs.put(refID, o != null ? o : dO); |
199 | tokrefs.put(tokIndex, o != null ? o : dO); |
200 | |
201 | // NOW parse the fields! |
202 | |
203 | final new HashMap<S, O> fields; |
204 | final O _o = o; |
205 | final DynamicObject _dO = dO; |
206 | if (i < tok.size() && tok.get(i).equals("(")) { |
207 | consume("("); |
208 | stack.add(r { |
209 | if (tok.get(i).equals(")")) { |
210 | consume(")"); |
211 | objRead(_o, _dO, fields); |
212 | } else { |
213 | S key = unquote(tok.get(i++)); |
214 | consume("="); |
215 | stack.add(this); |
216 | O value = parse(); |
217 | fields.put(key, value); |
218 | if (tok.get(i).equals(",")) i++; |
219 | } |
220 | }); |
221 | } else |
222 | objRead(o, dO, fields); |
223 | |
224 | return o != null ? o : dO; |
225 | } |
226 | |
227 | void objRead(O o, DynamicObject dO, Map<S, O> fields) { |
228 | if (o != null) |
229 | if (dO != null) { |
230 | if (debug) |
231 | printStructure("setOptAllDyn", fields); |
232 | setOptAllDyn(dO, fields); |
233 | } else |
234 | setOptAll(o, fields); |
235 | else for (S field : keys(fields)) |
236 | dO.fieldValues.put(field.intern(), fields.get(field)); |
237 | |
238 | if (o != null) |
239 | pcallOpt_noArgs(o, "_doneLoading"); |
240 | } |
241 | |
242 | Object parseSet(Set set) { |
243 | set.addAll((L) parseList()); |
244 | return set; |
245 | } |
246 | |
247 | Object parseLisp() { |
248 | consume("l"); |
249 | consume("("); |
250 | new ArrayList list; |
251 | stack.add(r { |
252 | if (tok.get(i).equals(")")) |
253 | consume(")"); |
254 | else { |
255 | stack.add(this); |
256 | list.add(parse()); |
257 | if (tok.get(i).equals(",")) i++; |
258 | } |
259 | }); |
260 | return newObject("main$Lisp", (S) list.get(0), subList(list, 1)); |
261 | } |
262 | |
263 | Object parseBitSet() { |
264 | consume("bitset"); |
265 | consume("{"); |
266 | new BitSet bs; |
267 | stack.add(r { |
268 | if (tok.get(i).equals("}")) |
269 | consume("}"); |
270 | else { |
271 | stack.add(this); |
272 | bs.set((Integer) parse()); |
273 | if (tok.get(i).equals(",")) i++; |
274 | } |
275 | }); |
276 | return bs; |
277 | } |
278 | |
279 | Object parseList() { |
280 | consume("["); |
281 | new ArrayList list; |
282 | stack.add(r { |
283 | if (tok.get(i).equals("]")) |
284 | consume("]"); |
285 | else { |
286 | stack.add(this); |
287 | O o = parse(); |
288 | //if (debug) print("List element type: " + getClassName(o)); |
289 | list.add(o); |
290 | if (tok.get(i).equals(",")) i++; |
291 | } |
292 | }); |
293 | return list; |
294 | } |
295 | |
296 | Object parseArray() { |
297 | S type = tok.get(i); |
298 | i++; |
299 | consume("{"); |
300 | final List list = new ArrayList; |
301 | |
302 | // TODO!! |
303 | |
304 | stack.add(r { |
305 | if (tok.get(i).equals("}")) |
306 | consume("}"); |
307 | else { |
308 | stack.add(this); |
309 | list.add(parse()); |
310 | if (tok.get(i).equals(",")) i++; |
311 | } |
312 | }); |
313 | if (type.equals("intarray")) |
314 | return toIntArray(list); |
315 | return list.toArray(); |
316 | } |
317 | |
318 | Object parseClass() { |
319 | consume("class"); |
320 | consume("("); |
321 | S name = tok.get(i); |
322 | i++; |
323 | consume(")"); |
324 | Class c = allDynamic ? null : findClass(name); |
325 | if (c != null) ret c; |
326 | new DynamicObject dO; |
327 | dO.className = "java.lang.Class"; |
328 | dO.fieldValues.put("name", name); |
329 | ret dO; |
330 | } |
331 | |
332 | Object parseBigInt() { |
333 | consume("bigint"); |
334 | consume("("); |
335 | S val = tok.get(i); |
336 | i++; |
337 | if (eq(val, "-")) { |
338 | val = "-" + tok.get(i); |
339 | i++; |
340 | } |
341 | consume(")"); |
342 | ret new BigInteger(val); |
343 | } |
344 | |
345 | Object parseDouble() { |
346 | consume("d"); |
347 | consume("("); |
348 | S val = unquote(tok.get(i)); |
349 | i++; |
350 | consume(")"); |
351 | ret Double.parseDouble(val); |
352 | } |
353 | |
354 | Object parseFloat() { |
355 | consume("fl"); |
356 | S val; |
357 | if (eq(tok.get(i), "(")) { |
358 | consume("("); |
359 | val = unquote(tok.get(i)); |
360 | i++; |
361 | consume(")"); |
362 | } else { |
363 | val = unquote(tok.get(i)); |
364 | i++; |
365 | } |
366 | ret Float.parseFloat(val); |
367 | } |
368 | |
369 | Object parseHashMap() { |
370 | i++; |
371 | return parseMap(new HashMap); |
372 | } |
373 | |
374 | Object parseHashSet() { |
375 | consume("hashset"); |
376 | return parseSet(new HashSet); |
377 | } |
378 | |
379 | Object parseTreeSet() { |
380 | consume("treeset"); |
381 | return parseSet(new TreeSet); |
382 | } |
383 | |
384 | Object parseMap() { |
385 | return parseMap(new TreeMap); |
386 | } |
387 | |
388 | Object parseMap(Map map) { |
389 | consume("{"); |
390 | while (!tok.get(i).equals("}")) { |
391 | O key = parse(); |
392 | consume("="); |
393 | Object value = parse(); |
394 | map.put(key, value); |
395 | if (tok.get(i).equals(",")) i++; |
396 | } |
397 | consume("}"); |
398 | return map; |
399 | } |
400 | |
401 | void consume(String s) { |
402 | if (!tok.get(i).equals(s)) { |
403 | S prevToken = i-1 >= 0 ? tok.get(i-1) : ""; |
404 | S nextTokens = join(tok.subList(i, Math.min(i+2, tok.size()))); |
405 | fail(quote(s) + " expected: " + prevToken + " " + nextTokens + " (" + i + "/" + tok.size() + ")"); |
406 | } |
407 | i++; |
408 | } |
409 | |
410 | O parse_x() { |
411 | O o = parse(); |
412 | while (nempty(stack)) |
413 | popLast(stack).run(); |
414 | ret o; |
415 | } |
416 | } |
417 | |
418 | ret new X().parse_x(); |
419 | } |
420 | |
421 | static boolean unstructure_debug; |
Began life as a copy of #1005604
download show line numbers debug dex old transpilations
Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, ddnzoavkxhuk, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1005658 |
Snippet name: | unstructure (v8, virtual stack, dev.) |
Eternal ID of this version: | #1005658/1 |
Text MD5: | c838217897ac97113ac26a743137c1e8 |
Author: | stefan |
Category: | javax |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2016-12-13 15:56:28 |
Source code size: | 11328 bytes / 421 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 578 / 573 |
Referenced in: | [show references] |