swc_ecma_ast/
lit.rs

1use std::{
2    borrow::Cow,
3    fmt::{self, Display, Formatter},
4    hash::{Hash, Hasher},
5};
6
7use is_macro::Is;
8use num_bigint::BigInt as BigIntValue;
9use swc_atoms::{atom, Atom};
10use swc_common::{ast_node, util::take::Take, EqIgnoreSpan, Span, DUMMY_SP};
11
12use crate::jsx::JSXText;
13
14#[ast_node]
15#[derive(Eq, Hash, EqIgnoreSpan, Is)]
16#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
17#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
18pub enum Lit {
19    #[tag("StringLiteral")]
20    Str(Str),
21
22    #[tag("BooleanLiteral")]
23    Bool(Bool),
24
25    #[tag("NullLiteral")]
26    Null(Null),
27
28    #[tag("NumericLiteral")]
29    Num(Number),
30
31    #[tag("BigIntLiteral")]
32    BigInt(BigInt),
33
34    #[tag("RegExpLiteral")]
35    Regex(Regex),
36
37    #[tag("JSXText")]
38    JSXText(JSXText),
39}
40
41macro_rules! bridge_lit_from {
42    ($bridge: ty, $src:ty) => {
43        bridge_expr_from!(crate::Lit, $src);
44        bridge_from!(Lit, $bridge, $src);
45    };
46}
47
48bridge_expr_from!(Lit, Str);
49bridge_expr_from!(Lit, Bool);
50bridge_expr_from!(Lit, Number);
51bridge_expr_from!(Lit, BigInt);
52bridge_expr_from!(Lit, Regex);
53bridge_expr_from!(Lit, Null);
54bridge_expr_from!(Lit, JSXText);
55
56bridge_lit_from!(Str, &'_ str);
57bridge_lit_from!(Str, Atom);
58bridge_lit_from!(Str, Cow<'_, str>);
59bridge_lit_from!(Str, String);
60bridge_lit_from!(Bool, bool);
61bridge_lit_from!(Number, f64);
62bridge_lit_from!(Number, usize);
63bridge_lit_from!(BigInt, BigIntValue);
64
65impl Lit {
66    pub fn set_span(&mut self, span: Span) {
67        match self {
68            Lit::Str(s) => s.span = span,
69            Lit::Bool(b) => b.span = span,
70            Lit::Null(n) => n.span = span,
71            Lit::Num(n) => n.span = span,
72            Lit::BigInt(n) => n.span = span,
73            Lit::Regex(n) => n.span = span,
74            Lit::JSXText(n) => n.span = span,
75        }
76    }
77}
78
79#[ast_node("BigIntLiteral")]
80#[derive(Eq, Hash)]
81pub struct BigInt {
82    pub span: Span,
83    #[cfg_attr(any(feature = "rkyv-impl"), rkyv(with = EncodeBigInt))]
84    pub value: Box<BigIntValue>,
85
86    /// Use `None` value only for transformations to avoid recalculate
87    /// characters in big integer
88    pub raw: Option<Atom>,
89}
90
91#[cfg(feature = "shrink-to-fit")]
92impl shrink_to_fit::ShrinkToFit for BigInt {
93    #[inline(always)]
94    fn shrink_to_fit(&mut self) {}
95}
96
97impl EqIgnoreSpan for BigInt {
98    fn eq_ignore_span(&self, other: &Self) -> bool {
99        self.value == other.value
100    }
101}
102
103#[cfg(feature = "rkyv-impl")]
104#[derive(Debug, Clone, Copy)]
105#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
106#[cfg_attr(feature = "rkyv-impl", repr(C))]
107pub struct EncodeBigInt;
108
109#[cfg(feature = "rkyv-impl")]
110impl rkyv::with::ArchiveWith<Box<BigIntValue>> for EncodeBigInt {
111    type Archived = rkyv::Archived<String>;
112    type Resolver = rkyv::Resolver<String>;
113
114    fn resolve_with(
115        field: &Box<BigIntValue>,
116        resolver: Self::Resolver,
117        out: rkyv::Place<Self::Archived>,
118    ) {
119        use rkyv::Archive;
120
121        let s = field.to_string();
122        s.resolve(resolver, out);
123    }
124}
125
126#[cfg(feature = "rkyv-impl")]
127impl<S> rkyv::with::SerializeWith<Box<BigIntValue>, S> for EncodeBigInt
128where
129    S: ?Sized + rancor::Fallible + rkyv::ser::Writer,
130    S::Error: rancor::Source,
131{
132    fn serialize_with(
133        field: &Box<BigIntValue>,
134        serializer: &mut S,
135    ) -> Result<Self::Resolver, S::Error> {
136        let field = field.to_string();
137        rkyv::string::ArchivedString::serialize_from_str(&field, serializer)
138    }
139}
140
141#[cfg(feature = "rkyv-impl")]
142impl<D> rkyv::with::DeserializeWith<rkyv::Archived<String>, Box<BigIntValue>, D> for EncodeBigInt
143where
144    D: ?Sized + rancor::Fallible,
145{
146    fn deserialize_with(
147        field: &rkyv::Archived<String>,
148        deserializer: &mut D,
149    ) -> Result<Box<BigIntValue>, D::Error> {
150        use rkyv::Deserialize;
151
152        let s: String = field.deserialize(deserializer)?;
153
154        Ok(Box::new(s.parse().unwrap()))
155    }
156}
157
158#[cfg(feature = "arbitrary")]
159#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
160impl<'a> arbitrary::Arbitrary<'a> for BigInt {
161    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
162        let span = u.arbitrary()?;
163        let value = Box::new(u.arbitrary::<usize>()?.into());
164        let raw = Some(u.arbitrary::<String>()?.into());
165
166        Ok(Self { span, value, raw })
167    }
168}
169
170impl From<BigIntValue> for BigInt {
171    #[inline]
172    fn from(value: BigIntValue) -> Self {
173        BigInt {
174            span: DUMMY_SP,
175            value: Box::new(value),
176            raw: None,
177        }
178    }
179}
180
181/// A string literal.
182#[ast_node("StringLiteral")]
183#[derive(Eq, Hash)]
184#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
185pub struct Str {
186    pub span: Span,
187
188    pub value: Atom,
189
190    /// Use `None` value only for transformations to avoid recalculate escaped
191    /// characters in strings
192    pub raw: Option<Atom>,
193}
194
195impl Take for Str {
196    fn dummy() -> Self {
197        Str {
198            span: DUMMY_SP,
199            value: atom!(""),
200            raw: None,
201        }
202    }
203}
204
205#[cfg(feature = "arbitrary")]
206#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
207impl<'a> arbitrary::Arbitrary<'a> for Str {
208    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
209        let span = u.arbitrary()?;
210        let value = u.arbitrary::<String>()?.into();
211        let raw = Some(u.arbitrary::<String>()?.into());
212
213        Ok(Self { span, value, raw })
214    }
215}
216
217impl Str {
218    #[inline]
219    pub fn is_empty(&self) -> bool {
220        self.value.is_empty()
221    }
222
223    pub fn from_tpl_raw(tpl_raw: &str) -> Atom {
224        let mut buf = String::with_capacity(tpl_raw.len());
225
226        let mut iter = tpl_raw.chars();
227
228        while let Some(c) = iter.next() {
229            match c {
230                '\\' => {
231                    if let Some(next) = iter.next() {
232                        match next {
233                            '`' | '$' | '\\' => {
234                                buf.push(next);
235                            }
236                            'b' => {
237                                buf.push('\u{0008}');
238                            }
239                            'f' => {
240                                buf.push('\u{000C}');
241                            }
242                            'n' => {
243                                buf.push('\n');
244                            }
245                            'r' => {
246                                buf.push('\r');
247                            }
248                            't' => {
249                                buf.push('\t');
250                            }
251                            'v' => {
252                                buf.push('\u{000B}');
253                            }
254                            _ => {
255                                buf.push('\\');
256                                buf.push(next);
257                            }
258                        }
259                    }
260                }
261
262                c => {
263                    buf.push(c);
264                }
265            }
266        }
267
268        buf.into()
269    }
270}
271
272impl EqIgnoreSpan for Str {
273    fn eq_ignore_span(&self, other: &Self) -> bool {
274        self.value == other.value
275    }
276}
277
278impl From<Atom> for Str {
279    #[inline]
280    fn from(value: Atom) -> Self {
281        Str {
282            span: DUMMY_SP,
283            value,
284            raw: None,
285        }
286    }
287}
288
289bridge_from!(Str, Atom, &'_ str);
290bridge_from!(Str, Atom, String);
291bridge_from!(Str, Atom, Cow<'_, str>);
292
293/// A boolean literal.
294///
295///
296/// # Creation
297///
298/// If you are creating a boolean literal with a dummy span, please use
299/// `true.into()` or `false.into()`, instead of creating this struct directly.
300///
301/// All of `Box<Expr>`, `Expr`, `Lit`, `Bool` implements `From<bool>`.
302#[ast_node("BooleanLiteral")]
303#[derive(Copy, Eq, Hash, EqIgnoreSpan)]
304#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
305#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
306pub struct Bool {
307    pub span: Span,
308    pub value: bool,
309}
310
311impl Take for Bool {
312    fn dummy() -> Self {
313        Bool {
314            span: DUMMY_SP,
315            value: false,
316        }
317    }
318}
319
320impl From<bool> for Bool {
321    #[inline]
322    fn from(value: bool) -> Self {
323        Bool {
324            span: DUMMY_SP,
325            value,
326        }
327    }
328}
329
330#[ast_node("NullLiteral")]
331#[derive(Copy, Eq, Hash, EqIgnoreSpan)]
332#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
333#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
334pub struct Null {
335    pub span: Span,
336}
337
338impl Take for Null {
339    fn dummy() -> Self {
340        Null { span: DUMMY_SP }
341    }
342}
343
344#[ast_node("RegExpLiteral")]
345#[derive(Eq, Hash, EqIgnoreSpan)]
346#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
347pub struct Regex {
348    pub span: Span,
349
350    #[cfg_attr(feature = "serde-impl", serde(rename = "pattern"))]
351    pub exp: Atom,
352
353    #[cfg_attr(feature = "serde-impl", serde(default))]
354    pub flags: Atom,
355}
356
357impl Take for Regex {
358    fn dummy() -> Self {
359        Self {
360            span: DUMMY_SP,
361            exp: Default::default(),
362            flags: Default::default(),
363        }
364    }
365}
366
367#[cfg(feature = "arbitrary")]
368#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
369impl<'a> arbitrary::Arbitrary<'a> for Regex {
370    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
371        let span = u.arbitrary()?;
372        let exp = u.arbitrary::<String>()?.into();
373        let flags = atom!(""); // TODO
374
375        Ok(Self { span, exp, flags })
376    }
377}
378
379/// A numeric literal.
380///
381///
382/// # Creation
383///
384/// If you are creating a numeric literal with a dummy span, please use
385/// `literal.into()`, instead of creating this struct directly.
386///
387/// All of `Box<Expr>`, `Expr`, `Lit`, `Number` implements `From<64>` and
388/// `From<usize>`.
389
390#[ast_node("NumericLiteral")]
391#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
392pub struct Number {
393    pub span: Span,
394    /// **Note**: This should not be `NaN`. Use [crate::Ident] to represent NaN.
395    ///
396    /// If you store `NaN` in this field, a hash map will behave strangely.
397    pub value: f64,
398
399    /// Use `None` value only for transformations to avoid recalculate
400    /// characters in number literal
401    pub raw: Option<Atom>,
402}
403
404impl Eq for Number {}
405
406impl EqIgnoreSpan for Number {
407    fn eq_ignore_span(&self, other: &Self) -> bool {
408        self.value == other.value && self.value.is_sign_positive() == other.value.is_sign_positive()
409    }
410}
411
412#[allow(clippy::derived_hash_with_manual_eq)]
413#[allow(clippy::transmute_float_to_int)]
414impl Hash for Number {
415    fn hash<H: Hasher>(&self, state: &mut H) {
416        fn integer_decode(val: f64) -> (u64, i16, i8) {
417            let bits: u64 = val.to_bits();
418            let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 };
419            let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16;
420            let mantissa = if exponent == 0 {
421                (bits & 0xfffffffffffff) << 1
422            } else {
423                (bits & 0xfffffffffffff) | 0x10000000000000
424            };
425
426            exponent -= 1023 + 52;
427            (mantissa, exponent, sign)
428        }
429
430        self.span.hash(state);
431        integer_decode(self.value).hash(state);
432        self.raw.hash(state);
433    }
434}
435
436impl Display for Number {
437    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
438        if self.value.is_infinite() {
439            if self.value.is_sign_positive() {
440                Display::fmt("Infinity", f)
441            } else {
442                Display::fmt("-Infinity", f)
443            }
444        } else {
445            Display::fmt(&self.value, f)
446        }
447    }
448}
449
450#[cfg(feature = "arbitrary")]
451#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
452impl<'a> arbitrary::Arbitrary<'a> for Number {
453    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
454        let span = u.arbitrary()?;
455        let value = u.arbitrary::<f64>()?;
456        let raw = Some(u.arbitrary::<String>()?.into());
457
458        Ok(Self { span, value, raw })
459    }
460}
461
462impl From<f64> for Number {
463    #[inline]
464    fn from(value: f64) -> Self {
465        Number {
466            span: DUMMY_SP,
467            value,
468            raw: None,
469        }
470    }
471}
472
473impl From<usize> for Number {
474    #[inline]
475    fn from(value: usize) -> Self {
476        Number {
477            span: DUMMY_SP,
478            value: value as _,
479            raw: None,
480        }
481    }
482}