graph-ecs/src/query-parser/match.js

90 lines
2.4 KiB
JavaScript

import { identifier, Keyword, literal, Symbols, ws } from './common.js'
import { Node, Edge, KeyValuePair, Label, Name, Direction, DirectedEdge, Relationship, Component, Match } from './types.js'
import { construct, curry, head, is } from '../fn.js'
import { many, maybe, map, seq, skip, between, noCaseString, separated, list, any } from '../parser.js'
const { Bracket, Colon, Comma, Hyphen } = Symbols
const name = map(
construct(Name),
identifier
)
const trim = curry((parser, state) => (
between(ws, parser, ws, state)
))
const label = map(([x]) => new Label(x), seq(trim(skip(Colon)), identifier))
const bracketed = curry((value, { Left, Right }, state) => (
between(trim(Left), value, trim(Right), state)
))
const kvp = map(
construct(KeyValuePair),
separated(name, trim(Colon), literal)
)
export const kvps = list(trim(Comma), kvp)
export const properties = bracketed(kvps, Bracket.Curly)
const id = seq(maybe(name), maybe(label))
const makeObj = Type => params => {
const [name, label] = is(Name, params[0]) ? [params[0], params[1]] : [undefined, params[0]]
const properties = !name ? params.slice(1) : params.slice(2)
return new Type(name, label, properties)
}
const component = map(makeObj(Component), seq(id, maybe(properties)))
const components = list(trim(Comma), component)
export const node = map(
x => new Node(x),
bracketed(components, Bracket.Round)
)
export const edge = map(
x => new Edge(x),
bracketed(component, Bracket.Square)
)
const arrowRight = map(() => Direction.Right, seq(Hyphen, Bracket.Angle.Right))
const arrowLeft = map(() => Direction.Left, seq(Bracket.Angle.Left, Hyphen))
const relationshipRight = map(([edge, direction]) =>
DirectedEdge.fromEdge(edge, direction),
seq(skip(Hyphen), edge, arrowRight)
)
const relationshipLeft = map(([direction, edge]) =>
DirectedEdge.fromEdge(edge, direction),
seq(arrowLeft, edge, skip(Hyphen))
)
const relationship = seq(any(relationshipRight, relationshipLeft), node)
const makeRelationship = args => {
const len = args.length - 1
if (len <= 0) {
return new Match(head(args))
}
let right = args[len]
for (let i = len; i > 0; i -= 2) {
const edge = args[i - 1]
const left = args[i - 2]
right = new Relationship(left, edge, right)
}
return new Match(right)
}
const params = map(
makeRelationship,
seq(node, many(relationship))
)
export const matchClause = seq(skip(Keyword.Match), ws, params)