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 }