summaryrefslogtreecommitdiff
path: root/node_modules/sucrase/dist/esm/parser/traverser/util.js
diff options
context:
space:
mode:
authorPhilipp Tanlak <philipp.tanlak@gmail.com>2025-11-24 20:54:57 +0100
committerPhilipp Tanlak <philipp.tanlak@gmail.com>2025-11-24 20:57:48 +0100
commitb1e2c8fd5cb5dfa46bc440a12eafaf56cd844b1c (patch)
tree49d360fd6cbc6a2754efe93524ac47ff0fbe0f7d /node_modules/sucrase/dist/esm/parser/traverser/util.js
Docs
Diffstat (limited to 'node_modules/sucrase/dist/esm/parser/traverser/util.js')
-rw-r--r--node_modules/sucrase/dist/esm/parser/traverser/util.js104
1 files changed, 104 insertions, 0 deletions
diff --git a/node_modules/sucrase/dist/esm/parser/traverser/util.js b/node_modules/sucrase/dist/esm/parser/traverser/util.js
new file mode 100644
index 0000000..6a2b2d9
--- /dev/null
+++ b/node_modules/sucrase/dist/esm/parser/traverser/util.js
@@ -0,0 +1,104 @@
+import {eat, finishToken, lookaheadTypeAndKeyword, match, nextTokenStart} from "../tokenizer/index";
+
+import {formatTokenType, TokenType as tt} from "../tokenizer/types";
+import {charCodes} from "../util/charcodes";
+import {input, state} from "./base";
+
+// ## Parser utilities
+
+// Tests whether parsed token is a contextual keyword.
+export function isContextual(contextualKeyword) {
+ return state.contextualKeyword === contextualKeyword;
+}
+
+export function isLookaheadContextual(contextualKeyword) {
+ const l = lookaheadTypeAndKeyword();
+ return l.type === tt.name && l.contextualKeyword === contextualKeyword;
+}
+
+// Consumes contextual keyword if possible.
+export function eatContextual(contextualKeyword) {
+ return state.contextualKeyword === contextualKeyword && eat(tt.name);
+}
+
+// Asserts that following token is given contextual keyword.
+export function expectContextual(contextualKeyword) {
+ if (!eatContextual(contextualKeyword)) {
+ unexpected();
+ }
+}
+
+// Test whether a semicolon can be inserted at the current position.
+export function canInsertSemicolon() {
+ return match(tt.eof) || match(tt.braceR) || hasPrecedingLineBreak();
+}
+
+export function hasPrecedingLineBreak() {
+ const prevToken = state.tokens[state.tokens.length - 1];
+ const lastTokEnd = prevToken ? prevToken.end : 0;
+ for (let i = lastTokEnd; i < state.start; i++) {
+ const code = input.charCodeAt(i);
+ if (
+ code === charCodes.lineFeed ||
+ code === charCodes.carriageReturn ||
+ code === 0x2028 ||
+ code === 0x2029
+ ) {
+ return true;
+ }
+ }
+ return false;
+}
+
+export function hasFollowingLineBreak() {
+ const nextStart = nextTokenStart();
+ for (let i = state.end; i < nextStart; i++) {
+ const code = input.charCodeAt(i);
+ if (
+ code === charCodes.lineFeed ||
+ code === charCodes.carriageReturn ||
+ code === 0x2028 ||
+ code === 0x2029
+ ) {
+ return true;
+ }
+ }
+ return false;
+}
+
+export function isLineTerminator() {
+ return eat(tt.semi) || canInsertSemicolon();
+}
+
+// Consume a semicolon, or, failing that, see if we are allowed to
+// pretend that there is a semicolon at this position.
+export function semicolon() {
+ if (!isLineTerminator()) {
+ unexpected('Unexpected token, expected ";"');
+ }
+}
+
+// Expect a token of a given type. If found, consume it, otherwise,
+// raise an unexpected token error at given pos.
+export function expect(type) {
+ const matched = eat(type);
+ if (!matched) {
+ unexpected(`Unexpected token, expected "${formatTokenType(type)}"`);
+ }
+}
+
+/**
+ * Transition the parser to an error state. All code needs to be written to naturally unwind in this
+ * state, which allows us to backtrack without exceptions and without error plumbing everywhere.
+ */
+export function unexpected(message = "Unexpected token", pos = state.start) {
+ if (state.error) {
+ return;
+ }
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const err = new SyntaxError(message);
+ err.pos = pos;
+ state.error = err;
+ state.pos = input.length;
+ finishToken(tt.eof);
+}