Skip to main content

linkrs_query/parser/ast/
utils.rs

1//! Tool functions and auxiliary features
2
3use super::stmt::*;
4use linkrs_core::types::expr::expression_context::ExpressionAnalysisContext;
5use linkrs_core::types::expr::{ContextualExpression, Expression, ExpressionMeta};
6use linkrs_core::Value;
7use std::sync::Arc;
8
9/// Expression Factory – Used for creating expression nodes
10pub struct ExprFactory;
11
12impl ExprFactory {
13    /// Create a constant expression.
14    pub fn constant(value: Value, ctx: Arc<ExpressionAnalysisContext>) -> ContextualExpression {
15        let expr = Expression::Literal(value);
16        let meta = ExpressionMeta::new(expr);
17        let id = ctx.register_expression(meta);
18        ContextualExpression::new(id, ctx)
19    }
20
21    /// Create a variable expression.
22    pub fn variable(name: String, ctx: Arc<ExpressionAnalysisContext>) -> ContextualExpression {
23        let expr = Expression::Variable(name);
24        let meta = ExpressionMeta::new(expr);
25        let id = ctx.register_expression(meta);
26        ContextualExpression::new(id, ctx)
27    }
28
29    /// Create a binary expression
30    pub fn binary(
31        left: ContextualExpression,
32        op: linkrs_core::types::operators::BinaryOperator,
33        right: ContextualExpression,
34    ) -> ContextualExpression {
35        let ctx = left.context().clone();
36        let left_expr = left
37            .expression()
38            .expect("Left expression should exist")
39            .expression()
40            .clone();
41        let right_expr = right
42            .expression()
43            .expect("Right expression should exist")
44            .expression()
45            .clone();
46        let expr = Expression::Binary {
47            left: Box::new(left_expr),
48            op,
49            right: Box::new(right_expr),
50        };
51        let meta = ExpressionMeta::new(expr);
52        let id = ctx.register_expression(meta);
53        ContextualExpression::new(id, ctx)
54    }
55
56    /// Create a monomial expression.
57    pub fn unary(
58        op: linkrs_core::types::operators::UnaryOperator,
59        operand: ContextualExpression,
60    ) -> ContextualExpression {
61        let ctx = operand.context().clone();
62        let operand_expr = operand
63            .expression()
64            .expect("Operand expression should exist")
65            .expression()
66            .clone();
67        let expr = Expression::Unary {
68            op,
69            operand: Box::new(operand_expr),
70        };
71        let meta = ExpressionMeta::new(expr);
72        let id = ctx.register_expression(meta);
73        ContextualExpression::new(id, ctx)
74    }
75
76    /// Create a function call expression.
77    pub fn function_call(
78        name: String,
79        args: Vec<ContextualExpression>,
80        _distinct: bool,
81    ) -> ContextualExpression {
82        let ctx = if args.is_empty() {
83            Arc::new(ExpressionAnalysisContext::new())
84        } else {
85            args[0].context().clone()
86        };
87        let arg_exprs: Vec<Expression> = args
88            .iter()
89            .map(|arg| {
90                arg.expression()
91                    .expect("Arg expression should exist")
92                    .expression()
93                    .clone()
94            })
95            .collect();
96        let expr = Expression::function(name, arg_exprs);
97        let meta = ExpressionMeta::new(expr);
98        let id = ctx.register_expression(meta);
99        ContextualExpression::new(id, ctx)
100    }
101
102    /// Creating attribute access expressions
103    pub fn property_access(object: ContextualExpression, property: String) -> ContextualExpression {
104        let ctx = object.context().clone();
105        let object_expr = object
106            .expression()
107            .expect("Object expression should exist")
108            .expression()
109            .clone();
110        let expr = Expression::Property {
111            object: Box::new(object_expr),
112            property,
113        };
114        let meta = ExpressionMeta::new(expr);
115        let id = ctx.register_expression(meta);
116        ContextualExpression::new(id, ctx)
117    }
118
119    /// Create a list expression.
120    pub fn list(elements: Vec<ContextualExpression>) -> ContextualExpression {
121        let ctx = if elements.is_empty() {
122            Arc::new(ExpressionAnalysisContext::new())
123        } else {
124            elements[0].context().clone()
125        };
126        let element_exprs: Vec<Expression> = elements
127            .iter()
128            .map(|elem| {
129                elem.expression()
130                    .expect("Element expression should exist")
131                    .expression()
132                    .clone()
133            })
134            .collect();
135        let expr = Expression::List(element_exprs);
136        let meta = ExpressionMeta::new(expr);
137        let id = ctx.register_expression(meta);
138        ContextualExpression::new(id, ctx)
139    }
140
141    /// Create a mapping expression.
142    pub fn map(pairs: Vec<(String, ContextualExpression)>) -> ContextualExpression {
143        let ctx = if pairs.is_empty() {
144            Arc::new(ExpressionAnalysisContext::new())
145        } else {
146            pairs[0].1.context().clone()
147        };
148        let value_exprs: Vec<(String, Expression)> = pairs
149            .iter()
150            .map(|(key, value)| {
151                (
152                    key.clone(),
153                    value
154                        .expression()
155                        .expect("Value expression should exist")
156                        .expression()
157                        .clone(),
158                )
159            })
160            .collect();
161        let expr = Expression::Map(value_exprs);
162        let meta = ExpressionMeta::new(expr);
163        let id = ctx.register_expression(meta);
164        ContextualExpression::new(id, ctx)
165    }
166
167    /// Creating a CASE expression
168    pub fn case(
169        match_expression: Option<ContextualExpression>,
170        when_then_pairs: Vec<(ContextualExpression, ContextualExpression)>,
171        default: Option<ContextualExpression>,
172    ) -> ContextualExpression {
173        let ctx = match_expression
174            .as_ref()
175            .map(|e| e.context().clone())
176            .or_else(|| when_then_pairs.first().map(|(w, _)| w.context().clone()))
177            .or_else(|| default.as_ref().map(|d| d.context().clone()))
178            .unwrap_or_else(|| Arc::new(ExpressionAnalysisContext::new()));
179
180        let test_expr = match_expression.map(|e| {
181            Box::new(
182                e.expression()
183                    .expect("Match expression should exist")
184                    .expression()
185                    .clone(),
186            )
187        });
188        let conditions = when_then_pairs
189            .iter()
190            .map(|(when, then)| {
191                let when_expr = when
192                    .expression()
193                    .expect("When expression should exist")
194                    .expression()
195                    .clone();
196                let then_expr = then
197                    .expression()
198                    .expect("Then expression should exist")
199                    .expression()
200                    .clone();
201                (when_expr, then_expr)
202            })
203            .collect();
204        let default_expr = default.map(|d| {
205            Box::new(
206                d.expression()
207                    .expect("Default expression should exist")
208                    .expression()
209                    .clone(),
210            )
211        });
212        let expr = Expression::Case {
213            test_expr,
214            conditions,
215            default: default_expr,
216        };
217        let meta = ExpressionMeta::new(expr);
218        let id = ctx.register_expression(meta);
219        ContextualExpression::new(id, ctx)
220    }
221
222    /// Create an subscript expression.
223    pub fn subscript(
224        collection: ContextualExpression,
225        index: ContextualExpression,
226    ) -> ContextualExpression {
227        let ctx = collection.context().clone();
228        let collection_expr = collection
229            .expression()
230            .expect("Collection expression should exist")
231            .expression()
232            .clone();
233        let index_expr = index
234            .expression()
235            .expect("Index expression should exist")
236            .expression()
237            .clone();
238        let expr = Expression::Subscript {
239            collection: Box::new(collection_expr),
240            index: Box::new(index_expr),
241        };
242        let meta = ExpressionMeta::new(expr);
243        let id = ctx.register_expression(meta);
244        ContextualExpression::new(id, ctx)
245    }
246
247    /// Create a comparative expression
248    pub fn compare(
249        left: ContextualExpression,
250        op: linkrs_core::types::operators::BinaryOperator,
251        right: ContextualExpression,
252    ) -> ContextualExpression {
253        Self::binary(left, op, right)
254    }
255
256    /// Create a logical expression
257    pub fn logical(
258        left: ContextualExpression,
259        op: linkrs_core::types::operators::BinaryOperator,
260        right: ContextualExpression,
261    ) -> ContextualExpression {
262        Self::binary(left, op, right)
263    }
264
265    /// Create arithmetic expressions.
266    pub fn arithmetic(
267        left: ContextualExpression,
268        op: linkrs_core::types::operators::BinaryOperator,
269        right: ContextualExpression,
270    ) -> ContextualExpression {
271        Self::binary(left, op, right)
272    }
273}
274
275/// Statement Factory – Used for creating statement nodes
276pub struct StmtFactory;
277
278impl StmtFactory {
279    /// Create a query statement.
280    pub fn query(statements: Vec<Stmt>, span: Span) -> Stmt {
281        Stmt::Query(QueryStmt::new(statements, span))
282    }
283
284    /// Create the CREATE node statement.
285    pub fn create_node(
286        variable: Option<String>,
287        labels: Vec<String>,
288        properties: Option<ContextualExpression>,
289        span: Span,
290    ) -> Stmt {
291        Stmt::Create(CreateStmt {
292            span,
293            target: CreateTarget::Node {
294                variable,
295                labels,
296                properties,
297            },
298            if_not_exists: false,
299        })
300    }
301
302    /// Create a CREATE edge statement.
303    pub fn create_edge(
304        variable: Option<String>,
305        edge_type: String,
306        src: ContextualExpression,
307        dst: ContextualExpression,
308        properties: Option<ContextualExpression>,
309        direction: EdgeDirection,
310        span: Span,
311    ) -> Stmt {
312        Stmt::Create(CreateStmt {
313            span,
314            target: CreateTarget::Edge {
315                variable,
316                edge_type,
317                src,
318                dst,
319                properties,
320                direction,
321            },
322            if_not_exists: false,
323        })
324    }
325
326    /// Create a MATCH statement
327    pub fn match_stmt(
328        patterns: Vec<Pattern>,
329        where_clause: Option<ContextualExpression>,
330        return_clause: Option<ReturnClause>,
331        order_by: Option<OrderByClause>,
332        limit: Option<LimitClause>,
333        skip: Option<SkipClause>,
334        span: Span,
335    ) -> Stmt {
336        Stmt::Match(MatchStmt {
337            span,
338            patterns,
339            join_hint: None,
340            where_clause,
341            return_clause,
342            order_by,
343            limit,
344            skip,
345            optional: false,
346            delete_clause: None,
347        })
348    }
349
350    /// Create a DELETE statement
351    pub fn delete(target: DeleteTarget, span: Span) -> Stmt {
352        Stmt::Delete(DeleteStmt {
353            span,
354            target,
355            with_edge: false,
356            detach: false,
357        })
358    }
359
360    /// Create a DELETE statement with the WITH EDGE option
361    pub fn delete_with_edge(target: DeleteTarget, span: Span) -> Stmt {
362        Stmt::Delete(DeleteStmt {
363            span,
364            target,
365            with_edge: true,
366            detach: false,
367        })
368    }
369
370    /// Create an UPDATE statement
371    pub fn update(
372        target: UpdateTarget,
373        set_clause: SetClause,
374        where_clause: Option<ContextualExpression>,
375        span: Span,
376    ) -> Stmt {
377        Stmt::Update(UpdateStmt {
378            span,
379            target,
380            set_clause,
381            where_clause,
382            is_upsert: false,
383            yield_clause: None,
384        })
385    }
386
387    /// Create GO statements
388    pub fn go(
389        steps: Steps,
390        from: FromClause,
391        over: Option<OverClause>,
392        where_clause: Option<ContextualExpression>,
393        yield_clause: Option<YieldClause>,
394        span: Span,
395    ) -> Stmt {
396        Stmt::Go(GoStmt {
397            span,
398            steps,
399            from,
400            over,
401            where_clause,
402            yield_clause,
403        })
404    }
405
406    /// Create a FETCH statement
407    pub fn fetch(target: FetchTarget, span: Span) -> Stmt {
408        Stmt::Fetch(FetchStmt {
409            span,
410            target,
411            yield_clause: None,
412        })
413    }
414
415    /// Create a USE statement
416    pub fn r#use(space: String, span: Span) -> Stmt {
417        Stmt::Use(UseStmt { span, space })
418    }
419
420    /// Creating a SHOW statement
421    pub fn show(target: ShowTarget, span: Span) -> Stmt {
422        Stmt::Show(ShowStmt { span, target })
423    }
424
425    /// Creating an EXPLAIN statement
426    pub fn explain(statement: Box<Stmt>, span: Span) -> Stmt {
427        Stmt::Explain(ExplainStmt {
428            span,
429            statement,
430            format: ExplainFormat::default(),
431            analyze: false,
432        })
433    }
434
435    /// Creating a formatted EXPLAIN statement
436    pub fn explain_with_format(statement: Box<Stmt>, format: ExplainFormat, span: Span) -> Stmt {
437        Stmt::Explain(ExplainStmt {
438            span,
439            statement,
440            format,
441            analyze: false,
442        })
443    }
444
445    /// Create a PROFILE statement
446    pub fn profile(statement: Box<Stmt>, span: Span) -> Stmt {
447        Stmt::Profile(ProfileStmt {
448            span,
449            statement,
450            format: ExplainFormat::default(),
451        })
452    }
453
454    /// Create a formatted PROFILE statement.
455    pub fn profile_with_format(statement: Box<Stmt>, format: ExplainFormat, span: Span) -> Stmt {
456        Stmt::Profile(ProfileStmt {
457            span,
458            statement,
459            format,
460        })
461    }
462
463    /// Create a LOOKUP statement
464    pub fn lookup(
465        target: LookupTarget,
466        where_clause: Option<ContextualExpression>,
467        yield_clause: Option<YieldClause>,
468        span: Span,
469    ) -> Stmt {
470        Stmt::Lookup(LookupStmt {
471            span,
472            target,
473            where_clause,
474            yield_clause,
475        })
476    }
477
478    /// Create the SUBGRAPH statement
479    pub fn subgraph(
480        steps: Steps,
481        from: FromClause,
482        over: Option<OverClause>,
483        where_clause: Option<ContextualExpression>,
484        yield_clause: Option<YieldClause>,
485        span: Span,
486    ) -> Stmt {
487        Stmt::Subgraph(SubgraphStmt {
488            span,
489            steps,
490            from,
491            over,
492            where_clause,
493            yield_clause,
494            with_prop: false,
495        })
496    }
497
498    /// Create the FIND PATH statement
499    pub fn find_path(
500        from: FromClause,
501        to: ContextualExpression,
502        over: Option<OverClause>,
503        where_clause: Option<ContextualExpression>,
504        shortest: bool,
505        yield_clause: Option<YieldClause>,
506        span: Span,
507    ) -> Stmt {
508        Stmt::FindPath(FindPathStmt {
509            span,
510            from,
511            to,
512            over,
513            where_clause,
514            shortest,
515            max_steps: None,
516            limit: None,
517            skip: None,
518            yield_clause,
519            weight_expression: None,
520            heuristic_expression: None,
521            with_loop: false,
522            with_trail: false,
523            with_walk: false,
524        })
525    }
526}
527
528/// Pattern Factory – Used for creating pattern nodes
529pub struct PatternFactory;
530
531impl PatternFactory {
532    /// Create a node pattern.
533    pub fn node(
534        variable: Option<String>,
535        labels: Vec<String>,
536        properties: Option<ContextualExpression>,
537        predicates: Vec<ContextualExpression>,
538        span: Span,
539    ) -> Pattern {
540        Pattern::Node(NodePattern::new(
541            variable, labels, properties, predicates, span,
542        ))
543    }
544
545    /// Create a edge pattern.
546    pub fn edge(
547        variable: Option<String>,
548        edge_types: Vec<String>,
549        properties: Option<ContextualExpression>,
550        predicates: Vec<ContextualExpression>,
551        direction: EdgeDirection,
552        range: Option<EdgeRange>,
553        span: Span,
554    ) -> Pattern {
555        Pattern::Edge(EdgePattern::new(
556            variable, edge_types, properties, predicates, direction, range, span,
557        ))
558    }
559
560    /// Create a path pattern
561    pub fn path(elements: Vec<PathElement>, span: Span) -> Pattern {
562        Pattern::Path(PathPattern::new(elements, span))
563    }
564
565    /// Create a variable pattern.
566    pub fn variable(name: String, span: Span) -> Pattern {
567        Pattern::Variable(VariablePattern::new(name, span))
568    }
569
570    /// Create a simple node pattern.
571    pub fn simple_node(variable: Option<String>, labels: Vec<String>, span: Span) -> Pattern {
572        Self::node(variable, labels, None, vec![], span)
573    }
574
575    /// Create a simple edge pattern.
576    pub fn simple_edge(
577        variable: Option<String>,
578        edge_types: Vec<String>,
579        direction: EdgeDirection,
580        span: Span,
581    ) -> Pattern {
582        Self::edge(variable, edge_types, None, vec![], direction, None, span)
583    }
584
585    /// Create a directed edge pattern
586    pub fn directed_edge(
587        variable: Option<String>,
588        edge_type: String,
589        direction: EdgeDirection,
590        span: Span,
591    ) -> Pattern {
592        Self::simple_edge(variable, vec![edge_type], direction, span)
593    }
594
595    /// Create an undirected edge pattern.
596    pub fn undirected_edge(variable: Option<String>, edge_type: String, span: Span) -> Pattern {
597        Self::simple_edge(variable, vec![edge_type], EdgeDirection::Both, span)
598    }
599}
600
601/// The AST builder is used for constructing complex AST (Abstract Syntax Tree) structures.
602pub struct AstBuilder {
603    span: Span,
604}
605
606impl AstBuilder {
607    pub fn new(span: Span) -> Self {
608        Self { span }
609    }
610
611    /// Constructing a simple MATCH query
612    pub fn build_simple_match(
613        &self,
614        pattern: Pattern,
615        return_expression: ContextualExpression,
616    ) -> Stmt {
617        let return_clause = ReturnClause {
618            span: self.span,
619            items: vec![ReturnItem::Expression {
620                expression: return_expression,
621                alias: None,
622            }],
623            distinct: false,
624            order_by: None,
625            limit: None,
626            skip: None,
627            sample: None,
628            having_clause: None,
629        };
630
631        StmtFactory::match_stmt(
632            vec![pattern],
633            None,
634            Some(return_clause),
635            None,
636            None,
637            None,
638            self.span,
639        )
640    }
641
642    /// Constructing a simple CREATE node query
643    pub fn build_create_node(&self, variable: Option<String>, labels: Vec<String>) -> Stmt {
644        StmtFactory::create_node(variable, labels, None, self.span)
645    }
646
647    /// Constructing a simple CREATE edge query
648    pub fn build_create_edge(
649        &self,
650        variable: Option<String>,
651        edge_type: String,
652        src: ContextualExpression,
653        dst: ContextualExpression,
654        direction: EdgeDirection,
655    ) -> Stmt {
656        StmtFactory::create_edge(variable, edge_type, src, dst, None, direction, self.span)
657    }
658
659    /// Constructing a simple DELETE query
660    pub fn build_delete_vertices(&self, tag: String, vertices: Vec<ContextualExpression>) -> Stmt {
661        StmtFactory::delete(
662            DeleteTarget::Vertices {
663                tag,
664                vids: vertices,
665            },
666            self.span,
667        )
668    }
669
670    /// Constructing a simple UPDATE query
671    pub fn build_update_vertex(
672        &self,
673        vertex: ContextualExpression,
674        assignments: Vec<Assignment>,
675    ) -> Stmt {
676        let set_clause = SetClause {
677            span: self.span,
678            assignments,
679        };
680
681        StmtFactory::update(UpdateTarget::Vertex(vertex), set_clause, None, self.span)
682    }
683}
684
685#[cfg(test)]
686mod tests {
687    use super::*;
688    use linkrs_core::Value;
689    use std::sync::Arc;
690
691    #[test]
692    fn test_expr_factory() {
693        let ctx = Arc::new(ExpressionAnalysisContext::new());
694
695        // Testing constant expressions
696        let const_expression = ExprFactory::constant(Value::Int(42), ctx.clone());
697        assert!(const_expression.expression().is_some());
698
699        // Test variable expressions
700        let var_expression = ExprFactory::variable("x".into(), ctx.clone());
701        assert!(var_expression.expression().is_some());
702
703        // Testing binary expressions
704        let left = ExprFactory::constant(Value::Int(5), ctx.clone());
705        let right = ExprFactory::constant(Value::Int(3), ctx.clone());
706        let binary_expression = ExprFactory::binary(
707            left,
708            linkrs_core::types::operators::BinaryOperator::Add,
709            right,
710        );
711        assert!(binary_expression.expression().is_some());
712    }
713}