summaryrefslogtreecommitdiff
path: root/src/wasm-s-parser.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm-s-parser.h')
-rw-r--r--src/wasm-s-parser.h450
1 files changed, 450 insertions, 0 deletions
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
new file mode 100644
index 000000000..41e6e852c
--- /dev/null
+++ b/src/wasm-s-parser.h
@@ -0,0 +1,450 @@
+
+//
+// Parses WebAssembly code in S-Expression format, as in .wast files.
+//
+
+#include "wasm.h"
+#include "mixed_arena.h"
+
+#define abort_on(str) { std::cerr << "aborting on " << str << '\n'; abort(); }
+
+namespace wasm {
+
+int debug;
+
+using namespace cashew;
+// Globals
+
+IString MODULE("module"),
+ FUNC("func"),
+ PARAM("param"),
+ RESULT("result"),
+ MEMORY("memory"),
+ EXPORT("export"),
+ TABLE("table"),
+ LOCAL("local");
+
+//
+// An element in an S-Expression: a list or a string
+//
+
+class Element {
+ typedef std::vector<Element*> List;
+
+ bool isList_;
+ union {
+ List list_;
+ IString str_;
+ };
+
+public:
+ Element() : isList_(true) {}
+
+ bool isList() { return isList_; }
+ bool isString() { return !isList_; }
+
+ // list methods
+
+ List& list() {
+ assert(isList_);
+ return list_;
+ }
+
+ Element* operator[](unsigned i) {
+ return list()[i];
+ }
+
+ size_t size() {
+ return list().size();
+ }
+
+ // string methods
+
+ IString str() {
+ assert(!isList_);
+ return str_;
+ }
+
+ Element* setString(IString str__) {
+ isList_ = false;
+ str_ = str__;
+ return this;
+ }
+
+ // printing
+
+ friend std::ostream& operator<<(std::ostream& o, Element& e) {
+ if (e.isList_) {
+ o << '(';
+ for (auto item : e.list_) o << ' ' << *item;
+ o << " )";
+ } else {
+ o << e.str_.str;
+ }
+ }
+};
+
+//
+// Generic S-Expression parsing into lists
+//
+
+class SExpressionParser {
+ char *beginning;
+ char* input;
+
+ MixedArena allocator;
+
+public:
+ // Assumes control of and modifies the input.
+ SExpressionParser(char* input) : beginning(input), input(input) {}
+
+ Element* parseEverything() {
+ return parseInnerList();
+ }
+
+private:
+ // parses the internal part of a list, inside the parens.
+ Element* parseInnerList() {
+ if (input[0] == ';') {
+ // comment
+ input++;
+ input = strstr(input, ";)");
+ assert(input);
+ return nullptr;
+ }
+ auto ret = allocator.alloc<Element>();
+ while (1) {
+ Element* curr = parse();
+ if (!curr) return ret;
+ ret->list().push_back(curr);
+ }
+ }
+
+ Element* parse() {
+ skipWhitespace();
+ if (input[0] == 0 || input[0] == ')') return nullptr;
+ if (input[0] == '(') {
+ // a list
+ input++;
+ auto ret = parseInnerList();
+ skipWhitespace();
+ assert(input[0] == ')');
+ input++;
+ return ret;
+ }
+ return parseString();
+ }
+
+ void skipWhitespace() {
+ while (isspace(input[0])) input++;
+ }
+
+ Element* parseString() {
+ char *start = input;
+ while (input[0] && !isspace(input[0]) && input[0] != ')') input++;
+ char temp = input[0];
+ input[0] = 0;
+ auto ret = allocator.alloc<Element>()->setString(IString(start, false)); // TODO: reuse the string here, carefully
+ input[0] = temp;
+ return ret;
+ }
+};
+
+//
+// SExpressions => WebAssembly module
+//
+
+class SExpressionWasmBuilder {
+ Module& wasm;
+
+ MixedArena allocator;
+ SExpressionParser parser;
+
+public:
+ // Assumes control of and modifies the input.
+ SExpressionWasmBuilder(Module& wasm, char* input) : wasm(wasm), parser(input) {
+ Element* root = parser.parseEverything();
+ if (debug) std::cout << *root << '\n';
+ assert(root);
+ Element* module = (*root)[0];
+ assert((*module)[0]->str() == MODULE);
+ for (unsigned i = 1; i < module->size(); i++) {
+ parseModuleElement(*(*module)[i]);
+ }
+ }
+
+private:
+
+ void parseModuleElement(Element& curr) {
+ IString id = curr[0]->str();
+ if (id == FUNC) return parseFunction(curr);
+ if (id == MEMORY) return parseMemory(curr);
+ if (id == EXPORT) return parseExport(curr);
+ if (id == TABLE) return parseTable(curr);
+ std::cerr << "bad module element " << id.str << '\n';
+ abort();
+ }
+
+ std::map<Name, WasmType> currLocalTypes;
+
+ void parseFunction(Element& s) {
+ auto func = allocator.alloc<Function>();
+ func->name = s[1]->str();
+ for (unsigned i = 2; i < s.size(); i++) {
+ Element& curr = *s[i];
+ IString id = curr[0]->str();
+ if (id == PARAM) {
+ IString name = curr[1]->str();
+ WasmType type = stringToWasmType(curr[2]->str());
+ func->params.emplace_back(name, type);
+ currLocalTypes[name] = type;
+ } else if (id == RESULT) {
+ func->result = stringToWasmType(curr[1]->str());
+ } else if (id == LOCAL) {
+ IString name = curr[1]->str();
+ WasmType type = stringToWasmType(curr[2]->str());
+ func->locals.emplace_back(name, type);
+ currLocalTypes[name] = type;
+ } else {
+ func->body = parseExpression(curr);
+ }
+ }
+ currLocalTypes.clear();
+ }
+
+ static WasmType stringToWasmType(IString str) {
+ return stringToWasmType(str.str);
+ }
+
+ static WasmType stringToWasmType(const char* str) {
+ if (str[0] == 'i') {
+ if (str[1] == '3') return i32;
+ return i64;
+ }
+ if (str[0] == 'f') {
+ if (str[1] == '3') return f32;
+ return f64;
+ }
+ abort();
+ }
+
+ Expression* parseExpression(Element& s) {
+ if (debug) std::cerr << "parse expression " << s << '\n';
+ IString id = s[0]->str();
+ const char *str = id.str;
+ const char *dot = strchr(str, '.');
+ if (dot) {
+ // type.operation (e.g. i32.add)
+ WasmType type = stringToWasmType(str);
+ const char *op = dot + 1;
+ switch (op[0]) {
+ case 'a': {
+ if (op[1] == 'd') return makeBinary(s, BinaryOp::Add, type);
+ if (op[1] == 'n') return makeBinary(s, BinaryOp::And, type);
+ abort_on(op);
+ }
+ case 'c': {
+ if (op[1] == 'o') {
+ if (op[2] == 'p') return makeBinary(s, BinaryOp::CopySign, type);
+ if (op[2] == 'n') {
+ if (op[3] == 'v') return makeConvert(s, op[8] == 'u' ? ConvertOp::ConvertUInt32 : ConvertOp::ConvertSInt32, type);
+ if (op[3] == 's') return makeConst(s, type);
+ }
+ }
+ if (op[1] == 'l') return makeUnary(s, UnaryOp::Clz, type);
+ abort_on(op);
+ }
+ case 'd': {
+ if (op[1] == 'i') {
+ if (op[3] == '_') return makeBinary(s, op[4] == 'u' ? BinaryOp::DivU : BinaryOp::DivS, type);
+ if (op[3] == 0) return makeBinary(s, BinaryOp::Div, type);
+ }
+ abort_on(op);
+ }
+ case 'e': {
+ if (op[1] == 'q') return makeCompare(s, RelationalOp::Eq, type);
+ abort_on(op);
+ }
+ case 'f': {
+ if (op[1] == 'l') return makeUnary(s, UnaryOp::Floor, type);
+ abort_on(op);
+ }
+ case 'g': {
+ if (op[1] == 't') {
+ if (op[2] == '_') return makeCompare(s, op[3] == 'u' ? RelationalOp::GtU : RelationalOp::GtS, type);
+ if (op[2] == 0) return makeCompare(s, RelationalOp::Gt, type);
+ }
+ if (op[1] == 'e') {
+ if (op[2] == '_') return makeCompare(s, op[3] == 'u' ? RelationalOp::GeU : RelationalOp::GeS, type);
+ if (op[2] == 0) return makeCompare(s, RelationalOp::Ge, type);
+ }
+ abort_on(op);
+ }
+ case 'l': {
+ if (op[1] == 't') {
+ if (op[2] == '_') return makeCompare(s, op[3] == 'u' ? RelationalOp::LtU : RelationalOp::LtS, type);
+ if (op[2] == 0) return makeCompare(s, RelationalOp::Lt, type);
+ }
+ if (op[1] == 'e') {
+ if (op[2] == '_') return makeCompare(s, op[3] == 'u' ? RelationalOp::LeU : RelationalOp::LeS, type);
+ if (op[2] == 0) return makeCompare(s, RelationalOp::Le, type);
+ }
+ abort_on(op);
+ }
+ case 'm': {
+ if (op[1] == 'i') return makeBinary(s, BinaryOp::Min, type);
+ if (op[1] == 'a') return makeBinary(s, BinaryOp::Max, type);
+ if (op[1] == 'u') return makeBinary(s, BinaryOp::Mul, type);
+ abort_on(op);
+ }
+ case 'n': {
+ if (op[1] == 'e') {
+ if (op[2] == 0) return makeCompare(s, RelationalOp::Ne, type);
+ if (op[2] == 'g') return makeUnary(s, UnaryOp::Neg, type);
+ }
+ abort_on(op);
+ }
+ case 'o': {
+ if (op[1] == 'r') return makeBinary(s, BinaryOp::Or, type);
+ abort_on(op);
+ }
+ case 'r': {
+ if (op[1] == 'e') {
+ return makeBinary(s, op[3] == 'u' ? BinaryOp::RemU : BinaryOp::RemS, type);
+ }
+ abort_on(op);
+ }
+ case 's': {
+ if (op[1] == 'h') {
+ if (op[2] == 'l') return makeBinary(s, BinaryOp::Shl, type);
+ return makeBinary(s, op[4] == 'u' ? BinaryOp::ShrU : BinaryOp::ShrS, type);
+ }
+ if (op[1] == 'u') return makeBinary(s, BinaryOp::Sub, type);
+ abort_on(op);
+ }
+ case 't': {
+ if (op[1] == 'r') return makeConvert(s, ConvertOp::TruncSFloat64, type);
+ abort_on(op);
+ }
+ case 'x': {
+ if (op[1] == 'o') return makeBinary(s, BinaryOp::Xor, type);
+ abort_on(op);
+ }
+ default: abort_on(op);
+ }
+ } else {
+ // other expression
+ switch (str[0]) {
+ case 'b': {
+ if (str[1] == 'l') return makeBlock(s);
+ abort_on(str);
+ }
+ case 'g': {
+ if (str[1] == 'e') return makeGetLocal(s);
+ abort_on(str);
+ }
+ case 's': {
+ if (str[1] == 'e') return makeSetLocal(s);
+ abort_on(str);
+ }
+ default: abort_on(str);
+ }
+ }
+ }
+
+ Expression* makeBinary(Element& s, BinaryOp op, WasmType type) {
+ auto ret = allocator.alloc<Binary>();
+ ret->op = op;
+ ret->left = parseExpression(*s[1]);
+ ret->right = parseExpression(*s[2]);
+ ret->type = type;
+ return ret;
+ }
+
+ Expression* makeUnary(Element& s, UnaryOp op, WasmType type) {
+ auto ret = allocator.alloc<Unary>();
+ ret->op = op;
+ ret->value = parseExpression(*s[1]);
+ ret->type = type;
+ return ret;
+ }
+
+ Expression* makeCompare(Element& s, RelationalOp op, WasmType type) {
+ auto ret = allocator.alloc<Compare>();
+ ret->op = op;
+ ret->left = parseExpression(*s[1]);
+ ret->right = parseExpression(*s[2]);
+ ret->type = type;
+ return ret;
+ }
+
+ Expression* makeConvert(Element& s, ConvertOp op, WasmType type) {
+ auto ret = allocator.alloc<Convert>();
+ ret->op = op;
+ ret->value = parseExpression(*s[1]);
+ ret->type = type;
+ return ret;
+ }
+
+ Expression* makeGetLocal(Element& s) {
+ auto ret = allocator.alloc<GetLocal>();
+ ret->name = s[1]->str();
+ ret->type = currLocalTypes[ret->name];
+ return ret;
+ }
+
+ Expression* makeSetLocal(Element& s) {
+ auto ret = allocator.alloc<SetLocal>();
+ ret->name = s[1]->str();
+ ret->value = parseExpression(*s[2]);
+ ret->type = currLocalTypes[ret->name];
+ return ret;
+ }
+
+ Expression* makeBlock(Element& s) {
+ auto ret = allocator.alloc<Block>();
+ size_t i = 1;
+ if (s[1]->isString()) {
+ ret->name = s[1]->str();
+ i++;
+ }
+ for (; i < s.size(); i++) {
+ ret->list.push_back(parseExpression(*s[i]));
+ }
+ return ret;
+ }
+
+ Expression* makeConst(Element& s, WasmType type) {
+ auto ret = allocator.alloc<Const>();
+ ret->value.type = type;
+ const char *value = s[1]->str().str;
+ switch (type) {
+ case i32: ret->value.i32 = atoi(value); break;
+ case i64: ret->value.i64 = atol(value); break;
+ case f32: ret->value.f32 = atof(value); break;
+ case f64: ret->value.f64 = atof(value); break;
+ default: abort();
+ }
+ return ret;
+ }
+
+ void parseMemory(Element& s) {
+ wasm.memorySize = atoi(s[1]->str().str);
+ }
+
+ void parseExport(Element& s) {
+ Export ex;
+ ex.name = s[1]->str();
+ ex.value = s[2]->str();
+ wasm.exports.push_back(ex);
+ }
+
+ void parseTable(Element& s) {
+ for (size_t i = 1; i < s.size(); i++) {
+ wasm.table.names.push_back(s[i]->str());
+ }
+ }
+};
+
+} // namespace wasm
+