Skip to main content

oak_jasm/parser/
mod.rs

1//! Parser implementation for the JASM language.
2
3/// Element types for the JASM language.
4pub mod element_type;
5
6use crate::{
7    language::JasmLanguage,
8    lexer::{JasmLexer, token_type::JasmTokenType},
9    parser::element_type::JasmElementType,
10};
11use oak_core::{
12    parser::{ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer},
13    source::{Source, TextEdit},
14};
15
16pub(crate) type State<'a, S> = ParserState<'a, JasmLanguage, S>;
17
18/// Parser for the JASM language.
19pub struct JasmParser<'config> {
20    /// The language configuration.
21    pub config: &'config JasmLanguage,
22}
23
24impl<'config> JasmParser<'config> {
25    /// Creates a new `JasmParser`.
26    pub fn new(config: &'config JasmLanguage) -> Self {
27        Self { config }
28    }
29
30    fn skip_trivia<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
31        while state.not_at_end() && (state.at(JasmTokenType::Whitespace) || state.at(JasmTokenType::Newline) || state.at(JasmTokenType::Comment)) {
32            state.bump();
33        }
34    }
35
36    fn parse_class<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
37        let cp = state.checkpoint();
38
39        // Parse modifiers
40        while state.not_at_end()
41            && matches!(
42                state.current().map(|t| t.kind),
43                Some(JasmTokenType::Public)
44                    | Some(JasmTokenType::Private)
45                    | Some(JasmTokenType::Protected)
46                    | Some(JasmTokenType::Static)
47                    | Some(JasmTokenType::Final)
48                    | Some(JasmTokenType::Abstract)
49                    | Some(JasmTokenType::Synthetic)
50                    | Some(JasmTokenType::Deprecated)
51            )
52        {
53            state.bump();
54            self.skip_trivia(state);
55        }
56
57        // Error recovery: if no class keyword, skip until we find one or end of file
58        if !state.at(JasmTokenType::ClassKw) {
59            while state.not_at_end() && !state.at(JasmTokenType::ClassKw) {
60                state.advance();
61            }
62        }
63
64        state.expect(JasmTokenType::ClassKw).ok();
65        self.skip_trivia(state);
66
67        // Class name
68        if state.at(JasmTokenType::Identifier) {
69            state.bump();
70        }
71        else {
72            // Error recovery: skip until identifier or next keyword
73            while state.not_at_end() && !state.at(JasmTokenType::Identifier) && !state.at(JasmTokenType::ExtendsKw) && !state.at(JasmTokenType::ImplementsKw) && !state.at(JasmTokenType::LeftBrace) {
74                state.advance();
75            }
76        }
77        self.skip_trivia(state);
78
79        // Parse extends clause
80        if state.at(JasmTokenType::ExtendsKw) {
81            state.bump();
82            self.skip_trivia(state);
83            if state.at(JasmTokenType::Identifier) {
84                state.bump();
85            }
86            else {
87                // Error recovery: skip until identifier or next keyword
88                while state.not_at_end() && !state.at(JasmTokenType::Identifier) && !state.at(JasmTokenType::ImplementsKw) && !state.at(JasmTokenType::LeftBrace) {
89                    state.advance();
90                }
91            }
92            self.skip_trivia(state);
93        }
94
95        // Parse implements clause
96        if state.at(JasmTokenType::ImplementsKw) {
97            state.bump();
98            self.skip_trivia(state);
99            // Parse interface list
100            while state.not_at_end() && !state.at(JasmTokenType::LeftBrace) {
101                if state.at(JasmTokenType::Identifier) {
102                    state.bump();
103                    self.skip_trivia(state);
104                    if state.at(JasmTokenType::Comma) {
105                        state.bump();
106                        self.skip_trivia(state);
107                    }
108                    else {
109                        break;
110                    }
111                }
112                else {
113                    // Error recovery: skip until identifier or left brace
114                    while state.not_at_end() && !state.at(JasmTokenType::Identifier) && !state.at(JasmTokenType::LeftBrace) {
115                        state.advance();
116                    }
117                }
118            }
119        }
120
121        if state.eat(JasmTokenType::LeftBrace) {
122            while state.not_at_end() && !state.at(JasmTokenType::RightBrace) {
123                self.skip_trivia(state);
124                if state.not_at_end() && !state.at(JasmTokenType::RightBrace) {
125                    if state.at(JasmTokenType::MethodKw)
126                        || matches!(
127                            state.current().map(|t| t.kind),
128                            Some(JasmTokenType::Public)
129                                | Some(JasmTokenType::Private)
130                                | Some(JasmTokenType::Protected)
131                                | Some(JasmTokenType::Static)
132                                | Some(JasmTokenType::Final)
133                                | Some(JasmTokenType::Abstract)
134                                | Some(JasmTokenType::Synthetic)
135                                | Some(JasmTokenType::Deprecated)
136                        )
137                    {
138                        // Check if it's a method or field by looking ahead or just trying both
139                        // For JASM, both methods and fields can have modifiers.
140                        // Usually method has MethodKw later, field has FieldKw.
141                        // Simple heuristic: if we see MethodKw later, it's a method.
142                        let mut lookahead = 0;
143                        let mut is_method = false;
144                        while let Some(t) = state.peek_at(lookahead) {
145                            if t.kind == JasmTokenType::MethodKw {
146                                is_method = true;
147                                break;
148                            }
149                            if t.kind == JasmTokenType::Newline || t.kind == JasmTokenType::Semicolon || t.kind == JasmTokenType::LeftBrace {
150                                break;
151                            }
152                            lookahead += 1;
153                        }
154
155                        if is_method {
156                            self.parse_method(state);
157                        }
158                        else {
159                            self.parse_field(state);
160                        }
161                    }
162                    else if state.at(JasmTokenType::FieldKw) {
163                        self.parse_field(state);
164                    }
165                    else if state.at(JasmTokenType::MethodKw) {
166                        self.parse_method(state);
167                    }
168                    else {
169                        // Error recovery: skip until next meaningful token
170                        state.advance();
171                    }
172                }
173                self.skip_trivia(state);
174            }
175            state.eat(JasmTokenType::RightBrace);
176        }
177
178        state.finish_at(cp, JasmElementType::Class);
179    }
180
181    fn parse_field<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
182        let cp = state.checkpoint();
183
184        // Parse modifiers
185        while state.not_at_end()
186            && matches!(
187                state.current().map(|t| t.kind),
188                Some(JasmTokenType::Public) | Some(JasmTokenType::Private) | Some(JasmTokenType::Protected) | Some(JasmTokenType::Static) | Some(JasmTokenType::Final) | Some(JasmTokenType::Synthetic) | Some(JasmTokenType::Deprecated)
189            )
190        {
191            state.bump();
192            self.skip_trivia(state);
193        }
194
195        // Error recovery: if no field keyword, skip until we find one or end of file
196        if !state.at(JasmTokenType::FieldKw) {
197            while state.not_at_end() && !state.at(JasmTokenType::FieldKw) && !state.at(JasmTokenType::MethodKw) && !state.at(JasmTokenType::RightBrace) {
198                state.advance();
199            }
200        }
201
202        state.expect(JasmTokenType::FieldKw).ok();
203        self.skip_trivia(state);
204
205        // Field name
206        if state.at(JasmTokenType::Identifier) {
207            state.bump();
208        }
209        else {
210            // Error recovery: skip until identifier or descriptor
211            while state.not_at_end() && !state.at(JasmTokenType::Identifier) && !state.at(JasmTokenType::String) && !state.at(JasmTokenType::Newline) && !state.at(JasmTokenType::Semicolon) {
212                state.advance();
213            }
214        }
215        self.skip_trivia(state);
216
217        // Field descriptor
218        if state.at(JasmTokenType::Identifier) || state.at(JasmTokenType::String) {
219            state.bump();
220        }
221
222        // Skip until end of line or semicolon
223        while state.not_at_end() && !state.at(JasmTokenType::Newline) && !state.at(JasmTokenType::Semicolon) {
224            state.bump();
225        }
226        state.eat(JasmTokenType::Semicolon);
227        state.finish_at(cp, JasmElementType::Field);
228    }
229
230    fn parse_method<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
231        let cp = state.checkpoint();
232
233        // Parse modifiers
234        while state.not_at_end()
235            && matches!(
236                state.current().map(|t| t.kind),
237                Some(JasmTokenType::Public)
238                    | Some(JasmTokenType::Private)
239                    | Some(JasmTokenType::Protected)
240                    | Some(JasmTokenType::Static)
241                    | Some(JasmTokenType::Final)
242                    | Some(JasmTokenType::Abstract)
243                    | Some(JasmTokenType::Native)
244                    | Some(JasmTokenType::Synchronized)
245                    | Some(JasmTokenType::Synthetic)
246                    | Some(JasmTokenType::Deprecated)
247                    | Some(JasmTokenType::Varargs)
248            )
249        {
250            state.bump();
251            self.skip_trivia(state);
252        }
253
254        // Error recovery: if no method keyword, skip until we find one or end of file
255        if !state.at(JasmTokenType::MethodKw) {
256            while state.not_at_end() && !state.at(JasmTokenType::MethodKw) && !state.at(JasmTokenType::FieldKw) && !state.at(JasmTokenType::RightBrace) {
257                state.advance();
258            }
259        }
260
261        state.expect(JasmTokenType::MethodKw).ok();
262        self.skip_trivia(state);
263
264        // Method name
265        if state.at(JasmTokenType::Identifier) {
266            state.bump();
267        }
268        else {
269            // Error recovery: skip until identifier or descriptor
270            while state.not_at_end() && !state.at(JasmTokenType::Identifier) && !state.at(JasmTokenType::String) && !state.at(JasmTokenType::LeftBrace) {
271                state.advance();
272            }
273        }
274        self.skip_trivia(state);
275
276        // Method descriptor
277        if state.at(JasmTokenType::Identifier) || state.at(JasmTokenType::String) {
278            state.bump();
279        }
280        self.skip_trivia(state);
281
282        if state.eat(JasmTokenType::LeftBrace) {
283            while state.not_at_end() && !state.at(JasmTokenType::RightBrace) {
284                self.skip_trivia(state);
285                if !state.not_at_end() || state.at(JasmTokenType::RightBrace) {
286                    break;
287                }
288
289                if state.at(JasmTokenType::StackKw) || state.at(JasmTokenType::LocalsKw) {
290                    state.bump();
291                    self.skip_trivia(state);
292                    if state.at(JasmTokenType::Number) {
293                        state.bump();
294                    }
295                    continue;
296                }
297
298                let inst_cp = state.checkpoint();
299
300                // Instructions or directives
301                while state.not_at_end() && !state.at(JasmTokenType::Newline) && !state.at(JasmTokenType::RightBrace) {
302                    state.bump();
303                }
304
305                state.finish_at(inst_cp, JasmElementType::Instruction);
306                self.skip_trivia(state);
307            }
308            state.eat(JasmTokenType::RightBrace);
309        }
310
311        state.finish_at(cp, JasmElementType::Method);
312    }
313}
314
315impl<'config> Parser<JasmLanguage> for JasmParser<'config> {
316    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<JasmLanguage>) -> ParseOutput<'a, JasmLanguage> {
317        let lexer = JasmLexer::new(&self.config);
318        parse_with_lexer(&lexer, text, edits, cache, |state| {
319            let checkpoint = state.checkpoint();
320
321            while state.not_at_end() {
322                self.skip_trivia(state);
323                if state.at(JasmTokenType::At) {
324                    state.bump();
325                    self.skip_trivia(state);
326                    // Parse annotation
327                    if state.at(JasmTokenType::Identifier) {
328                        state.bump();
329                        // Parse annotation arguments
330                        if state.at(JasmTokenType::LeftParen) {
331                            state.bump();
332                            self.skip_trivia(state);
333                            // Parse arguments
334                            while state.not_at_end() && !state.at(JasmTokenType::RightParen) {
335                                if state.at(JasmTokenType::Identifier) || state.at(JasmTokenType::String) || state.at(JasmTokenType::Number) {
336                                    state.bump();
337                                }
338                                else if state.at(JasmTokenType::Comma) {
339                                    state.bump();
340                                }
341                                else {
342                                    state.advance();
343                                }
344                                self.skip_trivia(state);
345                            }
346                            if state.at(JasmTokenType::RightParen) {
347                                state.bump();
348                            }
349                        }
350                    }
351                }
352                else if state.at(JasmTokenType::Dot) {
353                    state.bump();
354                    self.skip_trivia(state);
355                    if state.at(JasmTokenType::SourceKw) {
356                        state.bump();
357                        self.skip_trivia(state);
358                        // Parse source file path
359                        if state.at(JasmTokenType::String) {
360                            state.bump();
361                        }
362                        else if state.at(JasmTokenType::Identifier) {
363                            state.bump();
364                        }
365                    }
366                    else if state.at(JasmTokenType::SuperKw) {
367                        state.bump();
368                        self.skip_trivia(state);
369                        // Parse super class
370                        if state.at(JasmTokenType::Identifier) {
371                            state.bump();
372                        }
373                    }
374                    else if state.at(JasmTokenType::InterfaceKw) {
375                        state.bump();
376                        self.skip_trivia(state);
377                        // Parse interface
378                        if state.at(JasmTokenType::Identifier) {
379                            state.bump();
380                        }
381                    }
382                    else if state.at(JasmTokenType::CatchKw) {
383                        state.bump();
384                        self.skip_trivia(state);
385                        // Parse exception handler
386                        if state.at(JasmTokenType::Identifier) {
387                            state.bump();
388                        }
389                        self.skip_trivia(state);
390                        if state.at(JasmTokenType::Identifier) {
391                            state.bump();
392                        }
393                    }
394                    else if state.at(JasmTokenType::AttributeKw) {
395                        state.bump();
396                        self.skip_trivia(state);
397                        // Parse attribute
398                        if state.at(JasmTokenType::Identifier) {
399                            state.bump();
400                        }
401                        self.skip_trivia(state);
402                        if state.at(JasmTokenType::String) || state.at(JasmTokenType::Identifier) {
403                            state.bump();
404                        }
405                    }
406                    else if state.at(JasmTokenType::StackMapKw) {
407                        state.bump();
408                        self.skip_trivia(state);
409                        // Parse stack map frame
410                        while state.not_at_end() && !state.at(JasmTokenType::Newline) {
411                            if state.at(JasmTokenType::Identifier) || state.at(JasmTokenType::Number) {
412                                state.bump();
413                            }
414                            else {
415                                state.advance();
416                            }
417                            self.skip_trivia(state);
418                        }
419                    }
420                }
421                else if state.at(JasmTokenType::ClassKw) {
422                    self.parse_class(state);
423                }
424                else {
425                    state.advance();
426                }
427                self.skip_trivia(state);
428            }
429
430            Ok(state.finish_at(checkpoint, JasmElementType::Root))
431        })
432    }
433}