diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am index 59b6bff..d2bd989 100644 --- a/lib/src/Makefile.am +++ b/lib/src/Makefile.am @@ -66,6 +66,13 @@ libcypher_parser_la_SOURCES = \ ast_on_match.c \ ast_order_by.c \ ast_parameter.c \ + ast_path_pattern.c \ + ast_path_pattern_any.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 \ @@ -78,6 +85,8 @@ libcypher_parser_la_SOURCES = \ ast_query_clause.c \ ast_query_option.c \ ast_range.c \ + ast_range_plus.c \ + ast_range_optional.c \ ast_reduce.c \ ast_rel_id_lookup.c \ ast_rel_index_lookup.c \ diff --git a/lib/src/ast.c b/lib/src/ast.c index 9ece2b5..db033ed 100644 --- a/lib/src/ast.c +++ b/lib/src/ast.c @@ -125,7 +125,16 @@ 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_any; + 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 *range_plus; + const struct cypher_astnode_vt *range_optional; const struct cypher_astnode_vt *command; const struct cypher_astnode_vt *comment; const struct cypher_astnode_vt *line_comment; @@ -242,7 +251,16 @@ 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_any = &cypher_path_pattern_any_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, + .range_plus = &cypher_range_plus_astnode_vt, + .range_optional = &cypher_range_optional_astnode_vt, .command = &cypher_command_astnode_vt, .line_comment = &cypher_line_comment_astnode_vt, .block_comment = &cypher_block_comment_astnode_vt, @@ -369,7 +387,16 @@ 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_ANY = VT_OFFSET(path_pattern_any); +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_RANGE_PLUS = VT_OFFSET(range_plus); +const uint8_t CYPHER_AST_RANGE_OPTIONAL = VT_OFFSET(range_optional); const uint8_t CYPHER_AST_COMMAND = VT_OFFSET(command); const uint8_t CYPHER_AST_COMMENT = VT_OFFSET(comment); const uint8_t CYPHER_AST_LINE_COMMENT = VT_OFFSET(line_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..dc81978 --- /dev/null +++ b/lib/src/ast_path_pattern.c @@ -0,0 +1,111 @@ +#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..bacef42 --- /dev/null +++ b/lib/src/ast_path_pattern_alternative.c @@ -0,0 +1,115 @@ +#include "../../config.h" +#include "astnode.h" +#include "util.h" +#include + +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_ALTERNATIVE, 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) { + struct path_pattern_alternative *node = container_of(self, struct path_pattern_alternative, _astnode); + + size_t n = 0; + for (unsigned int i = 0; i < node->nelements; ++i) + { + ssize_t r; + if (i < node->nelements - 1) + { + 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) + { + return -1; + } + n += r; + } + + return n; +} \ No newline at end of file diff --git a/lib/src/ast_path_pattern_any.c b/lib/src/ast_path_pattern_any.c new file mode 100644 index 0000000..a1d9e51 --- /dev/null +++ b/lib/src/ast_path_pattern_any.c @@ -0,0 +1,59 @@ +#include "../../config.h" +#include "astnode.h" +#include "util.h" +#include + +struct path_pattern_any +{ + cypher_astnode_t _astnode; +}; + +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_any_astnode_vt = + { .name = "path pattern any", + .detailstr = detailstr, + .release = cypher_astnode_release, + .clone = clone }; + + +cypher_astnode_t *cypher_ast_path_pattern_any(cypher_astnode_t **children, unsigned int nchildren, + struct cypher_input_range range) +{ + struct path_pattern_any *node = calloc(1, sizeof(struct path_pattern_any)); + if (node == NULL) + { + return NULL; + } + if (cypher_astnode_init(&(node->_astnode), CYPHER_AST_PATH_PATTERN_ANY, + NULL, 0, range)) + { + goto cleanup; + } + 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_ANY, NULL); + cypher_astnode_t *clone = cypher_ast_path_pattern_any(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_path_pattern_base.c b/lib/src/ast_path_pattern_base.c new file mode 100644 index 0000000..7d389a0 --- /dev/null +++ b/lib/src/ast_path_pattern_base.c @@ -0,0 +1,141 @@ +#include "../../config.h" +#include "astnode.h" +#include "util.h" +#include + +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_varlength( + 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->varlength; +} + +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) { + struct path_pattern_base *node = container_of(self, struct path_pattern_base, _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->path_base != NULL) + { + r = snprintf(str+n, (n < size)? size-n : 0, "@%u", + node->path_base->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; + + if (node->varlength != NULL) + { + r = snprintf(str+n, (n < size)? size-n : 0, " range=@%u", + node->varlength->ordinal); + if (r < 0) + { + return -1; + } + n += r; + } + return n; +} \ 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..12483f5 --- /dev/null +++ b/lib/src/ast_path_pattern_edge.c @@ -0,0 +1,78 @@ +#include "../../config.h" +#include "astnode.h" +#include "util.h" +#include + +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) { + struct path_pattern_edge *node = container_of(self, struct path_pattern_edge, _astnode); + return snprintf(str, size, "edge label=@%u", node->reltype->ordinal); +} diff --git a/lib/src/ast_path_pattern_expression.c b/lib/src/ast_path_pattern_expression.c new file mode 100644 index 0000000..c64e826 --- /dev/null +++ b/lib/src/ast_path_pattern_expression.c @@ -0,0 +1,132 @@ +#include "../../config.h" +#include "astnode.h" +#include "util.h" +#include + +struct path_pattern_expression +{ + cypher_astnode_t _astnode; + size_t nelements; + const cypher_astnode_t *properties; + 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 = "path expression", + .detailstr = detailstr, + .release = cypher_astnode_release, + .clone = clone }; + +cypher_astnode_t *cypher_ast_path_pattern_expression(const cypher_astnode_t *properties, + 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->properties = properties; + 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]; +} + +const cypher_astnode_t *cypher_ast_path_pattern_get_properties( + 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->properties; +} + +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 *properties = (node->properties == NULL) ? NULL : + children[child_index(self, node->properties)]; + + cypher_astnode_t *clone = cypher_ast_path_pattern_expression(properties, elements, node->nelements, children, self->nchildren, + self->range); + return clone; +} + +ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) { + struct path_pattern_expression *node = container_of(self, struct path_pattern_expression, _astnode); + + size_t n = 0; + ssize_t r; + for (unsigned int i = 0; i < node->nelements; ++i) + { + r = snprintf(str+n, (n < size)? size-n : 0, "@%u ", + node->elements[i]->ordinal); + if (r < 0) + { + return -1; + } + n += r; + } + + if (node->properties != NULL) + { + r = snprintf(str+n, (n < size)? size-n : 0, "{%u}", + node->properties->ordinal); + + if (r < 0) + { + return -1; + } + n += r; + } + + return n; +} \ 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..5fd1231 --- /dev/null +++ b/lib/src/ast_path_pattern_reference.c @@ -0,0 +1,76 @@ +#include "../../config.h" +#include "astnode.h" +#include "util.h" +#include + +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); + + return clone; +} + +ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) { + struct path_pattern_reference *node = container_of(self, struct path_pattern_reference, _astnode); + return snprintf(str, size, "~@%u", node->identifier->ordinal); +} 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/ast_range_optional.c b/lib/src/ast_range_optional.c new file mode 100644 index 0000000..a9e7bff --- /dev/null +++ b/lib/src/ast_range_optional.c @@ -0,0 +1,59 @@ +#include "../../config.h" +#include "astnode.h" +#include "operators.h" +#include "util.h" +#include + + +struct range_optional +{ + cypher_astnode_t _astnode; +}; + + +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_range_optional_astnode_vt = + { .name = "optional", + .detailstr = detailstr, + .release = cypher_astnode_release, + .clone = clone }; + + +cypher_astnode_t *cypher_ast_range_optional(cypher_astnode_t **children, + unsigned int nchildren, struct cypher_input_range range) +{ + struct range_optional *node = calloc(1, sizeof(struct range_optional)); + if (node == NULL) + { + return NULL; + } + if (cypher_astnode_init(&(node->_astnode), CYPHER_AST_RANGE_OPTIONAL, + NULL, 0, range)) + { + free(node); + return NULL; + } + return &(node->_astnode); +} + + +cypher_astnode_t *clone(const cypher_astnode_t *self, + cypher_astnode_t **children) +{ + REQUIRE_TYPE(self, CYPHER_AST_RANGE_OPTIONAL, NULL); + cypher_astnode_t *clone = cypher_ast_range_optional(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) +{ + REQUIRE_TYPE(self, CYPHER_AST_RANGE_OPTIONAL, -1); + return snprintf(str, size, "?"); +} diff --git a/lib/src/ast_range_plus.c b/lib/src/ast_range_plus.c new file mode 100644 index 0000000..05f3563 --- /dev/null +++ b/lib/src/ast_range_plus.c @@ -0,0 +1,59 @@ +#include "../../config.h" +#include "astnode.h" +#include "operators.h" +#include "util.h" +#include + + +struct range_plus +{ + cypher_astnode_t _astnode; +}; + + +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_range_plus_astnode_vt = + { .name = "one or more", + .detailstr = detailstr, + .release = cypher_astnode_release, + .clone = clone }; + + +cypher_astnode_t *cypher_ast_range_plus(cypher_astnode_t **children, + unsigned int nchildren, struct cypher_input_range range) +{ + struct range_plus *node = calloc(1, sizeof(struct range_plus)); + if (node == NULL) + { + return NULL; + } + if (cypher_astnode_init(&(node->_astnode), CYPHER_AST_RANGE_PLUS, + NULL, 0, range)) + { + free(node); + return NULL; + } + return &(node->_astnode); +} + + +cypher_astnode_t *clone(const cypher_astnode_t *self, + cypher_astnode_t **children) +{ + REQUIRE_TYPE(self, CYPHER_AST_RANGE_PLUS, NULL); + cypher_astnode_t *clone = cypher_ast_range_plus(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) +{ + REQUIRE_TYPE(self, CYPHER_AST_RANGE_PLUS, -1); + return snprintf(str, size, "+"); +} diff --git a/lib/src/ast_rel_pattern.c b/lib/src/ast_rel_pattern.c index 7f83143..ece9d15 100644 --- a/lib/src/ast_rel_pattern.c +++ b/lib/src/ast_rel_pattern.c @@ -58,9 +58,10 @@ cypher_astnode_t *cypher_ast_rel_pattern(enum cypher_rel_direction direction, cypher_astnode_instanceof(properties, CYPHER_AST_MAP) || cypher_astnode_instanceof(properties, CYPHER_AST_PARAMETER), NULL); REQUIRE_CONTAINS_OPTIONAL(children, nchildren, properties, NULL); - REQUIRE_CHILD_OPTIONAL(children, nchildren, varlength, - CYPHER_AST_RANGE, NULL); - + REQUIRE(varlength == NULL || + cypher_astnode_instanceof(varlength, CYPHER_AST_RANGE) || + cypher_astnode_instanceof(varlength, CYPHER_AST_RANGE_PLUS) || + cypher_astnode_instanceof(varlength, CYPHER_AST_RANGE_OPTIONAL), NULL); struct rel_pattern *node = calloc(1, sizeof(struct rel_pattern) + nreltypes * sizeof(cypher_astnode_t *)); if (node == NULL) diff --git a/lib/src/astnode.h b/lib/src/astnode.h index 8337fc3..05fc862 100644 --- a/lib/src/astnode.h +++ b/lib/src/astnode.h @@ -238,7 +238,16 @@ 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_any_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_range_plus_astnode_vt; +extern const struct cypher_astnode_vt cypher_range_optional_astnode_vt; extern const struct cypher_astnode_vt cypher_command_astnode_vt; extern const struct cypher_astnode_vt cypher_comment_astnode_vt; extern const struct cypher_astnode_vt cypher_line_comment_astnode_vt; diff --git a/lib/src/cypher-parser.h.in b/lib/src/cypher-parser.h.in index b4a89dd..3db1df5 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -336,8 +336,26 @@ 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 any node */ +extern const cypher_astnode_type_t CYPHER_AST_PATH_PATTERN_ANY; +/** 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 range plus node. */ +extern const cypher_astnode_type_t CYPHER_AST_RANGE_PLUS; +/** Type for an AST optional node. */ +extern const cypher_astnode_type_t CYPHER_AST_RANGE_OPTIONAL; /** Type for an AST command node. */ extern const cypher_astnode_type_t CYPHER_AST_COMMAND; /** Type for an AST comment node. */ @@ -4882,6 +4900,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 +4908,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 +4937,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 +4988,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 +5020,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,6 +5205,211 @@ __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. + * @param [range] The input range. + * @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); + +/** + * 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); + +/** + * 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 properties 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_MAP` node, a `CYPHER_AST_PARAMETER` node, or null. + */ +const cypher_astnode_t *cypher_ast_path_pattern_get_properties( + const cypher_astnode_t *node); + +/** + * Construct a 'CYPHER_AST_PATH_PATTERN_ANY' node. + * + * @param [children] The children of the node. + * @param [nchildren] The number of children. + * @param [range] The input range. + * @return An AST node, or NULL if an error occurs (errno will be set). + */ +cypher_astnode_t *cypher_ast_path_pattern_any(cypher_astnode_t **children, unsigned int nchildren, + struct cypher_input_range range); + +/** + * Construct a 'CYPHER_AST_PATH_PATTERN_EXPRESISON' node. + * @param [properties] Pattern expression properties. + * @param [elements] 'CYPHER_AST_PATH_PATTERN_ALTERNATIVE' nodes. + * @param [nelements] The number of elements. + * @param [children] The children of the node. + * @param [nchildren] The number of children. + * @param [range] The input range. + * @return An AST node, or NULL if an error occurs (errno will be set). + */ +cypher_astnode_t *cypher_ast_path_pattern_expression(const cypher_astnode_t *properties, + cypher_astnode_t * const *elements, unsigned int nelements, cypher_astnode_t **children, + unsigned int nchildren, struct cypher_input_range range); + +/** + * Get the amount of elements of a 'CYPHER_AST_PATH_PATTERN_EXPRESSION' node. + * @param [node] The AST node. + * @return The amount of elements in the node. + */ +unsigned int cypher_ast_path_pattern_expression_get_nelements( + const cypher_astnode_t *node); + +/** + * Get an element of 'CYPHER_AST_PATH_PATTERN_EXPRESSION' node. + * @param [node] The AST node. + * @param [index] The index of desired element. + * @return An AST node, or NULL if an error occurs (errno will be set). + */ +const cypher_astnode_t *cypher_ast_path_pattern_expression_get_element( + const cypher_astnode_t *node, unsigned int index); + +/** + * Construct a 'CYPHER_AST_PATH_PATTERN_ALTERNATIVE' node. + * @param [elements] 'CYPHER_AST_PATH_PATTERN_BASE' nodes. + * @param [nelements] The number of elements. + * @param [children] The children of the node. + * @param [nchildren] The number of children. + * @param [range] The input range. + * @return An AST node, or NULL if an error occurs (errno will be set). + */ +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); + +/** + * Get the amount of elemets of a 'CYPHER_AST_PATH_PATTERN_ALTERNATIVE' node. + * @param [node] The AST node. + * @return The amount of elements in the node. + */ +unsigned int cypher_ast_path_pattern_alternative_get_nelements( + const cypher_astnode_t *node); + +/** + * Get an element of a 'CYPHER_AST_PATH_PATTERN_ALTERNATIVE' node. + * @param [node] The AST node. + * @return An AST node, or NULL if an error occurs (errno will be set). + */ +const cypher_astnode_t *cypher_ast_path_pattern_alternative_get_element( + const cypher_astnode_t *astnode, unsigned int index); + +/** + * Construct a `CYPHER_AST_PATH_PATTERN_BASE` node. + * + * @param [direction] The direction of the element. + * @param [varlength] A `CYPHER_AST_RANGE` node, or null. + * @param [path_base] A 'CYPHER_AST_PATH_PATTERN_BASE' node. + * @param [children] The children of the node. + * @param [nchildren] The number of children. + * @param [range] The input range. + * @return An AST node, or NULL if an error occurs (errno will be set). + */ +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); + +/** + * Get the direction of a 'CYPHER_AST_PATH_PATTERN_BASE' node. + * @param [node] The AST node. + * @return The direction of the path base. + */ +enum cypher_rel_direction cypher_ast_path_pattern_base_get_direction( + const cypher_astnode_t *node); + +/** + * Get the varlength of a 'CYPHER_AST_PATH_PATTERN_BASE' node. + * @param [node] The AST node. + * @return The direction of the path base. + */ +const cypher_astnode_t *cypher_ast_path_pattern_base_get_varlength( + const cypher_astnode_t *node); + + +/** + * Get the child of a 'CYPHER_AST_PATH_PATTERN_BASE' node. + * @param [node] The AST node. + * @return An AST node, or NULL if an error occurs (errno will be set). + */ +const cypher_astnode_t *cypher_ast_path_pattern_base_get_child( + const cypher_astnode_t *node); + +/** + * Construct a 'CYPHER_AST_PATH_PATTERN_EDGE' node. + * @param [reltype] The relationship type of the edge. + * @param [children] The children of the node. + * @param [nchildren] The number of children. + * @param [range] The input range. + * @return An AST node, or NULL if an error occurs (errno will be set). + */ +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); + +/** + * Get the relationship type of a 'CYPHER_AST_PATH_PATTERN_EDGE' node. + * @param [node] The AST node. + * @return The 'CYPHER_AST_RELTYPE' node, or NULL. + */ +const cypher_astnode_t *cypher_ast_path_pattern_edge_get_reltype( + const cypher_astnode_t *node); + +/** + * Cpnstruct a 'CYPHER_AST_PATH_PATTERN_REFERENCE' node. + * @param [identifier] A 'CYPHER_AST_IDENTIFIER' node. + * @param [children] The children of the node. + * @param [nchildren] The number of children. + * @param [range] The input range. + * @return An AST node, or NULL if an error occurs (errno will be set). + */ +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 identifier of a 'CYPHER_AST_PATH_PATTERN_REFERENCE' node. + * @param [node] The AST node. + * @return An AST node, or NULL if an error occurs (errno will be set). + */ +const cypher_astnode_t *cypher_ast_path_pattern_reference_get_identifier( + const cypher_astnode_t *node); + + /** * Construct a `CYPHER_AST_RANGE` node. @@ -5226,6 +5452,27 @@ __cypherlang_pure const cypher_astnode_t *cypher_ast_range_get_end( const cypher_astnode_t *node); +/** + * Construct a `CYPHER_AST_RANGE_PLUS` node. + * + * @param [children] The children of the node. + * @param [nchildren] The number of children. + * @param [range] The input range. + * @return An AST node, or NULL if an error occurs (errno will be set). + */ +cypher_astnode_t *cypher_ast_range_plus(cypher_astnode_t **children, + unsigned int nchildren, struct cypher_input_range range); + +/** + * Construct a `CYPHER_AST_RANGE_OPTIONAL` node. + * + * @param [children] The children of the node. + * @param [nchildren] The number of children. + * @param [range] The input range. + * @return An AST node, or NULL if an error occurs (errno will be set). + */ +cypher_astnode_t *cypher_ast_range_optional(cypher_astnode_t **children, + unsigned int nchildren, struct cypher_input_range range); /** * Construct a `CYPHER_AST_COMMAND` node. diff --git a/lib/src/parser.c b/lib/src/parser.c index 0ea585d..74de936 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,9 +459,32 @@ 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_any() _path_pattern_any(yy) +static cypher_astnode_t *_path_pattern_any(yycontext *yy); +#define path_pattern_expression(e) _path_pattern_expression(yy, e) +static cypher_astnode_t *_path_pattern_expression(yycontext *yy, cypher_astnode_t *properties); +#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); +#define range_plus() _range_plus(yy) +static cypher_astnode_t *_range_plus(yycontext *yy); +#define range_optional() _range_optional(yy) +static cypher_astnode_t *_range_optional(yycontext *yy); #define command(name) _command(yy, name) static cypher_astnode_t *_command(yycontext *yy, cypher_astnode_t *name); #define string(s, n) _string(yy, s, n) @@ -2863,11 +2888,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 +2996,139 @@ 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_any(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_any( + 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, cypher_astnode_t *properties) { + 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(properties, + 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) { + 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->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) @@ -2990,6 +3149,41 @@ cypher_astnode_t *_range(yycontext *yy, cypher_astnode_t *start, return add_child(yy, node); } +cypher_astnode_t *_range_plus(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_range_plus( + 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); +} + +cypher_astnode_t *_range_optional(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_range_optional( + 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); +} cypher_astnode_t *_command(yycontext *yy, cypher_astnode_t *name) { diff --git a/lib/src/parser.leg b/lib/src/parser.leg index b9f14da..0589ec5 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); } + )* - ( e:pattern-properties | e:_null_ ) > { $$ = path_pattern_expression(e); } + +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:rel-plus | l:rel-optional | l:_null_) > { $$ = path_pattern_base(BIDIRECTIONAL, l, p); } + | _empty_ + - (l:rel-varlength | l:rel-plus | l:rel-optional | l:_null_) > { $$ = path_pattern_base(INBOUND, l, p); } + ) + | p:path-base + ( - RIGHT-ARROW-HEAD + - (l:rel-varlength | l:rel-plus | l:rel-optional | l:_null_) > { $$ = path_pattern_base(OUTBOUND, l, p); } + | _empty_ + - (l:rel-varlength | l:rel-plus | l:rel-optional | l:_null_) > { $$ = path_pattern_base(BIDIRECTIONAL, l, p); } + ) + ) + +path-base = + p:path-edge { $$ = p; } + | p:path-group { $$ = p; } + | p:path-reference { $$ = p; } + | path-any { $$ = p; } + | 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_pattern_any(); } + +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 @@ -753,7 +822,7 @@ relationship-pattern = | _empty_ > { $$ = simple_rel_pattern(INBOUND); } ) | LEFT-SQ-PAREN - (i:identifier | i:_null_) - rel-types? (l:rel-varlength | l:_null_) + rel-types? (l:rel-varlength | l:rel-plus | l:rel-optional | l:_null_) (p:pattern-properties | p:_null_) RIGHT-SQ-PAREN - DASH ( - RIGHT-ARROW-HEAD > { $$ = rel_pattern(BIDIRECTIONAL, i, l, p); } | _empty_ > { $$ = rel_pattern(INBOUND, i, l, p); } @@ -765,7 +834,7 @@ relationship-pattern = | _empty_ > { $$ = simple_rel_pattern(BIDIRECTIONAL); } ) | LEFT-SQ-PAREN - (i:identifier | i:_null_) - rel-types? (l:rel-varlength | l:_null_) + rel-types? (l:rel-varlength | l:rel-plus | l:rel-optional | l:_null_) (p:pattern-properties | p:_null_) RIGHT-SQ-PAREN - DASH ( - RIGHT-ARROW-HEAD > { $$ = rel_pattern(OUTBOUND, i, l, p); } | _empty_ > { $$ = rel_pattern(BIDIRECTIONAL, i, l, p); } @@ -788,6 +857,12 @@ rel-varlength = | < STAR - > { $$ = range(NULL, NULL); } ) +rel-plus = + < PLUS > { $$ = range_plus(); } + +rel-optional = + < QUESTIONMARK > { $$ = range_optional(); } + pattern-properties = map-literal | parameter @@ -937,6 +1012,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 +1071,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 -) diff --git a/lib/test/check_pattern.c b/lib/test/check_pattern.c index 16119f2..d0d9bea 100644 --- a/lib/test/check_pattern.c +++ b/lib/test/check_pattern.c @@ -1105,6 +1105,210 @@ START_TEST (parse_all_shortest_paths) } END_TEST +START_TEST (parse_simple_path_pattern) +{ + struct cypher_input_position last = cypher_input_position_zero; + result = cypher_parse("MATCH (me)-/~cousin/-(my_cousin);", + &last, NULL, 0); + ck_assert_ptr_ne(result, NULL); + ck_assert_int_eq(last.offset, 32); + + ck_assert(cypher_parse_result_fprint_ast(result, memstream, 0, NULL, 0) == 0); + fflush(memstream); + const char *expected = "\n" +" @0 0..33 statement body=@1\n" +" @1 0..33 > query clauses=[@2]\n" +" @2 0..33 > > MATCH pattern=@3\n" +" @3 6..32 > > > pattern paths=[@4]\n" +" @4 6..32 > > > > pattern path (@5)-/@7/-(@13)\n" +" @5 6..10 > > > > > node pattern (@6)\n" +" @6 7..9 > > > > > > identifier `me`\n" +" @7 10..21 > > > > > path pattern -/@8/-\n" +" @8 12..19 > > > > > > path expression @9\n" +" @9 12..19 > > > > > > > alternative @10\n" +"@10 12..19 > > > > > > > > path base @11\n" +"@11 12..19 > > > > > > > > > path pattern reference ~@12\n" +"@12 13..19 > > > > > > > > > > identifier `cousin`\n" +"@13 21..32 > > > > > node pattern (@14)\n" +"@14 22..31 > > > > > > identifier `my_cousin`\n"; + ck_assert_str_eq(memstream_buffer, expected); + + const cypher_astnode_t *ast = cypher_parse_result_get_directive(result, 0); + const cypher_astnode_t *query = cypher_ast_statement_get_body(ast); + const cypher_astnode_t *match = cypher_ast_query_get_clause(query, 0); + + const cypher_astnode_t *pattern = cypher_ast_match_get_pattern(match); + ck_assert_int_eq(cypher_astnode_type(pattern), CYPHER_AST_PATTERN); + + ck_assert_int_eq(cypher_ast_pattern_npaths(pattern), 1); + const cypher_astnode_t *path = cypher_ast_pattern_get_path(pattern, 0); + ck_assert_int_eq(cypher_astnode_type(path), CYPHER_AST_PATTERN_PATH); + + ck_assert_int_eq(cypher_ast_pattern_path_nelements(path), 3); + const cypher_astnode_t *node = cypher_ast_pattern_path_get_element(path, 0); + ck_assert_int_eq(cypher_astnode_type(node), CYPHER_AST_NODE_PATTERN); + + const cypher_astnode_t *id = cypher_ast_node_pattern_get_identifier(node); + ck_assert_int_eq(cypher_astnode_type(id), CYPHER_AST_IDENTIFIER); + ck_assert_str_eq(cypher_ast_identifier_get_name(id), "me"); + + ck_assert_int_eq(cypher_ast_node_pattern_nlabels(node), 0); + ck_assert_ptr_eq(cypher_ast_node_pattern_get_label(node, 0), NULL); + ck_assert_ptr_eq(cypher_ast_node_pattern_get_properties(node), NULL); + + const cypher_astnode_t *path_pattern = cypher_ast_pattern_path_get_element(path, 1); + ck_assert_int_eq(cypher_astnode_type(path), CYPHER_AST_PATH_PATTERN); + + ck_assert_int_eq(cypher_ast_path_pattern_get_direction(path_pattern), + CYPHER_REL_BIDIRECTIONAL); + + const cypher_astnode_t *expr = cypher_ast_path_pattern_get_expression(path_pattern); + ck_assert_int_eq(cypher_astnode_type(expr), CYPHER_AST_PATH_PATTERN_EXPRESSION); + ck_assert_int_eq(cypher_ast_path_pattern_expression_get_nelements(expr), 1); + ck_assert_ptr_eq(cypher_ast_path_pattern_expression_get_element(expr, 1), NULL); + + const cypher_astnode_t *alt = cypher_ast_path_pattern_expression_get_element(expr, 0); + ck_assert_int_eq(cypher_astnode_type(alt), CYPHER_AST_PATH_PATTERN_ALTERNATIVE); + ck_assert_int_eq(cypher_ast_path_pattern_alternative_get_nelements(alt), 1); + ck_assert_ptr_eq(cypher_ast_path_pattern_alternative_get_element(alt, 1), NULL); + + const cypher_astnode_t *base = cypher_ast_path_pattern_alternative_get_element(alt, 0); + ck_assert_int_eq(cypher_astnode_type(base), CYPHER_AST_PATH_PATTERN_BASE); + ck_assert_int_eq(cypher_ast_path_pattern_base_get_direction(base), CYPHER_REL_BIDIRECTIONAL); + + const cypher_astnode_t *ref = cypher_ast_path_pattern_base_get_child(base); + const cypher_astnode_t *id_ref = cypher_ast_path_pattern_reference_get_identifier(ref); + ck_assert_str_eq(cypher_ast_identifier_get_name(id_ref), "cousin"); + + const cypher_astnode_t *node2 = cypher_ast_pattern_path_get_element(path, 2); + ck_assert_int_eq(cypher_astnode_type(node2), CYPHER_AST_NODE_PATTERN); + + const cypher_astnode_t *id2 = cypher_ast_node_pattern_get_identifier(node2); + ck_assert_int_eq(cypher_astnode_type(id2), CYPHER_AST_IDENTIFIER); + ck_assert_str_eq(cypher_ast_identifier_get_name(id2), "my_cousin"); +} +END_TEST + +START_TEST (parse_path_pattern_multiple_elements) +{ + struct cypher_input_position last = cypher_input_position_zero; + result = cypher_parse("MATCH ()-/- -/-();", + &last, NULL, 0); + ck_assert_ptr_ne(result, NULL); + ck_assert_int_eq(last.offset, 46); + + ck_assert(cypher_parse_result_fprint_ast(result, memstream, 0, NULL, 0) == 0); + fflush(memstream); + const char *expected = "\n" +" @0 0..18 statement body=@1\n" +" @1 0..18 > query clauses=[@2]\n" +" @2 0..18 > > MATCH pattern=@3\n" +" @3 6..17 > > > pattern paths=[@4]\n" +" @4 6..17 > > > > pattern path (@5)-/@6/-(@14)\n" +" @5 6..8 > > > > > node pattern ()\n" +" @6 8..15 > > > > > path pattern -/@7/-\n" +" @7 10..13 > > > > > > path expression @8 @11\n" +" @8 10..12 > > > > > > > alternative @9\n" +" @9 10..12 > > > > > > > > path base\n" +"@10 10..11 > > > > > > > > > path pattern any -\n" +"@11 12..13 > > > > > > > alternative @12\n" +"@12 12..13 > > > > > > > > path base\n" +"@13 12..13 > > > > > > > > > path pattern any -\n" +"@14 15..17 > > > > > node pattern ()\n"; + ck_assert_str_eq(memstream_buffer, expected); + + const cypher_astnode_t *ast = cypher_parse_result_get_directive(result, 0); + const cypher_astnode_t *query = cypher_ast_statement_get_body(ast); + const cypher_astnode_t *match = cypher_ast_query_get_clause(query, 0); + + const cypher_astnode_t *pattern = cypher_ast_match_get_pattern(match); + ck_assert_int_eq(cypher_astnode_type(pattern), CYPHER_AST_PATTERN); + + ck_assert_int_eq(cypher_ast_pattern_npaths(pattern), 1); + const cypher_astnode_t *path = cypher_ast_pattern_get_path(pattern, 0); + ck_assert_int_eq(cypher_astnode_type(path), CYPHER_AST_PATTERN_PATH); + + ck_assert_int_eq(cypher_ast_pattern_path_nelements(path), 3); + const cypher_astnode_t *node = cypher_ast_pattern_path_get_element(path, 0); + ck_assert_int_eq(cypher_astnode_type(node), CYPHER_AST_NODE_PATTERN); + + ck_assert_int_eq(cypher_ast_node_pattern_nlabels(node), 0); + ck_assert_ptr_eq(cypher_ast_node_pattern_get_label(node, 0), NULL); + ck_assert_ptr_eq(cypher_ast_node_pattern_get_properties(node), NULL); + + const cypher_astnode_t *path_pattern = cypher_ast_pattern_path_get_element(path, 1); + ck_assert_int_eq(cypher_astnode_type(path_pattern), CYPHER_AST_PATH_PATTERN); + + ck_assert_int_eq(cypher_ast_path_pattern_get_direction(path_pattern), + CYPHER_REL_BIDIRECTIONAL); + + const cypher_astnode_t *expr = cypher_ast_path_pattern_get_expression(path_pattern); + ck_assert_int_eq(cypher_astnode_type(expr), CYPHER_AST_PATH_PATTERN_EXPRESSION); + ck_assert_int_eq(cypher_ast_path_pattern_expression_get_nelements(expr), 2); + ck_assert_ptr_eq(cypher_ast_path_pattern_expression_get_element(expr, 2), NULL); + + const cypher_astnode_t *alt1 = cypher_ast_path_pattern_expression_get_element(expr, 0); + ck_assert_int_eq(cypher_astnode_type(alt1), CYPHER_AST_PATH_PATTERN_ALTERNATIVE); + + const cypher_astnode_t *alt2 = cypher_ast_path_pattern_expression_get_element(expr, 1); + ck_assert_int_eq(cypher_astnode_type(alt2), CYPHER_AST_PATH_PATTERN_ALTERNATIVE); +} +END_TEST + +START_TEST (parse_path_pattern_multiple_alternatives) +{ + struct cypher_input_position last = cypher_input_position_zero; + result = cypher_parse("MATCH ()-/:A | :B/-();", + &last, NULL, 0); + ck_assert_ptr_ne(result, NULL); + ck_assert_int_eq(last.offset, 22); + + ck_assert(cypher_parse_result_fprint_ast(result, memstream, 0, NULL, 0) == 0); + fflush(memstream); + const char *expected = "\n" +" @0 0..22 statement body=@1\n" +" @1 0..22 > query clauses=[@2]\n" +" @2 0..21 > > MATCH pattern=@3\n" +" @3 6..21 > > > pattern paths=[@4]\n" +" @4 6..21 > > > > pattern path (@5)-/@6/-(@15)\n" +" @5 6..8 > > > > > node pattern ()\n" +" @6 8..19 > > > > > path pattern -/@7/-\n" +" @7 10..17 > > > > > > path expression @8\n" +" @8 10..17 > > > > > > > alternative @9 | @12\n" +" @9 10..13 > > > > > > > > path base @10\n" +"@10 10..13 > > > > > > > > > path pattern edge edge label = @11\n" +"@11 10..12 > > > > > > > > > > rel type :`A`\n" +"@12 15..17 > > > > > > > > path base @13\n" +"@13 15..17 > > > > > > > > > path pattern edge edge label = @14\n" +"@14 15..17 > > > > > > > > > > rel type :`B`\n" +"@15 19..21 > > > > > node pattern ()\n"; + ck_assert_str_eq(memstream_buffer, expected); + + const cypher_astnode_t *ast = cypher_parse_result_get_directive(result, 0); + const cypher_astnode_t *query = cypher_ast_statement_get_body(ast); + const cypher_astnode_t *match = cypher_ast_query_get_clause(query, 0); + + const cypher_astnode_t *pattern = cypher_ast_match_get_pattern(match); + ck_assert_int_eq(cypher_astnode_type(pattern), CYPHER_AST_PATTERN); + + ck_assert_int_eq(cypher_ast_pattern_npaths(pattern), 1); + const cypher_astnode_t *path = cypher_ast_pattern_get_path(pattern, 0); + ck_assert_int_eq(cypher_astnode_type(path), CYPHER_AST_PATTERN_PATH); + + const cypher_astnode_t *path_pattern = cypher_ast_pattern_path_get_element(path, 1); + ck_assert_int_eq(cypher_astnode_type(path_pattern), CYPHER_AST_PATH_PATTERN); + + const cypher_astnode_t *expr = cypher_ast_path_pattern_get_expression(path_pattern); + ck_assert_int_eq(cypher_astnode_type(expr), CYPHER_AST_PATH_PATTERN_EXPRESSION); + ck_assert_int_eq(cypher_ast_path_pattern_expression_get_nelements(expr), 1); + ck_assert_ptr_eq(cypher_ast_path_pattern_expression_get_element(expr, 1), NULL); + + const cypher_astnode_t *alt = cypher_ast_path_pattern_expression_get_element(expr, 0); + ck_assert_int_eq(cypher_astnode_type(alt), CYPHER_AST_PATH_PATTERN_ALTERNATIVE); + ck_assert_int_eq(cypher_ast_path_pattern_alternative_get_nelements(alt), 2); + ck_assert_ptr_eq(cypher_ast_path_pattern_alternative_get_element(alt, 2), NULL); +} +END_TEST TCase* pattern_tcase(void) { @@ -1125,5 +1329,8 @@ TCase* pattern_tcase(void) tcase_add_test(tc, parse_named_path); tcase_add_test(tc, parse_shortest_path); tcase_add_test(tc, parse_all_shortest_paths); + tcase_add_test(tc, parse_simple_path_pattern); + tcase_add_test(tc, parse_path_pattern_multiple_elements); + tcase_add_test(tc, parse_path_pattern_multiple_alternatives); return tc; } diff --git a/lib/test/check_query.c b/lib/test/check_query.c index 8ad89e6..34518f2 100644 --- a/lib/test/check_query.c +++ b/lib/test/check_query.c @@ -192,6 +192,51 @@ START_TEST (parse_query_with_periodic_commit_option_with_no_limit) } END_TEST +START_TEST (parse_named_path_predicate_query) +{ + struct cypher_input_position last = cypher_input_position_zero; + result = cypher_parse("PATH PATTERN p = ()-/()/-();", + &last, NULL, 0); + ck_assert_ptr_ne(result, NULL); + ck_assert_int_eq(last.offset, 28); + + ck_assert(cypher_parse_result_fprint_ast(result, memstream, 0, NULL, 0) == 0); + fflush(memstream); + const char *expected = "\n" +" @0 0..28 statement body=@1\n" +" @1 0..28 > query clauses=[@2]\n" +" @2 0..27 > > named path @3 = @4\n" +" @3 13..14 > > > identifier `p`\n" +" @4 17..27 > > > pattern path (@5)-/@6/-(@11)\n" +" @5 17..19 > > > > node pattern ()\n" +" @6 19..25 > > > > path pattern -/@7/-\n" +" @7 21..23 > > > > > path expression @8\n" +" @8 21..23 > > > > > > alternative @9\n" +" @9 21..23 > > > > > > > path base @10\n" +"@10 21..23 > > > > > > > > node pattern ()\n" +"@11 25..27 > > > > node pattern ()\n"; + ck_assert_str_eq(memstream_buffer, expected); + + ck_assert_int_eq(cypher_parse_result_ndirectives(result), 1); + const cypher_astnode_t *ast = cypher_parse_result_get_directive(result, 0); + ck_assert_int_eq(cypher_astnode_type(ast), CYPHER_AST_STATEMENT); + ck_assert_int_eq(cypher_astnode_range(ast).start.offset, 0); + ck_assert_int_eq(cypher_astnode_range(ast).end.offset, 28); + + const cypher_astnode_t *query = cypher_ast_statement_get_body(ast); + ck_assert_int_eq(cypher_astnode_type(query), CYPHER_AST_QUERY); + ck_assert_int_eq(cypher_astnode_range(query).start.offset, 0); + ck_assert_int_eq(cypher_astnode_range(query).end.offset, 28); + + ck_assert_int_eq(cypher_ast_query_noptions(query), 0); + ck_assert_ptr_eq(cypher_ast_query_get_option(query, 0), NULL); + + ck_assert_int_eq(cypher_ast_query_nclauses(query), 1); + + const cypher_astnode_t *clause = cypher_ast_query_get_clause(query, 0); + ck_assert_int_eq(cypher_astnode_type(clause), CYPHER_AST_NAMED_PATH); +} +END_TEST TCase* query_tcase(void) { @@ -200,5 +245,6 @@ TCase* query_tcase(void) tcase_add_test(tc, parse_query_with_no_options); tcase_add_test(tc, parse_query_with_periodic_commit_option); tcase_add_test(tc, parse_query_with_periodic_commit_option_with_no_limit); + tcase_add_test(tc, parse_named_path_predicate_query); return tc; }