rehype-citation
Version:
rehype plugin to add citation and bibliography from bibtex files
101 lines (100 loc) • 3.59 kB
JavaScript
// @ts-nocheck
class TokenStack {
constructor(array) {
this.stack = array;
this.index = 0;
this.current = this.stack[this.index];
}
static getPatternText(pattern) {
return `"${pattern instanceof RegExp ? pattern.source : pattern}"`;
}
static getMatchCallback(pattern) {
if (Array.isArray(pattern)) {
const matches = pattern.map(TokenStack.getMatchCallback);
return (token) => matches.some((matchCallback) => matchCallback(token));
}
else if (pattern instanceof Function) {
return pattern;
}
else if (pattern instanceof RegExp) {
return (token) => pattern.test(token);
}
else {
return (token) => pattern === token;
}
}
tokensLeft() {
return this.stack.length - this.index;
}
matches(pattern) {
return TokenStack.getMatchCallback(pattern)(this.current, this.index, this.stack);
}
matchesSequence(sequence) {
const part = this.stack.slice(this.index, this.index + sequence.length).join('');
return typeof sequence === 'string'
? part === sequence
: sequence.every((pattern, index) => TokenStack.getMatchCallback(pattern)(part[index]));
}
consumeToken(pattern = /^[\s\S]$/, { inverse = false, spaced = true } = {}) {
if (spaced) {
this.consumeWhitespace();
}
const token = this.current;
const match = TokenStack.getMatchCallback(pattern)(token, this.index, this.stack);
if (match) {
this.current = this.stack[++this.index];
}
else {
throw new SyntaxError(`Unexpected token at index ${this.index}: Expected ${TokenStack.getPatternText(pattern)}, got "${token}"`);
}
if (spaced) {
this.consumeWhitespace();
}
return token;
}
consumeWhitespace(pattern = /^\s$/, { optional = true } = {}) {
return this.consume(pattern, {
min: +!optional,
});
}
consumeN(length) {
if (this.tokensLeft() < length) {
throw new SyntaxError('Not enough tokens left');
}
const start = this.index;
while (length--) {
this.current = this.stack[++this.index];
}
return this.stack.slice(start, this.index).join('');
}
consumeSequence(sequence) {
if (this.matchesSequence(sequence)) {
return this.consumeN(sequence.length);
}
else {
throw new SyntaxError(`Expected "${sequence}", got "${this.consumeN(sequence.length)}"`);
}
}
consume(pattern = /^[\s\S]$/, { min = 0, max = Infinity, inverse = false, tokenMap, tokenFilter } = {}) {
const start = this.index;
const match = TokenStack.getMatchCallback(pattern);
while (match(this.current, this.index, this.stack) !== inverse) {
this.current = this.stack[++this.index];
}
let consumed = this.stack.slice(start, this.index);
if (consumed.length < min) {
throw new SyntaxError(`Not enough ${TokenStack.getPatternText(pattern)}`);
}
else if (consumed.length > max) {
throw new SyntaxError(`Too many ${TokenStack.getPatternText(pattern)}`);
}
if (tokenMap) {
consumed = consumed.map(tokenMap);
}
if (tokenFilter) {
consumed = consumed.filter(tokenFilter);
}
return consumed.join('');
}
}
export default TokenStack;