1use crate::hir::*;
21use crate::Result;
22
23#[derive(Debug, Default)]
25pub struct JsModuleBuilder {
26 statements: Vec<Stmt>,
27 metadata: Option<GenerationMetadata>,
28}
29
30impl JsModuleBuilder {
31 #[must_use]
33 pub fn new() -> Self {
34 Self::default()
35 }
36
37 #[must_use]
39 pub fn metadata(mut self, metadata: GenerationMetadata) -> Self {
40 self.metadata = Some(metadata);
41 self
42 }
43
44 #[must_use]
46 pub fn stmt(mut self, stmt: Stmt) -> Self {
47 self.statements.push(stmt);
48 self
49 }
50
51 #[must_use]
53 pub fn stmts(mut self, stmts: impl IntoIterator<Item = Stmt>) -> Self {
54 self.statements.extend(stmts);
55 self
56 }
57
58 #[must_use]
60 pub fn comment(self, text: impl Into<String>) -> Self {
61 self.stmt(Stmt::Comment(text.into()))
62 }
63
64 pub fn let_decl(self, name: impl Into<String>, value: Expr) -> Result<Self> {
66 let ident = Identifier::new(name)?;
67 Ok(self.stmt(Stmt::Let { name: ident, value }))
68 }
69
70 pub fn const_decl(self, name: impl Into<String>, value: Expr) -> Result<Self> {
72 let ident = Identifier::new(name)?;
73 Ok(self.stmt(Stmt::Const { name: ident, value }))
74 }
75
76 pub fn assign(self, name: impl Into<String>, value: Expr) -> Result<Self> {
78 let ident = Identifier::new(name)?;
79 Ok(self.stmt(Stmt::Assign { name: ident, value }))
80 }
81
82 #[must_use]
84 pub fn expr(self, e: Expr) -> Self {
85 self.stmt(Stmt::Expr(e))
86 }
87
88 #[must_use]
90 pub fn class(self, class: JsClass) -> Self {
91 self.stmt(Stmt::Class(class))
92 }
93
94 pub fn register_processor(
96 self,
97 name: impl Into<String>,
98 class_name: impl Into<String>,
99 ) -> Result<Self> {
100 let class = Identifier::new(class_name)?;
101 Ok(self.stmt(Stmt::RegisterProcessor {
102 name: name.into(),
103 class,
104 }))
105 }
106
107 #[must_use]
109 pub fn build(self) -> JsModule {
110 JsModule {
111 statements: self.statements,
112 metadata: self.metadata,
113 }
114 }
115}
116
117#[derive(Debug)]
119pub struct JsClassBuilder {
120 name: Identifier,
121 extends: Option<Identifier>,
122 constructor: Option<Vec<Stmt>>,
123 methods: Vec<JsMethod>,
124}
125
126impl JsClassBuilder {
127 pub fn new(name: impl Into<String>) -> Result<Self> {
129 Ok(Self {
130 name: Identifier::new(name)?,
131 extends: None,
132 constructor: None,
133 methods: Vec::new(),
134 })
135 }
136
137 pub fn extends(mut self, parent: impl Into<String>) -> Result<Self> {
139 self.extends = Some(Identifier::new(parent)?);
140 Ok(self)
141 }
142
143 #[must_use]
145 pub fn constructor(mut self, body: Vec<Stmt>) -> Self {
146 self.constructor = Some(body);
147 self
148 }
149
150 pub fn method(
152 mut self,
153 name: impl Into<String>,
154 params: &[&str],
155 body: Vec<Stmt>,
156 ) -> Result<Self> {
157 let name = Identifier::new(name)?;
158 let params = params
159 .iter()
160 .map(|p| Identifier::new(*p))
161 .collect::<Result<Vec<_>>>()?;
162
163 self.methods.push(JsMethod { name, params, body });
164 Ok(self)
165 }
166
167 #[must_use]
169 pub fn build(self) -> JsClass {
170 JsClass {
171 name: self.name,
172 extends: self.extends,
173 constructor: self.constructor,
174 methods: self.methods,
175 }
176 }
177}
178
179#[derive(Debug)]
181pub struct JsSwitchBuilder {
182 expr: Expr,
183 cases: Vec<(Expr, Vec<Stmt>)>,
184 default: Option<Vec<Stmt>>,
185}
186
187impl JsSwitchBuilder {
188 #[must_use]
190 pub fn new(expr: Expr) -> Self {
191 Self {
192 expr,
193 cases: Vec::new(),
194 default: None,
195 }
196 }
197
198 #[must_use]
200 pub fn case(mut self, value: Expr, body: Vec<Stmt>) -> Self {
201 self.cases.push((value, body));
202 self
203 }
204
205 #[must_use]
207 pub fn default(mut self, body: Vec<Stmt>) -> Self {
208 self.default = Some(body);
209 self
210 }
211
212 #[must_use]
214 pub fn build(self) -> JsSwitch {
215 JsSwitch {
216 expr: self.expr,
217 cases: self.cases,
218 default: self.default,
219 }
220 }
221}
222
223impl Expr {
225 #[must_use]
227 pub const fn null() -> Self {
228 Self::Null
229 }
230
231 #[must_use]
233 pub const fn bool(v: bool) -> Self {
234 Self::Bool(v)
235 }
236
237 #[must_use]
239 pub fn num(v: impl Into<f64>) -> Self {
240 Self::Num(v.into())
241 }
242
243 #[must_use]
245 pub fn str(s: impl Into<String>) -> Self {
246 Self::Str(s.into())
247 }
248
249 pub fn ident(name: impl Into<String>) -> Result<Self> {
251 Ok(Self::Ident(Identifier::new(name)?))
252 }
253
254 #[must_use]
256 pub const fn this() -> Self {
257 Self::This
258 }
259
260 pub fn dot(self, prop: impl Into<String>) -> Result<Self> {
262 Ok(Self::Member {
263 object: Box::new(self),
264 property: Identifier::new(prop)?,
265 })
266 }
267
268 #[must_use]
270 pub fn index(self, idx: Expr) -> Self {
271 Self::Index {
272 object: Box::new(self),
273 index: Box::new(idx),
274 }
275 }
276
277 #[must_use]
279 pub fn call(self, args: Vec<Expr>) -> Self {
280 Self::Call {
281 callee: Box::new(self),
282 args,
283 }
284 }
285
286 #[must_use]
288 pub fn new_expr(self, args: Vec<Expr>) -> Self {
289 Self::New {
290 constructor: Box::new(self),
291 args,
292 }
293 }
294
295 #[must_use]
297 pub fn await_expr(self) -> Self {
298 Self::Await(Box::new(self))
299 }
300
301 #[must_use]
303 pub fn import(path: Expr) -> Self {
304 Self::Import(Box::new(path))
305 }
306
307 #[must_use]
311 pub fn add(self, other: Expr) -> Self {
312 Self::Binary {
313 left: Box::new(self),
314 op: BinOp::Add,
315 right: Box::new(other),
316 }
317 }
318
319 #[must_use]
321 pub fn sub(self, other: Expr) -> Self {
322 Self::Binary {
323 left: Box::new(self),
324 op: BinOp::Sub,
325 right: Box::new(other),
326 }
327 }
328
329 #[must_use]
331 pub fn mul(self, other: Expr) -> Self {
332 Self::Binary {
333 left: Box::new(self),
334 op: BinOp::Mul,
335 right: Box::new(other),
336 }
337 }
338
339 #[must_use]
341 pub fn div(self, other: Expr) -> Self {
342 Self::Binary {
343 left: Box::new(self),
344 op: BinOp::Div,
345 right: Box::new(other),
346 }
347 }
348
349 #[must_use]
351 pub fn modulo(self, other: Expr) -> Self {
352 Self::Binary {
353 left: Box::new(self),
354 op: BinOp::Mod,
355 right: Box::new(other),
356 }
357 }
358
359 #[must_use]
361 pub fn eq(self, other: Expr) -> Self {
362 Self::Binary {
363 left: Box::new(self),
364 op: BinOp::EqStrict,
365 right: Box::new(other),
366 }
367 }
368
369 #[must_use]
371 pub fn ne(self, other: Expr) -> Self {
372 Self::Binary {
373 left: Box::new(self),
374 op: BinOp::NeStrict,
375 right: Box::new(other),
376 }
377 }
378
379 #[must_use]
381 pub fn lt(self, other: Expr) -> Self {
382 Self::Binary {
383 left: Box::new(self),
384 op: BinOp::Lt,
385 right: Box::new(other),
386 }
387 }
388
389 #[must_use]
391 pub fn le(self, other: Expr) -> Self {
392 Self::Binary {
393 left: Box::new(self),
394 op: BinOp::Le,
395 right: Box::new(other),
396 }
397 }
398
399 #[must_use]
401 pub fn gt(self, other: Expr) -> Self {
402 Self::Binary {
403 left: Box::new(self),
404 op: BinOp::Gt,
405 right: Box::new(other),
406 }
407 }
408
409 #[must_use]
411 pub fn ge(self, other: Expr) -> Self {
412 Self::Binary {
413 left: Box::new(self),
414 op: BinOp::Ge,
415 right: Box::new(other),
416 }
417 }
418
419 #[must_use]
421 pub fn and(self, other: Expr) -> Self {
422 Self::Binary {
423 left: Box::new(self),
424 op: BinOp::And,
425 right: Box::new(other),
426 }
427 }
428
429 #[must_use]
431 pub fn or(self, other: Expr) -> Self {
432 Self::Binary {
433 left: Box::new(self),
434 op: BinOp::Or,
435 right: Box::new(other),
436 }
437 }
438
439 #[must_use]
443 pub fn not(self) -> Self {
444 Self::Unary {
445 op: UnaryOp::Not,
446 operand: Box::new(self),
447 }
448 }
449
450 #[must_use]
452 pub fn neg(self) -> Self {
453 Self::Unary {
454 op: UnaryOp::Neg,
455 operand: Box::new(self),
456 }
457 }
458
459 #[must_use]
461 pub fn type_of(self) -> Self {
462 Self::Unary {
463 op: UnaryOp::TypeOf,
464 operand: Box::new(self),
465 }
466 }
467
468 #[must_use]
470 pub fn ternary(self, then_expr: Expr, else_expr: Expr) -> Self {
471 Self::Ternary {
472 condition: Box::new(self),
473 then_expr: Box::new(then_expr),
474 else_expr: Box::new(else_expr),
475 }
476 }
477
478 #[must_use]
480 pub fn object(pairs: Vec<(&str, Expr)>) -> Self {
481 Self::Object(pairs.into_iter().map(|(k, v)| (k.to_string(), v)).collect())
482 }
483
484 #[must_use]
486 pub fn array(items: Vec<Expr>) -> Self {
487 Self::Array(items)
488 }
489
490 pub fn arrow(params: &[&str], body: Expr) -> Result<Self> {
492 let params = params
493 .iter()
494 .map(|p| Identifier::new(*p))
495 .collect::<Result<Vec<_>>>()?;
496 Ok(Self::Arrow {
497 params,
498 body: Box::new(body),
499 })
500 }
501
502 pub fn arrow_block(params: &[&str], body: Vec<Stmt>) -> Result<Self> {
504 let params = params
505 .iter()
506 .map(|p| Identifier::new(*p))
507 .collect::<Result<Vec<_>>>()?;
508 Ok(Self::ArrowBlock { params, body })
509 }
510
511 #[must_use]
513 pub fn assign(self, value: Expr) -> Self {
514 Self::Assign {
515 target: Box::new(self),
516 value: Box::new(value),
517 }
518 }
519}
520
521impl Stmt {
523 pub fn let_decl(name: impl Into<String>, value: Expr) -> Result<Self> {
525 Ok(Self::Let {
526 name: Identifier::new(name)?,
527 value,
528 })
529 }
530
531 pub fn const_decl(name: impl Into<String>, value: Expr) -> Result<Self> {
533 Ok(Self::Const {
534 name: Identifier::new(name)?,
535 value,
536 })
537 }
538
539 pub fn assign(name: impl Into<String>, value: Expr) -> Result<Self> {
541 Ok(Self::Assign {
542 name: Identifier::new(name)?,
543 value,
544 })
545 }
546
547 pub fn member_assign(obj: Expr, member: impl Into<String>, value: Expr) -> Result<Self> {
549 Ok(Self::MemberAssign {
550 object: obj,
551 member: Identifier::new(member)?,
552 value,
553 })
554 }
555
556 #[must_use]
558 pub fn add_assign(target: Expr, value: Expr) -> Self {
559 Self::AddAssign { target, value }
560 }
561
562 #[must_use]
564 pub fn post_increment(expr: Expr) -> Self {
565 Self::PostIncrement(expr)
566 }
567
568 #[must_use]
570 pub fn expr(e: Expr) -> Self {
571 Self::Expr(e)
572 }
573
574 #[must_use]
576 pub fn ret() -> Self {
577 Self::Return(None)
578 }
579
580 #[must_use]
582 pub fn ret_val(e: Expr) -> Self {
583 Self::Return(Some(e))
584 }
585
586 #[must_use]
588 pub fn if_then(cond: Expr, then_body: Vec<Stmt>) -> Self {
589 Self::If {
590 condition: cond,
591 then_branch: then_body,
592 else_branch: None,
593 }
594 }
595
596 #[must_use]
598 pub fn if_else(cond: Expr, then_body: Vec<Stmt>, else_body: Vec<Stmt>) -> Self {
599 Self::If {
600 condition: cond,
601 then_branch: then_body,
602 else_branch: Some(else_body),
603 }
604 }
605
606 pub fn for_loop(
608 var: impl Into<String>,
609 start: Expr,
610 end: Expr,
611 body: Vec<Stmt>,
612 ) -> Result<Self> {
613 Ok(Self::For {
614 var: Identifier::new(var)?,
615 start,
616 end,
617 body,
618 })
619 }
620
621 #[must_use]
623 pub fn while_loop(cond: Expr, body: Vec<Stmt>) -> Self {
624 Self::While {
625 condition: cond,
626 body,
627 }
628 }
629
630 pub fn try_catch(
632 body: Vec<Stmt>,
633 catch_var: impl Into<String>,
634 handler: Vec<Stmt>,
635 ) -> Result<Self> {
636 Ok(Self::TryCatch {
637 body,
638 catch_var: Identifier::new(catch_var)?,
639 handler,
640 })
641 }
642
643 #[must_use]
645 pub fn comment(text: impl Into<String>) -> Self {
646 Self::Comment(text.into())
647 }
648
649 #[must_use]
651 pub fn on_message(body: Vec<Stmt>) -> Self {
652 Self::OnMessage(body)
653 }
654
655 pub fn register_processor(name: impl Into<String>, class: impl Into<String>) -> Result<Self> {
657 Ok(Self::RegisterProcessor {
658 name: name.into(),
659 class: Identifier::new(class)?,
660 })
661 }
662}
663
664#[cfg(test)]
665#[allow(clippy::unwrap_used, clippy::panic)]
666mod tests {
667 use super::*;
668 use crate::hir::GenerationMetadata;
669
670 #[test]
675 fn module_builder_basic() {
676 let module = JsModuleBuilder::new()
677 .comment("test")
678 .let_decl("x", Expr::num(42))
679 .unwrap()
680 .build();
681
682 assert_eq!(module.statements.len(), 2);
683 }
684
685 #[test]
686 fn module_builder_metadata() {
687 let meta = GenerationMetadata {
688 tool: "test".into(),
689 version: "1.0".into(),
690 input_hash: "abc123".into(),
691 timestamp: "2024-01-01".into(),
692 regenerate_cmd: "cargo run --bin gen".into(),
693 };
694 let module = JsModuleBuilder::new().metadata(meta).build();
695 assert!(module.metadata.is_some());
696 assert_eq!(module.metadata.unwrap().tool, "test");
697 }
698
699 #[test]
700 fn module_builder_stmts() {
701 let stmts = vec![Stmt::comment("a"), Stmt::comment("b")];
702 let module = JsModuleBuilder::new().stmts(stmts).build();
703 assert_eq!(module.statements.len(), 2);
704 }
705
706 #[test]
707 fn module_builder_const_decl() {
708 let module = JsModuleBuilder::new()
709 .const_decl("GRAVITY", Expr::num(9.81))
710 .unwrap()
711 .build();
712 assert_eq!(module.statements.len(), 1);
713 }
714
715 #[test]
716 fn module_builder_assign() {
717 let module = JsModuleBuilder::new()
718 .assign("x", Expr::num(10))
719 .unwrap()
720 .build();
721 assert_eq!(module.statements.len(), 1);
722 }
723
724 #[test]
725 fn module_builder_expr() {
726 let module = JsModuleBuilder::new()
727 .expr(Expr::ident("foo").unwrap())
728 .build();
729 assert_eq!(module.statements.len(), 1);
730 }
731
732 #[test]
733 fn module_builder_class() {
734 let class = JsClassBuilder::new("Test").unwrap().build();
735 let module = JsModuleBuilder::new().class(class).build();
736 assert_eq!(module.statements.len(), 1);
737 }
738
739 #[test]
740 fn module_builder_register_processor() {
741 let module = JsModuleBuilder::new()
742 .register_processor("my-processor", "MyProcessor")
743 .unwrap()
744 .build();
745 assert_eq!(module.statements.len(), 1);
746 }
747
748 #[test]
753 fn class_builder_basic() -> Result<()> {
754 let class = JsClassBuilder::new("Foo")?
755 .extends("Bar")?
756 .constructor(vec![])
757 .method("baz", &["x", "y"], vec![Stmt::ret()])?
758 .build();
759
760 assert_eq!(class.name.as_str(), "Foo");
761 assert_eq!(
762 class
763 .extends
764 .as_ref()
765 .map(super::super::hir::Identifier::as_str),
766 Some("Bar")
767 );
768 assert_eq!(class.methods.len(), 1);
769 Ok(())
770 }
771
772 #[test]
773 fn class_builder_no_extends() {
774 let class = JsClassBuilder::new("Simple").unwrap().build();
775 assert!(class.extends.is_none());
776 assert!(class.constructor.is_none());
777 }
778
779 #[test]
780 fn class_builder_invalid_name() {
781 assert!(JsClassBuilder::new("class").is_err());
782 }
783
784 #[test]
785 fn class_builder_invalid_extends() {
786 let result = JsClassBuilder::new("Valid").unwrap().extends("123invalid");
787 assert!(result.is_err());
788 }
789
790 #[test]
791 fn class_builder_invalid_method_name() {
792 let result = JsClassBuilder::new("Valid")
793 .unwrap()
794 .method("class", &[], vec![]);
795 assert!(result.is_err());
796 }
797
798 #[test]
799 fn class_builder_invalid_method_param() {
800 let result = JsClassBuilder::new("Valid")
801 .unwrap()
802 .method("test", &["class"], vec![]);
803 assert!(result.is_err());
804 }
805
806 #[test]
811 fn switch_builder_basic() {
812 let sw = JsSwitchBuilder::new(Expr::ident("x").unwrap())
813 .case(Expr::num(1), vec![Stmt::ret_val(Expr::str("one"))])
814 .case(Expr::num(2), vec![Stmt::ret_val(Expr::str("two"))])
815 .default(vec![Stmt::ret_val(Expr::str("other"))])
816 .build();
817
818 assert_eq!(sw.cases.len(), 2);
819 assert!(sw.default.is_some());
820 }
821
822 #[test]
823 fn switch_builder_no_default() {
824 let sw = JsSwitchBuilder::new(Expr::num(0))
825 .case(Expr::num(0), vec![])
826 .build();
827
828 assert_eq!(sw.cases.len(), 1);
829 assert!(sw.default.is_none());
830 }
831
832 #[test]
837 fn expr_builder_chain() -> Result<()> {
838 let expr = Expr::ident("foo")?.dot("bar")?.call(vec![Expr::num(1)]);
839 match expr {
840 Expr::Call { callee, args } => {
841 assert_eq!(args.len(), 1);
842 match *callee {
843 Expr::Member { property, .. } => {
844 assert_eq!(property.as_str(), "bar");
845 }
846 _ => panic!("expected member"),
847 }
848 }
849 _ => panic!("expected call"),
850 }
851 Ok(())
852 }
853
854 #[test]
855 fn expr_literals() {
856 assert!(matches!(Expr::null(), Expr::Null));
857 assert!(matches!(Expr::bool(true), Expr::Bool(true)));
858 assert!(matches!(Expr::num(42), Expr::Num(n) if (n - 42.0).abs() < f64::EPSILON));
859 assert!(matches!(Expr::str("hi"), Expr::Str(s) if s == "hi"));
860 assert!(matches!(Expr::this(), Expr::This));
861 }
862
863 #[test]
864 fn expr_index() {
865 let expr = Expr::ident("arr").unwrap().index(Expr::num(0));
866 assert!(matches!(expr, Expr::Index { .. }));
867 }
868
869 #[test]
870 fn expr_new() {
871 let expr = Expr::ident("Date").unwrap().new_expr(vec![]);
872 assert!(matches!(expr, Expr::New { .. }));
873 }
874
875 #[test]
876 fn expr_await() {
877 let expr = Expr::ident("promise").unwrap().await_expr();
878 assert!(matches!(expr, Expr::Await(_)));
879 }
880
881 #[test]
882 fn expr_import() {
883 let expr = Expr::import(Expr::str("./module.js"));
884 assert!(matches!(expr, Expr::Import(_)));
885 }
886
887 #[test]
888 fn expr_binary_ops() {
889 let a = Expr::num(1);
890 let b = Expr::num(2);
891
892 assert!(matches!(
893 a.clone().add(b.clone()),
894 Expr::Binary { op: BinOp::Add, .. }
895 ));
896 assert!(matches!(
897 a.clone().sub(b.clone()),
898 Expr::Binary { op: BinOp::Sub, .. }
899 ));
900 assert!(matches!(
901 a.clone().mul(b.clone()),
902 Expr::Binary { op: BinOp::Mul, .. }
903 ));
904 assert!(matches!(
905 a.clone().div(b.clone()),
906 Expr::Binary { op: BinOp::Div, .. }
907 ));
908 assert!(matches!(
909 a.clone().modulo(b.clone()),
910 Expr::Binary { op: BinOp::Mod, .. }
911 ));
912 assert!(matches!(
913 a.clone().eq(b.clone()),
914 Expr::Binary {
915 op: BinOp::EqStrict,
916 ..
917 }
918 ));
919 assert!(matches!(
920 a.clone().ne(b.clone()),
921 Expr::Binary {
922 op: BinOp::NeStrict,
923 ..
924 }
925 ));
926 assert!(matches!(
927 a.clone().lt(b.clone()),
928 Expr::Binary { op: BinOp::Lt, .. }
929 ));
930 assert!(matches!(
931 a.clone().le(b.clone()),
932 Expr::Binary { op: BinOp::Le, .. }
933 ));
934 assert!(matches!(
935 a.clone().gt(b.clone()),
936 Expr::Binary { op: BinOp::Gt, .. }
937 ));
938 assert!(matches!(
939 a.clone().ge(b.clone()),
940 Expr::Binary { op: BinOp::Ge, .. }
941 ));
942 assert!(matches!(
943 a.clone().and(b.clone()),
944 Expr::Binary { op: BinOp::And, .. }
945 ));
946 assert!(matches!(
947 a.clone().or(b),
948 Expr::Binary { op: BinOp::Or, .. }
949 ));
950 }
951
952 #[test]
953 fn expr_unary_ops() {
954 let x = Expr::num(1);
955 assert!(matches!(
956 x.clone().not(),
957 Expr::Unary {
958 op: UnaryOp::Not,
959 ..
960 }
961 ));
962 assert!(matches!(
963 x.clone().neg(),
964 Expr::Unary {
965 op: UnaryOp::Neg,
966 ..
967 }
968 ));
969 assert!(matches!(
970 x.type_of(),
971 Expr::Unary {
972 op: UnaryOp::TypeOf,
973 ..
974 }
975 ));
976 }
977
978 #[test]
979 fn expr_ternary() {
980 let expr = Expr::bool(true).ternary(Expr::num(1), Expr::num(0));
981 assert!(matches!(expr, Expr::Ternary { .. }));
982 }
983
984 #[test]
985 fn expr_object() {
986 let expr = Expr::object(vec![("a", Expr::num(1)), ("b", Expr::num(2))]);
987 match expr {
988 Expr::Object(pairs) => assert_eq!(pairs.len(), 2),
989 _ => panic!("expected object"),
990 }
991 }
992
993 #[test]
994 fn expr_array() {
995 let expr = Expr::array(vec![Expr::num(1), Expr::num(2), Expr::num(3)]);
996 match expr {
997 Expr::Array(items) => assert_eq!(items.len(), 3),
998 _ => panic!("expected array"),
999 }
1000 }
1001
1002 #[test]
1003 fn expr_arrow() {
1004 let expr = Expr::arrow(&["x"], Expr::ident("x").unwrap()).unwrap();
1005 assert!(matches!(expr, Expr::Arrow { .. }));
1006 }
1007
1008 #[test]
1009 fn expr_arrow_block() {
1010 let expr =
1011 Expr::arrow_block(&["x"], vec![Stmt::ret_val(Expr::ident("x").unwrap())]).unwrap();
1012 assert!(matches!(expr, Expr::ArrowBlock { .. }));
1013 }
1014
1015 #[test]
1016 fn expr_arrow_invalid_param() {
1017 assert!(Expr::arrow(&["class"], Expr::null()).is_err());
1018 assert!(Expr::arrow_block(&["class"], vec![]).is_err());
1019 }
1020
1021 #[test]
1022 fn expr_assign() {
1023 let expr = Expr::ident("x").unwrap().assign(Expr::num(10));
1024 assert!(matches!(expr, Expr::Assign { .. }));
1025 }
1026
1027 #[test]
1028 fn expr_dot_invalid() {
1029 let result = Expr::ident("obj").unwrap().dot("class");
1030 assert!(result.is_err());
1031 }
1032
1033 #[test]
1038 fn stmt_let_const_assign() {
1039 assert!(Stmt::let_decl("x", Expr::num(1)).is_ok());
1040 assert!(Stmt::const_decl("Y", Expr::num(2)).is_ok());
1041 assert!(Stmt::assign("z", Expr::num(3)).is_ok());
1042
1043 assert!(Stmt::let_decl("class", Expr::num(1)).is_err());
1045 assert!(Stmt::const_decl("class", Expr::num(1)).is_err());
1046 assert!(Stmt::assign("class", Expr::num(1)).is_err());
1047 }
1048
1049 #[test]
1050 fn stmt_member_assign() {
1051 let stmt = Stmt::member_assign(Expr::this(), "prop", Expr::num(42)).unwrap();
1052 assert!(matches!(stmt, Stmt::MemberAssign { .. }));
1053
1054 assert!(Stmt::member_assign(Expr::this(), "class", Expr::null()).is_err());
1056 }
1057
1058 #[test]
1059 fn stmt_add_assign() {
1060 let stmt = Stmt::add_assign(Expr::ident("x").unwrap(), Expr::num(1));
1061 assert!(matches!(stmt, Stmt::AddAssign { .. }));
1062 }
1063
1064 #[test]
1065 fn stmt_post_increment() {
1066 let stmt = Stmt::post_increment(Expr::ident("i").unwrap());
1067 assert!(matches!(stmt, Stmt::PostIncrement(_)));
1068 }
1069
1070 #[test]
1071 fn stmt_return() {
1072 assert!(matches!(Stmt::ret(), Stmt::Return(None)));
1073 assert!(matches!(Stmt::ret_val(Expr::num(0)), Stmt::Return(Some(_))));
1074 }
1075
1076 #[test]
1077 fn stmt_if() {
1078 let stmt = Stmt::if_then(Expr::bool(true), vec![Stmt::ret()]);
1079 match stmt {
1080 Stmt::If { else_branch, .. } => assert!(else_branch.is_none()),
1081 _ => panic!("expected if"),
1082 }
1083
1084 let stmt = Stmt::if_else(Expr::bool(true), vec![], vec![]);
1085 match stmt {
1086 Stmt::If { else_branch, .. } => assert!(else_branch.is_some()),
1087 _ => panic!("expected if"),
1088 }
1089 }
1090
1091 #[test]
1092 fn stmt_for_loop() {
1093 let stmt = Stmt::for_loop("i", Expr::num(0), Expr::num(10), vec![]).unwrap();
1094 assert!(matches!(stmt, Stmt::For { .. }));
1095
1096 assert!(Stmt::for_loop("class", Expr::num(0), Expr::num(10), vec![]).is_err());
1098 }
1099
1100 #[test]
1101 fn stmt_while_loop() {
1102 let stmt = Stmt::while_loop(Expr::bool(true), vec![]);
1103 assert!(matches!(stmt, Stmt::While { .. }));
1104 }
1105
1106 #[test]
1107 fn stmt_try_catch() {
1108 let stmt = Stmt::try_catch(vec![], "e", vec![]).unwrap();
1109 assert!(matches!(stmt, Stmt::TryCatch { .. }));
1110
1111 assert!(Stmt::try_catch(vec![], "class", vec![]).is_err());
1113 }
1114
1115 #[test]
1116 fn stmt_comment() {
1117 let stmt = Stmt::comment("test comment");
1118 assert!(matches!(stmt, Stmt::Comment(_)));
1119 }
1120
1121 #[test]
1122 fn stmt_on_message() {
1123 let stmt = Stmt::on_message(vec![]);
1124 assert!(matches!(stmt, Stmt::OnMessage(_)));
1125 }
1126
1127 #[test]
1128 fn stmt_register_processor() {
1129 let stmt = Stmt::register_processor("my-proc", "MyProc").unwrap();
1130 assert!(matches!(stmt, Stmt::RegisterProcessor { .. }));
1131
1132 assert!(Stmt::register_processor("proc", "class").is_err());
1134 }
1135
1136 #[test]
1137 fn identifier_validation() {
1138 assert!(Identifier::new("validName").is_ok());
1139 assert!(Identifier::new("class").is_err());
1140 assert!(Identifier::new("123invalid").is_err());
1141 }
1142}