From cab89818fd92a53be0109a34c2bff8577804f020 Mon Sep 17 00:00:00 2001 From: Timur Date: Wed, 25 Dec 2019 20:41:31 +0300 Subject: [PATCH 01/22] Started work on supporting path patterns --- lib/src/Makefile.am | 1 + lib/src/ast.c | 3 + lib/src/ast_path_pattern.c | 127 +++++++++++++++++++++++++++++++++++++ lib/src/ast_pattern_path.c | 4 +- lib/src/astnode.h | 1 + lib/src/cypher-parser.h.in | 46 +++++++++++++- lib/src/parser.c | 25 ++++++++ lib/src/parser.leg | 76 +++++++++++++++++++++- 8 files changed, 276 insertions(+), 7 deletions(-) create mode 100644 lib/src/ast_path_pattern.c diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am index 59b6bff..4418da7 100644 --- a/lib/src/Makefile.am +++ b/lib/src/Makefile.am @@ -66,6 +66,7 @@ libcypher_parser_la_SOURCES = \ ast_on_match.c \ ast_order_by.c \ ast_parameter.c \ + ast_path_pattern.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..f01e30f 100644 --- a/lib/src/ast.c +++ b/lib/src/ast.c @@ -125,6 +125,7 @@ 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 *range; const struct cypher_astnode_vt *command; const struct cypher_astnode_vt *comment; @@ -242,6 +243,7 @@ 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, .range = &cypher_range_astnode_vt, .command = &cypher_command_astnode_vt, .line_comment = &cypher_line_comment_astnode_vt, @@ -369,6 +371,7 @@ 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_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_path_pattern.c b/lib/src/ast_path_pattern.c new file mode 100644 index 0000000..11a32bf --- /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 *identifier; + enum cypher_rel_direction direction; + //Need path alternatives and repetitions +}; + +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 *identifier, + 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->identifier = identifier; + 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->identifier, + node->direction, children, self->nchildren, + self->range); + return clone; +} + +const cypher_astnode_t *cypher_ast_path_pattern_get_identifier( + const cypher_astnode_t *astnode) +{ + struct path_pattern *node = + container_of(astnode, struct path_pattern, _astnode); + return node->identifier; +} + +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->identifier != NULL) + { + r = snprintf(str+n, (n < size)? size-n : 0, "@%u", + node->identifier->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_pattern_path.c b/lib/src/ast_pattern_path.c index b97608c..c849782 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 *)); diff --git a/lib/src/astnode.h b/lib/src/astnode.h index 8337fc3..1d0a3fe 100644 --- a/lib/src/astnode.h +++ b/lib/src/astnode.h @@ -238,6 +238,7 @@ 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_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..fe65837 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -336,6 +336,8 @@ 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 range node. */ extern const cypher_astnode_type_t CYPHER_AST_RANGE; /** Type for an AST command node. */ @@ -4967,7 +4969,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 +5001,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 +5186,44 @@ __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 [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 *identifier, + enum cypher_rel_direction direction, cypher_astnode_t **children, unsigned int nchildren, + struct cypher_input_range range); + +/** + * Get the identifier 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_IDENTIFIER` node, or null. + */ +__cypherlang_pure +const cypher_astnode_t *cypher_ast_path_pattern_get_identifier( + 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); /** * Construct a `CYPHER_AST_RANGE` node. diff --git a/lib/src/parser.c b/lib/src/parser.c index 0ea585d..9dd2258 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -457,6 +457,10 @@ 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(d) _path_pattern(yy, NULL, CYPHER_REL_##d) +#define named_path_predicate(i, d) _path_pattern(yy, i, CYPHER_REL_##d) +static cypher_astnode_t *_path_pattern(yycontext *yy, + cypher_astnode_t *identifier, enum cypher_rel_direction direction); #define range(s, e) _range(yy, s, e) static cypher_astnode_t *_range(yycontext *yy, cypher_astnode_t *start, cypher_astnode_t *end); @@ -2970,6 +2974,27 @@ cypher_astnode_t *_rel_pattern(yycontext *yy, return add_child(yy, node); } +cypher_astnode_t *_path_pattern(yycontext *yy, + cypher_astnode_t *identifier, 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(identifier, direction, + //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); +} 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..7016aa2 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -54,7 +54,7 @@ __statement = # Statement parsing #---------------------------------------------------- -cypher-statement = < statement-option* - (b:query | b:schema-command) > +cypher-statement = < statement-option* - (b:query | b:schema-command | b:named-path-predicate) > { $$ = statement(b); } statement-option = ( o:cypher-option | o:profile-option | o:explain-option ) @@ -84,6 +84,9 @@ schema-command = ) ~{ERR("a schema command")} (SEMICOLON | EOF) +named-path-predicate = + < PATHPATTERN i:identifier EQUAL - p:pattern-path (WHERE e:expression | e:_null_) > { $$ = query(); } + create-index = < CREATE-INDEX-ON l:label LEFT-PAREN - p:prop-name { sequence_add(p); } @@ -726,7 +729,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 +739,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 +749,71 @@ node-pattern = < LEFT-PAREN - )* (p:pattern-properties | p:_null_) RIGHT-PAREN > { $$ = node_pattern(i, p); } +path-pattern = + < ( LEFT-ARROW-HEAD - DASH - DIV - path-expression - DIV - DASH + ( - RIGHT-ARROW-HEAD > { $$ = path_pattern(BIDIRECTIONAL); } + | _empty_ > { $$ = path_pattern(INBOUND); } + ) + | DASH - DIV - path-expression - DIV - DASH + ( - RIGHT-ARROW-HEAD > { $$ = path_pattern(OUTBOUND); } + | _empty_ > { $$ = path_pattern(BIDIRECTIONAL); } + ) + ) + +path-expression = + < ( p:path-repetition { sequence_add(p); } + ( - PIPE - p:path-repetition { sequence_add(p); } + )* + )* > + - + +path-repetition = + p:path-direction - ( { $$ = p; } + (MULT - (range-detail)? ) | PLUS | QUESTIONMARK + )? + - + +path-direction = + < ( LEFT-ARROW-HEAD - p:path-base + ( - RIGHT-ARROW-HEAD > #{ $$ = path_base(BIDIRECTIONAL); } + | _empty_ > #{ $$ = path_base(INBOUND); } + ) + | p:path-base + ( - RIGHT-ARROW-HEAD > #{ $$ = path_base(OUTBOUND); } + | _empty_ > #{ $$ = path_base(BIDIRECTIONAL); } + ) + ) + +path-base = + ( p:path-edge + | p:path-any + | p:path-node + | p:path-reference + | p:path-group + ) { $$ = p; } + - + +path-edge = + < l:label > #{ sequence_add(l); } + +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 = + '~' - symbolic-name + +path-group = + < LEFT-SQ-PAREN - path-expression ( p:pattern-properties | p:_null_ ) - RIGHT-SQ-PAREN > + +range-detail = + < ( s:integer-literal | s:parameter | s:_null_ ) - ELLIPSIS - ( q:integer-literal | q:parameter | q:_null_ ) > + relationship-pattern = < ( LEFT-ARROW-HEAD - DASH - ( DASH @@ -937,6 +1005,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 +1064,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 -) From 9796584eb9d686c7e53a64ccf2848f987f525170 Mon Sep 17 00:00:00 2001 From: Timur Date: Wed, 26 Feb 2020 17:57:31 +0300 Subject: [PATCH 02/22] Minor fix to PATTERN_PATH printing result --- lib/src/ast_pattern_path.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/src/ast_pattern_path.c b/lib/src/ast_pattern_path.c index c849782..867bcb4 100644 --- a/lib/src/ast_pattern_path.c +++ b/lib/src/ast_pattern_path.c @@ -198,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) { From a336dda7e10226600187a3a1755feab8a09fdc3e Mon Sep 17 00:00:00 2001 From: Timur Date: Mon, 16 Mar 2020 22:20:25 +0300 Subject: [PATCH 03/22] Temporary improvements to structure --- lib/src/ast_path_base.c | 126 ++++++++++++++++++++++++++++++++++ lib/src/ast_path_expression.c | 0 lib/src/ast_path_pattern.c | 49 +++++++++++-- lib/src/cypher-parser.h.in | 30 +++++++- lib/src/parser.c | 6 +- lib/src/parser.leg | 52 +++++++------- 6 files changed, 227 insertions(+), 36 deletions(-) create mode 100644 lib/src/ast_path_base.c create mode 100644 lib/src/ast_path_expression.c diff --git a/lib/src/ast_path_base.c b/lib/src/ast_path_base.c new file mode 100644 index 0000000..6c36e9e --- /dev/null +++ b/lib/src/ast_path_base.c @@ -0,0 +1,126 @@ +/* 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_base +{ + cypher_astnode_t _astnode; + const cypher_astnode_t *identifier; + 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_base_astnode_vt = + { .name = "path base", + .detailstr = detailstr, + .release = cypher_astnode_release, + .clone = clone }; + +cypher_astnode_t *cypher_ast_path_base(const cypher_astnode_t *identifier, + enum cypher_rel_direction direction, cypher_astnode_t **children, unsigned int nchildren, + struct cypher_input_range range) +{ + struct path_base *node = calloc(1, sizeof(struct path_base)); + if (node == NULL) + { + return NULL; + } + if (cypher_astnode_init(&(node->_astnode), CYPHER_AST_PATH_BASE, + children, nchildren, range)) + { + goto cleanup; + } + node->identifier = identifier; + 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->identifier, + node->direction, children, self->nchildren, + self->range); + return clone; +} + +const cypher_astnode_t *cypher_ast_path_pattern_get_identifier( + const cypher_astnode_t *astnode) +{ + struct path_pattern *node = + container_of(astnode, struct path_pattern, _astnode); + return node->identifier; +} + +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->identifier != NULL) + { + r = snprintf(str+n, (n < size)? size-n : 0, "@%u", + node->identifier->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_expression.c b/lib/src/ast_path_expression.c new file mode 100644 index 0000000..e69de29 diff --git a/lib/src/ast_path_pattern.c b/lib/src/ast_path_pattern.c index 11a32bf..b2a5b0a 100644 --- a/lib/src/ast_path_pattern.c +++ b/lib/src/ast_path_pattern.c @@ -24,6 +24,8 @@ struct path_pattern cypher_astnode_t _astnode; const cypher_astnode_t *identifier; enum cypher_rel_direction direction; + size_t nelements; + const cypher_astnode_t *elements[]; //Need path alternatives and repetitions }; @@ -38,10 +40,12 @@ const struct cypher_astnode_vt cypher_path_pattern_astnode_vt = .clone = clone }; cypher_astnode_t *cypher_ast_path_pattern(const cypher_astnode_t *identifier, - enum cypher_rel_direction direction, cypher_astnode_t **children, unsigned int nchildren, + enum cypher_rel_direction direction, cypher_astnode_t * const *elements, + unsigned int nelements, cypher_astnode_t **children, unsigned int nchildren, struct cypher_input_range range) { - struct path_pattern *node = calloc(1, sizeof(struct path_pattern)); + struct path_pattern *node = calloc(1, sizeof(struct path_pattern) + + nelements * sizeof(cypher_astnode_t *)); if (node == NULL) { return NULL; @@ -51,6 +55,8 @@ cypher_astnode_t *cypher_ast_path_pattern(const cypher_astnode_t *identifier, { goto cleanup; } + memcpy(node->elements, elements, nelements * sizeof(cypher_astnode_t *)); + node->nelements = nelements; node->identifier = identifier; node->direction = direction; return &(node->_astnode); @@ -69,8 +75,19 @@ cypher_astnode_t *clone(const cypher_astnode_t *self, /**REQUIRE_TYPE(self, CYPHER_AST_PATH_PATTERN, NULL);*/ struct path_pattern *node = container_of(self, struct path_pattern, _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(node->identifier, - node->direction, children, self->nchildren, + node->direction, elements, node->nelements, children, self->nchildren, self->range); return clone; } @@ -91,6 +108,26 @@ enum cypher_rel_direction cypher_ast_path_pattern_get_direction( return node->direction; } +unsigned int cypher_ast_path_pattern_nelements(const cypher_astnode_t *astnode) +{ + //REQUIRE_TYPE(astnode, CYPHER_AST_PATH_PATTERN, 0); + struct path_pattern *node = container_of(astnode, struct path_pattern, _astnode); + return node->nelements; +} + + +const cypher_astnode_t *cypher_ast_path_pattern_get_element( + const cypher_astnode_t *astnode, unsigned int index) +{ + //REQUIRE_TYPE(astnode, CYPHER_AST_PATH_PATTERN, NULL); + struct path_pattern *node = container_of(astnode, struct path_pattern, _astnode); + if (index >= node->nelements) + { + return NULL; + } + return node->elements[index]; +} + ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) { struct path_pattern *node = container_of(self, struct path_pattern, _astnode); @@ -106,7 +143,7 @@ ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) if (node->identifier != NULL) { - r = snprintf(str+n, (n < size)? size-n : 0, "@%u", + r = snprintf(str+n, (n < size)? size-n : 0, "'@%u'", node->identifier->ordinal); if (r < 0) { @@ -114,9 +151,13 @@ ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) } n += r; } + + r = snprint_sequence(str+n, (n < size)? size-n : 0, + node->elements, node->nelements); r = snprintf(str+n, (n < size)? size-n : 0, "/-%s", (node->direction == CYPHER_REL_OUTBOUND)? ">" : ""); + if (r < 0) { return -1; diff --git a/lib/src/cypher-parser.h.in b/lib/src/cypher-parser.h.in index fe65837..1a881c1 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -5189,14 +5189,18 @@ const cypher_astnode_t *cypher_ast_rel_pattern_get_properties( /** * Construct a `CYPHER_AST_PATH_PATTERN` node. * + * @param [identifier] Path pattern identifier. * @param [direction] The direction of the pattern. + * @param [elements] The elements of the path pattern. + * @param [nelements] Number of elements in path 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 *identifier, - enum cypher_rel_direction direction, cypher_astnode_t **children, unsigned int nchildren, + enum cypher_rel_direction direction, cypher_astnode_t * const *elements, + unsigned int nelements, cypher_astnode_t **children, unsigned int nchildren, struct cypher_input_range range); /** @@ -5224,6 +5228,30 @@ const cypher_astnode_t *cypher_ast_path_pattern_get_identifier( __cypherlang_pure enum cypher_rel_direction cypher_ast_path_pattern_get_direction( const cypher_astnode_t *node); +/** + * Get the amount of elements in 'CYPHER_AST_PATH_PATTERN' node. + * + * If the node is not an instance of `CYPHER_AST_PATH_PATTERN` then the result + * will be undefined. + * + * @param [astnode] The AST node. + * @return The amount of elements. +*/ +unsigned int cypher_ast_path_pattern_nelements(const cypher_astnode_t *astnode); + +/** + * Get the element of 'CYPHER_AST_PATH_PATTERN' node. + * + * If the node is not an instance of `CYPHER_AST_PATH_PATTERN` then the result + * will be undefined. + * + * @param [astnode] The AST node. + * @param [index] Index of desired element. + * + * @return A 'CYPHER_AST_PATH_BASE' node which will be part of the path pattern. +*/ +const cypher_astnode_t *cypher_ast_path_pattern_get_element( + const cypher_astnode_t *astnode, unsigned int index); /** * Construct a `CYPHER_AST_RANGE` node. diff --git a/lib/src/parser.c b/lib/src/parser.c index 9dd2258..2275b5f 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -2980,8 +2980,8 @@ cypher_astnode_t *_path_pattern(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(identifier, direction, - //astnodes_elements(&(yy->prev_block->sequence)), - //astnodes_size(&(yy->prev_block->sequence)), + 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); @@ -2989,7 +2989,7 @@ cypher_astnode_t *_path_pattern(yycontext *yy, { abort_parse(yy); } - //astnodes_clear(&(yy->prev_block->sequence)); + astnodes_clear(&(yy->prev_block->sequence)); astnodes_clear(&(yy->prev_block->children)); block_free(yy->prev_block); yy->prev_block = NULL; diff --git a/lib/src/parser.leg b/lib/src/parser.leg index 7016aa2..a67bcb9 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -85,7 +85,8 @@ schema-command = (SEMICOLON | EOF) named-path-predicate = - < PATHPATTERN i:identifier EQUAL - p:pattern-path (WHERE e:expression | e:_null_) > { $$ = query(); } + < PATHPATTERN i:identifier EQUAL - p:pattern-path { sequence_add(p); } + (WHERE e:expression | e:_null_) > { $$ = query(); } create-index = < CREATE-INDEX-ON l:label LEFT-PAREN - p:prop-name @@ -749,7 +750,7 @@ node-pattern = < LEFT-PAREN - )* (p:pattern-properties | p:_null_) RIGHT-PAREN > { $$ = node_pattern(i, p); } -path-pattern = +path-pattern = < ( LEFT-ARROW-HEAD - DASH - DIV - path-expression - DIV - DASH ( - RIGHT-ARROW-HEAD > { $$ = path_pattern(BIDIRECTIONAL); } | _empty_ > { $$ = path_pattern(INBOUND); } @@ -761,58 +762,53 @@ path-pattern = ) path-expression = - < ( p:path-repetition { sequence_add(p); } + < < ( p:path-repetition { sequence_add(p); } ( - PIPE - p:path-repetition { sequence_add(p); } - )* - )* > + )* > { $$ = path_expression_single(); } + )* > { } - path-repetition = - p:path-direction - ( { $$ = p; } - (MULT - (range-detail)? ) | PLUS | QUESTIONMARK + p:path-direction - ( { sequence_add(p); } + rel-varlength + | PLUS + | QUESTIONMARK )? - path-direction = - < ( LEFT-ARROW-HEAD - p:path-base + < ( LEFT-ARROW-HEAD - p:path-base { sequence_add(p); } ( - RIGHT-ARROW-HEAD > #{ $$ = path_base(BIDIRECTIONAL); } | _empty_ > #{ $$ = path_base(INBOUND); } ) - | p:path-base + | p:path-base { sequence_add(p); } ( - RIGHT-ARROW-HEAD > #{ $$ = path_base(OUTBOUND); } | _empty_ > #{ $$ = path_base(BIDIRECTIONAL); } ) ) -path-base = - ( p:path-edge - | p:path-any - | p:path-node - | p:path-reference - | p:path-group - ) { $$ = p; } +path-base = + l:label { $$ = l; } + | path-any + | p:path-node { $$ = p; } + | p:path-reference { $$ = p; } + | p:path-group { $$ = p; } - -path-edge = - < l:label > #{ sequence_add(l); } - -path-any = +path-any = < DASH > -path-node = +path-node = < LEFT-PAREN - ( n:label { sequence_add(n); } )* ( p:pattern-properties | p:_null_ ) RIGHT-PAREN > { $$ = node_pattern(NULL, p); } -path-reference = - '~' - symbolic-name - -path-group = - < LEFT-SQ-PAREN - path-expression ( p:pattern-properties | p:_null_ ) - RIGHT-SQ-PAREN > +path-reference = + '~' - identifier -range-detail = - < ( s:integer-literal | s:parameter | s:_null_ ) - ELLIPSIS - ( q:integer-literal | q:parameter | q:_null_ ) > +path-group = + < LEFT-SQ-PAREN - p:path-expression ( p:pattern-properties | p:_null_ ) - RIGHT-SQ-PAREN > relationship-pattern = < ( LEFT-ARROW-HEAD - DASH - From e3cd38c4adc1ce436ca707f8ef7f2c59d14bbc59 Mon Sep 17 00:00:00 2001 From: Timur Date: Tue, 17 Mar 2020 16:38:15 +0300 Subject: [PATCH 04/22] Fixed the mistakes I made yesterday --- lib/src/parser.leg | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/src/parser.leg b/lib/src/parser.leg index a67bcb9..5bd4184 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -762,14 +762,14 @@ path-pattern = ) path-expression = - < < ( p:path-repetition { sequence_add(p); } - ( - PIPE - p:path-repetition { sequence_add(p); } - )* > { $$ = path_expression_single(); } - )* > { } + < < ( p:path-repetition #{ sequence_add(p); } + ( - PIPE - p:path-repetition #{ sequence_add(p); } + )* > #{ $$ = path_expression_single(); } + )* > #{ } - path-repetition = - p:path-direction - ( { sequence_add(p); } + p:path-direction - ( #{ sequence_add(p); } rel-varlength | PLUS | QUESTIONMARK @@ -777,11 +777,11 @@ path-repetition = - path-direction = - < ( LEFT-ARROW-HEAD - p:path-base { sequence_add(p); } + < ( LEFT-ARROW-HEAD - p:path-base #{ sequence_add(p); } ( - RIGHT-ARROW-HEAD > #{ $$ = path_base(BIDIRECTIONAL); } | _empty_ > #{ $$ = path_base(INBOUND); } ) - | p:path-base { sequence_add(p); } + | p:path-base #{ sequence_add(p); } ( - RIGHT-ARROW-HEAD > #{ $$ = path_base(OUTBOUND); } | _empty_ > #{ $$ = path_base(BIDIRECTIONAL); } ) From 090ab60243c7c1681054c17c4887aa2539ddf01a Mon Sep 17 00:00:00 2001 From: simletonsl Date: Thu, 19 Mar 2020 14:23:34 +0300 Subject: [PATCH 05/22] add path pattern alternative --- lib/src/Makefile.am | 1 + lib/src/ast.c | 3 +++ lib/src/astnode.h | 1 + lib/src/cypher-parser.h.in | 6 ++++++ lib/src/parser.c | 23 +++++++++++++++++++++++ lib/src/parser.leg | 30 ++++++++++++++++++------------ 6 files changed, 52 insertions(+), 12 deletions(-) diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am index 4418da7..97f638d 100644 --- a/lib/src/Makefile.am +++ b/lib/src/Makefile.am @@ -67,6 +67,7 @@ libcypher_parser_la_SOURCES = \ ast_order_by.c \ ast_parameter.c \ ast_path_pattern.c \ + ast_path_pattern_alternative.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 f01e30f..f3ef547 100644 --- a/lib/src/ast.c +++ b/lib/src/ast.c @@ -126,6 +126,7 @@ struct cypher_astnode_vts 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_alternative; const struct cypher_astnode_vt *range; const struct cypher_astnode_vt *command; const struct cypher_astnode_vt *comment; @@ -244,6 +245,7 @@ static const struct cypher_astnode_vts cypher_astnode_vts = .node_pattern = &cypher_node_pattern_astnode_vt, .rel_pattern = &cypher_rel_pattern_astnode_vt, .path_pattern = &cypher_path_pattern_astnode_vt, + .path_pattern_alternative = &cypher_path_pattern_astnode_alternative_vt, .range = &cypher_range_astnode_vt, .command = &cypher_command_astnode_vt, .line_comment = &cypher_line_comment_astnode_vt, @@ -372,6 +374,7 @@ 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_ALTERNATIVE = VT_OFFSET(path_pattern_alternative); 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/astnode.h b/lib/src/astnode.h index 1d0a3fe..93e5b40 100644 --- a/lib/src/astnode.h +++ b/lib/src/astnode.h @@ -239,6 +239,7 @@ 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_astnode_alternative_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 1a881c1..2165c2e 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -338,6 +338,8 @@ extern const cypher_astnode_type_t CYPHER_AST_NODE_PATTERN; 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 node alternative. */ +extern const cypher_astnode_type_t CYPHER_AST_PATH_PATTERN_ALTERNATIVE; /** Type for an AST range node. */ extern const cypher_astnode_type_t CYPHER_AST_RANGE; /** Type for an AST command node. */ @@ -5203,6 +5205,10 @@ cypher_astnode_t *cypher_ast_path_pattern(const cypher_astnode_t *identifier, 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); + /** * Get the identifier of a `CYPHER_AST_PATH_PATTERN` node. * diff --git a/lib/src/parser.c b/lib/src/parser.c index 2275b5f..2887501 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -461,6 +461,9 @@ static cypher_astnode_t *_rel_pattern(yycontext *yy, #define named_path_predicate(i, d) _path_pattern(yy, i, CYPHER_REL_##d) static cypher_astnode_t *_path_pattern(yycontext *yy, cypher_astnode_t *identifier, enum cypher_rel_direction direction); +#define path_pattern_alternative() _path_pattern_alternative(yy) +static cypher_astnode_t *_path_pattern_alternative(yycontext *yy); + #define range(s, e) _range(yy, s, e) static cypher_astnode_t *_range(yycontext *yy, cypher_astnode_t *start, cypher_astnode_t *end); @@ -2996,6 +2999,26 @@ cypher_astnode_t *_path_pattern(yycontext *yy, 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); +} + 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 5bd4184..2139f99 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -761,20 +761,26 @@ path-pattern = ) ) + path-expression = - < < ( p:path-repetition #{ sequence_add(p); } - ( - PIPE - p:path-repetition #{ sequence_add(p); } - )* > #{ $$ = path_expression_single(); } - )* > #{ } - - + path-alternative + (- path-alternative + )* -path-repetition = - p:path-direction - ( #{ sequence_add(p); } - rel-varlength - | PLUS - | QUESTIONMARK - )? - - +path-alternative = + < p:path-repetition { sequence_add(p); } + (- PIPE - p:path-repetition { sequence_add(p); } + )* > { path_pattern_alternative(); } + +path-repetition = l:label { $$ = l; } + +# path-repetition = +# p:path-direction - ( #{ sequence_add(p); } +# rel-varlength +# | PLUS +# | QUESTIONMARK +# )? +# - path-direction = < ( LEFT-ARROW-HEAD - p:path-base #{ sequence_add(p); } From 0a34a6f9e9467654271b8d6427926a1a99c08a19 Mon Sep 17 00:00:00 2001 From: simletonsl Date: Thu, 19 Mar 2020 21:43:19 +0300 Subject: [PATCH 06/22] add path ast_path_pattern_alternative --- lib/src/ast_path_pattern_alternative.c | 73 ++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 lib/src/ast_path_pattern_alternative.c diff --git a/lib/src/ast_path_pattern_alternative.c b/lib/src/ast_path_pattern_alternative.c new file mode 100644 index 0000000..bd57946 --- /dev/null +++ b/lib/src/ast_path_pattern_alternative.c @@ -0,0 +1,73 @@ +#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_astnode_alternative_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; +} + +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 From 221eb6f1588c715ea578da0db1260070700a6b5f Mon Sep 17 00:00:00 2001 From: Timur Date: Thu, 19 Mar 2020 22:35:12 +0300 Subject: [PATCH 07/22] Added path expression node support --- lib/src/Makefile.am | 1 + lib/src/ast.c | 5 +- lib/src/ast_path_expression.c | 0 lib/src/ast_path_pattern_alternative.c | 2 +- lib/src/ast_path_pattern_expression.c | 72 ++++++++++++++++++++++++++ lib/src/astnode.h | 3 +- lib/src/cypher-parser.h.in | 8 ++- lib/src/parser.c | 22 ++++++++ lib/src/parser.leg | 8 +-- 9 files changed, 113 insertions(+), 8 deletions(-) delete mode 100644 lib/src/ast_path_expression.c create mode 100644 lib/src/ast_path_pattern_expression.c diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am index 97f638d..a3cea28 100644 --- a/lib/src/Makefile.am +++ b/lib/src/Makefile.am @@ -67,6 +67,7 @@ libcypher_parser_la_SOURCES = \ ast_order_by.c \ ast_parameter.c \ ast_path_pattern.c \ + ast_path_pattern_expression.c \ ast_path_pattern_alternative.c \ ast_pattern.c \ ast_pattern_comprehension.c \ diff --git a/lib/src/ast.c b/lib/src/ast.c index f3ef547..4c0dc36 100644 --- a/lib/src/ast.c +++ b/lib/src/ast.c @@ -126,6 +126,7 @@ struct cypher_astnode_vts 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 *range; const struct cypher_astnode_vt *command; @@ -245,7 +246,8 @@ static const struct cypher_astnode_vts cypher_astnode_vts = .node_pattern = &cypher_node_pattern_astnode_vt, .rel_pattern = &cypher_rel_pattern_astnode_vt, .path_pattern = &cypher_path_pattern_astnode_vt, - .path_pattern_alternative = &cypher_path_pattern_astnode_alternative_vt, + .path_pattern_expression = &cypher_path_pattern_expression_astnode_vt, + .path_pattern_alternative = &cypher_path_pattern_alternative_astnode_vt, .range = &cypher_range_astnode_vt, .command = &cypher_command_astnode_vt, .line_comment = &cypher_line_comment_astnode_vt, @@ -374,6 +376,7 @@ 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_RANGE = VT_OFFSET(range); const uint8_t CYPHER_AST_COMMAND = VT_OFFSET(command); diff --git a/lib/src/ast_path_expression.c b/lib/src/ast_path_expression.c deleted file mode 100644 index e69de29..0000000 diff --git a/lib/src/ast_path_pattern_alternative.c b/lib/src/ast_path_pattern_alternative.c index bd57946..7cad648 100644 --- a/lib/src/ast_path_pattern_alternative.c +++ b/lib/src/ast_path_pattern_alternative.c @@ -11,7 +11,7 @@ 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_alternative_vt = +const struct cypher_astnode_vt cypher_path_pattern_alternative_astnode_vt = { .name = "alternative", .detailstr = detailstr, diff --git a/lib/src/ast_path_pattern_expression.c b/lib/src/ast_path_pattern_expression.c new file mode 100644 index 0000000..20d856e --- /dev/null +++ b/lib/src/ast_path_pattern_expression.c @@ -0,0 +1,72 @@ +#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; +} + +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/astnode.h b/lib/src/astnode.h index 93e5b40..cbeb5d5 100644 --- a/lib/src/astnode.h +++ b/lib/src/astnode.h @@ -239,7 +239,8 @@ 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_astnode_alternative_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_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 2165c2e..54a8706 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -338,7 +338,9 @@ extern const cypher_astnode_type_t CYPHER_AST_NODE_PATTERN; 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 node alternative. */ +/** 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 range node. */ extern const cypher_astnode_type_t CYPHER_AST_RANGE; @@ -5205,6 +5207,10 @@ cypher_astnode_t *cypher_ast_path_pattern(const cypher_astnode_t *identifier, unsigned int nelements, 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); diff --git a/lib/src/parser.c b/lib/src/parser.c index 2887501..43c2fef 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -461,6 +461,8 @@ static cypher_astnode_t *_rel_pattern(yycontext *yy, #define named_path_predicate(i, d) _path_pattern(yy, i, CYPHER_REL_##d) static cypher_astnode_t *_path_pattern(yycontext *yy, cypher_astnode_t *identifier, 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); @@ -2999,6 +3001,26 @@ cypher_astnode_t *_path_pattern(yycontext *yy, 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"); diff --git a/lib/src/parser.leg b/lib/src/parser.leg index 2139f99..4dc635a 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -763,14 +763,14 @@ path-pattern = path-expression = - path-alternative - (- path-alternative - )* + < 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_pattern_alternative(); } path-repetition = l:label { $$ = l; } From f1fb4e55ab8d17569acf443e40d08d7a68d49b77 Mon Sep 17 00:00:00 2001 From: Timur Date: Thu, 19 Mar 2020 23:48:43 +0300 Subject: [PATCH 08/22] Unbloated path_pattern --- lib/src/ast_path_pattern.c | 65 +++++++------------------------------- lib/src/cypher-parser.h.in | 39 ++++------------------- lib/src/parser.c | 12 +++---- lib/src/parser.leg | 17 ++++++---- 4 files changed, 32 insertions(+), 101 deletions(-) diff --git a/lib/src/ast_path_pattern.c b/lib/src/ast_path_pattern.c index b2a5b0a..13e9c3a 100644 --- a/lib/src/ast_path_pattern.c +++ b/lib/src/ast_path_pattern.c @@ -22,11 +22,8 @@ struct path_pattern { cypher_astnode_t _astnode; - const cypher_astnode_t *identifier; + const cypher_astnode_t *expression; enum cypher_rel_direction direction; - size_t nelements; - const cypher_astnode_t *elements[]; - //Need path alternatives and repetitions }; static cypher_astnode_t *clone(const cypher_astnode_t *self, @@ -39,13 +36,11 @@ const struct cypher_astnode_vt cypher_path_pattern_astnode_vt = .release = cypher_astnode_release, .clone = clone }; -cypher_astnode_t *cypher_ast_path_pattern(const cypher_astnode_t *identifier, - enum cypher_rel_direction direction, cypher_astnode_t * const *elements, - unsigned int nelements, cypher_astnode_t **children, unsigned int nchildren, +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) + - nelements * sizeof(cypher_astnode_t *)); + struct path_pattern *node = calloc(1, sizeof(struct path_pattern)); if (node == NULL) { return NULL; @@ -55,9 +50,7 @@ cypher_astnode_t *cypher_ast_path_pattern(const cypher_astnode_t *identifier, { goto cleanup; } - memcpy(node->elements, elements, nelements * sizeof(cypher_astnode_t *)); - node->nelements = nelements; - node->identifier = identifier; + node->expression = expression; node->direction = direction; return &(node->_astnode); @@ -75,29 +68,18 @@ cypher_astnode_t *clone(const cypher_astnode_t *self, /**REQUIRE_TYPE(self, CYPHER_AST_PATH_PATTERN, NULL);*/ struct path_pattern *node = container_of(self, struct path_pattern, _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(node->identifier, - node->direction, elements, node->nelements, children, self->nchildren, + 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_identifier( +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->identifier; + return node->expression; } enum cypher_rel_direction cypher_ast_path_pattern_get_direction( @@ -108,26 +90,6 @@ enum cypher_rel_direction cypher_ast_path_pattern_get_direction( return node->direction; } -unsigned int cypher_ast_path_pattern_nelements(const cypher_astnode_t *astnode) -{ - //REQUIRE_TYPE(astnode, CYPHER_AST_PATH_PATTERN, 0); - struct path_pattern *node = container_of(astnode, struct path_pattern, _astnode); - return node->nelements; -} - - -const cypher_astnode_t *cypher_ast_path_pattern_get_element( - const cypher_astnode_t *astnode, unsigned int index) -{ - //REQUIRE_TYPE(astnode, CYPHER_AST_PATH_PATTERN, NULL); - struct path_pattern *node = container_of(astnode, struct path_pattern, _astnode); - if (index >= node->nelements) - { - return NULL; - } - return node->elements[index]; -} - ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) { struct path_pattern *node = container_of(self, struct path_pattern, _astnode); @@ -141,10 +103,10 @@ ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) } n += r; - if (node->identifier != NULL) + if (node->expression != NULL) { - r = snprintf(str+n, (n < size)? size-n : 0, "'@%u'", - node->identifier->ordinal); + r = snprintf(str+n, (n < size)? size-n : 0, "@%u", + node->expression->ordinal); if (r < 0) { return -1; @@ -152,9 +114,6 @@ ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) n += r; } - r = snprint_sequence(str+n, (n < size)? size-n : 0, - node->elements, node->nelements); - r = snprintf(str+n, (n < size)? size-n : 0, "/-%s", (node->direction == CYPHER_REL_OUTBOUND)? ">" : ""); diff --git a/lib/src/cypher-parser.h.in b/lib/src/cypher-parser.h.in index 54a8706..6028059 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -5193,18 +5193,15 @@ const cypher_astnode_t *cypher_ast_rel_pattern_get_properties( /** * Construct a `CYPHER_AST_PATH_PATTERN` node. * - * @param [identifier] Path pattern identifier. + * @param [expression] Path pattern expresison. * @param [direction] The direction of the pattern. - * @param [elements] The elements of the path pattern. - * @param [nelements] Number of elements in path 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 *identifier, - enum cypher_rel_direction direction, cypher_astnode_t * const *elements, - unsigned int nelements, cypher_astnode_t **children, unsigned int nchildren, +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, @@ -5216,16 +5213,16 @@ cypher_astnode_t *cypher_ast_path_pattern_alternative(cypher_astnode_t * const * struct cypher_input_range range); /** - * Get the identifier of a `CYPHER_AST_PATH_PATTERN` 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_IDENTIFIER` node, or null. + * @return A `CYPHER_AST_PATH_PATTERN_EXPRESSION` node, or null. */ __cypherlang_pure -const cypher_astnode_t *cypher_ast_path_pattern_get_identifier( +const cypher_astnode_t *cypher_ast_path_pattern_get_expression( const cypher_astnode_t *node); /** @@ -5240,30 +5237,6 @@ const cypher_astnode_t *cypher_ast_path_pattern_get_identifier( __cypherlang_pure enum cypher_rel_direction cypher_ast_path_pattern_get_direction( const cypher_astnode_t *node); -/** - * Get the amount of elements in 'CYPHER_AST_PATH_PATTERN' node. - * - * If the node is not an instance of `CYPHER_AST_PATH_PATTERN` then the result - * will be undefined. - * - * @param [astnode] The AST node. - * @return The amount of elements. -*/ -unsigned int cypher_ast_path_pattern_nelements(const cypher_astnode_t *astnode); - -/** - * Get the element of 'CYPHER_AST_PATH_PATTERN' node. - * - * If the node is not an instance of `CYPHER_AST_PATH_PATTERN` then the result - * will be undefined. - * - * @param [astnode] The AST node. - * @param [index] Index of desired element. - * - * @return A 'CYPHER_AST_PATH_BASE' node which will be part of the path pattern. -*/ -const cypher_astnode_t *cypher_ast_path_pattern_get_element( - const cypher_astnode_t *astnode, unsigned int index); /** * Construct a `CYPHER_AST_RANGE` node. diff --git a/lib/src/parser.c b/lib/src/parser.c index 43c2fef..ff67f93 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -457,10 +457,9 @@ 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(d) _path_pattern(yy, NULL, CYPHER_REL_##d) -#define named_path_predicate(i, d) _path_pattern(yy, i, CYPHER_REL_##d) +#define path_pattern(e, d) _path_pattern(yy, e, CYPHER_REL_##d) static cypher_astnode_t *_path_pattern(yycontext *yy, - cypher_astnode_t *identifier, enum cypher_rel_direction direction); + 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) @@ -2980,13 +2979,11 @@ cypher_astnode_t *_rel_pattern(yycontext *yy, } cypher_astnode_t *_path_pattern(yycontext *yy, - cypher_astnode_t *identifier, enum cypher_rel_direction direction) + 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(identifier, direction, - astnodes_elements(&(yy->prev_block->sequence)), - astnodes_size(&(yy->prev_block->sequence)), + 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); @@ -2994,7 +2991,6 @@ cypher_astnode_t *_path_pattern(yycontext *yy, { abort_parse(yy); } - astnodes_clear(&(yy->prev_block->sequence)); astnodes_clear(&(yy->prev_block->children)); block_free(yy->prev_block); yy->prev_block = NULL; diff --git a/lib/src/parser.leg b/lib/src/parser.leg index 4dc635a..91fb566 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -751,13 +751,13 @@ node-pattern = < LEFT-PAREN - RIGHT-PAREN > { $$ = node_pattern(i, p); } path-pattern = - < ( LEFT-ARROW-HEAD - DASH - DIV - path-expression - DIV - DASH - ( - RIGHT-ARROW-HEAD > { $$ = path_pattern(BIDIRECTIONAL); } - | _empty_ > { $$ = path_pattern(INBOUND); } + < ( LEFT-ARROW-HEAD - DASH - DIV - e:path-expression - DIV - DASH + ( - RIGHT-ARROW-HEAD > { $$ = path_pattern(e, BIDIRECTIONAL); } + | _empty_ > { $$ = path_pattern(e, INBOUND); } ) - | DASH - DIV - path-expression - DIV - DASH - ( - RIGHT-ARROW-HEAD > { $$ = path_pattern(OUTBOUND); } - | _empty_ > { $$ = path_pattern(BIDIRECTIONAL); } + | DASH - DIV - e:path-expression - DIV - DASH + ( - RIGHT-ARROW-HEAD > { $$ = path_pattern(e, OUTBOUND); } + | _empty_ > { $$ = path_pattern(e, BIDIRECTIONAL); } ) ) @@ -772,7 +772,10 @@ path-alternative = (- PIPE - p:path-repetition { sequence_add(p); } )* > { $$ = path_pattern_alternative(); } -path-repetition = l:label { $$ = l; } +path-repetition = + p:label { $$ = p; } + | p:path-node { $$ = p; } + | path-any # path-repetition = # p:path-direction - ( #{ sequence_add(p); } From f2108acde447f13af48db7f79758037b4eba50d3 Mon Sep 17 00:00:00 2001 From: simletonsl Date: Mon, 23 Mar 2020 01:50:32 +0300 Subject: [PATCH 09/22] add path base ast node --- lib/src/Makefile.am | 1 + lib/src/ast.c | 3 ++ lib/src/ast_path_pattern_base.c | 81 +++++++++++++++++++++++++++++++++ lib/src/astnode.h | 1 + lib/src/cypher-parser.h.in | 6 +++ lib/src/parser.c | 28 +++++++++++- lib/src/parser.leg | 57 ++++++++++------------- 7 files changed, 143 insertions(+), 34 deletions(-) create mode 100644 lib/src/ast_path_pattern_base.c diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am index a3cea28..8bfcda0 100644 --- a/lib/src/Makefile.am +++ b/lib/src/Makefile.am @@ -69,6 +69,7 @@ libcypher_parser_la_SOURCES = \ ast_path_pattern.c \ ast_path_pattern_expression.c \ ast_path_pattern_alternative.c \ + ast_path_pattern_base.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 4c0dc36..6c4f753 100644 --- a/lib/src/ast.c +++ b/lib/src/ast.c @@ -128,6 +128,7 @@ struct cypher_astnode_vts 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 *range; const struct cypher_astnode_vt *command; const struct cypher_astnode_vt *comment; @@ -248,6 +249,7 @@ static const struct cypher_astnode_vts cypher_astnode_vts = .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, .range = &cypher_range_astnode_vt, .command = &cypher_command_astnode_vt, .line_comment = &cypher_line_comment_astnode_vt, @@ -378,6 +380,7 @@ 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_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_path_pattern_base.c b/lib/src/ast_path_pattern_base.c new file mode 100644 index 0000000..c50c8a4 --- /dev/null +++ b/lib/src/ast_path_pattern_base.c @@ -0,0 +1,81 @@ +#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; +} + + +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/astnode.h b/lib/src/astnode.h index cbeb5d5..1ee62eb 100644 --- a/lib/src/astnode.h +++ b/lib/src/astnode.h @@ -241,6 +241,7 @@ 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_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 6028059..2a4f073 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -342,6 +342,8 @@ extern const cypher_astnode_type_t CYPHER_AST_PATH_PATTERN; 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 range node. */ extern const cypher_astnode_type_t CYPHER_AST_RANGE; /** Type for an AST command node. */ @@ -5212,6 +5214,10 @@ cypher_astnode_t *cypher_ast_path_pattern_alternative(cypher_astnode_t * const * 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); + /** * Get the expression of a `CYPHER_AST_PATH_PATTERN` node. * diff --git a/lib/src/parser.c b/lib/src/parser.c index ff67f93..0208444 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -464,7 +464,10 @@ static cypher_astnode_t *_path_pattern(yycontext *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 range(s, e) _range(yy, s, e) static cypher_astnode_t *_range(yycontext *yy, cypher_astnode_t *start, cypher_astnode_t *end); @@ -3037,6 +3040,29 @@ static cypher_astnode_t *_path_pattern_alternative(yycontext *yy) { 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); +} + 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 91fb566..af54626 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -761,7 +761,6 @@ path-pattern = ) ) - path-expression = < p:path-alternative { sequence_add(p); } (- p:path-alternative { sequence_add(p); } @@ -772,37 +771,32 @@ path-alternative = (- PIPE - p:path-repetition { sequence_add(p); } )* > { $$ = path_pattern_alternative(); } -path-repetition = - p:label { $$ = p; } - | p:path-node { $$ = p; } - | path-any - -# path-repetition = -# p:path-direction - ( #{ sequence_add(p); } -# rel-varlength -# | PLUS -# | QUESTIONMARK -# )? -# - - -path-direction = - < ( LEFT-ARROW-HEAD - p:path-base #{ sequence_add(p); } - ( - RIGHT-ARROW-HEAD > #{ $$ = path_base(BIDIRECTIONAL); } - | _empty_ > #{ $$ = path_base(INBOUND); } - ) - | p:path-base #{ sequence_add(p); } - ( - RIGHT-ARROW-HEAD > #{ $$ = path_base(OUTBOUND); } - | _empty_ > #{ $$ = path_base(BIDIRECTIONAL); } - ) - ) - +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, NULL, p); } + | _empty_ + - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(BIDIRECTIONAL, NULL, p); } + ) + ) + +# TODO: make ast nodes for label, path-group, ..., path-reference path-base = - l:label { $$ = l; } - | path-any - | p:path-node { $$ = p; } - | p:path-reference { $$ = p; } + l:label { $$ = l; } | p:path-group { $$ = p; } - - +# | path-any { } +# | p:path-node { $$ = p; } +# | p:path-reference { $$ = p; } +# - + +path-group = + LEFT-SQ-PAREN - p:path-expression - RIGHT-SQ-PAREN {$$ = p} path-any = < DASH > @@ -816,9 +810,6 @@ path-node = path-reference = '~' - identifier -path-group = - < LEFT-SQ-PAREN - p:path-expression ( p:pattern-properties | p:_null_ ) - RIGHT-SQ-PAREN > - relationship-pattern = < ( LEFT-ARROW-HEAD - DASH - ( DASH From 3d00ad39d25d9853fa56a92e56b6ca10826fd559 Mon Sep 17 00:00:00 2001 From: Timur Date: Tue, 24 Mar 2020 16:21:32 +0300 Subject: [PATCH 10/22] Added Named-Path-Predicate support --- lib/src/ast_named_path.c | 30 ++++++++++++++++++++++++++---- lib/src/ast_statement.c | 3 ++- lib/src/cypher-parser.h.in | 7 +++++-- lib/src/parser.c | 11 +++++++---- lib/src/parser.leg | 11 ++++++----- 5 files changed, 46 insertions(+), 16 deletions(-) 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_statement.c b/lib/src/ast_statement.c index 7fd793b..62c48d1 100644 --- a/lib/src/ast_statement.c +++ b/lib/src/ast_statement.c @@ -48,7 +48,8 @@ cypher_astnode_t *cypher_ast_statement(cypher_astnode_t * const *options, REQUIRE_CHILD_ALL(children, nchildren, options, noptions, CYPHER_AST_STATEMENT_OPTION, NULL); REQUIRE(cypher_astnode_instanceof(body, CYPHER_AST_QUERY) || - cypher_astnode_instanceof(body, CYPHER_AST_SCHEMA_COMMAND), NULL); + cypher_astnode_instanceof(body, CYPHER_AST_SCHEMA_COMMAND) || + cypher_astnode_instanceof(body, CYPHER_AST_NAMED_PATH), NULL); REQUIRE_CONTAINS(children, nchildren, body, NULL); struct statement *node = calloc(1, sizeof(struct statement) + diff --git a/lib/src/cypher-parser.h.in b/lib/src/cypher-parser.h.in index 2a4f073..a442ca9 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -4890,6 +4890,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. @@ -4897,8 +4898,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. @@ -4926,6 +4927,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. diff --git a/lib/src/parser.c b/lib/src/parser.c index 0208444..5f49a37 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); @@ -2874,11 +2876,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); diff --git a/lib/src/parser.leg b/lib/src/parser.leg index af54626..a05a166 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -84,9 +84,10 @@ schema-command = ) ~{ERR("a schema command")} (SEMICOLON | EOF) -named-path-predicate = - < PATHPATTERN i:identifier EQUAL - p:pattern-path { sequence_add(p); } - (WHERE e:expression | e:_null_) > { $$ = query(); } +named-path-predicate = + < 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 @@ -780,9 +781,9 @@ path-repetition = ) | p:path-base ( - RIGHT-ARROW-HEAD - - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(OUTBOUND, NULL, p); } + - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(OUTBOUND, l, p); } | _empty_ - - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(BIDIRECTIONAL, NULL, p); } + - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(BIDIRECTIONAL, l, p); } ) ) From d813f54cfdb56a5315994426ca45d15958565f17 Mon Sep 17 00:00:00 2001 From: simletonsl Date: Thu, 26 Mar 2020 03:21:32 +0300 Subject: [PATCH 11/22] add path edge ast node --- lib/src/Makefile.am | 1 + lib/src/ast.c | 3 ++ lib/src/ast_path_pattern_edge.c | 66 +++++++++++++++++++++++++++++++++ lib/src/astnode.h | 1 + lib/src/cypher-parser.h.in | 5 +++ lib/src/parser.c | 21 +++++++++++ lib/src/parser.leg | 7 +++- 7 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 lib/src/ast_path_pattern_edge.c diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am index 8bfcda0..3354cd2 100644 --- a/lib/src/Makefile.am +++ b/lib/src/Makefile.am @@ -70,6 +70,7 @@ libcypher_parser_la_SOURCES = \ ast_path_pattern_expression.c \ ast_path_pattern_alternative.c \ ast_path_pattern_base.c \ + ast_path_pattern_edge.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 6c4f753..b49da9e 100644 --- a/lib/src/ast.c +++ b/lib/src/ast.c @@ -129,6 +129,7 @@ struct cypher_astnode_vts 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 *range; const struct cypher_astnode_vt *command; const struct cypher_astnode_vt *comment; @@ -250,6 +251,7 @@ static const struct cypher_astnode_vts cypher_astnode_vts = .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, .range = &cypher_range_astnode_vt, .command = &cypher_command_astnode_vt, .line_comment = &cypher_line_comment_astnode_vt, @@ -381,6 +383,7 @@ 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_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_path_pattern_edge.c b/lib/src/ast_path_pattern_edge.c new file mode 100644 index 0000000..8187bf9 --- /dev/null +++ b/lib/src/ast_path_pattern_edge.c @@ -0,0 +1,66 @@ +#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; +} + + +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/astnode.h b/lib/src/astnode.h index 1ee62eb..6eb134b 100644 --- a/lib/src/astnode.h +++ b/lib/src/astnode.h @@ -242,6 +242,7 @@ 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_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 a442ca9..a4e64ee 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -344,6 +344,8 @@ extern const cypher_astnode_type_t CYPHER_AST_PATH_PATTERN_EXPRESSION; 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 range node. */ extern const cypher_astnode_type_t CYPHER_AST_RANGE; /** Type for an AST command node. */ @@ -5221,6 +5223,9 @@ cypher_astnode_t *cypher_ast_path_pattern_base(enum cypher_rel_direction directi 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); /** * Get the expression of a `CYPHER_AST_PATH_PATTERN` node. * diff --git a/lib/src/parser.c b/lib/src/parser.c index 5f49a37..c7e70af 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -470,6 +470,9 @@ static cypher_astnode_t *_path_pattern_alternative(yycontext *yy); 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 range(s, e) _range(yy, s, e) static cypher_astnode_t *_range(yycontext *yy, cypher_astnode_t *start, cypher_astnode_t *end); @@ -3066,6 +3069,24 @@ static cypher_astnode_t *_path_pattern_base(yycontext *yy, 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); +} + 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 a05a166..f40b2d1 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -789,13 +789,16 @@ path-repetition = # TODO: make ast nodes for label, path-group, ..., path-reference path-base = - l:label { $$ = l; } - | p:path-group { $$ = p; } + e:path-edge { $$ = e; } + | p:path-group { $$ = p; } # | path-any { } # | p:path-node { $$ = p; } # | p:path-reference { $$ = p; } # - +path-edge = + < r:rel-type > { $$ = path_pattern_edge(r); } + path-group = LEFT-SQ-PAREN - p:path-expression - RIGHT-SQ-PAREN {$$ = p} From 09d97f48d3f8294d4cdcac1a6caba8745c3474e5 Mon Sep 17 00:00:00 2001 From: simletonsl Date: Thu, 26 Mar 2020 22:11:12 +0300 Subject: [PATCH 12/22] add some getters --- lib/src/ast_path_pattern_alternative.c | 16 ++++++++++++++++ lib/src/ast_path_pattern_base.c | 17 +++++++++++++++++ lib/src/ast_path_pattern_edge.c | 8 ++++++++ lib/src/ast_path_pattern_expression.c | 16 ++++++++++++++++ lib/src/cypher-parser.h.in | 20 ++++++++++++++++++++ lib/src/parser.leg | 2 +- 6 files changed, 78 insertions(+), 1 deletion(-) diff --git a/lib/src/ast_path_pattern_alternative.c b/lib/src/ast_path_pattern_alternative.c index 7cad648..df24263 100644 --- a/lib/src/ast_path_pattern_alternative.c +++ b/lib/src/ast_path_pattern_alternative.c @@ -46,6 +46,22 @@ cypher_astnode_t *cypher_ast_path_pattern_alternative(cypher_astnode_t * const * 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);*/ diff --git a/lib/src/ast_path_pattern_base.c b/lib/src/ast_path_pattern_base.c index c50c8a4..825798d 100644 --- a/lib/src/ast_path_pattern_base.c +++ b/lib/src/ast_path_pattern_base.c @@ -45,6 +45,23 @@ cypher_astnode_t *cypher_ast_path_pattern_base(enum cypher_rel_direction directi 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) diff --git a/lib/src/ast_path_pattern_edge.c b/lib/src/ast_path_pattern_edge.c index 8187bf9..432ac29 100644 --- a/lib/src/ast_path_pattern_edge.c +++ b/lib/src/ast_path_pattern_edge.c @@ -43,6 +43,14 @@ cypher_astnode_t *cypher_ast_path_pattern_edge( const cypher_astnode_t *reltype, 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) diff --git a/lib/src/ast_path_pattern_expression.c b/lib/src/ast_path_pattern_expression.c index 20d856e..f137e9e 100644 --- a/lib/src/ast_path_pattern_expression.c +++ b/lib/src/ast_path_pattern_expression.c @@ -46,6 +46,22 @@ cypher_astnode_t *cypher_ast_path_pattern_expression(cypher_astnode_t * const *e 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); diff --git a/lib/src/cypher-parser.h.in b/lib/src/cypher-parser.h.in index a4e64ee..ea8493a 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -5252,6 +5252,26 @@ __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); /** * Construct a `CYPHER_AST_RANGE` node. * diff --git a/lib/src/parser.leg b/lib/src/parser.leg index f40b2d1..8c5a0dd 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -783,7 +783,7 @@ path-repetition = ( - RIGHT-ARROW-HEAD - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(OUTBOUND, l, p); } | _empty_ - - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(BIDIRECTIONAL, l, p); } + - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(OUTBOUND, l, p); } ) ) From fdb9a6d542aa4e5347c672d20953673132d3ebe7 Mon Sep 17 00:00:00 2001 From: simletonsl Date: Sun, 29 Mar 2020 00:55:29 +0300 Subject: [PATCH 13/22] add path reference ast node --- lib/src/Makefile.am | 1 + lib/src/ast.c | 3 ++ lib/src/ast_path_pattern_reference.c | 74 ++++++++++++++++++++++++++++ lib/src/astnode.h | 1 + lib/src/cypher-parser.h.in | 11 ++++- lib/src/parser.c | 22 +++++++++ lib/src/parser.leg | 4 +- 7 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 lib/src/ast_path_pattern_reference.c diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am index 3354cd2..c73c2f3 100644 --- a/lib/src/Makefile.am +++ b/lib/src/Makefile.am @@ -71,6 +71,7 @@ libcypher_parser_la_SOURCES = \ 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 b49da9e..cc5213a 100644 --- a/lib/src/ast.c +++ b/lib/src/ast.c @@ -130,6 +130,7 @@ struct cypher_astnode_vts 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; @@ -252,6 +253,7 @@ static const struct cypher_astnode_vts cypher_astnode_vts = .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, @@ -384,6 +386,7 @@ const uint8_t CYPHER_AST_PATH_PATTERN_EXPRESSION = VT_OFFSET(path_pattern_expres 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_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/astnode.h b/lib/src/astnode.h index 6eb134b..25d9edf 100644 --- a/lib/src/astnode.h +++ b/lib/src/astnode.h @@ -243,6 +243,7 @@ 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 ea8493a..7870e91 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -346,6 +346,8 @@ extern const cypher_astnode_type_t CYPHER_AST_PATH_PATTERN_ALTERNATIVE; 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. */ @@ -5223,7 +5225,11 @@ cypher_astnode_t *cypher_ast_path_pattern_base(enum cypher_rel_direction directi 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 *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); /** @@ -5272,6 +5278,9 @@ const cypher_astnode_t *cypher_ast_path_pattern_base_get_child( 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 c7e70af..a25267c 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -473,6 +473,9 @@ static cypher_astnode_t *_path_pattern_base(yycontext *yy, #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); @@ -3087,6 +3090,25 @@ static cypher_astnode_t *_path_pattern_edge(yycontext *yy, cypher_astnode_t *rel 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 8c5a0dd..184e4e3 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -791,9 +791,9 @@ path-repetition = path-base = e:path-edge { $$ = e; } | p:path-group { $$ = p; } + | p:path-reference { $$ = p; } # | path-any { } # | p:path-node { $$ = p; } -# | p:path-reference { $$ = p; } # - path-edge = @@ -812,7 +812,7 @@ path-node = RIGHT-PAREN > { $$ = node_pattern(NULL, p); } path-reference = - '~' - identifier + < '~' - i:identifier > { path_pattern_reference(i); } relationship-pattern = < ( LEFT-ARROW-HEAD - DASH - From 689f6ec3978cd40fa7d0531172e6cc13896c05fa Mon Sep 17 00:00:00 2001 From: Timur Date: Sun, 29 Mar 2020 17:05:40 +0300 Subject: [PATCH 14/22] Moved named-path-predicate to query clauses --- lib/src/ast_path_base.c | 126 ---------------------------------------- lib/src/ast_query.c | 7 ++- lib/src/ast_statement.c | 3 +- lib/src/parser.leg | 7 ++- 4 files changed, 10 insertions(+), 133 deletions(-) delete mode 100644 lib/src/ast_path_base.c diff --git a/lib/src/ast_path_base.c b/lib/src/ast_path_base.c deleted file mode 100644 index 6c36e9e..0000000 --- a/lib/src/ast_path_base.c +++ /dev/null @@ -1,126 +0,0 @@ -/* 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_base -{ - cypher_astnode_t _astnode; - const cypher_astnode_t *identifier; - 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_base_astnode_vt = - { .name = "path base", - .detailstr = detailstr, - .release = cypher_astnode_release, - .clone = clone }; - -cypher_astnode_t *cypher_ast_path_base(const cypher_astnode_t *identifier, - enum cypher_rel_direction direction, cypher_astnode_t **children, unsigned int nchildren, - struct cypher_input_range range) -{ - struct path_base *node = calloc(1, sizeof(struct path_base)); - if (node == NULL) - { - return NULL; - } - if (cypher_astnode_init(&(node->_astnode), CYPHER_AST_PATH_BASE, - children, nchildren, range)) - { - goto cleanup; - } - node->identifier = identifier; - 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->identifier, - node->direction, children, self->nchildren, - self->range); - return clone; -} - -const cypher_astnode_t *cypher_ast_path_pattern_get_identifier( - const cypher_astnode_t *astnode) -{ - struct path_pattern *node = - container_of(astnode, struct path_pattern, _astnode); - return node->identifier; -} - -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->identifier != NULL) - { - r = snprintf(str+n, (n < size)? size-n : 0, "@%u", - node->identifier->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_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_statement.c b/lib/src/ast_statement.c index 62c48d1..7fd793b 100644 --- a/lib/src/ast_statement.c +++ b/lib/src/ast_statement.c @@ -48,8 +48,7 @@ cypher_astnode_t *cypher_ast_statement(cypher_astnode_t * const *options, REQUIRE_CHILD_ALL(children, nchildren, options, noptions, CYPHER_AST_STATEMENT_OPTION, NULL); REQUIRE(cypher_astnode_instanceof(body, CYPHER_AST_QUERY) || - cypher_astnode_instanceof(body, CYPHER_AST_SCHEMA_COMMAND) || - cypher_astnode_instanceof(body, CYPHER_AST_NAMED_PATH), NULL); + cypher_astnode_instanceof(body, CYPHER_AST_SCHEMA_COMMAND), NULL); REQUIRE_CONTAINS(children, nchildren, body, NULL); struct statement *node = calloc(1, sizeof(struct statement) + diff --git a/lib/src/parser.leg b/lib/src/parser.leg index 184e4e3..d066529 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -54,7 +54,7 @@ __statement = # Statement parsing #---------------------------------------------------- -cypher-statement = < statement-option* - (b:query | b:schema-command | b:named-path-predicate) > +cypher-statement = < statement-option* - (b:query | b:schema-command) > { $$ = statement(b); } statement-option = ( o:cypher-option | o:profile-option | o:explain-option ) @@ -84,7 +84,7 @@ schema-command = ) ~{ERR("a schema command")} (SEMICOLON | EOF) -named-path-predicate = +named-path-clause = < PATHPATTERN i:identifier EQUAL - p:pattern-path - (WHERE e:expression | e:_null_) > { $$ = named_path_predicate(i, p, e); } @@ -171,6 +171,7 @@ clause = | loadcsv-clause | start-clause | union-clause + | named-path-clause ) ~{ERR("a clause")} loadcsv-clause = @@ -800,7 +801,7 @@ path-edge = < r:rel-type > { $$ = path_pattern_edge(r); } path-group = - LEFT-SQ-PAREN - p:path-expression - RIGHT-SQ-PAREN {$$ = p} + LEFT-SQ-PAREN - p:path-expression - RIGHT-SQ-PAREN { $$ = p; } path-any = < DASH > From 6612e13b49a2c7820e3c3557db3f49505bfc3059 Mon Sep 17 00:00:00 2001 From: Timur Date: Sun, 29 Mar 2020 20:20:29 +0300 Subject: [PATCH 15/22] Finished all the necessary work on the parser. --- lib/src/Makefile.am | 1 + lib/src/ast.c | 3 ++ lib/src/ast_path_pattern_alternative.c | 27 ++++++++++++- lib/src/ast_path_pattern_any.c | 56 ++++++++++++++++++++++++++ lib/src/ast_path_pattern_base.c | 45 +++++++++++++++++---- lib/src/ast_path_pattern_edge.c | 3 +- lib/src/ast_path_pattern_expression.c | 51 ++++++++++++++++++++--- lib/src/ast_path_pattern_reference.c | 7 ++-- lib/src/astnode.h | 1 + lib/src/cypher-parser.h.in | 12 +++++- lib/src/parser.c | 29 ++++++++++--- lib/src/parser.leg | 22 +++++----- 12 files changed, 220 insertions(+), 37 deletions(-) create mode 100644 lib/src/ast_path_pattern_any.c diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am index c73c2f3..6774f9c 100644 --- a/lib/src/Makefile.am +++ b/lib/src/Makefile.am @@ -67,6 +67,7 @@ libcypher_parser_la_SOURCES = \ 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 \ diff --git a/lib/src/ast.c b/lib/src/ast.c index cc5213a..53e8ae5 100644 --- a/lib/src/ast.c +++ b/lib/src/ast.c @@ -126,6 +126,7 @@ struct cypher_astnode_vts 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; @@ -249,6 +250,7 @@ static const struct cypher_astnode_vts cypher_astnode_vts = .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, @@ -382,6 +384,7 @@ 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); diff --git a/lib/src/ast_path_pattern_alternative.c b/lib/src/ast_path_pattern_alternative.c index df24263..b4de36e 100644 --- a/lib/src/ast_path_pattern_alternative.c +++ b/lib/src/ast_path_pattern_alternative.c @@ -84,6 +84,29 @@ cypher_astnode_t *clone(const cypher_astnode_t *self, cypher_astnode_t **childre } 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); + 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..278e2bf --- /dev/null +++ b/lib/src/ast_path_pattern_any.c @@ -0,0 +1,56 @@ +#include "astnode.h" + +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 index 825798d..32e90b3 100644 --- a/lib/src/ast_path_pattern_base.c +++ b/lib/src/ast_path_pattern_base.c @@ -84,15 +84,46 @@ cypher_astnode_t *clone(const cypher_astnode_t *self, } 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"); + 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; - const char *child = NULL; + 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; + } - if (base->path_base->type == CYPHER_AST_LABEL) { - child = "label"; + r = snprintf(str+n, (n < size)? size-n : 0, "%s", + (node->direction == CYPHER_REL_OUTBOUND)? ">" : ""); + + if (r < 0) + { + return -1; } + n += r; - return snprintf(str, size, "dir: %s, child: %s", dir, child); + 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 index 432ac29..f3ff219 100644 --- a/lib/src/ast_path_pattern_edge.c +++ b/lib/src/ast_path_pattern_edge.c @@ -70,5 +70,6 @@ cypher_astnode_t *clone(const cypher_astnode_t *self, } ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) { - return snprintf(str, size, "path pattern edge"); + 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 index f137e9e..d0a30a3 100644 --- a/lib/src/ast_path_pattern_expression.c +++ b/lib/src/ast_path_pattern_expression.c @@ -4,6 +4,7 @@ struct path_pattern_expression { cypher_astnode_t _astnode; size_t nelements; + const cypher_astnode_t *properties; const cypher_astnode_t *elements[]; }; @@ -13,12 +14,13 @@ 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", + .name = "path 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 *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) + @@ -35,6 +37,7 @@ cypher_astnode_t *cypher_ast_path_pattern_expression(cypher_astnode_t * const *e } memcpy(node->elements, elements, nelements * sizeof(cypher_astnode_t *)); + node->properties = properties; node->nelements = nelements; return &(node->_astnode); @@ -62,6 +65,15 @@ const cypher_astnode_t *cypher_ast_path_pattern_expression_get_element( 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); @@ -77,12 +89,41 @@ cypher_astnode_t *clone(const cypher_astnode_t *self, cypher_astnode_t **childre elements[i] = children[child_index(self, node->elements[i])]; } - cypher_astnode_t *clone = cypher_ast_path_pattern_expression(elements, node->nelements, children, self->nchildren, + 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) { - const struct path_pattern_expression *expression = (const struct path_pattern_expression *) self; - return snprintf(str, size, "Expression, elems: %ld", expression->nelements); + 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 index 91db19a..fed4d54 100644 --- a/lib/src/ast_path_pattern_reference.c +++ b/lib/src/ast_path_pattern_reference.c @@ -36,7 +36,7 @@ cypher_astnode_t *cypher_ast_path_pattern_reference( const cypher_astnode_t *ide return &(node->_astnode); int errsv; - cleanup: +cleanup: errsv = errno; free(node); errno = errsv; @@ -64,11 +64,10 @@ cypher_astnode_t *clone(const cypher_astnode_t *self, 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, "~"); + 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/astnode.h b/lib/src/astnode.h index 25d9edf..fbbb0d9 100644 --- a/lib/src/astnode.h +++ b/lib/src/astnode.h @@ -238,6 +238,7 @@ 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; diff --git a/lib/src/cypher-parser.h.in b/lib/src/cypher-parser.h.in index 7870e91..afe099e 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -338,6 +338,8 @@ extern const cypher_astnode_type_t CYPHER_AST_NODE_PATTERN; 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. */ @@ -5213,10 +5215,13 @@ 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, +cypher_astnode_t *cypher_ast_path_pattern_any(cypher_astnode_t **children, unsigned int nchildren, struct cypher_input_range range); +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); + 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); @@ -5264,6 +5269,9 @@ unsigned int cypher_ast_path_pattern_expression_get_nelements( const cypher_astnode_t *cypher_ast_path_pattern_expression_get_element( const cypher_astnode_t *astnode, unsigned int index); +const cypher_astnode_t *cypher_ast_path_pattern_get_properties( + const cypher_astnode_t *astnode); + unsigned int cypher_ast_path_pattern_alternative_get_nelements( const cypher_astnode_t *astnode); diff --git a/lib/src/parser.c b/lib/src/parser.c index a25267c..00552ca 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -462,8 +462,10 @@ static cypher_astnode_t *_rel_pattern(yycontext *yy, #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_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) @@ -3009,10 +3011,27 @@ cypher_astnode_t *_path_pattern(yycontext *yy, return add_child(yy, node); } -static cypher_astnode_t *_path_pattern_expression(yycontext *yy) { +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_expression( + 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)), @@ -3091,7 +3110,6 @@ static cypher_astnode_t *_path_pattern_edge(yycontext *yy, cypher_astnode_t *rel } 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, @@ -3102,7 +3120,6 @@ static cypher_astnode_t *_path_pattern_reference(yycontext *yy, cypher_astnode_t { abort_parse(yy); } - astnodes_clear(&(yy->prev_block->sequence)); astnodes_clear(&(yy->prev_block->children)); block_free(yy->prev_block); yy->prev_block = NULL; diff --git a/lib/src/parser.leg b/lib/src/parser.leg index d066529..921d87f 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -766,7 +766,7 @@ path-pattern = path-expression = < p:path-alternative { sequence_add(p); } (- p:path-alternative { sequence_add(p); } - )* > { $$ = path_pattern_expression(); } + )* - ( e:pattern-properties | e:_null_ ) > { $$ = path_pattern_expression(e); } path-alternative = < p:path-repetition { sequence_add(p); } @@ -784,27 +784,27 @@ path-repetition = ( - 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); } + - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(BIDIRECTIONAL, l, p); } ) ) -# TODO: make ast nodes for label, path-group, ..., path-reference path-base = - e:path-edge { $$ = e; } + p:path-edge { $$ = p; } | p:path-group { $$ = p; } | p:path-reference { $$ = p; } -# | path-any { } -# | p:path-node { $$ = 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; } + LEFT-SQ-PAREN - p:path-expression + - RIGHT-SQ-PAREN { $$ = p; } path-any = - < DASH > + < DASH > { $$ = path_pattern_any(); } path-node = < LEFT-PAREN - @@ -813,7 +813,7 @@ path-node = RIGHT-PAREN > { $$ = node_pattern(NULL, p); } path-reference = - < '~' - i:identifier > { path_pattern_reference(i); } + < '~' - i:identifier > { $$ = path_pattern_reference(i); } relationship-pattern = < ( LEFT-ARROW-HEAD - DASH - @@ -855,6 +855,8 @@ rel-varlength = | STAR - < (s:integer-literal | s:_null_) > { $$ = range(s, s); } | < STAR - > { $$ = range(NULL, NULL); } +# | < PLUS - > { $$ = range(1, NULL); } +# | < QUESTIONMARK - > { $$ = range(NULL, 1); } ) pattern-properties = map-literal | parameter From e8593da876a6b1dfd470f6f58553c2d6ba136688 Mon Sep 17 00:00:00 2001 From: Timur Date: Tue, 14 Apr 2020 17:27:19 +0300 Subject: [PATCH 16/22] Added one test for new syntax --- lib/test/check_pattern.c | 83 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/lib/test/check_pattern.c b/lib/test/check_pattern.c index 16119f2..1fea4ed 100644 --- a/lib/test/check_pattern.c +++ b/lib/test/check_pattern.c @@ -1105,6 +1105,88 @@ START_TEST (parse_all_shortest_paths) } END_TEST +START_TEST (parse_path_pattern) +{ + struct cypher_input_positionlast = 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 = 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), + CYPHER_REL_BIDIRECTIONAL); + + const cypher_astnode_t *expr = cypher_path_pattern_get_expression(path); + 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_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_int_eq(cypher_ast_path_pattern_alternative_get_element(alt, 1), NULL); + + const cypher_astnode_t *base = cypher_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); + ck_assert_str_eq(cypher_ast_path_pattern_reference_get_identifier(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 TCase* pattern_tcase(void) { @@ -1125,5 +1207,6 @@ 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_path_pattern); return tc; } From 9317257a93ae63ef7b5b2872361dc800473b10a1 Mon Sep 17 00:00:00 2001 From: Timur Date: Tue, 14 Apr 2020 18:27:47 +0300 Subject: [PATCH 17/22] Added multiple alternatives test. --- lib/test/check_pattern.c | 93 +++++++++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 11 deletions(-) diff --git a/lib/test/check_pattern.c b/lib/test/check_pattern.c index 1fea4ed..8cef174 100644 --- a/lib/test/check_pattern.c +++ b/lib/test/check_pattern.c @@ -1105,9 +1105,9 @@ START_TEST (parse_all_shortest_paths) } END_TEST -START_TEST (parse_path_pattern) +START_TEST (parse_simple_path_pattern) { - struct cypher_input_positionlast = cypher_input_position_zero; + 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); @@ -1156,29 +1156,30 @@ START_TEST (parse_path_pattern) 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 = cypher_ast_pattern_path_get_element(path, 1); + 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), + ck_assert_int_eq(cypher_ast_path_pattern_get_direction(path_pattern), CYPHER_REL_BIDIRECTIONAL); - const cypher_astnode_t *expr = cypher_path_pattern_get_expression(path); + 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_path_pattern_expression_get_element(expr, 0); + 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_int_eq(cypher_ast_path_pattern_alternative_get_element(alt, 1), NULL); + ck_assert_ptr_eq(cypher_ast_path_pattern_alternative_get_element(alt, 1), NULL); - const cypher_astnode_t *base = cypher_path_pattern_alternative_get_element(alt, 0); + 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); - ck_assert_str_eq(cypher_ast_path_pattern_reference_get_identifier(ref), "cousin"); - + 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); @@ -1188,6 +1189,75 @@ START_TEST (parse_path_pattern) } END_TEST +START_TEST (parse_path_pattern_multiple_alternatives) +{ + 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); + + 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_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 + TCase* pattern_tcase(void) { TCase *tc = tcase_create("pattern"); @@ -1207,6 +1277,7 @@ 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_path_pattern); + tcase_add_test(tc, parse_simple_path_pattern); + tcase_add_test(tc, parse_path_pattern_multiple_alternatives); return tc; } From 8fa639af646b139ea8173f1ed39c8bc71cce09f5 Mon Sep 17 00:00:00 2001 From: Timur Date: Thu, 16 Apr 2020 20:28:56 +0300 Subject: [PATCH 18/22] Added more path pattern tests and fixed ast output --- lib/src/ast_path_pattern_alternative.c | 5 ++- lib/src/ast_path_pattern_base.c | 2 +- lib/src/ast_path_pattern_edge.c | 2 +- lib/test/check_pattern.c | 62 ++++++++++++++++++++++++-- lib/test/check_query.c | 46 +++++++++++++++++++ 5 files changed, 110 insertions(+), 7 deletions(-) diff --git a/lib/src/ast_path_pattern_alternative.c b/lib/src/ast_path_pattern_alternative.c index b4de36e..bacef42 100644 --- a/lib/src/ast_path_pattern_alternative.c +++ b/lib/src/ast_path_pattern_alternative.c @@ -1,4 +1,7 @@ +#include "../../config.h" #include "astnode.h" +#include "util.h" +#include struct path_pattern_alternative { @@ -64,7 +67,7 @@ const cypher_astnode_t *cypher_ast_path_pattern_alternative_get_element( cypher_astnode_t *clone(const cypher_astnode_t *self, cypher_astnode_t **children) { - /**REQUIRE_TYPE(self, CYPHER_AST_PATH_PATTERN, NULL);*/ + 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 = diff --git a/lib/src/ast_path_pattern_base.c b/lib/src/ast_path_pattern_base.c index 32e90b3..c70bca6 100644 --- a/lib/src/ast_path_pattern_base.c +++ b/lib/src/ast_path_pattern_base.c @@ -117,7 +117,7 @@ ssize_t detailstr(const cypher_astnode_t *self, char *str, size_t size) { if (node->varlength != NULL) { - r = snprintf(str+n, (n < size)? size-n : 0, " range = @%u", + r = snprintf(str+n, (n < size)? size-n : 0, " range=@%u", node->varlength->ordinal); if (r < 0) { diff --git a/lib/src/ast_path_pattern_edge.c b/lib/src/ast_path_pattern_edge.c index f3ff219..9e977bc 100644 --- a/lib/src/ast_path_pattern_edge.c +++ b/lib/src/ast_path_pattern_edge.c @@ -71,5 +71,5 @@ cypher_astnode_t *clone(const cypher_astnode_t *self, 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); + return snprintf(str, size, "edge label=@%u", node->reltype->ordinal); } diff --git a/lib/test/check_pattern.c b/lib/test/check_pattern.c index 8cef174..54377bf 100644 --- a/lib/test/check_pattern.c +++ b/lib/test/check_pattern.c @@ -1189,7 +1189,7 @@ START_TEST (parse_simple_path_pattern) } END_TEST -START_TEST (parse_path_pattern_multiple_alternatives) +START_TEST (parse_path_pattern_multiple_elements) { struct cypher_input_position last = cypher_input_position_zero; result = cypher_parse("MATCH ()-/- -/-();", @@ -1232,9 +1232,6 @@ START_TEST (parse_path_pattern_multiple_alternatives) 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_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); @@ -1258,6 +1255,62 @@ START_TEST (parse_path_pattern_multiple_alternatives) } 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) { TCase *tc = tcase_create("pattern"); @@ -1278,6 +1331,7 @@ TCase* pattern_tcase(void) 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..e519642 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; } From b2c75148b1f8c44dcae2b3e3674bfecf0cd95649 Mon Sep 17 00:00:00 2001 From: Timur Date: Thu, 16 Apr 2020 20:43:57 +0300 Subject: [PATCH 19/22] Pretty much finished writing path pattern tests --- lib/test/check_pattern.c | 3 +-- lib/test/check_query.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/test/check_pattern.c b/lib/test/check_pattern.c index 54377bf..d0d9bea 100644 --- a/lib/test/check_pattern.c +++ b/lib/test/check_pattern.c @@ -1281,8 +1281,7 @@ START_TEST (parse_path_pattern_multiple_alternatives) "@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" - +"@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); diff --git a/lib/test/check_query.c b/lib/test/check_query.c index e519642..34518f2 100644 --- a/lib/test/check_query.c +++ b/lib/test/check_query.c @@ -214,7 +214,7 @@ START_TEST (parse_named_path_predicate_query) " @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" +"@11 25..27 > > > > node pattern ()\n"; ck_assert_str_eq(memstream_buffer, expected); ck_assert_int_eq(cypher_parse_result_ndirectives(result), 1); From 8ff19f656c1575e4a050ce00e05d7349ce4b1cd1 Mon Sep 17 00:00:00 2001 From: Timur Date: Thu, 16 Apr 2020 22:18:44 +0300 Subject: [PATCH 20/22] Added missing comments. --- lib/src/ast_path_pattern.c | 18 +-- lib/src/ast_path_pattern_any.c | 3 + lib/src/ast_path_pattern_base.c | 3 + lib/src/ast_path_pattern_edge.c | 3 + lib/src/ast_path_pattern_expression.c | 3 + lib/src/ast_path_pattern_reference.c | 3 + lib/src/cypher-parser.h.in | 177 +++++++++++++++++++++----- 7 files changed, 159 insertions(+), 51 deletions(-) diff --git a/lib/src/ast_path_pattern.c b/lib/src/ast_path_pattern.c index 13e9c3a..dc81978 100644 --- a/lib/src/ast_path_pattern.c +++ b/lib/src/ast_path_pattern.c @@ -1,19 +1,3 @@ -/* 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" @@ -65,7 +49,7 @@ cypher_astnode_t *cypher_ast_path_pattern(const cypher_astnode_t *expression, cypher_astnode_t *clone(const cypher_astnode_t *self, cypher_astnode_t **children) { - /**REQUIRE_TYPE(self, CYPHER_AST_PATH_PATTERN, NULL);*/ + 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, diff --git a/lib/src/ast_path_pattern_any.c b/lib/src/ast_path_pattern_any.c index 278e2bf..a1d9e51 100644 --- a/lib/src/ast_path_pattern_any.c +++ b/lib/src/ast_path_pattern_any.c @@ -1,4 +1,7 @@ +#include "../../config.h" #include "astnode.h" +#include "util.h" +#include struct path_pattern_any { diff --git a/lib/src/ast_path_pattern_base.c b/lib/src/ast_path_pattern_base.c index c70bca6..c69fadf 100644 --- a/lib/src/ast_path_pattern_base.c +++ b/lib/src/ast_path_pattern_base.c @@ -1,4 +1,7 @@ +#include "../../config.h" #include "astnode.h" +#include "util.h" +#include struct path_pattern_base { cypher_astnode_t _astnode; diff --git a/lib/src/ast_path_pattern_edge.c b/lib/src/ast_path_pattern_edge.c index 9e977bc..12483f5 100644 --- a/lib/src/ast_path_pattern_edge.c +++ b/lib/src/ast_path_pattern_edge.c @@ -1,4 +1,7 @@ +#include "../../config.h" #include "astnode.h" +#include "util.h" +#include struct path_pattern_edge { diff --git a/lib/src/ast_path_pattern_expression.c b/lib/src/ast_path_pattern_expression.c index d0a30a3..c64e826 100644 --- a/lib/src/ast_path_pattern_expression.c +++ b/lib/src/ast_path_pattern_expression.c @@ -1,4 +1,7 @@ +#include "../../config.h" #include "astnode.h" +#include "util.h" +#include struct path_pattern_expression { diff --git a/lib/src/ast_path_pattern_reference.c b/lib/src/ast_path_pattern_reference.c index fed4d54..5fd1231 100644 --- a/lib/src/ast_path_pattern_reference.c +++ b/lib/src/ast_path_pattern_reference.c @@ -1,4 +1,7 @@ +#include "../../config.h" #include "astnode.h" +#include "util.h" +#include struct path_pattern_reference { diff --git a/lib/src/cypher-parser.h.in b/lib/src/cypher-parser.h.in index afe099e..78cc7e9 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -5208,6 +5208,7 @@ const cypher_astnode_t *cypher_ast_rel_pattern_get_properties( * @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 @@ -5215,28 +5216,19 @@ 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_any(cypher_astnode_t **children, unsigned int nchildren, - struct cypher_input_range range); - -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); - -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); +/** + * 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); -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. * @@ -5251,44 +5243,161 @@ 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. + * 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 The direction of the relationship. + * @return A `CYPHER_AST_MAP` node, a `CYPHER_AST_PARAMETER` node, or null. */ -__cypherlang_pure -enum cypher_rel_direction cypher_ast_path_pattern_get_direction( +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 *astnode); + 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 *astnode, unsigned int index); + const cypher_astnode_t *node, unsigned int index); -const cypher_astnode_t *cypher_ast_path_pattern_get_properties( - const cypher_astnode_t *astnode); +/** + * 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 *astnode); + 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 *astnode); + 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 *astnode); + 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 *astnode); + 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 *astnode); + const cypher_astnode_t *node); + + + /** * Construct a `CYPHER_AST_RANGE` node. * From 4bb9d7f54113d2aaf0bf9dfaed65b0437b6141c1 Mon Sep 17 00:00:00 2001 From: Timur Date: Sat, 9 May 2020 17:24:54 +0300 Subject: [PATCH 21/22] Added "+" and "?" support --- lib/src/Makefile.am | 2 ++ lib/src/ast.c | 6 ++++ lib/src/ast_path_pattern_base.c | 9 +++++ lib/src/ast_range_optional.c | 59 +++++++++++++++++++++++++++++++++ lib/src/ast_range_plus.c | 59 +++++++++++++++++++++++++++++++++ lib/src/ast_rel_pattern.c | 7 ++-- lib/src/astnode.h | 2 ++ lib/src/cypher-parser.h.in | 34 +++++++++++++++++++ lib/src/parser.c | 41 ++++++++++++++++++++++- lib/src/parser.leg | 21 +++++++----- 10 files changed, 227 insertions(+), 13 deletions(-) create mode 100644 lib/src/ast_range_optional.c create mode 100644 lib/src/ast_range_plus.c diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am index 6774f9c..d2bd989 100644 --- a/lib/src/Makefile.am +++ b/lib/src/Makefile.am @@ -85,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 53e8ae5..db033ed 100644 --- a/lib/src/ast.c +++ b/lib/src/ast.c @@ -133,6 +133,8 @@ struct cypher_astnode_vts 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; @@ -257,6 +259,8 @@ static const struct cypher_astnode_vts cypher_astnode_vts = .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, @@ -391,6 +395,8 @@ 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_path_pattern_base.c b/lib/src/ast_path_pattern_base.c index c69fadf..7d389a0 100644 --- a/lib/src/ast_path_pattern_base.c +++ b/lib/src/ast_path_pattern_base.c @@ -57,6 +57,15 @@ enum cypher_rel_direction cypher_ast_path_pattern_base_get_direction( 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) { 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 fbbb0d9..05fc862 100644 --- a/lib/src/astnode.h +++ b/lib/src/astnode.h @@ -246,6 +246,8 @@ 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 78cc7e9..3db1df5 100644 --- a/lib/src/cypher-parser.h.in +++ b/lib/src/cypher-parser.h.in @@ -352,6 +352,10 @@ extern const cypher_astnode_type_t CYPHER_AST_PATH_PATTERN_EDGE; 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. */ @@ -5348,6 +5352,15 @@ cypher_astnode_t *cypher_ast_path_pattern_base(enum cypher_rel_direction directi 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. @@ -5439,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 00552ca..74de936 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -472,7 +472,7 @@ static cypher_astnode_t *_path_pattern_alternative(yycontext *yy); 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) +#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) @@ -481,6 +481,10 @@ static cypher_astnode_t *_path_pattern_reference(yycontext *yy, #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) @@ -3145,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 921d87f..926d3bd 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -776,15 +776,15 @@ path-alternative = path-repetition = < ( LEFT-ARROW-HEAD - p:path-base ( - RIGHT-ARROW-HEAD - - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(BIDIRECTIONAL, l, p); } + - (l:rel-varlength | l:rel-plus | l:rel-optional | l:_null_) > { $$ = path_pattern_base(BIDIRECTIONAL, l, p); } | _empty_ - - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(INBOUND, l, p); } + - (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:_null_) > { $$ = path_pattern_base(OUTBOUND, l, p); } + - (l:rel-varlength | l:rel-plus | l:rel-optional | l:_null_) > { $$ = path_pattern_base(OUTBOUND, l, p); } | _empty_ - - (l:rel-varlength | l:_null_) > { $$ = path_pattern_base(BIDIRECTIONAL, l, p); } + - (l:rel-varlength | l:rel-plus | l:rel-optional | l:_null_) > { $$ = path_pattern_base(BIDIRECTIONAL, l, p); } ) ) @@ -822,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); } @@ -834,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); } @@ -855,9 +855,12 @@ rel-varlength = | STAR - < (s:integer-literal | s:_null_) > { $$ = range(s, s); } | < STAR - > { $$ = range(NULL, NULL); } -# | < PLUS - > { $$ = range(1, NULL); } -# | < QUESTIONMARK - > { $$ = range(NULL, 1); } - ) + +rel-plus = + < PLUS > { $$ = range_plus(); } + +rel-optional = + < QUESTIONMARK > { $$ = range_optional(); } pattern-properties = map-literal | parameter From 9c2e7324ffccd0a0c3a9233b50e1761427feedf8 Mon Sep 17 00:00:00 2001 From: Timur Date: Sat, 9 May 2020 17:38:49 +0300 Subject: [PATCH 22/22] Fixed the biggest bug of them all --- lib/src/parser.leg | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/parser.leg b/lib/src/parser.leg index 926d3bd..0589ec5 100644 --- a/lib/src/parser.leg +++ b/lib/src/parser.leg @@ -855,6 +855,7 @@ rel-varlength = | STAR - < (s:integer-literal | s:_null_) > { $$ = range(s, s); } | < STAR - > { $$ = range(NULL, NULL); } + ) rel-plus = < PLUS > { $$ = range_plus(); }