2022-11-20 17:24:27 -05:00

79 lines
2.1 KiB
JavaScript

import { useDocument } from '@terrace/core'
import { createStringReader } from '@terrace/core/readers/js-string'
export async function parse(lines, schema) {
const doc = useDocument(createStringReader(lines))
function createScope(line, matchers) {
return {
block: [line, []],
handlers: [],
matchers: matchers
}
}
const macros = schema.macros
const scopes = [createScope('root', {
title ({ addHandler }) {
addHandler(scope => [scope[0].split(' ')[0], scope[0].split(' ').slice(1).join(' ')])
},
options ({ addMatcher }) {
addMatcher({
parameter1({ addHandler }) {
addHandler(scope => [scope[0].split(' ')[0], +scope[0].split(' ').slice(1).join(' ')])
}
})
}
})]
let ended = false
while (!ended) {
ended = await doc.next()
if (ended) break;
const level = doc.level() + 1
// Trigger macros for closed scopes.
for (let i = scopes.length - 1; i >= level; --i) {
const scope = scopes[i]
const parent = scopes[i - 1]
// Remove empty arrays from scopes without children.
if (scope.block[1] && !scope.block[1].length) scope.block.length = 1
// Postprocess scope with relevant macros.
scope.block = scope.handlers.reduce((block, handler) => {
return handler(block)
}, [...scope.block])
// Add to parent block.
parent.block[1].push(scope.block)
}
// Reset scope length to avoid dangling scopes.
scopes.length = level
// Define current scope
const scope = scopes[level] = createScope(doc.line(), {})
// Determine parent for this scope.
const parent = scopes[level - 1]
// Add a postprocess handler for this scope.
// Ie. When we leave the scope, rewrite it using this handler.
function addHandler(handler) {
scope.handlers.push(handler)
}
// Add matchers for this scope's children.
function addMatcher(definition) {
scope.matchers = definition
}
// Run matching matchers for this scope.
if (parent.matchers[doc.head()]) {
parent.matchers[doc.head()]({ addHandler, addMatcher })
}
}
return scopes[0].block
}