| /* |
| * Copyright 2015 WebAssembly Community Group participants |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "simple_ast.h" |
| |
| namespace cashew { |
| |
| // Ref methods |
| |
| Ref& Ref::operator[](unsigned x) { |
| return (*get())[x]; |
| } |
| |
| Ref& Ref::operator[](IString x) { |
| return (*get())[x]; |
| } |
| |
| bool Ref::operator==(const char *str) { |
| return get()->isString() && !strcmp(get()->str.str, str); |
| } |
| |
| bool Ref::operator!=(const char *str) { |
| return get()->isString() ? !!strcmp(get()->str.str, str) : true; |
| } |
| |
| bool Ref::operator==(const IString &str) { |
| return get()->isString() && get()->str == str; |
| } |
| |
| bool Ref::operator!=(const IString &str) { |
| return get()->isString() && get()->str != str; |
| } |
| |
| bool Ref::operator==(Ref other) { |
| return **this == *other; |
| } |
| |
| bool Ref::operator!() { |
| return !get() || get()->isNull(); |
| } |
| |
| // Arena |
| |
| Arena arena; |
| |
| Arena::~Arena() { |
| for (auto* chunk : chunks) { |
| delete[] chunk; |
| } |
| for (auto* chunk : arr_chunks) { |
| delete[] chunk; |
| } |
| } |
| |
| Ref Arena::alloc() { |
| if (chunks.size() == 0 || index == CHUNK_SIZE) { |
| chunks.push_back(new Value[CHUNK_SIZE]); |
| index = 0; |
| } |
| return &chunks.back()[index++]; |
| } |
| |
| ArrayStorage* Arena::allocArray() { |
| if (arr_chunks.size() == 0 || arr_index == CHUNK_SIZE) { |
| arr_chunks.push_back(new ArrayStorage[CHUNK_SIZE]); |
| arr_index = 0; |
| } |
| return &arr_chunks.back()[arr_index++]; |
| } |
| |
| // dump |
| |
| void dump(const char *str, Ref node, bool pretty) { |
| std::cerr << str << ": "; |
| if (!!node) node->stringify(std::cerr, pretty); |
| else std::cerr << "(nullptr)"; |
| std::cerr << std::endl; |
| } |
| |
| // AST traversals |
| |
| // Traversals |
| |
| struct TraverseInfo { |
| TraverseInfo() {} |
| TraverseInfo(Ref node, ArrayStorage* arr) : node(node), arr(arr), index(0) {} |
| Ref node; |
| ArrayStorage* arr; |
| int index; |
| }; |
| |
| template <class T, int init> |
| struct StackedStack { // a stack, on the stack |
| T stackStorage[init]; |
| T* storage; |
| int used, available; // used amount, available amount |
| bool alloced; |
| |
| StackedStack() : used(0), available(init), alloced(false) { |
| storage = stackStorage; |
| } |
| ~StackedStack() { |
| if (alloced) free(storage); |
| } |
| |
| int size() { return used; } |
| |
| void push_back(const T& t) { |
| assert(used <= available); |
| if (used == available) { |
| available *= 2; |
| if (!alloced) { |
| T* old = storage; |
| storage = (T*)malloc(sizeof(T)*available); |
| memcpy(storage, old, sizeof(T)*used); |
| alloced = true; |
| } else { |
| T *newStorage = (T*)realloc(storage, sizeof(T)*available); |
| assert(newStorage); |
| storage = newStorage; |
| } |
| } |
| assert(used < available); |
| assert(storage); |
| storage[used++] = t; |
| } |
| |
| T& back() { |
| assert(used > 0); |
| return storage[used-1]; |
| } |
| |
| void pop_back() { |
| assert(used > 0); |
| used--; |
| } |
| }; |
| |
| #define visitable(node) (node->isArray() && node->size() > 0) |
| |
| #define TRAV_STACK 40 |
| |
| // Traverse, calling visit before the children |
| void traversePre(Ref node, std::function<void (Ref)> visit) { |
| if (!visitable(node)) return; |
| visit(node); |
| StackedStack<TraverseInfo, TRAV_STACK> stack; |
| int index = 0; |
| ArrayStorage* arr = &node->getArray(); |
| int arrsize = (int)arr->size(); |
| Ref* arrdata = arr->data(); |
| stack.push_back(TraverseInfo(node, arr)); |
| while (1) { |
| if (index < arrsize) { |
| Ref sub = *(arrdata+index); |
| index++; |
| if (visitable(sub)) { |
| stack.back().index = index; |
| index = 0; |
| visit(sub); |
| arr = &sub->getArray(); |
| arrsize = (int)arr->size(); |
| arrdata = arr->data(); |
| stack.push_back(TraverseInfo(sub, arr)); |
| } |
| } else { |
| stack.pop_back(); |
| if (stack.size() == 0) break; |
| TraverseInfo& back = stack.back(); |
| index = back.index; |
| arr = back.arr; |
| arrsize = (int)arr->size(); |
| arrdata = arr->data(); |
| } |
| } |
| } |
| |
| // Traverse, calling visitPre before the children and visitPost after |
| void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::function<void (Ref)> visitPost) { |
| if (!visitable(node)) return; |
| visitPre(node); |
| StackedStack<TraverseInfo, TRAV_STACK> stack; |
| int index = 0; |
| ArrayStorage* arr = &node->getArray(); |
| int arrsize = (int)arr->size(); |
| Ref* arrdata = arr->data(); |
| stack.push_back(TraverseInfo(node, arr)); |
| while (1) { |
| if (index < arrsize) { |
| Ref sub = *(arrdata+index); |
| index++; |
| if (visitable(sub)) { |
| stack.back().index = index; |
| index = 0; |
| visitPre(sub); |
| arr = &sub->getArray(); |
| arrsize = (int)arr->size(); |
| arrdata = arr->data(); |
| stack.push_back(TraverseInfo(sub, arr)); |
| } |
| } else { |
| visitPost(stack.back().node); |
| stack.pop_back(); |
| if (stack.size() == 0) break; |
| TraverseInfo& back = stack.back(); |
| index = back.index; |
| arr = back.arr; |
| arrsize = (int)arr->size(); |
| arrdata = arr->data(); |
| } |
| } |
| } |
| |
| // Traverse, calling visitPre before the children and visitPost after. If pre returns false, do not traverse children |
| void traversePrePostConditional(Ref node, std::function<bool (Ref)> visitPre, std::function<void (Ref)> visitPost) { |
| if (!visitable(node)) return; |
| if (!visitPre(node)) return; |
| StackedStack<TraverseInfo, TRAV_STACK> stack; |
| int index = 0; |
| ArrayStorage* arr = &node->getArray(); |
| int arrsize = (int)arr->size(); |
| Ref* arrdata = arr->data(); |
| stack.push_back(TraverseInfo(node, arr)); |
| while (1) { |
| if (index < arrsize) { |
| Ref sub = *(arrdata+index); |
| index++; |
| if (visitable(sub)) { |
| if (visitPre(sub)) { |
| stack.back().index = index; |
| index = 0; |
| arr = &sub->getArray(); |
| arrsize = (int)arr->size(); |
| arrdata = arr->data(); |
| stack.push_back(TraverseInfo(sub, arr)); |
| } |
| } |
| } else { |
| visitPost(stack.back().node); |
| stack.pop_back(); |
| if (stack.size() == 0) break; |
| TraverseInfo& back = stack.back(); |
| index = back.index; |
| arr = back.arr; |
| arrsize = (int)arr->size(); |
| arrdata = arr->data(); |
| } |
| } |
| } |
| |
| // Traverses all the top-level functions in the document |
| void traverseFunctions(Ref ast, std::function<void (Ref)> visit) { |
| if (!ast || ast->size() == 0) return; |
| if (ast[0] == TOPLEVEL) { |
| Ref stats = ast[1]; |
| for (size_t i = 0; i < stats->size(); i++) { |
| Ref curr = stats[i]; |
| if (curr[0] == DEFUN) visit(curr); |
| } |
| } else if (ast[0] == DEFUN) { |
| visit(ast); |
| } |
| } |
| |
| // ValueBuilder |
| |
| IStringSet ValueBuilder::statable("assign call binary unary-prefix name num conditional dot new sub seq string object array"); |
| |
| } // namespace cashew |