diff --git a/docs/experiments/parsers/v3/core.js b/docs/experiments/parsers/v3/core.js new file mode 100644 index 0000000..cd15d52 --- /dev/null +++ b/docs/experiments/parsers/v3/core.js @@ -0,0 +1,121 @@ +import { createLineData, useDocument } from '@terrace/core' +import { createStringReader } from '@terrace/core/readers/js-string' + +export const SYMBOLS = { + TAIL: Symbol('TAIL'), + UNMATCHED: Symbol('UNMATCHED') +} + +export const BASE_MACROS = { + // string ({ line }) { + // return tail(line).toString() + // }, + // number ({ line }) { + // const num = +tail(line) + // if (isNaN(num) || line === '') return + // return num + // }, + // primitive (args) { + // const num = macros.number(args) + // return num !== undefined ? num : macros.string(args) + // }, + // any (args) { + // const macro = args.macros[args.head] + // if (macro) return macro(args) + // const numResult = args.macros.number(args) + // if (numResult !== undefined) return numResult + // args.tail = args.line + // return args.macros.string(args) + // }, + // scope ({ addScope, head, tail, line }, definition) { + // return addScope({ definition, head, tail, line }) + // } +} + +export function getTail(key, macro) { + return args => { + const scope = args.scope + const result = macro(args) + if (result === undefined) return + if (!scope[key]) scope[key] = [] + scope[key].push(result) + } +} + +export function getText(key, macro) { + return (args) => { + const scope = args.scope + const result = macro({...args, tail: args.line }) + if (result === undefined) return + if (!scope[key]) scope[key] = [] + scope[key].push(result) + } +} + +export function getUnmatched(macro) { + return (args) => { + const key = args.head + const scope = args.scope + const result = macro(args) + if (result === undefined) return + if (!scope[key]) scope[key] = [] + scope[key].push(result) + } +} + +export function isMacro (macro) { + return (args) => args.macros[macro](args) +} + +export function isCollection (macro) { + return (args) => ({ [args.head]: macro(args) }) +} + +export function isScope (definition) { + return (args) => args.macros.scope(args, definition) +} + +export async function parse(lines, schema) { + const doc = useDocument(createStringReader(lines)) + + const macros = schema.macros + const scopes = [[]] + + function addScope() { + const scope = [] + scopes[scopes.length] = scope + return scope + } + + let ended = false + let lastLevel = 0 + while (!ended) { + ended = await doc.next() + if (ended) break; + const level = doc.level() + scopes.length = level + 1 + + if (lastLevel == level) { + console.log('||DECREASE: ', doc.line()) + } + + lastLevel = level + + let entry = [doc.line()] + + Object.keys(schema.root).forEach(key => { + if (entry[0] === key || entry[0].startsWith(`${key} `)) { + entry = schema.root[key]({ entry, macros }) + } + }) + + const scope = scopes[level] || [] + if (!scopes[level]) { + scopes[level] = scope + scopes[level - 1].at(-1)[1] = scopes[level] + } + scope.push(entry) + } + + return scopes[0] +} diff --git a/docs/experiments/parsers/v3/example.js b/docs/experiments/parsers/v3/example.js new file mode 100644 index 0000000..e49ca24 --- /dev/null +++ b/docs/experiments/parsers/v3/example.js @@ -0,0 +1,139 @@ +import { SYMBOLS, parse, isScope, isMacro, getTail, getText, getUnmatched, isCollection } from './core.js' + +const schemaTCE = ` +macros + primitive match number string + section + pos number tail + content string unmatched + position number + options + parameter1 number + parameter2 string + literal unmatched string + unmatched primitive + +root + title string + options options collection + subsection section + list + - string array + collection + section collection + collection2 + unmatched collection +` + +function head(line) { + return line.split(' ')[0] +} + +function tail(line) { + return line.split(' ').slice(1).join(' ') +} + +function chain(...macroNames) { + return (args) => { + for (const macroName of macroNames) { + if (args.macros[macroName]) args.entry = args.macros[macroName](args) + } + return args.entry + } +} + +const schema = { + macros: { + tail({ entry }) { + return [head(entry[0]), tail(entry[0])] + }, + string({ entry }) { + return [entry[0], entry[1].toString()] + }, + number({ entry }) { + const num = +entry[1] + if (isNaN(num) || entry[1] === '') return entry + return [entry[0], num] + }, + primitive(args) { + const num = macros.number(args) + return num !== undefined ? num : macros.string(args) + }, + scope({ addScope, head, tail, entry }, definition) { + return addScope({ definition, head, tail, entry }) + }, + section: isScope({ + [SYMBOLS.TAIL]: getTail('pos', isMacro('number')), + [SYMBOLS.UNMATCHED]: getText('content', isMacro('string')), + position: isMacro('number') + }), + options: isScope({ + parameter1: isMacro('number'), + parameter2: isMacro('string'), + unmatched: isMacro('string'), + [SYMBOLS.UNMATCHED]: getUnmatched(isMacro('any')), + }), + any(args) { + const macro = args.macros[args.head] + if (macro) return macro(args) + const numResult = args.macros.number(args) + if (numResult !== undefined) return numResult + args.tail = args.entry + return args.macros.string(args) + }, + }, + root: { + // Move tail into value position and cast it to string. + title: chain('tail', 'string'), + // options: isMacro('options'), + // subsection: isMacro('section'), + // list: isScope({ + // '-': isMacro('string') + // }), + // collection: isScope({ + // section: isCollection(isMacro('section')) + // }), + // collection2: isScope({ + // [SYMBOLS.UNMATCHED]: getUnmatched(isMacro('any')) + // }) + } +} + + +const lines = [ + `title Example`, + `options`, + ` parameter1 30`, + ` parameter2 Enim eu id anim minim reprehenderit nostrud eu amet deserunt ea ut do cupidatat ea.`, + `options`, + ` parameter1 0`, + ` parameter2 Esse incididunt et est adipisicing eiusmod aliqua enim ea aliqua id enim.`, + `subsection`, + ` position 1`, + ` Ea dolore in aliquip fugiat anim adipisicing amet aute tempor et deserunt est duis sint.`, + `subsection 2`, + ` position 2`, + ` Aute deserunt incididunt ad in sint adipisicing est officia velit pariatur ipsum deserunt quis nulla.`, + ` Ea dolore in aliquip fugiat anim adipisicing amet aute tempor et deserunt est duis sint.`, + `list`, + ` - item 1`, + ` - item 2`, + `collection`, + ` section`, + ` lorem ipsum 1`, + ` section`, + ` lorem ipsum 2`, + `collection2`, + ` section`, + ` position 3`, + ` Laborum aute anim occaecat occaecat pariatur tempor proident magna sit magna non non.`, + ` list`, + ` 1`, + ` 2` +] + +async function main() { + console.dir(await parse(lines, schema), { depth: null }) +} + +main()