jvm_assembler/formats/jasm/parser/
mod.rs1use crate::formats::jasm::{ast::*, lexer::JasmTokenType};
6use gaia_types::{
7 helpers::Url,
8 reader::{SourcePosition, Token, TokenStream},
9 GaiaError, Result, SourceLocation,
10};
11
12#[derive(Debug)]
14pub struct JasmParser {
15 url: Option<Url>,
17}
18
19impl JasmParser {
20 pub fn new(url: Option<Url>) -> Self {
22 Self { url }
23 }
24
25 pub fn parse(&self, tokens: TokenStream<JasmTokenType>) -> Result<JasmRoot> {
33 let mut parser_state = ParserState::new(self.url.clone(), tokens.raw, tokens.tokens.into_inner());
34
35 parser_state.parse_root()
36 }
37}
38
39struct ParserState<'input> {
41 url: Option<Url>,
43 source: &'input str,
45 tokens: Vec<Token<JasmTokenType>>,
47 position: usize,
49}
50
51impl<'input> ParserState<'input> {
52 fn new(url: Option<Url>, source: &'input str, tokens: Vec<Token<JasmTokenType>>) -> Self {
54 Self { url, source, tokens, position: 0 }
55 }
56
57 fn current(&self) -> Option<&Token<JasmTokenType>> {
59 self.tokens.get(self.position)
60 }
61
62 fn advance(&mut self) -> Option<&Token<JasmTokenType>> {
64 if self.position < self.tokens.len() {
65 self.position += 1;
66 }
67 self.current()
68 }
69
70 fn check(&self, token_type: JasmTokenType) -> bool {
72 self.current().map(|t| t.token_type) == Some(token_type)
73 }
74
75 fn consume(&mut self, token_type: JasmTokenType) -> Result<Token<JasmTokenType>> {
77 if self.check(token_type) {
78 let token = self.current().unwrap().clone();
79 self.advance();
80 Ok(token)
81 }
82 else {
83 Err(GaiaError::custom_error(format!(
84 "Expected {:?}, found {:?} at {:?}",
85 token_type,
86 self.current().map(|t| t.token_type),
87 self.location_from_token(self.current().unwrap())
88 )))
89 }
90 }
91
92 fn skip_whitespace_and_comments(&mut self) {
94 while let Some(token) = self.current() {
95 match token.token_type {
96 JasmTokenType::Whitespace | JasmTokenType::Comment => {
97 self.advance();
98 }
99 _ => break,
100 }
101 }
102 }
103
104 fn parse_root(&mut self) -> Result<JasmRoot> {
106 self.skip_whitespace_and_comments();
107
108 let class = self.parse_class()?;
109
110 Ok(JasmRoot { class })
111 }
112
113 fn parse_class(&mut self) -> Result<JasmClass> {
115 let mut modifiers = Vec::new();
117 while let Some(token) = self.current() {
118 match token.token_type {
119 JasmTokenType::Public => {
120 modifiers.push("public".to_string());
121 self.advance();
122 }
123 JasmTokenType::Private => {
124 modifiers.push("private".to_string());
125 self.advance();
126 }
127 JasmTokenType::Protected => {
128 modifiers.push("protected".to_string());
129 self.advance();
130 }
131 JasmTokenType::Static => {
132 modifiers.push("static".to_string());
133 self.advance();
134 }
135 JasmTokenType::Final => {
136 modifiers.push("final".to_string());
137 self.advance();
138 }
139 JasmTokenType::Super => {
140 modifiers.push("super".to_string());
141 self.advance();
142 }
143 JasmTokenType::Abstract => {
144 modifiers.push("abstract".to_string());
145 self.advance();
146 }
147 _ => break,
148 }
149 self.skip_whitespace_and_comments();
150 }
151
152 self.consume(JasmTokenType::Class)?;
154 self.skip_whitespace_and_comments();
155
156 let name_token = self.consume(JasmTokenType::Identifier)?;
158 let name = self.token_text(&name_token).to_string();
159 self.skip_whitespace_and_comments();
160
161 let mut version = None;
163 if self.check(JasmTokenType::Version) {
164 self.advance(); self.skip_whitespace_and_comments();
166 let version_token = self.consume(JasmTokenType::Number)?;
167 let version_text = self.token_text(&version_token).to_string(); if self.check(JasmTokenType::Colon) {
171 self.advance(); self.skip_whitespace_and_comments();
173 let minor_token = self.consume(JasmTokenType::Number)?;
174 let minor_text = self.token_text(&minor_token).to_string(); version = Some(format!("{}:{}", version_text, minor_text));
176 }
177 else {
178 version = Some(version_text);
179 }
180 self.skip_whitespace_and_comments();
181 }
182
183 self.consume(JasmTokenType::LeftBrace)?;
185 self.skip_whitespace_and_comments();
186
187 let mut methods = Vec::new();
189 let mut fields = Vec::new();
190 let mut source_file = None;
191
192 while !self.check(JasmTokenType::RightBrace) && self.current().is_some() {
193 self.skip_whitespace_and_comments();
194
195 if let Some(token) = self.current() {
196 match token.token_type {
197 JasmTokenType::Method
198 | JasmTokenType::Public
199 | JasmTokenType::Private
200 | JasmTokenType::Protected
201 | JasmTokenType::Static
202 | JasmTokenType::Final => {
203 methods.push(self.parse_method()?);
204 }
205 JasmTokenType::Field => {
206 fields.push(self.parse_field()?);
207 }
208 JasmTokenType::SourceFile => {
209 source_file = Some(self.parse_source_file()?);
210 }
211 _ => {
212 return Err(GaiaError::custom_error(format!(
213 "Unexpected token in class body: {:?} at {:?}",
214 token.token_type,
215 self.location_from_token(token)
216 )));
217 }
218 }
219 }
220 self.skip_whitespace_and_comments();
221 }
222
223 self.consume(JasmTokenType::RightBrace)?;
225
226 Ok(JasmClass { modifiers, name, version, methods, fields, source_file })
227 }
228
229 fn parse_method(&mut self) -> Result<JasmMethod> {
231 let mut modifiers = Vec::new();
233 while let Some(token) = self.current() {
234 match token.token_type {
235 JasmTokenType::Public => {
236 modifiers.push("public".to_string());
237 self.advance();
238 }
239 JasmTokenType::Private => {
240 modifiers.push("private".to_string());
241 self.advance();
242 }
243 JasmTokenType::Protected => {
244 modifiers.push("protected".to_string());
245 self.advance();
246 }
247 JasmTokenType::Static => {
248 modifiers.push("static".to_string());
249 self.advance();
250 }
251 JasmTokenType::Final => {
252 modifiers.push("final".to_string());
253 self.advance();
254 }
255 JasmTokenType::Synchronized => {
256 modifiers.push("synchronized".to_string());
257 self.advance();
258 }
259 JasmTokenType::Native => {
260 modifiers.push("native".to_string());
261 self.advance();
262 }
263 _ => break,
264 }
265 self.skip_whitespace_and_comments();
266 }
267
268 self.consume(JasmTokenType::Method)?;
270 self.skip_whitespace_and_comments();
271
272 let name_and_descriptor = self.parse_method_signature()?;
274 self.skip_whitespace_and_comments();
275
276 let mut stack_size = None;
278 let mut locals_count = None;
279
280 if self.check(JasmTokenType::Stack) {
281 self.advance(); self.skip_whitespace_and_comments();
283 let stack_token = self.consume(JasmTokenType::Number)?;
284 stack_size = Some(self.token_text(&stack_token).parse().unwrap_or(0));
285 self.skip_whitespace_and_comments();
286 }
287
288 if self.check(JasmTokenType::Locals) {
289 self.advance(); self.skip_whitespace_and_comments();
291 let locals_token = self.consume(JasmTokenType::Number)?;
292 locals_count = Some(self.token_text(&locals_token).parse().unwrap_or(0));
293 self.skip_whitespace_and_comments();
294 }
295
296 self.consume(JasmTokenType::LeftBrace)?;
298 self.skip_whitespace_and_comments();
299
300 let mut instructions = Vec::new();
302 while !self.check(JasmTokenType::RightBrace) && self.current().is_some() {
303 self.skip_whitespace_and_comments();
304 if let Some(instruction) = self.parse_instruction()? {
305 instructions.push(instruction);
306 }
307 self.skip_whitespace_and_comments();
308 }
309
310 self.consume(JasmTokenType::RightBrace)?;
312
313 Ok(JasmMethod { modifiers, name_and_descriptor, stack_size, locals_count, instructions })
314 }
315
316 fn parse_method_signature(&mut self) -> Result<String> {
318 let mut signature = String::new();
319
320 if self.check(JasmTokenType::StringLiteral) || self.check(JasmTokenType::Identifier) {
322 let token = self.current().unwrap();
323 let text = self.token_text(token);
324 if text.starts_with('"') && text.ends_with('"') {
325 signature.push_str(&text[1..text.len() - 1]); }
327 else {
328 signature.push_str(text);
329 }
330 self.advance();
331 }
332
333 self.skip_whitespace_and_comments();
334
335 if self.check(JasmTokenType::Colon) {
337 signature.push(':');
338 self.advance();
339 self.skip_whitespace_and_comments();
340 }
341
342 if self.check(JasmTokenType::TypeDescriptor) || self.check(JasmTokenType::StringLiteral) {
344 let token = self.current().unwrap();
345 let text = self.token_text(token);
346 if text.starts_with('"') && text.ends_with('"') {
347 signature.push_str(&text[1..text.len() - 1]); }
349 else {
350 signature.push_str(text);
351 }
352 self.advance();
353 }
354
355 Ok(signature)
356 }
357
358 fn parse_instruction(&mut self) -> Result<Option<JasmInstruction>> {
360 if let Some(token) = self.current() {
361 match token.token_type {
362 JasmTokenType::ALoad0
364 | JasmTokenType::ALoad1
365 | JasmTokenType::ALoad2
366 | JasmTokenType::ALoad3
367 | JasmTokenType::ILoad0
368 | JasmTokenType::ILoad1
369 | JasmTokenType::ILoad2
370 | JasmTokenType::ILoad3
371 | JasmTokenType::Return
372 | JasmTokenType::IReturn
373 | JasmTokenType::AReturn
374 | JasmTokenType::Nop
375 | JasmTokenType::Dup
376 | JasmTokenType::Pop => {
377 let instruction_name = self.token_text(token).to_string();
378 self.advance();
379 self.skip_whitespace_and_comments();
380
381 if self.check(JasmTokenType::Semicolon) {
383 self.advance();
384 }
385
386 Ok(Some(JasmInstruction::Simple(instruction_name)))
387 }
388
389 JasmTokenType::Ldc | JasmTokenType::LdcW | JasmTokenType::Ldc2W => {
391 let instruction_name = self.token_text(token).to_string();
392 self.advance();
393 self.skip_whitespace_and_comments();
394
395 let argument = if self.check(JasmTokenType::String) {
397 self.advance(); self.skip_whitespace_and_comments();
399 if self.check(JasmTokenType::StringLiteral) {
400 let arg_token = self.current().unwrap();
401 let arg_text = self.token_text(arg_token).to_string(); self.advance();
403 format!("String {}", arg_text)
404 }
405 else {
406 return Err(GaiaError::custom_error(format!(
407 "Expected string literal after String at {:?}",
408 self.location_from_token(self.current().unwrap_or(&Token {
409 token_type: JasmTokenType::Eof,
410 position: SourcePosition { line: 0, column: 0, offset: 0, length: 0 }
411 }))
412 )));
413 }
414 }
415 else if self.check(JasmTokenType::StringLiteral) || self.check(JasmTokenType::Number) {
416 let arg_token = self.current().unwrap();
417 let arg_text = self.token_text(arg_token).to_string();
418 self.advance();
419 arg_text
420 }
421 else {
422 return Err(GaiaError::custom_error(format!(
423 "Expected argument for ldc instruction at {:?}",
424 self.location_from_token(self.current().unwrap_or(&Token {
425 token_type: JasmTokenType::Eof,
426 position: SourcePosition { line: 0, column: 0, offset: 0, length: 0 }
427 }))
428 )));
429 };
430
431 self.skip_whitespace_and_comments();
432
433 if self.check(JasmTokenType::Semicolon) {
435 self.advance();
436 }
437
438 Ok(Some(JasmInstruction::WithArgument { instruction: instruction_name, argument }))
439 }
440
441 JasmTokenType::InvokeSpecial
443 | JasmTokenType::InvokeVirtual
444 | JasmTokenType::InvokeStatic
445 | JasmTokenType::InvokeInterface => {
446 let instruction_name = self.token_text(token).to_string();
447 self.advance();
448 self.skip_whitespace_and_comments();
449
450 self.consume(JasmTokenType::Method)?;
452 self.skip_whitespace_and_comments();
453
454 let method_ref = self.parse_method_reference()?;
456 self.skip_whitespace_and_comments();
457
458 if self.check(JasmTokenType::Semicolon) {
460 self.advance();
461 }
462
463 Ok(Some(JasmInstruction::MethodCall { instruction: instruction_name, method_ref }))
464 }
465
466 JasmTokenType::GetStatic | JasmTokenType::PutStatic | JasmTokenType::GetField | JasmTokenType::PutField => {
468 let instruction_name = self.token_text(token).to_string();
469 self.advance();
470 self.skip_whitespace_and_comments();
471
472 self.consume(JasmTokenType::Field)?;
474 self.skip_whitespace_and_comments();
475
476 let field_ref = self.parse_field_reference()?;
478 self.skip_whitespace_and_comments();
479
480 if self.check(JasmTokenType::Semicolon) {
482 self.advance();
483 }
484
485 Ok(Some(JasmInstruction::FieldAccess { instruction: instruction_name, field_ref }))
486 }
487
488 _ => {
489 self.advance();
491 Ok(None)
492 }
493 }
494 }
495 else {
496 Ok(None)
497 }
498 }
499
500 fn parse_method_reference(&mut self) -> Result<String> {
502 let mut method_ref = String::new();
503
504 if self.check(JasmTokenType::Identifier) {
506 let token = self.current().unwrap();
507 method_ref.push_str(self.token_text(token));
508 self.advance();
509 }
510
511 if self.check(JasmTokenType::Dot) {
513 method_ref.push('.');
514 self.advance();
515 }
516
517 if self.check(JasmTokenType::StringLiteral) || self.check(JasmTokenType::Identifier) {
519 let token = self.current().unwrap();
520 let text = self.token_text(token);
521 if text.starts_with('"') && text.ends_with('"') {
522 method_ref.push_str(&text[1..text.len() - 1]); }
524 else {
525 method_ref.push_str(text);
526 }
527 self.advance();
528 }
529
530 if self.check(JasmTokenType::Colon) {
532 method_ref.push(':');
533 self.advance();
534 }
535
536 if self.check(JasmTokenType::TypeDescriptor) || self.check(JasmTokenType::StringLiteral) {
538 let token = self.current().unwrap();
539 let text = self.token_text(token);
540 if text.starts_with('"') && text.ends_with('"') {
541 method_ref.push_str(&text[1..text.len() - 1]); }
543 else {
544 method_ref.push_str(text);
545 }
546 self.advance();
547 }
548
549 Ok(method_ref)
550 }
551
552 fn parse_field_reference(&mut self) -> Result<String> {
554 let mut field_ref = String::new();
555
556 if self.check(JasmTokenType::Identifier) {
558 let token = self.current().unwrap();
559 field_ref.push_str(self.token_text(token));
560 self.advance();
561 }
562
563 if self.check(JasmTokenType::Dot) {
565 field_ref.push('.');
566 self.advance();
567 }
568
569 if self.check(JasmTokenType::Identifier) {
571 let token = self.current().unwrap();
572 field_ref.push_str(self.token_text(token));
573 self.advance();
574 }
575
576 if self.check(JasmTokenType::Colon) {
578 field_ref.push(':');
579 self.advance();
580 }
581
582 if self.check(JasmTokenType::TypeDescriptor) || self.check(JasmTokenType::StringLiteral) {
584 let token = self.current().unwrap();
585 let text = self.token_text(token);
586 if text.starts_with('"') && text.ends_with('"') {
587 field_ref.push_str(&text[1..text.len() - 1]); }
589 else {
590 field_ref.push_str(text);
591 }
592 self.advance();
593 }
594
595 Ok(field_ref)
596 }
597
598 fn parse_field(&mut self) -> Result<JasmField> {
600 let mut modifiers = Vec::new();
602 while let Some(token) = self.current() {
603 match token.token_type {
604 JasmTokenType::Public => {
605 modifiers.push("public".to_string());
606 self.advance();
607 }
608 JasmTokenType::Private => {
609 modifiers.push("private".to_string());
610 self.advance();
611 }
612 JasmTokenType::Protected => {
613 modifiers.push("protected".to_string());
614 self.advance();
615 }
616 JasmTokenType::Static => {
617 modifiers.push("static".to_string());
618 self.advance();
619 }
620 JasmTokenType::Final => {
621 modifiers.push("final".to_string());
622 self.advance();
623 }
624 _ => break,
625 }
626 self.skip_whitespace_and_comments();
627 }
628
629 self.consume(JasmTokenType::Field)?;
631 self.skip_whitespace_and_comments();
632
633 let name_and_descriptor = self.parse_field_signature()?;
635 self.skip_whitespace_and_comments();
636
637 if self.check(JasmTokenType::Semicolon) {
639 self.advance();
640 }
641
642 Ok(JasmField { modifiers, name_and_descriptor })
643 }
644
645 fn parse_field_signature(&mut self) -> Result<String> {
647 let mut signature = String::new();
648
649 if self.check(JasmTokenType::Identifier) {
651 let token = self.current().unwrap();
652 signature.push_str(self.token_text(token));
653 self.advance();
654 }
655
656 self.skip_whitespace_and_comments();
657
658 if self.check(JasmTokenType::Colon) {
660 signature.push(':');
661 self.advance();
662 self.skip_whitespace_and_comments();
663 }
664
665 if self.check(JasmTokenType::TypeDescriptor) || self.check(JasmTokenType::StringLiteral) {
667 let token = self.current().unwrap();
668 let text = self.token_text(token);
669 if text.starts_with('"') && text.ends_with('"') {
670 signature.push_str(&text[1..text.len() - 1]); }
672 else {
673 signature.push_str(text);
674 }
675 self.advance();
676 }
677
678 Ok(signature)
679 }
680
681 fn parse_source_file(&mut self) -> Result<String> {
683 self.consume(JasmTokenType::SourceFile)?;
685 self.skip_whitespace_and_comments();
686
687 let filename_token = self.consume(JasmTokenType::StringLiteral)?;
689 let filename = self.token_text(&filename_token).to_string(); let filename =
693 if filename.starts_with('"') && filename.ends_with('"') { &filename[1..filename.len() - 1] } else { &filename };
694
695 self.skip_whitespace_and_comments();
696
697 if self.check(JasmTokenType::Semicolon) {
699 self.advance();
700 }
701
702 Ok(filename.to_string())
703 }
704
705 fn token_text(&self, token: &Token<JasmTokenType>) -> &str {
707 let range = token.get_range();
708 &self.source[range]
709 }
710
711 fn location_from_token(&self, token: &Token<JasmTokenType>) -> SourceLocation {
713 SourceLocation { line: token.position.line, column: token.position.column, url: self.url.clone().map(|u| u.clone()) }
714 }
715}
716
717impl Default for JasmParser {
718 fn default() -> Self {
719 Self::new(None)
720 }
721}