1use std::borrow::Cow;
2use std::collections::BTreeMap;
3use std::collections::HashMap;
4use std::convert::Infallible;
5use std::env::Args;
6use std::fmt::Debug;
7use std::fmt::Display;
8use std::hash::Hash;
9
10use portal_jsc_swc_batch::ImportMapping;
11use portal_jsc_swc_util::BreakKind;
12use portal_jsc_swc_util::ModuleMapper;
13use portal_jsc_swc_util::{Extract, ImportMapper, ImportOr, MakeSpanned};
14use swc_atoms::Atom;
15use swc_common::{Span, Spanned};
16use swc_ecma_ast::BreakStmt;
17use swc_ecma_ast::ContinueStmt;
18use swc_ecma_ast::Function;
19use swc_ecma_ast::Id;
20use swc_ecma_ast::KeyValueProp;
21use swc_ecma_ast::MethodProp;
22use swc_ecma_ast::ObjectLit;
23use swc_ecma_ast::Param;
24use swc_ecma_ast::Prop;
25use swc_ecma_ast::SwitchCase;
26use swc_ecma_ast::SwitchStmt;
27use swc_ecma_ast::{
28 ArrowExpr, AssignExpr, AssignOp, BinExpr, BinaryOp, BindingIdent, BlockStmt, CallExpr, Expr,
29 ExprOrSpread, ExprStmt, Ident, IdentName, IfStmt, LabeledStmt, Lit, MemberExpr, MemberProp,
30 ReturnStmt, SimpleAssignTarget, Stmt, WhileStmt,
31};
32use typenum::Same;
33
34pub trait Dialect {
35 type Mark<T>: Extract<T>;
36 type MarkSpanned<T: Spanned + Clone + Debug + Hash + Eq>: Same<Output = Self::Mark<T>>
37 + Extract<T>
38 + Spanned
39 + Clone
40 + Debug
41 + Hash
42 + Eq;
43 type Tag: Spanned + Clone + Debug + Hash + Eq;
44 fn span<T: Spanned + Clone + Debug + Hash + Eq>(
45 a: Self::Mark<()>,
46 b: T,
47 ) -> Self::MarkSpanned<T>;
48 fn despan<T: Spanned + Clone + Debug + Hash + Eq>(
49 a: Self::MarkSpanned<T>,
50 ) -> (Self::Mark<()>, T);
51}
52
53#[non_exhaustive]
54#[derive(Clone, Hash, Debug, PartialEq, Eq, Spanned)]
55pub enum SimplExpr<D: Dialect> {
56 Lit(Lit),
57 Ident(D::MarkSpanned<SimplPath>),
58 Assign(MakeSpanned<SimplAssignment<D>>),
59 Bin(MakeSpanned<SimplBinOp<D>>),
60 Call(MakeSpanned<Box<SimplCallExpr<D>>>),
61 Select(MakeSpanned<SimplSelectExpr<D>>),
62}
63#[non_exhaustive]
64#[derive(Clone, Hash, Debug, PartialEq, Eq, Spanned)]
65pub enum SimplStmt<D: Dialect> {
66 Expr(MakeSpanned<Box<SimplExpr<D>>>),
67 Block(MakeSpanned<Vec<SimplStmt<D>>>),
68 If(MakeSpanned<SimplIf<D>>),
69 Return(MakeSpanned<Box<SimplExpr<D>>>),
70 Break(Ident),
71 Continue(Ident),
72 Switch(MakeSpanned<SimplSwitchStmt<D>>),
73}
74#[derive(Clone, Hash, Debug, PartialEq, Eq, Spanned)]
75pub struct SimplPath {
76 #[span]
77 pub root: Ident,
78 pub keys: Vec<Atom>,
79}
80#[derive(Clone, Hash, Debug, PartialEq, Eq)]
81pub struct SimplPathId {
82 pub root: Id,
83 pub keys: Vec<Atom>,
84}
85impl SimplPath {
86 pub fn to_id(&self) -> SimplPathId {
87 SimplPathId {
88 root: self.root.to_id(),
89 keys: self.keys.clone(),
90 }
91 }
92}
93impl SimplPathId {
94 pub fn span(self, span: Span) -> SimplPath {
95 SimplPath {
96 root: Ident {
97 span: span,
98 ctxt: self.root.1,
99 sym: self.root.0,
100 optional: false,
101 },
102 keys: self.keys,
103 }
104 }
105}
106#[derive(Clone, Hash, Debug, PartialEq, Eq)]
107pub struct FuncId<E, P> {
108 pub path: P,
110 pub template_args: BTreeMap<Atom, E>,
111}
112impl<E, P: Spanned> Spanned for FuncId<E, P> {
113 fn span(&self) -> Span {
114 self.path.span()
115 }
116}
117impl<E, P> FuncId<E, P> {
118 pub fn map<F, Q, Error>(
119 self,
120 mut path: impl FnMut(P) -> Result<Q, Error>,
121 mut arg: impl FnMut(E) -> Result<F, Error>,
122 ) -> Result<FuncId<F, Q>, Error> {
123 Ok(FuncId {
124 path: path(self.path)?,
125 template_args: self
126 .template_args
127 .into_iter()
128 .map(|(k, v)| Ok((k, arg(v)?)))
129 .collect::<Result<_, Error>>()?,
130 })
131 }
132}
133#[derive(Clone, Hash, Debug, PartialEq, Eq, Spanned)]
134pub enum SimplCallExpr<D: Dialect> {
135 Path {
136 #[span]
137 path: FuncId<Expr, D::MarkSpanned<SimplPath>>,
138 args: Vec<SimplExpr<D>>,
139 },
140 Tag {
141 #[span]
142 tag: FuncId<Expr, D::Tag>,
143 args: Vec<SimplExpr<D>>,
144 },
145 Block(Box<SimplStmt<D>>),
146}
147#[derive(Clone, Hash, Debug, PartialEq, Eq)]
148pub struct SimplSelectExpr<D: Dialect> {
149 pub scrutinee: Box<SimplExpr<D>>,
150 pub cases: BTreeMap<Id, (Vec<SimplStmt<D>>, Vec<Ident>)>,
151}
152#[derive(Clone, Hash, Debug, PartialEq, Eq)]
153pub struct SimplSwitchStmt<D: Dialect> {
154 pub scrutinee: Box<SimplExpr<D>>,
155 pub label: Ident,
156 pub cases: Vec<(Box<SimplExpr<D>>, Vec<SimplStmt<D>>, BreakKind)>,
157}
158#[derive(Clone, Hash, Debug, PartialEq, Eq)]
159pub struct SimplAssignment<D: Dialect> {
160 pub target: D::MarkSpanned<SimplPath>,
161 pub assign: AssignOp,
162 pub body: Box<SimplExpr<D>>,
163}
164#[derive(Clone, Hash, Debug, PartialEq, Eq)]
165pub struct SimplBinOp<D: Dialect> {
166 pub lhs: Box<SimplExpr<D>>,
167 pub op: BinaryOp,
168 pub rhs: Box<SimplExpr<D>>,
169}
170#[derive(Clone, Hash, Debug, PartialEq, Eq)]
171pub struct SimplIf<D: Dialect> {
172 pub kind: SimplIfKind<D>,
173 pub cond: Box<SimplExpr<D>>,
174 pub body: Vec<SimplStmt<D>>,
175}
176
177#[derive(Clone, Hash, Debug, PartialEq, Eq)]
178pub enum SimplIfKind<D: Dialect> {
179 If { r#else: Vec<SimplStmt<D>> },
180 While { label: Ident },
181}
182
183impl<D: Dialect> SimplStmt<D> {
184 pub fn apply_label(&mut self, label: &Ident) {
185 match self {
186 SimplStmt::Expr(make_spanned) | SimplStmt::Return(make_spanned) => {}
187 SimplStmt::Block(make_spanned) => {
188 for a in make_spanned.value.iter_mut() {
189 a.apply_label(label);
190 }
191 }
192 SimplStmt::If(make_spanned) => match &mut make_spanned.value.kind {
193 SimplIfKind::If { r#else } => {
194 for a in make_spanned.value.body.iter_mut().chain(r#else.iter_mut()) {
195 a.apply_label(label);
196 }
197 }
198 SimplIfKind::While { label: l } => {
199 *l = label.clone();
200 }
201 },
202 SimplStmt::Break(l) | SimplStmt::Continue(l) => {
203 *l = label.clone();
204 }
205 SimplStmt::Switch(make_spanned) => {
206 make_spanned.value.label = label.clone();
207 }
208 }
209 }
210}
211
212impl<D: Dialect<Tag = Infallible>> From<SimplExpr<D>> for Expr {
213 fn from(value: SimplExpr<D>) -> Self {
214 match value {
215 SimplExpr::Ident(i) => match i.extract_own() {
216 p => p.keys.into_iter().fold(Expr::Ident(p.root), |a, b| {
217 let asp = a.span();
218 Expr::Member(MemberExpr {
219 span: a.span(),
220 obj: Box::new(a),
221 prop: swc_ecma_ast::MemberProp::Ident(IdentName { span: asp, sym: b }),
222 })
223 }),
224 },
225 SimplExpr::Assign(a) => match a.value.target.extract_own() {
226 mut p => {
227 let h = match p.keys.pop() {
228 None => SimpleAssignTarget::Ident(BindingIdent {
229 id: p.root,
230 type_ann: None,
231 }),
232 Some(pa) => SimpleAssignTarget::Member(MemberExpr {
233 span: a.span,
234 obj: Box::new(p.keys.into_iter().fold(Expr::Ident(p.root), |a, b| {
235 let asp = a.span();
236 Expr::Member(MemberExpr {
237 span: a.span(),
238 obj: Box::new(a),
239 prop: swc_ecma_ast::MemberProp::Ident(IdentName {
240 span: asp,
241 sym: b,
242 }),
243 })
244 })),
245 prop: MemberProp::Ident(IdentName {
246 span: a.span,
247 sym: pa,
248 }),
249 }),
250 };
251 Expr::Assign(AssignExpr {
252 span: a.span,
253 op: a.value.assign,
254 left: swc_ecma_ast::AssignTarget::Simple(h),
255 right: Box::new((*a.value.body).into()),
256 })
257 }
258 },
259 SimplExpr::Bin(b) => Expr::Bin(BinExpr {
260 span: b.span,
261 op: b.value.op,
262 left: Box::new((*b.value.lhs).into()),
263 right: Box::new((*b.value.rhs).into()),
264 }),
265 SimplExpr::Lit(l) => Expr::Lit(l),
266 SimplExpr::Call(c) => match *c.value {
267 SimplCallExpr::Path { path, args } => {
268 let pid = match path.path.extract_own() {
269 p => p.keys.into_iter().fold(Expr::Ident(p.root), |a, b| {
270 let asp = a.span();
271 Expr::Member(MemberExpr {
272 span: a.span(),
273 obj: Box::new(a),
274 prop: swc_ecma_ast::MemberProp::Ident(IdentName {
275 span: asp,
276 sym: b,
277 }),
278 })
279 }),
280 };
281 Expr::Call(CallExpr {
282 span: c.span,
283 ctxt: Default::default(),
284 callee: swc_ecma_ast::Callee::Expr(Box::new(pid)),
285 args: if path.template_args.len() == 0 {
286 None
287 } else {
288 Some(Expr::Object(ObjectLit {
289 span: c.span,
290 props: path
291 .template_args
292 .into_iter()
293 .map(|(a, b)| {
294 swc_ecma_ast::PropOrSpread::Prop(Box::new(Prop::KeyValue(
295 KeyValueProp {
296 key: swc_ecma_ast::PropName::Ident(IdentName {
297 span: c.span,
298 sym: a,
299 }),
300 value: Box::new(b),
301 },
302 )))
303 })
304 .collect(),
305 }))
306 }
307 .into_iter()
308 .chain(args.into_iter().map(|a| a.into()))
309 .map(|a| ExprOrSpread {
310 spread: None,
311 expr: Box::new(a),
312 })
313 .collect(),
314 type_args: None,
315 })
316 }
317 SimplCallExpr::Block(simpl_stmt) => {
318 let pid = Expr::Arrow(ArrowExpr {
319 span: c.span,
320 ctxt: Default::default(),
321 params: vec![],
322 body: Box::new(swc_ecma_ast::BlockStmtOrExpr::BlockStmt(BlockStmt {
323 span: c.span,
324 ctxt: Default::default(),
325 stmts: vec![(*simpl_stmt).into()],
326 })),
327 is_async: false,
328 is_generator: false,
329 type_params: None,
330 return_type: None,
331 });
332 Expr::Call(CallExpr {
333 span: c.span,
334 ctxt: Default::default(),
335 callee: swc_ecma_ast::Callee::Expr(Box::new(pid)),
336 args: vec![],
337 type_args: None,
338 })
339 }
340 SimplCallExpr::Tag { tag, args } => match tag {},
341 },
342 SimplExpr::Select(s) => Expr::Call(CallExpr {
343 span: s.span,
344 ctxt: Default::default(),
345 callee: swc_ecma_ast::Callee::Expr(Box::new(Expr::Member(MemberExpr {
346 span: s.span,
347 obj: Box::new((*s.value.scrutinee).into()),
348 prop: MemberProp::Ident(IdentName {
349 span: s.span,
350 sym: Atom::new("$match"),
351 }),
352 }))),
353 args: vec![ExprOrSpread {
354 spread: None,
355 expr: Box::new(Expr::Object(ObjectLit {
356 span: s.span,
357 props: s
358 .value
359 .cases
360 .into_iter()
361 .map(|p| {
362 swc_ecma_ast::PropOrSpread::Prop(Box::new(Prop::Method(
363 MethodProp {
364 key: swc_ecma_ast::PropName::Ident(IdentName {
365 span: s.span,
366 sym: p.0 .0,
367 }),
368 function: Box::new(Function {
369 params: p
370 .1
371 .1
372 .into_iter()
373 .map(|x| Param {
374 span: s.span,
375 decorators: vec![],
376 pat: swc_ecma_ast::Pat::Ident(x.into()),
377 })
378 .collect(),
379 decorators: vec![],
380 span: s.span,
381 ctxt: Default::default(),
382 body: Some(BlockStmt {
383 span: s.span,
384 ctxt: Default::default(),
385 stmts: p
386 .1
387 .0
388 .into_iter()
389 .map(|a| a.into())
390 .collect(),
391 }),
392 is_generator: false,
393 is_async: false,
394 type_params: None,
395 return_type: None,
396 }),
397 },
398 )))
399 })
400 .collect(),
401 })),
402 }],
403 type_args: None,
404 }),
405 _ => todo!(),
406 }
407 }
408}
409impl<D: Dialect> SimplStmt<D> {
410 pub fn flat(self) -> Vec<SimplStmt<D>> {
411 match self {
412 SimplStmt::Block(b) => b.value.into_iter().flat_map(|a| a.flat()).collect(),
413 SimplStmt::If(i) => vec![SimplStmt::If(MakeSpanned {
414 value: SimplIf {
415 kind: match i.value.kind {
416 SimplIfKind::If { r#else } => SimplIfKind::If {
417 r#else: r#else.into_iter().flat_map(|a| a.flat()).collect(),
418 },
419 a => a,
420 },
421 cond: i.value.cond,
422 body: i.value.body.into_iter().flat_map(|a| a.flat()).collect(),
423 },
424 span: i.span,
425 })],
426 a => vec![a],
427 }
428 }
429}
430impl<D: Dialect<Tag = Infallible>> From<SimplStmt<D>> for Stmt {
431 fn from(value: SimplStmt<D>) -> Self {
432 match value {
433 SimplStmt::Expr(e) => Stmt::Expr(ExprStmt {
434 expr: Box::new((*e.value).into()),
435 span: e.span,
436 }),
437 SimplStmt::Block(b) => Stmt::Block(BlockStmt {
438 span: b.span,
439 ctxt: Default::default(),
440 stmts: b.value.into_iter().map(|a| a.into()).collect(),
441 }),
442 SimplStmt::Return(e) => Stmt::Return(ReturnStmt {
443 span: e.span,
444 arg: Some(Box::new((*e.value).into())),
445 }),
446 SimplStmt::If(i) => match i.value.kind {
447 SimplIfKind::If { r#else } => Stmt::If(IfStmt {
448 span: i.span,
449 test: Box::new((*i.value.cond).into()),
450 cons: Box::new(Stmt::Block(BlockStmt {
451 span: i.span,
452 ctxt: Default::default(),
453 stmts: i.value.body.into_iter().map(|a| a.into()).collect(),
454 })),
455 alt: if r#else.len() != 0 {
456 Some(Box::new(Stmt::Block(BlockStmt {
457 span: i.span,
458 ctxt: Default::default(),
459 stmts: r#else.into_iter().map(|a| a.into()).collect(),
460 })))
461 } else {
462 None
463 },
464 }),
465 SimplIfKind::While { label } => Stmt::Labeled(LabeledStmt {
466 span: i.span,
467 label,
468 body: Box::new(Stmt::While(WhileStmt {
469 span: i.span,
470 test: Box::new((*i.value.cond).into()),
471 body: Box::new(Stmt::Block(BlockStmt {
472 span: i.span,
473 ctxt: Default::default(),
474 stmts: i.value.body.into_iter().map(|a| a.into()).collect(),
475 })),
476 })),
477 }),
478 },
479 SimplStmt::Break(b) => Stmt::Break(BreakStmt {
480 span: b.span(),
481 label: Some(b),
482 }),
483 SimplStmt::Continue(b) => Stmt::Continue(ContinueStmt {
484 span: b.span(),
485 label: Some(b),
486 }),
487 SimplStmt::Switch(s) => Stmt::Labeled(LabeledStmt {
488 span: s.span,
489 label: s.value.label,
490 body: Box::new(Stmt::Switch(SwitchStmt {
491 span: s.span,
492 discriminant: Box::new((*s.value.scrutinee).into()),
493 cases: s
494 .value
495 .cases
496 .into_iter()
497 .map(|(a, b, c)| SwitchCase {
498 span: s.span,
499 test: Some(Box::new((*a).into())),
500 cons: b
501 .into_iter()
502 .map(|c| c.into())
503 .chain(match c {
504 BreakKind::BreakAfter => Some(Stmt::Break(BreakStmt {
505 span: s.span,
506 label: None,
507 })),
508 BreakKind::DoNotBreakAfter => None,
509 })
510 .collect(),
511 })
512 .collect(),
513 })),
514 }),
515 _ => todo!(),
516 }
517 }
518}
519#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
520pub enum Error {
521 Unsupported,
522}
523impl Display for Error {
524 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
525 match self {
526 Error::Unsupported => write!(f, "unsupported syntax construct"),
527 }
528 }
529}
530impl std::error::Error for Error {
531 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
532 match self {
533 _ => None,
534 }
535 }
536}
537pub trait ConvTagLookup<D: Dialect> {
538 fn lookup_tag<'a, 'b>(
539 &self,
540 a: &'a Expr,
541 args: &'b [ExprOrSpread],
542 ) -> Result<(D::Tag, Cow<'b, [ExprOrSpread]>), &'a Expr>;
543}
544pub trait ConvCtx<D: ConvDialect>: ImportMapper + ModuleMapper + ConvTagLookup<D> {}
545impl<D: ConvDialect, T: ImportMapper + ModuleMapper + ConvTagLookup<D> + ?Sized> ConvCtx<D> for T {}
546pub trait Conv {
547 type Target<D: Dialect>;
548 fn conv<D: ConvDialect>(&self, imports: &impl ConvCtx<D>) -> Result<Self::Target<D>, Error>;
549}
550pub trait ConvDialect: Dialect {
551 type IMark<T>: Extract<T> + From<T>;
552 type IMarkSpanned<T: Spanned + Clone + Debug + Hash + Eq>: Same<Output = Self::IMark<T>>
553 + Extract<T>
554 + From<T>
555 + Spanned
556 + Clone
557 + Debug
558 + Hash
559 + Eq;
560 fn new_import<T: Eq + Hash + Clone + Spanned + Debug>(
561 a: ImportOr<Self::IMarkSpanned<T>>,
562 ) -> Self::MarkSpanned<T>;
563 fn get_import<T: Eq + Hash + Clone + Spanned + Debug>(
564 a: Self::MarkSpanned<T>,
565 ) -> ImportOr<Self::IMarkSpanned<T>>;
566}
567impl Conv for Expr {
568 type Target<D: Dialect> = SimplExpr<D>;
569
570 fn conv<D: ConvDialect>(&self, imports: &impl ConvCtx<D>) -> Result<Self::Target<D>, Error> {
571 Ok(match self {
572 Expr::Lit(l) => SimplExpr::Lit(l.clone()),
573 Expr::Ident(i) => SimplExpr::Ident(match i.clone() {
574 i => D::new_import(match imports.import_of(&i.to_id()) {
575 None => ImportOr::NotImport(
576 SimplPath {
577 root: i,
578 keys: vec![],
579 }
580 .into(),
581 ),
582 Some((a, b)) => ImportOr::Import {
583 value: SimplPath {
584 root: i,
585 keys: vec![],
586 }
587 .into(),
588 module: a,
589 name: b,
590 },
591 }),
592 }),
593 Expr::Member(m) => {
594 let a: SimplExpr<D> = m.obj.conv(imports)?;
595 let mut path: ImportOr<_> = D::get_import(match a {
596 SimplExpr::Ident(path) => path,
597 SimplExpr::Assign(a) => a.value.target,
598 _ => return Err(Error::Unsupported),
599 });
600 let MemberProp::Ident(i) = &m.prop else {
601 return Err(Error::Unsupported);
602 };
603 match &mut path {
604 ImportOr::NotImport(value) => {
605 value.as_mut().keys.push(i.sym.clone());
606 }
607 ImportOr::Import {
608 value,
609 module,
610 name,
611 } => {
612 value.as_mut().keys.push(i.sym.clone());
613 }
614 };
615 SimplExpr::Ident(D::new_import(path))
616 }
617 Expr::Assign(a) => {
618 let e: SimplExpr<D> = match &a.left {
619 swc_ecma_ast::AssignTarget::Simple(simple_assign_target) => {
620 match simple_assign_target {
621 swc_ecma_ast::SimpleAssignTarget::Ident(binding_ident) => {
622 Expr::Ident(binding_ident.id.clone()).conv(imports)?
623 }
624 swc_ecma_ast::SimpleAssignTarget::Member(m) => {
625 let a: SimplExpr<D> = m.obj.conv(imports)?;
626 let mut path: ImportOr<_> = D::get_import(match a {
627 SimplExpr::Ident(path) => path,
628 SimplExpr::Assign(a) => a.value.target,
629 _ => return Err(Error::Unsupported),
630 });
631 let MemberProp::Ident(i) = &m.prop else {
632 return Err(Error::Unsupported);
633 };
634 match &mut path {
635 ImportOr::NotImport(value) => {
636 value.as_mut().keys.push(i.sym.clone());
637 }
638 ImportOr::Import {
639 value,
640 module,
641 name,
642 } => {
643 value.as_mut().keys.push(i.sym.clone());
644 }
645 };
646 SimplExpr::Ident(D::new_import(path))
647 }
648 swc_ecma_ast::SimpleAssignTarget::SuperProp(super_prop_expr) => todo!(),
649 swc_ecma_ast::SimpleAssignTarget::Paren(paren_expr) => todo!(),
650 swc_ecma_ast::SimpleAssignTarget::OptChain(opt_chain_expr) => todo!(),
651 swc_ecma_ast::SimpleAssignTarget::TsAs(ts_as_expr) => todo!(),
652 swc_ecma_ast::SimpleAssignTarget::TsSatisfies(ts_satisfies_expr) => {
653 todo!()
654 }
655 swc_ecma_ast::SimpleAssignTarget::TsNonNull(ts_non_null_expr) => {
656 todo!()
657 }
658 swc_ecma_ast::SimpleAssignTarget::TsTypeAssertion(
659 ts_type_assertion,
660 ) => todo!(),
661 swc_ecma_ast::SimpleAssignTarget::TsInstantiation(ts_instantiation) => {
662 todo!()
663 }
664 swc_ecma_ast::SimpleAssignTarget::Invalid(invalid) => todo!(),
665 }
666 }
667 swc_ecma_ast::AssignTarget::Pat(assign_target_pat) => todo!(),
668 };
669 let mut path = match e {
670 SimplExpr::Ident(path) => path,
671 SimplExpr::Assign(a) => a.value.target,
672 _ => return Err(Error::Unsupported),
673 };
674 SimplExpr::Assign(MakeSpanned {
675 value: SimplAssignment {
676 target: path,
677 assign: a.op,
678 body: Box::new(a.right.conv(imports)?),
679 },
680 span: a.span,
681 })
682 }
683 Expr::Bin(b) => SimplExpr::Bin(MakeSpanned {
684 value: SimplBinOp {
685 lhs: Box::new(b.left.conv(imports)?),
686 op: b.op,
687 rhs: Box::new(b.right.conv(imports)?),
688 },
689 span: b.span,
690 }),
691 Expr::Call(c) => {
692 match &c.callee {
693 swc_ecma_ast::Callee::Super(_) => todo!(),
694 swc_ecma_ast::Callee::Import(import) => todo!(),
695 swc_ecma_ast::Callee::Expr(expr) => {
696 match &**expr {
697 Expr::Fn(f) if f.function.params.len() == 0 => {
698 SimplExpr::Call(MakeSpanned {
699 value: Box::new(SimplCallExpr::Block(Box::new(
700 SimplStmt::Block(MakeSpanned {
701 value: f
702 .function
703 .body
704 .iter()
705 .flat_map(|a| a.stmts.iter())
706 .map(|s| s.conv(imports))
707 .collect::<Result<Vec<_>, _>>()?,
708 span: f.span(),
709 }),
710 ))),
711 span: f.span(),
712 })
713 }
714 Expr::Arrow(f) if f.params.len() == 0 => SimplExpr::Call(MakeSpanned {
715 value: Box::new(SimplCallExpr::Block(Box::new(SimplStmt::Block(
716 MakeSpanned {
717 value: match &*f.body {
718 swc_ecma_ast::BlockStmtOrExpr::BlockStmt(
719 block_stmt,
720 ) => block_stmt
721 .stmts
722 .iter()
723 .map(|s| s.conv(imports))
724 .collect::<Result<Vec<_>, _>>()?,
725 swc_ecma_ast::BlockStmtOrExpr::Expr(expr) => {
726 vec![SimplStmt::Return(MakeSpanned {
727 value: Box::new(expr.conv(imports)?),
728 span: f.span(),
729 })]
730 }
731 },
732 span: f.span(),
733 },
734 )))),
735 span: f.span(),
736 }),
737 e => {
738 match imports.lookup_tag(e, &c.args) {
739 Err(e) => {
740 let a: SimplExpr<D> = e.conv(imports)?;
741 let mut path = match &a {
742 SimplExpr::Ident(path) => path,
743 SimplExpr::Assign(a) => &a.value.target,
744 _ => return Err(Error::Unsupported),
745 }
746 .clone();
747 match (
748 &c.args[..],
749 path.as_ref().keys.last().map(|a| a as &str),
750 ) {
751 (
752 &[ExprOrSpread {
753 ref spread,
754 ref expr,
755 }],
756 Some("$match"),
757 ) if expr.as_object().is_some() => {
758 let obj = expr.as_object().unwrap();
759 SimplExpr::Select(MakeSpanned {
760 value: SimplSelectExpr {
761 scrutinee: Box::new(a),
762 cases: obj
763 .props
764 .iter()
765 .filter_map(|a| a.as_prop())
766 .filter_map(|p| {
767 let (id, body, args) = match &**p {
768 Prop::Method(m) => (
769 m.key.as_ident()?.clone(),
770 m.function.body.as_ref(),
771 m.function
772 .params
773 .iter()
774 .map(|a| a.pat.clone())
775 .collect(),
776 ),
777 Prop::KeyValue(a) => match &*a.value {
778 Expr::Fn(f) => (
779 IdentName {
780 span: a.key.as_ident().unwrap().span,
781 sym: a.key.as_ident().unwrap().sym.clone(),
782 },
783 f.function.body.as_ref(),
784 f.function
785 .params
786 .iter()
787 .map(|a| a.pat.clone())
788 .collect(),
789 ),
790 Expr::Arrow(f) => (
791 IdentName {
792 span: a.key.as_ident().unwrap().span,
793 sym: a.key.as_ident().unwrap().sym.clone(),
794 },
795 f.body.as_block_stmt(),
796 f.params.clone(),
797 ),
798 _ => return None,
799 },
800 _ => return None,
801 };
802 Some((id, body, args))
803 })
804 .map(|(id, body, arg)| {
805 Ok((
806 Ident::new(
807 id.sym,
808 id.span,
809 Default::default(),
810 )
811 .to_id(),
812 (
813 body.iter()
814 .flat_map(|c| c.stmts.iter())
815 .map(|a| a.conv(imports))
816 .collect::<Result<Vec<_>, _>>()?,
817 arg.iter()
818 .filter_map(|a| a.as_ident())
819 .map(|a| a.id.clone())
820 .collect(),
821 ),
822 ))
823 })
824 .collect::<Result<BTreeMap<_, _>, Error>>()?,
825 },
826 span: c.span,
827 })
828 }
829 _ => {
830 let mut args = c.args.as_slice();
831 let mut template = BTreeMap::new();
832 while let Some(([a], b)) = args.split_first_chunk()
833 {
834 if let Some(a) = a.expr.as_object() {
835 args = b;
836 for k in a
837 .props
838 .iter()
839 .filter_map(|p| p.as_prop())
840 .filter_map(|p| p.as_key_value())
841 {
842 template.insert(
843 match k.key.as_ident() {
844 Some(a) => a.sym.clone(),
845 None => {
846 return Err(
847 Error::Unsupported,
848 )
849 }
850 },
851 (&*k.value).clone(),
852 );
853 }
854 } else {
855 break;
856 }
857 }
858 SimplExpr::Call(MakeSpanned {
859 value: Box::new(SimplCallExpr::Path {
860 path: FuncId {
861 path: path,
862 template_args: template,
863 },
864 args: args
865 .iter()
866 .map(|a| a.expr.conv(imports))
867 .collect::<Result<Vec<_>, _>>()?,
868 }),
869 span: c.span,
870 })
871 }
872 }
873 }
874 Ok((t, args)) => {
875 let mut args = args.as_ref();
876 let mut template = BTreeMap::new();
877 while let Some(([a], b)) = args.split_first_chunk() {
878 if let Some(a) = a.expr.as_object() {
879 args = b;
880 for k in a
881 .props
882 .iter()
883 .filter_map(|p| p.as_prop())
884 .filter_map(|p| p.as_key_value())
885 {
886 template.insert(
887 match k.key.as_ident() {
888 Some(a) => a.sym.clone(),
889 None => return Err(Error::Unsupported),
890 },
891 (&*k.value).clone(),
892 );
893 }
894 } else {
895 break;
896 }
897 }
898 SimplExpr::Call(MakeSpanned {
899 value: Box::new(SimplCallExpr::Tag {
900 tag: FuncId {
901 path: t,
902 template_args: template,
903 },
904 args: args
905 .iter()
906 .map(|a| a.expr.conv(imports))
907 .collect::<Result<Vec<_>, _>>()?,
908 }),
909 span: c.span,
910 })
911 }
912 }
913 }
914 }
915 }
916 }
917 }
918 _ => return Err(Error::Unsupported),
919 })
920 }
921}
922impl Conv for Stmt {
923 type Target<D: Dialect> = SimplStmt<D>;
924
925 fn conv<D: ConvDialect>(&self, imports: &impl ConvCtx<D>) -> Result<Self::Target<D>, Error> {
926 Ok(match self {
927 Stmt::Break(b) => SimplStmt::Break(match b.label.as_ref().cloned() {
928 Some(l) => l,
929 None => return Err(Error::Unsupported),
930 }),
931 Stmt::Continue(b) => SimplStmt::Continue(match b.label.as_ref().cloned() {
932 Some(l) => l,
933 None => return Err(Error::Unsupported),
934 }),
935 Stmt::Expr(e) => SimplStmt::Expr(MakeSpanned {
936 value: Box::new(e.expr.conv(imports)?),
937 span: e.span,
938 }),
939 Stmt::Block(b) => SimplStmt::Block(MakeSpanned {
940 value: b
941 .stmts
942 .iter()
943 .map(|a| a.conv(imports))
944 .collect::<Result<Vec<_>, _>>()?,
945 span: b.span,
946 }),
947 Stmt::If(i) => SimplStmt::If(MakeSpanned {
948 value: SimplIf {
949 kind: SimplIfKind::If {
950 r#else: i
951 .alt
952 .iter()
953 .map(|a| a.conv(imports))
954 .collect::<Result<Vec<_>, _>>()?,
955 },
956 cond: Box::new(i.test.conv(imports)?),
957 body: vec![i.cons.conv(imports)?],
958 },
959 span: i.span,
960 }),
961 Stmt::While(w) => SimplStmt::If(MakeSpanned {
962 value: SimplIf {
963 kind: SimplIfKind::While {
964 label: Ident::new_private(Atom::new("$"), w.span),
965 },
966 cond: Box::new(w.test.conv(imports)?),
967 body: vec![w.body.conv(imports)?],
968 },
969 span: w.span,
970 }),
971 Stmt::Labeled(l) => {
972 let mut w = l.body.conv(imports)?;
973 w.apply_label(&l.label);
974 w
975 }
976 Stmt::Return(r) => SimplStmt::Return(MakeSpanned {
977 value: Box::new(match r.arg.as_ref() {
978 None => return Err(Error::Unsupported),
979 Some(a) => a.conv(imports)?,
980 }),
981 span: r.span,
982 }),
983 Stmt::Switch(s) => SimplStmt::Switch(MakeSpanned {
984 value: SimplSwitchStmt {
985 scrutinee: Box::new(s.discriminant.conv(imports)?),
986 label: Ident::new_private(Atom::new("$"), s.span),
987 cases: s
988 .cases
989 .iter()
990 .map(|c| {
991 let Some(a) = c.test.as_ref() else {
992 return Err(Error::Unsupported);
993 };
994 let a = a.conv(imports)?;
995 let (b, d) = match c.cons.last() {
996 Some(Stmt::Break(BreakStmt { span, label: None })) => (
997 c.cons[..(c.cons.len() - 1)]
998 .iter()
999 .map(|a| a.conv(imports))
1000 .collect::<Result<Vec<_>, Error>>()?,
1001 BreakKind::BreakAfter,
1002 ),
1003 _ => (
1004 c.cons
1005 .iter()
1006 .map(|a| a.conv(imports))
1007 .collect::<Result<Vec<_>, Error>>()?,
1008 BreakKind::DoNotBreakAfter,
1009 ),
1010 };
1011 Ok((Box::new(a), b, d))
1012 })
1013 .collect::<Result<_, Error>>()?,
1014 },
1015 span: s.span(),
1016 }),
1017 _ => return Err(Error::Unsupported),
1018 })
1019 }
1020}