UNPKG

sucrase

Version:

Super-fast alternative to Babel for when you can target modern JS runtimes

149 lines (148 loc) 4.94 kB
import { IdentifierRole } from "../sucrase-babylon/tokenizer"; export default class TokenProcessor { constructor(code, tokens) { this.code = code; this.tokens = tokens; this.resultCode = ""; this.tokenIndex = 0; } /** * Make a new TokenProcessor for things like lookahead. */ snapshot() { return { resultCode: this.resultCode, tokenIndex: this.tokenIndex }; } restoreToSnapshot(snapshot) { this.resultCode = snapshot.resultCode; this.tokenIndex = snapshot.tokenIndex; } getResultCodeIndex() { return this.resultCode.length; } getCodeInsertedSinceIndex(initialResultCodeIndex) { return this.resultCode.slice(initialResultCodeIndex); } reset() { this.resultCode = ""; this.tokenIndex = 0; } matchesAtIndex(index, tagLabels) { if (index < 0) { return false; } for (let i = 0; i < tagLabels.length; i++) { if (index + i >= this.tokens.length) { return false; } if (this.tokens[index + i].type.label !== tagLabels[i]) { return false; } } return true; } matchesNameAtIndex(index, name) { return this.matchesAtIndex(index, ["name"]) && this.tokens[index].value === name; } matchesNameAtRelativeIndex(relativeIndex, name) { const index = this.currentIndex() + relativeIndex; return this.matchesAtIndex(index, ["name"]) && this.tokens[index].value === name; } matchesAtRelativeIndex(relativeIndex, tagLabels) { return this.matchesAtIndex(this.currentIndex() + relativeIndex, tagLabels); } matches(tagLabels) { return this.matchesAtIndex(this.tokenIndex, tagLabels); } matchesName(name) { return this.matchesNameAtIndex(this.tokenIndex, name); } /** * Check if this is a "real" instance of the keyword rather than an object key or property access. */ matchesKeyword(name) { const token = this.currentToken(); if (this.matchesAtRelativeIndex(-1, ["."])) { return false; } if (token.identifierRole === IdentifierRole.ObjectKey) { return false; } return token.type.label === name || (token.type.label === "name" && token.value === name); } matchesContextIdAndLabel(label, contextId) { return this.matches([label]) && this.currentToken().contextId === contextId; } previousWhitespace() { return this.code.slice(this.tokenIndex > 0 ? this.tokens[this.tokenIndex - 1].end : 0, this.tokens[this.tokenIndex].start); } replaceToken(newCode) { this.resultCode += this.previousWhitespace(); this.resultCode += newCode; this.tokenIndex++; } replaceTokenTrimmingLeftWhitespace(newCode) { this.resultCode += this.previousWhitespace().replace(/[\t ]/g, ""); this.resultCode += newCode; this.tokenIndex++; } removeInitialToken() { this.replaceToken(""); } removeToken() { this.replaceTokenTrimmingLeftWhitespace(""); } copyExpectedToken(label) { if (this.tokens[this.tokenIndex].type.label !== label) { throw new Error(`Expected token ${label}`); } this.copyToken(); } copyToken() { this.resultCode += this.code.slice(this.tokenIndex > 0 ? this.tokens[this.tokenIndex - 1].end : 0, this.tokens[this.tokenIndex].end); this.tokenIndex++; } appendCode(code) { this.resultCode += code; } currentToken() { return this.tokens[this.tokenIndex]; } currentTokenCode() { const token = this.currentToken(); return this.code.slice(token.start, token.end); } tokenAtRelativeIndex(relativeIndex) { return this.tokens[this.tokenIndex + relativeIndex]; } currentIndex() { return this.tokenIndex; } /** * Move to the next token. Only suitable in preprocessing steps. When * generating new code, you should use copyToken or removeToken. */ nextToken() { if (this.tokenIndex === this.tokens.length) { throw new Error("Unexpectedly reached end of input."); } this.tokenIndex++; } previousToken() { this.tokenIndex--; } expectToken(label) { if (this.tokens[this.tokenIndex].type.label !== label) { throw new Error(`Expected token ${label}`); } } finish() { if (this.tokenIndex !== this.tokens.length) { throw new Error("Tried to finish processing tokens before reaching the end."); } this.resultCode += this.code.slice(this.tokens[this.tokens.length - 1].end); return this.resultCode; } isAtEnd() { return this.tokenIndex === this.tokens.length; } }