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        state.expect(JasmTokenType::ClassKw).ok();
58        self.skip_trivia(state);
59
60        // Class name
61        if state.at(JasmTokenType::Identifier) {
62            state.bump();
63        }
64        self.skip_trivia(state);
65
66        if state.eat(JasmTokenType::LeftBrace) {
67            while state.not_at_end() && !state.at(JasmTokenType::RightBrace) {
68                self.skip_trivia(state);
69                if state.at(JasmTokenType::MethodKw)
70                    || matches!(
71                        state.current().map(|t| t.kind),
72                        Some(JasmTokenType::Public)
73                            | Some(JasmTokenType::Private)
74                            | Some(JasmTokenType::Protected)
75                            | Some(JasmTokenType::Static)
76                            | Some(JasmTokenType::Final)
77                            | Some(JasmTokenType::Abstract)
78                            | Some(JasmTokenType::Synthetic)
79                            | Some(JasmTokenType::Deprecated)
80                    )
81                {
82                    // Check if it's a method or field by looking ahead or just trying both
83                    // For JASM, both methods and fields can have modifiers.
84                    // Usually method has MethodKw later, field has FieldKw.
85                    // Simple heuristic: if we see MethodKw later, it's a method.
86                    let mut lookahead = 0;
87                    let mut is_method = false;
88                    while let Some(t) = state.peek_at(lookahead) {
89                        if t.kind == JasmTokenType::MethodKw {
90                            is_method = true;
91                            break;
92                        }
93                        if t.kind == JasmTokenType::Newline || t.kind == JasmTokenType::Semicolon || t.kind == JasmTokenType::LeftBrace {
94                            break;
95                        }
96                        lookahead += 1;
97                    }
98
99                    if is_method {
100                        self.parse_method(state);
101                    }
102                    else {
103                        self.parse_field(state);
104                    }
105                }
106                else if state.at(JasmTokenType::FieldKw) {
107                    self.parse_field(state);
108                }
109                else if state.at(JasmTokenType::MethodKw) {
110                    self.parse_method(state);
111                }
112                else {
113                    state.advance();
114                }
115                self.skip_trivia(state);
116            }
117            state.eat(JasmTokenType::RightBrace);
118        }
119
120        state.finish_at(cp, JasmElementType::Class);
121    }
122
123    fn parse_field<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
124        let cp = state.checkpoint();
125
126        // Parse modifiers
127        while state.not_at_end()
128            && matches!(
129                state.current().map(|t| t.kind),
130                Some(JasmTokenType::Public) | Some(JasmTokenType::Private) | Some(JasmTokenType::Protected) | Some(JasmTokenType::Static) | Some(JasmTokenType::Final) | Some(JasmTokenType::Synthetic) | Some(JasmTokenType::Deprecated)
131            )
132        {
133            state.bump();
134            self.skip_trivia(state);
135        }
136
137        state.expect(JasmTokenType::FieldKw).ok();
138        self.skip_trivia(state);
139
140        // Field name
141        if state.at(JasmTokenType::Identifier) {
142            state.bump();
143        }
144        self.skip_trivia(state);
145
146        // Field descriptor
147        if state.at(JasmTokenType::Identifier) || state.at(JasmTokenType::String) {
148            state.bump();
149        }
150
151        while state.not_at_end() && !state.at(JasmTokenType::Newline) && !state.at(JasmTokenType::Semicolon) {
152            state.bump();
153        }
154        state.eat(JasmTokenType::Semicolon);
155        state.finish_at(cp, JasmElementType::Field);
156    }
157
158    fn parse_method<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
159        let cp = state.checkpoint();
160
161        // Parse modifiers
162        while state.not_at_end()
163            && matches!(
164                state.current().map(|t| t.kind),
165                Some(JasmTokenType::Public)
166                    | Some(JasmTokenType::Private)
167                    | Some(JasmTokenType::Protected)
168                    | Some(JasmTokenType::Static)
169                    | Some(JasmTokenType::Final)
170                    | Some(JasmTokenType::Abstract)
171                    | Some(JasmTokenType::Native)
172                    | Some(JasmTokenType::Synchronized)
173                    | Some(JasmTokenType::Synthetic)
174                    | Some(JasmTokenType::Deprecated)
175                    | Some(JasmTokenType::Varargs)
176            )
177        {
178            state.bump();
179            self.skip_trivia(state);
180        }
181
182        state.expect(JasmTokenType::MethodKw).ok();
183        self.skip_trivia(state);
184
185        // Method name
186        if state.at(JasmTokenType::Identifier) {
187            state.bump();
188        }
189        self.skip_trivia(state);
190
191        // Method descriptor
192        if state.at(JasmTokenType::Identifier) || state.at(JasmTokenType::String) {
193            state.bump();
194        }
195        self.skip_trivia(state);
196
197        if state.eat(JasmTokenType::LeftBrace) {
198            while state.not_at_end() && !state.at(JasmTokenType::RightBrace) {
199                self.skip_trivia(state);
200                if !state.not_at_end() || state.at(JasmTokenType::RightBrace) {
201                    break;
202                }
203
204                if state.at(JasmTokenType::StackKw) || state.at(JasmTokenType::LocalsKw) {
205                    state.bump();
206                    self.skip_trivia(state);
207                    if state.at(JasmTokenType::Number) {
208                        state.bump();
209                    }
210                    continue;
211                }
212
213                let inst_cp = state.checkpoint();
214
215                // Instructions or directives
216                while state.not_at_end() && !state.at(JasmTokenType::Newline) && !state.at(JasmTokenType::RightBrace) {
217                    state.bump();
218                }
219
220                state.finish_at(inst_cp, JasmElementType::Instruction);
221                self.skip_trivia(state);
222            }
223            state.eat(JasmTokenType::RightBrace);
224        }
225
226        state.finish_at(cp, JasmElementType::Method);
227    }
228}
229
230impl<'config> Parser<JasmLanguage> for JasmParser<'config> {
231    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<JasmLanguage>) -> ParseOutput<'a, JasmLanguage> {
232        let lexer = JasmLexer::new(&self.config);
233        parse_with_lexer(&lexer, text, edits, cache, |state| {
234            let checkpoint = state.checkpoint();
235
236            while state.not_at_end() {
237                self.skip_trivia(state);
238                if state.at(JasmTokenType::ClassKw) {
239                    self.parse_class(state);
240                }
241                else {
242                    state.advance();
243                }
244                self.skip_trivia(state);
245            }
246
247            Ok(state.finish_at(checkpoint, JasmElementType::Root))
248        })
249    }
250}