Skip to main content

boa_ast/expression/
tagged_template.rs

1use super::Expression;
2use crate::{
3    Span, Spanned,
4    visitor::{VisitWith, Visitor, VisitorMut},
5};
6use boa_interner::{Interner, Sym, ToInternedString};
7use core::{fmt::Write as _, ops::ControlFlow};
8
9/// A [`TaggedTemplate`][moz] expression, as defined by the [spec].
10///
11/// `TaggedTemplate`s are a type of template literals that are parsed by a custom function to generate
12/// arbitrary objects from the inner strings and expressions.
13///
14/// [moz]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates
15/// [spec]: https://tc39.es/ecma262/#sec-tagged-templates
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
18#[derive(Clone, Debug, PartialEq)]
19pub struct TaggedTemplate {
20    tag: Box<Expression>,
21    raws: Box<[Sym]>,
22    cookeds: Box<[Option<Sym>]>,
23    exprs: Box<[Expression]>,
24    identifier: u64,
25    span: Span,
26}
27
28impl TaggedTemplate {
29    /// Creates a new tagged template with a tag, the list of raw strings, the cooked strings and
30    /// the expressions.
31    #[inline]
32    #[must_use]
33    pub fn new(
34        tag: Expression,
35        raws: Box<[Sym]>,
36        cookeds: Box<[Option<Sym>]>,
37        exprs: Box<[Expression]>,
38        identifier: u64,
39        span: Span,
40    ) -> Self {
41        Self {
42            tag: tag.into(),
43            raws,
44            cookeds,
45            exprs,
46            identifier,
47            span,
48        }
49    }
50
51    /// Gets the tag function of the template.
52    #[inline]
53    #[must_use]
54    pub const fn tag(&self) -> &Expression {
55        &self.tag
56    }
57
58    /// Gets the inner raw strings of the template.
59    #[inline]
60    #[must_use]
61    pub const fn raws(&self) -> &[Sym] {
62        &self.raws
63    }
64
65    /// Gets the cooked strings of the template.
66    #[inline]
67    #[must_use]
68    pub const fn cookeds(&self) -> &[Option<Sym>] {
69        &self.cookeds
70    }
71
72    /// Gets the interpolated expressions of the template.
73    #[inline]
74    #[must_use]
75    pub const fn exprs(&self) -> &[Expression] {
76        &self.exprs
77    }
78
79    /// Gets the unique identifier of the template.
80    #[inline]
81    #[must_use]
82    pub const fn identifier(&self) -> u64 {
83        self.identifier
84    }
85}
86
87impl Spanned for TaggedTemplate {
88    #[inline]
89    fn span(&self) -> Span {
90        self.span
91    }
92}
93
94impl ToInternedString for TaggedTemplate {
95    #[inline]
96    fn to_interned_string(&self, interner: &Interner) -> String {
97        let mut buf = format!("{}`", self.tag.to_interned_string(interner));
98        let mut exprs = self.exprs.iter();
99
100        for raw in &self.raws {
101            let _ = write!(buf, "{}", interner.resolve_expect(*raw));
102            if let Some(expr) = exprs.next() {
103                let _ = write!(buf, "${{{}}}", expr.to_interned_string(interner));
104            }
105        }
106        buf.push('`');
107
108        buf
109    }
110}
111
112impl From<TaggedTemplate> for Expression {
113    #[inline]
114    fn from(template: TaggedTemplate) -> Self {
115        Self::TaggedTemplate(template)
116    }
117}
118
119impl VisitWith for TaggedTemplate {
120    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
121    where
122        V: Visitor<'a>,
123    {
124        visitor.visit_expression(&self.tag)?;
125        for raw in &*self.raws {
126            visitor.visit_sym(raw)?;
127        }
128        for cooked in self.cookeds.iter().flatten() {
129            visitor.visit_sym(cooked)?;
130        }
131        for expr in &*self.exprs {
132            visitor.visit_expression(expr)?;
133        }
134        ControlFlow::Continue(())
135    }
136
137    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
138    where
139        V: VisitorMut<'a>,
140    {
141        visitor.visit_expression_mut(&mut self.tag)?;
142        for raw in &mut *self.raws {
143            visitor.visit_sym_mut(raw)?;
144        }
145        for cooked in self.cookeds.iter_mut().flatten() {
146            visitor.visit_sym_mut(cooked)?;
147        }
148        for expr in &mut *self.exprs {
149            visitor.visit_expression_mut(expr)?;
150        }
151        ControlFlow::Continue(())
152    }
153}