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}