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