1pub 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
18pub struct JasmParser<'config> {
20 pub config: &'config JasmLanguage,
22}
23
24impl<'config> JasmParser<'config> {
25 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 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 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 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 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 if state.at(JasmTokenType::Identifier) {
142 state.bump();
143 }
144 self.skip_trivia(state);
145
146 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 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 if state.at(JasmTokenType::Identifier) {
187 state.bump();
188 }
189 self.skip_trivia(state);
190
191 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 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}