]>
git.proxmox.com Git - rustc.git/blob - src/binaryen/src/emscripten-optimizer/simple_ast.h
2 * Copyright 2015 WebAssembly Community Group participants
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #ifndef wasm_simple_ast_h
18 #define wasm_simple_ast_h
32 #include <unordered_map>
33 #include <unordered_set>
38 #include "support/safe_integer.h"
39 #include "mixed_arena.h"
41 #define err(str) fprintf(stderr, str "\n");
42 #define errv(str, ...) fprintf(stderr, str "\n", __VA_ARGS__);
50 void dump(const char *str
, Ref node
, bool pretty
=false);
52 // Reference to a value, plus some operators for convenience
56 Ref(Value
*v
=nullptr) : inst(v
) {}
58 Value
* get() { return inst
; }
60 Value
& operator*() { return *inst
; }
61 Value
* operator->() { return inst
; }
62 Ref
& operator[](unsigned x
);
63 Ref
& operator[](IString x
);
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
75 // Arena allocation, free it all on process exit
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
{
83 auto* ret
= static_cast<T
*>(allocSpace(sizeof(T
)));
89 extern GlobalMixedArena arena
;
91 class ArrayStorage
: public ArenaVectorBase
<ArrayStorage
, Ref
> {
93 void allocate(size_t size
) {
94 allocatedElements
= size
;
95 data
= static_cast<Ref
*>(arena
.allocSpace(sizeof(Ref
) * allocatedElements
));
111 Assign_
= 6, // ref = target
117 typedef std::unordered_map
<IString
, Ref
> ObjectStorage
;
119 #ifdef _MSC_VER // MSVC does not allow unrestricted unions: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf
122 union { // TODO: optimize
133 // constructors all copy their input
134 Value() : type(Null
), num(0) {}
135 explicit Value(const char *s
) : type(Null
) {
138 explicit Value(double n
) : type(Null
) {
141 explicit Value(ArrayStorage
&a
) : type(Null
) {
145 // no bool constructor - would endanger the double one (int might convert the wrong way)
152 if (type
== Array
) { arr
->clear(); }
153 else if (type
== Object
) delete obj
;
158 Value
& setString(const char *s
) {
164 Value
& setString(const IString
&s
) {
170 Value
& setNumber(double n
) {
176 Value
& setArray(ArrayStorage
&a
) {
179 arr
= arena
.alloc
<ArrayStorage
>();
183 Value
& setArray(size_t size_hint
=0) {
186 arr
= arena
.alloc
<ArrayStorage
>();
187 arr
->reserve(size_hint
);
195 Value
& setBool(bool b
) { // Bool in the name, as otherwise might overload over int
204 obj
= new ObjectStorage();
207 Value
& setAssign(Ref target
, Ref value
);
208 Value
& setAssignName(IString target
, Ref value
);
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_
; }
219 bool isBool(bool b
) { return type
== Bool
&& b
== boo
; } // avoid overloading == as it might overload over int
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
;
229 const char* getCString() {
233 IString
& getIString() {
237 double& getNumber() {
241 ArrayStorage
& getArray() {
251 AssignName
* asAssignName();
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
260 Value
& operator=(const Value
& other
) {
262 switch (other
.type
) {
264 setString(other
.str
);
267 setNumber(other
.num
);
270 setArray(*other
.arr
);
284 bool operator==(const Value
& other
) {
285 if (type
!= other
.type
) return false;
286 switch (other
.type
) {
288 return str
== other
.str
;
290 return num
== other
.num
;
292 return this == &other
; // if you want a deep compare, use deepCompare
296 return boo
== other
.boo
;
298 return this == &other
; // if you want a deep compare, use deepCompare
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++; }
312 char *close
= strchr(curr
, '"');
314 *close
= 0; // end this string, and reuse it straight from the input
317 } else if (*curr
== '[') {
322 while (*curr
!= ']') {
323 Ref temp
= arena
.alloc
<Value
>();
324 arr
->push_back(temp
);
325 curr
= temp
->parse(curr
);
327 if (*curr
== ']') break;
328 assert(*curr
== ',');
333 } else if (*curr
== 'n') {
335 assert(strncmp(curr
, "null", 4) == 0);
338 } else if (*curr
== 't') {
340 assert(strncmp(curr
, "true", 4) == 0);
343 } else if (*curr
== 'f') {
345 assert(strncmp(curr
, "false", 5) == 0);
348 } else if (*curr
== '{') {
353 while (*curr
!= '}') {
354 assert(*curr
== '"');
356 char *close
= strchr(curr
, '"');
358 *close
= 0; // end this string, and reuse it straight from the input
362 assert(*curr
== ':');
365 Ref value
= arena
.alloc
<Value
>();
366 curr
= value
->parse(curr
);
369 if (*curr
== '}') break;
370 assert(*curr
== ',');
378 setNumber(strtod(curr
, &after
));
384 void stringify(std::ostream
&os
, bool pretty
=false);
397 void setSize(size_t size
) {
399 auto old
= arr
->size();
400 if (old
!= size
) arr
->resize(size
);
402 for (auto i
= old
; i
< size
; i
++) {
403 (*arr
)[i
] = arena
.alloc
<Value
>();
408 Ref
& operator[](unsigned x
) {
413 Value
& push_back(Ref r
) {
420 Ref ret
= arr
->back();
427 if (arr
->size() == 0) return nullptr;
431 void splice(int x
, int num
) {
433 arr
->erase(arr
->begin() + x
, arr
->begin() + x
+ num
);
436 int indexOf(Ref other
) {
438 for (size_t i
= 0; i
< arr
->size(); i
++) {
439 if (other
== (*arr
)[i
]) return i
;
444 Ref
map(std::function
<Ref (Ref node
)> func
) {
446 Ref ret
= arena
.alloc
<Value
>();
448 for (size_t i
= 0; i
< arr
->size(); i
++) {
449 ret
->push_back(func((*arr
)[i
]));
454 Ref
filter(std::function
<bool (Ref node
)> func
) {
456 Ref ret
= arena
.alloc
<Value
>();
458 for (size_t i
= 0; i
< arr
->size(); i
++) {
459 Ref curr
= (*arr
)[i
];
460 if (func(curr
)) ret
->push_back(curr
);
466 void forEach(std::function<void (Ref)> func) {
467 for (size_t i = 0; i < arr->size(); i++) {
479 Ref
& operator[](IString x
) {
484 bool has(IString x
) {
486 return obj
->count(x
) > 0;
490 struct Assign
: public Value
{
493 Assign(Ref targetInit
, Ref valueInit
) {
495 target() = targetInit
;
499 Assign() : Assign(nullptr, nullptr) {}
509 struct AssignName
: public Value
{
512 AssignName(IString targetInit
, Ref valueInit
) {
514 target() = targetInit
;
518 AssignName() : AssignName(IString(), nullptr) {}
530 // Traverse, calling visit before the children
531 void traversePre(Ref node
, std::function
<void (Ref
)> visit
);
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
);
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
);
539 // Traverses all the top-level functions in the document
540 void traverseFunctions(Ref ast
, std::function
<void (Ref
)> visit
);
542 // JS printing support
545 bool pretty
, finalize
;
551 bool possibleSpace
; // add a space to separate identifiers
555 JSPrinter(bool pretty_
, bool finalize_
, Ref ast_
) : pretty(pretty_
), finalize(finalize_
), buffer(0), size(0), used(0), indent(0), possibleSpace(false), ast(ast_
) {}
568 void ensure(int safety
=100) {
569 if (size
>= used
+ safety
) {
572 size
= std::max((size_t)1024, size
* 2) + safety
;
574 buffer
= (char*)malloc(size
);
576 errv("Out of memory allocating %zd bytes for output buffer!", size
);
580 char *buf
= (char*)realloc(buffer
, size
);
583 errv("Out of memory allocating %zd bytes for output buffer!", size
);
592 if (!pretty
&& c
== '}' && buffer
[used
-1] == ';') used
--; // optimize ;} into }, the ; is not separating anything
597 void emit(const char *s
) {
601 strncpy(buffer
+ used
, s
, len
+1);
608 for (int i
= 0; i
< indent
; i
++) emit(' ');
612 if (pretty
) emit(' ');
616 if (pretty
) emit(' ');
617 else possibleSpace
= true;
620 void maybeSpace(char s
) {
622 possibleSpace
= false;
623 if (isIdentPart(s
)) emit(' ');
627 bool isNothing(Ref node
) {
628 return node
->isArray() && node
[0] == TOPLEVEL
&& node
[1]->size() == 0;
631 bool isDefun(Ref node
) {
632 return node
->isArray() && node
[0] == DEFUN
;
635 bool isBlock(Ref node
) {
636 return node
->isArray() && node
[0] == BLOCK
;
639 bool isIf(Ref node
) {
640 return node
->isArray() && node
[0] == IF
;
643 void print(Ref node
) {
645 if (node
->isString()) {
649 if (node
->isNumber()) {
653 if (node
->isAssignName()) {
654 printAssignName(node
);
657 if (node
->isAssign()) {
661 IString type
= node
[0]->getIString();
662 switch (type
.str
[0]) {
664 if (type
== ARRAY
) printArray(node
);
669 if (type
== BINARY
) printBinary(node
);
670 else if (type
== BLOCK
) printBlock(node
);
671 else if (type
== BREAK
) printBreak(node
);
676 if (type
== CALL
) printCall(node
);
677 else if (type
== CONDITIONAL
) printConditional(node
);
678 else if (type
== CONTINUE
) printContinue(node
);
683 if (type
== DEFUN
) printDefun(node
);
684 else if (type
== DO
) printDo(node
);
685 else if (type
== DOT
) printDot(node
);
690 if (type
== IF
) printIf(node
);
695 if (type
== LABEL
) printLabel(node
);
700 if (type
== NEW
) printNew(node
);
705 if (type
== OBJECT
) printObject(node
);
709 if (type
== RETURN
) printReturn(node
);
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
);
722 if (type
== TOPLEVEL
) printToplevel(node
);
723 else if (type
== TRY
) printTry(node
);
728 if (type
== UNARY_PREFIX
) printUnaryPrefix(node
);
733 if (type
== VAR
) printVar(node
);
738 if (type
== WHILE
) printWhile(node
);
743 errv("cannot yet print %s\n", type
.str
);
749 // print a node, and if nothing is emitted, emit something instead
750 void print(Ref node
, const char *otherwise
) {
753 if (used
== last
) emit(otherwise
);
756 void printStats(Ref stats
) {
758 for (size_t i
= 0; i
< stats
->size(); i
++) {
760 if (!isNothing(curr
)) {
761 if (first
) first
= false;
764 if (!isDefun(curr
) && !isBlock(curr
) && !isIf(curr
)) {
771 void printToplevel(Ref node
) {
772 if (node
[1]->size() > 0) {
777 void printBlock(Ref node
) {
778 if (node
->size() == 1 || node
[1]->size() == 0) {
791 void printDefun(Ref node
) {
793 emit(node
[1]->getCString());
796 for (size_t i
= 0; i
< args
->size(); i
++) {
797 if (i
> 0) (pretty
? emit(", ") : emit(','));
798 emit(args
[i
]->getCString());
802 if (node
->size() == 3 || node
[3]->size() == 0) {
816 void printAssign(Ref node
) {
817 auto* assign
= node
->asAssign();
818 printChild(assign
->target(), node
, -1);
822 printChild(assign
->value(), node
, 1);
825 void printAssignName(Ref node
) {
826 auto *assign
= node
->asAssignName();
827 emit(assign
->target().c_str());
831 printChild(assign
->value(), node
, 1);
834 void printName(Ref node
) {
835 emit(node
->getCString());
838 static char* numToString(double d
, bool finalize
=true) {
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
;
852 static char format
[6];
853 for (int i
= 0; i
<= 18; i
++) {
858 format
[3] = e
? 'e' : 'f';
862 format
[3] = '0' + (i
- 10);
863 format
[4] = e
? 'e' : 'f';
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;
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
);
879 unsigned long long tempULL
;
880 sscanf(buffer
, "%llx", &tempULL
);
881 temp
= (double)tempULL
;
883 sscanf(buffer
, "%lf", &temp
);
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
);
890 //errv("%.18f, %.18e => %s => %.18f, %.18e, %llu (%d)\n", d, d, buffer, temp, temp, uu, temp == d);
892 (e
? err_e
: err_f
) = fabs(temp
- d
);
893 //errv("current attempt: %.18f => %s", d, buffer);
895 char *dot
= strchr(buffer
, '.');
897 // remove trailing zeros
899 while (*end
>= '0' && *end
<= '9') end
++;
901 while (*end
== '0') {
905 } while (*copy
++ != 0);
908 //errv("%.18f => %s", d, buffer);
909 // remove preceding zeros
910 while (*buffer
== '0') {
914 } while (*copy
++ != 0);
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);
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
;
931 } else if (num
< 100) {
932 test
[1] = '0' + (num
/ 10);
933 test
[2] = '0' + (num
% 10);
937 test
[1] = '0' + (num
/ 100);
938 test
[2] = '0' + (num
% 100) / 10;
939 test
[3] = '0' + (num
% 10);
944 //errv("..current attempt: %.18f => %s", d, buffer);
946 //fprintf(stderr, "options:\n%s\n%s\n (first? %d)\n", storage_e, storage_f, strlen(storage_e) < strlen(storage_f));
948 if (err_e
== err_f
) {
949 ret
= strlen(storage_e
) < strlen(storage_f
) ? storage_e
: storage_f
;
951 ret
= err_e
< err_f
? storage_e
: storage_f
;
954 ret
--; // safe to go back one, there is one more char in full_*
960 void printNum(Ref node
) {
961 emit(numToString(node
->getNumber(), finalize
));
964 void printString(Ref node
) {
966 emit(node
[1]->getCString());
972 bool capturesOperators(Ref node
) {
974 return type
== CALL
|| type
== ARRAY
|| type
== OBJECT
|| type
== SEQ
;
977 int getPrecedence(Ref node
, bool parent
) {
978 if (node
->isAssign() || node
->isAssignName()) {
979 return OperatorClass::getPrecedence(OperatorClass::Binary
, SET
);
981 if (!node
->isArray()) {
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
);
995 // otherwise, this is something that fixes precedence explicitly, and we can ignore
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);
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)
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;
1023 void printChild(Ref child
, Ref parent
, int childPosition
=0) {
1024 bool parens
= needParens(parent
, child
, childPosition
);
1025 if (parens
) emit('(');
1027 if (parens
) emit(')');
1030 void printBinary(Ref node
) {
1031 printChild(node
[2], node
, -1);
1033 emit(node
[1]->getCString());
1035 printChild(node
[3], node
, 1);
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
1046 ensure(1); // we temporarily append a 0
1047 char *curr
= buffer
+ last
; // ensure might invalidate
1049 if (strchr(curr
, '.')) return; // already a decimal point, all good
1050 char *e
= strchr(curr
, 'e');
1056 curr
= buffer
+ last
; // ensure might invalidate
1057 char *end
= strchr(curr
, 0);
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
1071 emit(node
[1]->getCString());
1072 printChild(node
[2], node
, 1);
1075 void printConditional(Ref node
) {
1076 printChild(node
[1], node
, -1);
1080 printChild(node
[2], node
, 0);
1084 printChild(node
[3], node
, 1);
1087 void printCall(Ref node
) {
1088 printChild(node
[1], node
, 0);
1091 for (size_t i
= 0; i
< args
->size(); i
++) {
1092 if (i
> 0) (pretty
? emit(", ") : emit(','));
1093 printChild(args
[i
], node
, 0);
1098 void printSeq(Ref node
) {
1099 printChild(node
[1], node
, -1);
1102 printChild(node
[2], node
, 1);
1105 void printDot(Ref node
) {
1108 emit(node
[2]->getCString());
1111 void printSwitch(Ref node
) {
1120 Ref cases
= node
[2];
1121 for (size_t i
= 0; i
< cases
->size(); i
++) {
1130 if (c
[1]->size() > 0) {
1136 if (curr
!= used
) newline();
1137 else used
--; // avoid the extra indentation we added tentatively
1145 void printTry(Ref node
) {
1147 printBlock(node
[1]);
1151 printBlock(node
[3]);
1154 void printSub(Ref node
) {
1155 printChild(node
[1], node
, -1);
1161 void printVar(Ref node
) {
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) {
1176 static bool ifHasElse(Ref node
) {
1177 assert(node
->isArray() && node
[0] == IF
);
1178 return node
->size() >= 4 && !!node
[3];
1181 void printIf(Ref node
) {
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
);
1197 Ref child
= node
[2];
1198 while (child
->isArray() && child
[0] == IF
) {
1199 if (!ifHasElse(child
)) {
1203 child
= child
[3]; // continue into the else
1215 print(node
[2], "{}");
1216 if (!isBlock(node
[2])) emit(';');
1222 print(node
[3], "{}");
1223 if (!isBlock(node
[3])) emit(';');
1227 void printDo(Ref node
) {
1230 print(node
[2], "{}");
1239 void printWhile(Ref node
) {
1246 print(node
[2], "{}");
1249 void printLabel(Ref node
) {
1250 emit(node
[1]->getCString());
1257 void printReturn(Ref node
) {
1265 void printBreak(Ref node
) {
1269 emit(node
[1]->getCString());
1273 void printContinue(Ref node
) {
1277 emit(node
[1]->getCString());
1281 void printNew(Ref node
) {
1286 void printArray(Ref node
) {
1289 for (size_t i
= 0; i
< args
->size(); i
++) {
1290 if (i
> 0) (pretty
? emit(", ") : emit(','));
1296 void printObject(Ref node
) {
1301 for (size_t i
= 0; i
< args
->size(); i
++) {
1303 pretty
? emit(", ") : emit(',');
1306 const char *str
= args
[i
][0]->getCString();
1307 const char *check
= str
;
1308 bool needQuote
= false;
1310 if (!isalnum(*check
) && *check
!= '_' && *check
!= '$') {
1316 if (needQuote
) emit('"');
1318 if (needQuote
) emit('"');
1332 class ValueBuilder
{
1333 static Ref
makeRawString(const IString
& s
) {
1334 return &arena
.alloc
<Value
>()->setString(s
);
1337 static Ref
makeNull() {
1338 return &arena
.alloc
<Value
>()->setNull();
1342 static Ref
makeRawArray(int size_hint
=0) {
1343 return &arena
.alloc
<Value
>()->setArray(size_hint
);
1346 static Ref
makeToplevel() {
1347 return &makeRawArray(2)->push_back(makeRawString(TOPLEVEL
))
1348 .push_back(makeRawArray());
1351 static Ref
makeString(IString str
) {
1352 return &makeRawArray(2)->push_back(makeRawString(STRING
))
1353 .push_back(makeRawString(str
));
1356 static Ref
makeBlock() {
1357 return &makeRawArray(2)->push_back(makeRawString(BLOCK
))
1358 .push_back(makeRawArray());
1361 static Ref
makeName(IString name
) {
1362 return makeRawString(name
);
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());
1373 static void appendToBlock(Ref block
, Ref element
) {
1374 assert(block
[0] == BLOCK
);
1375 block
[1]->push_back(element
);
1378 static Ref
makeCall(Ref target
) {
1379 return &makeRawArray(3)->push_back(makeRawString(CALL
))
1381 .push_back(makeRawArray());
1383 static Ref
makeCall(Ref target
, Ref arg
) {
1384 Ref ret
= &makeRawArray(3)->push_back(makeRawString(CALL
))
1386 .push_back(makeRawArray());
1387 ret
[2]->push_back(arg
);
1390 static Ref
makeCall(IString target
) {
1391 Ref ret
= &makeRawArray(3)->push_back(makeRawString(CALL
))
1392 .push_back(makeName(target
))
1393 .push_back(makeRawArray());
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
]);
1405 return &makeRawArray(3)->push_back(makeRawString(CALL
))
1406 .push_back(makeName(target
))
1407 .push_back(callArgs
);
1410 static void appendToCall(Ref call
, Ref element
) {
1411 assert(call
[0] == CALL
);
1412 call
[2]->push_back(element
);
1415 static Ref
makeStatement(Ref contents
) {
1419 static Ref
makeDouble(double num
) {
1420 return &arena
.alloc
<Value
>()->setNumber(num
);
1422 static Ref
makeInt(uint32_t num
) {
1423 return makeDouble(double(num
));
1425 static Ref
makeNum(double num
) {
1426 return makeDouble(num
);
1429 static Ref
makeUnary(IString op
, Ref value
) {
1430 return &makeRawArray(3)->push_back(makeRawString(UNARY_PREFIX
))
1431 .push_back(makeRawString(op
))
1435 static Ref
makeBinary(Ref left
, IString op
, Ref right
) {
1437 if (left
->isString()) {
1438 return &arena
.alloc
<AssignName
>()->setAssignName(left
->getIString(), right
);
1440 return &arena
.alloc
<Assign
>()->setAssign(left
, right
);
1442 } else if (op
== COMMA
) {
1443 return &makeRawArray(3)->push_back(makeRawString(SEQ
))
1447 return &makeRawArray(4)->push_back(makeRawString(BINARY
))
1448 .push_back(makeRawString(op
))
1454 static Ref
makePrefix(IString op
, Ref right
) {
1455 return &makeRawArray(3)->push_back(makeRawString(UNARY_PREFIX
))
1456 .push_back(makeRawString(op
))
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());
1467 static void appendArgumentToFunction(Ref func
, IString arg
) {
1468 assert(func
[0] == DEFUN
);
1469 func
[2]->push_back(makeRawString(arg
));
1472 static Ref
makeVar(bool is_const
=false) {
1473 return &makeRawArray(2)->push_back(makeRawString(VAR
))
1474 .push_back(makeRawArray());
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
);
1484 static Ref
makeReturn(Ref value
) {
1485 return &makeRawArray(2)->push_back(makeRawString(RETURN
))
1486 .push_back(!!value
? value
: makeNull());
1489 static Ref
makeIndexing(Ref target
, Ref index
) {
1490 return &makeRawArray(3)->push_back(makeRawString(SUB
))
1495 static Ref
makeIf(Ref condition
, Ref ifTrue
, Ref ifFalse
) {
1496 return &makeRawArray(4)->push_back(makeRawString(IF
))
1497 .push_back(condition
)
1499 .push_back(!!ifFalse
? ifFalse
: makeNull());
1502 static Ref
makeConditional(Ref condition
, Ref ifTrue
, Ref ifFalse
) {
1503 return &makeRawArray(4)->push_back(makeRawString(CONDITIONAL
))
1504 .push_back(condition
)
1506 .push_back(ifFalse
);
1509 static Ref
makeSeq(Ref left
, Ref right
) {
1510 return &makeRawArray(3)->push_back(makeRawString(SEQ
))
1515 static Ref
makeDo(Ref body
, Ref condition
) {
1516 return &makeRawArray(3)->push_back(makeRawString(DO
))
1517 .push_back(condition
)
1521 static Ref
makeWhile(Ref condition
, Ref body
) {
1522 return &makeRawArray(3)->push_back(makeRawString(WHILE
))
1523 .push_back(condition
)
1527 static Ref
makeFor(Ref init
, Ref condition
, Ref inc
, Ref body
) {
1528 return &makeRawArray(5)->push_back(makeRawString(FOR
))
1530 .push_back(condition
)
1535 static Ref
makeBreak(IString label
) {
1536 return &makeRawArray(2)->push_back(makeRawString(BREAK
))
1537 .push_back(!!label
? makeRawString(label
) : makeNull());
1540 static Ref
makeContinue(IString label
) {
1541 return &makeRawArray(2)->push_back(makeRawString(CONTINUE
))
1542 .push_back(!!label
? makeRawString(label
) : makeNull());
1545 static Ref
makeLabel(IString name
, Ref body
) {
1546 return &makeRawArray(3)->push_back(makeRawString(LABEL
))
1547 .push_back(makeRawString(name
))
1551 static Ref
makeSwitch(Ref input
) {
1552 return &makeRawArray(3)->push_back(makeRawString(SWITCH
))
1554 .push_back(makeRawArray());
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()));
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()));
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
]);
1577 switch_
[2]->back()->back()->push_back(code
);
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
))
1590 static Ref
makeDot(Ref obj
, IString key
) {
1591 return &makeRawArray(3)->push_back(makeRawString(DOT
))
1593 .push_back(makeRawString(key
));
1596 template<typename
...Ts
>
1597 static Ref
makeDot(Ref obj
, Ref key
, Ts
... args
) {
1598 return makeDot(makeDot(obj
, key
), args
...);
1601 static Ref
makeDot(Ref obj
, Ref key
) {
1602 assert(key
->isString());
1603 return makeDot(obj
, key
->getIString());
1606 static Ref
makeNew(Ref call
) {
1607 return &makeRawArray(2)->push_back(makeRawString(NEW
))
1611 static Ref
makeArray() {
1612 return &makeRawArray(2)->push_back(makeRawString(ARRAY
))
1613 .push_back(makeRawArray());
1616 static void appendToArray(Ref array
, Ref element
) {
1617 assert(array
[0] == ARRAY
);
1618 array
[1]->push_back(element
);
1621 static Ref
makeObject() {
1622 return &makeRawArray(2)->push_back(makeRawString(OBJECT
))
1623 .push_back(makeRawArray());
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
))
1632 static Ref
makeSub(Ref obj
, Ref index
) {
1633 return &makeRawArray(2)->push_back(makeRawString(SUB
))
1638 static Ref
makePtrShift(Ref ptr
, int shifts
) {
1639 return makeBinary(ptr
, RSHIFT
, makeInt(shifts
));
1643 // Tolerates 0.0 in the input; does not trust a +() to be there.
1644 class DotZeroValueBuilder
: public ValueBuilder
{
1646 static Ref
makeDouble(double num
) {
1647 return makePrefix(PLUS
, ValueBuilder::makeDouble(num
));
1651 } // namespace cashew
1653 #endif // wasm_simple_ast_h