Skip to main content

boa_ast/expression/literal/
mod.rs

1//! This module contains all literal expressions, which represents the primitive values in ECMAScript.
2//!
3//! More information:
4//!  - [ECMAScript reference][spec]
5//!  - [MDN documentation][mdn]
6//!
7//! [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals
8//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
9
10mod array;
11mod object;
12mod template;
13
14pub use array::ArrayLiteral;
15use core::ops::ControlFlow;
16pub use object::{ObjectLiteral, ObjectMethodDefinition, PropertyDefinition};
17pub use template::{TemplateElement, TemplateLiteral};
18
19use crate::{
20    LinearSpan, LinearSpanIgnoreEq, Span, Spanned,
21    visitor::{VisitWith, Visitor, VisitorMut},
22};
23use boa_interner::{Interner, Sym, ToInternedString};
24use num_bigint::BigInt;
25
26use super::Expression;
27
28/// Literals represent values in ECMAScript.
29///
30/// These are fixed values **not variables** that you literally provide in your script.
31///
32/// More information:
33///  - [ECMAScript reference][spec]
34///  - [MDN documentation][mdn]
35///
36/// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals
37/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
40#[derive(Debug, Clone, PartialEq)]
41pub struct Literal {
42    kind: LiteralKind,
43    span: Span,
44    linear_span: LinearSpanIgnoreEq,
45}
46
47impl Literal {
48    /// Create a new [`Literal`].
49    #[inline]
50    #[must_use]
51    pub fn new<T: Into<LiteralKind>>(kind: T, span: Span) -> Self {
52        Self {
53            kind: kind.into(),
54            span,
55            linear_span: LinearSpanIgnoreEq(LinearSpan::default()),
56        }
57    }
58
59    /// Create a new [`Literal`] with a [`LinearSpan`] for source text tracking.
60    #[inline]
61    #[must_use]
62    pub fn with_linear_span<T: Into<LiteralKind>>(
63        kind: T,
64        span: Span,
65        linear_span: LinearSpan,
66    ) -> Self {
67        Self {
68            kind: kind.into(),
69            span,
70            linear_span: LinearSpanIgnoreEq(linear_span),
71        }
72    }
73
74    /// Get reference to the [`LiteralKind`] of [`Literal`].
75    #[inline]
76    #[must_use]
77    pub const fn kind(&self) -> &LiteralKind {
78        &self.kind
79    }
80
81    /// Get mutable reference to the [`LiteralKind`] of [`Literal`].
82    #[inline]
83    #[must_use]
84    pub const fn kind_mut(&mut self) -> &mut LiteralKind {
85        &mut self.kind
86    }
87
88    /// Get the [`LinearSpan`] of this literal in the source text.
89    #[inline]
90    #[must_use]
91    pub const fn linear_span(&self) -> LinearSpan {
92        self.linear_span.0
93    }
94
95    /// Get position of the node.
96    #[inline]
97    #[must_use]
98    pub const fn as_string(&self) -> Option<Sym> {
99        if let LiteralKind::String(sym) = self.kind() {
100            return Some(*sym);
101        }
102        None
103    }
104
105    /// Check if [`Literal`] is a [`LiteralKind::Undefined`].
106    #[inline]
107    #[must_use]
108    pub const fn is_undefined(&self) -> bool {
109        matches!(self.kind(), LiteralKind::Undefined)
110    }
111}
112
113impl Spanned for Literal {
114    #[inline]
115    fn span(&self) -> Span {
116        self.span
117    }
118}
119
120impl From<Literal> for Expression {
121    #[inline]
122    fn from(lit: Literal) -> Self {
123        Self::Literal(lit)
124    }
125}
126
127impl ToInternedString for Literal {
128    #[inline]
129    fn to_interned_string(&self, interner: &Interner) -> String {
130        self.kind().to_interned_string(interner)
131    }
132}
133
134impl VisitWith for Literal {
135    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
136    where
137        V: Visitor<'a>,
138    {
139        if let LiteralKind::String(sym) = &self.kind {
140            visitor.visit_sym(sym)
141        } else {
142            ControlFlow::Continue(())
143        }
144    }
145
146    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
147    where
148        V: VisitorMut<'a>,
149    {
150        if let LiteralKind::String(sym) = &mut self.kind {
151            visitor.visit_sym_mut(sym)
152        } else {
153            ControlFlow::Continue(())
154        }
155    }
156}
157
158/// Literals represent values in ECMAScript.
159///
160/// These are fixed values **not variables** that you literally provide in your script.
161///
162/// More information:
163///  - [ECMAScript reference][spec]
164///  - [MDN documentation][mdn]
165///
166/// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals
167/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
168#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
169#[derive(Clone, Debug, PartialEq)]
170pub enum LiteralKind {
171    /// A string literal is zero or more characters enclosed in double (`"`) or single (`'`) quotation marks.
172    ///
173    /// A string must be delimited by quotation marks of the same type (that is, either both single quotation marks, or both double quotation marks).
174    /// You can call any of the String object's methods on a string literal value.
175    /// ECMAScript automatically converts the string literal to a temporary String object,
176    /// calls the method, then discards the temporary String object.
177    ///
178    /// More information:
179    ///  - [ECMAScript reference][spec]
180    ///  - [MDN documentation][mdn]
181    ///
182    /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-string-value
183    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#String_literals
184    String(Sym),
185
186    /// A floating-point number literal.
187    ///
188    /// The exponent part is an "`e`" or "`E`" followed by an integer, which can be signed (preceded by "`+`" or "`-`").
189    /// A floating-point literal must have at least one digit, and either a decimal point or "`e`" (or "`E`").
190    ///
191    /// More information:
192    ///  - [ECMAScript reference][spec]
193    ///  - [MDN documentation][mdn]
194    ///
195    /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-number-value
196    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Floating-point_literals
197    Num(f64),
198
199    /// Integer types can be expressed in decimal (base 10), hexadecimal (base 16), octal (base 8) and binary (base 2).
200    ///
201    /// More information:
202    ///  - [ECMAScript reference][spec]
203    ///  - [MDN documentation][mdn]
204    ///
205    /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-number-value
206    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals
207    Int(i32),
208
209    /// `BigInt` provides a way to represent whole numbers larger than the largest number ECMAScript
210    /// can reliably represent with the `Number` primitive.
211    ///
212    /// More information:
213    ///  - [ECMAScript reference][spec]
214    ///  - [MDN documentation][mdn]
215    ///
216    /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-bigint-value
217    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals
218    BigInt(Box<BigInt>),
219
220    /// The Boolean type has two literal values: `true` and `false`.
221    ///
222    /// The Boolean object is a wrapper around the primitive Boolean data type.
223    ///
224    /// More information:
225    ///  - [ECMAScript reference][spec]
226    ///  - [MDN documentation][mdn]
227    ///
228    /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-boolean-value
229    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Boolean_literals
230    Bool(bool),
231
232    /// In JavaScript, `null` is marked as one of the primitive values, cause it's behaviour is seemingly primitive.
233    ///
234    /// In computer science, a null value represents a reference that points,
235    /// generally intentionally, to a nonexistent or invalid object or address.
236    /// The meaning of a null reference varies among language implementations.
237    ///
238    /// More information:
239    ///  - [ECMAScript reference][spec]
240    ///  - [MDN documentation][mdn]
241    ///
242    /// [spec]: https://tc39.es/ecma262/#sec-null-value
243    /// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/null
244    Null,
245
246    /// This represents the JavaScript `undefined` value, it does not reference the `undefined` global variable,
247    /// it will directly evaluate to `undefined`.
248    ///
249    /// NOTE: This is used for optimizations.
250    Undefined,
251}
252
253/// Manual implementation, because `Undefined` is never constructed during parsing.
254#[cfg(feature = "arbitrary")]
255impl<'a> arbitrary::Arbitrary<'a> for LiteralKind {
256    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
257        let c = <u8 as arbitrary::Arbitrary<'a>>::arbitrary(u)? % 6;
258        match c {
259            0 => Ok(Self::String(<Sym as arbitrary::Arbitrary>::arbitrary(u)?)),
260            1 => Ok(Self::Num(<f64 as arbitrary::Arbitrary>::arbitrary(u)?)),
261            2 => Ok(Self::Int(<i32 as arbitrary::Arbitrary>::arbitrary(u)?)),
262            3 => Ok(Self::BigInt(Box::new(
263                <BigInt as arbitrary::Arbitrary>::arbitrary(u)?,
264            ))),
265            4 => Ok(Self::Bool(<bool as arbitrary::Arbitrary>::arbitrary(u)?)),
266            5 => Ok(Self::Null),
267            _ => unreachable!(),
268        }
269    }
270}
271
272impl From<Sym> for LiteralKind {
273    #[inline]
274    fn from(string: Sym) -> Self {
275        Self::String(string)
276    }
277}
278
279impl From<f64> for LiteralKind {
280    #[inline]
281    fn from(num: f64) -> Self {
282        Self::Num(num)
283    }
284}
285
286impl From<i32> for LiteralKind {
287    #[inline]
288    fn from(i: i32) -> Self {
289        Self::Int(i)
290    }
291}
292
293impl From<BigInt> for LiteralKind {
294    #[inline]
295    fn from(i: BigInt) -> Self {
296        Self::BigInt(Box::new(i))
297    }
298}
299
300impl From<Box<BigInt>> for LiteralKind {
301    #[inline]
302    fn from(i: Box<BigInt>) -> Self {
303        Self::BigInt(i)
304    }
305}
306
307impl From<bool> for LiteralKind {
308    #[inline]
309    fn from(b: bool) -> Self {
310        Self::Bool(b)
311    }
312}
313
314impl ToInternedString for LiteralKind {
315    #[inline]
316    fn to_interned_string(&self, interner: &Interner) -> String {
317        match *self {
318            Self::String(st) => {
319                format!("\"{}\"", interner.resolve_expect(st))
320            }
321            Self::Num(num) => num.to_string(),
322            Self::Int(num) => num.to_string(),
323            Self::BigInt(ref num) => format!("{num}n"),
324            Self::Bool(v) => v.to_string(),
325            Self::Null => "null".to_owned(),
326            Self::Undefined => "undefined".to_owned(),
327        }
328    }
329}