Skip to main content

ktrs_parser/parsing/
abstract_kotlin_parsing.rs

1//! Port of `AbstractKotlinParsing.java`.
2
3use ktrs_syntax::SyntaxKind::{self, *};
4
5use super::Parser;
6use crate::builder::{EdgeBinder, Layer, Marker};
7use crate::kt_tokens::{self, WHITE_SPACE_OR_COMMENT_BIT_SET};
8use crate::token_set::TokenSet;
9
10impl Parser {
11    pub(crate) fn get_last_token(&mut self) -> Option<SyntaxKind> {
12        let mut i = 1;
13        let current_offset = self.my_builder.get_current_offset();
14        while i <= current_offset && WHITE_SPACE_OR_COMMENT_BIT_SET.contains(self.my_builder.raw_lookup(-i)) {
15            i += 1;
16        }
17        self.my_builder.raw_lookup(-i)
18    }
19
20    pub(crate) fn expect_2(&mut self, expectation: SyntaxKind, message: &str) -> bool {
21        self.expect_3(expectation, message, None)
22    }
23
24    pub(crate) fn mark(&mut self) -> Marker {
25        self.my_builder.mark()
26    }
27
28    pub(crate) fn error(&mut self, message: &str) {
29        self.my_builder.error(message);
30    }
31
32    pub(crate) fn expect_3(&mut self, expectation: SyntaxKind, message: &str, recovery_set: Option<TokenSet>) -> bool {
33        if self.expect(expectation) {
34            return true;
35        }
36
37        self.error_with_recovery(message, recovery_set);
38
39        false
40    }
41
42    pub(crate) fn expect(&mut self, expectation: SyntaxKind) -> bool {
43        if self.at(expectation) {
44            self.advance(); // expectation
45            return true;
46        }
47
48        if expectation == IDENTIFIER && self.my_builder.get_token_text() == Some("`") {
49            self.advance();
50        }
51
52        false
53    }
54
55    pub(crate) fn expect_no_advance(&mut self, expectation: SyntaxKind, message: &str) {
56        if self.at(expectation) {
57            self.advance(); // expectation
58            return;
59        }
60
61        self.error(message);
62    }
63
64    pub(crate) fn error_with_recovery(&mut self, message: &str, recovery_set: Option<TokenSet>) {
65        let tt = self.tt();
66        let recover = match recovery_set {
67            None => true,
68            Some(set) => {
69                set.contains(tt)
70                    || tt == Some(LBRACE)
71                    || tt == Some(RBRACE)
72                    || (set.contains(EOL_OR_SEMICOLON)
73                        && (self.eof() || tt == Some(SEMICOLON) || self.my_builder.newline_before_current_token()))
74            }
75        };
76        if recover {
77            self.error(message);
78        } else {
79            self.error_and_advance(message);
80        }
81    }
82
83    pub(crate) fn error_and_advance(&mut self, message: &str) {
84        self.error_and_advance_2(message, 1);
85    }
86
87    pub(crate) fn error_and_advance_2(&mut self, message: &str, advance_token_count: i32) {
88        let err = self.mark();
89        self.advance_1(advance_token_count);
90        err.error(self, message);
91    }
92
93    pub(crate) fn eof(&mut self) -> bool {
94        self.my_builder.eof()
95    }
96
97    pub(crate) fn advance(&mut self) {
98        self.my_builder.advance_lexer();
99    }
100
101    pub(crate) fn advance_1(&mut self, advance_token_count: i32) {
102        for _ in 0..advance_token_count {
103            self.advance(); // erroneous token
104        }
105    }
106
107    pub(crate) fn advance_at(&mut self, current: SyntaxKind) {
108        debug_assert!(self._at(current));
109        self.my_builder.advance_lexer();
110    }
111
112    /// `tokenId` is just an int alias of the `KtToken`; the kind itself serves (None = INVALID_Id).
113    pub(crate) fn get_token_id(&mut self) -> Option<SyntaxKind> {
114        self.tt()
115    }
116
117    #[inline]
118    pub(crate) fn tt(&mut self) -> Option<SyntaxKind> {
119        self.my_builder.get_token_type()
120    }
121
122    /// Side-effect-free version of `at()`.
123    #[inline]
124    pub(crate) fn _at(&mut self, expectation: SyntaxKind) -> bool {
125        let token = self.tt();
126        self.token_matches(token, expectation)
127    }
128
129    #[inline]
130    fn token_matches(&mut self, token: Option<SyntaxKind>, expectation: SyntaxKind) -> bool {
131        if token == Some(expectation) {
132            return true;
133        }
134        if expectation == EOL_OR_SEMICOLON {
135            if self.eof() {
136                return true;
137            }
138            if token == Some(SEMICOLON) {
139                return true;
140            }
141            if self.my_builder.newline_before_current_token() {
142                return true;
143            }
144        }
145        false
146    }
147
148    #[inline]
149    pub(crate) fn at(&mut self, expectation: SyntaxKind) -> bool {
150        let token = self.tt();
151        if token == Some(expectation) {
152            return true;
153        }
154        // Inlined so a constant `expectation` folds this away; the rest can only match in these cases.
155        if expectation != EOL_OR_SEMICOLON && expectation != IDENTIFIER && !kt_tokens::is_soft_keyword(expectation) {
156            return false;
157        }
158        self.at_rest(token, expectation)
159    }
160
161    /// The body of upstream's `at` after its `tt()`.
162    #[inline(never)]
163    fn at_rest(&mut self, token: Option<SyntaxKind>, expectation: SyntaxKind) -> bool {
164        // `_at(expectation)`, keeping its `tt()` for the second lookup (which can't differ).
165        if self.token_matches(token, expectation) {
166            return true;
167        }
168        if token == Some(IDENTIFIER)
169            && kt_tokens::is_soft_keyword(expectation)
170            && expectation.keyword_text() == self.my_builder.get_token_text()
171        {
172            self.my_builder.remap_current_token(expectation);
173            return true;
174        }
175        if expectation == IDENTIFIER && token.is_some_and(kt_tokens::is_soft_keyword) {
176            self.my_builder.remap_current_token(IDENTIFIER);
177            return true;
178        }
179        false
180    }
181
182    /// Side-effect-free version of `at_set()`.
183    pub(crate) fn _at_set(&mut self, set: TokenSet) -> bool {
184        let token = self.tt();
185        self.token_matches_set(token, set)
186    }
187
188    fn token_matches_set(&mut self, token: Option<SyntaxKind>, set: TokenSet) -> bool {
189        if set.contains(token) {
190            return true;
191        }
192        if set.contains(EOL_OR_SEMICOLON) {
193            if self.eof() {
194                return true;
195            }
196            if token == Some(SEMICOLON) {
197                return true;
198            }
199            if self.my_builder.newline_before_current_token() {
200                return true;
201            }
202        }
203        false
204    }
205
206    #[inline]
207    pub(crate) fn at_set(&mut self, set: TokenSet) -> bool {
208        let token = self.tt();
209        if set.contains(token) {
210            return true;
211        }
212        // As in `at`: folds away for a constant `set`.
213        if !set.contains(EOL_OR_SEMICOLON) && !set.contains(IDENTIFIER) && !set.intersects(kt_tokens::SOFT_KEYWORDS) {
214            return false;
215        }
216        self.at_set_rest(token, set)
217    }
218
219    /// The body of upstream's `atSet` after its `tt()`.
220    #[inline(never)]
221    fn at_set_rest(&mut self, token: Option<SyntaxKind>, set: TokenSet) -> bool {
222        // `_at_set(set)`, keeping its `tt()` for the second lookup (which can't differ).
223        if self.token_matches_set(token, set) {
224            return true;
225        }
226        if token == Some(IDENTIFIER) {
227            // Only a soft keyword in `set` can match by text, so most sets skip the text lookup.
228            if !set.intersects(kt_tokens::SOFT_KEYWORDS) {
229                return false;
230            }
231            let keyword_token = kt_tokens::soft_keyword_by_text(self.my_builder.get_token_text());
232            if let Some(keyword_token) = keyword_token.filter(|&k| set.contains(k)) {
233                self.my_builder.remap_current_token(keyword_token);
234                return true;
235            }
236        } else {
237            // We know at this point that `set` does not contain `token`
238            if set.contains(IDENTIFIER) && token.is_some_and(kt_tokens::is_soft_keyword) {
239                self.my_builder.remap_current_token(IDENTIFIER);
240                return true;
241            }
242        }
243        false
244    }
245
246    pub(crate) fn lookahead(&mut self, k: i32) -> Option<SyntaxKind> {
247        self.my_builder.look_ahead(k)
248    }
249
250    pub(crate) fn consume_if(&mut self, token: SyntaxKind) -> bool {
251        if self.at(token) {
252            self.advance(); // token
253            return true;
254        }
255        false
256    }
257
258    // TODO: Migrate to predicates
259    pub(crate) fn skip_until(&mut self, token_set: TokenSet) {
260        let stop_at_eol_or_semi = token_set.contains(EOL_OR_SEMICOLON);
261        while !self.eof() {
262            let tt = self.tt();
263            if token_set.contains(tt) || (stop_at_eol_or_semi && self.at(EOL_OR_SEMICOLON)) {
264                break;
265            }
266            self.advance();
267        }
268    }
269
270    pub(crate) fn error_until(&mut self, message: &str, token_set: TokenSet) {
271        debug_assert!(token_set.contains(LBRACE), "Cannot include LBRACE into error element!");
272        debug_assert!(token_set.contains(RBRACE), "Cannot include RBRACE into error element!");
273        let error = self.mark();
274        self.skip_until(token_set);
275        error.error(self, message);
276    }
277
278    pub(crate) fn error_if(&mut self, marker: Marker, condition: bool, message: &str) {
279        if condition {
280            marker.error(self, message);
281        } else {
282            marker.drop(self);
283        }
284    }
285
286    // `matchTokenStreamPredicate` lives in token_stream.rs; `OptionalMarker` in optional_marker.rs.
287
288    pub(crate) fn eol(&mut self) -> bool {
289        self.my_builder.newline_before_current_token() || self.eof()
290    }
291
292    pub(crate) fn close_declaration_with_comment_binders(
293        &mut self,
294        marker: Marker,
295        element_type: SyntaxKind,
296        preceding_non_doc_comments: bool,
297    ) {
298        marker.done(self, element_type);
299        let preceding =
300            if preceding_non_doc_comments { EdgeBinder::PrecedingComments } else { EdgeBinder::PrecedingDocComments };
301        marker.set_custom_edge_token_binders(self, Some(preceding), Some(EdgeBinder::TrailingComments));
302    }
303
304    /// `createTruncatedBuilder(eofPosition)` followed by a call on the created parser. Only
305    /// `KotlinParsing` calls it, so `create` is `KotlinParsing.create` = `createForTopLevel`
306    /// (lazy, top-level) even inside a by-clause.
307    pub(crate) fn create_truncated_builder<R>(&mut self, eof_position: i32, f: impl FnOnce(&mut Parser) -> R) -> R {
308        self.my_builder.push_layer(Layer::Truncated { eof_position });
309        self.with_context(true, None, f)
310    }
311
312    /// Test-only upstream (`substringWithContext(text, offset, offset, 20)`).
313    pub fn current_context(&mut self) -> String {
314        let offset = self.my_builder.get_current_offset() as usize;
315        let text = self.my_builder.get_original_text();
316        let start = floor_char_boundary(text, offset.saturating_sub(20));
317        let end = floor_char_boundary(text, (offset + 20).min(text.len()));
318        format!("{}<caret>{}", &text[start..offset], &text[offset..end])
319    }
320}
321
322fn floor_char_boundary(text: &str, mut index: usize) -> usize {
323    while !text.is_char_boundary(index) {
324        index -= 1;
325    }
326    index
327}
328