| let source, pos, end, |
| openTokenDepth, |
| lastTokenPos, |
| openTokenPosStack, |
| openClassPosStack, |
| curDynamicImport, |
| templateStackDepth, |
| facade, |
| lastSlashWasDivision, |
| nextBraceIsClass, |
| templateDepth, |
| templateStack, |
| imports, |
| exports, |
| name; |
| |
| function addImport (ss, s, e, d) { |
| const impt = { ss, se: d === -2 ? e : d === -1 ? e + 1 : 0, s, e, d, a: -1, n: undefined }; |
| imports.push(impt); |
| return impt; |
| } |
| |
| function addExport (s, e, ls, le) { |
| exports.push({ |
| s, |
| e, |
| ls, |
| le, |
| n: s[0] === '"' ? readString(s, '"') : s[0] === "'" ? readString(s, "'") : source.slice(s, e), |
| ln: ls[0] === '"' ? readString(ls, '"') : ls[0] === "'" ? readString(ls, "'") : source.slice(ls, le) |
| }); |
| } |
| |
| function readName (impt) { |
| let { d, s } = impt; |
| if (d !== -1) |
| s++; |
| impt.n = readString(s, source.charCodeAt(s - 1)); |
| } |
| |
| // Note: parsing is based on the _assumption_ that the source is already valid |
| export function parse (_source, _name) { |
| openTokenDepth = 0; |
| curDynamicImport = null; |
| templateDepth = -1; |
| lastTokenPos = -1; |
| lastSlashWasDivision = false; |
| templateStack = Array(1024); |
| templateStackDepth = 0; |
| openTokenPosStack = Array(1024); |
| openClassPosStack = Array(1024); |
| nextBraceIsClass = false; |
| facade = true; |
| name = _name || '@'; |
| |
| imports = []; |
| exports = []; |
| |
| source = _source; |
| pos = -1; |
| end = source.length - 1; |
| let ch = 0; |
| |
| // start with a pure "module-only" parser |
| m: while (pos++ < end) { |
| ch = source.charCodeAt(pos); |
| |
| if (ch === 32 || ch < 14 && ch > 8) |
| continue; |
| |
| switch (ch) { |
| case 101/*e*/: |
| if (openTokenDepth === 0 && keywordStart(pos) && source.startsWith('xport', pos + 1)) { |
| tryParseExportStatement(); |
| // export might have been a non-pure declaration |
| if (!facade) { |
| lastTokenPos = pos; |
| break m; |
| } |
| } |
| break; |
| case 105/*i*/: |
| if (keywordStart(pos) && source.startsWith('mport', pos + 1)) |
| tryParseImportStatement(); |
| break; |
| case 59/*;*/: |
| break; |
| case 47/*/*/: { |
| const next_ch = source.charCodeAt(pos + 1); |
| if (next_ch === 47/*/*/) { |
| lineComment(); |
| // dont update lastToken |
| continue; |
| } |
| else if (next_ch === 42/***/) { |
| blockComment(true); |
| // dont update lastToken |
| continue; |
| } |
| // fallthrough |
| } |
| default: |
| // as soon as we hit a non-module token, we go to main parser |
| facade = false; |
| pos--; |
| break m; |
| } |
| lastTokenPos = pos; |
| } |
| |
| while (pos++ < end) { |
| ch = source.charCodeAt(pos); |
| |
| if (ch === 32 || ch < 14 && ch > 8) |
| continue; |
| |
| switch (ch) { |
| case 101/*e*/: |
| if (openTokenDepth === 0 && keywordStart(pos) && source.startsWith('xport', pos + 1)) |
| tryParseExportStatement(); |
| break; |
| case 105/*i*/: |
| if (keywordStart(pos) && source.startsWith('mport', pos + 1)) |
| tryParseImportStatement(); |
| break; |
| case 99/*c*/: |
| if (keywordStart(pos) && source.startsWith('lass', pos + 1) && isBrOrWs(source.charCodeAt(pos + 5))) |
| nextBraceIsClass = true; |
| break; |
| case 40/*(*/: |
| openTokenPosStack[openTokenDepth++] = lastTokenPos; |
| break; |
| case 41/*)*/: |
| if (openTokenDepth === 0) |
| syntaxError(); |
| openTokenDepth--; |
| if (curDynamicImport && curDynamicImport.d === openTokenPosStack[openTokenDepth]) { |
| if (curDynamicImport.e === 0) |
| curDynamicImport.e = pos; |
| curDynamicImport.se = pos; |
| curDynamicImport = null; |
| } |
| break; |
| case 123/*{*/: |
| // dynamic import followed by { is not a dynamic import (so remove) |
| // this is a sneaky way to get around { import () {} } v { import () } |
| // block / object ambiguity without a parser (assuming source is valid) |
| if (source.charCodeAt(lastTokenPos) === 41/*)*/ && imports.length && imports[imports.length - 1].e === lastTokenPos) { |
| imports.pop(); |
| } |
| openClassPosStack[openTokenDepth] = nextBraceIsClass; |
| nextBraceIsClass = false; |
| openTokenPosStack[openTokenDepth++] = lastTokenPos; |
| break; |
| case 125/*}*/: |
| if (openTokenDepth === 0) |
| syntaxError(); |
| if (openTokenDepth-- === templateDepth) { |
| templateDepth = templateStack[--templateStackDepth]; |
| templateString(); |
| } |
| else { |
| if (templateDepth !== -1 && openTokenDepth < templateDepth) |
| syntaxError(); |
| } |
| break; |
| case 39/*'*/: |
| case 34/*"*/: |
| stringLiteral(ch); |
| break; |
| case 47/*/*/: { |
| const next_ch = source.charCodeAt(pos + 1); |
| if (next_ch === 47/*/*/) { |
| lineComment(); |
| // dont update lastToken |
| continue; |
| } |
| else if (next_ch === 42/***/) { |
| blockComment(true); |
| // dont update lastToken |
| continue; |
| } |
| else { |
| // Division / regex ambiguity handling based on checking backtrack analysis of: |
| // - what token came previously (lastToken) |
| // - if a closing brace or paren, what token came before the corresponding |
| // opening brace or paren (lastOpenTokenIndex) |
| const lastToken = source.charCodeAt(lastTokenPos); |
| const lastExport = exports[exports.length - 1]; |
| if (isExpressionPunctuator(lastToken) && |
| !(lastToken === 46/*.*/ && (source.charCodeAt(lastTokenPos - 1) >= 48/*0*/ && source.charCodeAt(lastTokenPos - 1) <= 57/*9*/)) && |
| !(lastToken === 43/*+*/ && source.charCodeAt(lastTokenPos - 1) === 43/*+*/) && !(lastToken === 45/*-*/ && source.charCodeAt(lastTokenPos - 1) === 45/*-*/) || |
| lastToken === 41/*)*/ && isParenKeyword(openTokenPosStack[openTokenDepth]) || |
| lastToken === 125/*}*/ && (isExpressionTerminator(openTokenPosStack[openTokenDepth]) || openClassPosStack[openTokenDepth]) || |
| lastToken === 47/*/*/ && lastSlashWasDivision || |
| isExpressionKeyword(lastTokenPos) || |
| !lastToken) { |
| regularExpression(); |
| lastSlashWasDivision = false; |
| } |
| else if (lastExport && lastTokenPos >= lastExport.s && lastTokenPos <= lastExport.e) { |
| // export default /some-regexp/ |
| regularExpression(); |
| lastSlashWasDivision = false; |
| } |
| else { |
| lastSlashWasDivision = true; |
| } |
| } |
| break; |
| } |
| case 96/*`*/: |
| templateString(); |
| break; |
| } |
| lastTokenPos = pos; |
| } |
| |
| if (templateDepth !== -1 || openTokenDepth) |
| syntaxError(); |
| |
| return [imports, exports, facade]; |
| } |
| |
| function tryParseImportStatement () { |
| const startPos = pos; |
| |
| pos += 6; |
| |
| let ch = commentWhitespace(true); |
| |
| switch (ch) { |
| // dynamic import |
| case 40/*(*/: |
| openTokenPosStack[openTokenDepth++] = startPos; |
| if (source.charCodeAt(lastTokenPos) === 46/*.*/) |
| return; |
| // dynamic import indicated by positive d |
| const impt = addImport(startPos, pos + 1, 0, startPos); |
| curDynamicImport = impt; |
| // try parse a string, to record a safe dynamic import string |
| pos++; |
| ch = commentWhitespace(true); |
| if (ch === 39/*'*/ || ch === 34/*"*/) { |
| stringLiteral(ch); |
| } |
| else { |
| pos--; |
| return; |
| } |
| pos++; |
| ch = commentWhitespace(true); |
| if (ch === 44/*,*/) { |
| impt.e = pos; |
| pos++; |
| ch = commentWhitespace(true); |
| impt.a = pos; |
| readName(impt); |
| pos--; |
| } |
| else if (ch === 41/*)*/) { |
| openTokenDepth--; |
| impt.e = pos; |
| impt.se = pos; |
| readName(impt); |
| } |
| else { |
| pos--; |
| } |
| return; |
| // import.meta |
| case 46/*.*/: |
| pos++; |
| ch = commentWhitespace(true); |
| // import.meta indicated by d === -2 |
| if (ch === 109/*m*/ && source.startsWith('eta', pos + 1) && source.charCodeAt(lastTokenPos) !== 46/*.*/) |
| addImport(startPos, startPos, pos + 4, -2); |
| return; |
| |
| default: |
| // no space after "import" -> not an import keyword |
| if (pos === startPos + 6) |
| break; |
| case 34/*"*/: |
| case 39/*'*/: |
| case 123/*{*/: |
| case 42/***/: |
| // import statement only permitted at base-level |
| if (openTokenDepth !== 0) { |
| pos--; |
| return; |
| } |
| while (pos < end) { |
| ch = source.charCodeAt(pos); |
| if (ch === 39/*'*/ || ch === 34/*"*/) { |
| readImportString(startPos, ch); |
| return; |
| } |
| pos++; |
| } |
| syntaxError(); |
| } |
| } |
| |
| function tryParseExportStatement () { |
| const sStartPos = pos; |
| const prevExport = exports.length; |
| |
| pos += 6; |
| |
| const curPos = pos; |
| |
| let ch = commentWhitespace(true); |
| |
| if (pos === curPos && !isPunctuator(ch)) |
| return; |
| |
| switch (ch) { |
| // export default ... |
| case 100/*d*/: |
| addExport(pos, pos + 7, -1, -1); |
| return; |
| |
| // export async? function*? name () { |
| case 97/*a*/: |
| pos += 5; |
| commentWhitespace(true); |
| // fallthrough |
| case 102/*f*/: |
| pos += 8; |
| ch = commentWhitespace(true); |
| if (ch === 42/***/) { |
| pos++; |
| ch = commentWhitespace(true); |
| } |
| const startPos = pos; |
| ch = readToWsOrPunctuator(ch); |
| addExport(startPos, pos, startPos, pos); |
| pos--; |
| return; |
| |
| // export class name ... |
| case 99/*c*/: |
| if (source.startsWith('lass', pos + 1) && isBrOrWsOrPunctuatorNotDot(source.charCodeAt(pos + 5))) { |
| pos += 5; |
| ch = commentWhitespace(true); |
| const startPos = pos; |
| ch = readToWsOrPunctuator(ch); |
| addExport(startPos, pos, startPos, pos); |
| pos--; |
| return; |
| } |
| pos += 2; |
| // fallthrough |
| |
| // export var/let/const name = ...(, name = ...)+ |
| case 118/*v*/: |
| case 109/*l*/: |
| // destructured initializations not currently supported (skipped for { or [) |
| // also, lexing names after variable equals is skipped (export var p = function () { ... }, q = 5 skips "q") |
| pos += 2; |
| facade = false; |
| do { |
| pos++; |
| ch = commentWhitespace(true); |
| const startPos = pos; |
| ch = readToWsOrPunctuator(ch); |
| // dont yet handle [ { destructurings |
| if (ch === 123/*{*/ || ch === 91/*[*/) { |
| pos--; |
| return; |
| } |
| if (pos === startPos) |
| return; |
| addExport(startPos, pos, startPos, pos); |
| ch = commentWhitespace(true); |
| if (ch === 61/*=*/) { |
| pos--; |
| return; |
| } |
| } while (ch === 44/*,*/); |
| pos--; |
| return; |
| |
| |
| // export {...} |
| case 123/*{*/: |
| pos++; |
| ch = commentWhitespace(true); |
| while (true) { |
| const startPos = pos; |
| readToWsOrPunctuator(ch); |
| const endPos = pos; |
| commentWhitespace(true); |
| ch = readExportAs(startPos, endPos); |
| // , |
| if (ch === 44/*,*/) { |
| pos++; |
| ch = commentWhitespace(true); |
| } |
| if (ch === 125/*}*/) |
| break; |
| if (pos === startPos) |
| return syntaxError(); |
| if (pos > end) |
| return syntaxError(); |
| } |
| pos++; |
| ch = commentWhitespace(true); |
| break; |
| |
| // export * |
| // export * as X |
| case 42/***/: |
| pos++; |
| commentWhitespace(true); |
| ch = readExportAs(pos, pos); |
| ch = commentWhitespace(true); |
| break; |
| } |
| |
| // from ... |
| if (ch === 102/*f*/ && source.startsWith('rom', pos + 1)) { |
| pos += 4; |
| readImportString(sStartPos, commentWhitespace(true)); |
| |
| // There were no local names. |
| for (let i = prevExport; i < exports.length; ++i) { |
| exports[i].ls = exports[i].le = -1; |
| exports[i].ln = undefined; |
| } |
| } |
| else { |
| pos--; |
| } |
| } |
| |
| /* |
| * Ported from Acorn |
| * |
| * MIT License |
| |
| * Copyright (C) 2012-2020 by various contributors (see AUTHORS) |
| |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| * THE SOFTWARE. |
| */ |
| let acornPos; |
| function readString (start, quote) { |
| acornPos = start; |
| let out = '', chunkStart = acornPos; |
| for (;;) { |
| if (acornPos >= source.length) syntaxError(); |
| const ch = source.charCodeAt(acornPos); |
| if (ch === quote) break; |
| if (ch === 92) { // '\' |
| out += source.slice(chunkStart, acornPos); |
| out += readEscapedChar(); |
| chunkStart = acornPos; |
| } |
| else if (ch === 0x2028 || ch === 0x2029) { |
| ++acornPos; |
| } |
| else { |
| if (isBr(ch)) syntaxError(); |
| ++acornPos; |
| } |
| } |
| out += source.slice(chunkStart, acornPos++); |
| return out; |
| } |
| |
| // Used to read escaped characters |
| |
| function readEscapedChar () { |
| let ch = source.charCodeAt(++acornPos); |
| ++acornPos; |
| switch (ch) { |
| case 110: return '\n'; // 'n' -> '\n' |
| case 114: return '\r'; // 'r' -> '\r' |
| case 120: return String.fromCharCode(readHexChar(2)); // 'x' |
| case 117: return readCodePointToString(); // 'u' |
| case 116: return '\t'; // 't' -> '\t' |
| case 98: return '\b'; // 'b' -> '\b' |
| case 118: return '\u000b'; // 'v' -> '\u000b' |
| case 102: return '\f'; // 'f' -> '\f' |
| case 13: if (source.charCodeAt(acornPos) === 10) ++acornPos; // '\r\n' |
| case 10: // ' \n' |
| return ''; |
| case 56: |
| case 57: |
| syntaxError(); |
| default: |
| if (ch >= 48 && ch <= 55) { |
| let octalStr = source.substr(acornPos - 1, 3).match(/^[0-7]+/)[0]; |
| let octal = parseInt(octalStr, 8); |
| if (octal > 255) { |
| octalStr = octalStr.slice(0, -1); |
| octal = parseInt(octalStr, 8); |
| } |
| acornPos += octalStr.length - 1; |
| ch = source.charCodeAt(acornPos); |
| if (octalStr !== '0' || ch === 56 || ch === 57) |
| syntaxError(); |
| return String.fromCharCode(octal); |
| } |
| if (isBr(ch)) { |
| // Unicode new line characters after \ get removed from output in both |
| // template literals and strings |
| return ''; |
| } |
| return String.fromCharCode(ch); |
| } |
| } |
| |
| // Used to read character escape sequences ('\x', '\u', '\U'). |
| |
| function readHexChar (len) { |
| const start = acornPos; |
| let total = 0, lastCode = 0; |
| for (let i = 0; i < len; ++i, ++acornPos) { |
| let code = source.charCodeAt(acornPos), val; |
| |
| if (code === 95) { |
| if (lastCode === 95 || i === 0) syntaxError(); |
| lastCode = code; |
| continue; |
| } |
| |
| if (code >= 97) val = code - 97 + 10; // a |
| else if (code >= 65) val = code - 65 + 10; // A |
| else if (code >= 48 && code <= 57) val = code - 48; // 0-9 |
| else break; |
| if (val >= 16) break; |
| lastCode = code; |
| total = total * 16 + val; |
| } |
| |
| if (lastCode === 95 || acornPos - start !== len) syntaxError(); |
| |
| return total; |
| } |
| |
| // Read a string value, interpreting backslash-escapes. |
| |
| function readCodePointToString () { |
| const ch = source.charCodeAt(acornPos); |
| let code; |
| if (ch === 123) { // '{' |
| ++acornPos; |
| code = readHexChar(source.indexOf('}', acornPos) - acornPos); |
| ++acornPos; |
| if (code > 0x10FFFF) syntaxError(); |
| } else { |
| code = readHexChar(4); |
| } |
| // UTF-16 Decoding |
| if (code <= 0xFFFF) return String.fromCharCode(code); |
| code -= 0x10000; |
| return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00); |
| } |
| |
| /* |
| * </ Acorn Port> |
| */ |
| |
| function readExportAs (startPos, endPos) { |
| let ch = source.charCodeAt(pos); |
| let ls = startPos, le = endPos; |
| if (ch === 97 /*a*/) { |
| pos += 2; |
| ch = commentWhitespace(true); |
| startPos = pos; |
| readToWsOrPunctuator(ch); |
| endPos = pos; |
| ch = commentWhitespace(true); |
| } |
| if (pos !== startPos) |
| addExport(startPos, endPos, ls, le); |
| return ch; |
| } |
| |
| function readImportString (ss, ch) { |
| const startPos = pos + 1; |
| if (ch === 39/*'*/ || ch === 34/*"*/) { |
| stringLiteral(ch); |
| } |
| else { |
| syntaxError(); |
| return; |
| } |
| const impt = addImport(ss, startPos, pos, -1); |
| readName(impt); |
| pos++; |
| ch = commentWhitespace(false); |
| if (ch !== 97/*a*/ || !source.startsWith('ssert', pos + 1)) { |
| pos--; |
| return; |
| } |
| const assertIndex = pos; |
| |
| pos += 6; |
| ch = commentWhitespace(true); |
| if (ch !== 123/*{*/) { |
| pos = assertIndex; |
| return; |
| } |
| const assertStart = pos; |
| do { |
| pos++; |
| ch = commentWhitespace(true); |
| if (ch === 39/*'*/ || ch === 34/*"*/) { |
| stringLiteral(ch); |
| pos++; |
| ch = commentWhitespace(true); |
| } |
| else { |
| ch = readToWsOrPunctuator(ch); |
| } |
| if (ch !== 58/*:*/) { |
| pos = assertIndex; |
| return; |
| } |
| pos++; |
| ch = commentWhitespace(true); |
| if (ch === 39/*'*/ || ch === 34/*"*/) { |
| stringLiteral(ch); |
| } |
| else { |
| pos = assertIndex; |
| return; |
| } |
| pos++; |
| ch = commentWhitespace(true); |
| if (ch === 44/*,*/) { |
| pos++; |
| ch = commentWhitespace(true); |
| if (ch === 125/*}*/) |
| break; |
| continue; |
| } |
| if (ch === 125/*}*/) |
| break; |
| pos = assertIndex; |
| return; |
| } while (true); |
| impt.a = assertStart; |
| impt.se = pos + 1; |
| } |
| |
| function commentWhitespace (br) { |
| let ch; |
| do { |
| ch = source.charCodeAt(pos); |
| if (ch === 47/*/*/) { |
| const next_ch = source.charCodeAt(pos + 1); |
| if (next_ch === 47/*/*/) |
| lineComment(); |
| else if (next_ch === 42/***/) |
| blockComment(br); |
| else |
| return ch; |
| } |
| else if (br ? !isBrOrWs(ch): !isWsNotBr(ch)) { |
| return ch; |
| } |
| } while (pos++ < end); |
| return ch; |
| } |
| |
| function templateString () { |
| while (pos++ < end) { |
| const ch = source.charCodeAt(pos); |
| if (ch === 36/*$*/ && source.charCodeAt(pos + 1) === 123/*{*/) { |
| pos++; |
| templateStack[templateStackDepth++] = templateDepth; |
| templateDepth = ++openTokenDepth; |
| return; |
| } |
| if (ch === 96/*`*/) |
| return; |
| if (ch === 92/*\*/) |
| pos++; |
| } |
| syntaxError(); |
| } |
| |
| function blockComment (br) { |
| pos++; |
| while (pos++ < end) { |
| const ch = source.charCodeAt(pos); |
| if (!br && isBr(ch)) |
| return; |
| if (ch === 42/***/ && source.charCodeAt(pos + 1) === 47/*/*/) { |
| pos++; |
| return; |
| } |
| } |
| } |
| |
| function lineComment () { |
| while (pos++ < end) { |
| const ch = source.charCodeAt(pos); |
| if (ch === 10/*\n*/ || ch === 13/*\r*/) |
| return; |
| } |
| } |
| |
| function stringLiteral (quote) { |
| while (pos++ < end) { |
| let ch = source.charCodeAt(pos); |
| if (ch === quote) |
| return; |
| if (ch === 92/*\*/) { |
| ch = source.charCodeAt(++pos); |
| if (ch === 13/*\r*/ && source.charCodeAt(pos + 1) === 10/*\n*/) |
| pos++; |
| } |
| else if (isBr(ch)) |
| break; |
| } |
| syntaxError(); |
| } |
| |
| function regexCharacterClass () { |
| while (pos++ < end) { |
| let ch = source.charCodeAt(pos); |
| if (ch === 93/*]*/) |
| return ch; |
| if (ch === 92/*\*/) |
| pos++; |
| else if (ch === 10/*\n*/ || ch === 13/*\r*/) |
| break; |
| } |
| syntaxError(); |
| } |
| |
| function regularExpression () { |
| while (pos++ < end) { |
| let ch = source.charCodeAt(pos); |
| if (ch === 47/*/*/) |
| return; |
| if (ch === 91/*[*/) |
| ch = regexCharacterClass(); |
| else if (ch === 92/*\*/) |
| pos++; |
| else if (ch === 10/*\n*/ || ch === 13/*\r*/) |
| break; |
| } |
| syntaxError(); |
| } |
| |
| function readToWsOrPunctuator (ch) { |
| do { |
| if (isBrOrWs(ch) || isPunctuator(ch)) |
| return ch; |
| } while (ch = source.charCodeAt(++pos)); |
| return ch; |
| } |
| |
| // Note: non-asii BR and whitespace checks omitted for perf / footprint |
| // if there is a significant user need this can be reconsidered |
| function isBr (c) { |
| return c === 13/*\r*/ || c === 10/*\n*/; |
| } |
| |
| function isWsNotBr (c) { |
| return c === 9 || c === 11 || c === 12 || c === 32 || c === 160; |
| } |
| |
| function isBrOrWs (c) { |
| return c > 8 && c < 14 || c === 32 || c === 160; |
| } |
| |
| function isBrOrWsOrPunctuatorNotDot (c) { |
| return c > 8 && c < 14 || c === 32 || c === 160 || isPunctuator(c) && c !== 46/*.*/; |
| } |
| |
| function keywordStart (pos) { |
| return pos === 0 || isBrOrWsOrPunctuatorNotDot(source.charCodeAt(pos - 1)); |
| } |
| |
| function readPrecedingKeyword (pos, match) { |
| if (pos < match.length - 1) |
| return false; |
| return source.startsWith(match, pos - match.length + 1) && (pos === 0 || isBrOrWsOrPunctuatorNotDot(source.charCodeAt(pos - match.length))); |
| } |
| |
| function readPrecedingKeyword1 (pos, ch) { |
| return source.charCodeAt(pos) === ch && (pos === 0 || isBrOrWsOrPunctuatorNotDot(source.charCodeAt(pos - 1))); |
| } |
| |
| // Detects one of case, debugger, delete, do, else, in, instanceof, new, |
| // return, throw, typeof, void, yield, await |
| function isExpressionKeyword (pos) { |
| switch (source.charCodeAt(pos)) { |
| case 100/*d*/: |
| switch (source.charCodeAt(pos - 1)) { |
| case 105/*i*/: |
| // void |
| return readPrecedingKeyword(pos - 2, 'vo'); |
| case 108/*l*/: |
| // yield |
| return readPrecedingKeyword(pos - 2, 'yie'); |
| default: |
| return false; |
| } |
| case 101/*e*/: |
| switch (source.charCodeAt(pos - 1)) { |
| case 115/*s*/: |
| switch (source.charCodeAt(pos - 2)) { |
| case 108/*l*/: |
| // else |
| return readPrecedingKeyword1(pos - 3, 101/*e*/); |
| case 97/*a*/: |
| // case |
| return readPrecedingKeyword1(pos - 3, 99/*c*/); |
| default: |
| return false; |
| } |
| case 116/*t*/: |
| // delete |
| return readPrecedingKeyword(pos - 2, 'dele'); |
| default: |
| return false; |
| } |
| case 102/*f*/: |
| if (source.charCodeAt(pos - 1) !== 111/*o*/ || source.charCodeAt(pos - 2) !== 101/*e*/) |
| return false; |
| switch (source.charCodeAt(pos - 3)) { |
| case 99/*c*/: |
| // instanceof |
| return readPrecedingKeyword(pos - 4, 'instan'); |
| case 112/*p*/: |
| // typeof |
| return readPrecedingKeyword(pos - 4, 'ty'); |
| default: |
| return false; |
| } |
| case 110/*n*/: |
| // in, return |
| return readPrecedingKeyword1(pos - 1, 105/*i*/) || readPrecedingKeyword(pos - 1, 'retur'); |
| case 111/*o*/: |
| // do |
| return readPrecedingKeyword1(pos - 1, 100/*d*/); |
| case 114/*r*/: |
| // debugger |
| return readPrecedingKeyword(pos - 1, 'debugge'); |
| case 116/*t*/: |
| // await |
| return readPrecedingKeyword(pos - 1, 'awai'); |
| case 119/*w*/: |
| switch (source.charCodeAt(pos - 1)) { |
| case 101/*e*/: |
| // new |
| return readPrecedingKeyword1(pos - 2, 110/*n*/); |
| case 111/*o*/: |
| // throw |
| return readPrecedingKeyword(pos - 2, 'thr'); |
| default: |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| function isParenKeyword (curPos) { |
| return source.charCodeAt(curPos) === 101/*e*/ && source.startsWith('whil', curPos - 4) || |
| source.charCodeAt(curPos) === 114/*r*/ && source.startsWith('fo', curPos - 2) || |
| source.charCodeAt(curPos - 1) === 105/*i*/ && source.charCodeAt(curPos) === 102/*f*/; |
| } |
| |
| function isPunctuator (ch) { |
| // 23 possible punctuator endings: !%&()*+,-./:;<=>?[]^{}|~ |
| return ch === 33/*!*/ || ch === 37/*%*/ || ch === 38/*&*/ || |
| ch > 39 && ch < 48 || ch > 57 && ch < 64 || |
| ch === 91/*[*/ || ch === 93/*]*/ || ch === 94/*^*/ || |
| ch > 122 && ch < 127; |
| } |
| |
| function isExpressionPunctuator (ch) { |
| // 20 possible expression endings: !%&(*+,-.:;<=>?[^{|~ |
| return ch === 33/*!*/ || ch === 37/*%*/ || ch === 38/*&*/ || |
| ch > 39 && ch < 47 && ch !== 41 || ch > 57 && ch < 64 || |
| ch === 91/*[*/ || ch === 94/*^*/ || ch > 122 && ch < 127 && ch !== 125/*}*/; |
| } |
| |
| function isExpressionTerminator (curPos) { |
| // detects: |
| // => ; ) finally catch else |
| // as all of these followed by a { will indicate a statement brace |
| switch (source.charCodeAt(curPos)) { |
| case 62/*>*/: |
| return source.charCodeAt(curPos - 1) === 61/*=*/; |
| case 59/*;*/: |
| case 41/*)*/: |
| return true; |
| case 104/*h*/: |
| return source.startsWith('catc', curPos - 4); |
| case 121/*y*/: |
| return source.startsWith('finall', curPos - 6); |
| case 101/*e*/: |
| return source.startsWith('els', curPos - 3); |
| } |
| return false; |
| } |
| |
| function syntaxError () { |
| throw Object.assign(new Error(`Parse error ${name}:${source.slice(0, pos).split('\n').length}:${pos - source.lastIndexOf('\n', pos - 1)}`), { idx: pos }); |
| } |