'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)); }