]>
Commit | Line | Data |
---|---|---|
abe05a73 XL |
1 | /* |
2 | * Copyright 2015 WebAssembly Community Group participants | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #ifndef wasm_simple_ast_h | |
18 | #define wasm_simple_ast_h | |
19 | ||
20 | #include <algorithm> | |
21 | #include <cassert> | |
22 | #include <cmath> | |
23 | #include <cstdio> | |
24 | #include <cstdlib> | |
25 | #include <cstring> | |
26 | #include <functional> | |
27 | #include <iomanip> | |
28 | #include <iostream> | |
29 | #include <limits> | |
30 | #include <ostream> | |
31 | #include <set> | |
32 | #include <unordered_map> | |
33 | #include <unordered_set> | |
34 | #include <vector> | |
35 | ||
36 | #include "parser.h" | |
37 | #include "snprintf.h" | |
38 | #include "support/safe_integer.h" | |
39 | #include "mixed_arena.h" | |
40 | ||
41 | #define err(str) fprintf(stderr, str "\n"); | |
42 | #define errv(str, ...) fprintf(stderr, str "\n", __VA_ARGS__); | |
43 | #define printErr err | |
44 | ||
45 | namespace cashew { | |
46 | ||
47 | struct Value; | |
48 | struct Ref; | |
49 | ||
50 | void dump(const char *str, Ref node, bool pretty=false); | |
51 | ||
52 | // Reference to a value, plus some operators for convenience | |
53 | struct Ref { | |
54 | Value* inst; | |
55 | ||
56 | Ref(Value *v=nullptr) : inst(v) {} | |
57 | ||
58 | Value* get() { return inst; } | |
59 | ||
60 | Value& operator*() { return *inst; } | |
61 | Value* operator->() { return inst; } | |
62 | Ref& operator[](unsigned x); | |
63 | Ref& operator[](IString x); | |
64 | ||
65 | // special conveniences | |
66 | bool operator==(const char *str); // comparison to string, which is by value | |
67 | bool operator!=(const char *str); | |
68 | bool operator==(const IString &str); | |
69 | bool operator!=(const IString &str); | |
70 | bool operator==(double d) { abort(); return false; } // prevent Ref == number, which is potentially ambiguous; use ->getNumber() == number | |
71 | bool operator==(Ref other); | |
72 | bool operator!(); // check if null, in effect | |
73 | }; | |
74 | ||
75 | // Arena allocation, free it all on process exit | |
76 | ||
77 | // A mixed arena for global allocation only, so members do not | |
78 | // receive an allocator, they all use the global one anyhow | |
79 | class GlobalMixedArena : public MixedArena { | |
80 | public: | |
81 | template<class T> | |
82 | T* alloc() { | |
83 | auto* ret = static_cast<T*>(allocSpace(sizeof(T))); | |
84 | new (ret) T(); | |
85 | return ret; | |
86 | } | |
87 | }; | |
88 | ||
89 | extern GlobalMixedArena arena; | |
90 | ||
91 | class ArrayStorage : public ArenaVectorBase<ArrayStorage, Ref> { | |
92 | public: | |
93 | void allocate(size_t size) { | |
94 | allocatedElements = size; | |
95 | data = static_cast<Ref*>(arena.allocSpace(sizeof(Ref) * allocatedElements)); | |
96 | } | |
97 | }; | |
98 | ||
99 | struct Assign; | |
100 | struct AssignName; | |
101 | ||
102 | // Main value type | |
103 | struct Value { | |
104 | enum Type { | |
105 | String = 0, | |
106 | Number = 1, | |
107 | Array = 2, | |
108 | Null = 3, | |
109 | Bool = 4, | |
110 | Object = 5, | |
111 | Assign_ = 6, // ref = target | |
112 | AssignName_ = 7 | |
113 | }; | |
114 | ||
115 | Type type; | |
116 | ||
117 | typedef std::unordered_map<IString, Ref> ObjectStorage; | |
118 | ||
119 | #ifdef _MSC_VER // MSVC does not allow unrestricted unions: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf | |
120 | IString str; | |
121 | #endif | |
122 | union { // TODO: optimize | |
123 | #ifndef _MSC_VER | |
124 | IString str; | |
125 | #endif | |
126 | double num; | |
127 | ArrayStorage *arr; | |
128 | bool boo; | |
129 | ObjectStorage *obj; | |
130 | Ref ref; | |
131 | }; | |
132 | ||
133 | // constructors all copy their input | |
134 | Value() : type(Null), num(0) {} | |
135 | explicit Value(const char *s) : type(Null) { | |
136 | setString(s); | |
137 | } | |
138 | explicit Value(double n) : type(Null) { | |
139 | setNumber(n); | |
140 | } | |
141 | explicit Value(ArrayStorage &a) : type(Null) { | |
142 | setArray(); | |
143 | *arr = a; | |
144 | } | |
145 | // no bool constructor - would endanger the double one (int might convert the wrong way) | |
146 | ||
147 | ~Value() { | |
148 | free(); | |
149 | } | |
150 | ||
151 | void free() { | |
152 | if (type == Array) { arr->clear(); } | |
153 | else if (type == Object) delete obj; | |
154 | type = Null; | |
155 | num = 0; | |
156 | } | |
157 | ||
158 | Value& setString(const char *s) { | |
159 | free(); | |
160 | type = String; | |
161 | str.set(s); | |
162 | return *this; | |
163 | } | |
164 | Value& setString(const IString &s) { | |
165 | free(); | |
166 | type = String; | |
167 | str.set(s); | |
168 | return *this; | |
169 | } | |
170 | Value& setNumber(double n) { | |
171 | free(); | |
172 | type = Number; | |
173 | num = n; | |
174 | return *this; | |
175 | } | |
176 | Value& setArray(ArrayStorage &a) { | |
177 | free(); | |
178 | type = Array; | |
179 | arr = arena.alloc<ArrayStorage>(); | |
180 | *arr = a; | |
181 | return *this; | |
182 | } | |
183 | Value& setArray(size_t size_hint=0) { | |
184 | free(); | |
185 | type = Array; | |
186 | arr = arena.alloc<ArrayStorage>(); | |
187 | arr->reserve(size_hint); | |
188 | return *this; | |
189 | } | |
190 | Value& setNull() { | |
191 | free(); | |
192 | type = Null; | |
193 | return *this; | |
194 | } | |
195 | Value& setBool(bool b) { // Bool in the name, as otherwise might overload over int | |
196 | free(); | |
197 | type = Bool; | |
198 | boo = b; | |
199 | return *this; | |
200 | } | |
201 | Value& setObject() { | |
202 | free(); | |
203 | type = Object; | |
204 | obj = new ObjectStorage(); | |
205 | return *this; | |
206 | } | |
207 | Value& setAssign(Ref target, Ref value); | |
208 | Value& setAssignName(IString target, Ref value); | |
209 | ||
210 | bool isString() { return type == String; } | |
211 | bool isNumber() { return type == Number; } | |
212 | bool isArray() { return type == Array; } | |
213 | bool isNull() { return type == Null; } | |
214 | bool isBool() { return type == Bool; } | |
215 | bool isObject() { return type == Object; } | |
216 | bool isAssign() { return type == Assign_; } | |
217 | bool isAssignName() { return type == AssignName_; } | |
218 | ||
219 | bool isBool(bool b) { return type == Bool && b == boo; } // avoid overloading == as it might overload over int | |
220 | ||
221 | // convenience function to check if something is an array and | |
222 | // also has a certain string as the first element. This is a | |
223 | // very common operation as the first element defines the node | |
224 | // type for most ast nodes | |
225 | bool isArray(IString name) { | |
226 | return isArray() && (*this)[0] == name; | |
227 | } | |
228 | ||
229 | const char* getCString() { | |
230 | assert(isString()); | |
231 | return str.str; | |
232 | } | |
233 | IString& getIString() { | |
234 | assert(isString()); | |
235 | return str; | |
236 | } | |
237 | double& getNumber() { | |
238 | assert(isNumber()); | |
239 | return num; | |
240 | } | |
241 | ArrayStorage& getArray() { | |
242 | assert(isArray()); | |
243 | return *arr; | |
244 | } | |
245 | bool& getBool() { | |
246 | assert(isBool()); | |
247 | return boo; | |
248 | } | |
249 | ||
250 | Assign* asAssign(); | |
251 | AssignName* asAssignName(); | |
252 | ||
253 | int32_t getInteger() { // convenience function to get a known integer | |
254 | assert(fmod(getNumber(), 1) == 0); | |
255 | int32_t ret = getNumber(); | |
256 | assert(double(ret) == getNumber()); // no loss in conversion | |
257 | return ret; | |
258 | } | |
259 | ||
260 | Value& operator=(const Value& other) { | |
261 | free(); | |
262 | switch (other.type) { | |
263 | case String: | |
264 | setString(other.str); | |
265 | break; | |
266 | case Number: | |
267 | setNumber(other.num); | |
268 | break; | |
269 | case Array: | |
270 | setArray(*other.arr); | |
271 | break; | |
272 | case Null: | |
273 | setNull(); | |
274 | break; | |
275 | case Bool: | |
276 | setBool(other.boo); | |
277 | break; | |
278 | default: | |
279 | abort(); // TODO | |
280 | } | |
281 | return *this; | |
282 | } | |
283 | ||
284 | bool operator==(const Value& other) { | |
285 | if (type != other.type) return false; | |
286 | switch (other.type) { | |
287 | case String: | |
288 | return str == other.str; | |
289 | case Number: | |
290 | return num == other.num; | |
291 | case Array: | |
292 | return this == &other; // if you want a deep compare, use deepCompare | |
293 | case Null: | |
294 | break; | |
295 | case Bool: | |
296 | return boo == other.boo; | |
297 | case Object: | |
298 | return this == &other; // if you want a deep compare, use deepCompare | |
299 | default: | |
300 | abort(); | |
301 | } | |
302 | return true; | |
303 | } | |
304 | ||
305 | char* parse(char* curr) { | |
306 | #define is_json_space(x) (x == 32 || x == 9 || x == 10 || x == 13) /* space, tab, linefeed/newline, or return */ | |
307 | #define skip() { while (*curr && is_json_space(*curr)) curr++; } | |
308 | skip(); | |
309 | if (*curr == '"') { | |
310 | // String | |
311 | curr++; | |
312 | char *close = strchr(curr, '"'); | |
313 | assert(close); | |
314 | *close = 0; // end this string, and reuse it straight from the input | |
315 | setString(curr); | |
316 | curr = close+1; | |
317 | } else if (*curr == '[') { | |
318 | // Array | |
319 | curr++; | |
320 | skip(); | |
321 | setArray(); | |
322 | while (*curr != ']') { | |
323 | Ref temp = arena.alloc<Value>(); | |
324 | arr->push_back(temp); | |
325 | curr = temp->parse(curr); | |
326 | skip(); | |
327 | if (*curr == ']') break; | |
328 | assert(*curr == ','); | |
329 | curr++; | |
330 | skip(); | |
331 | } | |
332 | curr++; | |
333 | } else if (*curr == 'n') { | |
334 | // Null | |
335 | assert(strncmp(curr, "null", 4) == 0); | |
336 | setNull(); | |
337 | curr += 4; | |
338 | } else if (*curr == 't') { | |
339 | // Bool true | |
340 | assert(strncmp(curr, "true", 4) == 0); | |
341 | setBool(true); | |
342 | curr += 4; | |
343 | } else if (*curr == 'f') { | |
344 | // Bool false | |
345 | assert(strncmp(curr, "false", 5) == 0); | |
346 | setBool(false); | |
347 | curr += 5; | |
348 | } else if (*curr == '{') { | |
349 | // Object | |
350 | curr++; | |
351 | skip(); | |
352 | setObject(); | |
353 | while (*curr != '}') { | |
354 | assert(*curr == '"'); | |
355 | curr++; | |
356 | char *close = strchr(curr, '"'); | |
357 | assert(close); | |
358 | *close = 0; // end this string, and reuse it straight from the input | |
359 | IString key(curr); | |
360 | curr = close+1; | |
361 | skip(); | |
362 | assert(*curr == ':'); | |
363 | curr++; | |
364 | skip(); | |
365 | Ref value = arena.alloc<Value>(); | |
366 | curr = value->parse(curr); | |
367 | (*obj)[key] = value; | |
368 | skip(); | |
369 | if (*curr == '}') break; | |
370 | assert(*curr == ','); | |
371 | curr++; | |
372 | skip(); | |
373 | } | |
374 | curr++; | |
375 | } else { | |
376 | // Number | |
377 | char *after; | |
378 | setNumber(strtod(curr, &after)); | |
379 | curr = after; | |
380 | } | |
381 | return curr; | |
382 | } | |
383 | ||
384 | void stringify(std::ostream &os, bool pretty=false); | |
385 | ||
386 | // String operations | |
387 | ||
388 | // Number operations | |
389 | ||
390 | // Array operations | |
391 | ||
392 | size_t size() { | |
393 | assert(isArray()); | |
394 | return arr->size(); | |
395 | } | |
396 | ||
397 | void setSize(size_t size) { | |
398 | assert(isArray()); | |
399 | auto old = arr->size(); | |
400 | if (old != size) arr->resize(size); | |
401 | if (old < size) { | |
402 | for (auto i = old; i < size; i++) { | |
403 | (*arr)[i] = arena.alloc<Value>(); | |
404 | } | |
405 | } | |
406 | } | |
407 | ||
408 | Ref& operator[](unsigned x) { | |
409 | assert(isArray()); | |
410 | return (*arr)[x]; | |
411 | } | |
412 | ||
413 | Value& push_back(Ref r) { | |
414 | assert(isArray()); | |
415 | arr->push_back(r); | |
416 | return *this; | |
417 | } | |
418 | Ref pop_back() { | |
419 | assert(isArray()); | |
420 | Ref ret = arr->back(); | |
421 | arr->pop_back(); | |
422 | return ret; | |
423 | } | |
424 | ||
425 | Ref back() { | |
426 | assert(isArray()); | |
427 | if (arr->size() == 0) return nullptr; | |
428 | return arr->back(); | |
429 | } | |
430 | ||
431 | void splice(int x, int num) { | |
432 | assert(isArray()); | |
433 | arr->erase(arr->begin() + x, arr->begin() + x + num); | |
434 | } | |
435 | ||
436 | int indexOf(Ref other) { | |
437 | assert(isArray()); | |
438 | for (size_t i = 0; i < arr->size(); i++) { | |
439 | if (other == (*arr)[i]) return i; | |
440 | } | |
441 | return -1; | |
442 | } | |
443 | ||
444 | Ref map(std::function<Ref (Ref node)> func) { | |
445 | assert(isArray()); | |
446 | Ref ret = arena.alloc<Value>(); | |
447 | ret->setArray(); | |
448 | for (size_t i = 0; i < arr->size(); i++) { | |
449 | ret->push_back(func((*arr)[i])); | |
450 | } | |
451 | return ret; | |
452 | } | |
453 | ||
454 | Ref filter(std::function<bool (Ref node)> func) { | |
455 | assert(isArray()); | |
456 | Ref ret = arena.alloc<Value>(); | |
457 | ret->setArray(); | |
458 | for (size_t i = 0; i < arr->size(); i++) { | |
459 | Ref curr = (*arr)[i]; | |
460 | if (func(curr)) ret->push_back(curr); | |
461 | } | |
462 | return ret; | |
463 | } | |
464 | ||
465 | /* | |
466 | void forEach(std::function<void (Ref)> func) { | |
467 | for (size_t i = 0; i < arr->size(); i++) { | |
468 | func((*arr)[i]); | |
469 | } | |
470 | } | |
471 | */ | |
472 | ||
473 | // Null operations | |
474 | ||
475 | // Bool operations | |
476 | ||
477 | // Object operations | |
478 | ||
479 | Ref& operator[](IString x) { | |
480 | assert(isObject()); | |
481 | return (*obj)[x]; | |
482 | } | |
483 | ||
484 | bool has(IString x) { | |
485 | assert(isObject()); | |
486 | return obj->count(x) > 0; | |
487 | } | |
488 | }; | |
489 | ||
490 | struct Assign : public Value { | |
491 | Ref value_; | |
492 | ||
493 | Assign(Ref targetInit, Ref valueInit) { | |
494 | type = Assign_; | |
495 | target() = targetInit; | |
496 | value() = valueInit; | |
497 | } | |
498 | ||
499 | Assign() : Assign(nullptr, nullptr) {} | |
500 | ||
501 | Ref& target() { | |
502 | return ref; | |
503 | } | |
504 | Ref& value() { | |
505 | return value_; | |
506 | } | |
507 | }; | |
508 | ||
509 | struct AssignName : public Value { | |
510 | IString target_; | |
511 | ||
512 | AssignName(IString targetInit, Ref valueInit) { | |
513 | type = AssignName_; | |
514 | target() = targetInit; | |
515 | value() = valueInit; | |
516 | } | |
517 | ||
518 | AssignName() : AssignName(IString(), nullptr) {} | |
519 | ||
520 | IString& target() { | |
521 | return target_; | |
522 | } | |
523 | Ref& value() { | |
524 | return ref; | |
525 | } | |
526 | }; | |
527 | ||
528 | // AST traversals | |
529 | ||
530 | // Traverse, calling visit before the children | |
531 | void traversePre(Ref node, std::function<void (Ref)> visit); | |
532 | ||
533 | // Traverse, calling visitPre before the children and visitPost after | |
534 | void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::function<void (Ref)> visitPost); | |
535 | ||
536 | // Traverse, calling visitPre before the children and visitPost after. If pre returns false, do not traverse children | |
537 | void traversePrePostConditional(Ref node, std::function<bool (Ref)> visitPre, std::function<void (Ref)> visitPost); | |
538 | ||
539 | // Traverses all the top-level functions in the document | |
540 | void traverseFunctions(Ref ast, std::function<void (Ref)> visit); | |
541 | ||
542 | // JS printing support | |
543 | ||
544 | struct JSPrinter { | |
545 | bool pretty, finalize; | |
546 | ||
547 | char *buffer; | |
548 | size_t size, used; | |
549 | ||
550 | int indent; | |
551 | bool possibleSpace; // add a space to separate identifiers | |
552 | ||
553 | Ref ast; | |
554 | ||
555 | JSPrinter(bool pretty_, bool finalize_, Ref ast_) : pretty(pretty_), finalize(finalize_), buffer(0), size(0), used(0), indent(0), possibleSpace(false), ast(ast_) {} | |
556 | ||
557 | ~JSPrinter() { | |
558 | free(buffer); | |
559 | } | |
560 | ||
561 | void printAst() { | |
562 | print(ast); | |
563 | buffer[used] = 0; | |
564 | } | |
565 | ||
566 | // Utils | |
567 | ||
568 | void ensure(int safety=100) { | |
569 | if (size >= used + safety) { | |
570 | return; | |
571 | } | |
572 | size = std::max((size_t)1024, size * 2) + safety; | |
573 | if (!buffer) { | |
574 | buffer = (char*)malloc(size); | |
575 | if (!buffer) { | |
576 | errv("Out of memory allocating %zd bytes for output buffer!", size); | |
577 | abort(); | |
578 | } | |
579 | } else { | |
580 | char *buf = (char*)realloc(buffer, size); | |
581 | if (!buf) { | |
582 | free(buffer); | |
583 | errv("Out of memory allocating %zd bytes for output buffer!", size); | |
584 | abort(); | |
585 | } | |
586 | buffer = buf; | |
587 | } | |
588 | } | |
589 | ||
590 | void emit(char c) { | |
591 | maybeSpace(c); | |
592 | if (!pretty && c == '}' && buffer[used-1] == ';') used--; // optimize ;} into }, the ; is not separating anything | |
593 | ensure(1); | |
594 | buffer[used++] = c; | |
595 | } | |
596 | ||
597 | void emit(const char *s) { | |
598 | maybeSpace(*s); | |
599 | int len = strlen(s); | |
600 | ensure(len+1); | |
601 | strncpy(buffer + used, s, len+1); | |
602 | used += len; | |
603 | } | |
604 | ||
605 | void newline() { | |
606 | if (!pretty) return; | |
607 | emit('\n'); | |
608 | for (int i = 0; i < indent; i++) emit(' '); | |
609 | } | |
610 | ||
611 | void space() { | |
612 | if (pretty) emit(' '); | |
613 | } | |
614 | ||
615 | void safeSpace() { | |
616 | if (pretty) emit(' '); | |
617 | else possibleSpace = true; | |
618 | } | |
619 | ||
620 | void maybeSpace(char s) { | |
621 | if (possibleSpace) { | |
622 | possibleSpace = false; | |
623 | if (isIdentPart(s)) emit(' '); | |
624 | } | |
625 | } | |
626 | ||
627 | bool isNothing(Ref node) { | |
628 | return node->isArray() && node[0] == TOPLEVEL && node[1]->size() == 0; | |
629 | } | |
630 | ||
631 | bool isDefun(Ref node) { | |
632 | return node->isArray() && node[0] == DEFUN; | |
633 | } | |
634 | ||
635 | bool isBlock(Ref node) { | |
636 | return node->isArray() && node[0] == BLOCK; | |
637 | } | |
638 | ||
639 | bool isIf(Ref node) { | |
640 | return node->isArray() && node[0] == IF; | |
641 | } | |
642 | ||
643 | void print(Ref node) { | |
644 | ensure(); | |
645 | if (node->isString()) { | |
646 | printName(node); | |
647 | return; | |
648 | } | |
649 | if (node->isNumber()) { | |
650 | printNum(node); | |
651 | return; | |
652 | } | |
653 | if (node->isAssignName()) { | |
654 | printAssignName(node); | |
655 | return; | |
656 | } | |
657 | if (node->isAssign()) { | |
658 | printAssign(node); | |
659 | return; | |
660 | } | |
661 | IString type = node[0]->getIString(); | |
662 | switch (type.str[0]) { | |
663 | case 'a': { | |
664 | if (type == ARRAY) printArray(node); | |
665 | else abort(); | |
666 | break; | |
667 | } | |
668 | case 'b': { | |
669 | if (type == BINARY) printBinary(node); | |
670 | else if (type == BLOCK) printBlock(node); | |
671 | else if (type == BREAK) printBreak(node); | |
672 | else abort(); | |
673 | break; | |
674 | } | |
675 | case 'c': { | |
676 | if (type == CALL) printCall(node); | |
677 | else if (type == CONDITIONAL) printConditional(node); | |
678 | else if (type == CONTINUE) printContinue(node); | |
679 | else abort(); | |
680 | break; | |
681 | } | |
682 | case 'd': { | |
683 | if (type == DEFUN) printDefun(node); | |
684 | else if (type == DO) printDo(node); | |
685 | else if (type == DOT) printDot(node); | |
686 | else abort(); | |
687 | break; | |
688 | } | |
689 | case 'i': { | |
690 | if (type == IF) printIf(node); | |
691 | else abort(); | |
692 | break; | |
693 | } | |
694 | case 'l': { | |
695 | if (type == LABEL) printLabel(node); | |
696 | else abort(); | |
697 | break; | |
698 | } | |
699 | case 'n': { | |
700 | if (type == NEW) printNew(node); | |
701 | else abort(); | |
702 | break; | |
703 | } | |
704 | case 'o': { | |
705 | if (type == OBJECT) printObject(node); | |
706 | break; | |
707 | } | |
708 | case 'r': { | |
709 | if (type == RETURN) printReturn(node); | |
710 | else abort(); | |
711 | break; | |
712 | } | |
713 | case 's': { | |
714 | if (type == SUB) printSub(node); | |
715 | else if (type == SEQ) printSeq(node); | |
716 | else if (type == SWITCH) printSwitch(node); | |
717 | else if (type == STRING) printString(node); | |
718 | else abort(); | |
719 | break; | |
720 | } | |
721 | case 't': { | |
722 | if (type == TOPLEVEL) printToplevel(node); | |
723 | else if (type == TRY) printTry(node); | |
724 | else abort(); | |
725 | break; | |
726 | } | |
727 | case 'u': { | |
728 | if (type == UNARY_PREFIX) printUnaryPrefix(node); | |
729 | else abort(); | |
730 | break; | |
731 | } | |
732 | case 'v': { | |
733 | if (type == VAR) printVar(node); | |
734 | else abort(); | |
735 | break; | |
736 | } | |
737 | case 'w': { | |
738 | if (type == WHILE) printWhile(node); | |
739 | else abort(); | |
740 | break; | |
741 | } | |
742 | default: { | |
743 | errv("cannot yet print %s\n", type.str); | |
744 | abort(); | |
745 | } | |
746 | } | |
747 | } | |
748 | ||
749 | // print a node, and if nothing is emitted, emit something instead | |
750 | void print(Ref node, const char *otherwise) { | |
751 | auto last = used; | |
752 | print(node); | |
753 | if (used == last) emit(otherwise); | |
754 | } | |
755 | ||
756 | void printStats(Ref stats) { | |
757 | bool first = true; | |
758 | for (size_t i = 0; i < stats->size(); i++) { | |
759 | Ref curr = stats[i]; | |
760 | if (!isNothing(curr)) { | |
761 | if (first) first = false; | |
762 | else newline(); | |
763 | print(curr); | |
764 | if (!isDefun(curr) && !isBlock(curr) && !isIf(curr)) { | |
765 | emit(';'); | |
766 | } | |
767 | } | |
768 | } | |
769 | } | |
770 | ||
771 | void printToplevel(Ref node) { | |
772 | if (node[1]->size() > 0) { | |
773 | printStats(node[1]); | |
774 | } | |
775 | } | |
776 | ||
777 | void printBlock(Ref node) { | |
778 | if (node->size() == 1 || node[1]->size() == 0) { | |
779 | emit("{}"); | |
780 | return; | |
781 | } | |
782 | emit('{'); | |
783 | indent++; | |
784 | newline(); | |
785 | printStats(node[1]); | |
786 | indent--; | |
787 | newline(); | |
788 | emit('}'); | |
789 | } | |
790 | ||
791 | void printDefun(Ref node) { | |
792 | emit("function "); | |
793 | emit(node[1]->getCString()); | |
794 | emit('('); | |
795 | Ref args = node[2]; | |
796 | for (size_t i = 0; i < args->size(); i++) { | |
797 | if (i > 0) (pretty ? emit(", ") : emit(',')); | |
798 | emit(args[i]->getCString()); | |
799 | } | |
800 | emit(')'); | |
801 | space(); | |
802 | if (node->size() == 3 || node[3]->size() == 0) { | |
803 | emit("{}"); | |
804 | return; | |
805 | } | |
806 | emit('{'); | |
807 | indent++; | |
808 | newline(); | |
809 | printStats(node[3]); | |
810 | indent--; | |
811 | newline(); | |
812 | emit('}'); | |
813 | newline(); | |
814 | } | |
815 | ||
816 | void printAssign(Ref node) { | |
817 | auto* assign = node->asAssign(); | |
818 | printChild(assign->target(), node, -1); | |
819 | space(); | |
820 | emit('='); | |
821 | space(); | |
822 | printChild(assign->value(), node, 1); | |
823 | } | |
824 | ||
825 | void printAssignName(Ref node) { | |
826 | auto *assign = node->asAssignName(); | |
827 | emit(assign->target().c_str()); | |
828 | space(); | |
829 | emit('='); | |
830 | space(); | |
831 | printChild(assign->value(), node, 1); | |
832 | } | |
833 | ||
834 | void printName(Ref node) { | |
835 | emit(node->getCString()); | |
836 | } | |
837 | ||
838 | static char* numToString(double d, bool finalize=true) { | |
839 | bool neg = d < 0; | |
840 | if (neg) d = -d; | |
841 | // try to emit the fewest necessary characters | |
842 | bool integer = fmod(d, 1) == 0; | |
843 | #define BUFFERSIZE 1000 | |
844 | static char full_storage_f[BUFFERSIZE], full_storage_e[BUFFERSIZE]; // f is normal, e is scientific for float, x for integer | |
845 | static char *storage_f = full_storage_f + 1, *storage_e = full_storage_e + 1; // full has one more char, for a possible '-' | |
846 | auto err_f = std::numeric_limits<double>::quiet_NaN(); | |
847 | auto err_e = std::numeric_limits<double>::quiet_NaN(); | |
848 | for (int e = 0; e <= 1; e++) { | |
849 | char *buffer = e ? storage_e : storage_f; | |
850 | double temp; | |
851 | if (!integer) { | |
852 | static char format[6]; | |
853 | for (int i = 0; i <= 18; i++) { | |
854 | format[0] = '%'; | |
855 | format[1] = '.'; | |
856 | if (i < 10) { | |
857 | format[2] = '0' + i; | |
858 | format[3] = e ? 'e' : 'f'; | |
859 | format[4] = 0; | |
860 | } else { | |
861 | format[2] = '1'; | |
862 | format[3] = '0' + (i - 10); | |
863 | format[4] = e ? 'e' : 'f'; | |
864 | format[5] = 0; | |
865 | } | |
866 | snprintf(buffer, BUFFERSIZE-1, format, d); | |
867 | sscanf(buffer, "%lf", &temp); | |
868 | //errv("%.18f, %.18e => %s => %.18f, %.18e (%d), ", d, d, buffer, temp, temp, temp == d); | |
869 | if (temp == d) break; | |
870 | } | |
871 | } else { | |
872 | // integer | |
873 | assert(d >= 0); | |
874 | if (wasm::isUInteger64(d)) { | |
875 | unsigned long long uu = wasm::toUInteger64(d); | |
876 | bool asHex = e && !finalize; | |
877 | snprintf(buffer, BUFFERSIZE-1, asHex ? "0x%llx" : "%llu", uu); | |
878 | if (asHex) { | |
879 | unsigned long long tempULL; | |
880 | sscanf(buffer, "%llx", &tempULL); | |
881 | temp = (double)tempULL; | |
882 | } else { | |
883 | sscanf(buffer, "%lf", &temp); | |
884 | } | |
885 | } else { | |
886 | // too large for a machine integer, just use floats | |
887 | snprintf(buffer, BUFFERSIZE-1, e ? "%e" : "%.0f", d); // even on integers, e with a dot is useful, e.g. 1.2e+200 | |
888 | sscanf(buffer, "%lf", &temp); | |
889 | } | |
890 | //errv("%.18f, %.18e => %s => %.18f, %.18e, %llu (%d)\n", d, d, buffer, temp, temp, uu, temp == d); | |
891 | } | |
892 | (e ? err_e : err_f) = fabs(temp - d); | |
893 | //errv("current attempt: %.18f => %s", d, buffer); | |
894 | //assert(temp == d); | |
895 | char *dot = strchr(buffer, '.'); | |
896 | if (dot) { | |
897 | // remove trailing zeros | |
898 | char *end = dot+1; | |
899 | while (*end >= '0' && *end <= '9') end++; | |
900 | end--; | |
901 | while (*end == '0') { | |
902 | char *copy = end; | |
903 | do { | |
904 | copy[0] = copy[1]; | |
905 | } while (*copy++ != 0); | |
906 | end--; | |
907 | } | |
908 | //errv("%.18f => %s", d, buffer); | |
909 | // remove preceding zeros | |
910 | while (*buffer == '0') { | |
911 | char *copy = buffer; | |
912 | do { | |
913 | copy[0] = copy[1]; | |
914 | } while (*copy++ != 0); | |
915 | } | |
916 | //errv("%.18f ===> %s", d, buffer); | |
917 | } else if (!integer || !e) { | |
918 | // no dot. try to change 12345000 => 12345e3 | |
919 | char *end = strchr(buffer, 0); | |
920 | end--; | |
921 | char *test = end; | |
922 | // remove zeros, and also doubles can use at most 24 digits, we can truncate any extras even if not zero | |
923 | while ((*test == '0' || test - buffer > 24) && test > buffer) test--; | |
924 | int num = end - test; | |
925 | if (num >= 3) { | |
926 | test++; | |
927 | test[0] = 'e'; | |
928 | if (num < 10) { | |
929 | test[1] = '0' + num; | |
930 | test[2] = 0; | |
931 | } else if (num < 100) { | |
932 | test[1] = '0' + (num / 10); | |
933 | test[2] = '0' + (num % 10); | |
934 | test[3] = 0; | |
935 | } else { | |
936 | assert(num < 1000); | |
937 | test[1] = '0' + (num / 100); | |
938 | test[2] = '0' + (num % 100) / 10; | |
939 | test[3] = '0' + (num % 10); | |
940 | test[4] = 0; | |
941 | } | |
942 | } | |
943 | } | |
944 | //errv("..current attempt: %.18f => %s", d, buffer); | |
945 | } | |
946 | //fprintf(stderr, "options:\n%s\n%s\n (first? %d)\n", storage_e, storage_f, strlen(storage_e) < strlen(storage_f)); | |
947 | char *ret; | |
948 | if (err_e == err_f) { | |
949 | ret = strlen(storage_e) < strlen(storage_f) ? storage_e : storage_f; | |
950 | } else { | |
951 | ret = err_e < err_f ? storage_e : storage_f; | |
952 | } | |
953 | if (neg) { | |
954 | ret--; // safe to go back one, there is one more char in full_* | |
955 | *ret = '-'; | |
956 | } | |
957 | return ret; | |
958 | } | |
959 | ||
960 | void printNum(Ref node) { | |
961 | emit(numToString(node->getNumber(), finalize)); | |
962 | } | |
963 | ||
964 | void printString(Ref node) { | |
965 | emit('"'); | |
966 | emit(node[1]->getCString()); | |
967 | emit('"'); | |
968 | } | |
969 | ||
970 | // Parens optimizing | |
971 | ||
972 | bool capturesOperators(Ref node) { | |
973 | Ref type = node[0]; | |
974 | return type == CALL || type == ARRAY || type == OBJECT || type == SEQ; | |
975 | } | |
976 | ||
977 | int getPrecedence(Ref node, bool parent) { | |
978 | if (node->isAssign() || node->isAssignName()) { | |
979 | return OperatorClass::getPrecedence(OperatorClass::Binary, SET); | |
980 | } | |
981 | if (!node->isArray()) { | |
982 | // node is a value | |
983 | return -1; | |
984 | } | |
985 | Ref type = node[0]; | |
986 | if (type == BINARY || type == UNARY_PREFIX) { | |
987 | return OperatorClass::getPrecedence(type == BINARY ? OperatorClass::Binary : OperatorClass::Prefix, node[1]->getIString()); | |
988 | } else if (type == SEQ) { | |
989 | return OperatorClass::getPrecedence(OperatorClass::Binary, COMMA); | |
990 | } else if (type == CALL) { | |
991 | return parent ? OperatorClass::getPrecedence(OperatorClass::Binary, COMMA) : -1; // call arguments are split by commas, but call itself is safe | |
992 | } else if (type == CONDITIONAL) { | |
993 | return OperatorClass::getPrecedence(OperatorClass::Tertiary, QUESTION); | |
994 | } | |
995 | // otherwise, this is something that fixes precedence explicitly, and we can ignore | |
996 | return -1; // XXX | |
997 | } | |
998 | ||
999 | // check whether we need parens for the child, when rendered in the parent | |
1000 | // @param childPosition -1 means it is printed to the left of parent, 0 means "anywhere", 1 means right | |
1001 | bool needParens(Ref parent, Ref child, int childPosition) { | |
1002 | int parentPrecedence = getPrecedence(parent, true); | |
1003 | int childPrecedence = getPrecedence(child, false); | |
1004 | ||
1005 | if (childPrecedence > parentPrecedence) return true; // child is definitely a danger | |
1006 | if (childPrecedence < parentPrecedence) return false; // definitely cool | |
1007 | // equal precedence, so associativity (rtl/ltr) is what matters | |
1008 | // (except for some exceptions, where multiple operators can combine into confusion) | |
1009 | if (parent->isArray() && parent[0] == UNARY_PREFIX) { | |
1010 | assert(child[0] == UNARY_PREFIX); | |
1011 | if ((parent[1] == PLUS || parent[1] == MINUS) && child[1] == parent[1]) { | |
1012 | // cannot emit ++x when we mean +(+x) | |
1013 | return true; | |
1014 | } | |
1015 | } | |
1016 | if (childPosition == 0) return true; // child could be anywhere, so always paren | |
1017 | if (childPrecedence < 0) return false; // both precedences are safe | |
1018 | // check if child is on the dangerous side | |
1019 | if (OperatorClass::getRtl(parentPrecedence)) return childPosition < 0; | |
1020 | else return childPosition > 0; | |
1021 | } | |
1022 | ||
1023 | void printChild(Ref child, Ref parent, int childPosition=0) { | |
1024 | bool parens = needParens(parent, child, childPosition); | |
1025 | if (parens) emit('('); | |
1026 | print(child); | |
1027 | if (parens) emit(')'); | |
1028 | } | |
1029 | ||
1030 | void printBinary(Ref node) { | |
1031 | printChild(node[2], node, -1); | |
1032 | space(); | |
1033 | emit(node[1]->getCString()); | |
1034 | space(); | |
1035 | printChild(node[3], node, 1); | |
1036 | } | |
1037 | ||
1038 | void printUnaryPrefix(Ref node) { | |
1039 | if (finalize && node[1] == PLUS && | |
1040 | (node[2]->isNumber() || | |
1041 | (node[2]->isArray() && node[2][0] == UNARY_PREFIX && | |
1042 | node[2][1] == MINUS && node[2][2]->isNumber()))) { | |
1043 | // emit a finalized number | |
1044 | int last = used; | |
1045 | print(node[2]); | |
1046 | ensure(1); // we temporarily append a 0 | |
1047 | char *curr = buffer + last; // ensure might invalidate | |
1048 | buffer[used] = 0; | |
1049 | if (strchr(curr, '.')) return; // already a decimal point, all good | |
1050 | char *e = strchr(curr, 'e'); | |
1051 | if (!e) { | |
1052 | emit(".0"); | |
1053 | return; | |
1054 | } | |
1055 | ensure(3); | |
1056 | curr = buffer + last; // ensure might invalidate | |
1057 | char *end = strchr(curr, 0); | |
1058 | while (end >= e) { | |
1059 | end[2] = end[0]; | |
1060 | end--; | |
1061 | } | |
1062 | e[0] = '.'; | |
1063 | e[1] = '0'; | |
1064 | used += 2; | |
1065 | return; | |
1066 | } | |
1067 | if ((buffer[used-1] == '-' && node[1] == MINUS) || | |
1068 | (buffer[used-1] == '+' && node[1] == PLUS)) { | |
1069 | emit(' '); // cannot join - and - to --, looks like the -- operator | |
1070 | } | |
1071 | emit(node[1]->getCString()); | |
1072 | printChild(node[2], node, 1); | |
1073 | } | |
1074 | ||
1075 | void printConditional(Ref node) { | |
1076 | printChild(node[1], node, -1); | |
1077 | space(); | |
1078 | emit('?'); | |
1079 | space(); | |
1080 | printChild(node[2], node, 0); | |
1081 | space(); | |
1082 | emit(':'); | |
1083 | space(); | |
1084 | printChild(node[3], node, 1); | |
1085 | } | |
1086 | ||
1087 | void printCall(Ref node) { | |
1088 | printChild(node[1], node, 0); | |
1089 | emit('('); | |
1090 | Ref args = node[2]; | |
1091 | for (size_t i = 0; i < args->size(); i++) { | |
1092 | if (i > 0) (pretty ? emit(", ") : emit(',')); | |
1093 | printChild(args[i], node, 0); | |
1094 | } | |
1095 | emit(')'); | |
1096 | } | |
1097 | ||
1098 | void printSeq(Ref node) { | |
1099 | printChild(node[1], node, -1); | |
1100 | emit(','); | |
1101 | space(); | |
1102 | printChild(node[2], node, 1); | |
1103 | } | |
1104 | ||
1105 | void printDot(Ref node) { | |
1106 | print(node[1]); | |
1107 | emit('.'); | |
1108 | emit(node[2]->getCString()); | |
1109 | } | |
1110 | ||
1111 | void printSwitch(Ref node) { | |
1112 | emit("switch"); | |
1113 | space(); | |
1114 | emit('('); | |
1115 | print(node[1]); | |
1116 | emit(')'); | |
1117 | space(); | |
1118 | emit('{'); | |
1119 | newline(); | |
1120 | Ref cases = node[2]; | |
1121 | for (size_t i = 0; i < cases->size(); i++) { | |
1122 | Ref c = cases[i]; | |
1123 | if (!c[0]) { | |
1124 | emit("default:"); | |
1125 | } else { | |
1126 | emit("case "); | |
1127 | print(c[0]); | |
1128 | emit(':'); | |
1129 | } | |
1130 | if (c[1]->size() > 0) { | |
1131 | indent++; | |
1132 | newline(); | |
1133 | auto curr = used; | |
1134 | printStats(c[1]); | |
1135 | indent--; | |
1136 | if (curr != used) newline(); | |
1137 | else used--; // avoid the extra indentation we added tentatively | |
1138 | } else { | |
1139 | newline(); | |
1140 | } | |
1141 | } | |
1142 | emit('}'); | |
1143 | } | |
1144 | ||
1145 | void printTry(Ref node) { | |
1146 | emit("try "); | |
1147 | printBlock(node[1]); | |
1148 | emit(" catch ("); | |
1149 | printName(node[2]); | |
1150 | emit(") "); | |
1151 | printBlock(node[3]); | |
1152 | } | |
1153 | ||
1154 | void printSub(Ref node) { | |
1155 | printChild(node[1], node, -1); | |
1156 | emit('['); | |
1157 | print(node[2]); | |
1158 | emit(']'); | |
1159 | } | |
1160 | ||
1161 | void printVar(Ref node) { | |
1162 | emit("var "); | |
1163 | Ref args = node[1]; | |
1164 | for (size_t i = 0; i < args->size(); i++) { | |
1165 | if (i > 0) (pretty ? emit(", ") : emit(',')); | |
1166 | emit(args[i][0]->getCString()); | |
1167 | if (args[i]->size() > 1) { | |
1168 | space(); | |
1169 | emit('='); | |
1170 | space(); | |
1171 | print(args[i][1]); | |
1172 | } | |
1173 | } | |
1174 | } | |
1175 | ||
1176 | static bool ifHasElse(Ref node) { | |
1177 | assert(node->isArray() && node[0] == IF); | |
1178 | return node->size() >= 4 && !!node[3]; | |
1179 | } | |
1180 | ||
1181 | void printIf(Ref node) { | |
1182 | emit("if"); | |
1183 | safeSpace(); | |
1184 | emit('('); | |
1185 | print(node[1]); | |
1186 | emit(')'); | |
1187 | space(); | |
1188 | // special case: we need braces to save us from ambiguity, if () { if () } else. otherwise else binds to inner if | |
1189 | // also need to recurse for if () { if () { } else { if () } else | |
1190 | // (note that this is only a problem if the if body has a single element in it, not a block or such, as then | |
1191 | // the block would be braced) | |
1192 | // this analysis is a little conservative - it assumes any child if could be confused with us, which implies | |
1193 | // all other braces vanished (the worst case for us, we are not saved by other braces). | |
1194 | bool needBraces = false; | |
1195 | bool hasElse = ifHasElse(node); | |
1196 | if (hasElse) { | |
1197 | Ref child = node[2]; | |
1198 | while (child->isArray() && child[0] == IF) { | |
1199 | if (!ifHasElse(child)) { | |
1200 | needBraces = true; | |
1201 | break; | |
1202 | } | |
1203 | child = child[3]; // continue into the else | |
1204 | } | |
1205 | } | |
1206 | if (needBraces) { | |
1207 | emit('{'); | |
1208 | indent++; | |
1209 | newline(); | |
1210 | print(node[2]); | |
1211 | indent--; | |
1212 | newline(); | |
1213 | emit('}'); | |
1214 | } else { | |
1215 | print(node[2], "{}"); | |
1216 | if (!isBlock(node[2])) emit(';'); | |
1217 | } | |
1218 | if (hasElse) { | |
1219 | space(); | |
1220 | emit("else"); | |
1221 | safeSpace(); | |
1222 | print(node[3], "{}"); | |
1223 | if (!isBlock(node[3])) emit(';'); | |
1224 | } | |
1225 | } | |
1226 | ||
1227 | void printDo(Ref node) { | |
1228 | emit("do"); | |
1229 | safeSpace(); | |
1230 | print(node[2], "{}"); | |
1231 | space(); | |
1232 | emit("while"); | |
1233 | space(); | |
1234 | emit('('); | |
1235 | print(node[1]); | |
1236 | emit(')'); | |
1237 | } | |
1238 | ||
1239 | void printWhile(Ref node) { | |
1240 | emit("while"); | |
1241 | space(); | |
1242 | emit('('); | |
1243 | print(node[1]); | |
1244 | emit(')'); | |
1245 | space(); | |
1246 | print(node[2], "{}"); | |
1247 | } | |
1248 | ||
1249 | void printLabel(Ref node) { | |
1250 | emit(node[1]->getCString()); | |
1251 | space(); | |
1252 | emit(':'); | |
1253 | space(); | |
1254 | print(node[2]); | |
1255 | } | |
1256 | ||
1257 | void printReturn(Ref node) { | |
1258 | emit("return"); | |
1259 | if (!!node[1]) { | |
1260 | emit(' '); | |
1261 | print(node[1]); | |
1262 | } | |
1263 | } | |
1264 | ||
1265 | void printBreak(Ref node) { | |
1266 | emit("break"); | |
1267 | if (!!node[1]) { | |
1268 | emit(' '); | |
1269 | emit(node[1]->getCString()); | |
1270 | } | |
1271 | } | |
1272 | ||
1273 | void printContinue(Ref node) { | |
1274 | emit("continue"); | |
1275 | if (!!node[1]) { | |
1276 | emit(' '); | |
1277 | emit(node[1]->getCString()); | |
1278 | } | |
1279 | } | |
1280 | ||
1281 | void printNew(Ref node) { | |
1282 | emit("new "); | |
1283 | print(node[1]); | |
1284 | } | |
1285 | ||
1286 | void printArray(Ref node) { | |
1287 | emit('['); | |
1288 | Ref args = node[1]; | |
1289 | for (size_t i = 0; i < args->size(); i++) { | |
1290 | if (i > 0) (pretty ? emit(", ") : emit(',')); | |
1291 | print(args[i]); | |
1292 | } | |
1293 | emit(']'); | |
1294 | } | |
1295 | ||
1296 | void printObject(Ref node) { | |
1297 | emit('{'); | |
1298 | indent++; | |
1299 | newline(); | |
1300 | Ref args = node[1]; | |
1301 | for (size_t i = 0; i < args->size(); i++) { | |
1302 | if (i > 0) { | |
1303 | pretty ? emit(", ") : emit(','); | |
1304 | newline(); | |
1305 | } | |
1306 | const char *str = args[i][0]->getCString(); | |
1307 | const char *check = str; | |
1308 | bool needQuote = false; | |
1309 | while (*check) { | |
1310 | if (!isalnum(*check) && *check != '_' && *check != '$') { | |
1311 | needQuote = true; | |
1312 | break; | |
1313 | } | |
1314 | check++; | |
1315 | } | |
1316 | if (needQuote) emit('"'); | |
1317 | emit(str); | |
1318 | if (needQuote) emit('"'); | |
1319 | emit(":"); | |
1320 | space(); | |
1321 | print(args[i][1]); | |
1322 | } | |
1323 | indent--; | |
1324 | newline(); | |
1325 | emit('}'); | |
1326 | } | |
1327 | }; | |
1328 | ||
1329 | ||
1330 | // cashew builder | |
1331 | ||
1332 | class ValueBuilder { | |
1333 | static Ref makeRawString(const IString& s) { | |
1334 | return &arena.alloc<Value>()->setString(s); | |
1335 | } | |
1336 | ||
1337 | static Ref makeNull() { | |
1338 | return &arena.alloc<Value>()->setNull(); | |
1339 | } | |
1340 | ||
1341 | public: | |
1342 | static Ref makeRawArray(int size_hint=0) { | |
1343 | return &arena.alloc<Value>()->setArray(size_hint); | |
1344 | } | |
1345 | ||
1346 | static Ref makeToplevel() { | |
1347 | return &makeRawArray(2)->push_back(makeRawString(TOPLEVEL)) | |
1348 | .push_back(makeRawArray()); | |
1349 | } | |
1350 | ||
1351 | static Ref makeString(IString str) { | |
1352 | return &makeRawArray(2)->push_back(makeRawString(STRING)) | |
1353 | .push_back(makeRawString(str)); | |
1354 | } | |
1355 | ||
1356 | static Ref makeBlock() { | |
1357 | return &makeRawArray(2)->push_back(makeRawString(BLOCK)) | |
1358 | .push_back(makeRawArray()); | |
1359 | } | |
1360 | ||
1361 | static Ref makeName(IString name) { | |
1362 | return makeRawString(name); | |
1363 | } | |
1364 | ||
1365 | static void setBlockContent(Ref target, Ref block) { | |
1366 | if (target[0] == TOPLEVEL) { | |
1367 | target[1]->setArray(block[1]->getArray()); | |
1368 | } else if (target[0] == DEFUN) { | |
1369 | target[3]->setArray(block[1]->getArray()); | |
1370 | } else abort(); | |
1371 | } | |
1372 | ||
1373 | static void appendToBlock(Ref block, Ref element) { | |
1374 | assert(block[0] == BLOCK); | |
1375 | block[1]->push_back(element); | |
1376 | } | |
1377 | ||
1378 | static Ref makeCall(Ref target) { | |
1379 | return &makeRawArray(3)->push_back(makeRawString(CALL)) | |
1380 | .push_back(target) | |
1381 | .push_back(makeRawArray()); | |
1382 | } | |
1383 | static Ref makeCall(Ref target, Ref arg) { | |
1384 | Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) | |
1385 | .push_back(target) | |
1386 | .push_back(makeRawArray()); | |
1387 | ret[2]->push_back(arg); | |
1388 | return ret; | |
1389 | } | |
1390 | static Ref makeCall(IString target) { | |
1391 | Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) | |
1392 | .push_back(makeName(target)) | |
1393 | .push_back(makeRawArray()); | |
1394 | return ret; | |
1395 | } | |
1396 | ||
1397 | template<typename ...Ts> | |
1398 | static Ref makeCall(IString target, Ts... args) { | |
1399 | size_t nArgs = sizeof...(Ts); | |
1400 | Ref callArgs = makeRawArray(nArgs); | |
1401 | Ref argArray[] = {args...}; | |
1402 | for (size_t i = 0; i < nArgs; ++i) { | |
1403 | callArgs->push_back(argArray[i]); | |
1404 | } | |
1405 | return &makeRawArray(3)->push_back(makeRawString(CALL)) | |
1406 | .push_back(makeName(target)) | |
1407 | .push_back(callArgs); | |
1408 | } | |
1409 | ||
1410 | static void appendToCall(Ref call, Ref element) { | |
1411 | assert(call[0] == CALL); | |
1412 | call[2]->push_back(element); | |
1413 | } | |
1414 | ||
1415 | static Ref makeStatement(Ref contents) { | |
1416 | return contents; | |
1417 | } | |
1418 | ||
1419 | static Ref makeDouble(double num) { | |
1420 | return &arena.alloc<Value>()->setNumber(num); | |
1421 | } | |
1422 | static Ref makeInt(uint32_t num) { | |
1423 | return makeDouble(double(num)); | |
1424 | } | |
1425 | static Ref makeNum(double num) { | |
1426 | return makeDouble(num); | |
1427 | } | |
1428 | ||
1429 | static Ref makeUnary(IString op, Ref value) { | |
1430 | return &makeRawArray(3)->push_back(makeRawString(UNARY_PREFIX)) | |
1431 | .push_back(makeRawString(op)) | |
1432 | .push_back(value); | |
1433 | } | |
1434 | ||
1435 | static Ref makeBinary(Ref left, IString op, Ref right) { | |
1436 | if (op == SET) { | |
1437 | if (left->isString()) { | |
1438 | return &arena.alloc<AssignName>()->setAssignName(left->getIString(), right); | |
1439 | } else { | |
1440 | return &arena.alloc<Assign>()->setAssign(left, right); | |
1441 | } | |
1442 | } else if (op == COMMA) { | |
1443 | return &makeRawArray(3)->push_back(makeRawString(SEQ)) | |
1444 | .push_back(left) | |
1445 | .push_back(right); | |
1446 | } else { | |
1447 | return &makeRawArray(4)->push_back(makeRawString(BINARY)) | |
1448 | .push_back(makeRawString(op)) | |
1449 | .push_back(left) | |
1450 | .push_back(right); | |
1451 | } | |
1452 | } | |
1453 | ||
1454 | static Ref makePrefix(IString op, Ref right) { | |
1455 | return &makeRawArray(3)->push_back(makeRawString(UNARY_PREFIX)) | |
1456 | .push_back(makeRawString(op)) | |
1457 | .push_back(right); | |
1458 | } | |
1459 | ||
1460 | static Ref makeFunction(IString name) { | |
1461 | return &makeRawArray(4)->push_back(makeRawString(DEFUN)) | |
1462 | .push_back(makeRawString(name)) | |
1463 | .push_back(makeRawArray()) | |
1464 | .push_back(makeRawArray()); | |
1465 | } | |
1466 | ||
1467 | static void appendArgumentToFunction(Ref func, IString arg) { | |
1468 | assert(func[0] == DEFUN); | |
1469 | func[2]->push_back(makeRawString(arg)); | |
1470 | } | |
1471 | ||
1472 | static Ref makeVar(bool is_const=false) { | |
1473 | return &makeRawArray(2)->push_back(makeRawString(VAR)) | |
1474 | .push_back(makeRawArray()); | |
1475 | } | |
1476 | ||
1477 | static void appendToVar(Ref var, IString name, Ref value) { | |
1478 | assert(var[0] == VAR); | |
1479 | Ref array = &makeRawArray(1)->push_back(makeRawString(name)); | |
1480 | if (!!value) array->push_back(value); | |
1481 | var[1]->push_back(array); | |
1482 | } | |
1483 | ||
1484 | static Ref makeReturn(Ref value) { | |
1485 | return &makeRawArray(2)->push_back(makeRawString(RETURN)) | |
1486 | .push_back(!!value ? value : makeNull()); | |
1487 | } | |
1488 | ||
1489 | static Ref makeIndexing(Ref target, Ref index) { | |
1490 | return &makeRawArray(3)->push_back(makeRawString(SUB)) | |
1491 | .push_back(target) | |
1492 | .push_back(index); | |
1493 | } | |
1494 | ||
1495 | static Ref makeIf(Ref condition, Ref ifTrue, Ref ifFalse) { | |
1496 | return &makeRawArray(4)->push_back(makeRawString(IF)) | |
1497 | .push_back(condition) | |
1498 | .push_back(ifTrue) | |
1499 | .push_back(!!ifFalse ? ifFalse : makeNull()); | |
1500 | } | |
1501 | ||
1502 | static Ref makeConditional(Ref condition, Ref ifTrue, Ref ifFalse) { | |
1503 | return &makeRawArray(4)->push_back(makeRawString(CONDITIONAL)) | |
1504 | .push_back(condition) | |
1505 | .push_back(ifTrue) | |
1506 | .push_back(ifFalse); | |
1507 | } | |
1508 | ||
1509 | static Ref makeSeq(Ref left, Ref right) { | |
1510 | return &makeRawArray(3)->push_back(makeRawString(SEQ)) | |
1511 | .push_back(left) | |
1512 | .push_back(right); | |
1513 | } | |
1514 | ||
1515 | static Ref makeDo(Ref body, Ref condition) { | |
1516 | return &makeRawArray(3)->push_back(makeRawString(DO)) | |
1517 | .push_back(condition) | |
1518 | .push_back(body); | |
1519 | } | |
1520 | ||
1521 | static Ref makeWhile(Ref condition, Ref body) { | |
1522 | return &makeRawArray(3)->push_back(makeRawString(WHILE)) | |
1523 | .push_back(condition) | |
1524 | .push_back(body); | |
1525 | } | |
1526 | ||
1527 | static Ref makeFor(Ref init, Ref condition, Ref inc, Ref body) { | |
1528 | return &makeRawArray(5)->push_back(makeRawString(FOR)) | |
1529 | .push_back(init) | |
1530 | .push_back(condition) | |
1531 | .push_back(inc) | |
1532 | .push_back(body); | |
1533 | } | |
1534 | ||
1535 | static Ref makeBreak(IString label) { | |
1536 | return &makeRawArray(2)->push_back(makeRawString(BREAK)) | |
1537 | .push_back(!!label ? makeRawString(label) : makeNull()); | |
1538 | } | |
1539 | ||
1540 | static Ref makeContinue(IString label) { | |
1541 | return &makeRawArray(2)->push_back(makeRawString(CONTINUE)) | |
1542 | .push_back(!!label ? makeRawString(label) : makeNull()); | |
1543 | } | |
1544 | ||
1545 | static Ref makeLabel(IString name, Ref body) { | |
1546 | return &makeRawArray(3)->push_back(makeRawString(LABEL)) | |
1547 | .push_back(makeRawString(name)) | |
1548 | .push_back(body); | |
1549 | } | |
1550 | ||
1551 | static Ref makeSwitch(Ref input) { | |
1552 | return &makeRawArray(3)->push_back(makeRawString(SWITCH)) | |
1553 | .push_back(input) | |
1554 | .push_back(makeRawArray()); | |
1555 | } | |
1556 | ||
1557 | static void appendCaseToSwitch(Ref switch_, Ref arg) { | |
1558 | assert(switch_[0] == SWITCH); | |
1559 | switch_[2]->push_back(&makeRawArray(2)->push_back(arg) | |
1560 | .push_back(makeRawArray())); | |
1561 | } | |
1562 | ||
1563 | static void appendDefaultToSwitch(Ref switch_) { | |
1564 | assert(switch_[0] == SWITCH); | |
1565 | switch_[2]->push_back(&makeRawArray(2)->push_back(makeNull()) | |
1566 | .push_back(makeRawArray())); | |
1567 | } | |
1568 | ||
1569 | static void appendCodeToSwitch(Ref switch_, Ref code, bool explicitBlock) { | |
1570 | assert(switch_[0] == SWITCH); | |
1571 | assert(code[0] == BLOCK); | |
1572 | if (!explicitBlock) { | |
1573 | for (size_t i = 0; i < code[1]->size(); i++) { | |
1574 | switch_[2]->back()->back()->push_back(code[1][i]); | |
1575 | } | |
1576 | } else { | |
1577 | switch_[2]->back()->back()->push_back(code); | |
1578 | } | |
1579 | } | |
1580 | ||
1581 | static Ref makeTry(Ref try_, Ref arg, Ref catch_) { | |
1582 | assert(try_[0] == BLOCK); | |
1583 | assert(catch_[0] == BLOCK); | |
1584 | return &makeRawArray(3)->push_back(makeRawString(TRY)) | |
1585 | .push_back(try_) | |
1586 | .push_back(arg) | |
1587 | .push_back(catch_); | |
1588 | } | |
1589 | ||
1590 | static Ref makeDot(Ref obj, IString key) { | |
1591 | return &makeRawArray(3)->push_back(makeRawString(DOT)) | |
1592 | .push_back(obj) | |
1593 | .push_back(makeRawString(key)); | |
1594 | } | |
1595 | ||
1596 | template<typename ...Ts> | |
1597 | static Ref makeDot(Ref obj, Ref key, Ts... args) { | |
1598 | return makeDot(makeDot(obj, key), args...); | |
1599 | } | |
1600 | ||
1601 | static Ref makeDot(Ref obj, Ref key) { | |
1602 | assert(key->isString()); | |
1603 | return makeDot(obj, key->getIString()); | |
1604 | } | |
1605 | ||
1606 | static Ref makeNew(Ref call) { | |
1607 | return &makeRawArray(2)->push_back(makeRawString(NEW)) | |
1608 | .push_back(call); | |
1609 | } | |
1610 | ||
1611 | static Ref makeArray() { | |
1612 | return &makeRawArray(2)->push_back(makeRawString(ARRAY)) | |
1613 | .push_back(makeRawArray()); | |
1614 | } | |
1615 | ||
1616 | static void appendToArray(Ref array, Ref element) { | |
1617 | assert(array[0] == ARRAY); | |
1618 | array[1]->push_back(element); | |
1619 | } | |
1620 | ||
1621 | static Ref makeObject() { | |
1622 | return &makeRawArray(2)->push_back(makeRawString(OBJECT)) | |
1623 | .push_back(makeRawArray()); | |
1624 | } | |
1625 | ||
1626 | static void appendToObject(Ref array, IString key, Ref value) { | |
1627 | assert(array[0] == OBJECT); | |
1628 | array[1]->push_back(&makeRawArray(2)->push_back(makeRawString(key)) | |
1629 | .push_back(value)); | |
1630 | } | |
1631 | ||
1632 | static Ref makeSub(Ref obj, Ref index) { | |
1633 | return &makeRawArray(2)->push_back(makeRawString(SUB)) | |
1634 | .push_back(obj) | |
1635 | .push_back(index); | |
1636 | } | |
1637 | ||
1638 | static Ref makePtrShift(Ref ptr, int shifts) { | |
1639 | return makeBinary(ptr, RSHIFT, makeInt(shifts)); | |
1640 | } | |
1641 | }; | |
1642 | ||
1643 | // Tolerates 0.0 in the input; does not trust a +() to be there. | |
1644 | class DotZeroValueBuilder : public ValueBuilder { | |
1645 | public: | |
1646 | static Ref makeDouble(double num) { | |
1647 | return makePrefix(PLUS, ValueBuilder::makeDouble(num)); | |
1648 | } | |
1649 | }; | |
1650 | ||
1651 | } // namespace cashew | |
1652 | ||
1653 | #endif // wasm_simple_ast_h |