78 lines
2.4 KiB
JavaScript
78 lines
2.4 KiB
JavaScript
import { useDocument } from '@terrace/core'
|
|
import { createStringReader } from '@terrace/core/readers/js-string'
|
|
|
|
export async function parse(lines, rootSchema) {
|
|
const doc = useDocument(createStringReader(lines))
|
|
|
|
const levelTracker = []
|
|
|
|
function createScope(level, line, schema = null) {
|
|
levelTracker.length = level
|
|
const entry = levelTracker[level] = {}
|
|
entry.scope = [line, []]
|
|
|
|
if (schema) {
|
|
entry.schemas = Object.values(schema),
|
|
entry.matchers = Object.keys(schema),
|
|
entry.counts = Object.values(schema).map(e => e.count)
|
|
}
|
|
|
|
return entry.scope
|
|
}
|
|
|
|
createScope(0, 'root', rootSchema)
|
|
|
|
// Simpler parsing logic, don't need to worry about schemas, matchers, or counts if no schema is specified.
|
|
if (!rootSchema) {
|
|
while (true) {
|
|
// If doc.next() returns true we've ended the document.
|
|
if (await doc.next()) break;
|
|
const level = doc.level()
|
|
// Determine parent for this scope.
|
|
const parent = levelTracker[level].scope
|
|
// If there's no parent, skip this line.
|
|
if (!parent) continue
|
|
|
|
// Create new scope
|
|
const scope = createScope(level + 1, doc.line())
|
|
// Add current scope to parent.
|
|
parent[1].push(scope)
|
|
}
|
|
// Full parsing logic
|
|
} else {
|
|
while (true) {
|
|
// If doc.next() returns true we've ended the document.
|
|
if (await doc.next()) break;
|
|
const level = doc.level()
|
|
if (!levelTracker[level]) continue
|
|
|
|
// Determine parent for this scope.
|
|
const parent = levelTracker[level].scope
|
|
const schemas = levelTracker[level].schemas
|
|
const matchers = levelTracker[level].matchers
|
|
const counts = levelTracker[level].counts
|
|
|
|
// Match the head value, or '?' for unspecified lines.
|
|
const matchIndex = matchers.findIndex(entry => entry === doc.head() || entry === '?')
|
|
|
|
// Handle trailing blocks of text. TODO: Proper trailing.
|
|
if (matchIndex === -1 && matchers.includes('? literal')) {
|
|
parent[1].push(...(await doc.content(level)).map(e => [e]))
|
|
continue
|
|
} else if (matchIndex === -1) continue
|
|
|
|
// Return if the match has already been "used up"
|
|
if (counts[matchIndex] === 0) continue
|
|
// "use up" one more match
|
|
counts[matchIndex] -= 1
|
|
|
|
const scopeSchema = schemas[matchIndex]
|
|
// Create new scope
|
|
const scope = createScope(level + 1, doc.line(), scopeSchema?.children)
|
|
parent[1].push(scope)
|
|
}
|
|
}
|
|
|
|
return levelTracker[0].scope
|
|
}
|