/* * 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 GlobalMixedArena arena; // Value Value& Value::setAssign(Ref target, Ref value) { asAssign()->target() = target; asAssign()->value() = value; return *this; } Value& Value::setAssignName(IString target, Ref value) { asAssignName()->target() = target; asAssignName()->value() = value; return *this; } Assign* Value::asAssign() { assert(isAssign()); return static_cast(this); } AssignName* Value::asAssignName() { assert(isAssignName()); return static_cast(this); } void Value::stringify(std::ostream &os, bool pretty) { static int indent = 0; #define indentify() { for (int i_ = 0; i_ < indent; i_++) os << " "; } switch (type) { case String: { if (str.str) { os << '"' << str.str << '"'; } else { os << "\"(null)\""; } break; } case Number: { os << std::setprecision(17) << num; // doubles can have 17 digits of precision break; } case Array: { if (arr->size() == 0) { os << "[]"; break; } os << '['; if (pretty) { os << std::endl; indent++; } for (size_t i = 0; i < arr->size(); i++) { if (i > 0) { if (pretty) os << "," << std::endl; else os << ", "; } indentify(); (*arr)[i]->stringify(os, pretty); } if (pretty) { os << std::endl; indent--; } indentify(); os << ']'; break; } case Null: { os << "null"; break; } case Bool: { os << (boo ? "true" : "false"); break; } case Object: { os << '{'; if (pretty) { os << std::endl; indent++; } bool first = true; for (auto i : *obj) { if (first) { first = false; } else { os << ", "; if (pretty) os << std::endl; } indentify(); os << '"' << i.first.c_str() << "\": "; i.second->stringify(os, pretty); } if (pretty) { os << std::endl; indent--; } indentify(); os << '}'; break; } case Assign_: { os << "["; ref->stringify(os, pretty); os << ", "; asAssign()->value()->stringify(os, pretty); os << "]"; break; } case AssignName_: { os << "[\"" << asAssignName()->target().str << "\""; os << ", "; asAssignName()->value()->stringify(os, pretty); os << "]"; break; } } } // 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() = default; TraverseInfo(Ref node, ArrayStorage* arr) : node(node), arr(arr), index(0) {} Ref node; ArrayStorage* arr; int index; }; template struct StackedStack { // a stack, on the stack T stackStorage[init]; T* storage; int used = 0; int available = init; // used amount, available amount bool alloced = false; StackedStack() { 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 visit) { if (!visitable(node)) return; visit(node); StackedStack stack; int index = 0; ArrayStorage* arr = &node->getArray(); int arrsize = (int)arr->size(); Ref* arrdata = &(*arr)[0]; 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)[0]; 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)[0]; } } } // Traverse, calling visitPre before the children and visitPost after void traversePrePost(Ref node, std::function visitPre, std::function visitPost) { if (!visitable(node)) return; visitPre(node); StackedStack stack; int index = 0; ArrayStorage* arr = &node->getArray(); int arrsize = (int)arr->size(); Ref* arrdata = &(*arr)[0]; 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)[0]; 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)[0]; } } } // Traverse, calling visitPre before the children and visitPost after. If pre returns false, do not traverse children void traversePrePostConditional(Ref node, std::function visitPre, std::function visitPost) { if (!visitable(node)) return; if (!visitPre(node)) return; StackedStack stack; int index = 0; ArrayStorage* arr = &node->getArray(); int arrsize = (int)arr->size(); Ref* arrdata = &(*arr)[0]; 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)[0]; 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)[0]; } } } // Traverses all the top-level functions in the document void traverseFunctions(Ref ast, std::function 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); } } } // namespace cashew