1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/// Shortcut for `quote_ident!(span.apply_mark(Mark::fresh(Mark::root())), s)`
#[macro_export]
macro_rules! private_ident {
    ($s:expr) => {
        private_ident!($crate::swc_common::DUMMY_SP, $s)
    };
    ($span:expr, $s:expr) => {{
        let mark = $crate::swc_common::Mark::new();
        let ctxt = $crate::swc_common::SyntaxContext::empty().apply_mark(mark);
        $crate::swc_ecma_ast::Ident::new($s.into(), $span, ctxt)
    }};
}

/// As we have multiple identifier types, the expected usage is
/// `quote_ident!("foo").into()`.
#[macro_export]
macro_rules! quote_ident {
    ($s:expr) => {{
        let sym: $crate::swc_atoms::Atom = $s.into();
        let id: $crate::swc_ecma_ast::IdentName = sym.into();

        id
    }};
    ($ctxt:expr, $s:expr) => {{
        let sym: $crate::swc_atoms::Atom = $s.into();
        let id: $crate::swc_ecma_ast::Ident =
            $crate::swc_ecma_ast::Ident::new(sym, $crate::swc_common::DUMMY_SP, $ctxt);

        id
    }};

    ($ctxt:expr, $span:expr, $s:expr) => {{
        $crate::swc_ecma_ast::Ident::new($s.into(), $span, $ctxt)
    }};
}

#[macro_export]
macro_rules! quote_str {
    ($s:expr) => {
        quote_str!($crate::swc_common::DUMMY_SP, $s)
    };
    ($span:expr, $s:expr) => {{
        $crate::swc_ecma_ast::Str {
            span: $span,
            raw: None,
            value: $s.into(),
        }
    }};
}

#[macro_export]
macro_rules! quote_expr {
    ($span:expr, null) => {{
        use $crate::swc_ecma_ast::*;
        Expr::Lit(Lit::Null(Null { span: $span }))
    }};

    ($span:expr, undefined) => {{
        box Expr::Ident(Ident::new("undefined", $span))
    }};
}

/// Creates a member expression.
///
/// # Usage
/// ```rust,ignore
/// member_expr!(span, Function.bind.apply);
/// ```
///
/// Returns Box<[Expr](swc_ecma_ast::Expr)>.
#[macro_export]
macro_rules! member_expr {
    ($ctxt:expr, $span:expr, $first:ident) => {{
        use $crate::swc_ecma_ast::Expr;
        Box::new(Expr::Ident($crate::quote_ident!($ctxt, $span, stringify!($first))))
    }};

    ($ctxt:expr, $span:expr, $first:ident . $($rest:tt)+) => {{
        let obj = member_expr!($ctxt, $span, $first);

        member_expr!(@EXT, $span, obj, $($rest)* )
    }};

    (@EXT, $span:expr, $obj:expr, $first:ident . $($rest:tt)* ) => {{
        use $crate::swc_ecma_ast::MemberProp;
        use $crate::swc_ecma_ast::IdentName;
        let prop = MemberProp::Ident(IdentName::new(stringify!($first).into(), $span));

        member_expr!(@EXT, $span, Box::new(Expr::Member(MemberExpr{
            span: $crate::swc_common::DUMMY_SP,
            obj: $obj,
            prop,
        })), $($rest)*)
    }};

    (@EXT, $span:expr, $obj:expr,  $first:ident) => {{
        use $crate::swc_ecma_ast::*;
        let prop = MemberProp::Ident(IdentName::new(stringify!($first).into(), $span));

        MemberExpr{
            span: $crate::swc_common::DUMMY_SP,
            obj: $obj,
            prop,
        }
    }};
}

#[cfg(test)]
mod tests {
    use swc_common::DUMMY_SP as span;
    use swc_ecma_ast::*;

    use crate::drop_span;

    #[test]
    fn quote_member_expr() {
        let expr: Box<Expr> = drop_span(member_expr!(
            Default::default(),
            Default::default(),
            Function.prototype.bind
        ))
        .into();

        assert_eq!(
            expr,
            Box::new(Expr::Member(MemberExpr {
                span,
                obj: Box::new(Expr::Member(MemberExpr {
                    span,
                    obj: member_expr!(Default::default(), Default::default(), Function),
                    prop: MemberProp::Ident("prototype".into()),
                })),
                prop: MemberProp::Ident("bind".into()),
            }))
        );
    }
}