Skip to main content

linkrs_query/parser/parsing/
parser.rs

1use std::sync::Arc;
2
3use crate::parser::ast::stmt::{Ast, Stmt};
4use crate::parser::core::error::{ParseError, ParseErrorKind};
5use crate::parser::parsing::expr_parser::parse_expression_with_context;
6use crate::parser::parsing::parse_context::{ParseContext, RecoveryScope};
7use crate::parser::parsing::stmt_parser::StmtParser;
8use crate::parser::TokenKind;
9use linkrs_core::types::expr::contextual::ContextualExpression;
10use linkrs_core::types::expr::expression_context::ExpressionAnalysisContext;
11
12/// Parser analysis results, including the AST (Statement + Expression Context).
13#[derive(Debug, Clone)]
14pub struct ParserResult {
15    /// Parsed AST (using Arc for shared ownership)
16    pub ast: Arc<Ast>,
17}
18
19pub struct Parser<'a> {
20    ctx: ParseContext<'a>,
21    expr_context: Arc<ExpressionAnalysisContext>,
22}
23
24impl<'a> Parser<'a> {
25    pub fn new(input: &'a str) -> Self {
26        let expr_context = Arc::new(ExpressionAnalysisContext::new());
27        let mut ctx = ParseContext::new(input);
28        ctx.set_expression_context(expr_context.clone());
29
30        Self { ctx, expr_context }
31    }
32
33    /// Attach the shared type-alias catalog so unknown type names resolve
34    /// through user-defined aliases (`CAST(x AS alias)`, DDL column types).
35    /// Defaults to off (previous behavior) when unset.
36    pub fn with_type_alias_manager(
37        mut self,
38        manager: Arc<linkrs_core::metadata::TypeAliasManager>,
39    ) -> Self {
40        self.ctx.set_type_alias_manager(manager);
41        self
42    }
43
44    pub fn from_string(input: String) -> Self {
45        let expr_context = Arc::new(ExpressionAnalysisContext::new());
46        let mut ctx = ParseContext::from_string(input);
47        ctx.set_expression_context(expr_context.clone());
48
49        Self { ctx, expr_context }
50    }
51
52    pub fn set_compat_mode(&mut self, enabled: bool) {
53        self.ctx.set_compat_mode(enabled);
54    }
55
56    pub fn parse(&mut self) -> Result<ParserResult, ParseError> {
57        let expr_context = Arc::new(ExpressionAnalysisContext::new());
58        self.ctx.set_expression_context(expr_context.clone());
59        self.expr_context = expr_context;
60        self.ctx.reset_recovery_count();
61
62        let stmt = match self.parse_statement() {
63            Ok(stmt) => stmt,
64            Err(e) => {
65                // Recovery may replace the original cause with a generic
66                // "Too many parse errors" terminator. Prefer the first recorded
67                // error so callers see the actionable diagnostic.
68                let recorded = self.ctx.errors();
69                if !recorded.is_empty() {
70                    let first = &recorded.errors[0];
71                    return Err(ParseError {
72                        kind: first.kind.clone(),
73                        message: Box::from(first.message.as_ref()),
74                        position: first.position,
75                        offset: first.offset,
76                        unexpected_token: first
77                            .unexpected_token
78                            .as_ref()
79                            .map(|t| Box::from(t.as_ref())),
80                        expected_tokens: first.expected_tokens.clone(),
81                        context: None,
82                        hints: first.hints.clone(),
83                        recovered: first.recovered,
84                    });
85                }
86                return Err(e);
87            }
88        };
89
90        // A statement must consume the whole input. An optional trailing
91        // semicolon terminator is allowed; any further token is a syntax
92        // error, otherwise trailing garbage such as `COMMIT junk` would be
93        // silently truncated to `COMMIT` and executed.
94        if self.ctx.match_token(TokenKind::Semicolon) {
95            // Optional statement terminator consumed.
96        }
97        if self.ctx.current_token().kind != TokenKind::Eof {
98            self.ctx.add_error(ParseError::new(
99                ParseErrorKind::UnexpectedToken,
100                format!(
101                    "Unexpected token after end of statement: {:?}",
102                    self.ctx.current_token().kind
103                ),
104                self.ctx.current_position(),
105            ));
106        }
107
108        let ast = Ast::new(stmt, self.expr_context.clone());
109        Ok(ParserResult { ast: Arc::new(ast) })
110    }
111
112    pub fn parse_statement(&mut self) -> Result<Stmt, ParseError> {
113        if self.ctx.is_recovery_exhausted() {
114            return Err(ParseError::new(
115                crate::parser::core::error::ParseErrorKind::SyntaxError,
116                "Too many parse errors, aborting".to_string(),
117                self.ctx.current_position(),
118            ));
119        }
120
121        match StmtParser::parse_statement(&mut self.ctx) {
122            Ok(stmt) => Ok(stmt),
123            Err(e) => {
124                if self.ctx.is_recovery_exhausted() {
125                    Err(e)
126                } else {
127                    self.ctx.try_recover(e, RecoveryScope::Statement)?;
128                    self.parse_statement()
129                }
130            }
131        }
132    }
133
134    pub fn parse_expression_contextual(&mut self) -> Result<ContextualExpression, ParseError> {
135        match parse_expression_with_context(&mut self.ctx, self.expr_context.clone()) {
136            Ok(expr) => Ok(expr),
137            Err(e) => {
138                if self.ctx.is_recovery_exhausted() {
139                    Err(e)
140                } else {
141                    self.ctx.try_recover(e, RecoveryScope::Expression)?;
142                    self.parse_expression_contextual()
143                }
144            }
145        }
146    }
147
148    pub fn expression_context(&self) -> &Arc<ExpressionAnalysisContext> {
149        &self.expr_context
150    }
151
152    pub fn expression_context_clone(&self) -> Arc<ExpressionAnalysisContext> {
153        self.expr_context.clone()
154    }
155
156    pub fn has_errors(&self) -> bool {
157        self.ctx.has_errors()
158    }
159
160    pub fn errors(&self) -> &crate::parser::ParseErrors {
161        self.ctx.errors()
162    }
163
164    pub fn take_errors(&mut self) -> crate::parser::ParseErrors {
165        self.ctx.take_errors()
166    }
167}