blob: 75685f80e3b94caf0553e21eceb973ac931b933e [file] [log] [blame] [edit]
/*
* 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<Assign*>(this);
}
AssignName* Value::asAssignName() {
assert(isAssignName());
return static_cast<AssignName*>(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() {}
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)[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<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)[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<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)[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<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);
}
}
} // namespace cashew