| "use strict"; |
| |
| Object.defineProperty(exports, "__esModule", { |
| value: true |
| }); |
| exports.parse = parse; |
| |
| var _helperCodeFrame = require("@webassemblyjs/helper-code-frame"); |
| |
| var t = _interopRequireWildcard(require("@webassemblyjs/ast")); |
| |
| var _numberLiterals = require("./number-literals"); |
| |
| var _stringLiterals = require("./string-literals"); |
| |
| var _tokenizer = require("./tokenizer"); |
| |
| function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } |
| |
| 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); } } |
| |
| function hasPlugin(name) { |
| if (name !== "wast") throw new Error("unknow plugin"); |
| return true; |
| } |
| |
| function isKeyword(token, id) { |
| return token.type === _tokenizer.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); |
| } |
| |
| 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" + (0, _helperCodeFrame.codeFrameFromSource)(source, token.loc) + "Assertion error: expected token of type " + type + ", given " + tokenToString(token)); |
| } |
| |
| eatToken(); |
| } |
| |
| function parseExportIndex(token) { |
| if (token.type === _tokenizer.tokens.identifier) { |
| var index = identifierFromToken(token); |
| eatToken(); |
| return index; |
| } else if (token.type === _tokenizer.tokens.number) { |
| var _index = t.numberLiteralFromRaw(token.value); |
| |
| eatToken(); |
| return _index; |
| } else { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.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 === _tokenizer.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 === _tokenizer.tokens.string || token.type === _tokenizer.tokens.identifier) { |
| id = t.identifier(token.value); |
| eatToken(); |
| } else { |
| id = t.withRaw(id, ""); // preserve anonymous |
| } |
| /** |
| * Maybe data |
| */ |
| |
| |
| if (lookaheadAndCheck(_tokenizer.tokens.openParen, _tokenizer.keywords.data)) { |
| eatToken(); // ( |
| |
| eatToken(); // data |
| // TODO(sven): do something with the data collected here |
| |
| var stringInitializer = token.value; |
| eatTokenOfType(_tokenizer.tokens.string); // Update limits accordingly |
| |
| limits = t.limit(stringInitializer.length); |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } |
| /** |
| * Maybe export |
| */ |
| |
| |
| if (lookaheadAndCheck(_tokenizer.tokens.openParen, _tokenizer.keywords.export)) { |
| eatToken(); // ( |
| |
| eatToken(); // export |
| |
| if (token.type !== _tokenizer.tokens.string) { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.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(_tokenizer.tokens.closeParen); |
| } |
| /** |
| * Memory signature |
| */ |
| |
| |
| if (token.type === _tokenizer.tokens.number) { |
| limits = t.limit((0, _numberLiterals.parse32I)(token.value)); |
| eatToken(); |
| |
| if (token.type === _tokenizer.tokens.number) { |
| limits.max = (0, _numberLiterals.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 === _tokenizer.tokens.number) { |
| memidx = token.value; |
| eatTokenOfType(_tokenizer.tokens.number); // . |
| } |
| |
| eatTokenOfType(_tokenizer.tokens.openParen); |
| var offset; |
| |
| if (token.type === _tokenizer.tokens.valtype) { |
| eatTokenOfType(_tokenizer.tokens.valtype); // i32 |
| |
| eatTokenOfType(_tokenizer.tokens.dot); // . |
| |
| if (token.value !== "const") { |
| throw new Error("constant expression required"); |
| } |
| |
| eatTokenOfType(_tokenizer.tokens.name); // const |
| |
| var numberLiteral = t.numberLiteralFromRaw(token.value, "i32"); |
| offset = t.objectInstruction("const", "i32", [numberLiteral]); |
| eatToken(); |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } else { |
| eatTokenOfType(_tokenizer.tokens.name); // get_global |
| |
| var _numberLiteral = t.numberLiteralFromRaw(token.value, "i32"); |
| |
| offset = t.instruction("get_global", [_numberLiteral]); |
| eatToken(); |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } |
| |
| var byteArray = (0, _stringLiterals.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 === _tokenizer.tokens.string || token.type === _tokenizer.tokens.identifier) { |
| name = identifierFromToken(token); |
| eatToken(); |
| } else { |
| name = t.withRaw(name, ""); // preserve anonymous |
| } |
| |
| while (token.type !== _tokenizer.tokens.closeParen) { |
| /** |
| * Maybe export |
| */ |
| if (lookaheadAndCheck(_tokenizer.tokens.openParen, _tokenizer.keywords.elem)) { |
| eatToken(); // ( |
| |
| eatToken(); // elem |
| |
| while (token.type === _tokenizer.tokens.identifier) { |
| elemIndices.push(t.identifier(token.value)); |
| eatToken(); |
| } |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } else if (lookaheadAndCheck(_tokenizer.tokens.openParen, _tokenizer.keywords.export)) { |
| eatToken(); // ( |
| |
| eatToken(); // export |
| |
| if (token.type !== _tokenizer.tokens.string) { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.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(_tokenizer.tokens.closeParen); |
| } else if (isKeyword(token, _tokenizer.keywords.anyfunc)) { |
| // It's the default value, we can ignore it |
| eatToken(); // anyfunc |
| } else if (token.type === _tokenizer.tokens.number) { |
| /** |
| * Table type |
| */ |
| var min = parseInt(token.value); |
| eatToken(); |
| |
| if (token.type === _tokenizer.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" + (0, _helperCodeFrame.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 !== _tokenizer.tokens.string) { |
| throw new Error("Expected a string, " + token.type + " given."); |
| } |
| |
| var moduleName = token.value; |
| eatToken(); |
| |
| if (token.type !== _tokenizer.tokens.string) { |
| throw new Error("Expected a string, " + token.type + " given."); |
| } |
| |
| var name = token.value; |
| eatToken(); |
| eatTokenOfType(_tokenizer.tokens.openParen); |
| var descr; |
| |
| if (isKeyword(token, _tokenizer.keywords.func)) { |
| eatToken(); // keyword |
| |
| var fnParams = []; |
| var fnResult = []; |
| var typeRef; |
| var fnName = t.identifier(getUniqueName("func")); |
| |
| if (token.type === _tokenizer.tokens.identifier) { |
| fnName = identifierFromToken(token); |
| eatToken(); |
| } |
| |
| while (token.type === _tokenizer.tokens.openParen) { |
| eatToken(); |
| |
| if (lookaheadAndCheck(_tokenizer.keywords.type) === true) { |
| eatToken(); |
| typeRef = parseTypeReference(); |
| } else if (lookaheadAndCheck(_tokenizer.keywords.param) === true) { |
| eatToken(); |
| fnParams.push.apply(fnParams, _toConsumableArray(parseFuncParam())); |
| } else if (lookaheadAndCheck(_tokenizer.keywords.result) === true) { |
| eatToken(); |
| fnResult.push.apply(fnResult, _toConsumableArray(parseFuncResult())); |
| } else { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.codeFrameFromSource)(source, token.loc) + "\n" + "Unexpected token in import of type" + ", given " + tokenToString(token)); |
| }(); |
| } |
| |
| eatTokenOfType(_tokenizer.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, _tokenizer.keywords.global)) { |
| eatToken(); // keyword |
| |
| if (token.type === _tokenizer.tokens.openParen) { |
| eatToken(); // ( |
| |
| eatTokenOfType(_tokenizer.tokens.keyword); // mut keyword |
| |
| var valtype = token.value; |
| eatToken(); |
| descr = t.globalType(valtype, "var"); |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } else { |
| var _valtype = token.value; |
| eatTokenOfType(_tokenizer.tokens.valtype); |
| descr = t.globalType(_valtype, "const"); |
| } |
| } else if (isKeyword(token, _tokenizer.keywords.memory) === true) { |
| eatToken(); // Keyword |
| |
| descr = parseMemory(); |
| } else if (isKeyword(token, _tokenizer.keywords.table) === true) { |
| eatToken(); // Keyword |
| |
| descr = parseTable(); |
| } else { |
| throw new Error("Unsupported import type: " + tokenToString(token)); |
| } |
| |
| eatTokenOfType(_tokenizer.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 === _tokenizer.tokens.identifier) { |
| label = identifierFromToken(token); |
| eatToken(); |
| } else { |
| label = t.withRaw(label, ""); // preserve anonymous |
| } |
| |
| while (token.type === _tokenizer.tokens.openParen) { |
| eatToken(); |
| |
| if (lookaheadAndCheck(_tokenizer.keywords.result) === true) { |
| eatToken(); |
| blockResult = token.value; |
| eatToken(); |
| } else if (lookaheadAndCheck(_tokenizer.tokens.name) === true || lookaheadAndCheck(_tokenizer.tokens.valtype) === true || token.type === "keyword" // is any keyword |
| ) { |
| // Instruction |
| instr.push(parseFuncInstr()); |
| } else { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.codeFrameFromSource)(source, token.loc) + "\n" + "Unexpected token in block body of type" + ", given " + tokenToString(token)); |
| }(); |
| } |
| |
| maybeIgnoreComment(); |
| eatTokenOfType(_tokenizer.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 === _tokenizer.tokens.identifier) { |
| label = identifierFromToken(token); |
| eatToken(); |
| } else { |
| label = t.withRaw(label, ""); // preserve anonymous |
| } |
| |
| while (token.type === _tokenizer.tokens.openParen) { |
| eatToken(); // ( |
| |
| /** |
| * Block signature |
| */ |
| |
| if (isKeyword(token, _tokenizer.keywords.result) === true) { |
| eatToken(); |
| blockResult = token.value; |
| eatTokenOfType(_tokenizer.tokens.valtype); |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| continue; |
| } |
| /** |
| * Then |
| */ |
| |
| |
| if (isKeyword(token, _tokenizer.keywords.then) === true) { |
| eatToken(); // then |
| |
| while (token.type === _tokenizer.tokens.openParen) { |
| eatToken(); // Instruction |
| |
| if (lookaheadAndCheck(_tokenizer.tokens.name) === true || lookaheadAndCheck(_tokenizer.tokens.valtype) === true || token.type === "keyword" // is any keyword |
| ) { |
| consequent.push(parseFuncInstr()); |
| } else { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.codeFrameFromSource)(source, token.loc) + "\n" + "Unexpected token in consequent body of type" + ", given " + tokenToString(token)); |
| }(); |
| } |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| continue; |
| } |
| /** |
| * Alternate |
| */ |
| |
| |
| if (isKeyword(token, _tokenizer.keywords.else)) { |
| eatToken(); // else |
| |
| while (token.type === _tokenizer.tokens.openParen) { |
| eatToken(); // Instruction |
| |
| if (lookaheadAndCheck(_tokenizer.tokens.name) === true || lookaheadAndCheck(_tokenizer.tokens.valtype) === true || token.type === "keyword" // is any keyword |
| ) { |
| alternate.push(parseFuncInstr()); |
| } else { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.codeFrameFromSource)(source, token.loc) + "\n" + "Unexpected token in alternate body of type" + ", given " + tokenToString(token)); |
| }(); |
| } |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| continue; |
| } |
| /** |
| * Test instruction |
| */ |
| |
| |
| if (lookaheadAndCheck(_tokenizer.tokens.name) === true || lookaheadAndCheck(_tokenizer.tokens.valtype) === true || token.type === "keyword" // is any keyword |
| ) { |
| testInstrs.push(parseFuncInstr()); |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| continue; |
| } |
| |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.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 === _tokenizer.tokens.identifier) { |
| label = identifierFromToken(token); |
| eatToken(); |
| } else { |
| label = t.withRaw(label, ""); // preserve anonymous |
| } |
| |
| while (token.type === _tokenizer.tokens.openParen) { |
| eatToken(); |
| |
| if (lookaheadAndCheck(_tokenizer.keywords.result) === true) { |
| eatToken(); |
| blockResult = token.value; |
| eatToken(); |
| } else if (lookaheadAndCheck(_tokenizer.tokens.name) === true || lookaheadAndCheck(_tokenizer.tokens.valtype) === true || token.type === "keyword" // is any keyword |
| ) { |
| // Instruction |
| instr.push(parseFuncInstr()); |
| } else { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.codeFrameFromSource)(source, token.loc) + "\n" + "Unexpected token in loop body" + ", given " + tokenToString(token)); |
| }(); |
| } |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } |
| |
| return t.loopInstruction(label, blockResult, instr); |
| } |
| |
| function parseCallIndirect() { |
| var typeRef; |
| var params = []; |
| var results = []; |
| var instrs = []; |
| |
| while (token.type !== _tokenizer.tokens.closeParen) { |
| if (lookaheadAndCheck(_tokenizer.tokens.openParen, _tokenizer.keywords.type)) { |
| eatToken(); // ( |
| |
| eatToken(); // type |
| |
| typeRef = parseTypeReference(); |
| } else if (lookaheadAndCheck(_tokenizer.tokens.openParen, _tokenizer.keywords.param)) { |
| eatToken(); // ( |
| |
| eatToken(); // param |
| |
| /** |
| * Params can be empty: |
| * (params)` |
| */ |
| |
| if (token.type !== _tokenizer.tokens.closeParen) { |
| params.push.apply(params, _toConsumableArray(parseFuncParam())); |
| } |
| } else if (lookaheadAndCheck(_tokenizer.tokens.openParen, _tokenizer.keywords.result)) { |
| eatToken(); // ( |
| |
| eatToken(); // result |
| |
| /** |
| * Results can be empty: |
| * (result)` |
| */ |
| |
| if (token.type !== _tokenizer.tokens.closeParen) { |
| results.push.apply(results, _toConsumableArray(parseFuncResult())); |
| } |
| } else { |
| eatTokenOfType(_tokenizer.tokens.openParen); |
| instrs.push(parseFuncInstr()); |
| } |
| |
| eatTokenOfType(_tokenizer.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 !== _tokenizer.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(_tokenizer.tokens.openParen); |
| |
| while (token.type !== _tokenizer.tokens.closeParen) { |
| if (isKeyword(token, _tokenizer.keywords.func)) { |
| type = "Func"; |
| eatToken(); |
| index = parseExportIndex(token); |
| } else if (isKeyword(token, _tokenizer.keywords.table)) { |
| type = "Table"; |
| eatToken(); |
| index = parseExportIndex(token); |
| } else if (isKeyword(token, _tokenizer.keywords.global)) { |
| type = "Global"; |
| eatToken(); |
| index = parseExportIndex(token); |
| } else if (isKeyword(token, _tokenizer.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(_tokenizer.tokens.closeParen); |
| return t.withLoc(node, endLoc, startLoc); |
| } |
| |
| function parseModule() { |
| var name = null; |
| var isBinary = false; |
| var isQuote = false; |
| var moduleFields = []; |
| |
| if (token.type === _tokenizer.tokens.identifier) { |
| name = token.value; |
| eatToken(); |
| } |
| |
| if (hasPlugin("wast") && token.type === _tokenizer.tokens.name && token.value === "binary") { |
| eatToken(); |
| isBinary = true; |
| } |
| |
| if (hasPlugin("wast") && token.type === _tokenizer.tokens.name && token.value === "quote") { |
| eatToken(); |
| isQuote = true; |
| } |
| |
| if (isBinary === true) { |
| var blob = []; |
| |
| while (token.type === _tokenizer.tokens.string) { |
| blob.push(token.value); |
| eatToken(); |
| maybeIgnoreComment(); |
| } |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| return t.binaryModule(name, blob); |
| } |
| |
| if (isQuote === true) { |
| var string = []; |
| |
| while (token.type === _tokenizer.tokens.string) { |
| string.push(token.value); |
| eatToken(); |
| } |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| return t.quoteModule(name, string); |
| } |
| |
| while (token.type !== _tokenizer.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(_tokenizer.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 === _tokenizer.tokens.name || isKeyword(token, _tokenizer.keywords.offset)) { |
| var key = token.value; |
| eatToken(); |
| eatTokenOfType(_tokenizer.tokens.equal); |
| var value = void 0; |
| |
| if (token.type === _tokenizer.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 !== _tokenizer.tokens.closeParen && ( // $FlowIgnore |
| token.type === _tokenizer.tokens.openParen || signaturePtr < signatureLength)) { |
| if (token.type === _tokenizer.tokens.identifier) { |
| args.push(t.identifier(token.value)); |
| eatToken(); |
| } else if (token.type === _tokenizer.tokens.valtype) { |
| // Handle locals |
| args.push(t.valtypeLiteral(token.value)); |
| eatToken(); |
| } else if (token.type === _tokenizer.tokens.string) { |
| args.push(t.stringLiteral(token.value)); |
| eatToken(); |
| } else if (token.type === _tokenizer.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 === _tokenizer.tokens.openParen) { |
| /** |
| * Maybe some nested instructions |
| */ |
| eatToken(); // Instruction |
| |
| if (lookaheadAndCheck(_tokenizer.tokens.name) === true || lookaheadAndCheck(_tokenizer.tokens.valtype) === true || token.type === "keyword" // is any keyword |
| ) { |
| // $FlowIgnore |
| args.push(parseFuncInstr()); |
| } else { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.codeFrameFromSource)(source, token.loc) + "\n" + "Unexpected token in nested instruction" + ", given " + tokenToString(token)); |
| }(); |
| } |
| |
| if (token.type === _tokenizer.tokens.closeParen) { |
| eatToken(); |
| } |
| } else { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.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 === _tokenizer.tokens.name || token.type === _tokenizer.tokens.valtype) { |
| var _name2 = token.value; |
| var object; |
| eatToken(); |
| |
| if (token.type === _tokenizer.tokens.dot) { |
| object = _name2; |
| eatToken(); |
| |
| if (token.type !== _tokenizer.tokens.name) { |
| throw new TypeError("Unknown token: " + token.type + ", name expected"); |
| } |
| |
| _name2 = token.value; |
| eatToken(); |
| } |
| |
| if (token.type === _tokenizer.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, _tokenizer.keywords.loop)) { |
| /** |
| * Else a instruction with a keyword (loop or block) |
| */ |
| eatToken(); // keyword |
| |
| return parseLoop(); |
| } else if (isKeyword(token, _tokenizer.keywords.block)) { |
| eatToken(); // keyword |
| |
| return parseBlock(); |
| } else if (isKeyword(token, _tokenizer.keywords.call_indirect)) { |
| eatToken(); // keyword |
| |
| return parseCallIndirect(); |
| } else if (isKeyword(token, _tokenizer.keywords.call)) { |
| eatToken(); // keyword |
| |
| var index; |
| |
| if (token.type === _tokenizer.tokens.identifier) { |
| index = identifierFromToken(token); |
| eatToken(); |
| } else if (token.type === _tokenizer.tokens.number) { |
| index = t.indexLiteral(token.value); |
| eatToken(); |
| } |
| |
| var instrArgs = []; // Nested instruction |
| |
| while (token.type === _tokenizer.tokens.openParen) { |
| eatToken(); |
| instrArgs.push(parseFuncInstr()); |
| eatTokenOfType(_tokenizer.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, _tokenizer.keywords.if)) { |
| eatToken(); // Keyword |
| |
| return parseIf(); |
| } else if (isKeyword(token, _tokenizer.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" + (0, _helperCodeFrame.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 === _tokenizer.tokens.identifier) { |
| fnName = identifierFromToken(token); |
| eatToken(); |
| } else { |
| fnName = t.withRaw(fnName, ""); // preserve anonymous |
| } |
| |
| maybeIgnoreComment(); |
| |
| while (token.type === _tokenizer.tokens.openParen || token.type === _tokenizer.tokens.name || token.type === _tokenizer.tokens.valtype) { |
| // Instructions without parens |
| if (token.type === _tokenizer.tokens.name || token.type === _tokenizer.tokens.valtype) { |
| fnBody.push(parseFuncInstr()); |
| continue; |
| } |
| |
| eatToken(); |
| |
| if (lookaheadAndCheck(_tokenizer.keywords.param) === true) { |
| eatToken(); |
| fnParams.push.apply(fnParams, _toConsumableArray(parseFuncParam())); |
| } else if (lookaheadAndCheck(_tokenizer.keywords.result) === true) { |
| eatToken(); |
| fnResult.push.apply(fnResult, _toConsumableArray(parseFuncResult())); |
| } else if (lookaheadAndCheck(_tokenizer.keywords.export) === true) { |
| eatToken(); |
| parseFuncExport(fnName); |
| } else if (lookaheadAndCheck(_tokenizer.keywords.type) === true) { |
| eatToken(); |
| typeRef = parseTypeReference(); |
| } else if (lookaheadAndCheck(_tokenizer.tokens.name) === true || lookaheadAndCheck(_tokenizer.tokens.valtype) === true || token.type === "keyword" // is any keyword |
| ) { |
| // Instruction |
| fnBody.push(parseFuncInstr()); |
| } else { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.codeFrameFromSource)(source, token.loc) + "\n" + "Unexpected token in func body" + ", given " + tokenToString(token)); |
| }(); |
| } |
| |
| eatTokenOfType(_tokenizer.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 !== _tokenizer.tokens.string) { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.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 === _tokenizer.tokens.identifier) { |
| id = identifierFromToken(token); |
| eatToken(); |
| } |
| |
| if (lookaheadAndCheck(_tokenizer.tokens.openParen, _tokenizer.keywords.func)) { |
| eatToken(); // ( |
| |
| eatToken(); // func |
| |
| if (token.type === _tokenizer.tokens.closeParen) { |
| eatToken(); // function with an empty signature, we can abort here |
| |
| return t.typeInstruction(id, t.signature([], [])); |
| } |
| |
| if (lookaheadAndCheck(_tokenizer.tokens.openParen, _tokenizer.keywords.param)) { |
| eatToken(); // ( |
| |
| eatToken(); // param |
| |
| params = parseFuncParam(); |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } |
| |
| if (lookaheadAndCheck(_tokenizer.tokens.openParen, _tokenizer.keywords.result)) { |
| eatToken(); // ( |
| |
| eatToken(); // result |
| |
| result = parseFuncResult(); |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } |
| |
| eatTokenOfType(_tokenizer.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 !== _tokenizer.tokens.closeParen) { |
| if (token.type !== _tokenizer.tokens.valtype) { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.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 === _tokenizer.tokens.identifier) { |
| ref = identifierFromToken(token); |
| eatToken(); |
| } else if (token.type === _tokenizer.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 === _tokenizer.tokens.identifier) { |
| name = identifierFromToken(token); |
| eatToken(); |
| } else { |
| name = t.withRaw(name, ""); // preserve anonymous |
| } |
| /** |
| * maybe export |
| */ |
| |
| |
| if (lookaheadAndCheck(_tokenizer.tokens.openParen, _tokenizer.keywords.export)) { |
| eatToken(); // ( |
| |
| eatToken(); // export |
| |
| var exportName = token.value; |
| eatTokenOfType(_tokenizer.tokens.string); |
| state.registredExportedElements.push({ |
| exportType: "Global", |
| name: exportName, |
| id: name |
| }); |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } |
| /** |
| * maybe import |
| */ |
| |
| |
| if (lookaheadAndCheck(_tokenizer.tokens.openParen, _tokenizer.keywords.import)) { |
| eatToken(); // ( |
| |
| eatToken(); // import |
| |
| var moduleName = token.value; |
| eatTokenOfType(_tokenizer.tokens.string); |
| var _name3 = token.value; |
| eatTokenOfType(_tokenizer.tokens.string); |
| importing = { |
| module: moduleName, |
| name: _name3, |
| descr: undefined |
| }; |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } |
| /** |
| * global_sig |
| */ |
| |
| |
| if (token.type === _tokenizer.tokens.valtype) { |
| type = t.globalType(token.value, "const"); |
| eatToken(); |
| } else if (token.type === _tokenizer.tokens.openParen) { |
| eatToken(); // ( |
| |
| if (isKeyword(token, _tokenizer.keywords.mut) === false) { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.codeFrameFromSource)(source, token.loc) + "\n" + "Unsupported global type, expected mut" + ", given " + tokenToString(token)); |
| }(); |
| } |
| |
| eatToken(); // mut |
| |
| type = t.globalType(token.value, "var"); |
| eatToken(); |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } |
| |
| if (type === undefined) { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.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 === _tokenizer.tokens.openParen) { |
| eatToken(); |
| init.push(parseFuncInstr()); |
| eatTokenOfType(_tokenizer.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 === _tokenizer.tokens.identifier) { |
| id = token.value; |
| eatToken(); |
| } |
| |
| if (token.type === _tokenizer.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 === _tokenizer.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 === _tokenizer.tokens.identifier) { |
| tableIndex = identifierFromToken(token); |
| eatToken(); |
| } |
| |
| if (token.type === _tokenizer.tokens.number) { |
| tableIndex = t.indexLiteral(token.value); |
| eatToken(); |
| } |
| |
| while (token.type !== _tokenizer.tokens.closeParen) { |
| if (lookaheadAndCheck(_tokenizer.tokens.openParen, _tokenizer.keywords.offset)) { |
| eatToken(); // ( |
| |
| eatToken(); // offset |
| |
| while (token.type !== _tokenizer.tokens.closeParen) { |
| eatTokenOfType(_tokenizer.tokens.openParen); |
| offset.push(parseFuncInstr()); |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } else if (token.type === _tokenizer.tokens.identifier) { |
| funcs.push(t.identifier(token.value)); |
| eatToken(); |
| } else if (token.type === _tokenizer.tokens.number) { |
| funcs.push(t.indexLiteral(token.value)); |
| eatToken(); |
| } else if (token.type === _tokenizer.tokens.openParen) { |
| eatToken(); // ( |
| |
| offset.push(parseFuncInstr()); |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } else { |
| throw function () { |
| return new Error("\n" + (0, _helperCodeFrame.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 === _tokenizer.tokens.identifier) { |
| var index = identifierFromToken(token); |
| eatToken(); |
| return t.start(index); |
| } |
| |
| if (token.type === _tokenizer.tokens.number) { |
| var _index2 = t.indexLiteral(token.value); |
| |
| eatToken(); |
| return t.start(_index2); |
| } |
| |
| throw new Error("Unknown start, token: " + tokenToString(token)); |
| } |
| |
| if (token.type === _tokenizer.tokens.openParen) { |
| eatToken(); |
| var startLoc = getStartLoc(); |
| |
| if (isKeyword(token, _tokenizer.keywords.export)) { |
| eatToken(); |
| var node = parseExport(); |
| |
| var _endLoc2 = getEndLoc(); |
| |
| return t.withLoc(node, _endLoc2, startLoc); |
| } |
| |
| if (isKeyword(token, _tokenizer.keywords.loop)) { |
| eatToken(); |
| |
| var _node = parseLoop(); |
| |
| var _endLoc3 = getEndLoc(); |
| |
| return t.withLoc(_node, _endLoc3, startLoc); |
| } |
| |
| if (isKeyword(token, _tokenizer.keywords.func)) { |
| eatToken(); |
| |
| var _node2 = parseFunc(); |
| |
| var _endLoc4 = getEndLoc(); |
| |
| maybeIgnoreComment(); |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| return t.withLoc(_node2, _endLoc4, startLoc); |
| } |
| |
| if (isKeyword(token, _tokenizer.keywords.module)) { |
| eatToken(); |
| |
| var _node3 = parseModule(); |
| |
| var _endLoc5 = getEndLoc(); |
| |
| return t.withLoc(_node3, _endLoc5, startLoc); |
| } |
| |
| if (isKeyword(token, _tokenizer.keywords.import)) { |
| eatToken(); |
| |
| var _node4 = parseImport(); |
| |
| var _endLoc6 = getEndLoc(); |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| return t.withLoc(_node4, _endLoc6, startLoc); |
| } |
| |
| if (isKeyword(token, _tokenizer.keywords.block)) { |
| eatToken(); |
| |
| var _node5 = parseBlock(); |
| |
| var _endLoc7 = getEndLoc(); |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| return t.withLoc(_node5, _endLoc7, startLoc); |
| } |
| |
| if (isKeyword(token, _tokenizer.keywords.memory)) { |
| eatToken(); |
| |
| var _node6 = parseMemory(); |
| |
| var _endLoc8 = getEndLoc(); |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| return t.withLoc(_node6, _endLoc8, startLoc); |
| } |
| |
| if (isKeyword(token, _tokenizer.keywords.data)) { |
| eatToken(); |
| |
| var _node7 = parseData(); |
| |
| var _endLoc9 = getEndLoc(); |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| return t.withLoc(_node7, _endLoc9, startLoc); |
| } |
| |
| if (isKeyword(token, _tokenizer.keywords.table)) { |
| eatToken(); |
| |
| var _node8 = parseTable(); |
| |
| var _endLoc10 = getEndLoc(); |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| return t.withLoc(_node8, _endLoc10, startLoc); |
| } |
| |
| if (isKeyword(token, _tokenizer.keywords.global)) { |
| eatToken(); |
| |
| var _node9 = parseGlobal(); |
| |
| var _endLoc11 = getEndLoc(); |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| return t.withLoc(_node9, _endLoc11, startLoc); |
| } |
| |
| if (isKeyword(token, _tokenizer.keywords.type)) { |
| eatToken(); |
| |
| var _node10 = parseType(); |
| |
| var _endLoc12 = getEndLoc(); |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| return t.withLoc(_node10, _endLoc12, startLoc); |
| } |
| |
| if (isKeyword(token, _tokenizer.keywords.start)) { |
| eatToken(); |
| |
| var _node11 = parseStart(); |
| |
| var _endLoc13 = getEndLoc(); |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| return t.withLoc(_node11, _endLoc13, startLoc); |
| } |
| |
| if (isKeyword(token, _tokenizer.keywords.elem)) { |
| eatToken(); |
| |
| var _node12 = parseElem(); |
| |
| var _endLoc14 = getEndLoc(); |
| |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| return t.withLoc(_node12, _endLoc14, startLoc); |
| } |
| |
| var instruction = parseFuncInstr(); |
| var endLoc = getEndLoc(); |
| maybeIgnoreComment(); |
| |
| if (_typeof(instruction) === "object") { |
| if (typeof token !== "undefined") { |
| eatTokenOfType(_tokenizer.tokens.closeParen); |
| } |
| |
| return t.withLoc(instruction, endLoc, startLoc); |
| } |
| } |
| |
| if (token.type === _tokenizer.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" + (0, _helperCodeFrame.codeFrameFromSource)(source, token.loc) + "\n" + "Unknown token" + ", given " + tokenToString(token)); |
| }(); |
| } |
| |
| var body = []; |
| |
| while (current < tokensList.length) { |
| body.push(walk()); |
| } |
| |
| return t.program(body); |
| } |