sucrase
Version:
Super-fast alternative to Babel for when you can target modern JS runtimes
149 lines (148 loc) • 4.94 kB
JavaScript
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;
}
}