Start making more generic.
This commit is contained in:
parent
a6435368aa
commit
172f9d5ca1
@ -1,276 +0,0 @@
|
||||
import { createLineData, useDocument } from '@terrace/core'
|
||||
import { createStringReader } from '@terrace/core/readers/js-string'
|
||||
|
||||
|
||||
const tceSchema = `
|
||||
types
|
||||
section object, text as content, tail as pos
|
||||
content string
|
||||
pos number
|
||||
position number
|
||||
options object
|
||||
parameter1 number
|
||||
parameter2 string
|
||||
|
||||
root object
|
||||
title string
|
||||
options options
|
||||
options2 options
|
||||
subsection
|
||||
type section
|
||||
collate collection
|
||||
list array
|
||||
- string
|
||||
collection collection
|
||||
section section
|
||||
`
|
||||
|
||||
const schema = {
|
||||
types: {
|
||||
section: {
|
||||
type: 'object',
|
||||
text: 'content',
|
||||
tail: 'pos',
|
||||
values: {
|
||||
content: 'string',
|
||||
pos: 'number',
|
||||
position: {
|
||||
type: 'number'
|
||||
}
|
||||
}
|
||||
},
|
||||
options: {
|
||||
type: 'object',
|
||||
values: {
|
||||
parameter1: 'number',
|
||||
parameter2: 'string'
|
||||
}
|
||||
},
|
||||
advList: {
|
||||
type: 'array',
|
||||
values: {
|
||||
section: 'section'
|
||||
}
|
||||
}
|
||||
},
|
||||
root: {
|
||||
type: 'object',
|
||||
values: {
|
||||
title: 'string',
|
||||
options: 'options',
|
||||
options2: 'options',
|
||||
subsection: {
|
||||
type: 'section',
|
||||
// Allows a particular repeated key to collect itself under a multi-value root as an array.
|
||||
collate: 'collection'
|
||||
},
|
||||
list: {
|
||||
// Defines an array where all entries must conform to specific types. No other keys are permitted.
|
||||
// Ex: [ value, value ]
|
||||
type: 'array',
|
||||
values: {
|
||||
'-': 'string'
|
||||
}
|
||||
},
|
||||
collection: {
|
||||
// Defines an array where all entries must conform to specific types. They will be segregated by key. No other keys are permitted.
|
||||
// Ex: [ {key: value }, { key: value }]
|
||||
type: 'collection',
|
||||
values: {
|
||||
section: 'section'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const lines = [
|
||||
`title Example`,
|
||||
`options`,
|
||||
` parameter1 30`,
|
||||
` parameter2 Enim eu id anim minim reprehenderit nostrud eu amet deserunt ea ut do cupidatat ea.`,
|
||||
`options2`,
|
||||
` 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`
|
||||
|
||||
]
|
||||
|
||||
|
||||
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)
|
||||
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 = definition.values[textKey]
|
||||
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]
|
||||
})
|
||||
}
|
||||
|
||||
async function main() {
|
||||
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 (doc.head() !== key) 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))
|
||||
}
|
||||
}
|
||||
|
||||
console.log(root)
|
||||
}
|
||||
|
||||
main()
|
164
docs/experiments/parsers/core.js
Normal file
164
docs/experiments/parsers/core.js
Normal file
@ -0,0 +1,164 @@
|
||||
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)
|
||||
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 = definition.values[textKey]
|
||||
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 (doc.head() !== key) 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
|
||||
}
|
114
docs/experiments/parsers/example.js
Normal file
114
docs/experiments/parsers/example.js
Normal file
@ -0,0 +1,114 @@
|
||||
import { parse } from './core.js'
|
||||
|
||||
const schemaTCE = `
|
||||
types
|
||||
section object, text as content, tail as pos
|
||||
content string
|
||||
pos number
|
||||
position number
|
||||
options object
|
||||
parameter1 number
|
||||
parameter2 string
|
||||
|
||||
root object
|
||||
title string
|
||||
options options
|
||||
options2 options
|
||||
subsection section
|
||||
collate collection
|
||||
list array
|
||||
- string
|
||||
collection collection
|
||||
section section
|
||||
`
|
||||
|
||||
const schema = {
|
||||
types: {
|
||||
section: {
|
||||
type: 'object',
|
||||
text: 'content',
|
||||
tail: 'pos',
|
||||
values: {
|
||||
content: 'string',
|
||||
pos: 'number',
|
||||
position: {
|
||||
type: 'number'
|
||||
}
|
||||
}
|
||||
},
|
||||
options: {
|
||||
type: 'object',
|
||||
values: {
|
||||
parameter1: 'number',
|
||||
parameter2: 'string'
|
||||
}
|
||||
},
|
||||
advList: {
|
||||
type: 'array',
|
||||
values: {
|
||||
section: 'section'
|
||||
}
|
||||
}
|
||||
},
|
||||
root: {
|
||||
type: 'object',
|
||||
values: {
|
||||
title: 'string',
|
||||
options: 'options',
|
||||
options2: 'options',
|
||||
subsection: {
|
||||
type: 'section',
|
||||
// Allows a particular repeated key to collect itself under a multi-value root as an array.
|
||||
collate: 'collection'
|
||||
},
|
||||
list: {
|
||||
// Defines an array where all entries must conform to specific types. No other keys are permitted.
|
||||
// Ex: [ value, value ]
|
||||
type: 'array',
|
||||
values: {
|
||||
'-': 'string'
|
||||
}
|
||||
},
|
||||
collection: {
|
||||
// Defines an array where all entries must conform to specific types. They will be segregated by key. No other keys are permitted.
|
||||
// Ex: [ {key: value }, { key: value }]
|
||||
type: 'collection',
|
||||
values: {
|
||||
section: 'section'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const lines = [
|
||||
`title Example`,
|
||||
`options`,
|
||||
` parameter1 30`,
|
||||
` parameter2 Enim eu id anim minim reprehenderit nostrud eu amet deserunt ea ut do cupidatat ea.`,
|
||||
`options2`,
|
||||
` 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`
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log(await parse(lines, schema))
|
||||
}
|
||||
|
||||
main()
|
43
docs/experiments/parsers/schema.js
Normal file
43
docs/experiments/parsers/schema.js
Normal file
@ -0,0 +1,43 @@
|
||||
const schemaSchemaTCE = `
|
||||
types
|
||||
primitive object, tail as type
|
||||
any primitive
|
||||
|
||||
root object
|
||||
types object, optional
|
||||
any primitive
|
||||
|
||||
root object
|
||||
any primitive
|
||||
`
|
||||
|
||||
const schemaSchemaJSON = {
|
||||
types: {
|
||||
primitive: {
|
||||
type: 'object',
|
||||
tail: 'type',
|
||||
values: {
|
||||
type: 'string',
|
||||
any: 'primitive'
|
||||
}
|
||||
},
|
||||
},
|
||||
root: {
|
||||
type: 'object',
|
||||
values: {
|
||||
types: {
|
||||
type: 'object',
|
||||
optional: true,
|
||||
values: {
|
||||
any: 'primitive'
|
||||
}
|
||||
},
|
||||
root: {
|
||||
type: 'object',
|
||||
values: {
|
||||
any: 'primitive'
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user