mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 12:55:55 +00:00
145 lines
No EOL
2.3 KiB
TypeScript
145 lines
No EOL
2.3 KiB
TypeScript
'use strict';
|
|
|
|
export function parse(path: string) {
|
|
return parseAST({
|
|
path,
|
|
i: 0
|
|
});
|
|
}
|
|
|
|
type ParseContext = {
|
|
path: string;
|
|
i: number;
|
|
}
|
|
|
|
export type PathNode = {
|
|
title: string;
|
|
key: string;
|
|
page: boolean;
|
|
tab: boolean;
|
|
}
|
|
|
|
function parseAST(ctx: ParseContext) {
|
|
const path = ctx.path,
|
|
length = path.length,
|
|
out = [];
|
|
|
|
let token: PathNode | null = null,
|
|
raw: string | null = null;
|
|
|
|
let old_tab = false,
|
|
old_page = false;
|
|
|
|
while ( ctx.i < length ) {
|
|
const start = ctx.i,
|
|
char = path[start],
|
|
next = path[start + 1];
|
|
|
|
if ( ! token )
|
|
token = {} as PathNode;
|
|
|
|
if ( ! raw )
|
|
raw = '';
|
|
|
|
// JSON
|
|
if ( char === '@' && next === '{') {
|
|
ctx.i++;
|
|
const tag = parseJSON(ctx);
|
|
if ( tag )
|
|
Object.assign(token, tag);
|
|
|
|
continue;
|
|
}
|
|
|
|
// Segment End?
|
|
const tab = char === `~` && next === '>',
|
|
page = char === '>' && next === '>',
|
|
segment = ! page && char === '>';
|
|
|
|
if ( ! segment && ! page && ! tab ) {
|
|
raw += char;
|
|
ctx.i++;
|
|
continue;
|
|
}
|
|
|
|
// We're at the end of a segment, so push
|
|
// the token out.
|
|
if ( tab || page )
|
|
ctx.i++;
|
|
|
|
token.title = raw.trim();
|
|
token.key = token.title.toSnakeCase();
|
|
|
|
token.page = old_page;
|
|
token.tab = old_tab;
|
|
|
|
old_page = page;
|
|
old_tab = tab;
|
|
|
|
out.push(token);
|
|
token = raw = null;
|
|
ctx.i++;
|
|
}
|
|
|
|
if ( token && raw ) {
|
|
token.title = raw.trim();
|
|
token.key = token.title.toSnakeCase();
|
|
token.page = old_page;
|
|
token.tab = old_tab;
|
|
out.push(token);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
function parseJSON(ctx: ParseContext) {
|
|
const path = ctx.path,
|
|
length = path.length,
|
|
|
|
start = ctx.i;
|
|
|
|
ctx.i++;
|
|
|
|
const stack = ['{'];
|
|
let string: string | boolean = false;
|
|
|
|
while ( ctx.i < length && stack.length ) {
|
|
const start = ctx.i,
|
|
char = path[start];
|
|
|
|
if ( string ) {
|
|
if ( char === '\\' ) {
|
|
ctx.i++;
|
|
continue;
|
|
}
|
|
|
|
if ( (char === '"' || char === "'") && char === string ) {
|
|
stack.pop();
|
|
string = false;
|
|
}
|
|
|
|
} else {
|
|
if ( char === '"' || char === "'" ) {
|
|
string = char;
|
|
stack.push(char);
|
|
}
|
|
|
|
if ( char === '{' || char === '[' )
|
|
stack.push(char);
|
|
|
|
if ( char === ']' ) {
|
|
if ( stack.pop() !== '[' )
|
|
throw new SyntaxError('Invalid JSON');
|
|
}
|
|
|
|
if ( char === '}' ) {
|
|
if ( stack.pop() !== '{' )
|
|
throw new SyntaxError('Invalid JSON');
|
|
}
|
|
}
|
|
|
|
ctx.i++;
|
|
}
|
|
|
|
return JSON.parse(path.slice(start, ctx.i));
|
|
} |