diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am index 59b6bff..c73c2f3 100644 --- a/lib/src/Makefile.am +++ b/lib/src/Makefile.am @@ -66,6 +66,12 @@ libcypher_parser_la_SOURCES = \ ast_on_match.c \ ast_order_by.c \ ast_parameter.c \ + ast_path_pattern.c \ + ast_path_pattern_expression.c \ + ast_path_pattern_alternative.c \ + ast_path_pattern_base.c \ + ast_path_pattern_edge.c \ + ast_path_pattern_reference.c \ ast_pattern.c \ ast_pattern_comprehension.c \ ast_pattern_path.c \ diff --git a/lib/src/ast.c b/lib/src/ast.c index 9ece2b5..cc5213a 100644 --- a/lib/src/ast.c +++ b/lib/src/ast.c @@ -125,6 +125,12 @@ struct cypher_astnode_vts const struct cypher_astnode_vt *pattern_path; const struct cypher_astnode_vt *node_pattern; const struct cypher_astnode_vt *rel_pattern; + const struct cypher_astnode_vt *path_pattern; + const struct cypher_astnode_vt *path_pattern_expression; + const struct cypher_astnode_vt *path_pattern_alternative; + const struct cypher_astnode_vt *path_pattern_base; + const struct cypher_astnode_vt *path_pattern_edge; + const struct cypher_astnode_vt *path_pattern_reference; const struct cypher_astnode_vt *range; const struct cypher_astnode_vt *command; const struct cypher_astnode_vt *comment; @@ -242,6 +248,12 @@ static const struct cypher_astnode_vts cypher_astnode_vts = .pattern_path = &cypher_pattern_path_astnode_vt, .node_pattern = &cypher_node_pattern_astnode_vt, .rel_pattern = &cypher_rel_pattern_astnode_vt, + .path_pattern = &cypher_path_pattern_astnode_vt, + .path_pattern_expression = &cypher_path_pattern_expression_astnode_vt, + .path_pattern_alternative = &cypher_path_pattern_alternative_astnode_vt, + .path_pattern_base = &cypher_path_pattern_base_astnode_vt, + .path_pattern_edge = &cypher_path_pattern_edge_astnode_vt, + .path_pattern_reference = &cypher_path_pattern_reference_astnode_vt, .range = &cypher_range_astnode_vt, .command = &cypher_command_astnode_vt, .line_comment = &cypher_line_comment_astnode_vt, @@ -369,6 +381,12 @@ const uint8_t CYPHER_AST_SHORTEST_PATH = VT_OFFSET(shortest_path); const uint8_t CYPHER_AST_PATTERN_PATH = VT_OFFSET(pattern_path); const uint8_t CYPHER_AST_NODE_PATTERN = VT_OFFSET(node_pattern); const uint8_t CYPHER_AST_REL_PATTERN = VT_OFFSET(rel_pattern); +const uint8_t CYPHER_AST_PATH_PATTERN = VT_OFFSET(path_pattern); +const uint8_t CYPHER_AST_PATH_PATTERN_EXPRESSION = VT_OFFSET(path_pattern_expression); +const uint8_t CYPHER_AST_PATH_PATTERN_ALTERNATIVE = VT_OFFSET(path_pattern_alternative); +const uint8_t CYPHER_AST_PATH_PATTERN_BASE = VT_OFFSET(path_pattern_base); +const uint8_t CYPHER_AST_PATH_PATTERN_EDGE = VT_OFFSET(path_pattern_edge); +const uint8_t CYPHER_AST_PATH_PATTERN_REFERENCE = VT_OFFSET(path_pattern_reference); const uint8_t CYPHER_AST_RANGE = VT_OFFSET(range); const uint8_t CYPHER_AST_COMMAND = VT_OFFSET(command); const uint8_t CYPHER_AST_COMMENT = VT_OFFSET(comment); diff --git a/lib/src/ast_named_path.c b/lib/src/ast_named_path.c index b2b63ee..4d8de5b 100644 --- a/lib/src/ast_named_path.c +++ b/lib/src/ast_named_path.c @@ -25,6 +25,7 @@ struct named_path cypher_pattern_path_astnode_t _pattern_path_astnode; const cypher_astnode_t *identifier; const cypher_astnode_t *path; + const cypher_astnode_t *condition; }; @@ -53,7 +54,7 @@ static const struct cypher_pattern_path_astnode_vt pp_vt = cypher_astnode_t *cypher_ast_named_path(const cypher_astnode_t *identifier, - const cypher_astnode_t *path, cypher_astnode_t **children, + const cypher_astnode_t *path, const cypher_astnode_t *condition, cypher_astnode_t **children, unsigned int nchildren, struct cypher_input_range range) { REQUIRE_CHILD(children, nchildren, identifier, CYPHER_AST_IDENTIFIER, NULL); @@ -72,6 +73,7 @@ cypher_astnode_t *cypher_ast_named_path(const cypher_astnode_t *identifier, } node->identifier = identifier; node->path = path; + node->condition = condition; return &(node->_pattern_path_astnode._astnode); } @@ -87,8 +89,9 @@ cypher_astnode_t *clone(const cypher_astnode_t *self, cypher_astnode_t *identifier = children[child_index(self, node->identifier)]; cypher_astnode_t *path = children[child_index(self, node->path)]; + cypher_astnode_t *condition = children[child_index(self, node->condition)]; - return cypher_ast_named_path(identifier, path, children, self->nchildren, + return cypher_ast_named_path(identifier, path, condition, children, self->nchildren, self->range); } @@ -116,6 +119,16 @@ const cypher_astnode_t *cypher_ast_named_path_get_path( return node->path; } +const cypher_astnode_t *cypher_ast_named_path_get_condition( + const cypher_astnode_t *astnode) +{ + REQUIRE_TYPE(astnode, CYPHER_AST_NAMED_PATH, NULL); + const cypher_pattern_path_astnode_t *ppnode = + container_of(astnode, cypher_pattern_path_astnode_t, _astnode); + struct named_path *node = + container_of(ppnode, struct named_path, _pattern_path_astnode); + return node->condition; +} unsigned int nelements(const cypher_pattern_path_astnode_t *self) { @@ -141,6 +154,15 @@ ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) container_of(self, cypher_pattern_path_astnode_t, _astnode); struct named_path *node = container_of(ppnode, struct named_path, _pattern_path_astnode); - return snprintf(str, size, "@%d = @%d", node->identifier->ordinal, - node->path->ordinal); + if (node->condition != NULL) + { + return snprintf(str, size, "@%d = @%d WHERE @%d", node->identifier->ordinal, + node->path->ordinal, node->condition->ordinal); + } + + else + { + return snprintf(str, size, "@%d = @%d", node->identifier->ordinal, + node->path->ordinal); + } } diff --git a/lib/src/ast_path_pattern.c b/lib/src/ast_path_pattern.c new file mode 100644 index 0000000..13e9c3a --- /dev/null +++ b/lib/src/ast_path_pattern.c @@ -0,0 +1,127 @@ +/* vi:set ts=4 sw=4 expandtab: + * + * Copyright 2016, Chris Leishman (http://github.com/cleishm) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "../../config.h" +#include "astnode.h" +#include "util.h" +#include + +struct path_pattern +{ + cypher_astnode_t _astnode; + const cypher_astnode_t *expression; + enum cypher_rel_direction direction; +}; + +static cypher_astnode_t *clone(const cypher_astnode_t *self, + cypher_astnode_t **children); +static ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size); + +const struct cypher_astnode_vt cypher_path_pattern_astnode_vt = + { .name = "path pattern", + .detailstr = detailstr, + .release = cypher_astnode_release, + .clone = clone }; + +cypher_astnode_t *cypher_ast_path_pattern(const cypher_astnode_t *expression, + enum cypher_rel_direction direction, cypher_astnode_t **children, unsigned int nchildren, + struct cypher_input_range range) +{ + struct path_pattern *node = calloc(1, sizeof(struct path_pattern)); + if (node == NULL) + { + return NULL; + } + if (cypher_astnode_init(&(node->_astnode), CYPHER_AST_PATH_PATTERN, + children, nchildren, range)) + { + goto cleanup; + } + node->expression = expression; + node->direction = direction; + return &(node->_astnode); + + int errsv; +cleanup: + errsv = errno; + free(node); + errno = errsv; + return NULL; +} + +cypher_astnode_t *clone(const cypher_astnode_t *self, + cypher_astnode_t **children) +{ + /**REQUIRE_TYPE(self, CYPHER_AST_PATH_PATTERN, NULL);*/ + struct path_pattern *node = container_of(self, struct path_pattern, _astnode); + + cypher_astnode_t *clone = cypher_ast_path_pattern(node->expression, + node->direction, children, self->nchildren, + self->range); + return clone; +} + +const cypher_astnode_t *cypher_ast_path_pattern_get_expression( + const cypher_astnode_t *astnode) +{ + struct path_pattern *node = + container_of(astnode, struct path_pattern, _astnode); + return node->expression; +} + +enum cypher_rel_direction cypher_ast_path_pattern_get_direction( + const cypher_astnode_t *astnode) +{ + struct path_pattern *node = + container_of(astnode, struct path_pattern, _astnode); + return node->direction; +} + +ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) +{ + struct path_pattern *node = container_of(self, struct path_pattern, _astnode); + + size_t n = 0; + ssize_t r = snprintf(str, size, "%s-/", + (node->direction == CYPHER_REL_INBOUND)? "<" : ""); + if (r < 0) + { + return -1; + } + n += r; + + if (node->expression != NULL) + { + r = snprintf(str+n, (n < size)? size-n : 0, "@%u", + node->expression->ordinal); + if (r < 0) + { + return -1; + } + n += r; + } + + r = snprintf(str+n, (n < size)? size-n : 0, "/-%s", + (node->direction == CYPHER_REL_OUTBOUND)? ">" : ""); + + if (r < 0) + { + return -1; + } + n += r; + + return n; +} \ No newline at end of file diff --git a/lib/src/ast_path_pattern_alternative.c b/lib/src/ast_path_pattern_alternative.c new file mode 100644 index 0000000..df24263 --- /dev/null +++ b/lib/src/ast_path_pattern_alternative.c @@ -0,0 +1,89 @@ +#include "astnode.h" + +struct path_pattern_alternative +{ + cypher_astnode_t _astnode; + size_t nelements; + const cypher_astnode_t *elements[]; +}; + +static cypher_astnode_t *clone(const cypher_astnode_t *self, + cypher_astnode_t **children); +static ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size); + +const struct cypher_astnode_vt cypher_path_pattern_alternative_astnode_vt = + { + .name = "alternative", + .detailstr = detailstr, + .release = cypher_astnode_release, + .clone = clone }; + +cypher_astnode_t *cypher_ast_path_pattern_alternative(cypher_astnode_t * const *elements, unsigned int nelements, + cypher_astnode_t **children, unsigned int nchildren, struct cypher_input_range range) +{ + struct path_pattern_alternative *node = calloc(1, sizeof(struct path_pattern_alternative) + + nelements * sizeof(cypher_astnode_t *)); + if (node == NULL) + { + return NULL; + } + + if (cypher_astnode_init(&(node->_astnode), CYPHER_AST_PATH_PATTERN_ALTERNATIVE, + children, nchildren, range)) + { + goto cleanup; + } + + memcpy(node->elements, elements, nelements * sizeof(cypher_astnode_t *)); + node->nelements = nelements; + return &(node->_astnode); + + int errsv; +cleanup: + errsv = errno; + free(node); + errno = errsv; + return NULL; +} + +unsigned int cypher_ast_path_pattern_alternative_get_nelements(const cypher_astnode_t *astnode) { + REQUIRE_TYPE(astnode, CYPHER_AST_PATH_PATTERN_ALTERNATIVE, NULL); + struct path_pattern_alternative *node = + container_of(astnode, struct path_pattern_alternative, _astnode); + return node->nelements; +} + +const cypher_astnode_t *cypher_ast_path_pattern_alternative_get_element( + const cypher_astnode_t *astnode, unsigned int index) +{ + REQUIRE_TYPE(astnode, CYPHER_AST_PATH_PATTERN_ALTERNATIVE, NULL); + struct path_pattern_alternative *node = + container_of(astnode, struct path_pattern_alternative, _astnode); + return node->elements[index]; +} + +cypher_astnode_t *clone(const cypher_astnode_t *self, cypher_astnode_t **children) +{ + /**REQUIRE_TYPE(self, CYPHER_AST_PATH_PATTERN, NULL);*/ + struct path_pattern_alternative *node = container_of(self, struct path_pattern_alternative, _astnode); + + cypher_astnode_t **elements = + calloc(node->nelements, sizeof(cypher_astnode_t *)); + if (elements == NULL) + { + return NULL; + } + for (unsigned int i = 0; i < node->nelements; ++i) + { + elements[i] = children[child_index(self, node->elements[i])]; + } + + cypher_astnode_t *clone = cypher_ast_path_pattern_alternative(elements, node->nelements, children, self->nchildren, + self->range); + return clone; +} + +ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) { + const struct path_pattern_alternative *alt = (const struct path_pattern_alternative *) self; + return snprintf(str, size, "Alt, elems: %ld", alt->nelements); +} \ No newline at end of file diff --git a/lib/src/ast_path_pattern_base.c b/lib/src/ast_path_pattern_base.c new file mode 100644 index 0000000..825798d --- /dev/null +++ b/lib/src/ast_path_pattern_base.c @@ -0,0 +1,98 @@ +#include "astnode.h" + +struct path_pattern_base { + cypher_astnode_t _astnode; + enum cypher_rel_direction direction; + const cypher_astnode_t *varlength; + const cypher_astnode_t *path_base; +}; + +static cypher_astnode_t *clone(const cypher_astnode_t *self, + cypher_astnode_t **children); +static ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size); + +const struct cypher_astnode_vt cypher_path_pattern_base_astnode_vt = + { .name = "path base", + .detailstr = detailstr, + .release = cypher_astnode_release, + .clone = clone }; + +cypher_astnode_t *cypher_ast_path_pattern_base(enum cypher_rel_direction direction, const cypher_astnode_t *varlength, + const cypher_astnode_t *path_base, cypher_astnode_t **children, + unsigned int nchildren, struct cypher_input_range range) +{ + struct path_pattern_base *node = calloc(1, sizeof(struct path_pattern_base)); + if (node == NULL) + { + return NULL; + } + if (cypher_astnode_init(&(node->_astnode), CYPHER_AST_PATH_PATTERN_BASE, + children, nchildren, range)) + { + goto cleanup; + } + + node->direction = direction; + node->varlength = varlength; + node->path_base = path_base; + return &(node->_astnode); + + int errsv; +cleanup: + errsv = errno; + free(node); + errno = errsv; + return NULL; +} + +enum cypher_rel_direction cypher_ast_path_pattern_base_get_direction( + const cypher_astnode_t *astnode) +{ + REQUIRE_TYPE(astnode, CYPHER_AST_PATH_PATTERN_BASE, NULL); + struct path_pattern_base *node = + container_of(astnode, struct path_pattern_base, _astnode); + return node->direction; +} + +const cypher_astnode_t *cypher_ast_path_pattern_base_get_child( + const cypher_astnode_t *astnode) +{ + REQUIRE_TYPE(astnode, CYPHER_AST_PATH_PATTERN_BASE, NULL); + struct path_pattern_base *node = + container_of(astnode, struct path_pattern_base, _astnode); + return node->path_base; +} + +cypher_astnode_t *clone(const cypher_astnode_t *self, + cypher_astnode_t **children) +{ + REQUIRE_TYPE(self, CYPHER_AST_PATH_PATTERN_BASE, NULL); + struct path_pattern_base *node = container_of(self, struct path_pattern_base, _astnode); + + cypher_astnode_t *varlength = (node->varlength == NULL) ? NULL : + children[child_index(self, node->varlength)]; + + cypher_astnode_t *path_base = (node->path_base == NULL) ? NULL : + children[child_index(self, node->path_base)]; + + cypher_astnode_t *clone = cypher_ast_path_pattern_base(node->direction, + varlength, path_base, children, self->nchildren, self->range); + + int errsv = errno; + errno = errsv; + return clone; +} + +ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) { + const struct path_pattern_base *base = (const struct path_pattern_base *) self; + const char *dir = (base->direction == CYPHER_REL_OUTBOUND) ? "outbound" : + (base->direction == CYPHER_REL_INBOUND ? "inbound" : "bidirectional"); + + const char *child = NULL; + + if (base->path_base->type == CYPHER_AST_LABEL) { + child = "label"; + } + + return snprintf(str, size, "dir: %s, child: %s", dir, child); +} \ No newline at end of file diff --git a/lib/src/ast_path_pattern_edge.c b/lib/src/ast_path_pattern_edge.c new file mode 100644 index 0000000..432ac29 --- /dev/null +++ b/lib/src/ast_path_pattern_edge.c @@ -0,0 +1,74 @@ +#include "astnode.h" + +struct path_pattern_edge +{ + cypher_astnode_t _astnode; + const cypher_astnode_t *reltype; +}; + +static cypher_astnode_t *clone(const cypher_astnode_t *self, + cypher_astnode_t **children); +static ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size); + + +const struct cypher_astnode_vt cypher_path_pattern_edge_astnode_vt = + { .name = "path pattern edge", + .detailstr = detailstr, + .release = cypher_astnode_release, + .clone = clone }; + + +cypher_astnode_t *cypher_ast_path_pattern_edge( const cypher_astnode_t *reltype, + cypher_astnode_t **children, unsigned int nchildren, + struct cypher_input_range range) +{ + struct path_pattern_edge *node = calloc(1, sizeof(struct path_pattern_edge)); + if (node == NULL) + { + return NULL; + } + if (cypher_astnode_init(&(node->_astnode), CYPHER_AST_PATH_PATTERN_EDGE, + children, nchildren, range)) + { + goto cleanup; + } + node->reltype = reltype; + return &(node->_astnode); + + int errsv; +cleanup: + errsv = errno; + free(node); + errno = errsv; + return NULL; +} + +const cypher_astnode_t *cypher_ast_path_pattern_edge_get_reltype( + const cypher_astnode_t *astnode) +{ + REQUIRE_TYPE(astnode, CYPHER_AST_PATH_PATTERN_EDGE, NULL); + struct path_pattern_edge *node = + container_of(astnode, struct path_pattern_edge, _astnode); + return node->reltype; +} + +cypher_astnode_t *clone(const cypher_astnode_t *self, + cypher_astnode_t **children) +{ + REQUIRE_TYPE(self, CYPHER_AST_PATH_PATTERN_EDGE, NULL); + struct path_pattern_edge *node = container_of(self, struct path_pattern_edge, _astnode); + + cypher_astnode_t *reltype = (node->reltype == NULL) ? NULL : + children[child_index(self, node->reltype)]; + + cypher_astnode_t *clone = cypher_ast_path_pattern_edge(reltype, + children, self->nchildren, self->range); + + int errsv = errno; + errno = errsv; + return clone; +} + +ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) { + return snprintf(str, size, "path pattern edge"); +} diff --git a/lib/src/ast_path_pattern_expression.c b/lib/src/ast_path_pattern_expression.c new file mode 100644 index 0000000..f137e9e --- /dev/null +++ b/lib/src/ast_path_pattern_expression.c @@ -0,0 +1,88 @@ +#include "astnode.h" + +struct path_pattern_expression +{ + cypher_astnode_t _astnode; + size_t nelements; + const cypher_astnode_t *elements[]; +}; + +static cypher_astnode_t *clone(const cypher_astnode_t *self, + cypher_astnode_t **children); +static ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size); + +const struct cypher_astnode_vt cypher_path_pattern_expression_astnode_vt = + { + .name = "expression", + .detailstr = detailstr, + .release = cypher_astnode_release, + .clone = clone }; + +cypher_astnode_t *cypher_ast_path_pattern_expression(cypher_astnode_t * const *elements, unsigned int nelements, + cypher_astnode_t **children, unsigned int nchildren, struct cypher_input_range range) +{ + struct path_pattern_expression *node = calloc(1, sizeof(struct path_pattern_expression) + + nelements * sizeof(cypher_astnode_t *)); + if (node == NULL) + { + return NULL; + } + + if (cypher_astnode_init(&(node->_astnode), CYPHER_AST_PATH_PATTERN_EXPRESSION, + children, nchildren, range)) + { + goto cleanup; + } + + memcpy(node->elements, elements, nelements * sizeof(cypher_astnode_t *)); + node->nelements = nelements; + return &(node->_astnode); + + int errsv; +cleanup: + errsv = errno; + free(node); + errno = errsv; + return NULL; +} + +unsigned int cypher_ast_path_pattern_expression_get_nelements(const cypher_astnode_t *astnode) { + REQUIRE_TYPE(astnode, CYPHER_AST_PATH_PATTERN_EXPRESSION, NULL); + struct path_pattern_expression *node = + container_of(astnode, struct path_pattern_expression, _astnode); + return node->nelements; +} + +const cypher_astnode_t *cypher_ast_path_pattern_expression_get_element( + const cypher_astnode_t *astnode, unsigned int index) +{ + REQUIRE_TYPE(astnode, CYPHER_AST_PATH_PATTERN_EXPRESSION, NULL); + struct path_pattern_expression *node = + container_of(astnode, struct path_pattern_expression, _astnode); + return node->elements[index]; +} + +cypher_astnode_t *clone(const cypher_astnode_t *self, cypher_astnode_t **children) +{ + struct path_pattern_expression *node = container_of(self, struct path_pattern_expression, _astnode); + + cypher_astnode_t **elements = + calloc(node->nelements, sizeof(cypher_astnode_t *)); + if (elements == NULL) + { + return NULL; + } + for (unsigned int i = 0; i < node->nelements; ++i) + { + elements[i] = children[child_index(self, node->elements[i])]; + } + + cypher_astnode_t *clone = cypher_ast_path_pattern_expression(elements, node->nelements, children, self->nchildren, + self->range); + return clone; +} + +ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) { + const struct path_pattern_expression *expression = (const struct path_pattern_expression *) self; + return snprintf(str, size, "Expression, elems: %ld", expression->nelements); +} \ No newline at end of file diff --git a/lib/src/ast_path_pattern_reference.c b/lib/src/ast_path_pattern_reference.c new file mode 100644 index 0000000..91db19a --- /dev/null +++ b/lib/src/ast_path_pattern_reference.c @@ -0,0 +1,74 @@ +#include "astnode.h" + +struct path_pattern_reference +{ + cypher_astnode_t _astnode; + const cypher_astnode_t *identifier; +}; + +static cypher_astnode_t *clone(const cypher_astnode_t *self, + cypher_astnode_t **children); +static ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size); + + +const struct cypher_astnode_vt cypher_path_pattern_reference_astnode_vt = + { .name = "path pattern reference", + .detailstr = detailstr, + .release = cypher_astnode_release, + .clone = clone }; + + +cypher_astnode_t *cypher_ast_path_pattern_reference( const cypher_astnode_t *identifier, + cypher_astnode_t **children, unsigned int nchildren, + struct cypher_input_range range) +{ + struct path_pattern_reference *node = calloc(1, sizeof(struct path_pattern_reference)); + if (node == NULL) + { + return NULL; + } + if (cypher_astnode_init(&(node->_astnode), CYPHER_AST_PATH_PATTERN_REFERENCE, + children, nchildren, range)) + { + goto cleanup; + } + node->identifier = identifier; + return &(node->_astnode); + + int errsv; + cleanup: + errsv = errno; + free(node); + errno = errsv; + return NULL; +} + +const cypher_astnode_t *cypher_ast_path_pattern_reference_get_identifier( + const cypher_astnode_t *astnode) +{ + REQUIRE_TYPE(astnode, CYPHER_AST_PATH_PATTERN_REFERENCE, NULL); + struct path_pattern_reference *node = + container_of(astnode, struct path_pattern_reference, _astnode); + return node->identifier; +} + +cypher_astnode_t *clone(const cypher_astnode_t *self, + cypher_astnode_t **children) +{ + REQUIRE_TYPE(self, CYPHER_AST_PATH_PATTERN_REFERENCE, NULL); + struct path_pattern_reference *node = container_of(self, struct path_pattern_reference, _astnode); + + cypher_astnode_t *identifier = (node->identifier == NULL) ? NULL : + children[child_index(self, node->identifier)]; + + cypher_astnode_t *clone = cypher_ast_path_pattern_reference(identifier, + children, self->nchildren, self->range); + + int errsv = errno; + errno = errsv; + return clone; +} + +ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) { + return snprintf(str, size, "~"); +} diff --git a/lib/src/ast_pattern_path.c b/lib/src/ast_pattern_path.c index b97608c..867bcb4 100644 --- a/lib/src/ast_pattern_path.c +++ b/lib/src/ast_pattern_path.c @@ -74,12 +74,14 @@ cypher_astnode_t *cypher_ast_pattern_path(cypher_astnode_t * const *elements, { REQUIRE(nelements % 2 == 1, NULL); REQUIRE(elements != NULL, NULL); +/* for (unsigned int i = 0; i < nelements; ++i) { REQUIRE_CHILD(children, nchildren, elements[i], - (i%2 == 0)? CYPHER_AST_NODE_PATTERN : CYPHER_AST_REL_PATTERN, + (i%2 == 0)? CYPHER_AST_NODE_PATTERN : (CYPHER_AST_REL_PATTERN || CYPHER_AST_PATH_PATTERN) , NULL); } +*/ struct pattern_path *node = calloc(1, sizeof(struct pattern_path) + nelements * sizeof(cypher_astnode_t *)); @@ -196,8 +198,17 @@ ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) } else { - r = snprintf(str+n, (n < size)? size-n : 0, "-[@%u]-", + if (node->elements[i]->type == CYPHER_AST_REL_PATTERN) + { + r = snprintf(str+n, (n < size)? size-n : 0, "-[@%u]-", node->elements[i]->ordinal); + } + else + { + r = snprintf(str+n, (n < size)? size-n : 0, "-/@%u/-", + node->elements[i]->ordinal); + } + } if (r < 0) { diff --git a/lib/src/ast_query.c b/lib/src/ast_query.c index 8bc9e88..c8b0911 100644 --- a/lib/src/ast_query.c +++ b/lib/src/ast_query.c @@ -51,8 +51,11 @@ cypher_astnode_t *cypher_ast_query(cypher_astnode_t * const *options, REQUIRE_CHILD_ALL(children, nchildren, options, noptions, CYPHER_AST_QUERY_OPTION, NULL); REQUIRE(nclauses > 0, NULL); - REQUIRE_CHILD_ALL(children, nchildren, clauses, nclauses, - CYPHER_AST_QUERY_CLAUSE, NULL); + for (unsigned int i = 0; i < nclauses; ++i) + { + REQUIRE(cypher_astnode_instanceof(clauses[i], CYPHER_AST_QUERY_CLAUSE) || + cypher_astnode_instanceof(clauses[i], CYPHER_AST_NAMED_PATH), NULL); + } struct query *node = calloc(1, sizeof(struct query) + nclauses * sizeof(cypher_astnode_t *)); diff --git a/lib/src/astnode.h b/lib/src/astnode.h index 8337fc3..25d9edf 100644 --- a/lib/src/astnode.h +++ b/lib/src/astnode.h @@ -238,6 +238,12 @@ extern const struct cypher_astnode_vt cypher_named_path_astnode_vt; extern const struct cypher_astnode_vt cypher_shortest_path_astnode_vt; extern const struct cypher_astnode_vt cypher_node_pattern_astnode_vt; extern const struct cypher_astnode_vt cypher_rel_pattern_astnode_vt; +extern const struct cypher_astnode_vt cypher_path_pattern_astnode_vt; +extern const struct cypher_astnode_vt cypher_path_pattern_expression_astnode_vt; +extern const struct cypher_astnode_vt cypher_path_pattern_alternative_astnode_vt; +extern const struct cypher_astnode_vt cypher_path_pattern_base_astnode_vt; +extern const struct cypher_astnode_vt cypher_path_pattern_edge_astnode_vt; +extern const struct cypher_astnode_vt cypher_path_pattern_reference_astnode_vt; extern const struct cypher_astnode_vt cypher_range_astnode_vt; extern const struct cypher_astnode_vt cypher_command_astnode_vt; extern const struct cypher_astnode_vt cypher_comment_astnode_vt; diff --git a/lib/src/cypher-parser.h.in b/lib/src/cypher-parser.h.in index b4a89dd..7870e91 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -336,6 +336,18 @@ extern const cypher_astnode_type_t CYPHER_AST_PATTERN_PATH; extern const cypher_astnode_type_t CYPHER_AST_NODE_PATTERN; /** Type for an AST relationship pattern node. */ extern const cypher_astnode_type_t CYPHER_AST_REL_PATTERN; +/** Type for an AST path pattern node. */ +extern const cypher_astnode_type_t CYPHER_AST_PATH_PATTERN; +/** Type for an AST path pattern expression node. */ +extern const cypher_astnode_type_t CYPHER_AST_PATH_PATTERN_EXPRESSION; +/** Type for an AST path pattern alternative node. */ +extern const cypher_astnode_type_t CYPHER_AST_PATH_PATTERN_ALTERNATIVE; +/** Type for an AST path pattern base node. */ +extern const cypher_astnode_type_t CYPHER_AST_PATH_PATTERN_BASE; +/** Type for an AST path pattern edge node. */ +extern const cypher_astnode_type_t CYPHER_AST_PATH_PATTERN_EDGE; +/** Type for an AST path pattern reference node. */ +extern const cypher_astnode_type_t CYPHER_AST_PATH_PATTERN_REFERENCE; /** Type for an AST range node. */ extern const cypher_astnode_type_t CYPHER_AST_RANGE; /** Type for an AST command node. */ @@ -4882,6 +4894,7 @@ const cypher_astnode_t *cypher_ast_pattern_get_path( * * @param [identifier] A `CYPHER_AST_IDENTIFIER` node. * @param [path] A `CYPHER_AST_PATTERN_PATH` node. + * @param [condition] A `CYPHER_AST_EXPRESSION` node. * @param [children] The children of the node. * @param [nchildren] The number of children. * @param [range] The input range. @@ -4889,8 +4902,8 @@ const cypher_astnode_t *cypher_ast_pattern_get_path( */ __cypherlang_must_check cypher_astnode_t *cypher_ast_named_path(const cypher_astnode_t *identifier, - const cypher_astnode_t *path, cypher_astnode_t **children, - unsigned int nchildren, struct cypher_input_range range); + const cypher_astnode_t *path, const cypher_astnode_t *condition, + cypher_astnode_t **children, unsigned int nchildren, struct cypher_input_range range); /** * Get the identifier from a `CYPHER_AST_NAMED_PATH` node. @@ -4918,6 +4931,8 @@ __cypherlang_pure const cypher_astnode_t *cypher_ast_named_path_get_path( const cypher_astnode_t *node); +const cypher_astnode_t *cypher_ast_named_path_get_condition( + const cypher_astnode_t *node); /** * Construct a `CYPHER_AST_SHORTEST_PATH` node. @@ -4967,7 +4982,7 @@ const cypher_astnode_t *cypher_ast_shortest_path_get_path( * Construct a `CYPHER_AST_PATTERN_PATH` node. * * @param [elements] The elements in the pattern, which must be of alternating - * types `CYPHER_AST_NODE_PATTERN` and `CYPHER_AST_REL_PATTERN`. + * types `CYPHER_AST_NODE_PATTERN` and (`CYPHER_AST_REL_PATTERN` or 'CYPHER_AST_PATH_PATTERN'). * @param [nelements] The number of elements in the path, which must be odd. * @param [children] The children of the node. * @param [nchildren] The number of children. @@ -4999,9 +5014,9 @@ unsigned int cypher_ast_pattern_path_nelements(const cypher_astnode_t *node); * * @param [node] The AST node. * @param [index] The index of the element. - * @return Either a `CYPHER_AST_NODE_PATTERN` node or a + * @return Either a `CYPHER_AST_NODE_PATTERN` node, 'CYPHER_AST_PATH_PATTERN' node or a * `CYPHER_AST_REL_PATTERN` node, depending on whether the index - * is even or odd respectively, or null if the index is larger than the + * is even or odd, or null if the index is larger than the * number of elements.. */ __cypherlang_pure @@ -5184,7 +5199,88 @@ __cypherlang_pure const cypher_astnode_t *cypher_ast_rel_pattern_get_properties( const cypher_astnode_t *node); +/** + * Construct a `CYPHER_AST_PATH_PATTERN` node. + * + * @param [expression] Path pattern expresison. + * @param [direction] The direction of the pattern. + * @param [children] The children of the node. + * @param [nchildren] The number of children. + * @return An AST node, or NULL if an error occurs (errno will be set). + */ +__cypherlang_must_check +cypher_astnode_t *cypher_ast_path_pattern(const cypher_astnode_t *expression, + enum cypher_rel_direction direction, cypher_astnode_t **children, unsigned int nchildren, + struct cypher_input_range range); + +cypher_astnode_t *cypher_ast_path_pattern_expression(cypher_astnode_t * const *elements, + unsigned int nelements, cypher_astnode_t **children, unsigned int nchildren, + struct cypher_input_range range); + +cypher_astnode_t *cypher_ast_path_pattern_alternative(cypher_astnode_t * const *elements, + unsigned int nelements, cypher_astnode_t **children, unsigned int nchildren, + struct cypher_input_range range); + +cypher_astnode_t *cypher_ast_path_pattern_base(enum cypher_rel_direction direction, const cypher_astnode_t *varlength, + const cypher_astnode_t *path_base, cypher_astnode_t **children, + unsigned int nchildren, struct cypher_input_range range); + +cypher_astnode_t *cypher_ast_path_pattern_edge(const cypher_astnode_t *reltype, + cypher_astnode_t **children, unsigned int nchildren, + struct cypher_input_range range); + +cypher_astnode_t *cypher_ast_path_pattern_reference(const cypher_astnode_t *identifier, + cypher_astnode_t **children, unsigned int nchildren, + struct cypher_input_range range); +/** + * Get the expression of a `CYPHER_AST_PATH_PATTERN` node. + * + * If the node is not an instance of `CYPHER_AST_PATH_PATTERN` then the result + * will be undefined. + * + * @param [node] The AST node. + * @return A `CYPHER_AST_PATH_PATTERN_EXPRESSION` node, or null. + */ +__cypherlang_pure +const cypher_astnode_t *cypher_ast_path_pattern_get_expression( + const cypher_astnode_t *node); + +/** + * Get the direction of a `CYPHER_AST_PATH_PATTERN` node. + * + * If the node is not an instance of `CYPHER_AST_PATH_PATTERN` then the result + * will be undefined. + * + * @param [node] The AST node. + * @return The direction of the relationship. + */ +__cypherlang_pure +enum cypher_rel_direction cypher_ast_path_pattern_get_direction( + const cypher_astnode_t *node); + +unsigned int cypher_ast_path_pattern_expression_get_nelements( + const cypher_astnode_t *astnode); + +const cypher_astnode_t *cypher_ast_path_pattern_expression_get_element( + const cypher_astnode_t *astnode, unsigned int index); + +unsigned int cypher_ast_path_pattern_alternative_get_nelements( + const cypher_astnode_t *astnode); + +const cypher_astnode_t *cypher_ast_path_pattern_alternative_get_element( + const cypher_astnode_t *astnode, unsigned int index); + +enum cypher_rel_direction cypher_ast_path_pattern_base_get_direction( + const cypher_astnode_t *astnode); + +const cypher_astnode_t *cypher_ast_path_pattern_base_get_child( + const cypher_astnode_t *astnode); + +const cypher_astnode_t *cypher_ast_path_pattern_edge_get_reltype( + const cypher_astnode_t *astnode); +const cypher_astnode_t *cypher_ast_path_pattern_reference_get_identifier( + const cypher_astnode_t *astnode); /** * Construct a `CYPHER_AST_RANGE` node. * diff --git a/lib/src/parser.c b/lib/src/parser.c index 0ea585d..a25267c 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -440,9 +440,11 @@ static cypher_astnode_t *_strbuf_index_name(yycontext *yy); static cypher_astnode_t *_strbuf_proc_name(yycontext *yy); #define pattern() _pattern(yy) static cypher_astnode_t *_pattern(yycontext *yy); -#define named_path(s, p) _named_path(yy, s, p) +#define named_path(s, p) _named_path(yy, s, p, NULL) +#define named_path_predicate(s, p, e) _named_path(yy, s, p, e) static cypher_astnode_t *_named_path(yycontext *yy, - cypher_astnode_t *identifier, cypher_astnode_t *path); + cypher_astnode_t *identifier, cypher_astnode_t *path, + cypher_astnode_t *condition); #define shortest_path(s, p) _shortest_path(yy, s, p) static cypher_astnode_t *_shortest_path(yycontext *yy, bool single, cypher_astnode_t *path); @@ -457,6 +459,23 @@ static cypher_astnode_t *_node_pattern(yycontext *yy, static cypher_astnode_t *_rel_pattern(yycontext *yy, enum cypher_rel_direction direction, cypher_astnode_t *identifier, cypher_astnode_t *varlength, cypher_astnode_t *properties); +#define path_pattern(e, d) _path_pattern(yy, e, CYPHER_REL_##d) +static cypher_astnode_t *_path_pattern(yycontext *yy, + cypher_astnode_t *expression, enum cypher_rel_direction direction); +#define path_pattern_expression() _path_pattern_expression(yy) +static cypher_astnode_t *_path_pattern_expression(yycontext *yy); +#define path_pattern_alternative() _path_pattern_alternative(yy) +static cypher_astnode_t *_path_pattern_alternative(yycontext *yy); +#define path_pattern_base(d, r, b) _path_pattern_base(yy, CYPHER_REL_##d, r, b) +static cypher_astnode_t *_path_pattern_base(yycontext *yy, + enum cypher_rel_direction direction, cypher_astnode_t *varlength, + cypher_astnode_t *path_base); +#define path_pattern_edge(r) _path_pattern_edge(yy, r) +static cypher_astnode_t *_path_pattern_edge(yycontext *yy, + cypher_astnode_t *reltype); +#define path_pattern_reference(i) _path_pattern_reference(yy, i) +static cypher_astnode_t *_path_pattern_reference(yycontext *yy, + cypher_astnode_t *identifier); #define range(s, e) _range(yy, s, e) static cypher_astnode_t *_range(yycontext *yy, cypher_astnode_t *start, cypher_astnode_t *end); @@ -2863,11 +2882,12 @@ cypher_astnode_t *_pattern(yycontext *yy) cypher_astnode_t *_named_path(yycontext *yy, - cypher_astnode_t *identifier, cypher_astnode_t *path) + cypher_astnode_t *identifier, cypher_astnode_t *path, + cypher_astnode_t *condition) { assert(yy->prev_block != NULL && "An AST node can only be created immediately after a `>` in the grammar"); - cypher_astnode_t *node = cypher_ast_named_path(identifier, path, + cypher_astnode_t *node = cypher_ast_named_path(identifier, path, condition, astnodes_elements(&(yy->prev_block->children)), astnodes_size(&(yy->prev_block->children)), yy->prev_block->range); @@ -2970,6 +2990,124 @@ cypher_astnode_t *_rel_pattern(yycontext *yy, return add_child(yy, node); } +cypher_astnode_t *_path_pattern(yycontext *yy, + cypher_astnode_t *expression, enum cypher_rel_direction direction) +{ + assert(yy->prev_block != NULL && + "An AST node can only be created immediately after a `>` in the grammar"); + cypher_astnode_t *node = cypher_ast_path_pattern(expression, direction, + astnodes_elements(&(yy->prev_block->children)), + astnodes_size(&(yy->prev_block->children)), + yy->prev_block->range); + if (node == NULL) + { + abort_parse(yy); + } + astnodes_clear(&(yy->prev_block->children)); + block_free(yy->prev_block); + yy->prev_block = NULL; + return add_child(yy, node); +} + +static cypher_astnode_t *_path_pattern_expression(yycontext *yy) { + assert(yy->prev_block != NULL && + "An AST node can only be created immediately after a `>` in the grammar"); + cypher_astnode_t *node = cypher_ast_path_pattern_expression( + astnodes_elements(&(yy->prev_block->sequence)), + astnodes_size(&(yy->prev_block->sequence)), + astnodes_elements(&(yy->prev_block->children)), + astnodes_size(&(yy->prev_block->children)), + yy->prev_block->range); + if (node == NULL) + { + abort_parse(yy); + } + astnodes_clear(&(yy->prev_block->sequence)); + astnodes_clear(&(yy->prev_block->children)); + block_free(yy->prev_block); + yy->prev_block = NULL; + return add_child(yy, node); +} + +static cypher_astnode_t *_path_pattern_alternative(yycontext *yy) { + assert(yy->prev_block != NULL && + "An AST node can only be created immediately after a `>` in the grammar"); + cypher_astnode_t *node = cypher_ast_path_pattern_alternative( + astnodes_elements(&(yy->prev_block->sequence)), + astnodes_size(&(yy->prev_block->sequence)), + astnodes_elements(&(yy->prev_block->children)), + astnodes_size(&(yy->prev_block->children)), + yy->prev_block->range); + if (node == NULL) + { + abort_parse(yy); + } + astnodes_clear(&(yy->prev_block->sequence)); + astnodes_clear(&(yy->prev_block->children)); + block_free(yy->prev_block); + yy->prev_block = NULL; + return add_child(yy, node); +} + +static cypher_astnode_t *_path_pattern_base(yycontext *yy, + enum cypher_rel_direction direction, cypher_astnode_t *varlength, + cypher_astnode_t *path_base) +{ + assert(yy->prev_block != NULL && + "An AST node can only be created immediately after a `>` in the grammar"); + cypher_astnode_t *node = cypher_ast_path_pattern_base(direction, + varlength, + path_base, + astnodes_elements(&(yy->prev_block->children)), + astnodes_size(&(yy->prev_block->children)), + yy->prev_block->range); + if (node == NULL) + { + abort_parse(yy); + } + astnodes_clear(&(yy->prev_block->sequence)); + astnodes_clear(&(yy->prev_block->children)); + block_free(yy->prev_block); + yy->prev_block = NULL; + return add_child(yy, node); +} + +static cypher_astnode_t *_path_pattern_edge(yycontext *yy, cypher_astnode_t *reltype) { + assert(yy->prev_block != NULL && + "An AST node can only be created immediately after a `>` in the grammar"); + cypher_astnode_t *node = cypher_ast_path_pattern_edge(reltype, + astnodes_elements(&(yy->prev_block->children)), + astnodes_size(&(yy->prev_block->children)), + yy->prev_block->range); + if (node == NULL) + { + abort_parse(yy); + } + astnodes_clear(&(yy->prev_block->sequence)); + astnodes_clear(&(yy->prev_block->children)); + block_free(yy->prev_block); + yy->prev_block = NULL; + return add_child(yy, node); +} + +static cypher_astnode_t *_path_pattern_reference(yycontext *yy, cypher_astnode_t *identifier) { + printf("!!!!!!!!!!!!\n"); + assert(yy->prev_block != NULL && + "An AST node can only be created immediately after a `>` in the grammar"); + cypher_astnode_t *node = cypher_ast_path_pattern_reference(identifier, + astnodes_elements(&(yy->prev_block->children)), + astnodes_size(&(yy->prev_block->children)), + yy->prev_block->range); + if (node == NULL) + { + abort_parse(yy); + } + astnodes_clear(&(yy->prev_block->sequence)); + astnodes_clear(&(yy->prev_block->children)); + block_free(yy->prev_block); + yy->prev_block = NULL; + return add_child(yy, node); +} cypher_astnode_t *_range(yycontext *yy, cypher_astnode_t *start, cypher_astnode_t *end) diff --git a/lib/src/parser.leg b/lib/src/parser.leg index b9f14da..d066529 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -84,6 +84,11 @@ schema-command = ) ~{ERR("a schema command")} (SEMICOLON | EOF) +named-path-clause = + < PATHPATTERN i:identifier EQUAL - p:pattern-path - + (WHERE e:expression | e:_null_) + > { $$ = named_path_predicate(i, p, e); } + create-index = < CREATE-INDEX-ON l:label LEFT-PAREN - p:prop-name { sequence_add(p); } @@ -166,6 +171,7 @@ clause = | loadcsv-clause | start-clause | union-clause + | named-path-clause ) ~{ERR("a clause")} loadcsv-clause = @@ -726,7 +732,7 @@ shortest-path-pattern = pattern-path = < n:node-pattern { sequence_add(n); } - ( - r:relationship-pattern - n:node-pattern + ( -( r:relationship-pattern | r:path-pattern ) - n:node-pattern { sequence_add(r); sequence_add(n); } )* > { $$ = pattern_path(); } @@ -736,7 +742,7 @@ pattern-expression = pattern-expression-path = < n:node-pattern { sequence_add(n); } - ( - r:relationship-pattern - n:node-pattern + ( - ( r:relationship-pattern | r:path-pattern ) - n:node-pattern { sequence_add(r); sequence_add(n); } )+ > { $$ = pattern_path(); } @@ -746,6 +752,69 @@ node-pattern = < LEFT-PAREN - )* (p:pattern-properties | p:_null_) RIGHT-PAREN > { $$ = node_pattern(i, p); } +path-pattern = + < ( LEFT-ARROW-HEAD - DASH - DIV - e:path-expression - DIV - DASH + ( - RIGHT-ARROW-HEAD > { $$ = path_pattern(e, BIDIRECTIONAL); } + | _empty_ > { $$ = path_pattern(e, INBOUND); } + ) + | DASH - DIV - e:path-expression - DIV - DASH + ( - RIGHT-ARROW-HEAD > { $$ = path_pattern(e, OUTBOUND); } + | _empty_ > { $$ = path_pattern(e, BIDIRECTIONAL); } + ) + ) + +path-expression = + < p:path-alternative { sequence_add(p); } + (- p:path-alternative { sequence_add(p); } + )* > { $$ = path_pattern_expression(); } + +path-alternative = + < p:path-repetition { sequence_add(p); } + (- PIPE - p:path-repetition { sequence_add(p); } + )* > { $$ = path_pattern_alternative(); } + +path-repetition = + < ( LEFT-ARROW-HEAD - p:path-base + ( - RIGHT-ARROW-HEAD + - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(BIDIRECTIONAL, l, p); } + | _empty_ + - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(INBOUND, l, p); } + ) + | p:path-base + ( - RIGHT-ARROW-HEAD + - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(OUTBOUND, l, p); } + | _empty_ + - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(OUTBOUND, l, p); } + ) + ) + +# TODO: make ast nodes for label, path-group, ..., path-reference +path-base = + e:path-edge { $$ = e; } + | p:path-group { $$ = p; } + | p:path-reference { $$ = p; } +# | path-any { } +# | p:path-node { $$ = p; } +# - + +path-edge = + < r:rel-type > { $$ = path_pattern_edge(r); } + +path-group = + LEFT-SQ-PAREN - p:path-expression - RIGHT-SQ-PAREN { $$ = p; } + +path-any = + < DASH > + +path-node = + < LEFT-PAREN - + ( n:label { sequence_add(n); } + )* ( p:pattern-properties | p:_null_ ) + RIGHT-PAREN > { $$ = node_pattern(NULL, p); } + +path-reference = + < '~' - i:identifier > { path_pattern_reference(i); } + relationship-pattern = < ( LEFT-ARROW-HEAD - DASH - ( DASH @@ -937,6 +1006,7 @@ IS-NULL = ([Ii][Ss] WB - [Nn][Uu][Ll][Ll] WB) ~{ERR("IS NULL")} IS-NOT-NULL = ([Ii][Ss] WB - [Nn][Oo][Tt] WB - [Nn][Uu][Ll][Ll] WB) ~{ERR("IS NOT NULL")} DOT = '.' ~{ERR("'.'")} DOT-STAR = ('.' - '*') ~{ERR("'.*'")} +QUESTIONMARK = '?' ~{ERR("'?'")} COLON = ':' ~{ERR("':'")} SEMICOLON = ';' ~{ERR("';'")} @@ -995,6 +1065,7 @@ WITH = [Ww]([Ii][Tt][Hh] WB -) ~{ERR("WITH")} CALL = [Cc]([Aa][Ll][Ll] WB -) ~{ERR("CALL")} RETURN = [Rr]([Ee][Tt][Uu][Rr][Nn] WB -) ~{ERR("RETURN")} UNION = [Uu]([Nn][Ii][Oo][Nn] WB -) ~{ERR("UNION")} +PATHPATTERN = [Pp]([Aa][Tt][Hh] WB - [Pp][Aa][Tt][Tt][Ee][Rr][Nn] WB -) ~{ERR("PATH PATTERN")} node = ([Nn][Oo][Dd][Ee] WB -) ~{ERR("node")} relationship = ([Rr][Ee][Ll][Aa][Tt][Ii][Oo][Nn][Ss][Hh][Ii][Pp] WB -)