1#![allow(dead_code)]
8
9use alloc::{
10 string::{String, ToString},
11 vec,
12 vec::Vec,
13};
14
15use super::js_lexer::{JsLexer, JsNumber, JsToken};
16
17pub type AstNodeId = usize;
23
24pub const AST_NONE: AstNodeId = usize::MAX;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum BinOp {
30 Add,
31 Sub,
32 Mul,
33 Div,
34 Mod,
35 Eq,
36 StrictEq,
37 NotEq,
38 StrictNotEq,
39 Lt,
40 Gt,
41 LtEq,
42 GtEq,
43 And,
44 Or,
45 BitAnd,
46 BitOr,
47 BitXor,
48 ShiftLeft,
49 ShiftRight,
50 Instanceof,
51 In,
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum UnaryOp {
57 Neg,
58 Not,
59 Typeof,
60 Void,
61 Delete,
62 BitNot,
63}
64
65#[derive(Debug, Clone)]
67pub enum AstNode {
68 Program(Vec<AstNodeId>),
70
71 VarDecl {
73 name: String,
74 init: Option<AstNodeId>,
75 kind: VarKind,
76 },
77
78 FuncDecl {
80 name: String,
81 params: Vec<String>,
82 body: AstNodeId,
83 },
84
85 Return(Option<AstNodeId>),
87
88 If {
90 condition: AstNodeId,
91 then_branch: AstNodeId,
92 else_branch: Option<AstNodeId>,
93 },
94
95 While {
97 condition: AstNodeId,
98 body: AstNodeId,
99 },
100
101 For {
103 init: Option<AstNodeId>,
104 condition: Option<AstNodeId>,
105 update: Option<AstNodeId>,
106 body: AstNodeId,
107 },
108
109 Block(Vec<AstNodeId>),
111
112 ExprStatement(AstNodeId),
114
115 BinaryExpr {
117 op: BinOp,
118 left: AstNodeId,
119 right: AstNodeId,
120 },
121
122 UnaryExpr { op: UnaryOp, operand: AstNodeId },
124
125 AssignExpr { target: AstNodeId, value: AstNodeId },
127
128 CompoundAssign {
130 op: BinOp,
131 target: AstNodeId,
132 value: AstNodeId,
133 },
134
135 CallExpr {
137 callee: AstNodeId,
138 args: Vec<AstNodeId>,
139 },
140
141 MemberExpr { object: AstNodeId, property: String },
143
144 IndexExpr { object: AstNodeId, index: AstNodeId },
146
147 ObjectLiteral(Vec<(String, AstNodeId)>),
149
150 ArrayLiteral(Vec<AstNodeId>),
152
153 StringLit(String),
155
156 NumberLit(JsNumber),
158
159 BoolLit(bool),
161
162 NullLit,
164
165 UndefinedLit,
167
168 Identifier(String),
170
171 This,
173
174 FuncExpr {
176 params: Vec<String>,
177 body: AstNodeId,
178 },
179
180 ArrowFunc {
182 params: Vec<String>,
183 body: AstNodeId,
184 },
185
186 NewExpr {
188 callee: AstNodeId,
189 args: Vec<AstNodeId>,
190 },
191
192 TypeofExpr(AstNodeId),
194
195 Throw(AstNodeId),
197
198 TryCatch {
200 try_body: AstNodeId,
201 catch_param: Option<String>,
202 catch_body: Option<AstNodeId>,
203 finally_body: Option<AstNodeId>,
204 },
205
206 Break,
208
209 Continue,
211
212 Empty,
214}
215
216#[derive(Debug, Clone, Copy, PartialEq, Eq)]
218pub enum VarKind {
219 Var,
220 Let,
221 Const,
222}
223
224pub struct AstArena {
230 nodes: Vec<AstNode>,
231}
232
233impl Default for AstArena {
234 fn default() -> Self {
235 Self::new()
236 }
237}
238
239impl AstArena {
240 pub fn new() -> Self {
241 Self {
242 nodes: Vec::with_capacity(256),
243 }
244 }
245
246 pub fn alloc(&mut self, node: AstNode) -> AstNodeId {
248 let id = self.nodes.len();
249 self.nodes.push(node);
250 id
251 }
252
253 pub fn get(&self, id: AstNodeId) -> Option<&AstNode> {
255 self.nodes.get(id)
256 }
257
258 pub fn get_mut(&mut self, id: AstNodeId) -> Option<&mut AstNode> {
260 self.nodes.get_mut(id)
261 }
262
263 pub fn len(&self) -> usize {
265 self.nodes.len()
266 }
267
268 pub fn is_empty(&self) -> bool {
270 self.nodes.is_empty()
271 }
272}
273
274pub struct JsParser {
280 tokens: Vec<JsToken>,
282 pos: usize,
284 pub arena: AstArena,
286 pub errors: Vec<String>,
288}
289
290impl JsParser {
291 pub fn from_source(source: &str) -> Self {
293 let tokens = JsLexer::new(source).tokenize_all();
294 Self {
295 tokens,
296 pos: 0,
297 arena: AstArena::new(),
298 errors: Vec::new(),
299 }
300 }
301
302 pub fn from_tokens(tokens: Vec<JsToken>) -> Self {
304 Self {
305 tokens,
306 pos: 0,
307 arena: AstArena::new(),
308 errors: Vec::new(),
309 }
310 }
311
312 pub fn parse(&mut self) -> AstNodeId {
314 let mut stmts = Vec::new();
315 while !self.at_end() {
316 if self.check(&JsToken::Semicolon) {
317 self.advance_pos();
318 continue;
319 }
320 let stmt = self.parse_statement();
321 stmts.push(stmt);
322 }
323 self.arena.alloc(AstNode::Program(stmts))
324 }
325
326 fn current(&self) -> &JsToken {
329 self.tokens.get(self.pos).unwrap_or(&JsToken::Eof)
330 }
331
332 fn peek_token(&self) -> &JsToken {
333 self.tokens.get(self.pos + 1).unwrap_or(&JsToken::Eof)
334 }
335
336 fn at_end(&self) -> bool {
337 matches!(self.current(), JsToken::Eof)
338 }
339
340 fn check(&self, expected: &JsToken) -> bool {
341 core::mem::discriminant(self.current()) == core::mem::discriminant(expected)
342 }
343
344 fn advance_pos(&mut self) -> JsToken {
345 let tok = self.current().clone();
346 if self.pos < self.tokens.len() {
347 self.pos += 1;
348 }
349 tok
350 }
351
352 fn expect(&mut self, expected: &JsToken) -> bool {
353 if self.check(expected) {
354 self.advance_pos();
355 true
356 } else {
357 self.errors.push(alloc::format!(
358 "Expected {:?}, got {:?}",
359 expected,
360 self.current()
361 ));
362 false
363 }
364 }
365
366 fn eat_semicolon(&mut self) {
367 if self.check(&JsToken::Semicolon) {
368 self.advance_pos();
369 }
370 }
371
372 fn parse_statement(&mut self) -> AstNodeId {
375 match self.current() {
376 JsToken::Let => self.parse_var_decl(VarKind::Let),
377 JsToken::Const => self.parse_var_decl(VarKind::Const),
378 JsToken::Var => self.parse_var_decl(VarKind::Var),
379 JsToken::Function => self.parse_function_decl(),
380 JsToken::Return => self.parse_return(),
381 JsToken::If => self.parse_if(),
382 JsToken::While => self.parse_while(),
383 JsToken::For => self.parse_for(),
384 JsToken::OpenBrace => self.parse_block(),
385 JsToken::Throw => self.parse_throw(),
386 JsToken::Try => self.parse_try(),
387 JsToken::Break => {
388 self.advance_pos();
389 self.eat_semicolon();
390 self.arena.alloc(AstNode::Break)
391 }
392 JsToken::Continue => {
393 self.advance_pos();
394 self.eat_semicolon();
395 self.arena.alloc(AstNode::Continue)
396 }
397 _ => self.parse_expression_statement(),
398 }
399 }
400
401 fn parse_var_decl(&mut self, kind: VarKind) -> AstNodeId {
402 self.advance_pos(); let name = match self.advance_pos() {
404 JsToken::Identifier(n) => n,
405 _ => {
406 self.errors
407 .push("Expected identifier in variable declaration".to_string());
408 String::from("_error")
409 }
410 };
411 let init = if self.check(&JsToken::Assign) {
412 self.advance_pos();
413 Some(self.parse_expression(0))
414 } else {
415 None
416 };
417 self.eat_semicolon();
418 self.arena.alloc(AstNode::VarDecl { name, init, kind })
419 }
420
421 fn parse_function_decl(&mut self) -> AstNodeId {
422 self.advance_pos(); let name = match self.advance_pos() {
424 JsToken::Identifier(n) => n,
425 _ => {
426 self.errors.push("Expected function name".to_string());
427 String::from("_error")
428 }
429 };
430 let params = self.parse_param_list();
431 let body = self.parse_block();
432 self.arena.alloc(AstNode::FuncDecl { name, params, body })
433 }
434
435 fn parse_param_list(&mut self) -> Vec<String> {
436 let mut params = Vec::new();
437 self.expect(&JsToken::OpenParen);
438 while !self.check(&JsToken::CloseParen) && !self.at_end() {
439 if let JsToken::Identifier(name) = self.advance_pos() {
440 params.push(name);
441 }
442 if self.check(&JsToken::Comma) {
443 self.advance_pos();
444 }
445 }
446 self.expect(&JsToken::CloseParen);
447 params
448 }
449
450 fn parse_return(&mut self) -> AstNodeId {
451 self.advance_pos(); let value =
453 if self.check(&JsToken::Semicolon) || self.check(&JsToken::CloseBrace) || self.at_end()
454 {
455 None
456 } else {
457 Some(self.parse_expression(0))
458 };
459 self.eat_semicolon();
460 self.arena.alloc(AstNode::Return(value))
461 }
462
463 fn parse_if(&mut self) -> AstNodeId {
464 self.advance_pos(); self.expect(&JsToken::OpenParen);
466 let condition = self.parse_expression(0);
467 self.expect(&JsToken::CloseParen);
468 let then_branch = self.parse_statement();
469 let else_branch = if self.check(&JsToken::Else) {
470 self.advance_pos();
471 Some(self.parse_statement())
472 } else {
473 None
474 };
475 self.arena.alloc(AstNode::If {
476 condition,
477 then_branch,
478 else_branch,
479 })
480 }
481
482 fn parse_while(&mut self) -> AstNodeId {
483 self.advance_pos(); self.expect(&JsToken::OpenParen);
485 let condition = self.parse_expression(0);
486 self.expect(&JsToken::CloseParen);
487 let body = self.parse_statement();
488 self.arena.alloc(AstNode::While { condition, body })
489 }
490
491 fn parse_for(&mut self) -> AstNodeId {
492 self.advance_pos(); self.expect(&JsToken::OpenParen);
494 let init = if self.check(&JsToken::Semicolon) {
495 None
496 } else if self.check(&JsToken::Let)
497 || self.check(&JsToken::Var)
498 || self.check(&JsToken::Const)
499 {
500 let kind = match self.current() {
501 JsToken::Let => VarKind::Let,
502 JsToken::Const => VarKind::Const,
503 _ => VarKind::Var,
504 };
505 Some(self.parse_var_decl(kind))
506 } else {
507 let expr = self.parse_expression(0);
508 Some(self.arena.alloc(AstNode::ExprStatement(expr)))
509 };
510 if self.check(&JsToken::Semicolon) {
512 self.advance_pos();
513 }
514 let condition = if self.check(&JsToken::Semicolon) {
515 None
516 } else {
517 Some(self.parse_expression(0))
518 };
519 self.expect(&JsToken::Semicolon);
520 let update = if self.check(&JsToken::CloseParen) {
521 None
522 } else {
523 Some(self.parse_assignment_expression())
524 };
525 self.expect(&JsToken::CloseParen);
526 let body = self.parse_statement();
527 self.arena.alloc(AstNode::For {
528 init,
529 condition,
530 update,
531 body,
532 })
533 }
534
535 fn parse_block(&mut self) -> AstNodeId {
536 self.expect(&JsToken::OpenBrace);
537 let mut stmts = Vec::new();
538 while !self.check(&JsToken::CloseBrace) && !self.at_end() {
539 if self.check(&JsToken::Semicolon) {
540 self.advance_pos();
541 continue;
542 }
543 stmts.push(self.parse_statement());
544 }
545 self.expect(&JsToken::CloseBrace);
546 self.arena.alloc(AstNode::Block(stmts))
547 }
548
549 fn parse_throw(&mut self) -> AstNodeId {
550 self.advance_pos(); let expr = self.parse_expression(0);
552 self.eat_semicolon();
553 self.arena.alloc(AstNode::Throw(expr))
554 }
555
556 fn parse_try(&mut self) -> AstNodeId {
557 self.advance_pos(); let try_body = self.parse_block();
559
560 let (catch_param, catch_body) = if self.check(&JsToken::Catch) {
561 self.advance_pos();
562 let param = if self.check(&JsToken::OpenParen) {
563 self.advance_pos();
564 let name = match self.advance_pos() {
565 JsToken::Identifier(n) => Some(n),
566 _ => None,
567 };
568 self.expect(&JsToken::CloseParen);
569 name
570 } else {
571 None
572 };
573 let body = self.parse_block();
574 (param, Some(body))
575 } else {
576 (None, None)
577 };
578
579 let finally_body = if self.check(&JsToken::Finally) {
580 self.advance_pos();
581 Some(self.parse_block())
582 } else {
583 None
584 };
585
586 self.arena.alloc(AstNode::TryCatch {
587 try_body,
588 catch_param,
589 catch_body,
590 finally_body,
591 })
592 }
593
594 fn parse_assignment_expression(&mut self) -> AstNodeId {
597 let expr = self.parse_expression(0);
598 let node = match self.current() {
599 JsToken::Assign => {
600 self.advance_pos();
601 let value = self.parse_expression(0);
602 AstNode::AssignExpr {
603 target: expr,
604 value,
605 }
606 }
607 JsToken::PlusAssign => {
608 self.advance_pos();
609 let value = self.parse_expression(0);
610 AstNode::CompoundAssign {
611 op: BinOp::Add,
612 target: expr,
613 value,
614 }
615 }
616 JsToken::MinusAssign => {
617 self.advance_pos();
618 let value = self.parse_expression(0);
619 AstNode::CompoundAssign {
620 op: BinOp::Sub,
621 target: expr,
622 value,
623 }
624 }
625 JsToken::StarAssign => {
626 self.advance_pos();
627 let value = self.parse_expression(0);
628 AstNode::CompoundAssign {
629 op: BinOp::Mul,
630 target: expr,
631 value,
632 }
633 }
634 JsToken::SlashAssign => {
635 self.advance_pos();
636 let value = self.parse_expression(0);
637 AstNode::CompoundAssign {
638 op: BinOp::Div,
639 target: expr,
640 value,
641 }
642 }
643 _ => return expr,
644 };
645 self.arena.alloc(node)
646 }
647
648 fn parse_expression_statement(&mut self) -> AstNodeId {
649 let expr = self.parse_expression(0);
650
651 let node = match self.current() {
653 JsToken::Assign => {
654 self.advance_pos();
655 let value = self.parse_expression(0);
656 AstNode::AssignExpr {
657 target: expr,
658 value,
659 }
660 }
661 JsToken::PlusAssign => {
662 self.advance_pos();
663 let value = self.parse_expression(0);
664 AstNode::CompoundAssign {
665 op: BinOp::Add,
666 target: expr,
667 value,
668 }
669 }
670 JsToken::MinusAssign => {
671 self.advance_pos();
672 let value = self.parse_expression(0);
673 AstNode::CompoundAssign {
674 op: BinOp::Sub,
675 target: expr,
676 value,
677 }
678 }
679 JsToken::StarAssign => {
680 self.advance_pos();
681 let value = self.parse_expression(0);
682 AstNode::CompoundAssign {
683 op: BinOp::Mul,
684 target: expr,
685 value,
686 }
687 }
688 JsToken::SlashAssign => {
689 self.advance_pos();
690 let value = self.parse_expression(0);
691 AstNode::CompoundAssign {
692 op: BinOp::Div,
693 target: expr,
694 value,
695 }
696 }
697 _ => AstNode::ExprStatement(expr),
698 };
699
700 let id = self.arena.alloc(node);
701 self.eat_semicolon();
702 id
703 }
704
705 fn parse_expression(&mut self, min_prec: u8) -> AstNodeId {
708 let mut left = self.parse_unary();
709
710 while let Some((op, prec)) = self.current_binop() {
711 if prec < min_prec {
712 break;
713 }
714 self.advance_pos();
715 let right = self.parse_expression(prec + 1);
716 left = self.arena.alloc(AstNode::BinaryExpr { op, left, right });
717 }
718
719 left
720 }
721
722 fn current_binop(&self) -> Option<(BinOp, u8)> {
723 match self.current() {
724 JsToken::Or => Some((BinOp::Or, 3)),
725 JsToken::And => Some((BinOp::And, 4)),
726 JsToken::BitOr => Some((BinOp::BitOr, 5)),
727 JsToken::BitXor => Some((BinOp::BitXor, 6)),
728 JsToken::BitAnd => Some((BinOp::BitAnd, 7)),
729 JsToken::EqEq => Some((BinOp::Eq, 8)),
730 JsToken::EqEqEq => Some((BinOp::StrictEq, 8)),
731 JsToken::NotEq => Some((BinOp::NotEq, 8)),
732 JsToken::NotEqEq => Some((BinOp::StrictNotEq, 8)),
733 JsToken::Lt => Some((BinOp::Lt, 9)),
734 JsToken::Gt => Some((BinOp::Gt, 9)),
735 JsToken::LtEq => Some((BinOp::LtEq, 9)),
736 JsToken::GtEq => Some((BinOp::GtEq, 9)),
737 JsToken::Instanceof => Some((BinOp::Instanceof, 9)),
738 JsToken::In => Some((BinOp::In, 9)),
739 JsToken::ShiftLeft => Some((BinOp::ShiftLeft, 10)),
740 JsToken::ShiftRight => Some((BinOp::ShiftRight, 10)),
741 JsToken::Plus => Some((BinOp::Add, 11)),
742 JsToken::Minus => Some((BinOp::Sub, 11)),
743 JsToken::Star => Some((BinOp::Mul, 12)),
744 JsToken::Slash => Some((BinOp::Div, 12)),
745 JsToken::Percent => Some((BinOp::Mod, 12)),
746 _ => None,
747 }
748 }
749
750 fn parse_unary(&mut self) -> AstNodeId {
751 match self.current() {
752 JsToken::Minus => {
753 self.advance_pos();
754 let operand = self.parse_unary();
755 self.arena.alloc(AstNode::UnaryExpr {
756 op: UnaryOp::Neg,
757 operand,
758 })
759 }
760 JsToken::Not => {
761 self.advance_pos();
762 let operand = self.parse_unary();
763 self.arena.alloc(AstNode::UnaryExpr {
764 op: UnaryOp::Not,
765 operand,
766 })
767 }
768 JsToken::Typeof => {
769 self.advance_pos();
770 let operand = self.parse_unary();
771 self.arena.alloc(AstNode::TypeofExpr(operand))
772 }
773 JsToken::Void => {
774 self.advance_pos();
775 let operand = self.parse_unary();
776 self.arena.alloc(AstNode::UnaryExpr {
777 op: UnaryOp::Void,
778 operand,
779 })
780 }
781 JsToken::Delete => {
782 self.advance_pos();
783 let operand = self.parse_unary();
784 self.arena.alloc(AstNode::UnaryExpr {
785 op: UnaryOp::Delete,
786 operand,
787 })
788 }
789 JsToken::New => self.parse_new_expr(),
790 _ => self.parse_call_member(),
791 }
792 }
793
794 fn parse_new_expr(&mut self) -> AstNodeId {
795 self.advance_pos(); let callee = self.parse_primary();
797 let args = if self.check(&JsToken::OpenParen) {
798 self.parse_arguments()
799 } else {
800 Vec::new()
801 };
802 self.arena.alloc(AstNode::NewExpr { callee, args })
803 }
804
805 fn parse_call_member(&mut self) -> AstNodeId {
806 let mut node = self.parse_primary();
807
808 loop {
809 match self.current() {
810 JsToken::Dot => {
811 self.advance_pos();
812 let prop = match self.advance_pos() {
813 JsToken::Identifier(n) => n,
814 _ => {
815 self.errors
816 .push("Expected property name after '.'".to_string());
817 String::from("_error")
818 }
819 };
820 node = self.arena.alloc(AstNode::MemberExpr {
821 object: node,
822 property: prop,
823 });
824 }
825 JsToken::OpenBracket => {
826 self.advance_pos();
827 let index = self.parse_expression(0);
828 self.expect(&JsToken::CloseBracket);
829 node = self.arena.alloc(AstNode::IndexExpr {
830 object: node,
831 index,
832 });
833 }
834 JsToken::OpenParen => {
835 let args = self.parse_arguments();
836 node = self.arena.alloc(AstNode::CallExpr { callee: node, args });
837 }
838 _ => break,
839 }
840 }
841
842 node
843 }
844
845 fn parse_arguments(&mut self) -> Vec<AstNodeId> {
846 let mut args = Vec::new();
847 self.expect(&JsToken::OpenParen);
848 while !self.check(&JsToken::CloseParen) && !self.at_end() {
849 args.push(self.parse_expression(0));
850 if self.check(&JsToken::Comma) {
851 self.advance_pos();
852 }
853 }
854 self.expect(&JsToken::CloseParen);
855 args
856 }
857
858 fn parse_primary(&mut self) -> AstNodeId {
859 let tok = self.current().clone();
860 match tok {
861 JsToken::Number(n) => {
862 self.advance_pos();
863 self.arena.alloc(AstNode::NumberLit(n))
864 }
865 JsToken::StringLiteral(s) => {
866 self.advance_pos();
867 self.arena.alloc(AstNode::StringLit(s))
868 }
869 JsToken::True => {
870 self.advance_pos();
871 self.arena.alloc(AstNode::BoolLit(true))
872 }
873 JsToken::False => {
874 self.advance_pos();
875 self.arena.alloc(AstNode::BoolLit(false))
876 }
877 JsToken::Null => {
878 self.advance_pos();
879 self.arena.alloc(AstNode::NullLit)
880 }
881 JsToken::Undefined => {
882 self.advance_pos();
883 self.arena.alloc(AstNode::UndefinedLit)
884 }
885 JsToken::This => {
886 self.advance_pos();
887 self.arena.alloc(AstNode::This)
888 }
889 JsToken::Identifier(ref name) => {
890 let name = name.clone();
891 self.advance_pos();
892
893 if self.check(&JsToken::Arrow) {
896 self.advance_pos();
897 let body = if self.check(&JsToken::OpenBrace) {
898 self.parse_block()
899 } else {
900 let expr = self.parse_expression(0);
901 self.arena.alloc(AstNode::Return(Some(expr)))
902 };
903 return self.arena.alloc(AstNode::ArrowFunc {
904 params: vec![name],
905 body,
906 });
907 }
908
909 self.arena.alloc(AstNode::Identifier(name))
910 }
911 JsToken::OpenParen => {
912 self.advance_pos();
913
914 if self.is_arrow_params() {
918 let params = self.parse_arrow_params();
919 self.expect(&JsToken::Arrow);
920 let body = if self.check(&JsToken::OpenBrace) {
921 self.parse_block()
922 } else {
923 let expr = self.parse_expression(0);
924 self.arena.alloc(AstNode::Return(Some(expr)))
925 };
926 return self.arena.alloc(AstNode::ArrowFunc { params, body });
927 }
928
929 let expr = self.parse_expression(0);
930 self.expect(&JsToken::CloseParen);
931 expr
932 }
933 JsToken::OpenBrace => self.parse_object_literal(),
934 JsToken::OpenBracket => self.parse_array_literal(),
935 JsToken::Function => {
936 self.advance_pos();
937 if let JsToken::Identifier(_) = self.current() {
939 self.advance_pos();
940 };
941 let params = self.parse_param_list();
942 let body = self.parse_block();
943 self.arena.alloc(AstNode::FuncExpr { params, body })
944 }
945 _ => {
946 self.errors
947 .push(alloc::format!("Unexpected token in expression: {:?}", tok));
948 self.advance_pos();
949 self.arena.alloc(AstNode::Empty)
950 }
951 }
952 }
953
954 fn parse_object_literal(&mut self) -> AstNodeId {
955 self.advance_pos(); let mut props = Vec::new();
957 while !self.check(&JsToken::CloseBrace) && !self.at_end() {
958 let key = match self.advance_pos() {
959 JsToken::Identifier(k) => k,
960 JsToken::StringLiteral(k) => k,
961 _ => {
962 self.errors.push("Expected property key".to_string());
963 String::from("_error")
964 }
965 };
966 self.expect(&JsToken::Colon);
967 let value = self.parse_expression(0);
968 props.push((key, value));
969 if self.check(&JsToken::Comma) {
970 self.advance_pos();
971 }
972 }
973 self.expect(&JsToken::CloseBrace);
974 self.arena.alloc(AstNode::ObjectLiteral(props))
975 }
976
977 fn parse_array_literal(&mut self) -> AstNodeId {
978 self.advance_pos(); let mut elements = Vec::new();
980 while !self.check(&JsToken::CloseBracket) && !self.at_end() {
981 elements.push(self.parse_expression(0));
982 if self.check(&JsToken::Comma) {
983 self.advance_pos();
984 }
985 }
986 self.expect(&JsToken::CloseBracket);
987 self.arena.alloc(AstNode::ArrayLiteral(elements))
988 }
989
990 fn is_arrow_params(&self) -> bool {
992 let mut depth = 0;
994 let mut i = self.pos;
995 while i < self.tokens.len() {
996 match &self.tokens[i] {
997 JsToken::CloseParen if depth == 0 => {
998 return matches!(self.tokens.get(i + 1), Some(JsToken::Arrow));
1000 }
1001 JsToken::OpenParen => depth += 1,
1002 JsToken::CloseParen => depth -= 1,
1003 JsToken::Eof => return false,
1004 _ => {}
1005 }
1006 i += 1;
1007 }
1008 false
1009 }
1010
1011 fn parse_arrow_params(&mut self) -> Vec<String> {
1013 let mut params = Vec::new();
1014 while !self.check(&JsToken::CloseParen) && !self.at_end() {
1015 if let JsToken::Identifier(name) = self.advance_pos() {
1016 params.push(name);
1017 }
1018 if self.check(&JsToken::Comma) {
1019 self.advance_pos();
1020 }
1021 }
1022 self.expect(&JsToken::CloseParen);
1023 params
1024 }
1025}
1026
1027#[cfg(test)]
1032mod tests {
1033 use super::*;
1034
1035 fn parse(src: &str) -> JsParser {
1036 let mut parser = JsParser::from_source(src);
1037 parser.parse();
1038 parser
1039 }
1040
1041 #[test]
1042 fn test_empty_program() {
1043 let p = parse("");
1044 assert_eq!(p.arena.len(), 1);
1045 matches!(p.arena.get(0), Some(AstNode::Program(stmts)) if stmts.is_empty());
1046 }
1047
1048 #[test]
1049 fn test_number_literal() {
1050 let p = parse("42;");
1051 assert!(p.errors.is_empty());
1052 assert!(p.arena.len() >= 2); }
1054
1055 #[test]
1056 fn test_string_literal() {
1057 let p = parse("\"hello\";");
1058 assert!(p.errors.is_empty());
1059 }
1060
1061 #[test]
1062 fn test_var_decl_let() {
1063 let p = parse("let x = 10;");
1064 assert!(p.errors.is_empty());
1065 let has_var = (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::VarDecl { name, kind: VarKind::Let, .. }) if name == "x"));
1067 assert!(has_var);
1068 }
1069
1070 #[test]
1071 fn test_var_decl_const() {
1072 let p = parse("const y = 'hello';");
1073 assert!(p.errors.is_empty());
1074 let has_const = (0..p.arena.len()).any(|i| {
1075 matches!(
1076 p.arena.get(i),
1077 Some(AstNode::VarDecl {
1078 kind: VarKind::Const,
1079 ..
1080 })
1081 )
1082 });
1083 assert!(has_const);
1084 }
1085
1086 #[test]
1087 fn test_function_decl() {
1088 let p = parse("function add(a, b) { return a + b; }");
1089 assert!(p.errors.is_empty());
1090 let has_func = (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::FuncDecl { name, params, .. }) if name == "add" && params.len() == 2));
1091 assert!(has_func);
1092 }
1093
1094 #[test]
1095 fn test_if_else() {
1096 let p = parse("if (x > 0) { y = 1; } else { y = 2; }");
1097 assert!(p.errors.is_empty());
1098 let has_if = (0..p.arena.len()).any(|i| {
1099 matches!(
1100 p.arena.get(i),
1101 Some(AstNode::If {
1102 else_branch: Some(_),
1103 ..
1104 })
1105 )
1106 });
1107 assert!(has_if);
1108 }
1109
1110 #[test]
1111 fn test_while_loop() {
1112 let p = parse("while (i < 10) { i = i + 1; }");
1113 assert!(p.errors.is_empty());
1114 let has_while =
1115 (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::While { .. })));
1116 assert!(has_while);
1117 }
1118
1119 #[test]
1120 fn test_for_loop() {
1121 let p = parse("for (let i = 0; i < 10; i = i + 1) { x = i; }");
1122 assert!(p.errors.is_empty());
1123 let has_for =
1124 (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::For { .. })));
1125 assert!(has_for);
1126 }
1127
1128 #[test]
1129 fn test_binary_expr() {
1130 let p = parse("1 + 2 * 3;");
1131 assert!(p.errors.is_empty());
1132 let bin_count = (0..p.arena.len())
1134 .filter(|&i| matches!(p.arena.get(i), Some(AstNode::BinaryExpr { .. })))
1135 .count();
1136 assert_eq!(bin_count, 2);
1137 }
1138
1139 #[test]
1140 fn test_precedence() {
1141 let p = parse("1 + 2 * 3;");
1142 for i in 0..p.arena.len() {
1145 if let Some(AstNode::BinaryExpr {
1146 op: BinOp::Add,
1147 right,
1148 ..
1149 }) = p.arena.get(i)
1150 {
1151 assert!(matches!(
1153 p.arena.get(*right),
1154 Some(AstNode::BinaryExpr { op: BinOp::Mul, .. })
1155 ));
1156 }
1157 }
1158 }
1159
1160 #[test]
1161 fn test_unary_neg() {
1162 let p = parse("-x;");
1163 assert!(p.errors.is_empty());
1164 let has_unary = (0..p.arena.len()).any(|i| {
1165 matches!(
1166 p.arena.get(i),
1167 Some(AstNode::UnaryExpr {
1168 op: UnaryOp::Neg,
1169 ..
1170 })
1171 )
1172 });
1173 assert!(has_unary);
1174 }
1175
1176 #[test]
1177 fn test_call_expr() {
1178 let p = parse("foo(1, 2, 3);");
1179 assert!(p.errors.is_empty());
1180 let has_call = (0..p.arena.len()).any(
1181 |i| matches!(p.arena.get(i), Some(AstNode::CallExpr { args, .. }) if args.len() == 3),
1182 );
1183 assert!(has_call);
1184 }
1185
1186 #[test]
1187 fn test_member_expr() {
1188 let p = parse("obj.prop;");
1189 assert!(p.errors.is_empty());
1190 let has_member = (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::MemberExpr { property, .. }) if property == "prop"));
1191 assert!(has_member);
1192 }
1193
1194 #[test]
1195 fn test_index_expr() {
1196 let p = parse("arr[0];");
1197 assert!(p.errors.is_empty());
1198 let has_index =
1199 (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::IndexExpr { .. })));
1200 assert!(has_index);
1201 }
1202
1203 #[test]
1204 fn test_object_literal() {
1205 let p = parse("let o = {a: 1, b: 2};");
1206 assert!(p.errors.is_empty());
1207 let has_obj = (0..p.arena.len()).any(
1208 |i| matches!(p.arena.get(i), Some(AstNode::ObjectLiteral(props)) if props.len() == 2),
1209 );
1210 assert!(has_obj);
1211 }
1212
1213 #[test]
1214 fn test_array_literal() {
1215 let p = parse("let a = [1, 2, 3];");
1216 assert!(p.errors.is_empty());
1217 let has_arr = (0..p.arena.len()).any(
1218 |i| matches!(p.arena.get(i), Some(AstNode::ArrayLiteral(elems)) if elems.len() == 3),
1219 );
1220 assert!(has_arr);
1221 }
1222
1223 #[test]
1224 fn test_assignment() {
1225 let p = parse("x = 5;");
1226 assert!(p.errors.is_empty());
1227 let has_assign =
1228 (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::AssignExpr { .. })));
1229 assert!(has_assign);
1230 }
1231
1232 #[test]
1233 fn test_compound_assign() {
1234 let p = parse("x += 1;");
1235 assert!(p.errors.is_empty());
1236 let has_compound = (0..p.arena.len()).any(|i| {
1237 matches!(
1238 p.arena.get(i),
1239 Some(AstNode::CompoundAssign { op: BinOp::Add, .. })
1240 )
1241 });
1242 assert!(has_compound);
1243 }
1244
1245 #[test]
1246 fn test_try_catch() {
1247 let p = parse("try { x(); } catch (e) { log(e); }");
1248 assert!(p.errors.is_empty());
1249 let has_try = (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::TryCatch { catch_param: Some(ref name), .. }) if name == "e"));
1250 assert!(has_try);
1251 }
1252
1253 #[test]
1254 fn test_try_finally() {
1255 let p = parse("try { x(); } finally { cleanup(); }");
1256 assert!(p.errors.is_empty());
1257 let has_finally = (0..p.arena.len()).any(|i| {
1258 matches!(
1259 p.arena.get(i),
1260 Some(AstNode::TryCatch {
1261 finally_body: Some(_),
1262 ..
1263 })
1264 )
1265 });
1266 assert!(has_finally);
1267 }
1268
1269 #[test]
1270 fn test_throw() {
1271 let p = parse("throw new Error();");
1272 assert!(p.errors.is_empty());
1273 let has_throw =
1274 (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::Throw(_))));
1275 assert!(has_throw);
1276 }
1277
1278 #[test]
1279 fn test_new_expr() {
1280 let p = parse("new Foo(1);");
1281 assert!(p.errors.is_empty());
1282 let has_new =
1283 (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::NewExpr { .. })));
1284 assert!(has_new);
1285 }
1286
1287 #[test]
1288 fn test_arrow_function() {
1289 let p = parse("let f = x => x + 1;");
1290 assert!(p.errors.is_empty());
1291 let has_arrow = (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::ArrowFunc { params, .. }) if params.len() == 1));
1292 assert!(has_arrow);
1293 }
1294
1295 #[test]
1296 fn test_function_expr() {
1297 let p = parse("let f = function(a) { return a; };");
1298 assert!(p.errors.is_empty());
1299 let has_func_expr =
1300 (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::FuncExpr { .. })));
1301 assert!(has_func_expr);
1302 }
1303
1304 #[test]
1305 fn test_break_continue() {
1306 let p = parse("while (true) { break; continue; }");
1307 assert!(p.errors.is_empty());
1308 let has_break = (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::Break)));
1309 let has_continue =
1310 (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::Continue)));
1311 assert!(has_break);
1312 assert!(has_continue);
1313 }
1314
1315 #[test]
1316 fn test_chained_member() {
1317 let p = parse("a.b.c;");
1318 assert!(p.errors.is_empty());
1319 let member_count = (0..p.arena.len())
1320 .filter(|&i| matches!(p.arena.get(i), Some(AstNode::MemberExpr { .. })))
1321 .count();
1322 assert_eq!(member_count, 2);
1323 }
1324
1325 #[test]
1326 fn test_nested_calls() {
1327 let p = parse("a(b(c()));");
1328 assert!(p.errors.is_empty());
1329 let call_count = (0..p.arena.len())
1330 .filter(|&i| matches!(p.arena.get(i), Some(AstNode::CallExpr { .. })))
1331 .count();
1332 assert_eq!(call_count, 3);
1333 }
1334
1335 #[test]
1336 fn test_typeof() {
1337 let p = parse("typeof x;");
1338 assert!(p.errors.is_empty());
1339 let has_typeof =
1340 (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::TypeofExpr(_))));
1341 assert!(has_typeof);
1342 }
1343
1344 #[test]
1345 fn test_this() {
1346 let p = parse("this.x;");
1347 assert!(p.errors.is_empty());
1348 let has_this = (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::This)));
1349 assert!(has_this);
1350 }
1351
1352 #[test]
1353 fn test_boolean_literals() {
1354 let p = parse("true; false;");
1355 assert!(p.errors.is_empty());
1356 let has_true =
1357 (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::BoolLit(true))));
1358 let has_false =
1359 (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::BoolLit(false))));
1360 assert!(has_true);
1361 assert!(has_false);
1362 }
1363
1364 #[test]
1365 fn test_null_undefined() {
1366 let p = parse("null; undefined;");
1367 assert!(p.errors.is_empty());
1368 }
1369
1370 #[test]
1371 fn test_logical_and_or() {
1372 let p = parse("a && b || c;");
1373 assert!(p.errors.is_empty());
1374 }
1375
1376 #[test]
1377 fn test_comparison_ops() {
1378 let p = parse("a < b; c >= d; e === f; g !== h;");
1379 assert!(p.errors.is_empty());
1380 }
1381
1382 #[test]
1383 fn test_return_no_value() {
1384 let p = parse("function f() { return; }");
1385 assert!(p.errors.is_empty());
1386 let has_return =
1387 (0..p.arena.len()).any(|i| matches!(p.arena.get(i), Some(AstNode::Return(None))));
1388 assert!(has_return);
1389 }
1390
1391 #[test]
1392 fn test_if_no_else() {
1393 let p = parse("if (x) { y; }");
1394 assert!(p.errors.is_empty());
1395 let has_if = (0..p.arena.len()).any(|i| {
1396 matches!(
1397 p.arena.get(i),
1398 Some(AstNode::If {
1399 else_branch: None,
1400 ..
1401 })
1402 )
1403 });
1404 assert!(has_if);
1405 }
1406
1407 #[test]
1408 fn test_empty_block() {
1409 let p = parse("{}");
1410 assert!(p.errors.is_empty());
1411 let has_block = (0..p.arena.len())
1412 .any(|i| matches!(p.arena.get(i), Some(AstNode::Block(stmts)) if stmts.is_empty()));
1413 assert!(has_block);
1414 }
1415
1416 #[test]
1417 fn test_method_call() {
1418 let p = parse("console.log('hello');");
1419 assert!(p.errors.is_empty());
1420 }
1421
1422 #[test]
1423 fn test_ast_arena_basic() {
1424 let mut arena = AstArena::new();
1425 let id = arena.alloc(AstNode::NullLit);
1426 assert_eq!(id, 0);
1427 assert_eq!(arena.len(), 1);
1428 assert!(!arena.is_empty());
1429 }
1430
1431 #[test]
1432 fn test_not_operator() {
1433 let p = parse("!x;");
1434 assert!(p.errors.is_empty());
1435 let has_not = (0..p.arena.len()).any(|i| {
1436 matches!(
1437 p.arena.get(i),
1438 Some(AstNode::UnaryExpr {
1439 op: UnaryOp::Not,
1440 ..
1441 })
1442 )
1443 });
1444 assert!(has_not);
1445 }
1446
1447 #[test]
1448 fn test_multiple_statements() {
1449 let p = parse("let a = 1; let b = 2; let c = a + b;");
1450 assert!(p.errors.is_empty());
1451 let var_count = (0..p.arena.len())
1452 .filter(|&i| matches!(p.arena.get(i), Some(AstNode::VarDecl { .. })))
1453 .count();
1454 assert_eq!(var_count, 3);
1455 }
1456}