leaving for flight, brb
This commit is contained in:
parent
a8d9f36b34
commit
c1520b7a59
4 changed files with 119 additions and 20 deletions
|
@ -1,6 +1,6 @@
|
|||
import { join } from '../fn.js'
|
||||
import { alpha, alphanumeric, any, char, digit, list, many, map, maybe, noCaseString, separated, seq, skip, string, until } from '../parser.js'
|
||||
import { Alias, Identifier, Literal, ObjectPath } from './types.js'
|
||||
import { Alias, Identifier, Literal, ObjectPath, Property } from './types.js'
|
||||
|
||||
export const Symbol = Object.freeze({
|
||||
Bracket: Object.freeze({
|
||||
|
@ -47,7 +47,7 @@ const str = quoted
|
|||
const toBoolean = v => v === 'false' ? false : true
|
||||
const boolean = map(([v]) => toBoolean(v), any(string('true'), string('false')))
|
||||
|
||||
const property = seq(skip(Symbol.Period), identifier)
|
||||
const property = map(([x]) => new Property(x), seq(skip(Symbol.Period), identifier))
|
||||
const accessor = map(x => new ObjectPath(...x), seq(identifier, property, many(property)))
|
||||
|
||||
export const literal = map(([x]) => new Literal(x), any(float, integer, str, boolean))
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { is } from '../fn.js'
|
||||
import { pipe } from 'bitecs'
|
||||
import { assoc, assocPath, is, nth } from '../fn.js'
|
||||
|
||||
class Value {
|
||||
#value
|
||||
|
@ -26,6 +27,40 @@ export class Identifier extends Value {
|
|||
constructor(value) {
|
||||
super(value)
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.value.toString()
|
||||
}
|
||||
|
||||
from(component) {
|
||||
return component
|
||||
}
|
||||
}
|
||||
|
||||
export class ObjectIdentifier extends Identifier {
|
||||
constructor(value) {
|
||||
super(value)
|
||||
}
|
||||
|
||||
from(component) {
|
||||
return new Proxy(component, {
|
||||
get(target, entity, _receiver) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(target).map(([k, v]) => [k, v[entity]])
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Property extends Identifier {
|
||||
constructor(value) {
|
||||
super(value)
|
||||
}
|
||||
|
||||
from(component) {
|
||||
return component[this.value]
|
||||
}
|
||||
}
|
||||
|
||||
export class Name extends Identifier {
|
||||
|
@ -67,11 +102,11 @@ export class ObjectPath extends Identifier {
|
|||
}
|
||||
|
||||
toString() {
|
||||
return this.path.reduce((acc, val) => acc + val, this.value)
|
||||
return `${this.value}.${this.path.join('.')}`
|
||||
}
|
||||
|
||||
from(obj) {
|
||||
return this.path.reduce((obj, key) => obj && obj[key], obj)
|
||||
from(component) {
|
||||
return this.path.reduce((acc, v) => v.from(acc), component)
|
||||
}
|
||||
|
||||
equals(value) {
|
||||
|
@ -139,15 +174,62 @@ export class Relationship {
|
|||
}
|
||||
}
|
||||
|
||||
const isExactly = (Type, value) => Object.getPrototypeOf(value) === Type.prototype
|
||||
|
||||
export class ReturnValues {
|
||||
constructor(...args) {
|
||||
this.values = args
|
||||
this.values = args.map(x => isExactly(Identifier, x) ? new ObjectIdentifier(x) : x)
|
||||
}
|
||||
|
||||
#isTag(type) {
|
||||
type && Object.getOwnPropertySymbols(type).find(
|
||||
(s) => s.description === 'tagStore'
|
||||
) || false
|
||||
}
|
||||
|
||||
#forComponentType(value, type) {
|
||||
if (this.#isTag(type)) {
|
||||
return () => ({})
|
||||
} else if (typeof type === 'function') {
|
||||
return type
|
||||
} else {
|
||||
return value.from(type)
|
||||
//return Object.fromEntries(
|
||||
// Object.entries(type).map(([k, v]) => [k, v[entity]])
|
||||
//)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: adjust froms to accent entity, return data with built up objects as necessary
|
||||
from(components) {
|
||||
/*
|
||||
* const { entity, query: { components } } = node
|
||||
* const returns2 = components.map(c => ({ ...c, returnValue: returns.find(x => x.equals(c.name)) }))
|
||||
* returns2.map(({ returnValue, type }) => getValue(returnValue, type, entity))
|
||||
* const values = returns2.filter(({ name }) => returns.includes(name))
|
||||
* .map(({ name, type, returnValue }) => ({ [name]: getValue(returnValue, type, entity) }))
|
||||
* const values = components.filter(({ name }) => returns.includes(name))
|
||||
* .map(({ name, type }) => ({ [name]: getValue(type, entity) }))
|
||||
* return Object.assign(obj, ...values)
|
||||
*/
|
||||
|
||||
const values = components.map(component => ({ ...component, returnValue: this.findName(component.name) }))
|
||||
.map(({ name, type, returnValue }) => ([returnValue.toString(), this.#forComponentType(returnValue, type)]))
|
||||
console.log(this, values)
|
||||
}
|
||||
|
||||
find(fn) {
|
||||
return this.values.find(fn)
|
||||
}
|
||||
|
||||
findName(name) {
|
||||
return this.find(value => value.equals(name))
|
||||
}
|
||||
|
||||
filter(fn) {
|
||||
return this.values.filter(fn)
|
||||
}
|
||||
|
||||
includes(value) {
|
||||
return this.find(x => x.equals(value))
|
||||
}
|
||||
|
|
22
src/query.js
22
src/query.js
|
@ -101,24 +101,32 @@ const isTag = type => (
|
|||
) || false
|
||||
)
|
||||
|
||||
const getValue = (type, entity) => {
|
||||
const getValue = (returnValue, type, entity) => {
|
||||
if (isTag(type)) {
|
||||
return {}
|
||||
} else if (typeof type === 'function') {
|
||||
return type(entity)
|
||||
} else {
|
||||
return Object.fromEntries(
|
||||
Object.entries(type).map(([k, v]) => [k, v[entity]])
|
||||
)
|
||||
return returnValue.from(type)
|
||||
//return Object.fromEntries(
|
||||
// Object.entries(type).map(([k, v]) => [k, v[entity]])
|
||||
//)
|
||||
}
|
||||
}
|
||||
|
||||
const resolveNode = curry((node, obj) => {
|
||||
const returns = path('query.schema.returnValues', node)
|
||||
const { entity, query: { components } } = node
|
||||
const values = components.filter(({ name }) => returns.includes(name))
|
||||
.map(({ name, type }) => ({ [name]: getValue(type, entity) }))
|
||||
return Object.assign(obj, ...values)
|
||||
// TODO: do this work in prepareQuery
|
||||
returns.from(components)
|
||||
|
||||
//const returns2 = components.map(c => ({ ...c, returnValue: returns.find(x => x.equals(c.name)) }))
|
||||
//returns2.map(({ returnValue, type }) => getValue(returnValue, type, entity))
|
||||
//const values = returns2.filter(({ name }) => returns.includes(name))
|
||||
// .map(({ name, type, returnValue }) => ({ [name]: getValue(returnValue, type, entity) }))
|
||||
//const values = components.filter(({ name }) => returns.includes(name))
|
||||
// .map(({ name, type }) => ({ [name]: getValue(type, entity) }))
|
||||
//return Object.assign(obj, ...values)
|
||||
})
|
||||
|
||||
const resolveRelationship = edges => {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { before, describe, it } from 'node:test'
|
||||
import { addComponent, addEntity, createWorld, defineComponent, Types } from 'bitecs'
|
||||
import { before, beforeEach, describe, it } from 'node:test'
|
||||
import { addComponent, addEntity, createWorld, defineComponent, resetWorld, Types } from 'bitecs'
|
||||
import assert from './assert.js'
|
||||
import { query } from '../src/query.js'
|
||||
import { identity } from '../src/fn.js'
|
||||
|
||||
let engine = {}
|
||||
|
||||
|
@ -26,7 +25,6 @@ describe('query', () => {
|
|||
before(() => {
|
||||
const world = { default: createWorld() }
|
||||
const component = {
|
||||
Entity: identity,
|
||||
Player: defineComponent(null, 10),
|
||||
NPC: defineComponent(null, 10),
|
||||
Health: defineComponent({ current: Types.ui8, max: Types.ui8 }),
|
||||
|
@ -39,7 +37,11 @@ describe('query', () => {
|
|||
engine = { world, component }
|
||||
})
|
||||
|
||||
it('should execute basic queries', () => {
|
||||
beforeEach(() => {
|
||||
Object.values(engine.world).forEach(world => resetWorld(world))
|
||||
})
|
||||
|
||||
it('should query on single components', () => {
|
||||
const world = engine.world.default
|
||||
const { Player, NPC, Knows } = engine.component
|
||||
|
||||
|
@ -82,9 +84,16 @@ describe('query', () => {
|
|||
|
||||
Health.max[player] = 50
|
||||
Health.current[player] = 25
|
||||
|
||||
// an unspecified component should return an Entity
|
||||
assert.deepEqual(
|
||||
query('MATCH (e, h:Health) return e, h.max', engine).unwrap(),
|
||||
[{ e: 11, h: { max: 50 } }]
|
||||
)
|
||||
|
||||
assert.deepEqual(
|
||||
query('MATCH (e:Entity, h:Health) return e, h.max', engine).unwrap(),
|
||||
[{ e: 11, h: { current: 25, max: 50 } }]
|
||||
[{ e: 11, h: { max: 50 } }]
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue