Skip to main content

linkrs_query/parser/parsing/parse_context/
literals.rs

1//! Literal, identifier and keyword consumption helpers of `ParseContext`.
2//!
3//! The token-consumption surface of the parser context is kept apart from
4//! the context core (construction, checkpointing, token access, error
5//! recovery). Methods of the same type may be defined across files, so the
6//! public API of `ParseContext` is unchanged.
7
8use super::ParseContext;
9use crate::parser::core::error::{ParseError, ParseErrorKind};
10use crate::parser::TokenKind;
11
12impl<'a> ParseContext<'a> {
13    pub fn expect_identifier(&mut self) -> Result<String, ParseError> {
14        match &self.current_token.kind {
15            TokenKind::Identifier(s) => {
16                let id = s.clone();
17                self.next_token();
18                Ok(id)
19            }
20            // Allow certain keywords to be used as identifiers.
21            TokenKind::Count => {
22                self.next_token();
23                Ok("count".to_string())
24            }
25            TokenKind::Sum => {
26                self.next_token();
27                Ok("sum".to_string())
28            }
29            TokenKind::Avg => {
30                self.next_token();
31                Ok("avg".to_string())
32            }
33            TokenKind::Min => {
34                self.next_token();
35                Ok("min".to_string())
36            }
37            TokenKind::Max => {
38                self.next_token();
39                Ok("max".to_string())
40            }
41            TokenKind::Weight => {
42                self.next_token();
43                Ok("weight".to_string())
44            }
45            TokenKind::User | TokenKind::Order => {
46                let s = self.current_token.lexeme.clone();
47                self.next_token();
48                Ok(s)
49            }
50            TokenKind::Status => {
51                self.next_token();
52                Ok("status".to_string())
53            }
54            TokenKind::Contains => {
55                self.next_token();
56                Ok("CONTAINS".to_string())
57            }
58            TokenKind::Tags => {
59                self.next_token();
60                Ok("tags".to_string())
61            }
62            TokenKind::Tag => {
63                self.next_token();
64                Ok("tag".to_string())
65            }
66            TokenKind::Path => {
67                self.next_token();
68                Ok("path".to_string())
69            }
70            TokenKind::Vertex => {
71                self.next_token();
72                Ok("vertex".to_string())
73            }
74            TokenKind::KeywordVector => {
75                self.next_token();
76                Ok("VECTOR".to_string())
77            }
78            TokenKind::Vertices => {
79                self.next_token();
80                Ok("vertices".to_string())
81            }
82            TokenKind::Edges => {
83                self.next_token();
84                Ok("edges".to_string())
85            }
86            TokenKind::Admin
87            | TokenKind::God
88            | TokenKind::Dba
89            | TokenKind::Guest
90            | TokenKind::Space => {
91                let s = self.current_token.lexeme.clone();
92                self.next_token();
93                Ok(s)
94            }
95            // Reserved words usable as tag/property names (e.g.
96            // `CREATE TAG ... (data STRING)`, `MATCH (:transaction)`)
97            // and as configuration module/key names (e.g.
98            // `SHOW CONFIGS transaction`, `UPDATE CONFIGS database host`).
99            TokenKind::Data | TokenKind::Transaction | TokenKind::Host => {
100                let s = self.current_token.lexeme.clone();
101                self.next_token();
102                Ok(s)
103            }
104            // Access-mode keywords are only reserved after BEGIN; allow them
105            // as identifiers elsewhere (e.g. a tag/property named `read`).
106            TokenKind::Read | TokenKind::Only | TokenKind::Write => {
107                let s = self.current_token.lexeme.clone();
108                self.next_token();
109                Ok(s)
110            }
111            // Option-keywords used as keys in LOAD/EXPORT OPTIONS (...)
112            TokenKind::Format | TokenKind::Header | TokenKind::Delimiter => {
113                let s = self.current_token.lexeme.clone();
114                self.next_token();
115                Ok(s)
116            }
117            // Type-name keywords are accepted as identifiers so they can be
118            // used as macro/tag/property names (e.g. `CREATE MACRO double(x)`
119            // or a tag literally named `int`). This mirrors the expression
120            // parser's handling of type keywords as function/variable names.
121            TokenKind::Bool
122            | TokenKind::Int
123            | TokenKind::Int8
124            | TokenKind::Int16
125            | TokenKind::Int32
126            | TokenKind::Int64
127            | TokenKind::Float
128            | TokenKind::Double
129            | TokenKind::String
130            | TokenKind::FixedString
131            | TokenKind::Timestamp
132            | TokenKind::Date
133            | TokenKind::Time
134            | TokenKind::Datetime
135            | TokenKind::Serial
136            | TokenKind::Geography
137            | TokenKind::List
138            | TokenKind::Map
139            | TokenKind::Struct
140            | TokenKind::Array
141            | TokenKind::UUID
142            | TokenKind::Duration => {
143                let s = self.current_token.lexeme.clone();
144                self.next_token();
145                Ok(s)
146            }
147            _ => {
148                let pos = self.current_position();
149                Err(ParseError::new(
150                    ParseErrorKind::UnexpectedToken,
151                    format!("Expected identifier, found {:?}", self.current_token.kind),
152                    pos,
153                )
154                .with_expected_tokens(vec!["identifier".to_string()]))
155            }
156        }
157    }
158
159    /// Parse a boolean literal (`true` / `false`).
160    ///
161    /// Unlike a fuzzy identifier comparison, any non-boolean token is reported
162    /// explicitly so that `SET LOCKED = yes` fails instead of silently
163    /// behaving as `false`.
164    pub fn expect_boolean_literal(&mut self) -> Result<bool, ParseError> {
165        match self.current_token.kind {
166            TokenKind::BooleanLiteral(value) => {
167                self.next_token();
168                Ok(value)
169            }
170            _ => {
171                let pos = self.current_position();
172                Err(ParseError::new(
173                    ParseErrorKind::UnexpectedToken,
174                    format!(
175                        "Expected boolean value, found {:?}",
176                        self.current_token.kind
177                    ),
178                    pos,
179                )
180                .with_expected_tokens(vec!["true".to_string(), "false".to_string()]))
181            }
182        }
183    }
184
185    /// Parse a DCL object name (username / space name).
186    ///
187    /// Accepts identifiers, keywords, numbers, string literals and hyphenated
188    /// sequences (e.g. `user-name-123`, `NULL`, `12345`, `"'; DROP USER --"`).
189    pub fn expect_dcl_name(&mut self) -> Result<String, ParseError> {
190        let mut name = self.expect_dcl_name_part()?;
191        while self.match_token(TokenKind::Minus) {
192            name.push('-');
193            name.push_str(&self.expect_dcl_name_part()?);
194        }
195        Ok(name)
196    }
197
198    fn expect_dcl_name_part(&mut self) -> Result<String, ParseError> {
199        let token = self.current_token().clone();
200        match &token.kind {
201            TokenKind::Identifier(s) | TokenKind::StringLiteral(s) => {
202                let s = s.clone();
203                self.next_token();
204                Ok(s)
205            }
206            TokenKind::IntegerLiteral(_) => {
207                let lexeme = token.lexeme.clone();
208                self.next_token();
209                Ok(lexeme)
210            }
211            _ => {
212                // Structural DCL keywords cannot be used as object names, so
213                // that e.g. `CREATE USER WITH PASSWORD 'x'` reports an error
214                // instead of treating WITH as the username.
215                if matches!(
216                    token.kind,
217                    TokenKind::With
218                        | TokenKind::Password
219                        | TokenKind::Role
220                        | TokenKind::To
221                        | TokenKind::From
222                        | TokenKind::On
223                        | TokenKind::In
224                        | TokenKind::If
225                        | TokenKind::Exists
226                        | TokenKind::Set
227                        | TokenKind::Locked
228                ) {
229                    let pos = self.current_position();
230                    return Err(ParseError::new(
231                        ParseErrorKind::UnexpectedToken,
232                        format!("Expected name, found {:?}", self.current_token.kind),
233                        pos,
234                    ));
235                }
236                if token.lexeme.is_empty() {
237                    let pos = self.current_position();
238                    Err(ParseError::new(
239                        ParseErrorKind::UnexpectedToken,
240                        format!("Expected name, found {:?}", self.current_token.kind),
241                        pos,
242                    ))
243                } else {
244                    let lexeme = token.lexeme.clone();
245                    self.next_token();
246                    Ok(lexeme)
247                }
248            }
249        }
250    }
251
252    pub fn expect_string_literal(&mut self) -> Result<String, ParseError> {
253        match &self.current_token.kind {
254            TokenKind::StringLiteral(s) => {
255                let s = s.clone();
256                self.next_token();
257                Ok(s)
258            }
259            _ => {
260                let pos = self.current_position();
261                Err(ParseError::new(
262                    ParseErrorKind::UnexpectedToken,
263                    format!(
264                        "Expected string literal, found {:?}",
265                        self.current_token.kind
266                    ),
267                    pos,
268                )
269                .with_expected_tokens(vec!["string literal".to_string()]))
270            }
271        }
272    }
273
274    pub fn expect_integer_literal(&mut self) -> Result<i64, ParseError> {
275        match &self.current_token.kind {
276            TokenKind::IntegerLiteral(n) => {
277                let n = *n;
278                self.next_token();
279                Ok(n)
280            }
281            _ => {
282                let pos = self.current_position();
283                Err(ParseError::new(
284                    ParseErrorKind::UnexpectedToken,
285                    format!(
286                        "Expected integer literal, found {:?}",
287                        self.current_token.kind
288                    ),
289                    pos,
290                )
291                .with_expected_tokens(vec!["integer literal".to_string()]))
292            }
293        }
294    }
295
296    pub fn expect_float_literal(&mut self) -> Result<f64, ParseError> {
297        match &self.current_token.kind {
298            TokenKind::FloatLiteral(f) => {
299                let f = *f;
300                self.next_token();
301                Ok(f)
302            }
303            _ => {
304                let pos = self.current_position();
305                Err(ParseError::new(
306                    ParseErrorKind::UnexpectedToken,
307                    format!(
308                        "Expected float literal, found {:?}",
309                        self.current_token.kind
310                    ),
311                    pos,
312                )
313                .with_expected_tokens(vec!["float literal".to_string()]))
314            }
315        }
316    }
317
318    pub fn is_identifier_or_in_token(&self) -> bool {
319        matches!(
320            self.current_token.kind,
321            TokenKind::Identifier(_) | TokenKind::In
322        )
323    }
324
325    pub fn is_identifier_token(&self) -> bool {
326        matches!(
327            self.current_token.kind,
328            TokenKind::Identifier(_)
329                | TokenKind::Count
330                | TokenKind::Sum
331                | TokenKind::Avg
332                | TokenKind::Min
333                | TokenKind::Max
334                | TokenKind::Weight
335                | TokenKind::User
336                | TokenKind::Order
337                | TokenKind::Status
338                | TokenKind::Contains
339                | TokenKind::Tag
340                | TokenKind::Tags
341                | TokenKind::Path
342                | TokenKind::Vertex
343                | TokenKind::Vertices
344                | TokenKind::Edges
345        )
346    }
347
348    pub fn check_keyword(&mut self, keyword: &str) -> bool {
349        match &self.current_token.kind {
350            TokenKind::Identifier(kw) => kw.eq_ignore_ascii_case(keyword),
351            TokenKind::Index => keyword.eq_ignore_ascii_case("INDEX"),
352            TokenKind::On => keyword.eq_ignore_ascii_case("ON"),
353            TokenKind::Drop => keyword.eq_ignore_ascii_case("DROP"),
354            TokenKind::Create => keyword.eq_ignore_ascii_case("CREATE"),
355            TokenKind::Alter => keyword.eq_ignore_ascii_case("ALTER"),
356            TokenKind::Show => keyword.eq_ignore_ascii_case("SHOW"),
357            TokenKind::Desc => {
358                keyword.eq_ignore_ascii_case("DESC") || keyword.eq_ignore_ascii_case("DESCRIBE")
359            }
360            TokenKind::Search => keyword.eq_ignore_ascii_case("SEARCH"),
361            TokenKind::Lookup => keyword.eq_ignore_ascii_case("LOOKUP"),
362            TokenKind::Match => keyword.eq_ignore_ascii_case("MATCH"),
363            TokenKind::KeywordVector => keyword.eq_ignore_ascii_case("VECTOR"),
364            TokenKind::If => keyword.eq_ignore_ascii_case("IF"),
365            TokenKind::Not => keyword.eq_ignore_ascii_case("NOT"),
366            TokenKind::Exists => keyword.eq_ignore_ascii_case("EXISTS"),
367            TokenKind::User => keyword.eq_ignore_ascii_case("USER"),
368            TokenKind::Tag => keyword.eq_ignore_ascii_case("TAG"),
369            TokenKind::Edge => keyword.eq_ignore_ascii_case("EDGE"),
370            TokenKind::Space => keyword.eq_ignore_ascii_case("SPACE"),
371            TokenKind::Insert => keyword.eq_ignore_ascii_case("INSERT"),
372            TokenKind::Delete => keyword.eq_ignore_ascii_case("DELETE"),
373            TokenKind::Detach => keyword.eq_ignore_ascii_case("DETACH"),
374            TokenKind::Update => keyword.eq_ignore_ascii_case("UPDATE"),
375            TokenKind::Return => keyword.eq_ignore_ascii_case("RETURN"),
376            TokenKind::Where => keyword.eq_ignore_ascii_case("WHERE"),
377            TokenKind::Set => keyword.eq_ignore_ascii_case("SET"),
378            TokenKind::Remove => keyword.eq_ignore_ascii_case("REMOVE"),
379            TokenKind::Add => keyword.eq_ignore_ascii_case("ADD"),
380            TokenKind::With => keyword.eq_ignore_ascii_case("WITH"),
381            TokenKind::Yield => keyword.eq_ignore_ascii_case("YIELD"),
382            TokenKind::Go => keyword.eq_ignore_ascii_case("GO"),
383            TokenKind::Over => keyword.eq_ignore_ascii_case("OVER"),
384            TokenKind::Step => keyword.eq_ignore_ascii_case("STEP"),
385            TokenKind::Upto => keyword.eq_ignore_ascii_case("UPTO"),
386            TokenKind::Limit => keyword.eq_ignore_ascii_case("LIMIT"),
387            TokenKind::Asc => keyword.eq_ignore_ascii_case("ASC"),
388            TokenKind::Order => keyword.eq_ignore_ascii_case("ORDER"),
389            TokenKind::By => keyword.eq_ignore_ascii_case("BY"),
390            TokenKind::Skip => keyword.eq_ignore_ascii_case("SKIP"),
391            TokenKind::Unwind => keyword.eq_ignore_ascii_case("UNWIND"),
392            TokenKind::Optional => keyword.eq_ignore_ascii_case("OPTIONAL"),
393            TokenKind::Distinct => keyword.eq_ignore_ascii_case("DISTINCT"),
394            TokenKind::All => keyword.eq_ignore_ascii_case("ALL"),
395            TokenKind::Null => keyword.eq_ignore_ascii_case("NULL"),
396            TokenKind::Is => keyword.eq_ignore_ascii_case("IS"),
397            TokenKind::And => keyword.eq_ignore_ascii_case("AND"),
398            TokenKind::Or => keyword.eq_ignore_ascii_case("OR"),
399            TokenKind::Xor => keyword.eq_ignore_ascii_case("XOR"),
400            TokenKind::Contains => keyword.eq_ignore_ascii_case("CONTAINS"),
401            TokenKind::StartsWith => keyword.eq_ignore_ascii_case("STARTS WITH"),
402            TokenKind::EndsWith => keyword.eq_ignore_ascii_case("ENDS WITH"),
403            TokenKind::Case => keyword.eq_ignore_ascii_case("CASE"),
404            TokenKind::When => keyword.eq_ignore_ascii_case("WHEN"),
405            TokenKind::Then => keyword.eq_ignore_ascii_case("THEN"),
406            TokenKind::Else => keyword.eq_ignore_ascii_case("ELSE"),
407            TokenKind::End => keyword.eq_ignore_ascii_case("END"),
408            TokenKind::Union => keyword.eq_ignore_ascii_case("UNION"),
409            TokenKind::Intersect => keyword.eq_ignore_ascii_case("INTERSECT"),
410            TokenKind::SetMinus => keyword.eq_ignore_ascii_case("SET MINUS"),
411            TokenKind::Group => keyword.eq_ignore_ascii_case("GROUP"),
412            TokenKind::Having => keyword.eq_ignore_ascii_case("HAVING"),
413            TokenKind::Between => keyword.eq_ignore_ascii_case("BETWEEN"),
414            TokenKind::Comment => keyword.eq_ignore_ascii_case("COMMENT"),
415            TokenKind::Admin => keyword.eq_ignore_ascii_case("ADMIN"),
416            TokenKind::Edges => keyword.eq_ignore_ascii_case("EDGES"),
417            TokenKind::Vertex => keyword.eq_ignore_ascii_case("VERTEX"),
418            TokenKind::Vertices => keyword.eq_ignore_ascii_case("VERTICES"),
419            TokenKind::Tags => keyword.eq_ignore_ascii_case("TAGS"),
420            TokenKind::Indexes => keyword.eq_ignore_ascii_case("INDEXES"),
421            TokenKind::Find => keyword.eq_ignore_ascii_case("FIND"),
422            TokenKind::Path => keyword.eq_ignore_ascii_case("PATH"),
423            TokenKind::From => keyword.eq_ignore_ascii_case("FROM"),
424            TokenKind::To => keyword.eq_ignore_ascii_case("TO"),
425            TokenKind::As => keyword.eq_ignore_ascii_case("AS"),
426            TokenKind::Upsert => keyword.eq_ignore_ascii_case("UPSERT"),
427            TokenKind::In => keyword.eq_ignore_ascii_case("IN"),
428            TokenKind::Rollback => keyword.eq_ignore_ascii_case("ROLLBACK"),
429            _ => false,
430        }
431    }
432
433    /// Check if the next tokens match the given sequence (non-consuming)
434    pub fn check_keyword_sequence(&mut self, keywords: &[&str]) -> bool {
435        if keywords.is_empty() {
436            return false;
437        }
438
439        // Save lexer state
440        let saved_lexer = self.lexer.clone();
441        let saved_token = self.current_token.clone();
442
443        // Check each keyword in sequence
444        for (i, &keyword) in keywords.iter().enumerate() {
445            if i > 0 {
446                self.next_token();
447            }
448            if !self.check_keyword(keyword) {
449                // Restore lexer state
450                self.lexer = saved_lexer;
451                self.current_token = saved_token;
452                return false;
453            }
454        }
455
456        // Restore lexer state
457        self.lexer = saved_lexer;
458        self.current_token = saved_token;
459        true
460    }
461
462    pub fn consume_keyword(&mut self, keyword: &str) -> Result<(), ParseError> {
463        if self.check_keyword(keyword) {
464            self.next_token();
465            Ok(())
466        } else {
467            let pos = self.current_position();
468            Err(ParseError::new(
469                ParseErrorKind::UnexpectedToken,
470                format!(
471                    "Expected keyword '{}', found {:?}",
472                    keyword, self.current_token.kind
473                ),
474                pos,
475            ))
476        }
477    }
478
479    pub fn consume_identifier(&mut self) -> Result<String, ParseError> {
480        self.expect_identifier()
481    }
482
483    pub fn consume_string(&mut self) -> Result<String, ParseError> {
484        self.expect_string_literal()
485    }
486
487    pub fn consume_float(&mut self) -> Result<f64, ParseError> {
488        self.expect_float_literal()
489    }
490
491    pub fn consume_int(&mut self) -> Result<i64, ParseError> {
492        self.expect_integer_literal()
493    }
494
495    pub fn consume_token(&mut self, token: &str) -> Result<(), ParseError> {
496        match token {
497            "(" => self.expect_token(TokenKind::LParen),
498            ")" => self.expect_token(TokenKind::RParen),
499            "," => self.expect_token(TokenKind::Comma),
500            ";" => self.expect_token(TokenKind::Semicolon),
501            ":" => self.expect_token(TokenKind::Colon),
502            "+" => self.expect_token(TokenKind::Plus),
503            "-" => self.expect_token(TokenKind::Minus),
504            "*" => self.expect_token(TokenKind::Star),
505            "/" => self.expect_token(TokenKind::Div),
506            "=" => self.expect_token(TokenKind::Eq),
507            "!=" => self.expect_token(TokenKind::Ne),
508            "<" => self.expect_token(TokenKind::Lt),
509            "<=" => self.expect_token(TokenKind::Le),
510            ">" => self.expect_token(TokenKind::Gt),
511            ">=" => self.expect_token(TokenKind::Ge),
512            _ => {
513                let pos = self.current_position();
514                Err(ParseError::new(
515                    ParseErrorKind::UnexpectedToken,
516                    format!("Unsupported token: {}", token),
517                    pos,
518                ))
519            }
520        }
521    }
522
523    pub fn consume_optional_token(&mut self, token: &str) -> bool {
524        match token {
525            "(" => self.match_token(TokenKind::LParen),
526            ")" => self.match_token(TokenKind::RParen),
527            "," => self.match_token(TokenKind::Comma),
528            ";" => self.match_token(TokenKind::Semicolon),
529            ":" => self.match_token(TokenKind::Colon),
530            "+" => self.match_token(TokenKind::Plus),
531            "-" => self.match_token(TokenKind::Minus),
532            "*" => self.match_token(TokenKind::Star),
533            "/" => self.match_token(TokenKind::Div),
534            "=" => self.match_token(TokenKind::Eq),
535            "!=" => self.match_token(TokenKind::Ne),
536            "<" => self.match_token(TokenKind::Lt),
537            "<=" => self.match_token(TokenKind::Le),
538            ">" => self.match_token(TokenKind::Gt),
539            ">=" => self.match_token(TokenKind::Ge),
540            _ => false,
541        }
542    }
543
544    pub fn try_consume_string(&mut self) -> Option<String> {
545        if let TokenKind::StringLiteral(s) = &self.current_token.kind {
546            let s = s.clone();
547            self.next_token();
548            Some(s)
549        } else {
550            None
551        }
552    }
553
554    pub fn try_consume_quoted_string(&mut self) -> Option<String> {
555        self.try_consume_string()
556    }
557}