UNPKG

js-ast-parser

Version:
1,150 lines 62 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JavascriptAstParsser = void 0; const JavascriptAst_1 = require("./JavascriptAst"); const constants_1 = require("./constants"); const patterns_1 = require("./patterns"); const javasriptAstParserUtil_1 = require("./javasriptAstParserUtil"); const UnExpectStatement_1 = require("./lib/statement/UnExpectStatement"); const ImportStatement_1 = require("./lib/statement/ImportStatement"); const StringLiteralExpression_1 = require("./lib/expression/StringLiteralExpression"); const IdentifierLiteralExpression_1 = require("./lib/expression/IdentifierLiteralExpression"); const BinaryExpression_1 = require("./lib/expression/BinaryExpression"); const MemberExpression_1 = require("./lib/expression/MemberExpression"); const CallExpression_1 = require("./lib/expression/CallExpression"); const TernaryExpression_1 = require("./lib/expression/TernaryExpression"); const TemplateLiteralExppression_1 = require("./lib/expression/TemplateLiteralExppression"); const VariableDeclarationStatement_1 = require("./lib/statement/VariableDeclarationStatement"); const FunctionDeclarationStatement_1 = require("./lib/statement/FunctionDeclarationStatement"); const Block_1 = require("./Block"); const SwitchCaseStatement_1 = require("./lib/statement/SwitchCaseStatement"); const TryCatchStatement_1 = require("./lib/statement/TryCatchStatement"); const DoWhileStatement_1 = require("./lib/statement/DoWhileStatement"); const WhileStatement_1 = require("./lib/statement/WhileStatement"); const ForStatement_1 = require("./lib/statement/ForStatement"); const IfStatement_1 = require("./lib/statement/IfStatement"); const ObjectLiteralExpression_1 = require("./lib/expression/ObjectLiteralExpression"); const ArrayLiteralExpression_1 = require("./lib/expression/ArrayLiteralExpression"); const UnaryExpression_1 = require("./lib/expression/UnaryExpression"); const SwitchStatement_1 = require("./lib/statement/SwitchStatement"); const WithStatement_1 = require("./lib/statement/WithStatement"); const NewExpressioin_1 = require("./lib/expression/NewExpressioin"); const ExportDeclarationStatement_1 = require("./lib/statement/ExportDeclarationStatement"); const ClassDeclarationStatement_1 = require("./lib/statement/ClassDeclarationStatement"); const AssignmentExpression_1 = require("./lib/expression/AssignmentExpression"); const RegExpressioin_1 = require("./lib/expression/RegExpressioin"); const ParticularLiteralExpression_1 = require("./lib/expression/ParticularLiteralExpression"); const NumberLiteratalExpression_1 = require("./lib/expression/NumberLiteratalExpression"); const ReturnStatement_1 = require("./lib/statement/ReturnStatement"); const ThrowStatement_1 = require("./lib/statement/ThrowStatement"); const DeleteStatement_1 = require("./lib/statement/DeleteStatement"); const TypeOfExpression_1 = require("./lib/expression/TypeOfExpression"); const FuntionExpression_1 = require("./lib/expression/FuntionExpression"); const scope_analyzer_1 = require("./lib/scope-analyzer/scope-analyzer"); const BreakStatement_1 = require("./lib/statement/BreakStatement"); const ContinueStatement_1 = require("./lib/statement/ContinueStatement"); const VoidExpression_1 = require("./lib/expression/VoidExpression"); const BracketEnwrapedExpressioin_1 = require("./lib/expression/BracketEnwrapedExpressioin"); const DebuggerStatement_1 = require("./lib/statement/DebuggerStatement"); class JavascriptAstParsser { constructor(input) { this.input = input; this.pos = 0; this.currentToken = {}; this.excutionFlag = 0; this.startTokens = []; this.tokens = []; this.currentLine = 1; this.isParsingFunDeclaration = false; this.isParsingLoop = false; this.depth = 1; this.isParsingVarDeclaration = false; } get curCharCode() { return this.input.charCodeAt(this.pos); } get nextCharCode() { return this.input.charCodeAt(this.pos + 1); } get curChar() { return this.input.charAt(this.pos); } parse(analyze = true) { this.ast = new JavascriptAst_1.JavascriptAst(); let topLevelBlock = this.ast.topLevelBlock; this.ast.topLevelBlock.depth = this.depth; while (!this.isReadAll()) { let node = this.parseNode(); if (!node) { if (this.currentToken.value === '{') { const reset = this.markPos(); node = this.parseObjLiteralExp(); if (node.code === constants_1.NodeCode.ObjectLiteralExpression && !node.properties.length && node.unexpectedNodes.length) { reset(); node = this.parseBlock(); } } else if (this.currentToken.value === '[') { node = this.parseArrExp(); } } else if (node.isUnexpectEnd) { node = new UnExpectStatement_1.UnExpectStatement(node, 'unexpect end value'); } node && topLevelBlock.body.push(node); } debugger; if (analyze) { new scope_analyzer_1.JavascriptScopeAnalyzer().analyze(this.ast.topLevelBlock); } return this.ast; } isReadAll() { return this.pos > this.input.length - 1; } isLineEnd() { let pos = this.pos, line = this.currentLine; this.skipSpace(); let curLine = this.currentLine; let isLineEnd = curLine > line || this.isReadAll(); this.pos = pos; this.currentLine = line; return isLineEnd; } advance(offset) { this.pos = this.pos + (offset || 1); } markPos() { let pos = this.pos, token = this.currentToken, len = this.tokens.length, startTokens = this.startTokens.slice(); return () => { this.pos = pos; this.currentToken = token; this.tokens.length = len; this.startTokens = startTokens; }; } skipSpace() { loop: while (!this.isReadAll()) { let ch = this.input.charCodeAt(this.pos); switch (ch) { case 32: case 160: ++this.pos; break; case 13: if (this.input.charCodeAt(this.pos + 1) === 10) { ++this.pos; } case 10: case 8232: case 8233: this.pos++; this.currentLine++; break; case 47: if (this.nextCharCode === 47) { this.pos++; this.skipLineComment(); break; } else if (this.nextCharCode === 42) { this.pos++; this.skipBlockComment(); break; } break loop; default: if (ch > 8 && ch < 14) { ++this.pos; } else { break loop; } } } } consumeNextWordIfMatch(expectedWord) { let reset = this.markPos(); this.next(); if (this.currentToken.value === expectedWord) { return true; } reset(); return false; } isNextWordIs(expectedWord) { let reset = this.markPos(); this.next(); if (this.currentToken.value === expectedWord) { reset(); return true; } reset(); return false; } startParse(node, excutionFlag) { let prevParsingNode = this.parsingNode, prevIsParsingLoop = this.isParsingLoop, isParsingFunDeclaration = this.isParsingFunDeclaration; this.parsingNode = node; excutionFlag && (this.excutionFlag |= excutionFlag); return (expectExpressionEnd = true) => { if (expectExpressionEnd && !prevParsingNode) { this.expectExpressionEnd(); } node.loc.end = this.pos; if (this.parsingNode.code === constants_1.NodeCode.FunctionDeclarationStatement) { this.isParsingFunDeclaration = false; } else if ((constants_1.NodeCode.Loop & this.parsingNode.code) === this.parsingNode.code) { this.isParsingLoop = false; } this.parsingNode = prevParsingNode; this.isParsingLoop = prevIsParsingLoop; this.isParsingFunDeclaration = isParsingFunDeclaration; excutionFlag && (this.excutionFlag &= ~excutionFlag); if (node.isUnexpectEnd && this.parsingNode) { this.parsingNode.isUnexpectEnd = true; } return node; }; } expectToken(expectedToken) { if (this.isReadAll()) { this.parsingNode.isUnexpectEnd = true; return false; } expectedToken = typeof expectedToken === 'string' ? [expectedToken] : expectedToken; for (let value of expectedToken) { this.next(); if (this.currentToken.value !== value) { this.parsingNode.unexpectedNodes.push(new UnExpectStatement_1.UnExpectStatement(this.currentToken.value, `expect ${value}`, this.currentToken.loc)); return false; } } return true; } expectExpressionEnd() { this.isReadAll() || this.isLineEnd() || this.expectToken(';'); } parseNode(context = {}) { if (this.isReadAll()) return null; this.next(); let node; switch (this.currentToken.type) { case 'Keyword': return this.parseIsStartWithKeyword(); case 'ParticularLiteral': return new ParticularLiteralExpression_1.ParticularLiteralExpression(this.currentToken); case 'TemplateLiteralExpStart': return this.parseTemplateLiteralExp(); case 'IdentifierLiteral': node = new IdentifierLiteralExpression_1.IdentifierLiteralExpression(this.currentToken); if (this.excutionFlag & constants_1.NEED_PURE_IDENTIFIER) return node; return this.tryParseAssign(node) || !this.isParsingVarDeclaration && this.tryParse(node) || node; case 'StringLiteral': return this.tryParse(new StringLiteralExpression_1.StringLiteralExpression(this.currentToken)); case 'UnclosedStringLiteral': return new UnExpectStatement_1.UnExpectStatement(new StringLiteralExpression_1.StringLiteralExpression(this.currentToken), 'unclosed string', this.currentToken.loc); case 'NumberLiteral': node = new NumberLiteratalExpression_1.NumberLiteralExpression(this.currentToken); return this.tryParse(node); case 'Operator': if (!context.expectOperator && patterns_1.Patterns.prefixUnary.test(this.currentToken.value)) { return this.parseUnaryExpression(); } return null; case 'RegExpStart': if (context.expectOperator) { return null; } return this.parseRegStatement(); case 'IllegalToken': return new UnExpectStatement_1.UnExpectStatement(this.currentToken.value, 'Illegal token', this.currentToken.loc); } if (!this.currentToken.value && this.isReadAll()) { if (this.parsingNode) this.parsingNode.isUnexpectEnd = true; } return null; } next() { this.skipSpace(); this.currentToken = { loc: { start: null, end: null } }; this.currentToken.loc.start = this.pos; this.readWord(); this.currentToken.loc.end = this.pos; this.tokens.push(Object.assign(this.currentToken)); return this.currentToken; } readWord() { if ((0, javasriptAstParserUtil_1.isIdentifierStart)(this.curCharCode)) { this.currentToken.value = this.readIdentifier(); if (!(this.excutionFlag & constants_1.IS_PARSING_ACCESSPROP) && !(this.excutionFlag & constants_1.IS_PARSING_OBJECT_KEY) && (this.currentToken.value === 'instanceof' || this.currentToken.value === 'in')) { this.currentToken.type = 'Operator'; } else if (patterns_1.Patterns.keywords.test(this.currentToken.value) && !(this.excutionFlag & constants_1.IS_PARSING_ACCESSPROP) && !(this.excutionFlag & constants_1.IS_PARSING_OBJECT_KEY)) { this.currentToken.type = 'Keyword'; } else if (patterns_1.Patterns.ParticularLiteral.test(this.currentToken.value)) { this.currentToken.type = 'ParticularLiteral'; } else { this.currentToken.type = 'IdentifierLiteral'; } } else if ((0, javasriptAstParserUtil_1.isNumberStart)(this.curCharCode)) { this.currentToken.type = 'NumberLiteral'; this.currentToken.value = this.readNumber(); } else if (this.curCharCode === 39 || this.curCharCode === 34) { let value = this.readString(); if (this.curCharCode !== this.stringStartToken) { this.currentToken.type = 'UnclosedStringLiteral'; } else { this.currentToken.type = 'StringLiteral'; this.advance(); } this.currentToken.value = value; } else if (patterns_1.Patterns.operators.test(this.curChar)) { this.currentToken.type = 'Operator'; this.currentToken.value = this.readOperator(); this.currentToken.isAssignOperator = patterns_1.Patterns.assign.test(this.currentToken.value); } else { let start = this.pos; this.advance(); this.currentToken.value = this.input.slice(start, this.pos); if (patterns_1.Patterns.blockStart.test(this.currentToken.value)) { this.currentToken.type = 'BlockStart'; } else if (patterns_1.Patterns.blockClose.test(this.currentToken.value)) { this.currentToken.type = 'BlockClose'; } else if (this.currentToken.value === '`') { this.currentToken.type = 'TemplateLiteralExpStart'; } else if (this.currentToken.value && ';.,:'.indexOf(this.currentToken.value) < 0) { this.currentToken.value += this.readIllegalToken(); this.currentToken.type = 'IllegalToken'; } } } readIllegalToken() { let start = this.pos; while (!this.isLineEnd() && this.curChar !== ';') { this.advance(); } return this.input.slice(start, this.pos); } readNumber() { let start = this.pos; this.consumeNumber(); return this.input.slice(start, this.pos); } consumeNumber() { if (this.curCharCode === 48) { switch (this.nextCharCode) { case 98: this.advance(2); this.consumeBinary(); break; case 120: this.advance(2); this.consumeHex(); break; default: this.advance(); break; } } else { this.currentToken.type = 'NumberLiteral'; let isReadingDecimal = false; while (((0, javasriptAstParserUtil_1.isNumberCharCode)(this.curCharCode) || this.curCharCode === 46) && !this.isReadAll()) { if (this.curCharCode === 46) { if (isReadingDecimal) { this.consumeUnexpect([]); return; } isReadingDecimal = true; } this.advance(); } } } readString() { this.stringStartToken = this.curCharCode; this.advance(); let start = this.pos; this.consumeString(); return this.input.slice(start, this.pos); } consumeString() { while (this.curCharCode !== this.stringStartToken && this.curCharCode !== 10 && !this.isReadAll()) { this.advance(); } } readIdentifier() { let start = this.pos; this.advance(); this.consumeIdentifier(); return this.input.slice(start, this.pos); } consumeIdentifier() { while ((0, javasriptAstParserUtil_1.isIdentifierChar)(this.curCharCode)) { this.advance(); if (this.isReadAll()) break; } } readOperator() { let start = this.pos; if (patterns_1.Patterns.canReapeat.test(this.curChar) && this.nextCharCode === this.curCharCode || patterns_1.Patterns.canPrefixAssign.test(this.curChar) && this.nextCharCode === 61 || this.curCharCode === 33 && this.nextCharCode === 61) { this.advance(); } if (this.curCharCode === 61 && this.nextCharCode === 61) { this.advance(); } this.advance(); return this.input.slice(start, this.pos); } safelyParseNode() { let node = this.parseNode(); if (!this.parsingNode) { return node; } if ((0, javasriptAstParserUtil_1.assertUnexpect)(this.parsingNode, node)) return null; return node; } getExpectedNode(expectedCode) { let node = this.safelyParseNode(); if (node) { if ((expectedCode & node.code) === node.code) return node; this.parsingNode.unexpectedNodes.push(new UnExpectStatement_1.UnExpectStatement(node, '应为表达式')); } else if (this.isReadAll()) { this.parsingNode.isUnexpectEnd = true; } else { if (this.parsingNode && this.parsingNode.unexpectedNodes.length) return null; if (this.currentToken.value === '(' && expectedCode === constants_1.NodeCode.Expression) return this.parseBracketEnwrapedExpression(); if (this.currentToken.value === '{' && (expectedCode & constants_1.NodeCode.ObjectLiteralExpression) === constants_1.NodeCode.ObjectLiteralExpression) return this.parseObjLiteralExp(); if (this.currentToken.value === '[' && (expectedCode & constants_1.NodeCode.ArrayLiteralExpression) === constants_1.NodeCode.ArrayLiteralExpression) return this.parseArrExp(); if (this.currentToken.value === '/' && (expectedCode & constants_1.NodeCode.RegExpression) === constants_1.NodeCode.RegExpression) return this.parseRegStatement(); this.parsingNode.unexpectedNodes.push(new UnExpectStatement_1.UnExpectStatement(this.currentToken.value, `字符 '${this.currentToken.value}' 不该出现在此处`, this.currentToken.loc)); } return null; } expectExpression() { return this.getExpectedNode(constants_1.NodeCode.Expression); } tryToGetExpectedNode(expectedCode) { let reset = this.markPos(); let node = this.parseNode(); if (node && !node.unexpectedNodes.length && (node.code & expectedCode) === node.code) return node; reset(); return null; } parseBlock(terminationTest = (node) => !node && this.currentToken.value === '}') { let prevDepth = this.depth; this.depth++; let block = new Block_1.Block(), prevContext = this.excutionFlag, prevParsingNode = this.parsingNode; block.loc = { start: this.pos, end: null }; block.depth = this.depth; this.excutionFlag = 0; this.parsingNode = null; while (true) { if (this.isReadAll()) { prevParsingNode.isUnexpectEnd = true; break; } let node = this.parseNode(); if (!node) { if (this.currentToken.value === '{') { const reset = this.markPos(); node = this.parseObjLiteralExp(); if (node.code === constants_1.NodeCode.ObjectLiteralExpression && !node.properties.length && node.unexpectedNodes.length) { reset(); node = this.parseBlock(); } } else if (this.currentToken.value === '[') { node = this.parseArrExp(); } } if (node) { if (node.isUnexpectEnd) { block.body.push(new UnExpectStatement_1.UnExpectStatement(node, 'unexpect end value')); } else { block.body.push(node); } } if (terminationTest(node)) break; } this.excutionFlag = prevContext; this.parsingNode = prevParsingNode; this.depth = prevDepth; let len = block.body.length; if (len) { block.loc.end = block.body[len - 1].loc.end; } else { block.loc.end = block.loc.start; } return block; } parseUnaryExpression() { let node = new UnaryExpression_1.UnaryExpression(this.currentToken); let finishParse = this.startParse(node); node.prefix = true; node.operator = this.currentToken.value; node.argument = this.expectExpression(); return finishParse(); } parseImportStatement() { let node = this.tryToParseCallExpression(new IdentifierLiteralExpression_1.IdentifierLiteralExpression(this.currentToken)); if (node) { return node; } else { node = new ImportStatement_1.ImportStatement(this.currentToken); let finishParse = this.startParse(node, constants_1.IS_PARSING_IMPORT); if (node.from = this.tryToGetExpectedNode(constants_1.NodeCode.StringLiteralExpression)) return finishParse(); if (this.consumeNextWordIfMatch('{')) { node.specifiers = this.getImportedSpecifiers(); } else if (node.imported = this.getExpectedNode(constants_1.NodeCode.IdentifierLiteralExpression)) { if (this.consumeNextWordIfMatch('as') && !(node.local = this.getExpectedNode(constants_1.NodeCode.IdentifierLiteralExpression))) { return finishParse(); } } if (node.unexpectedNodes.length || !this.expectToken('from')) return finishParse(); node.from = this.getExpectedNode(constants_1.NodeCode.StringLiteralExpression); return finishParse(); } } getImportedSpecifiers() { let specifiers = [], expectComma = false; while (!this.consumeNextWordIfMatch('}') && !this.isReadAll()) { if (expectComma) { if (!this.expectToken(',')) { break; } } else { let node = this.getExpectedNode(constants_1.NodeCode.IdentifierLiteralExpression); if (!node) break; let specifier = { imported: node, local: null }; if (this.consumeNextWordIfMatch(':') || this.consumeNextWordIfMatch('as')) { if (!(specifier.local = this.getExpectedNode(constants_1.NodeCode.IdentifierLiteralExpression))) { break; } } specifiers.push(specifier); } expectComma = !expectComma; } return specifiers; } parseVarDeclarationStatement() { this.isParsingVarDeclaration = true; let statement = new VariableDeclarationStatement_1.VariableDeclarationStatement(this.currentToken); let finishParse = this.startParse(statement, constants_1.IS_PARSING_VARDECLARATION); let declaration; while (declaration = this.getExpectedNode(constants_1.NodeCode.IdentifierLiteralExpression | constants_1.NodeCode.AssignmentExpression)) { statement.declarations.push(declaration); if (!this.consumeNextWordIfMatch(',')) { break; } } this.isParsingVarDeclaration = false; return finishParse(); } parseFunDeclarationStatement() { let isClassMethod = this.currentToken.value !== 'function'; let statement = new FunctionDeclarationStatement_1.FunctionDeclarationStatement(this.currentToken); let finishParse = this.startParse(statement, constants_1.IS_PARSING_FUNDECLARATION); this.isParsingFunDeclaration = true; if ((statement.identifier = this.getExpectedNode(constants_1.NodeCode.IdentifierLiteralExpression)) && this.expectToken('(')) { let params; if (!this.consumeNextWordIfMatch(')')) { while (params = this.getExpectedNode(constants_1.NodeCode.IdentifierLiteralExpression)) { statement.params.push(params); if (this.consumeNextWordIfMatch(')')) { break; } else if (this.expectToken(',')) { continue; } } } } if (isClassMethod && statement.identifier) statement.loc.start = statement.identifier.loc.start; if (statement.unexpectedNodes.length) return finishParse(); if (this.expectToken('{')) { statement.body = this.parseBlock(); statement.body.functionBlock = true; } return finishParse(); } tryParseFunciotnExpress() { let currentToken = this.currentToken; if (this.consumeNextWordIfMatch('(')) { this.isParsingFunDeclaration = true; let expression = new FuntionExpression_1.FunctionExpression(currentToken); let finishParse = this.startParse(expression); let param; if (!this.consumeNextWordIfMatch(')')) { while (param = this.getExpectedNode(constants_1.NodeCode.IdentifierLiteralExpression)) { expression.params.push(param); if (this.consumeNextWordIfMatch(')') || !this.expectToken(',')) break; } } ; if (!expression.unexpectedNodes.length && this.expectToken('{')) { expression.body = this.parseBlock(); expression.body.functionBlock = true; } return finishParse(); } return null; } parseObjLiteralExp() { let expression = new ObjectLiteralExpression_1.ObjectLiteralExpression(this.currentToken); let finishParse = this.startParse(expression); let key, value; this.excutionFlag |= constants_1.IS_PARSING_OBJECT_KEY; while (!this.consumeNextWordIfMatch('}') && (key = this.getExpectedNode(constants_1.NodeCode.ObjectKey))) { this.excutionFlag &= ~constants_1.IS_PARSING_OBJECT_KEY; if (this.expectToken(':') && (value = this.getExpectedNode(constants_1.NodeCode.Expression))) { expression.properties.push({ key: key, value: value }); this.excutionFlag |= constants_1.IS_PARSING_OBJECT_KEY; if (this.consumeNextWordIfMatch('}') || !this.expectToken(',')) { break; } } else { break; } } this.excutionFlag &= ~constants_1.IS_PARSING_OBJECT_KEY; if (expression.unexpectedNodes.length) { return finishParse(); } else { return this.tryParse(finishParse(false)); } } parseArrExp() { let expression = new ArrayLiteralExpression_1.ArrayLiteralExpression(this.currentToken); let finishParse = this.startParse(expression); let value; while (!this.consumeNextWordIfMatch(']') && (value = this.expectExpression())) { expression.items.push(value); if (this.consumeNextWordIfMatch(']') || !this.expectToken(',')) { break; } } if (expression.unexpectedNodes.length) { return finishParse(); } else { return this.tryParse(finishParse(false)); } } parseTryCatchStatment() { let statement = new TryCatchStatement_1.TryCathchStatement(this.currentToken); let finishParse = this.startParse(statement, constants_1.IS_PARSING_TRY); if (!this.expectToken('{')) { return finishParse(); } statement.body = this.parseBlock(); if (this.consumeNextWordIfMatch('catch')) { let catchHandler = this.parseCatchHandler(); if (catchHandler.unexpectedNodes.length) { statement.unexpectedNodes.push(catchHandler); return finishParse(); } statement.catchHandler = catchHandler; } if (this.consumeNextWordIfMatch('finally') && this.expectToken('{')) { statement.finalizer = this.parseBlock(); } if (!statement.catchHandler && !statement.finalizer) statement.unexpectedNodes.push(new UnExpectStatement_1.UnExpectStatement(this.currentToken.value, 'expect finally', this.currentToken.loc)); return finishParse(); } parseCatchHandler() { let statement = new FunctionDeclarationStatement_1.FunctionDeclarationStatement(this.currentToken); let finishParse = this.startParse(statement, constants_1.IS_PARSING_FUNDECLARATION); this.isParsingFunDeclaration = true; statement.identifier = new IdentifierLiteralExpression_1.IdentifierLiteralExpression(this.currentToken); if (this.expectToken('(')) { let params; if (!this.consumeNextWordIfMatch(')')) { while (params = this.getExpectedNode(constants_1.NodeCode.IdentifierLiteralExpression)) { statement.params.push(params); if (this.consumeNextWordIfMatch(')')) { break; } else if (this.expectToken(',')) { continue; } } } } if (statement.unexpectedNodes.length) return finishParse(); if (this.expectToken('{')) { statement.body = this.parseBlock(); statement.body.functionBlock = true; } return finishParse(); } parseDoWihleStatement() { let statement = new DoWhileStatement_1.DoWhileStatement(this.currentToken); let finishParse = this.startParse(statement, constants_1.IS_PARSING_DOWHILE); this.isParsingLoop = true; if (!this.expectToken('{')) return finishParse(); statement.body = this.parseBlock(); this.expectToken('while') && this.expectToken('(') && (statement.test = this.expectExpression()) && this.expectToken(')'); return finishParse(); } parseWhileStatment() { let statement = new WhileStatement_1.WhileStatement(this.currentToken); let finishParse = this.startParse(statement, constants_1.IS_PARSING_SWITCH); this.isParsingLoop = true; if (this.expectToken('(') && (statement.test = this.expectExpression()) && this.expectToken(')') && this.expectToken('{')) { statement.consequence = this.parseBlock(); } return finishParse(); } parseExpressions(endFlag) { let exprs = []; while (!this.isReadAll()) { let expr = this.expectExpression(); exprs.push(expr); if (this.isNextWordIs(endFlag) || !this.consumeNextWordIfMatch(',')) return exprs; } } parseForStatement() { let statement = new ForStatement_1.ForStatement(this.currentToken); let finishParse = this.startParse(statement); this.isParsingLoop = true; if (!this.expectToken('(')) return finishParse(); if (!this.consumeNextWordIfMatch(';')) { let init = this.getExpectedNode(constants_1.NodeCode.Expression | constants_1.NodeCode.VariableDeclarationStatement); if (!init) { return finishParse(); } else if (init.code === constants_1.NodeCode.IdentifierLiteralExpression || init.code === constants_1.NodeCode.VariableDeclarationStatement && init.declarations.length === 1 && init.declarations[0].code !== constants_1.NodeCode.AssignmentExpression) { if ((statement.forOf = this.consumeNextWordIfMatch('of')) || (statement.forIn = this.consumeNextWordIfMatch('in'))) { statement.left = init; if ((statement.forIn ? statement.right = this.expectExpression() : statement.right = this.expectExpression()) && this.expectToken(')') && this.expectToken('{')) { statement.body = this.parseBlock(); } return finishParse(); } } statement.init = init; if (!this.expectToken(';')) return finishParse(); } if (!this.consumeNextWordIfMatch(';') && !(statement.test = this.expectExpression()) && !this.expectToken(';')) { return finishParse(); } if (this.consumeNextWordIfMatch(')') || this.expectToken(';') && (statement.update = this.parseExpressions(')')) && this.expectToken(')')) { if (this.expectToken('{')) statement.body = this.parseBlock(); } return finishParse(); } parseRegStatement() { let expression = new RegExpressioin_1.RegExpressioin(this.currentToken); let finishParse = this.startParse(expression); let pos = this.pos - 1, escape = false; while (!this.isReadAll() && !(!escape && this.curChar === '/')) { this.advance(); if (this.curChar === '\n') { expression.unexpectedNodes.push(new UnExpectStatement_1.UnExpectStatement(this.input.slice(pos, this.pos), '未完成的正则表达式', { start: pos, end: this.pos })); return finishParse(); } if (escape) { escape = false; } else if (this.curChar === '\\') { escape = true; } } this.advance(); expression.pattern = this.input.slice(pos, this.pos); if (this.curChar === 'i' || this.curChar === 'g') { let reset = this.markPos(); this.next(); if (!/i|g|ig|gi/.test(this.currentToken.value)) { reset(); } else { expression.flag = this.currentToken.value; } } return finishParse(); } parseIfStatement() { let statement = new IfStatement_1.IfStatement(this.currentToken); let finishParse = this.startParse(statement); let expectLineEnd = false; let parseConditionBlock = () => { if (this.consumeNextWordIfMatch('{')) { return this.parseBlock(); } else { expectLineEnd = true; let currentLine = this.currentLine; let reset = this.markPos(); let block = new Block_1.Block(); if (this.consumeNextWordIfMatch('else')) { if (currentLine === this.currentLine) { statement.unexpectedNodes.push(new UnExpectStatement_1.UnExpectStatement('else', 'statement expect', this.currentToken.loc)); return null; } reset(); return block; } else if (this.consumeNextWordIfMatch(';')) { reset(); return block; } let nextStatement = this.safelyParseNode(); if (nextStatement) { block.body.push(nextStatement); } return block; } }; if (!this.expectToken('(') || !(statement.test = this.expectExpression()) || !this.expectToken(')')) { return finishParse(); } statement.consequent = parseConditionBlock(); if (statement.unexpectedNodes.length || expectLineEnd && !this.isLineEnd()) return finishParse(); expectLineEnd = false; if (this.consumeNextWordIfMatch('else')) { if (this.consumeNextWordIfMatch('if')) { statement.alternate = this.parseIfStatement(); } else if (this.expectToken('{')) { statement.alternate = this.parseBlock(); } } return finishParse(); } parseBreakStatement() { let statement = new BreakStatement_1.BreakStatement(this.currentToken); let finishParse = this.startParse(statement); if (!this.isLineEnd() && !this.isNextWordIs(';')) { statement.argument = this.expectExpression(); } if (!this.isParsingLoop) return new UnExpectStatement_1.UnExpectStatement(finishParse(), '"break" 语句只能在循环语句里使用'); return finishParse(); } parseReturnStatement() { let statement = new ReturnStatement_1.ReturnStatement(this.currentToken); let finishParse = this.startParse(statement); if (!this.isLineEnd() && !this.isNextWordIs(';')) { statement.argument = this.expectExpression(); } if (!this.isParsingFunDeclaration) return new UnExpectStatement_1.UnExpectStatement(finishParse(), 'A "return" statement can only be used within a function body'); return finishParse(); } parseContinueStatement() { let statement = new ContinueStatement_1.ContinueStatement(this.currentToken); let finishParse = this.startParse(statement); if (!this.isLineEnd() && !this.isNextWordIs(';')) { statement.argument = this.expectExpression(); } if (!this.isParsingLoop) return new UnExpectStatement_1.UnExpectStatement(finishParse(), '"continue" 语句只能用在循环语句内'); return finishParse(); } parseSwitchStatement() { let statement = new SwitchStatement_1.SwitchStatement(this.currentToken); let finishParse = this.startParse(statement); let caseStatement; if (!(this.expectToken('(') && (statement.discriminant = this.expectExpression()) && this.expectToken([')', '{']))) return finishParse(); while (!this.consumeNextWordIfMatch('}')) { if (caseStatement = this.parseCaseStatement()) { if (caseStatement.unexpectedNodes.length) { break; } statement.cases.push(caseStatement); } else { break; } } return finishParse(); } parseCaseStatement() { if (!this.consumeNextWordIfMatch('case') && !this.consumeNextWordIfMatch('default')) { this.next(); this.parsingNode.unexpectedNodes.push(new UnExpectStatement_1.UnExpectStatement(this.currentToken.value, 'expect "case" or "default"', this.currentToken.loc)); return null; } let statement = new SwitchCaseStatement_1.SwitchCaseStatement(this.currentToken); let finishParse = this.startParse(statement, constants_1.IS_PARSING_CASE); this.isParsingLoop = true; statement.isDefault = this.currentToken.value === 'default'; if (!statement.isDefault && !(statement.test = this.expectExpression())) { return finishParse(); } if (this.expectToken(':')) { if (this.consumeNextWordIfMatch('{')) { statement.consequent = this.parseBlock(); } else { let reset = this.markPos(); statement.consequent = this.parseBlock((statement) => { if (!statement && ['case', 'default', '}'].includes(this.currentToken.value)) { reset(); return true; } else { reset = this.markPos(); } }); } } return finishParse(); } parseThrowStatement() { let statement = new ThrowStatement_1.ThrowStatement(this.currentToken); let finishParse = this.startParse(statement); statement.argument = this.expectExpression(); return finishParse(); } parseWithStatement() { let statement = new WithStatement_1.WithStatement(this.currentToken); let finishParse = this.startParse(statement); if (this.expectToken('(') && (statement.context = this.expectExpression()) && this.expectToken(')') && this.expectToken('{')) { statement.body = this.parseBlock(); } return finishParse(); } parseDeleteStatement() { let statement = new DeleteStatement_1.DeleteStatement(this.currentToken); let finishParse = this.startParse(statement); statement.argument = this.getExpectedNode(constants_1.NodeCode.MemberExpression); return finishParse(); } parseNewStatement() { let expression = new NewExpressioin_1.NewExpressioin(this.currentToken); let finishParse = this.startParse(expression); expression.callee = this.getExpectedNode(constants_1.NodeCode.CallExpression | constants_1.NodeCode.IdentifierLiteralExpression); return finishParse(); } parseClassDeclarationStatement() { let statement = new ClassDeclarationStatement_1.ClassDeclarationStatement(this.currentToken); let finishParse = this.startParse(statement, constants_1.IS_PARSING_CLASSDECLARATION); if (!(statement.className = this.getExpectedNode(constants_1.NodeCode.IdentifierLiteralExpression)) || this.consumeNextWordIfMatch('extends') && !(statement.super = this.getExpectedNode(constants_1.NodeCode.IdentifierLiteralExpression)) || !this.expectToken('{')) { return finishParse(); } while (!this.consumeNextWordIfMatch('}') && !this.isReadAll()) { let method = this.parseClassMehod(); if (!method) return finishParse(); statement.methods.push(method); } return finishParse(); } parseClassMehod() { let reset = this.markPos(); this.next(); if (this.currentToken.type !== 'IdentifierLiteral') { this.parsingNode.unexpectedNodes.push(new UnExpectStatement_1.UnExpectStatement(this.currentToken.value, 'expect identifier', this.currentToken.loc)); return null; } reset(); let statement = this.parseFunDeclarationStatement(); if (statement.unexpectedNodes.length) { this.parsingNode.unexpectedNodes.push(statement); return null; } return statement; } parseExportStatement() { let statement = new ExportDeclarationStatement_1.ExportDeclarationStatement(this.currentToken); let finishParse = this.startParse(statement); if (this.consumeNextWordIfMatch('{')) { let identifier; while (identifier = this.getExpectedNode(constants_1.NodeCode.IdentifierLiteralExpression)) { let exportSpecifier = { exported: identifier, local: null }; if (this.consumeNextWordIfMatch('as')) { if (exportSpecifier.local = this.getExpectedNode(constants_1.NodeCode.IdentifierLiteralExpression)) { statement.specifiers.push(exportSpecifier); } else { return finishParse(); } } statement.specifiers.push(exportSpecifier); if (this.consumeNextWordIfMatch('}')) break; if (!this.expectToken(',')) return finishParse(); } } else { let identifier = this.getExpectedNode(constants_1.NodeCode.Declaration | constants_1.NodeCode.IdentifierLiteralExpression); if (identifier) { statement.exported = identifier; if ((identifier.code & constants_1.NodeCode.Declaration) === identifier.code) { return finishParse(); } if (this.consumeNextWordIfMatch('as') && !(statement.local = this.getExpectedNode(constants_1.NodeCode.IdentifierLiteralExpression))) { return finishParse(); } } else { return finishParse(); } } this.expectToken('from') && (statement.from = this.getExpectedNode(constants_1.NodeCode.StringLiteralExpression)); return finishParse(); } parseTypeOfExpression() { let expression = new TypeOfExpression_1.TypeOfExpression(this.currentToken); let finishParse = this.startParse(expression, constants_1.IS_PARSING_TYPEOF); expression.argument = this.expectExpression(); if (expression.unexpectedNodes.length) { return finishParse(); } else { return this.tryParse(finishParse(false)); } } parseVoidExpression() { let expression = new VoidExpression_1.VoidExpression(this.currentToken); let finishParse = this.startParse(expression, constants_1.IS_PARSING_VOID); expression.argument = this.expectExpression(); if (expression.unexpectedNodes.length) { return finishParse(); } else { return this.tryParse(finishParse(false)); } } parseTemplateLiteralExp() { let node = new TemplateLiteralExppression_1.TemplateLiteralExppression(this.currentToken); let finishParse = this.startParse(node); let consumeString = () => { while (this.curCharCode !== 96 && !this.isReadAll() && this.input.slice(this.pos, this.pos + 2) !== '${') { this.advance(); } }; while (this.curCharCode !== 96) { let start = this.pos, str; consumeString(); str = new StringLiteralExpression_1.StringLiteralExpression({ value: this.input.slice(start, this.pos), loc: { start: start, end: this.pos } }); if (this.isReadAll()) { str && node.unexpectedNodes.push(new UnExpectStatement_1.UnExpectStatement(str, '未闭合的模板字符串', { start: node.loc.start, end: this.pos })); return finishParse(); } node.content.push(str); if (t