Skip to main content

linkrs_query/parser/parsing/
parse_context.rs

1use std::sync::Arc;
2
3use crate::parser::core::error::{ParseError, ParseErrorKind};
4use crate::parser::lexing::LexError;
5use crate::parser::lexing::Lexer;
6use crate::parser::ParseErrors;
7use crate::parser::Token;
8use crate::parser::TokenKind;
9use linkrs_core::types::expr::expression_context::ExpressionAnalysisContext;
10use linkrs_core::types::{Position, Span};
11
12const RECOVERY_LIMIT: usize = 5;
13
14const STATEMENT_SYNC_TOKENS: &[TokenKind] = &[
15    TokenKind::Semicolon,
16    TokenKind::Match,
17    TokenKind::Go,
18    TokenKind::Fetch,
19    TokenKind::Insert,
20    TokenKind::Copy,
21    TokenKind::Delete,
22    TokenKind::Update,
23    TokenKind::Upsert,
24    TokenKind::Create,
25    TokenKind::Drop,
26    TokenKind::Show,
27    TokenKind::Explain,
28    TokenKind::Profile,
29    TokenKind::Group,
30    TokenKind::Lookup,
31    TokenKind::Find,
32    TokenKind::Return,
33    TokenKind::With,
34    TokenKind::Unwind,
35    TokenKind::Set,
36    TokenKind::Use,
37    TokenKind::Begin,
38    TokenKind::Commit,
39    TokenKind::Rollback,
40    TokenKind::Savepoint,
41    TokenKind::Release,
42    TokenKind::Search,
43    TokenKind::Dollar,
44    TokenKind::Eof,
45];
46
47const CLAUSE_SYNC_TOKENS: &[TokenKind] = &[
48    TokenKind::Semicolon,
49    TokenKind::Where,
50    TokenKind::Return,
51    TokenKind::With,
52    TokenKind::Yield,
53    TokenKind::Order,
54    TokenKind::Limit,
55    TokenKind::Skip,
56    TokenKind::Having,
57    TokenKind::Group,
58    TokenKind::Union,
59    TokenKind::Intersect,
60    TokenKind::SetMinus,
61    TokenKind::Pipe,
62    TokenKind::Match,
63    TokenKind::Go,
64    TokenKind::Find,
65    TokenKind::Lookup,
66    TokenKind::Insert,
67    TokenKind::Update,
68    TokenKind::Delete,
69    TokenKind::And,
70    TokenKind::Or,
71    TokenKind::Eof,
72];
73
74const EXPR_SYNC_TOKENS: &[TokenKind] = &[
75    TokenKind::Comma,
76    TokenKind::RParen,
77    TokenKind::RBracket,
78    TokenKind::RBrace,
79    TokenKind::Then,
80    TokenKind::Else,
81    TokenKind::End,
82];
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub enum RecoveryScope {
86    Statement,
87    Clause,
88    Expression,
89}
90
91pub struct ParseContext<'a> {
92    lexer: Lexer<'a>,
93    current_token: Token,
94    next_token: Option<Token>,
95    errors: ParseErrors,
96    compat_mode: bool,
97    upsert_mode: bool,
98    edge_syntax_mode: bool,
99    /// When true, a top-level `|` is NOT consumed as the bitwise-OR operator.
100    /// Set while parsing a list-comprehension source/filter operand so the
101    /// comprehension body separator `|` is not swallowed; cleared again inside
102    /// any bracketed group where `|` is unambiguously bitwise-OR.
103    suppress_pipe_or: bool,
104    recursion_depth: usize,
105    max_recursion_depth: usize,
106    expr_context: Arc<ExpressionAnalysisContext>,
107    recovery_count: usize,
108    /// Shared user-defined type-alias catalog. When set, unknown type names
109    /// resolve through aliases (`CAST(x AS alias)`, DDL column types).
110    type_alias_manager: Option<Arc<linkrs_core::metadata::TypeAliasManager>>,
111    /// Recursive comprehension parsed during edge pattern parsing
112    recursive_comprehension: Option<crate::parser::ast::pattern::RecursiveComprehension>,
113}
114
115pub struct ParseContextCheckpoint {
116    lexer_ckpt: crate::parser::lexing::LexerCheckpoint,
117    current_token: Token,
118    next_token: Option<Token>,
119}
120
121mod literals;
122
123impl<'a> ParseContext<'a> {
124    pub fn new(input: &'a str) -> Self {
125        let lexer = Lexer::new(input);
126        let current_token = lexer.current_token().clone();
127        let expr_context = Arc::new(ExpressionAnalysisContext::new());
128
129        Self {
130            lexer,
131            current_token,
132            next_token: None,
133            errors: ParseErrors::new(),
134            compat_mode: false,
135            upsert_mode: false,
136            edge_syntax_mode: false,
137            suppress_pipe_or: false,
138            recursion_depth: 0,
139            max_recursion_depth: 100,
140            expr_context,
141            recovery_count: 0,
142            type_alias_manager: None,
143            recursive_comprehension: None,
144        }
145    }
146
147    pub fn from_string(input: String) -> Self {
148        let lexer = Lexer::from_string(input);
149        let current_token = lexer.current_token().clone();
150        let expr_context = Arc::new(ExpressionAnalysisContext::new());
151
152        Self {
153            lexer,
154            current_token,
155            next_token: None,
156            errors: ParseErrors::new(),
157            compat_mode: false,
158            upsert_mode: false,
159            edge_syntax_mode: false,
160            suppress_pipe_or: false,
161            recursion_depth: 0,
162            max_recursion_depth: 100,
163            expr_context,
164            recovery_count: 0,
165            type_alias_manager: None,
166            recursive_comprehension: None,
167        }
168    }
169
170    pub fn checkpoint(&self) -> ParseContextCheckpoint {
171        ParseContextCheckpoint {
172            lexer_ckpt: self.lexer.checkpoint(),
173            current_token: self.current_token.clone(),
174            next_token: self.next_token.clone(),
175        }
176    }
177
178    pub fn restore(&mut self, ckpt: ParseContextCheckpoint) {
179        self.lexer.restore(ckpt.lexer_ckpt);
180        self.current_token = ckpt.current_token;
181        self.next_token = ckpt.next_token;
182    }
183
184    pub fn set_expression_context(&mut self, expr_context: Arc<ExpressionAnalysisContext>) {
185        self.expr_context = expr_context;
186    }
187
188    pub fn expression_context(&self) -> &Arc<ExpressionAnalysisContext> {
189        &self.expr_context
190    }
191
192    /// Attach the shared user-defined type-alias catalog.
193    pub fn set_type_alias_manager(
194        &mut self,
195        manager: Arc<linkrs_core::metadata::TypeAliasManager>,
196    ) {
197        self.type_alias_manager = Some(manager);
198    }
199
200    /// Resolve a raw type name through builtin types, then user-defined
201    /// aliases. Returns `None` when the name denotes neither.
202    pub fn resolve_named_type(&self, name: &str) -> Option<linkrs_core::DataType> {
203        if let Ok(builtin) = name.parse::<linkrs_core::DataType>() {
204            return Some(builtin);
205        }
206        let manager = self.type_alias_manager.as_ref()?;
207        manager
208            .resolve(name, &|text| text.parse::<linkrs_core::DataType>().ok())
209            .ok()
210    }
211
212    pub fn expression_context_clone(&self) -> Arc<ExpressionAnalysisContext> {
213        self.expr_context.clone()
214    }
215
216    pub fn take_recursive_comprehension(
217        &mut self,
218    ) -> Option<crate::parser::ast::pattern::RecursiveComprehension> {
219        self.recursive_comprehension.take()
220    }
221
222    pub fn set_recursive_comprehension(
223        &mut self,
224        rc: crate::parser::ast::pattern::RecursiveComprehension,
225    ) {
226        self.recursive_comprehension = Some(rc);
227    }
228
229    pub fn lexer(&self) -> &Lexer<'a> {
230        &self.lexer
231    }
232
233    pub fn lexer_mut(&mut self) -> &mut Lexer<'a> {
234        &mut self.lexer
235    }
236
237    pub fn set_compat_mode(&mut self, enabled: bool) {
238        self.compat_mode = enabled;
239    }
240
241    pub fn set_upsert_mode(&mut self, enabled: bool) {
242        self.upsert_mode = enabled;
243    }
244
245    pub fn is_upsert_mode(&self) -> bool {
246        self.upsert_mode
247    }
248
249    /// Whether the parser is inside a DML edge statement (`src -> dst`)
250    /// where `->` is an edge arrow rather than the JSON access operator.
251    pub fn is_edge_syntax_mode(&self) -> bool {
252        self.edge_syntax_mode
253    }
254
255    /// Run `f` with DML edge syntax enabled, then restore the previous
256    /// mode even on error.
257    pub fn with_edge_syntax_mode<T>(
258        &mut self,
259        f: impl FnOnce(&mut Self) -> Result<T, ParseError>,
260    ) -> Result<T, ParseError> {
261        let saved = self.edge_syntax_mode;
262        self.edge_syntax_mode = true;
263        let result = f(self);
264        self.edge_syntax_mode = saved;
265        result
266    }
267
268    /// Whether `|` should be treated as a list-comprehension separator instead
269    /// of the bitwise-OR operator in the current parsing position.
270    pub fn is_pipe_or_suppressed(&self) -> bool {
271        self.suppress_pipe_or
272    }
273
274    /// Run `f` with `suppress_pipe_or` forced to `value`, restoring the
275    /// previous setting afterwards (even on error).
276    pub fn with_pipe_or_suppression<T>(
277        &mut self,
278        value: bool,
279        f: impl FnOnce(&mut Self) -> Result<T, ParseError>,
280    ) -> Result<T, ParseError> {
281        let saved = self.suppress_pipe_or;
282        self.suppress_pipe_or = value;
283        let result = f(self);
284        self.suppress_pipe_or = saved;
285        result
286    }
287
288    pub fn enter_recursion(&mut self) -> Result<(), ParseError> {
289        self.recursion_depth += 1;
290        if self.recursion_depth > self.max_recursion_depth {
291            let pos = self.current_position();
292            Err(ParseError::new(
293                ParseErrorKind::SyntaxError,
294                "Recursion limit exceeded".to_string(),
295                pos,
296            ))
297        } else {
298            Ok(())
299        }
300    }
301
302    pub fn exit_recursion(&mut self) {
303        if self.recursion_depth > 0 {
304            self.recursion_depth -= 1;
305        }
306    }
307
308    pub fn add_error(&mut self, error: ParseError) {
309        self.errors.add(error);
310    }
311
312    pub fn add_lex_error(&mut self, error: LexError) {
313        self.errors.add(error.into());
314    }
315
316    pub fn errors(&self) -> &ParseErrors {
317        &self.errors
318    }
319
320    pub fn has_errors(&self) -> bool {
321        !self.errors.is_empty() || self.lexer.has_errors()
322    }
323
324    pub fn take_errors(&mut self) -> ParseErrors {
325        for lex_error in self.lexer.take_errors() {
326            self.errors.add(lex_error.into());
327        }
328        std::mem::take(&mut self.errors)
329    }
330
331    pub fn current_position(&self) -> Position {
332        self.lexer.current_position()
333    }
334
335    pub fn current_span(&self) -> Span {
336        let pos = self.current_position();
337        Span::new(pos, pos)
338    }
339
340    pub fn merge_span(&self, start: Position, end: Position) -> Span {
341        Span::new(start, end)
342    }
343
344    /// Span from `start` (usually another span's `.start`) up to the current
345    /// cursor position. Replaces the `end_span = ctx.current_span()` /
346    /// `merge_span(start, end_span.end)` idiom spread across the parsers.
347    pub fn span_since(&self, start: Position) -> Span {
348        let end = self.current_span();
349        Span::new(start, end.end)
350    }
351
352    pub fn current_token(&self) -> &Token {
353        &self.current_token
354    }
355
356    pub fn next_token(&mut self) {
357        if let Some(tok) = self.next_token.take() {
358            self.current_token = tok;
359        } else {
360            self.current_token = self.lexer.next_token();
361        }
362    }
363
364    /// Return the next token WITHOUT consuming it.
365    ///
366    /// The lookahead is buffered in the context, so the token returned here
367    /// is the one that the next [`ParseContext::next_token`] (or
368    /// `match_token` / `expect_token`) call will consume.
369    pub fn peek_token(&mut self) -> &Token {
370        if self.next_token.is_none() {
371            self.next_token = Some(self.lexer.next_token());
372        }
373        self.next_token
374            .as_ref()
375            .expect("lookahead buffer should be filled")
376    }
377
378    pub fn match_token(&mut self, expected: TokenKind) -> bool {
379        if self.current_token.kind == expected {
380            self.next_token();
381            true
382        } else {
383            false
384        }
385    }
386
387    pub fn check_token(&self, expected: TokenKind) -> bool {
388        self.current_token.kind == expected
389    }
390
391    pub fn expect_token(&mut self, expected: TokenKind) -> Result<(), ParseError> {
392        if self.current_token.kind == expected {
393            self.next_token();
394            Ok(())
395        } else {
396            let pos = self.current_position();
397            Err(ParseError::new(
398                ParseErrorKind::UnexpectedToken,
399                format!(
400                    "Expected {:?}, found {:?}",
401                    expected, self.current_token.kind
402                ),
403                pos,
404            )
405            .with_expected_tokens(vec![format!("{:?}", expected)]))
406        }
407    }
408
409    pub fn is_recovery_exhausted(&self) -> bool {
410        self.recovery_count >= RECOVERY_LIMIT
411    }
412
413    pub fn record_recovery(&mut self) {
414        self.recovery_count += 1;
415    }
416
417    pub fn reset_recovery_count(&mut self) {
418        self.recovery_count = 0;
419    }
420
421    pub fn synchronize(&mut self, scope: RecoveryScope) {
422        if self.is_recovery_exhausted() {
423            return;
424        }
425
426        let sync_tokens: &[TokenKind] = match scope {
427            RecoveryScope::Statement => STATEMENT_SYNC_TOKENS,
428            RecoveryScope::Clause => CLAUSE_SYNC_TOKENS,
429            RecoveryScope::Expression => EXPR_SYNC_TOKENS,
430        };
431
432        while self.current_token.kind != TokenKind::Eof {
433            if sync_tokens.contains(&self.current_token.kind) {
434                return;
435            }
436            self.next_token();
437        }
438    }
439
440    pub fn try_recover(
441        &mut self,
442        error: ParseError,
443        scope: RecoveryScope,
444    ) -> Result<(), ParseError> {
445        if self.is_recovery_exhausted() {
446            return Err(error);
447        }
448
449        self.add_error(error.mark_recovered());
450        self.record_recovery();
451        self.synchronize(scope);
452        Ok(())
453    }
454
455    /// Parse a clause body with clause-level error recovery.
456    ///
457    /// On failure the error is recorded and the parser synchronizes to the
458    /// next clause boundary keyword, so the remaining clauses of the
459    /// enclosing statement are still parsed. `fallback` must produce a
460    /// structurally valid placeholder for the failed clause; the query is
461    /// still rejected once the collected errors are inspected by the caller.
462    /// Returns the original error, unrecorded, when the recovery limit is
463    /// reached.
464    pub fn recover_clause<T>(
465        &mut self,
466        fallback: impl FnOnce(&mut ParseContext) -> Result<T, ParseError>,
467        parse: impl FnOnce(&mut ParseContext) -> Result<T, ParseError>,
468    ) -> Result<T, ParseError> {
469        match parse(self) {
470            Ok(value) => Ok(value),
471            Err(error) => match self.try_recover(error, RecoveryScope::Clause) {
472                Ok(()) => fallback(self),
473                Err(error) => Err(error),
474            },
475        }
476    }
477
478    pub fn consume_value(&mut self) -> Result<linkrs_core::Value, ParseError> {
479        match &self.current_token.kind {
480            TokenKind::StringLiteral(s) => {
481                let s = s.clone();
482                self.next_token();
483                Ok(linkrs_core::Value::string(s))
484            }
485            TokenKind::IntegerLiteral(n) => {
486                let n = *n;
487                self.next_token();
488                Ok(linkrs_core::Value::BigInt(n))
489            }
490            TokenKind::FloatLiteral(f) => {
491                let f = *f;
492                self.next_token();
493                Ok(linkrs_core::Value::Double(f))
494            }
495            TokenKind::BooleanLiteral(b) => {
496                let b = *b;
497                self.next_token();
498                Ok(linkrs_core::Value::Bool(b))
499            }
500            TokenKind::Null => {
501                self.next_token();
502                Ok(linkrs_core::Value::Null(linkrs_core::null::NullType::Null))
503            }
504            _ => {
505                let pos = self.current_position();
506                Err(ParseError::new(
507                    ParseErrorKind::UnexpectedToken,
508                    format!("Expected value, found {:?}", self.current_token.kind),
509                    pos,
510                ))
511            }
512        }
513    }
514}