travel/admin/node_modules/@webassemblyjs/wast-parser/esm/grammar.js

1756 lines
48 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
import { codeFrameFromSource } from "@webassemblyjs/helper-code-frame";
import * as t from "@webassemblyjs/ast";
import { parse32I } from "./number-literals";
import { parseString } from "./string-literals";
import { tokens, keywords } from "./tokenizer";
function hasPlugin(name) {
if (name !== "wast") throw new Error("unknow plugin");
return true;
}
function isKeyword(token, id) {
return token.type === tokens.keyword && token.value === id;
}
function tokenToString(token) {
if (token.type === "keyword") {
return "keyword (".concat(token.value, ")");
}
return token.type;
}
function identifierFromToken(token) {
var _token$loc = token.loc,
end = _token$loc.end,
start = _token$loc.start;
return t.withLoc(t.identifier(token.value), end, start);
}
export function parse(tokensList, source) {
var current = 0;
var getUniqueName = t.getUniqueNameGenerator();
var state = {
registredExportedElements: []
}; // But this time we're going to use recursion instead of a `while` loop. So we
// define a `walk` function.
function walk() {
var token = tokensList[current];
function eatToken() {
token = tokensList[++current];
}
function getEndLoc() {
var currentToken = token;
if (typeof currentToken === "undefined") {
var lastToken = tokensList[tokensList.length - 1];
currentToken = lastToken;
}
return currentToken.loc.end;
}
function getStartLoc() {
return token.loc.start;
}
function eatTokenOfType(type) {
if (token.type !== type) {
throw new Error("\n" + codeFrameFromSource(source, token.loc) + "Assertion error: expected token of type " + type + ", given " + tokenToString(token));
}
eatToken();
}
function parseExportIndex(token) {
if (token.type === tokens.identifier) {
var index = identifierFromToken(token);
eatToken();
return index;
} else if (token.type === tokens.number) {
var _index = t.numberLiteralFromRaw(token.value);
eatToken();
return _index;
} else {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "unknown export index" + ", given " + tokenToString(token));
}();
}
}
function lookaheadAndCheck() {
var len = arguments.length;
for (var i = 0; i < len; i++) {
var tokenAhead = tokensList[current + i];
var expectedToken = i < 0 || arguments.length <= i ? undefined : arguments[i];
if (tokenAhead.type === "keyword") {
if (isKeyword(tokenAhead, expectedToken) === false) {
return false;
}
} else if (expectedToken !== tokenAhead.type) {
return false;
}
}
return true;
} // TODO(sven): there is probably a better way to do this
// can refactor it if it get out of hands
function maybeIgnoreComment() {
if (typeof token === "undefined") {
// Ignore
return;
}
while (token.type === tokens.comment) {
eatToken();
if (typeof token === "undefined") {
// Hit the end
break;
}
}
}
/**
* Parses a memory instruction
*
* WAST:
*
* memory: ( memory <name>? <memory_sig> )
* ( memory <name>? ( export <string> ) <...> )
* ( memory <name>? ( import <string> <string> ) <memory_sig> )
* ( memory <name>? ( export <string> )* ( data <string>* )
* memory_sig: <nat> <nat>?
*
*/
function parseMemory() {
var id = t.identifier(getUniqueName("memory"));
var limits = t.limit(0);
if (token.type === tokens.string || token.type === tokens.identifier) {
id = t.identifier(token.value);
eatToken();
} else {
id = t.withRaw(id, ""); // preserve anonymous
}
/**
* Maybe data
*/
if (lookaheadAndCheck(tokens.openParen, keywords.data)) {
eatToken(); // (
eatToken(); // data
// TODO(sven): do something with the data collected here
var stringInitializer = token.value;
eatTokenOfType(tokens.string); // Update limits accordingly
limits = t.limit(stringInitializer.length);
eatTokenOfType(tokens.closeParen);
}
/**
* Maybe export
*/
if (lookaheadAndCheck(tokens.openParen, keywords.export)) {
eatToken(); // (
eatToken(); // export
if (token.type !== tokens.string) {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Expected string in export" + ", given " + tokenToString(token));
}();
}
var _name = token.value;
eatToken();
state.registredExportedElements.push({
exportType: "Memory",
name: _name,
id: id
});
eatTokenOfType(tokens.closeParen);
}
/**
* Memory signature
*/
if (token.type === tokens.number) {
limits = t.limit(parse32I(token.value));
eatToken();
if (token.type === tokens.number) {
limits.max = parse32I(token.value);
eatToken();
}
}
return t.memory(limits, id);
}
/**
* Parses a data section
* https://webassembly.github.io/spec/core/text/modules.html#data-segments
*
* WAST:
*
* data: ( data <index>? <offset> <string> )
*/
function parseData() {
// optional memory index
var memidx = 0;
if (token.type === tokens.number) {
memidx = token.value;
eatTokenOfType(tokens.number); // .
}
eatTokenOfType(tokens.openParen);
var offset;
if (token.type === tokens.valtype) {
eatTokenOfType(tokens.valtype); // i32
eatTokenOfType(tokens.dot); // .
if (token.value !== "const") {
throw new Error("constant expression required");
}
eatTokenOfType(tokens.name); // const
var numberLiteral = t.numberLiteralFromRaw(token.value, "i32");
offset = t.objectInstruction("const", "i32", [numberLiteral]);
eatToken();
eatTokenOfType(tokens.closeParen);
} else {
eatTokenOfType(tokens.name); // get_global
var _numberLiteral = t.numberLiteralFromRaw(token.value, "i32");
offset = t.instruction("get_global", [_numberLiteral]);
eatToken();
eatTokenOfType(tokens.closeParen);
}
var byteArray = parseString(token.value);
eatToken(); // "string"
return t.data(t.memIndexLiteral(memidx), offset, t.byteArray(byteArray));
}
/**
* Parses a table instruction
*
* WAST:
*
* table: ( table <name>? <table_type> )
* ( table <name>? ( export <string> ) <...> )
* ( table <name>? ( import <string> <string> ) <table_type> )
* ( table <name>? ( export <string> )* <elem_type> ( elem <var>* ) )
*
* table_type: <nat> <nat>? <elem_type>
* elem_type: anyfunc
*
* elem: ( elem <var>? (offset <instr>* ) <var>* )
* ( elem <var>? <expr> <var>* )
*/
function parseTable() {
var name = t.identifier(getUniqueName("table"));
var limit = t.limit(0);
var elemIndices = [];
var elemType = "anyfunc";
if (token.type === tokens.string || token.type === tokens.identifier) {
name = identifierFromToken(token);
eatToken();
} else {
name = t.withRaw(name, ""); // preserve anonymous
}
while (token.type !== tokens.closeParen) {
/**
* Maybe export
*/
if (lookaheadAndCheck(tokens.openParen, keywords.elem)) {
eatToken(); // (
eatToken(); // elem
while (token.type === tokens.identifier) {
elemIndices.push(t.identifier(token.value));
eatToken();
}
eatTokenOfType(tokens.closeParen);
} else if (lookaheadAndCheck(tokens.openParen, keywords.export)) {
eatToken(); // (
eatToken(); // export
if (token.type !== tokens.string) {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Expected string in export" + ", given " + tokenToString(token));
}();
}
var exportName = token.value;
eatToken();
state.registredExportedElements.push({
exportType: "Table",
name: exportName,
id: name
});
eatTokenOfType(tokens.closeParen);
} else if (isKeyword(token, keywords.anyfunc)) {
// It's the default value, we can ignore it
eatToken(); // anyfunc
} else if (token.type === tokens.number) {
/**
* Table type
*/
var min = parseInt(token.value);
eatToken();
if (token.type === tokens.number) {
var max = parseInt(token.value);
eatToken();
limit = t.limit(min, max);
} else {
limit = t.limit(min);
}
eatToken();
} else {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unexpected token" + ", given " + tokenToString(token));
}();
}
}
if (elemIndices.length > 0) {
return t.table(elemType, limit, name, elemIndices);
} else {
return t.table(elemType, limit, name);
}
}
/**
* Parses an import statement
*
* WAST:
*
* import: ( import <string> <string> <imkind> )
* imkind: ( func <name>? <func_sig> )
* ( global <name>? <global_sig> )
* ( table <name>? <table_sig> )
* ( memory <name>? <memory_sig> )
*
* global_sig: <type> | ( mut <type> )
*/
function parseImport() {
if (token.type !== tokens.string) {
throw new Error("Expected a string, " + token.type + " given.");
}
var moduleName = token.value;
eatToken();
if (token.type !== tokens.string) {
throw new Error("Expected a string, " + token.type + " given.");
}
var name = token.value;
eatToken();
eatTokenOfType(tokens.openParen);
var descr;
if (isKeyword(token, keywords.func)) {
eatToken(); // keyword
var fnParams = [];
var fnResult = [];
var typeRef;
var fnName = t.identifier(getUniqueName("func"));
if (token.type === tokens.identifier) {
fnName = identifierFromToken(token);
eatToken();
}
while (token.type === tokens.openParen) {
eatToken();
if (lookaheadAndCheck(keywords.type) === true) {
eatToken();
typeRef = parseTypeReference();
} else if (lookaheadAndCheck(keywords.param) === true) {
eatToken();
fnParams.push.apply(fnParams, _toConsumableArray(parseFuncParam()));
} else if (lookaheadAndCheck(keywords.result) === true) {
eatToken();
fnResult.push.apply(fnResult, _toConsumableArray(parseFuncResult()));
} else {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unexpected token in import of type" + ", given " + tokenToString(token));
}();
}
eatTokenOfType(tokens.closeParen);
}
if (typeof fnName === "undefined") {
throw new Error("Imported function must have a name");
}
descr = t.funcImportDescr(fnName, typeRef !== undefined ? typeRef : t.signature(fnParams, fnResult));
} else if (isKeyword(token, keywords.global)) {
eatToken(); // keyword
if (token.type === tokens.openParen) {
eatToken(); // (
eatTokenOfType(tokens.keyword); // mut keyword
var valtype = token.value;
eatToken();
descr = t.globalType(valtype, "var");
eatTokenOfType(tokens.closeParen);
} else {
var _valtype = token.value;
eatTokenOfType(tokens.valtype);
descr = t.globalType(_valtype, "const");
}
} else if (isKeyword(token, keywords.memory) === true) {
eatToken(); // Keyword
descr = parseMemory();
} else if (isKeyword(token, keywords.table) === true) {
eatToken(); // Keyword
descr = parseTable();
} else {
throw new Error("Unsupported import type: " + tokenToString(token));
}
eatTokenOfType(tokens.closeParen);
return t.moduleImport(moduleName, name, descr);
}
/**
* Parses a block instruction
*
* WAST:
*
* expr: ( block <name>? <block_sig> <instr>* )
* instr: block <name>? <block_sig> <instr>* end <name>?
* block_sig : ( result <type>* )*
*
*/
function parseBlock() {
var label = t.identifier(getUniqueName("block"));
var blockResult = null;
var instr = [];
if (token.type === tokens.identifier) {
label = identifierFromToken(token);
eatToken();
} else {
label = t.withRaw(label, ""); // preserve anonymous
}
while (token.type === tokens.openParen) {
eatToken();
if (lookaheadAndCheck(keywords.result) === true) {
eatToken();
blockResult = token.value;
eatToken();
} else if (lookaheadAndCheck(tokens.name) === true || lookaheadAndCheck(tokens.valtype) === true || token.type === "keyword" // is any keyword
) {
// Instruction
instr.push(parseFuncInstr());
} else {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unexpected token in block body of type" + ", given " + tokenToString(token));
}();
}
maybeIgnoreComment();
eatTokenOfType(tokens.closeParen);
}
return t.blockInstruction(label, instr, blockResult);
}
/**
* Parses a if instruction
*
* WAST:
*
* expr:
* ( if <name>? <block_sig> ( then <instr>* ) ( else <instr>* )? )
* ( if <name>? <block_sig> <expr>+ ( then <instr>* ) ( else <instr>* )? )
*
* instr:
* if <name>? <block_sig> <instr>* end <name>?
* if <name>? <block_sig> <instr>* else <name>? <instr>* end <name>?
*
* block_sig : ( result <type>* )*
*
*/
function parseIf() {
var blockResult = null;
var label = t.identifier(getUniqueName("if"));
var testInstrs = [];
var consequent = [];
var alternate = [];
if (token.type === tokens.identifier) {
label = identifierFromToken(token);
eatToken();
} else {
label = t.withRaw(label, ""); // preserve anonymous
}
while (token.type === tokens.openParen) {
eatToken(); // (
/**
* Block signature
*/
if (isKeyword(token, keywords.result) === true) {
eatToken();
blockResult = token.value;
eatTokenOfType(tokens.valtype);
eatTokenOfType(tokens.closeParen);
continue;
}
/**
* Then
*/
if (isKeyword(token, keywords.then) === true) {
eatToken(); // then
while (token.type === tokens.openParen) {
eatToken(); // Instruction
if (lookaheadAndCheck(tokens.name) === true || lookaheadAndCheck(tokens.valtype) === true || token.type === "keyword" // is any keyword
) {
consequent.push(parseFuncInstr());
} else {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unexpected token in consequent body of type" + ", given " + tokenToString(token));
}();
}
eatTokenOfType(tokens.closeParen);
}
eatTokenOfType(tokens.closeParen);
continue;
}
/**
* Alternate
*/
if (isKeyword(token, keywords.else)) {
eatToken(); // else
while (token.type === tokens.openParen) {
eatToken(); // Instruction
if (lookaheadAndCheck(tokens.name) === true || lookaheadAndCheck(tokens.valtype) === true || token.type === "keyword" // is any keyword
) {
alternate.push(parseFuncInstr());
} else {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unexpected token in alternate body of type" + ", given " + tokenToString(token));
}();
}
eatTokenOfType(tokens.closeParen);
}
eatTokenOfType(tokens.closeParen);
continue;
}
/**
* Test instruction
*/
if (lookaheadAndCheck(tokens.name) === true || lookaheadAndCheck(tokens.valtype) === true || token.type === "keyword" // is any keyword
) {
testInstrs.push(parseFuncInstr());
eatTokenOfType(tokens.closeParen);
continue;
}
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unexpected token in if body" + ", given " + tokenToString(token));
}();
}
return t.ifInstruction(label, testInstrs, blockResult, consequent, alternate);
}
/**
* Parses a loop instruction
*
* WAT:
*
* blockinstr :: 'loop' I:label rt:resulttype (in:instr*) 'end' id?
*
* WAST:
*
* instr :: loop <name>? <block_sig> <instr>* end <name>?
* expr :: ( loop <name>? <block_sig> <instr>* )
* block_sig :: ( result <type>* )*
*
*/
function parseLoop() {
var label = t.identifier(getUniqueName("loop"));
var blockResult;
var instr = [];
if (token.type === tokens.identifier) {
label = identifierFromToken(token);
eatToken();
} else {
label = t.withRaw(label, ""); // preserve anonymous
}
while (token.type === tokens.openParen) {
eatToken();
if (lookaheadAndCheck(keywords.result) === true) {
eatToken();
blockResult = token.value;
eatToken();
} else if (lookaheadAndCheck(tokens.name) === true || lookaheadAndCheck(tokens.valtype) === true || token.type === "keyword" // is any keyword
) {
// Instruction
instr.push(parseFuncInstr());
} else {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unexpected token in loop body" + ", given " + tokenToString(token));
}();
}
eatTokenOfType(tokens.closeParen);
}
return t.loopInstruction(label, blockResult, instr);
}
function parseCallIndirect() {
var typeRef;
var params = [];
var results = [];
var instrs = [];
while (token.type !== tokens.closeParen) {
if (lookaheadAndCheck(tokens.openParen, keywords.type)) {
eatToken(); // (
eatToken(); // type
typeRef = parseTypeReference();
} else if (lookaheadAndCheck(tokens.openParen, keywords.param)) {
eatToken(); // (
eatToken(); // param
/**
* Params can be empty:
* (params)`
*/
if (token.type !== tokens.closeParen) {
params.push.apply(params, _toConsumableArray(parseFuncParam()));
}
} else if (lookaheadAndCheck(tokens.openParen, keywords.result)) {
eatToken(); // (
eatToken(); // result
/**
* Results can be empty:
* (result)`
*/
if (token.type !== tokens.closeParen) {
results.push.apply(results, _toConsumableArray(parseFuncResult()));
}
} else {
eatTokenOfType(tokens.openParen);
instrs.push(parseFuncInstr());
}
eatTokenOfType(tokens.closeParen);
}
return t.callIndirectInstruction(typeRef !== undefined ? typeRef : t.signature(params, results), instrs);
}
/**
* Parses an export instruction
*
* WAT:
*
* export: ( export <string> <exkind> )
* exkind: ( func <var> )
* ( global <var> )
* ( table <var> )
* ( memory <var> )
* var: <nat> | <name>
*
*/
function parseExport() {
if (token.type !== tokens.string) {
throw new Error("Expected string after export, got: " + token.type);
}
var name = token.value;
eatToken();
var moduleExportDescr = parseModuleExportDescr();
return t.moduleExport(name, moduleExportDescr);
}
function parseModuleExportDescr() {
var startLoc = getStartLoc();
var type = "";
var index;
eatTokenOfType(tokens.openParen);
while (token.type !== tokens.closeParen) {
if (isKeyword(token, keywords.func)) {
type = "Func";
eatToken();
index = parseExportIndex(token);
} else if (isKeyword(token, keywords.table)) {
type = "Table";
eatToken();
index = parseExportIndex(token);
} else if (isKeyword(token, keywords.global)) {
type = "Global";
eatToken();
index = parseExportIndex(token);
} else if (isKeyword(token, keywords.memory)) {
type = "Memory";
eatToken();
index = parseExportIndex(token);
}
eatToken();
}
if (type === "") {
throw new Error("Unknown export type");
}
if (index === undefined) {
throw new Error("Exported function must have a name");
}
var node = t.moduleExportDescr(type, index);
var endLoc = getEndLoc();
eatTokenOfType(tokens.closeParen);
return t.withLoc(node, endLoc, startLoc);
}
function parseModule() {
var name = null;
var isBinary = false;
var isQuote = false;
var moduleFields = [];
if (token.type === tokens.identifier) {
name = token.value;
eatToken();
}
if (hasPlugin("wast") && token.type === tokens.name && token.value === "binary") {
eatToken();
isBinary = true;
}
if (hasPlugin("wast") && token.type === tokens.name && token.value === "quote") {
eatToken();
isQuote = true;
}
if (isBinary === true) {
var blob = [];
while (token.type === tokens.string) {
blob.push(token.value);
eatToken();
maybeIgnoreComment();
}
eatTokenOfType(tokens.closeParen);
return t.binaryModule(name, blob);
}
if (isQuote === true) {
var string = [];
while (token.type === tokens.string) {
string.push(token.value);
eatToken();
}
eatTokenOfType(tokens.closeParen);
return t.quoteModule(name, string);
}
while (token.type !== tokens.closeParen) {
moduleFields.push(walk());
if (state.registredExportedElements.length > 0) {
state.registredExportedElements.forEach(function (decl) {
moduleFields.push(t.moduleExport(decl.name, t.moduleExportDescr(decl.exportType, decl.id)));
});
state.registredExportedElements = [];
}
token = tokensList[current];
}
eatTokenOfType(tokens.closeParen);
return t.module(name, moduleFields);
}
/**
* Parses the arguments of an instruction
*/
function parseFuncInstrArguments(signature) {
var args = [];
var namedArgs = {};
var signaturePtr = 0;
while (token.type === tokens.name || isKeyword(token, keywords.offset)) {
var key = token.value;
eatToken();
eatTokenOfType(tokens.equal);
var value = void 0;
if (token.type === tokens.number) {
value = t.numberLiteralFromRaw(token.value);
} else {
throw new Error("Unexpected type for argument: " + token.type);
}
namedArgs[key] = value;
eatToken();
} // $FlowIgnore
var signatureLength = signature.vector ? Infinity : signature.length;
while (token.type !== tokens.closeParen && ( // $FlowIgnore
token.type === tokens.openParen || signaturePtr < signatureLength)) {
if (token.type === tokens.identifier) {
args.push(t.identifier(token.value));
eatToken();
} else if (token.type === tokens.valtype) {
// Handle locals
args.push(t.valtypeLiteral(token.value));
eatToken();
} else if (token.type === tokens.string) {
args.push(t.stringLiteral(token.value));
eatToken();
} else if (token.type === tokens.number) {
args.push( // TODO(sven): refactor the type signature handling
// https://github.com/xtuc/webassemblyjs/pull/129 is a good start
t.numberLiteralFromRaw(token.value, // $FlowIgnore
signature[signaturePtr] || "f64")); // $FlowIgnore
if (!signature.vector) {
++signaturePtr;
}
eatToken();
} else if (token.type === tokens.openParen) {
/**
* Maybe some nested instructions
*/
eatToken(); // Instruction
if (lookaheadAndCheck(tokens.name) === true || lookaheadAndCheck(tokens.valtype) === true || token.type === "keyword" // is any keyword
) {
// $FlowIgnore
args.push(parseFuncInstr());
} else {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unexpected token in nested instruction" + ", given " + tokenToString(token));
}();
}
if (token.type === tokens.closeParen) {
eatToken();
}
} else {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unexpected token in instruction argument" + ", given " + tokenToString(token));
}();
}
}
return {
args: args,
namedArgs: namedArgs
};
}
/**
* Parses an instruction
*
* WAT:
*
* instr :: plaininst
* blockinstr
*
* blockinstr :: 'block' I:label rt:resulttype (in:instr*) 'end' id?
* 'loop' I:label rt:resulttype (in:instr*) 'end' id?
* 'if' I:label rt:resulttype (in:instr*) 'else' id? (in2:intr*) 'end' id?
*
* plaininst :: 'unreachable'
* 'nop'
* 'br' l:labelidx
* 'br_if' l:labelidx
* 'br_table' l*:vec(labelidx) ln:labelidx
* 'return'
* 'call' x:funcidx
* 'call_indirect' x, I:typeuse
*
* WAST:
*
* instr:
* <expr>
* <op>
* block <name>? <block_sig> <instr>* end <name>?
* loop <name>? <block_sig> <instr>* end <name>?
* if <name>? <block_sig> <instr>* end <name>?
* if <name>? <block_sig> <instr>* else <name>? <instr>* end <name>?
*
* expr:
* ( <op> )
* ( <op> <expr>+ )
* ( block <name>? <block_sig> <instr>* )
* ( loop <name>? <block_sig> <instr>* )
* ( if <name>? <block_sig> ( then <instr>* ) ( else <instr>* )? )
* ( if <name>? <block_sig> <expr>+ ( then <instr>* ) ( else <instr>* )? )
*
* op:
* unreachable
* nop
* br <var>
* br_if <var>
* br_table <var>+
* return
* call <var>
* call_indirect <func_sig>
* drop
* select
* get_local <var>
* set_local <var>
* tee_local <var>
* get_global <var>
* set_global <var>
* <type>.load((8|16|32)_<sign>)? <offset>? <align>?
* <type>.store(8|16|32)? <offset>? <align>?
* current_memory
* grow_memory
* <type>.const <value>
* <type>.<unop>
* <type>.<binop>
* <type>.<testop>
* <type>.<relop>
* <type>.<cvtop>/<type>
*
* func_type: ( type <var> )? <param>* <result>*
*/
function parseFuncInstr() {
var startLoc = getStartLoc();
maybeIgnoreComment();
/**
* A simple instruction
*/
if (token.type === tokens.name || token.type === tokens.valtype) {
var _name2 = token.value;
var object;
eatToken();
if (token.type === tokens.dot) {
object = _name2;
eatToken();
if (token.type !== tokens.name) {
throw new TypeError("Unknown token: " + token.type + ", name expected");
}
_name2 = token.value;
eatToken();
}
if (token.type === tokens.closeParen) {
var _endLoc = token.loc.end;
if (typeof object === "undefined") {
return t.withLoc(t.instruction(_name2), _endLoc, startLoc);
} else {
return t.withLoc(t.objectInstruction(_name2, object, []), _endLoc, startLoc);
}
}
var signature = t.signatureForOpcode(object || "", _name2);
var _parseFuncInstrArgume = parseFuncInstrArguments(signature),
_args = _parseFuncInstrArgume.args,
_namedArgs = _parseFuncInstrArgume.namedArgs;
var endLoc = token.loc.end;
if (typeof object === "undefined") {
return t.withLoc(t.instruction(_name2, _args, _namedArgs), endLoc, startLoc);
} else {
return t.withLoc(t.objectInstruction(_name2, object, _args, _namedArgs), endLoc, startLoc);
}
} else if (isKeyword(token, keywords.loop)) {
/**
* Else a instruction with a keyword (loop or block)
*/
eatToken(); // keyword
return parseLoop();
} else if (isKeyword(token, keywords.block)) {
eatToken(); // keyword
return parseBlock();
} else if (isKeyword(token, keywords.call_indirect)) {
eatToken(); // keyword
return parseCallIndirect();
} else if (isKeyword(token, keywords.call)) {
eatToken(); // keyword
var index;
if (token.type === tokens.identifier) {
index = identifierFromToken(token);
eatToken();
} else if (token.type === tokens.number) {
index = t.indexLiteral(token.value);
eatToken();
}
var instrArgs = []; // Nested instruction
while (token.type === tokens.openParen) {
eatToken();
instrArgs.push(parseFuncInstr());
eatTokenOfType(tokens.closeParen);
}
if (typeof index === "undefined") {
throw new Error("Missing argument in call instruciton");
}
if (instrArgs.length > 0) {
return t.callInstruction(index, instrArgs);
} else {
return t.callInstruction(index);
}
} else if (isKeyword(token, keywords.if)) {
eatToken(); // Keyword
return parseIf();
} else if (isKeyword(token, keywords.module) && hasPlugin("wast")) {
eatToken(); // In WAST you can have a module as an instruction's argument
// we will cast it into a instruction to not break the flow
// $FlowIgnore
var module = parseModule();
return module;
} else {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unexpected instruction in function body" + ", given " + tokenToString(token));
}();
}
}
/*
* Parses a function
*
* WAT:
*
* functype :: ( 'func' t1:vec(param) t2:vec(result) )
* param :: ( 'param' id? t:valtype )
* result :: ( 'result' t:valtype )
*
* WAST:
*
* func :: ( func <name>? <func_sig> <local>* <instr>* )
* ( func <name>? ( export <string> ) <...> )
* ( func <name>? ( import <string> <string> ) <func_sig> )
* func_sig :: ( type <var> )? <param>* <result>*
* param :: ( param <type>* ) | ( param <name> <type> )
* result :: ( result <type>* )
* local :: ( local <type>* ) | ( local <name> <type> )
*
*/
function parseFunc() {
var fnName = t.identifier(getUniqueName("func"));
var typeRef;
var fnBody = [];
var fnParams = [];
var fnResult = []; // name
if (token.type === tokens.identifier) {
fnName = identifierFromToken(token);
eatToken();
} else {
fnName = t.withRaw(fnName, ""); // preserve anonymous
}
maybeIgnoreComment();
while (token.type === tokens.openParen || token.type === tokens.name || token.type === tokens.valtype) {
// Instructions without parens
if (token.type === tokens.name || token.type === tokens.valtype) {
fnBody.push(parseFuncInstr());
continue;
}
eatToken();
if (lookaheadAndCheck(keywords.param) === true) {
eatToken();
fnParams.push.apply(fnParams, _toConsumableArray(parseFuncParam()));
} else if (lookaheadAndCheck(keywords.result) === true) {
eatToken();
fnResult.push.apply(fnResult, _toConsumableArray(parseFuncResult()));
} else if (lookaheadAndCheck(keywords.export) === true) {
eatToken();
parseFuncExport(fnName);
} else if (lookaheadAndCheck(keywords.type) === true) {
eatToken();
typeRef = parseTypeReference();
} else if (lookaheadAndCheck(tokens.name) === true || lookaheadAndCheck(tokens.valtype) === true || token.type === "keyword" // is any keyword
) {
// Instruction
fnBody.push(parseFuncInstr());
} else {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unexpected token in func body" + ", given " + tokenToString(token));
}();
}
eatTokenOfType(tokens.closeParen);
}
return t.func(fnName, typeRef !== undefined ? typeRef : t.signature(fnParams, fnResult), fnBody);
}
/**
* Parses shorthand export in func
*
* export :: ( export <string> )
*/
function parseFuncExport(funcId) {
if (token.type !== tokens.string) {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Function export expected a string" + ", given " + tokenToString(token));
}();
}
var name = token.value;
eatToken();
/**
* Func export shorthand, we trait it as a syntaxic sugar.
* A export ModuleField will be added later.
*
* We give the anonymous function a generated name and export it.
*/
var id = t.identifier(funcId.value);
state.registredExportedElements.push({
exportType: "Func",
name: name,
id: id
});
}
/**
* Parses a type instruction
*
* WAST:
*
* typedef: ( type <name>? ( func <param>* <result>* ) )
*/
function parseType() {
var id;
var params = [];
var result = [];
if (token.type === tokens.identifier) {
id = identifierFromToken(token);
eatToken();
}
if (lookaheadAndCheck(tokens.openParen, keywords.func)) {
eatToken(); // (
eatToken(); // func
if (token.type === tokens.closeParen) {
eatToken(); // function with an empty signature, we can abort here
return t.typeInstruction(id, t.signature([], []));
}
if (lookaheadAndCheck(tokens.openParen, keywords.param)) {
eatToken(); // (
eatToken(); // param
params = parseFuncParam();
eatTokenOfType(tokens.closeParen);
}
if (lookaheadAndCheck(tokens.openParen, keywords.result)) {
eatToken(); // (
eatToken(); // result
result = parseFuncResult();
eatTokenOfType(tokens.closeParen);
}
eatTokenOfType(tokens.closeParen);
}
return t.typeInstruction(id, t.signature(params, result));
}
/**
* Parses a function result
*
* WAST:
*
* result :: ( result <type>* )
*/
function parseFuncResult() {
var results = [];
while (token.type !== tokens.closeParen) {
if (token.type !== tokens.valtype) {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unexpected token in func result" + ", given " + tokenToString(token));
}();
}
var valtype = token.value;
eatToken();
results.push(valtype);
}
return results;
}
/**
* Parses a type reference
*
*/
function parseTypeReference() {
var ref;
if (token.type === tokens.identifier) {
ref = identifierFromToken(token);
eatToken();
} else if (token.type === tokens.number) {
ref = t.numberLiteralFromRaw(token.value);
eatToken();
}
return ref;
}
/**
* Parses a global instruction
*
* WAST:
*
* global: ( global <name>? <global_sig> <instr>* )
* ( global <name>? ( export <string> ) <...> )
* ( global <name>? ( import <string> <string> ) <global_sig> )
*
* global_sig: <type> | ( mut <type> )
*
*/
function parseGlobal() {
var name = t.identifier(getUniqueName("global"));
var type; // Keep informations in case of a shorthand import
var importing = null;
maybeIgnoreComment();
if (token.type === tokens.identifier) {
name = identifierFromToken(token);
eatToken();
} else {
name = t.withRaw(name, ""); // preserve anonymous
}
/**
* maybe export
*/
if (lookaheadAndCheck(tokens.openParen, keywords.export)) {
eatToken(); // (
eatToken(); // export
var exportName = token.value;
eatTokenOfType(tokens.string);
state.registredExportedElements.push({
exportType: "Global",
name: exportName,
id: name
});
eatTokenOfType(tokens.closeParen);
}
/**
* maybe import
*/
if (lookaheadAndCheck(tokens.openParen, keywords.import)) {
eatToken(); // (
eatToken(); // import
var moduleName = token.value;
eatTokenOfType(tokens.string);
var _name3 = token.value;
eatTokenOfType(tokens.string);
importing = {
module: moduleName,
name: _name3,
descr: undefined
};
eatTokenOfType(tokens.closeParen);
}
/**
* global_sig
*/
if (token.type === tokens.valtype) {
type = t.globalType(token.value, "const");
eatToken();
} else if (token.type === tokens.openParen) {
eatToken(); // (
if (isKeyword(token, keywords.mut) === false) {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unsupported global type, expected mut" + ", given " + tokenToString(token));
}();
}
eatToken(); // mut
type = t.globalType(token.value, "var");
eatToken();
eatTokenOfType(tokens.closeParen);
}
if (type === undefined) {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Could not determine global type" + ", given " + tokenToString(token));
}();
}
maybeIgnoreComment();
var init = [];
if (importing != null) {
importing.descr = type;
init.push(t.moduleImport(importing.module, importing.name, importing.descr));
}
/**
* instr*
*/
while (token.type === tokens.openParen) {
eatToken();
init.push(parseFuncInstr());
eatTokenOfType(tokens.closeParen);
}
return t.global(type, init, name);
}
/**
* Parses a function param
*
* WAST:
*
* param :: ( param <type>* ) | ( param <name> <type> )
*/
function parseFuncParam() {
var params = [];
var id;
var valtype;
if (token.type === tokens.identifier) {
id = token.value;
eatToken();
}
if (token.type === tokens.valtype) {
valtype = token.value;
eatToken();
params.push({
id: id,
valtype: valtype
});
/**
* Shorthand notation for multiple anonymous parameters
* @see https://webassembly.github.io/spec/core/text/types.html#function-types
* @see https://github.com/xtuc/webassemblyjs/issues/6
*/
if (id === undefined) {
while (token.type === tokens.valtype) {
valtype = token.value;
eatToken();
params.push({
id: undefined,
valtype: valtype
});
}
}
} else {// ignore
}
return params;
}
/**
* Parses an element segments instruction
*
* WAST:
*
* elem: ( elem <var>? (offset <instr>* ) <var>* )
* ( elem <var>? <expr> <var>* )
*
* var: <nat> | <name>
*/
function parseElem() {
var tableIndex = t.indexLiteral(0);
var offset = [];
var funcs = [];
if (token.type === tokens.identifier) {
tableIndex = identifierFromToken(token);
eatToken();
}
if (token.type === tokens.number) {
tableIndex = t.indexLiteral(token.value);
eatToken();
}
while (token.type !== tokens.closeParen) {
if (lookaheadAndCheck(tokens.openParen, keywords.offset)) {
eatToken(); // (
eatToken(); // offset
while (token.type !== tokens.closeParen) {
eatTokenOfType(tokens.openParen);
offset.push(parseFuncInstr());
eatTokenOfType(tokens.closeParen);
}
eatTokenOfType(tokens.closeParen);
} else if (token.type === tokens.identifier) {
funcs.push(t.identifier(token.value));
eatToken();
} else if (token.type === tokens.number) {
funcs.push(t.indexLiteral(token.value));
eatToken();
} else if (token.type === tokens.openParen) {
eatToken(); // (
offset.push(parseFuncInstr());
eatTokenOfType(tokens.closeParen);
} else {
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unsupported token in elem" + ", given " + tokenToString(token));
}();
}
}
return t.elem(tableIndex, offset, funcs);
}
/**
* Parses the start instruction in a module
*
* WAST:
*
* start: ( start <var> )
* var: <nat> | <name>
*
* WAT:
* start ::= ( start x:funcidx )
*/
function parseStart() {
if (token.type === tokens.identifier) {
var index = identifierFromToken(token);
eatToken();
return t.start(index);
}
if (token.type === tokens.number) {
var _index2 = t.indexLiteral(token.value);
eatToken();
return t.start(_index2);
}
throw new Error("Unknown start, token: " + tokenToString(token));
}
if (token.type === tokens.openParen) {
eatToken();
var startLoc = getStartLoc();
if (isKeyword(token, keywords.export)) {
eatToken();
var node = parseExport();
var _endLoc2 = getEndLoc();
return t.withLoc(node, _endLoc2, startLoc);
}
if (isKeyword(token, keywords.loop)) {
eatToken();
var _node = parseLoop();
var _endLoc3 = getEndLoc();
return t.withLoc(_node, _endLoc3, startLoc);
}
if (isKeyword(token, keywords.func)) {
eatToken();
var _node2 = parseFunc();
var _endLoc4 = getEndLoc();
maybeIgnoreComment();
eatTokenOfType(tokens.closeParen);
return t.withLoc(_node2, _endLoc4, startLoc);
}
if (isKeyword(token, keywords.module)) {
eatToken();
var _node3 = parseModule();
var _endLoc5 = getEndLoc();
return t.withLoc(_node3, _endLoc5, startLoc);
}
if (isKeyword(token, keywords.import)) {
eatToken();
var _node4 = parseImport();
var _endLoc6 = getEndLoc();
eatTokenOfType(tokens.closeParen);
return t.withLoc(_node4, _endLoc6, startLoc);
}
if (isKeyword(token, keywords.block)) {
eatToken();
var _node5 = parseBlock();
var _endLoc7 = getEndLoc();
eatTokenOfType(tokens.closeParen);
return t.withLoc(_node5, _endLoc7, startLoc);
}
if (isKeyword(token, keywords.memory)) {
eatToken();
var _node6 = parseMemory();
var _endLoc8 = getEndLoc();
eatTokenOfType(tokens.closeParen);
return t.withLoc(_node6, _endLoc8, startLoc);
}
if (isKeyword(token, keywords.data)) {
eatToken();
var _node7 = parseData();
var _endLoc9 = getEndLoc();
eatTokenOfType(tokens.closeParen);
return t.withLoc(_node7, _endLoc9, startLoc);
}
if (isKeyword(token, keywords.table)) {
eatToken();
var _node8 = parseTable();
var _endLoc10 = getEndLoc();
eatTokenOfType(tokens.closeParen);
return t.withLoc(_node8, _endLoc10, startLoc);
}
if (isKeyword(token, keywords.global)) {
eatToken();
var _node9 = parseGlobal();
var _endLoc11 = getEndLoc();
eatTokenOfType(tokens.closeParen);
return t.withLoc(_node9, _endLoc11, startLoc);
}
if (isKeyword(token, keywords.type)) {
eatToken();
var _node10 = parseType();
var _endLoc12 = getEndLoc();
eatTokenOfType(tokens.closeParen);
return t.withLoc(_node10, _endLoc12, startLoc);
}
if (isKeyword(token, keywords.start)) {
eatToken();
var _node11 = parseStart();
var _endLoc13 = getEndLoc();
eatTokenOfType(tokens.closeParen);
return t.withLoc(_node11, _endLoc13, startLoc);
}
if (isKeyword(token, keywords.elem)) {
eatToken();
var _node12 = parseElem();
var _endLoc14 = getEndLoc();
eatTokenOfType(tokens.closeParen);
return t.withLoc(_node12, _endLoc14, startLoc);
}
var instruction = parseFuncInstr();
var endLoc = getEndLoc();
maybeIgnoreComment();
if (_typeof(instruction) === "object") {
if (typeof token !== "undefined") {
eatTokenOfType(tokens.closeParen);
}
return t.withLoc(instruction, endLoc, startLoc);
}
}
if (token.type === tokens.comment) {
var _startLoc = getStartLoc();
var builder = token.opts.type === "leading" ? t.leadingComment : t.blockComment;
var _node13 = builder(token.value);
eatToken(); // comment
var _endLoc15 = getEndLoc();
return t.withLoc(_node13, _endLoc15, _startLoc);
}
throw function () {
return new Error("\n" + codeFrameFromSource(source, token.loc) + "\n" + "Unknown token" + ", given " + tokenToString(token));
}();
}
var body = [];
while (current < tokensList.length) {
body.push(walk());
}
return t.program(body);
}