blob: d4a4b38b41b3c2fa27cc1e2e58b086d846c43bbb [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.
*/
//
// Parses WebAssembly code in S-Expression format, as in .wast files
// such as are in the spec test suite.
//
#ifndef wasm_wasm_s_parser_h
#define wasm_wasm_s_parser_h
#include <cmath>
#include "wasm.h"
#include "mixed_arena.h"
#include "shared-constants.h"
#include "parsing.h"
#include "asm_v_wasm.h"
namespace wasm {
using namespace cashew;
// Globals
int unhex(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
abort();
}
//
// An element in an S-Expression: a list or a string
//
class Element {
typedef std::vector<Element*> List;
bool isList_;
List list_;
IString str_;
bool dollared_;
public:
Element() : isList_(true) {}
bool isList() { return isList_; }
bool isStr() { return !isList_; }
bool dollared() { return dollared_; }
// 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_;
}
const char* c_str() {
assert(!isList_);
return str_.str;
}
Element* setString(IString str__, bool dollared__) {
isList_ = false;
str_ = str__;
dollared_ = dollared__;
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;
}
return o;
}
};
//
// Generic S-Expression parsing into lists
//
class SExpressionParser {
char* input;
MixedArena allocator;
public:
// Assumes control of and modifies the input.
SExpressionParser(char* input) : input(input) {
root = nullptr;
while (!root) { // keep parsing until we pass an initial comment
root = parseInnerList();
}
}
Element* root;
private:
// parses the internal part of a list, inside the parens.
Element* parseInnerList() {
if (input[0] == ';') {
// comment
input++;
if (input[0] == ';') {
while (input[0] != '\n') input++;
return nullptr;
}
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 (1) {
while (isspace(input[0])) input++;
if (input[0] == ';' && input[1] == ';') {
while (input[0] && input[0] != '\n') input++;
} else if (input[0] == '(' && input[1] == ';') {
input = strstr(input, ";)") + 2;
} else {
return;
}
}
}
Element* parseString() {
bool dollared = false;
if (input[0] == '$') {
input++;
dollared = true;
}
char *start = input;
if (input[0] == '"') {
// parse escaping \", but leave code escaped - we'll handle escaping in memory segments specifically
input++;
std::string str;
while (1) {
if (input[0] == '"') break;
if (input[0] == '\\') {
str += input[0];
str += input[1];
input += 2;
continue;
}
str += input[0];
input++;
}
input++;
return allocator.alloc<Element>()->setString(IString(str.c_str(), false), dollared);
}
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), dollared); // TODO: reuse the string here, carefully
input[0] = temp;
return ret;
}
};
//
// SExpressions => WebAssembly module
//
class SExpressionWasmBuilder {
AllocatingModule& wasm;
MixedArena& allocator;
std::function<void ()> onError;
int functionCounter;
std::map<Name, WasmType> functionTypes; // we need to know function return types before we parse their contents
bool debug;
public:
// Assumes control of and modifies the input.
SExpressionWasmBuilder(AllocatingModule& wasm, Element& module, std::function<void ()> onError, bool debug=false) : wasm(wasm), allocator(wasm.allocator), onError(onError), debug(debug) {
assert(module[0]->str() == MODULE);
functionCounter = 0;
for (unsigned i = 1; i < module.size(); i++) {
preParseFunctionType(*module[i]);
preParseImports(*module[i]);
}
functionCounter = 0;
for (unsigned i = 1; i < module.size(); i++) {
parseModuleElement(*module[i]);
}
}
private:
// pre-parse types and function definitions, so we know function return types before parsing their contents
void preParseFunctionType(Element& s) {
IString id = s[0]->str();
if (id == TYPE) return parseType(s);
if (id != FUNC) return;
size_t i = 1;
Name name;
if (s[i]->isStr()) {
name = s[i]->str();
i++;
} else {
// unnamed, use an index
name = Name::fromInt(functionCounter);
}
functionCounter++;
for (;i < s.size(); i++) {
Element& curr = *s[i];
IString id = curr[0]->str();
if (id == RESULT) {
functionTypes[name] = stringToWasmType(curr[1]->str());
return;
} else if (id == TYPE) {
Name typeName = curr[1]->str();
if (wasm.functionTypesMap.find(typeName) == wasm.functionTypesMap.end()) onError();
FunctionType* type = wasm.functionTypesMap[typeName];
functionTypes[name] = type->result;
return;
}
}
functionTypes[name] = none;
}
void preParseImports(Element& curr) {
IString id = curr[0]->str();
if (id == IMPORT) parseImport(curr);
}
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 == IMPORT) return; // already done
if (id == TABLE) return parseTable(curr);
if (id == TYPE) return; // already done
std::cerr << "bad module element " << id.str << '\n';
onError();
}
// function parsing state
Function *currFunction = nullptr;
std::map<Name, WasmType> currLocalTypes;
size_t localIndex; // params and locals
size_t otherIndex;
std::vector<Name> labelStack;
Name getPrefixedName(std::string prefix) {
return IString((prefix + std::to_string(otherIndex++)).c_str(), false);
}
void parseFunction(Element& s) {
auto func = currFunction = allocator.alloc<Function>();
size_t i = 1;
if (s[i]->isStr()) {
func->name = s[i]->str();
i++;
} else {
// unnamed, use an index
func->name = Name::fromInt(functionCounter);
}
functionCounter++;
func->body = nullptr;
localIndex = 0;
otherIndex = 0;
std::vector<NameType> typeParams; // we may have both params and a type. store the type info here
Block* autoBlock = nullptr; // we may need to add a block for the very top level
for (;i < s.size(); i++) {
Element& curr = *s[i];
IString id = curr[0]->str();
if (id == PARAM || id == LOCAL) {
size_t j = 1;
while (j < curr.size()) {
IString name;
WasmType type = none;
if (!curr[j]->dollared()) { // dollared input symbols cannot be types
type = stringToWasmType(curr[j]->str(), true);
}
if (type != none) {
// a type, so an unnamed parameter
name = Name::fromInt(localIndex);
} else {
name = curr[j]->str();
type = stringToWasmType(curr[j+1]->str());
j++;
}
j++;
if (id == PARAM) {
func->params.emplace_back(name, type);
} else {
func->locals.emplace_back(name, type);
}
localIndex++;
currLocalTypes[name] = type;
}
} else if (id == RESULT) {
func->result = stringToWasmType(curr[1]->str());
} else if (id == TYPE) {
Name name = curr[1]->str();
func->type = name;
if (wasm.functionTypesMap.find(name) == wasm.functionTypesMap.end()) onError();
FunctionType* type = wasm.functionTypesMap[name];
func->result = type->result;
for (size_t j = 0; j < type->params.size(); j++) {
IString name = Name::fromInt(j);
WasmType currType = type->params[j];
typeParams.emplace_back(name, currType);
currLocalTypes[name] = currType;
}
} else {
// body
if (typeParams.size() > 0 && func->params.size() == 0) {
func->params = typeParams;
}
Expression* ex = parseExpression(curr);
if (!func->body) {
func->body = ex;
} else {
if (!autoBlock) {
autoBlock = allocator.alloc<Block>();
autoBlock->list.push_back(func->body);
func->body = autoBlock;
}
autoBlock->list.push_back(ex);
}
}
}
if (!func->body) func->body = allocator.alloc<Nop>();
wasm.addFunction(func);
currLocalTypes.clear();
labelStack.clear();
currFunction = nullptr;
}
WasmType stringToWasmType(IString str, bool allowError=false, bool prefix=false) {
return stringToWasmType(str.str, allowError, prefix);
}
WasmType stringToWasmType(const char* str, bool allowError=false, bool prefix=false) {
if (str[0] == 'i') {
if (str[1] == '3' && str[2] == '2' && (prefix || str[3] == 0)) return i32;
if (str[1] == '6' && str[2] == '4' && (prefix || str[3] == 0)) return i64;
}
if (str[0] == 'f') {
if (str[1] == '3' && str[2] == '2' && (prefix || str[3] == 0)) return f32;
if (str[1] == '6' && str[2] == '4' && (prefix || str[3] == 0)) return f64;
}
if (allowError) return none;
onError();
abort();
}
public:
Expression* parseExpression(Element* s) {
return parseExpression(*s);
}
#define abort_on(str) { std::cerr << "aborting on " << str << '\n'; onError(); }
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, false, true);
const char *op = dot + 1;
switch (op[0]) {
case 'a': {
if (op[1] == 'b') return makeUnary(s, UnaryOp::Abs, type);
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] == 'e') return makeUnary(s, UnaryOp::Ceil, type);
if (op[1] == 'l') return makeUnary(s, UnaryOp::Clz, type);
if (op[1] == 'o') {
if (op[2] == 'p') return makeBinary(s, BinaryOp::CopySign, type);
if (op[2] == 'n') {
if (op[3] == 'v') {
if (op[8] == 's') return makeUnary(s, op[11] == '3' ? UnaryOp::ConvertSInt32 : UnaryOp::ConvertSInt64, type);
if (op[8] == 'u') return makeUnary(s, op[11] == '3' ? UnaryOp::ConvertUInt32 : UnaryOp::ConvertUInt64, type);
}
if (op[3] == 's') return makeConst(s, type);
}
}
if (op[1] == 't') return makeUnary(s, UnaryOp::Ctz, 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);
}
if (op[1] == 'e') return makeUnary(s, UnaryOp::DemoteFloat64, type);
abort_on(op);
}
case 'e': {
if (op[1] == 'q') return makeBinary(s, BinaryOp::Eq, type);
if (op[1] == 'x') return makeUnary(s, op[7] == 'u' ? UnaryOp::ExtendUInt32 : UnaryOp::ExtendSInt32, 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 makeBinary(s, op[3] == 'u' ? BinaryOp::GtU : BinaryOp::GtS, type);
if (op[2] == 0) return makeBinary(s, BinaryOp::Gt, type);
}
if (op[1] == 'e') {
if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BinaryOp::GeU : BinaryOp::GeS, type);
if (op[2] == 0) return makeBinary(s, BinaryOp::Ge, type);
}
abort_on(op);
}
case 'l': {
if (op[1] == 't') {
if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BinaryOp::LtU : BinaryOp::LtS, type);
if (op[2] == 0) return makeBinary(s, BinaryOp::Lt, type);
}
if (op[1] == 'e') {
if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BinaryOp::LeU : BinaryOp::LeS, type);
if (op[2] == 0) return makeBinary(s, BinaryOp::Le, type);
}
if (op[1] == 'o') return makeLoad(s, 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 makeBinary(s, BinaryOp::Ne, type);
if (op[2] == 'a') return makeUnary(s, UnaryOp::Nearest, 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 'p': {
if (op[1] == 'r') return makeUnary(s, UnaryOp::PromoteFloat32, type);
if (op[1] == 'o') return makeUnary(s, UnaryOp::Popcnt, type);
abort_on(op);
}
case 'r': {
if (op[1] == 'e') {
if (op[2] == 'm') return makeBinary(s, op[4] == 'u' ? BinaryOp::RemU : BinaryOp::RemS, type);
if (op[2] == 'i') return makeUnary(s, isWasmTypeFloat(type) ? UnaryOp::ReinterpretInt : UnaryOp::ReinterpretFloat, type);
}
abort_on(op);
}
case 's': {
if (op[1] == 'e') return makeSelect(s, type);
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);
if (op[1] == 'q') return makeUnary(s, UnaryOp::Sqrt, type);
if (op[1] == 't') return makeStore(s, type);
abort_on(op);
}
case 't': {
if (op[1] == 'r') {
if (op[6] == 's') return makeUnary(s, op[9] == '3' ? UnaryOp::TruncSFloat32 : UnaryOp::TruncSFloat64, type);
if (op[6] == 'u') return makeUnary(s, op[9] == '3' ? UnaryOp::TruncUFloat32 : UnaryOp::TruncUFloat64, type);
if (op[2] == 'u') return makeUnary(s, UnaryOp::Trunc, type);
}
abort_on(op);
}
case 'w': {
if (op[1] == 'r') return makeUnary(s, UnaryOp::WrapInt64, 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);
if (str[1] == 'r') return makeBreak(s);
abort_on(str);
}
case 'c': {
if (str[1] == 'a') {
if (id == CALL) return makeCall(s);
if (id == CALL_IMPORT) return makeCallImport(s);
if (id == CALL_INDIRECT) return makeCallIndirect(s);
}
abort_on(str);
}
case 'g': {
if (str[1] == 'e') return makeGetLocal(s);
if (str[1] == 'r') return makeHost(s, HostOp::GrowMemory);
abort_on(str);
}
case 'h': {
if (str[1] == 'a') return makeHost(s, HostOp::HasFeature);
abort_on(str);
}
case 'i': {
if (str[1] == 'f') return makeIf(s);
abort_on(str);
}
case 'l': {
if (str[1] == 'o') return makeLoop(s);
abort_on(str);
}
case 'm': {
if (str[1] == 'e') return makeHost(s, HostOp::MemorySize);
abort_on(str);
}
case 'n': {
if (str[1] == 'o') return allocator.alloc<Nop>();
abort_on(str);
}
case 'p': {
if (str[1] == 'a') return makeHost(s, HostOp::PageSize);
abort_on(str);
}
case 's': {
if (str[1] == 'e') return makeSetLocal(s);
abort_on(str);
}
case 'r': {
if (str[1] == 'e') return makeReturn(s);
abort_on(str);
}
case 't': {
if (str[1] == 'a') return makeSwitch(s); // aka tableswitch
abort_on(str);
}
case 'u': {
if (str[1] == 'n') return allocator.alloc<Unreachable>();
abort_on(str);
}
default: abort_on(str);
}
}
abort();
}
private:
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->finalize();
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* makeSelect(Element& s, WasmType type) {
auto ret = allocator.alloc<Select>();
ret->condition = parseExpression(s[1]);
ret->ifTrue = parseExpression(s[2]);
ret->ifFalse = parseExpression(s[3]);
ret->type = type;
return ret;
}
Expression* makeHost(Element& s, HostOp op) {
auto ret = allocator.alloc<Host>();
ret->op = op;
if (op == HostOp::HasFeature) {
ret->nameOperand = s[1]->str();
} else {
parseCallOperands(s, 1, ret);
}
ret->finalize();
return ret;
}
Name getLocalName(Element& s) {
if (s.dollared()) return s.str();
// this is a numeric index
size_t i = atoi(s.c_str());
size_t numParams = currFunction->params.size();
if (i < numParams) {
return currFunction->params[i].name;
} else {
return currFunction->locals[i - currFunction->params.size()].name;
}
}
Expression* makeGetLocal(Element& s) {
auto ret = allocator.alloc<GetLocal>();
ret->name = getLocalName(*s[1]);
ret->type = currLocalTypes[ret->name];
return ret;
}
Expression* makeSetLocal(Element& s) {
auto ret = allocator.alloc<SetLocal>();
ret->name = getLocalName(*s[1]);
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]->isStr()) {
ret->name = s[1]->str();
i++;
} else {
ret->name = getPrefixedName("block");
}
labelStack.push_back(ret->name);
for (; i < s.size(); i++) {
ret->list.push_back(parseExpression(s[i]));
}
labelStack.pop_back();
if (ret->list.size() > 0) ret->type = ret->list.back()->type;
return ret;
}
Expression* makeConst(Element& s, WasmType type) {
auto ret = parseConst(s[1]->str(), type, allocator);
if (!ret) onError();
return ret;
}
Expression* makeLoad(Element& s, WasmType type) {
const char *extra = strchr(s[0]->c_str(), '.') + 5; // after "type.load"
auto ret = allocator.alloc<Load>();
ret->type = type;
ret->bytes = getWasmTypeSize(type);
if (extra[0] == '8') {
ret->bytes = 1;
extra++;
} else if (extra[0] == '1') {
assert(extra[1] == '6');
ret->bytes = 2;
extra += 2;
} else if (extra[0] == '3') {
assert(extra[1] == '2');
ret->bytes = 4;
extra += 2;
}
ret->signed_ = extra[0] && extra[1] == 's';
size_t i = 1;
ret->offset = 0;
ret->align = 0;
while (!s[i]->isList()) {
const char *str = s[i]->c_str();
const char *eq = strchr(str, '=');
assert(eq);
eq++;
if (str[0] == 'a') {
ret->align = atoi(eq);
} else if (str[0] == 'o') {
uint64_t offset = atoll(eq);
if (offset > 0xffffffff) onError();
ret->offset = offset;
} else onError();
i++;
}
ret->ptr = parseExpression(s[i]);
return ret;
}
Expression* makeStore(Element& s, WasmType type) {
const char *extra = strchr(s[0]->c_str(), '.') + 6; // after "type.store"
auto ret = allocator.alloc<Store>();
ret->type = type;
ret->bytes = getWasmTypeSize(type);
if (extra[0] == '8') {
ret->bytes = 1;
extra++;
} else if (extra[0] == '1') {
assert(extra[1] == '6');
ret->bytes = 2;
extra += 2;
} else if (extra[0] == '3') {
assert(extra[1] == '2');
ret->bytes = 4;
extra += 2;
}
size_t i = 1;
ret->offset = 0;
ret->align = 0;
while (!s[i]->isList()) {
const char *str = s[i]->c_str();
const char *eq = strchr(str, '=');
assert(eq);
eq++;
if (str[0] == 'a') {
ret->align = atoi(eq);
} else if (str[0] == 'o') {
ret->offset = atoi(eq);
} else onError();
i++;
}
ret->ptr = parseExpression(s[i]);
ret->value = parseExpression(s[i+1]);
return ret;
}
Expression* makeIf(Element& s) {
auto ret = allocator.alloc<If>();
ret->condition = parseExpression(s[1]);
ret->ifTrue = parseExpression(s[2]);
if (s.size() == 4) {
ret->ifFalse = parseExpression(s[3]);
ret->finalize();
}
return ret;
}
Expression* makeMaybeBlock(Element& s, size_t i, size_t stopAt=-1) {
if (s.size() == i+1) return parseExpression(s[i]);
auto ret = allocator.alloc<Block>();
for (; i < s.size() && i < stopAt; i++) {
ret->list.push_back(parseExpression(s[i]));
}
if (ret->list.size() > 0) {
ret->type = ret->list.back()->type;
}
return ret;
}
Expression* makeLoop(Element& s) {
auto ret = allocator.alloc<Loop>();
size_t i = 1;
if (s[i]->isStr() && s[i+1]->isStr()) { // out can only be named if both are
ret->out = s[i]->str();
i++;
} else {
ret->out = getPrefixedName("loop-out");
}
if (s[i]->isStr()) {
ret->in = s[i]->str();
i++;
} else {
ret->in = getPrefixedName("loop-in");
}
labelStack.push_back(ret->out);
labelStack.push_back(ret->in);
ret->body = makeMaybeBlock(s, i);
labelStack.pop_back();
labelStack.pop_back();
ret->finalize();
return ret;
}
Expression* makeCall(Element& s) {
auto ret = allocator.alloc<Call>();
ret->target = s[1]->str();
ret->type = functionTypes[ret->target];
parseCallOperands(s, 2, ret);
return ret;
}
Expression* makeCallImport(Element& s) {
auto ret = allocator.alloc<CallImport>();
ret->target = s[1]->str();
Import* import = wasm.importsMap[ret->target];
ret->type = import->type->result;
parseCallOperands(s, 2, ret);
return ret;
}
Expression* makeCallIndirect(Element& s) {
auto ret = allocator.alloc<CallIndirect>();
IString type = s[1]->str();
assert(wasm.functionTypesMap.find(type) != wasm.functionTypesMap.end());
ret->fullType = wasm.functionTypesMap[type];
ret->type = ret->fullType->result;
ret->target = parseExpression(s[2]);
parseCallOperands(s, 3, ret);
return ret;
}
template<class T>
void parseCallOperands(Element& s, size_t i, T* call) {
while (i < s.size()) {
call->operands.push_back(parseExpression(s[i]));
i++;
}
}
Expression* makeBreak(Element& s) {
auto ret = allocator.alloc<Break>();
size_t i = 1;
if (s[0]->str() == BR_IF) {
ret->condition = parseExpression(s[i]);
i++;
}
if (s[i]->dollared()) {
ret->name = s[i]->str();
} else {
// offset, break to nth outside label
size_t offset = atol(s[i]->c_str());
assert(offset < labelStack.size());
ret->name = labelStack[labelStack.size() - 1 - offset];
}
i++;
if (i < s.size()) {
ret->value = parseExpression(s[i]);
}
return ret;
}
Expression* makeReturn(Element& s) {
auto ret = allocator.alloc<Return>();
if (s.size() >= 2) {
ret->value = parseExpression(s[1]);
}
return ret;
}
Expression* makeSwitch(Element& s) {
auto ret = allocator.alloc<Switch>();
size_t i = 1;
if (s[i]->isStr()) {
ret->name = s[i]->str();
i++;
} else {
ret->name = getPrefixedName("switch");
}
ret->value = parseExpression(s[i]);
i++;
Element& table = *s[i];
i++;
for (size_t j = 1; j < table.size(); j++) {
Element& curr = *table[j];
ret->targets.push_back(curr[1]->str());
}
Element& curr = *s[i];
ret->default_ = curr[1]->str();
i++;
for (; i < s.size(); i++) {
Element& curr = *s[i];
assert(curr[0]->str() == CASE);
if (curr.size() < 2) onError();
ret->cases.emplace_back(curr[1]->str(), makeMaybeBlock(curr, 2, curr.size()));
}
ret->type = ret->cases.size() > 0 ? ret->cases[0].body->type : none;
return ret;
}
void parseMemory(Element& s) {
wasm.memory.initial = atoi(s[1]->c_str());
if (s.size() == 2) return;
size_t i = 2;
if (s[i]->isStr()) {
wasm.memory.max = atoi(s[i]->c_str());
i++;
}
while (i < s.size()) {
Element& curr = *s[i];
assert(curr[0]->str() == SEGMENT);
const char *input = curr[2]->c_str();
char *data = (char*)malloc(strlen(input)); // over-allocated, since escaping collapses, but whatever
char *write = data;
while (1) {
if (input[0] == 0) break;
if (input[0] == '\\') {
if (input[1] == '"') {
*write++ = '"';
input += 2;
continue;
} else if (input[1] == '\'') {
*write++ = '\'';
input += 2;
continue;
} else if (input[1] == '\\') {
*write++ = '\\';
input += 2;
continue;
} else if (input[1] == 'n') {
*write++ = '\n';
input += 2;
continue;
} else if (input[1] == 't') {
*write++ = '\t';
input += 2;
continue;
} else {
*write++ = (char)(unhex(input[1])*16 + unhex(input[2]));
input += 3;
continue;
}
}
*write++ = input[0];
input++;
}
wasm.memory.segments.emplace_back(atoi(curr[1]->c_str()), data, write - data);
i++;
}
}
void parseExport(Element& s) {
auto ex = allocator.alloc<Export>();
ex->name = s[1]->str();
ex->value = s[2]->str();
wasm.addExport(ex);
}
void parseImport(Element& s) {
auto im = allocator.alloc<Import>();
im->name = s[1]->str();
im->module = s[2]->str();
if (!s[3]->isStr()) onError();
im->base = s[3]->str();
FunctionType type;
if (s.size() > 4) {
Element& params = *s[4];
IString id = params[0]->str();
if (id == PARAM) {
for (size_t i = 1; i < params.size(); i++) {
type.params.push_back(stringToWasmType(params[i]->str()));
}
} else if (id == RESULT) {
type.result = stringToWasmType(params[1]->str());
} else if (id == TYPE) {
IString name = params[1]->str();
assert(wasm.functionTypesMap.find(name) != wasm.functionTypesMap.end());
type = *wasm.functionTypesMap[name];
} else {
onError();
}
if (s.size() > 5) {
Element& result = *s[5];
assert(result[0]->str() == RESULT);
type.result = stringToWasmType(result[1]->str());
}
}
im->type = ensureFunctionType(getSig(&type), &wasm, allocator);
wasm.addImport(im);
}
void parseTable(Element& s) {
for (size_t i = 1; i < s.size(); i++) {
wasm.table.names.push_back(s[i]->str());
}
}
void parseType(Element& s) {
auto type = allocator.alloc<FunctionType>();
size_t i = 1;
if (s[i]->isStr()) {
type->name = s[i]->str();
i++;
}
Element& func = *s[i];
assert(func.isList());
for (size_t i = 1; i < func.size(); i++) {
Element& curr = *func[i];
if (curr[0]->str() == PARAM) {
for (size_t j = 1; j < curr.size(); j++) {
type->params.push_back(stringToWasmType(curr[j]->str()));
}
} else if (curr[0]->str() == RESULT) {
type->result = stringToWasmType(curr[1]->str());
}
}
wasm.addFunctionType(type);
}
};
} // namespace wasm
#endif // wasm_wasm_s_parser_h