yeah this sucks

This commit is contained in:
Rowan 2024-11-22 04:46:56 -06:00
parent ceab51b145
commit 90972e95e4
3 changed files with 87 additions and 53 deletions

View file

@ -23,6 +23,8 @@ export const tap = curry((fn, val) => {
})
export const always = x => () => x
export const orDefault = curry((value, defaultValue) => value || defaultValue)
export const orElse = curry((value, fn) => orDefault(value, fn()))
export const pipe = (...f) => v => reduce(applyTo, v, f)
export const apply = fn => args => fn.apply(null, args)
export const applyTo = curry((v, fn) => fn(v))
@ -32,9 +34,9 @@ export const useWith = curry((fn, tfns) =>
(...args) => pipe(enumerate, map(([i, a]) => tfns[i](a)), fn)(args)
))
export const converge = (fn, [head, ...rest]) => curryN(
head.length,
(...args) => pipe(...rest, fn)(head(...args))
export const converge = (fn, tfns) => curryN(
max(map(length, tfns)),
(...args) => fn(...map(tfn => tfn(...args), tfns))
)
export const diverge = (fn, tfns) => pipe(applyTo, flip(map)(tfns), apply(fn))
@ -50,8 +52,10 @@ export const when = curry((p, f, v) => ifElse(p, f, identity, v))
export const unless = curry((p, f, v) => ifElse(p, identity, f, v))
// predicates
export const and = (...booleans) => booleans.every(identity)
export const or = (...booleans) => booleans.some(identity)
export const every = curry((predicate, v) => v.every(predicate))
export const some = curry((predicate, v) => v.some(predicate))
export const all = curry((predicates, v) => every(applyTo(v), predicates))
export const any = curry((predicates, v) => some(applyTo(v), predicates))
export const isOk = value => !is(Result, value) || value.isOk()
// classes
@ -60,7 +64,14 @@ export const construct = Type => args => new Type(...args)
// strings
export const split = curry((delim, v) => v.split(delim))
// math
export const max = v => Math.max(...v)
export const min = v => Math.min(...v)
export const inc = v => v + 1
export const dec = v => v + 1
// arrays
export const map = curry((fn, v) => v.map(fn))
export const length = v => v.length
export const len = length
export const of = unless(isArray, value => ([value]))
@ -70,7 +81,7 @@ export const drop = curry((n, value) => value.slice(n))
export const nth = curry((index, value) => value[index])
export const head = nth(0)
export const tail = drop(1)
export const map = curry((fn, v) => v.map(fn))
export const last = v => v && v[v.length - 1]
export const filter = curry((fn, v) => v.filter(fn))
export const reduce = curry((fn, init, v) => v.reduce(fn, init))
export const find = curry((fn, v) => v.find(fn))
@ -88,10 +99,28 @@ export const assoc = curry((key, value, obj) => ({ ...obj, [key]: value }))
export const assocPath = curry((key, value, obj) => pipe(
makePath,
rev,
reduce((acc, val) => fromEntries([[val, acc]]), value),
mergeLeft(obj)
reduce((acc, val) => ({ [val]: acc }), value),
mergeRightDeep(obj),
)(key))
export const mergeLeftDeep = curry((a, b) => {
return reduce((acc, key) => {
const av = acc[key]
const bv = b[key]
return pipe(
ifElse(
every(is(Object)),
apply(mergeLeftDeep),
last
),
v => assoc(key, v, acc)
)([av, bv])
}, a, keys(b))
})
export const mergeRightDeep = flip(mergeLeftDeep)
export const prop = curry((key, obj) => obj && key && obj[key])
export const path = curry((key, obj) => reduce(flip(prop), obj, makePath(key)))
export const mergeLeft = curry((a, b) => ({ ...b, ...a }))

View file

@ -1,15 +1,9 @@
import { defineQuery, hasComponent } from 'bitecs'
import { query as q } from './query-parser/index.js'
import { parseAll } from './parser.js'
import { curry, of, is, reduce, flip, concat, map, when, assoc, mergeLeft, isNil, path, prop, ifElse, diverge, pipe, curryN, always, tap } from './fn.js'
import { curry, of, is, reduce, flip, concat, map, when, assoc, mergeLeft, isNil, path, prop, ifElse, diverge, pipe, always, assocPath, prepend, orDefault, useWith, applyTo } from './fn.js'
import { Relationship } from './query-parser/types.js'
/*
* necessary engine structure
* worlds: World[]
* Component: Component[]
*/
const prepareQuery = (query, component) => {
const { match, returnValues } = query
const relationship = is(Relationship, match)
@ -20,16 +14,14 @@ const prepareQuery = (query, component) => {
return { from, to, edge, relationship, schema: query }
} else {
const label = match.label?.value?.value
const name = match.name?.value?.value
const label = path('label.value.value', match)
const name = path('name.value.value', match)
const type = component[label]
return { name, label, type, relationship, query: defineQuery([type]), schema: query }
}
}
const getEndNode = end => end.relationship ? end.from : end
const matches = (world, type, query, set = new Set()) => entity => {
if (set.has(entity)) {
return true
@ -59,7 +51,6 @@ const queryRelationship = (from, edge, to, world) => {
return result
}
const cat = flip(concat)
const assembleQuery = (query, entities) => map(entity => of({ entity, query }), entities)
const executeQuery = curry((query, world) => {
@ -68,32 +59,27 @@ const executeQuery = curry((query, world) => {
}
const { from, to: _to, edge } = query
const to = getEndNode(_to)
const Edge = edge.type
const to = when(prop('relationship'), prop('from'), _to)
const edges = queryRelationship(
{ type: from.type, results: from.query(world) },
{ type: Edge, results: edge.query(world) },
{ type: edge.type, results: edge.query(world) },
{ type: to.type, results: to.query(world) },
world
)
if (_to.relationship) {
return reduce((acc, eid) => {
const next = {
..._to,
from: {
...to,
query: always(of(Edge.to[eid]))
}
}
const next = assocPath(
'from.query',
always(of(edge.type.to[eid])),
_to
)
return pipe(
executeQuery(next),
map(cat([eid])),
map(prepend(eid)),
map(([l, r]) => ([{ entity: l, query }, r])),
x => ([...acc, ...x]),
concat(acc)
)(world)
}, [], edges)
} else {
@ -102,6 +88,7 @@ const executeQuery = curry((query, world) => {
})
const returnValues = pipe(path('query.schema.returnValues.values'), map(prop('value')))
const nodeName = path('query.schema.match.name.value.value')
const includes = curry((val, arr) => !isNil(val) && arr.includes(val))
const maybeAssoc = curry((pred, key, value, obj) =>
@ -126,10 +113,11 @@ const resolveRelationship = edges => {
to
)
const resolve = resolveNode(returns)
return pipe(
resolveNode(returns, fn, Edge.from[entity]),
resolveNode(returns, en, entity),
resolveNode(returns, tn, Edge.to[entity]),
resolve(fn, Edge.from[entity]),
resolve(en, entity),
resolve(tn, Edge.to[entity]),
)({})
}
@ -138,19 +126,22 @@ const resolveReturns = map(
map(ifElse(
path('query.relationship'),
resolveRelationship,
diverge(resolveNode, [returnValues, path('query.schema.match.name.value.value'), prop('entity'), always({})])
diverge(resolveNode, [returnValues, nodeName, prop('entity'), always({})])
)),
reduce(mergeLeft, {})
)
)
export const query = curry((input, engine) => {
return parseAll(q, input).map(({ use, ...rest }) => {
const worldName = use && use.value || 'default'
const world = engine.world[worldName]
const prepared = prepareQuery(rest, engine.component)
const edges = executeQuery(prepared, world)
return resolveReturns(edges)
})
})
export const query = curry((input, { world, component }) =>
map(({ use, ...rest }) =>
pipe(
prop('value'),
orDefault('default'),
flip(prop)(world),
executeQuery(prepareQuery(rest, component)),
resolveReturns
)(use),
parseAll(q, input)
)
)

View file

@ -53,12 +53,26 @@ describe('query', () => {
knows(c, player) // 10
const q = 'MATCH (player:Player)-[e1:Knows]->(a:NPC)-[e2:Knows]->(b:NPC) RETURN player, e1, a, e2, b'
const result = query(q, engine)
assert.deepEqual(result.unwrap(), [
{ player: 0, e1: 4, a: 1, e2: 7, b: 2 },
{ player: 0, e1: 4, a: 1, e2: 8, b: 3 }
])
assert.deepEqual(
query('MATCH (player:Player) RETURN player', engine).unwrap(),
[{ player: 0 }]
)
assert.deepEqual(
query('MATCH (player:Player)-[e1:Knows]->(a:NPC) RETURN player, e1, a', engine).unwrap(),
[
{ player: 0, e1: 4, a: 1 },
{ player: 0, e1: 5, a: 3 }
]
)
assert.deepEqual(
query('MATCH (player:Player)-[e1:Knows]->(a:NPC)-[e2:Knows]->(b:NPC) RETURN player, e1, a, e2, b', engine).unwrap(),
[
{ player: 0, e1: 4, a: 1, e2: 7, b: 2 },
{ player: 0, e1: 4, a: 1, e2: 8, b: 3 }
]
)
})
})