Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# 0.3.0

Breaking: support path expressions instead of level.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ A high-level wrapper over [JsonHilo](https://github.com/xtao-org/jsonhilo) which
```js
// see also quickstart.js
// replace vx.y.z below with latest/desired version
import {JsonStrum} from 'https://cdn.jsdelivr.net/gh/xtao-org/jsonstrum@v0.2.0/mod.js'
import {JsonStrum} from 'https://cdn.jsdelivr.net/gh/xtao-org/jsonstrum@v0.3.0/mod.js'

const s = JsonStrum({
object: (object) => console.log('object', object),
array: (array) => console.log('array', array),
// will only parse and emit objects at this level of nesting
level: 1,
// will only parse and emit objects at given path
path: [{}, {}]
})

s.push(`
Expand Down
94 changes: 83 additions & 11 deletions mod.js
Original file line number Diff line number Diff line change
@@ -1,69 +1,141 @@
import { JsonHigh } from "./deps.js"

// todo: recursive descent, e.g. [{}, "x", {depthRange: []}, "obj"]
// depthRange: [0, 3] would be the same as ...([] | [{}] | [{}, {}] | [{}, {}, {}])
// depthRange: [] would be unbounded

// could also abstract the matcher into a module
const matchPath = (path, expr) => {
// perhaps this should only check if prefix of path matches
// i.e. it's enough that path.length >= expr.length
// or maybe there should be a flag parameter which determines whether the match should be exact or just prefix
if (path.length !== expr.length) return false
for (let i = 0; i < expr.length; ++i) {
const e = expr[i]
const p = path[i]

if (e === p) continue
if (Array.isArray(e)) {
if (e.length === 0) throw Error('empty alternative')
for (const k of e) {
if (p === k) continue
}
return false
}
// todo:
// assume object
const {range, regex} = e
if (range !== undefined) {
if (regex !== undefined) throw Error(`can't have regex & range simultaneously`)
// assume no more than 2 elements
// could also support step to match every nth element
const [from, to] = e
if (from !== undefined) {
if (p < from) return false
}
if (to !== undefined) {
if (p > to) return false
}
}
if (regex !== undefined) {
// assume match is a regex
if (regex.test(p)) continue
// throw Error(`must have either match or range`)
}
// assume empty object
continue
}
return true
}

// perhaps this should also support non-object values (primitives)
export const JsonStrum = ({
object,
array,
level = 0
// perhaps rename to query or pathQuery or pathExpr
path = [{}],
} = {}) => {
const ancestors = []
let parent = null
let current = null
let key = null
let currentLevel = 0
const currentPath = [-1]
const level = path.length - 1

const close = () => {
--currentLevel
if (currentLevel === level) {
currentPath.pop()
// if currentPath matches exactly
if (currentLevel === level && matchPath(currentPath, path)) {
if (Array.isArray(current)) {
array?.(current)
array?.(current, currentPath)
} else {
object?.(current)
object?.(current, currentPath)
}
current = null
parent = null
// if a prefix of currentPath matches
} else if (currentLevel > level) {
if (Array.isArray(parent)) {
parent.push(current)
} else {
parent[key] = current
parent[currentPath.at(-1)] = current
}
current = parent
parent = ancestors.pop()
}
}

const incrementIndex = () => {
const last = currentPath.at(-1)
if (typeof last === 'number') {
currentPath[currentPath.length - 1] += 1
}
}

return JsonHigh({
openArray: () => {
++currentLevel
if (currentLevel > level) {
// if a prefix of current path matches
if (currentLevel > level && matchPath(currentPath.slice(0, path.length), path)) {
if (current !== null) {
ancestors.push(parent)
parent = current
}
current = []
}
incrementIndex()
currentPath.push(-1)
},
openObject: () => {
++currentLevel
if (currentLevel > level) {
// if a prefix of current path matches
if (currentLevel > level && matchPath(currentPath.slice(0, path.length), path)) {
if (current !== null) {
ancestors.push(parent)
parent = current
}
current = {}
}
incrementIndex()
currentPath.push("")
},
closeArray: close,
closeObject: close,
key: (k) => { if (currentLevel > level) key = k },
key: (k) => {
// if (currentLevel > level && matchPath(currentPath.slice(0, path.length), path)) key = k
currentPath[currentPath.length - 1] = k
},
value: (value) => {
if (currentLevel > level) {
// if a prefix of current path matches
if (currentLevel > level && matchPath(currentPath.slice(0, path.length), path)) {
if (Array.isArray(current)) {
current.push(value)
} else {
current[key] = value
current[currentPath.at(-1)] = value
}
}
incrementIndex()
},
})
}
8 changes: 4 additions & 4 deletions quickstart.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {JsonStrum} from 'https://cdn.jsdelivr.net/gh/xtao-org/[email protected]/mod.js'
import {JsonStrum} from './mod.js'

const s = JsonStrum({
object: (object) => console.log('object', object),
array: (array) => console.log('array', array),
level: 1,
object: (object, p) => console.log('object', object, p),
array: (array, p) => console.log('array', array, p),
path: [{}, {}],
})

s.push(`
Expand Down
17 changes: 9 additions & 8 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {JsonStrum} from './mod.js'

const s = JsonStrum({
object: (object) => console.log('object', object),
array: (array) => console.log('array', array),
object: (object, p) => console.log('object', object, p),
array: (array, p) => console.log('array', array, p),
path: [{}, "sub"]
})

s.push(`
Expand All @@ -19,9 +20,9 @@ s.push(`,
[1, 2, 3]`)

const s2 = JsonStrum({
object: (object) => console.log('object', object),
array: (array) => console.log('array', array),
level: 1,
object: (object, p) => console.log('object', object, p),
array: (array, p) => console.log('array', array, p),
path: [{}, {}]
})

s2.push(`[
Expand All @@ -37,9 +38,9 @@ s2.push(`[


const s3 = JsonStrum({
object: (object) => console.log('object', object),
array: (array) => console.log('array', array),
level: 3,
object: (object, p) => console.log('object', object, p),
array: (array, p) => console.log('array', array, p),
path: [{}, {}, {}, {}]
})

s3.push(`
Expand Down