aubade
Version:
markdown, orchestrated.
119 lines (118 loc) • 3.78 kB
JavaScript
import { annotate, compose } from './engine.js';
export function contextualize(source) {
let pointer = 0;
return {
/** current index in the source */
get index() {
return pointer;
},
set index(value) {
pointer = value;
},
/** consume the input if it matches */
eat(text) {
if (text.length === 1)
return source[pointer] === text && !!++pointer;
if (text !== source.slice(pointer, pointer + text.length))
return false;
pointer += text.length;
return true;
},
/** read a fixed number of characters */
read(length) {
if (length === 1)
return source[pointer++];
const text = source.slice(pointer, pointer + length);
pointer += text.length;
return text;
},
/** eat until `pattern` is found */
locate(pattern) {
const start = pointer;
const match = pattern.exec(source.slice(pointer));
if (match) {
pointer = start + match.index;
return source.slice(start, pointer);
}
return '';
},
/** see the `pattern` ahead */
peek(pattern) {
if (typeof pattern === 'string') {
if (pattern.length === 1)
return source[pointer] === pattern ? pattern : '';
return source.slice(pointer, pointer + pattern.length) === pattern ? pattern : '';
}
const match = pattern.exec(source.slice(pointer));
return match ? source.slice(pointer, pointer + match.index) : '';
},
/** see the `n`-th character before/after */
see(n) {
if (n === 0)
return source[pointer] || ' ';
const index = pointer + n;
// treat out-of-bounds as whitespace
if (n < 0 && index < 0)
return ' ';
if (index >= source.length)
return ' ';
return source[index];
},
trim() {
while (pointer < source.length && /\s/.test(source[pointer])) {
pointer++;
}
},
};
}
function extract(token) {
if ('children' in token)
return token.children.map(extract).join('');
return 'text' in token ? token.text : '';
}
export const util = {
commit(array, item) {
if (item !== util.last(array))
array.push(item);
return item;
},
last(array) {
return array[array.length - 1];
},
is: {
alphanumeric(char) {
return /\p{L}|\p{N}|_/u.test(char);
},
punctuation(char) {
return /\p{P}|\p{S}/u.test(char);
},
whitespace(char) {
return /\p{Zs}/u.test(char) || /\s/.test(char);
},
'left-flanking'(before, after) {
return (!util.is.whitespace(after) &&
(!util.is.punctuation(after) || util.is.whitespace(before) || util.is.punctuation(before)));
},
'right-flanking'(before, after) {
return (!util.is.whitespace(before) &&
(!util.is.punctuation(before) || util.is.whitespace(after) || util.is.punctuation(after)));
},
},
};
export function match(ctx) {
const start = ctx.cursor.index;
for (const rule of ctx.rules) {
const token = rule({
annotate,
compose,
extract,
cursor: ctx.cursor,
stack: ctx.stack,
util,
});
if (token != null)
return token;
ctx.cursor.index = start;
}
return null;
}