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 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#[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 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#[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!(""); Ok(Self { span, exp, flags })
376 }
377}
378
379#[ast_node("NumericLiteral")]
391#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
392pub struct Number {
393 pub span: Span,
394 pub value: f64,
398
399 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}