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)