Skip to main content

boa_ast/expression/literal/
template.rs

1//! Template literal Expression.
2
3use crate::{
4    Span, Spanned,
5    expression::Expression,
6    visitor::{VisitWith, Visitor, VisitorMut},
7};
8use boa_interner::{Interner, Sym, ToInternedString};
9use core::{fmt::Write as _, ops::ControlFlow};
10
11/// Template literals are string literals allowing embedded expressions.
12///
13/// More information:
14///  - [ECMAScript reference][spec]
15///  - [MDN documentation][mdn]
16///
17/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
18/// [spec]: https://tc39.es/ecma262/#sec-template-literals
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[derive(Clone, Debug, PartialEq)]
21pub struct TemplateLiteral {
22    elements: Box<[TemplateElement]>,
23    span: Span,
24}
25
26/// Manual implementation, because string and expression in the element list must always appear in order.
27#[cfg(feature = "arbitrary")]
28impl<'a> arbitrary::Arbitrary<'a> for TemplateLiteral {
29    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
30        let len = u.arbitrary_len::<Box<[TemplateElement]>>()?;
31
32        let mut elements = Vec::with_capacity(len);
33        for i in 0..len {
34            if i & 1 == 0 {
35                elements.push(TemplateElement::String(
36                    <Sym as arbitrary::Arbitrary>::arbitrary(u)?,
37                ));
38            } else {
39                elements.push(TemplateElement::Expr(Expression::arbitrary(u)?));
40            }
41        }
42
43        Ok(Self::new(elements.into_boxed_slice(), Span::arbitrary(u)?))
44    }
45}
46
47impl From<TemplateLiteral> for Expression {
48    #[inline]
49    fn from(tem: TemplateLiteral) -> Self {
50        Self::TemplateLiteral(tem)
51    }
52}
53
54impl TemplateLiteral {
55    /// Creates a new `TemplateLiteral` from a list of [`TemplateElement`]s.
56    #[inline]
57    #[must_use]
58    pub fn new(elements: Box<[TemplateElement]>, span: Span) -> Self {
59        Self { elements, span }
60    }
61
62    /// Gets the element list of this `TemplateLiteral`.
63    #[must_use]
64    pub const fn elements(&self) -> &[TemplateElement] {
65        &self.elements
66    }
67}
68
69impl Spanned for TemplateLiteral {
70    #[inline]
71    fn span(&self) -> Span {
72        self.span
73    }
74}
75
76impl ToInternedString for TemplateLiteral {
77    #[inline]
78    fn to_interned_string(&self, interner: &Interner) -> String {
79        let mut buf = "`".to_owned();
80
81        for elt in &self.elements {
82            match elt {
83                TemplateElement::String(s) => {
84                    let _ = write!(buf, "{}", interner.resolve_expect(*s));
85                }
86                TemplateElement::Expr(n) => {
87                    let _ = write!(buf, "${{{}}}", n.to_interned_string(interner));
88                }
89            }
90        }
91        buf.push('`');
92
93        buf
94    }
95}
96
97impl VisitWith for TemplateLiteral {
98    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
99    where
100        V: Visitor<'a>,
101    {
102        for element in &*self.elements {
103            visitor.visit_template_element(element)?;
104        }
105        ControlFlow::Continue(())
106    }
107
108    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
109    where
110        V: VisitorMut<'a>,
111    {
112        for element in &mut *self.elements {
113            visitor.visit_template_element_mut(element)?;
114        }
115        ControlFlow::Continue(())
116    }
117}
118
119/// An element found within a [`TemplateLiteral`].
120///
121/// The [spec] doesn't define an element akin to `TemplateElement`. However, the AST defines this
122/// node as the equivalent of the components found in a template literal.
123///
124/// [spec]: https://tc39.es/ecma262/#sec-template-literals
125#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
126#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
127#[derive(Clone, Debug, PartialEq)]
128pub enum TemplateElement {
129    /// A simple string.
130    String(Sym),
131    /// An expression that is evaluated and replaced by its string representation.
132    Expr(Expression),
133}
134
135impl VisitWith for TemplateElement {
136    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
137    where
138        V: Visitor<'a>,
139    {
140        match self {
141            Self::String(sym) => visitor.visit_sym(sym),
142            Self::Expr(expr) => visitor.visit_expression(expr),
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        match self {
151            Self::String(sym) => visitor.visit_sym_mut(sym),
152            Self::Expr(expr) => visitor.visit_expression_mut(expr),
153        }
154    }
155}