Skip to main content

uqa_graph/cypher/
parser.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Recursive-descent parser for the supported openCypher grammar.
8
9use std::collections::BTreeMap;
10
11use uqa_core::Value;
12
13use crate::cypher::ast::{
14    BinaryOp, CaseExpr, CreateClause, CypherClause, CypherExpr, CypherQuery, DeleteClause,
15    FunctionCall, InList, IsNotNull, IsNull, ListComprehension, ListIndex, ListLiteral, ListSlice,
16    Literal, MapLiteral, MatchClause, MergeClause, NodePattern, OrderByItem, Parameter,
17    PathElement, PathPattern, PropertyAccess, RelDirection, RelPattern, ReturnClause, ReturnItem,
18    SetClause, SetItem, SetOperator, UnaryOp, UnwindClause, Variable, WithClause,
19};
20use crate::cypher::lexer::{is_keyword, tokenize, LexError, Token, TokenKind};
21
22mod atoms;
23mod clauses;
24mod expressions;
25mod patterns;
26mod stream;
27
28#[derive(Debug, thiserror::Error, PartialEq)]
29pub enum ParseError {
30    #[error(transparent)]
31    Lex(#[from] LexError),
32    #[error("expected {expected}, got {got:?} ({value:?}) at position {position}")]
33    Expected {
34        expected: &'static str,
35        got: TokenKind,
36        value: String,
37        position: usize,
38    },
39    #[error("expected keyword {keyword:?}, got {got:?} at position {position}")]
40    ExpectedKeyword {
41        keyword: &'static str,
42        got: String,
43        position: usize,
44    },
45    #[error("unexpected token {got:?} at position {position}")]
46    Unexpected { got: String, position: usize },
47}
48
49/// Parse a Cypher query string into a Cypher query AST.
50pub fn parse_cypher(source: &str) -> Result<CypherQuery, ParseError> {
51    let tokens = tokenize(source)?;
52    let mut parser = Parser { tokens, pos: 0 };
53    parser.parse()
54}
55
56struct Parser {
57    tokens: Vec<Token>,
58    pos: usize,
59}
60
61const RESERVED_KEYWORDS: &[&str] = &[
62    "AND",
63    "AS",
64    "ASC",
65    "BY",
66    "CASE",
67    "CONTAINS",
68    "CREATE",
69    "DELETE",
70    "DESC",
71    "DETACH",
72    "DISTINCT",
73    "ELSE",
74    "END",
75    "ENDS",
76    "EXISTS",
77    "FALSE",
78    "IN",
79    "IS",
80    "LIMIT",
81    "MATCH",
82    "MERGE",
83    "NODE",
84    "NOT",
85    "NULL",
86    "ON",
87    "OPTIONAL",
88    "OR",
89    "ORDER",
90    "RELATIONSHIP",
91    "REMOVE",
92    "RETURN",
93    "SET",
94    "SKIP",
95    "STARTS",
96    "THEN",
97    "TRUE",
98    "UNWIND",
99    "WHEN",
100    "WHERE",
101    "WITH",
102    "XOR",
103];
104
105#[cfg(test)]
106mod tests;