167 lines
5.2 KiB
JavaScript
167 lines
5.2 KiB
JavaScript
import { createLineData, useDocument } from '@terrace/core'
|
|
import { createStringReader } from '@terrace/core/readers/js-string'
|
|
|
|
export async function parse(lines, schema) {
|
|
const typeHandlers = {
|
|
string (doc) {
|
|
return doc.tail().slice(1)
|
|
},
|
|
number (doc) {
|
|
return +doc.tail().slice(1)
|
|
},
|
|
object (doc, handlers, definition, level) {
|
|
level = level != null ? level : doc.level() + 1
|
|
definition = lookup(definition)
|
|
|
|
const object = {}
|
|
for (let [key, child] of Object.entries(definition.values)) {
|
|
child = lookup(child)
|
|
if (key === 'any') key = null
|
|
addHandler(handlers, level, key, child, (key, value) => {
|
|
if (child.collate === 'array') {
|
|
if (!object[key]) object[key] = []
|
|
object[key].push(value)
|
|
return
|
|
} else if (child.collate === 'collection') {
|
|
if (!object[key]) object[key] = []
|
|
object[key].push({ [key]: value })
|
|
return
|
|
} else {
|
|
object[key] = value
|
|
}
|
|
})
|
|
}
|
|
|
|
if (definition.tail) {
|
|
const tailKey = definition.tail
|
|
const tailType = definition.values[tailKey]
|
|
object[tailKey] = typeHandlers[tailType](doc, handlers)
|
|
}
|
|
|
|
if (definition.text) {
|
|
const textKey = definition.text
|
|
const textType = 'string'
|
|
object[textKey] = ''
|
|
addHandler(handlers, level, '', textType, (key, value) => {
|
|
object[textKey] += object[textKey] ? `\n${doc.line()}` : doc.line()
|
|
})
|
|
}
|
|
|
|
return object
|
|
},
|
|
array (doc, handlers, definition, level) {
|
|
level = level != null ? level : doc.level() + 1
|
|
definition = lookup(definition)
|
|
|
|
const array = []
|
|
for (let [key, child] of Object.entries(definition.values)) {
|
|
child = lookup(child)
|
|
addHandler(handlers, level, key, child, (key, value) => {
|
|
array.push(value)
|
|
})
|
|
}
|
|
|
|
// if (definition.tail) {
|
|
// const tailKey = definition.tail
|
|
// const tailType = definition.values[tailKey]
|
|
// object[tailKey] = typeHandlers[tailType](doc, handlers)
|
|
// }
|
|
|
|
// if (definition.text) {
|
|
// const textKey = definition.text
|
|
// const textType = definition.values[textKey]
|
|
// object[textKey] = ''
|
|
// addHandler(handlers, level, '', textType, (key, value) => {
|
|
// object[textKey] += object[textKey] ? `\n${doc.line()}` : doc.line()
|
|
// })
|
|
// }
|
|
|
|
return array
|
|
},
|
|
collection (doc, handlers, definition, level) {
|
|
level = level != null ? level : doc.level() + 1
|
|
definition = lookup(definition)
|
|
|
|
const collection = []
|
|
for (let [key, child] of Object.entries(definition.values)) {
|
|
child = lookup(child)
|
|
addHandler(handlers, level, key, child, (key, value) => {
|
|
collection.push({ [key]: value })
|
|
})
|
|
}
|
|
|
|
// if (definition.tail) {
|
|
// const tailKey = definition.tail
|
|
// const tailType = definition.values[tailKey]
|
|
// object[tailKey] = typeHandlers[tailType](doc, handlers)
|
|
// }
|
|
|
|
// if (definition.text) {
|
|
// const textKey = definition.text
|
|
// const textType = definition.values[textKey]
|
|
// object[textKey] = ''
|
|
// addHandler(handlers, level, '', textType, (key, value) => {
|
|
// object[textKey] += object[textKey] ? `\n${doc.line()}` : doc.line()
|
|
// })
|
|
// }
|
|
|
|
return collection
|
|
},
|
|
}
|
|
|
|
function lookup (definition) {
|
|
const type = typeof definition === 'string' ? definition : definition.type
|
|
const resolvedDefinition = schema.types[type] || definition
|
|
return typeof definition === 'string' ? resolvedDefinition : Object.assign({}, definition, resolvedDefinition)
|
|
}
|
|
|
|
function registerTypes(typeHandlers, types) {
|
|
for (const [key, definition] of Object.entries(types)) {
|
|
const existingType = typeof definition === 'string' ? definition : definition.type
|
|
typeHandlers[key] = typeHandlers[existingType]
|
|
}
|
|
}
|
|
|
|
function addHandler(handlers, level, key, definition, resolve) {
|
|
const type = typeof definition === 'string' ? definition : definition.type
|
|
if (!handlers[level]) handlers[level] = []
|
|
handlers[level].push({
|
|
key,
|
|
resolve,
|
|
definition,
|
|
handler: typeHandlers[type]
|
|
})
|
|
}
|
|
|
|
const doc = useDocument(createStringReader(lines))
|
|
const handlers = []
|
|
|
|
registerTypes(typeHandlers, schema.types)
|
|
|
|
const root = typeHandlers.object(doc, handlers, schema.root, 0)
|
|
|
|
let ended = false
|
|
while (!ended) {
|
|
ended = await doc.next()
|
|
if (ended) break;
|
|
const level = doc.level()
|
|
handlers.length = level + 1
|
|
|
|
const unmatchedHandler = handlers[level]?.find(h => h.key === '')
|
|
let matched = false
|
|
for (const { key, definition, resolve, handler } of handlers[level] || []) {
|
|
if (key && doc.head() !== key) continue;
|
|
if (!doc.line()) continue;
|
|
resolve(doc.head(), handler(doc, handlers, definition))
|
|
matched = true
|
|
break
|
|
}
|
|
if (!matched && unmatchedHandler) {
|
|
const { resolve, definition, handler } = unmatchedHandler
|
|
resolve(doc.head(), handler(doc, handlers, definition))
|
|
}
|
|
}
|
|
|
|
return root
|
|
}
|