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 { join } from '../fn.js'
|
||||||
import { alpha, alphanumeric, any, char, digit, list, many, map, maybe, noCaseString, separated, seq, skip, string, until } from '../parser.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({
|
export const Symbol = Object.freeze({
|
||||||
Bracket: Object.freeze({
|
Bracket: Object.freeze({
|
||||||
|
@ -47,7 +47,7 @@ const str = quoted
|
||||||
const toBoolean = v => v === 'false' ? false : true
|
const toBoolean = v => v === 'false' ? false : true
|
||||||
const boolean = map(([v]) => toBoolean(v), any(string('true'), string('false')))
|
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)))
|
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))
|
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 {
|
class Value {
|
||||||
#value
|
#value
|
||||||
|
@ -26,6 +27,40 @@ export class Identifier extends Value {
|
||||||
constructor(value) {
|
constructor(value) {
|
||||||
super(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 {
|
export class Name extends Identifier {
|
||||||
|
@ -67,11 +102,11 @@ export class ObjectPath extends Identifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return this.path.reduce((acc, val) => acc + val, this.value)
|
return `${this.value}.${this.path.join('.')}`
|
||||||
}
|
}
|
||||||
|
|
||||||
from(obj) {
|
from(component) {
|
||||||
return this.path.reduce((obj, key) => obj && obj[key], obj)
|
return this.path.reduce((acc, v) => v.from(acc), component)
|
||||||
}
|
}
|
||||||
|
|
||||||
equals(value) {
|
equals(value) {
|
||||||
|
@ -139,15 +174,62 @@ export class Relationship {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isExactly = (Type, value) => Object.getPrototypeOf(value) === Type.prototype
|
||||||
|
|
||||||
export class ReturnValues {
|
export class ReturnValues {
|
||||||
constructor(...args) {
|
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) {
|
find(fn) {
|
||||||
return 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) {
|
includes(value) {
|
||||||
return this.find(x => x.equals(value))
|
return this.find(x => x.equals(value))
|
||||||
}
|
}
|
||||||
|
|
22
src/query.js
22
src/query.js
|
@ -101,24 +101,32 @@ const isTag = type => (
|
||||||
) || false
|
) || false
|
||||||
)
|
)
|
||||||
|
|
||||||
const getValue = (type, entity) => {
|
const getValue = (returnValue, type, entity) => {
|
||||||
if (isTag(type)) {
|
if (isTag(type)) {
|
||||||
return {}
|
return {}
|
||||||
} else if (typeof type === 'function') {
|
} else if (typeof type === 'function') {
|
||||||
return type(entity)
|
return type(entity)
|
||||||
} else {
|
} else {
|
||||||
return Object.fromEntries(
|
return returnValue.from(type)
|
||||||
Object.entries(type).map(([k, v]) => [k, v[entity]])
|
//return Object.fromEntries(
|
||||||
)
|
// Object.entries(type).map(([k, v]) => [k, v[entity]])
|
||||||
|
//)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolveNode = curry((node, obj) => {
|
const resolveNode = curry((node, obj) => {
|
||||||
const returns = path('query.schema.returnValues', node)
|
const returns = path('query.schema.returnValues', node)
|
||||||
const { entity, query: { components } } = node
|
const { entity, query: { components } } = node
|
||||||
const values = components.filter(({ name }) => returns.includes(name))
|
// TODO: do this work in prepareQuery
|
||||||
.map(({ name, type }) => ({ [name]: getValue(type, entity) }))
|
returns.from(components)
|
||||||
return Object.assign(obj, ...values)
|
|
||||||
|
//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 => {
|
const resolveRelationship = edges => {
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { before, describe, it } from 'node:test'
|
import { before, beforeEach, describe, it } from 'node:test'
|
||||||
import { addComponent, addEntity, createWorld, defineComponent, Types } from 'bitecs'
|
import { addComponent, addEntity, createWorld, defineComponent, resetWorld, Types } from 'bitecs'
|
||||||
import assert from './assert.js'
|
import assert from './assert.js'
|
||||||
import { query } from '../src/query.js'
|
import { query } from '../src/query.js'
|
||||||
import { identity } from '../src/fn.js'
|
|
||||||
|
|
||||||
let engine = {}
|
let engine = {}
|
||||||
|
|
||||||
|
@ -26,7 +25,6 @@ describe('query', () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
const world = { default: createWorld() }
|
const world = { default: createWorld() }
|
||||||
const component = {
|
const component = {
|
||||||
Entity: identity,
|
|
||||||
Player: defineComponent(null, 10),
|
Player: defineComponent(null, 10),
|
||||||
NPC: defineComponent(null, 10),
|
NPC: defineComponent(null, 10),
|
||||||
Health: defineComponent({ current: Types.ui8, max: Types.ui8 }),
|
Health: defineComponent({ current: Types.ui8, max: Types.ui8 }),
|
||||||
|
@ -39,7 +37,11 @@ describe('query', () => {
|
||||||
engine = { world, component }
|
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 world = engine.world.default
|
||||||
const { Player, NPC, Knows } = engine.component
|
const { Player, NPC, Knows } = engine.component
|
||||||
|
|
||||||
|
@ -82,9 +84,16 @@ describe('query', () => {
|
||||||
|
|
||||||
Health.max[player] = 50
|
Health.max[player] = 50
|
||||||
Health.current[player] = 25
|
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(
|
assert.deepEqual(
|
||||||
query('MATCH (e:Entity, h:Health) return e, h.max', engine).unwrap(),
|
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