jetcrab\parser/
recovery.rs

1use crate::lexer::{Token, TokenKind};
2use crate::parser::error::ParserError;
3use crate::vm::types::ErrorCount;
4
5#[derive(Debug, Clone, PartialEq)]
6pub enum RecoveryStrategy {
7    SkipUntil(Vec<String>),
8    SkipUntilStatement,
9    SkipUntilBlock,
10    SkipUntilFunction,
11    SkipUntilClass,
12    SkipUntilModule,
13    InsertToken(String),
14    ReplaceToken(String),
15    DeleteToken,
16    NoRecovery,
17}
18
19#[derive(Debug, Clone)]
20pub struct RecoveryContext {
21    pub current_token: Option<Token>,
22    pub previous_token: Option<Token>,
23    pub recovery_tokens: Vec<String>,
24    pub context: ParsingContext,
25}
26
27#[derive(Debug, Clone, PartialEq)]
28pub enum ParsingContext {
29    TopLevel,
30    Statement,
31    Block,
32    Function,
33    Class,
34    Module,
35    Expression,
36    Declaration,
37}
38
39impl RecoveryContext {
40    pub fn new(
41        current_token: Option<Token>,
42        previous_token: Option<Token>,
43        context: ParsingContext,
44    ) -> Self {
45        Self {
46            current_token,
47            previous_token,
48            recovery_tokens: Vec::new(),
49            context,
50        }
51    }
52
53    pub fn with_recovery_tokens(mut self, tokens: Vec<String>) -> Self {
54        self.recovery_tokens = tokens;
55        self
56    }
57
58    pub fn determine_strategy(&self) -> RecoveryStrategy {
59        match &self.context {
60            ParsingContext::TopLevel => {
61                if let Some(token) = &self.current_token {
62                    match token.kind {
63                        TokenKind::Semicolon | TokenKind::RightBrace => {
64                            RecoveryStrategy::SkipUntil(vec![";".to_string(), "}".to_string()])
65                        }
66                        _ => RecoveryStrategy::SkipUntilStatement,
67                    }
68                } else {
69                    RecoveryStrategy::NoRecovery
70                }
71            }
72
73            ParsingContext::Statement => {
74                RecoveryStrategy::SkipUntil(vec![";".to_string(), "}".to_string(), ")".to_string()])
75            }
76
77            ParsingContext::Block => RecoveryStrategy::SkipUntil(vec!["}".to_string()]),
78
79            ParsingContext::Function => {
80                RecoveryStrategy::SkipUntil(vec!["}".to_string(), ";".to_string()])
81            }
82
83            ParsingContext::Class => RecoveryStrategy::SkipUntil(vec!["}".to_string()]),
84
85            ParsingContext::Module => RecoveryStrategy::SkipUntil(vec![
86                "}".to_string(),
87                "import".to_string(),
88                "export".to_string(),
89            ]),
90
91            ParsingContext::Expression => RecoveryStrategy::SkipUntil(vec![
92                ";".to_string(),
93                ",".to_string(),
94                ")".to_string(),
95                "]".to_string(),
96                "}".to_string(),
97            ]),
98
99            ParsingContext::Declaration => {
100                RecoveryStrategy::SkipUntil(vec![";".to_string(), "}".to_string()])
101            }
102        }
103    }
104
105    pub fn is_recovery_token(&self, token: &Token) -> bool {
106        let token_str = format!("{:?}", token.kind);
107        self.recovery_tokens.iter().any(|t| token_str.contains(t))
108    }
109
110    pub fn current_position(&self) -> Option<crate::ast::Position> {
111        self.current_token.as_ref().map(|t| crate::ast::Position {
112            line: t.start().line,
113            column: t.start().column,
114        })
115    }
116}
117
118#[derive(Debug)]
119pub struct ErrorRecovery {
120    max_errors: ErrorCount,
121    error_count: ErrorCount,
122    errors: Vec<ParserError>,
123}
124
125impl ErrorRecovery {
126    pub fn new(max_errors: usize) -> Self {
127        Self {
128            max_errors: ErrorCount::new(max_errors),
129            error_count: ErrorCount::new(0),
130            errors: Vec::new(),
131        }
132    }
133
134    pub fn can_recover(&self) -> bool {
135        self.error_count.as_usize() < self.max_errors.as_usize()
136    }
137
138    pub fn add_error(&mut self, error: ParserError) {
139        self.errors.push(error);
140        self.error_count.increment();
141    }
142
143    pub fn errors(&self) -> &[ParserError] {
144        &self.errors
145    }
146
147    pub fn clear_errors(&mut self) {
148        self.errors.clear();
149        self.error_count.reset();
150    }
151
152    pub fn error_count(&self) -> usize {
153        self.error_count.as_usize()
154    }
155
156    pub fn has_errors(&self) -> bool {
157        !self.errors.is_empty()
158    }
159}
160
161impl Default for ErrorRecovery {
162    fn default() -> Self {
163        Self::new(100)
164    }
165}