Skip to main content

linkrs_query/parser/parsing/dml_parser/
create.rs

1use super::*;
2use crate::parser::parsing::pattern_common;
3
4impl DmlParser {
5    /// Parse the Cypher-style CREATE data statement (the CREATE token has already been consumed)
6    /// Support for grammar:
7    ///   CREATE (n:Label {prop: value})
8    ///   CREATE (a)-[:Type {prop: value}]->(b)
9    ///   CREATE (a:Label1)-[:Type]->(b:Label2)
10    pub fn parse_create_data_after_token(
11        &mut self,
12        ctx: &mut ParseContext,
13        start_span: crate::parser::ast::types::Span,
14    ) -> Result<Stmt, ParseError> {
15        // List of analysis modes (multiple modes can be separated by commas)
16        let mut patterns = Vec::new();
17
18        loop {
19            let pattern = self.parse_create_pattern(ctx)?;
20            patterns.push(pattern);
21
22            if !ctx.match_token(TokenKind::Comma) {
23                break;
24            }
25        }
26
27        let span = ctx.span_since(start_span.start);
28
29        Ok(Stmt::Create(CreateStmt {
30            span,
31            target: CreateTarget::Path { patterns },
32            if_not_exists: false,
33        }))
34    }
35
36    /// Parse a Cypher data pattern: a node followed by any number of edge
37    /// hops (`-[...]->`, `<-[...]-`, `-[...]-`), mirroring how the
38    /// traversal parser spells edges in MATCH.
39    fn parse_create_pattern(
40        &mut self,
41        ctx: &mut ParseContext,
42    ) -> Result<crate::parser::ast::pattern::Pattern, ParseError> {
43        use crate::parser::ast::pattern::*;
44
45        let start_node = self.parse_node_pattern(ctx)?;
46
47        // An edge hop starts with an optional `<-` (BackArrow) followed by a
48        // mandatory `-` (Minus), so only those tokens continue the path.
49        // The braceless `(a)->(b)` form carries no edge body and stays
50        // unsupported, matching the traversal parser.
51        if !matches!(
52            ctx.current_token().kind,
53            TokenKind::Minus | TokenKind::BackArrow
54        ) {
55            return Ok(Pattern::Node(start_node));
56        }
57
58        let start = start_node.span.start;
59        let mut elements = vec![PathElement::Node(start_node)];
60        while matches!(
61            ctx.current_token().kind,
62            TokenKind::Minus | TokenKind::BackArrow
63        ) {
64            let edge = self.parse_edge_pattern(ctx)?;
65            elements.push(PathElement::CreateEdge(edge));
66
67            let node = self.parse_node_pattern(ctx)?;
68            elements.push(PathElement::Node(node));
69        }
70
71        let end_span = ctx.current_span();
72        Ok(Pattern::Path(PathPattern {
73            span: ctx.merge_span(start, end_span.end),
74            elements,
75            name: None,
76        }))
77    }
78
79    /// Parse node pattern: (var:Label {prop: value})
80    fn parse_node_pattern(
81        &mut self,
82        ctx: &mut ParseContext,
83    ) -> Result<crate::parser::ast::pattern::NodePattern, ParseError> {
84        use crate::parser::ast::pattern::*;
85
86        let start_span = ctx.current_span();
87        ctx.expect_token(TokenKind::LParen)?;
88
89        // Optional variable names
90        let variable = if ctx.current_token().kind.is_identifier() {
91            Some(ctx.expect_identifier()?)
92        } else {
93            None
94        };
95
96        let mut labels = Vec::new();
97        if ctx.match_token(TokenKind::Colon) {
98            loop {
99                let label = ctx.expect_identifier()?;
100                pattern_common::reject_qualified_reference(ctx, &label)?;
101                labels.push(label);
102                if !ctx.match_token(TokenKind::Colon) {
103                    break;
104                }
105            }
106        }
107
108        let properties = if ctx.match_token(TokenKind::LBrace) {
109            let props = self.parse_property_map(ctx)?;
110            ctx.expect_token(TokenKind::RBrace)?;
111            Some(props)
112        } else {
113            None
114        };
115
116        ctx.expect_token(TokenKind::RParen)?;
117
118        let span = ctx.span_since(start_span.start);
119
120        Ok(NodePattern {
121            span,
122            variable,
123            labels,
124            properties,
125            predicates: Vec::new(),
126        })
127    }
128
129    /// Parse edge pattern: -[:Type {prop: value}]-> or <-[:Type {prop: value}]-
130    /// Parse a Cypher edge pattern: an optional incoming `<-` (BackArrow),
131    /// a mandatory `-` (Minus), the bracketed edge body, and a mandatory
132    /// trailing direction. This is the same grammar the traversal parser
133    /// uses for MATCH edges.
134    fn parse_edge_pattern(
135        &mut self,
136        ctx: &mut ParseContext,
137    ) -> Result<crate::parser::ast::pattern::CreateEdgePattern, ParseError> {
138        use crate::parser::ast::pattern::*;
139
140        let start_span = ctx.current_span();
141
142        let incoming =
143            ctx.match_token(TokenKind::BackArrow) || ctx.match_token(TokenKind::LeftArrow);
144        ctx.expect_token(TokenKind::Minus)?;
145
146        ctx.expect_token(TokenKind::LBracket)?;
147
148        let variable = if ctx.current_token().kind.is_identifier() {
149            Some(ctx.expect_identifier()?)
150        } else {
151            None
152        };
153
154        let mut edge_types = Vec::new();
155        if ctx.match_token(TokenKind::Colon) {
156            let edge_type = ctx.expect_identifier()?;
157            pattern_common::reject_qualified_reference(ctx, &edge_type)?;
158            edge_types.push(edge_type);
159        }
160
161        let properties = if ctx.match_token(TokenKind::LBrace) {
162            let props = self.parse_property_map(ctx)?;
163            ctx.expect_token(TokenKind::RBrace)?;
164            Some(props)
165        } else {
166            None
167        };
168
169        ctx.expect_token(TokenKind::RBracket)?;
170
171        // The trailing direction closes the hop: `->` or `-`.
172        let closing = ctx.current_token().kind.clone();
173        let direction =
174            pattern_common::resolve_edge_direction(incoming, &closing).ok_or_else(|| {
175                ParseError::new(
176                    crate::parser::core::error::ParseErrorKind::SyntaxError,
177                    "Expected '-', '->', or '<-' after edge pattern".to_string(),
178                    ctx.current_position(),
179                )
180            })?;
181        ctx.next_token();
182
183        let span = ctx.span_since(start_span.start);
184
185        Ok(CreateEdgePattern {
186            span,
187            variable,
188            edge_types,
189            properties,
190            direction,
191        })
192    }
193}