90 lines
2.4 KiB
JavaScript
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)
|
|
|