finalize dsl parser for now
This commit is contained in:
parent
bab47f8a95
commit
84e5bb4c02
9 changed files with 86 additions and 45 deletions
|
@ -17,6 +17,8 @@ export const and = (...booleans) => booleans.every(identity)
|
|||
export const or = (...booleans) => booleans.some(identity)
|
||||
export const isOk = r => !is(Result, r) || r.isOk()
|
||||
|
||||
export const construct = Type => args => new Type(...args)
|
||||
|
||||
export const of = value => Array.isArray(value) ? value : [value]
|
||||
export const head = value => value[0]
|
||||
export const tail = value => value.slice(1)
|
||||
|
@ -29,6 +31,8 @@ export const find = curry((fn, v) => v.find(fn))
|
|||
export const join = curry((sep, v) => v.join(sep))
|
||||
export const rev = v => v.slice().reverse()
|
||||
export const pipe = (...f) => v => f.reduce(apply, v)
|
||||
export const prepend = curry((value, iter) => [value, ...iter])
|
||||
export const append = curry((value, iter) => [...iter, value])
|
||||
|
||||
export const identity = x => x
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { inspect } from 'node:util'
|
||||
import { Result } from './result.js'
|
||||
import { Iterator, Stream } from './stream.js'
|
||||
import { curry, join, of } from './fn.js'
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
// MATCH (a:Label)-[e:Label]->(b:Label)
|
||||
// RETURN a, b
|
||||
//
|
|
@ -36,7 +36,7 @@ export const collect = parser => map(join(''), parser)
|
|||
export const quoted = collect(seq(skip(Symbol.Quote), until(Symbol.Quote), skip(Symbol.Quote)))
|
||||
export const number = seq(digit, many(digit))
|
||||
export const signed = seq(maybe(any(Symbol.Plus, Symbol.Hyphen)), number)
|
||||
export const ws = skip(many(Symbol.Space))
|
||||
export const ws = skip(many(any(Symbol.Newline, Symbol.Space)))
|
||||
export const identifier = map(([x]) => new Identifier(x), collect(seq(alpha, many(alphanumeric))))
|
||||
|
||||
const float = map(([n]) => parseFloat(n, 10), collect(seq(signed, Symbol.Period, number)))
|
||||
|
|
|
@ -1,25 +1,30 @@
|
|||
import { head, is } from '../fn.js'
|
||||
import { construct, head, ifElse, is, pipe, prepend } from '../fn.js'
|
||||
import { map, maybe, seq } from '../parser.js'
|
||||
import { word } from './common.js'
|
||||
import { matchClause } from './match.js'
|
||||
import { returnClause } from './return.js'
|
||||
import { Query, SelectedGraph } from './types.js'
|
||||
import { useClause } from './use.js'
|
||||
|
||||
const collect = args => {
|
||||
return args
|
||||
if (is(SelectedGraph, head(args))) {
|
||||
return new Query(...args)
|
||||
} else {
|
||||
return new Query(undefined, ...args)
|
||||
}
|
||||
}
|
||||
const hasUseClause = pipe(
|
||||
head,
|
||||
is(SelectedGraph)
|
||||
)
|
||||
|
||||
const constructQuery = construct(Query)
|
||||
|
||||
const collect = ifElse(
|
||||
hasUseClause,
|
||||
constructQuery,
|
||||
pipe(prepend(undefined), constructQuery)
|
||||
)
|
||||
|
||||
export const query = map(
|
||||
collect,
|
||||
seq(
|
||||
maybe(useClause),
|
||||
matchClause,
|
||||
returnClause
|
||||
word(matchClause),
|
||||
word(returnClause)
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { identifier, literal, Symbol, ws } from './common.js'
|
||||
import { Node, Edge, KeyValuePair, Label, Name, Direction, DirectedEdge, Relationship } from './types.js'
|
||||
import { curry, filter, is } from '../fn.js'
|
||||
import { construct, curry, filter, is } from '../fn.js'
|
||||
import { many, maybe, map, seq, skip, between, noCaseString, separated, list, any } from '../parser.js'
|
||||
|
||||
const { Bracket, Colon, Comma, Hyphen } = Symbol
|
||||
|
||||
const name = map(
|
||||
([x]) => new Name(x),
|
||||
construct(Name),
|
||||
identifier
|
||||
)
|
||||
|
||||
|
@ -21,7 +21,7 @@ const bracketed = curry((value, { Left, Right }, state) => (
|
|||
))
|
||||
|
||||
const kvp = map(
|
||||
([k, v]) => new KeyValuePair(k, v),
|
||||
construct(KeyValuePair),
|
||||
separated(name, trim(Colon), literal)
|
||||
)
|
||||
export const kvps = list(trim(Comma), kvp)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import util from 'node:util'
|
||||
import { describe, it } from 'node:test'
|
||||
import assert from '../assert.js'
|
||||
import { node, edge, matchClause } from '../../src/query/match.js'
|
||||
|
|
|
@ -1,40 +1,73 @@
|
|||
import { describe, it } from 'node:test'
|
||||
import assert from '../assert.js'
|
||||
import { query } from '../../src/query/index.js'
|
||||
import { Identifier, Query, ReturnValues } from '../../src/query/types.js'
|
||||
import { makeEdge, makeNode } from '../utils.js'
|
||||
import { Alias, Identifier, ObjectPath, Query, ReturnValues, SelectedGraph } from '../../src/query/types.js'
|
||||
import { makeNode, makeRelationship, makeRightEdge } from '../utils.js'
|
||||
|
||||
const i = n => new Identifier(n)
|
||||
const rv = (...v) => new ReturnValues(...v)
|
||||
const path = (...v) => new ObjectPath(...v)
|
||||
const alias = (...v) => new Alias(...v)
|
||||
const identifier = n => new Identifier(n)
|
||||
const graph = n => new SelectedGraph(identifier(n))
|
||||
const returnVals = (...v) => new ReturnValues(...v)
|
||||
const q = (u, m, rv) => new Query(u, m, rv)
|
||||
|
||||
describe('query', () => {
|
||||
//it('should match a node', () => {
|
||||
// assert.parseOk(query, 'MATCH (node:Label) RETURN node', ([actual]) => {
|
||||
// const expected = q(
|
||||
// undefined, // no use clause
|
||||
// makeNode('node', 'Label'),
|
||||
// rv(i('node'))
|
||||
// )
|
||||
it('should match a node', () => {
|
||||
assert.parseOk(query, 'MATCH (node:Label) RETURN node', ([actual]) => {
|
||||
const expected = q(
|
||||
undefined, // no use clause
|
||||
makeNode('node', 'Label'),
|
||||
returnVals(identifier('node'))
|
||||
)
|
||||
|
||||
// assert.deepEqual(actual, expected)
|
||||
// })
|
||||
//})
|
||||
assert.deepEqual(actual, expected)
|
||||
})
|
||||
})
|
||||
|
||||
it('should match a relationship', () => {
|
||||
assert.parseOk(query, 'MATCH (rown:Creature)-[:PETPATS]->(kbity:NetCat) RETURN rown, kbity', (actual) => {
|
||||
console.log(actual)
|
||||
//const expected = q(
|
||||
// undefined, // no use clause
|
||||
// [
|
||||
// makeNode('rown', 'Creature'),
|
||||
// makeEdge(undefined, 'PETPATS'),
|
||||
// makeNode('kbity', 'NetCat'),
|
||||
// ],
|
||||
// rv(i('rown'), i('kbity'))
|
||||
//)
|
||||
assert.parseOk(query, 'MATCH (rown:Creature)-[:Petpats]->(kbity:NetCat) RETURN rown, kbity', ([actual]) => {
|
||||
const expected = q(
|
||||
undefined, // no use clause
|
||||
makeRelationship(
|
||||
makeNode('rown', 'Creature'),
|
||||
makeRightEdge(undefined, 'Petpats'),
|
||||
makeNode('kbity', 'NetCat'),
|
||||
),
|
||||
returnVals(identifier('rown'), identifier('kbity'))
|
||||
)
|
||||
|
||||
//assert.deepEqual(actual, expected)
|
||||
assert.deepEqual(actual, expected)
|
||||
})
|
||||
})
|
||||
|
||||
it('should support the USE clause', () => {
|
||||
assert.parseOk(query, 'USE aminals MATCH (kbity:Cat) RETURN kbity', ([actual]) => {
|
||||
const expected = q(
|
||||
graph('aminals'),
|
||||
makeNode('kbity', 'Cat'),
|
||||
returnVals(identifier('kbity'))
|
||||
)
|
||||
|
||||
assert.deepEqual(actual, expected)
|
||||
})
|
||||
})
|
||||
|
||||
it('should support complex queries', () => {
|
||||
assert.parseOk(query, 'USE aminals MATCH (kbity:Cat { type: "cute" })-[:Eats { schedule: "daily" }]->(snacc:Feesh { type: "vegan", weight: 0.7 }) RETURN kbity.name AS name, snacc AS food', ([actual]) => {
|
||||
const expected = q(
|
||||
graph('aminals'),
|
||||
makeRelationship(
|
||||
makeNode('kbity', 'Cat', [['type', 'cute']]),
|
||||
makeRightEdge(undefined, 'Eats', [['schedule', 'daily']]),
|
||||
makeNode('snacc', 'Feesh', [['type', 'vegan'], ['weight', 0.7]])
|
||||
),
|
||||
returnVals(
|
||||
alias(path(identifier('kbity'), identifier('name')), identifier('name')),
|
||||
alias(identifier('snacc'), identifier('food'))
|
||||
)
|
||||
)
|
||||
|
||||
assert.deepEqual(actual, expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -11,5 +11,7 @@ export const graphObject = (name, label, props = [], Type = Node) => new Type(
|
|||
export const makeNode = graphObject
|
||||
export const makeEdge = (name, label, props = []) => graphObject(name, label, props, Edge)
|
||||
export const makeDirectedEdge = (name, label, direction, props = []) => DirectedEdge.fromEdge(makeEdge(name, label, props), direction)
|
||||
export const makeLeftEdge = (name, label, props = []) => DirectedEdge.fromEdge(makeEdge(name, label, props), 0)
|
||||
export const makeRightEdge = (name, label, props = []) => DirectedEdge.fromEdge(makeEdge(name, label, props), 1)
|
||||
export const makeRelationship = (from, edge, to) => new Relationship(from, edge, to)
|
||||
|
||||
|
|
Loading…
Reference in a new issue