boa_ast/expression/
optional.rs1use super::{Expression, access::PropertyAccessField};
2use crate::{
3 Span, Spanned,
4 function::PrivateName,
5 join_nodes,
6 visitor::{VisitWith, Visitor, VisitorMut},
7};
8use boa_interner::{Interner, ToInternedString};
9use core::{fmt::Write as _, ops::ControlFlow};
10
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
14#[derive(Clone, Debug, PartialEq)]
15pub enum OptionalOperationKind {
16 SimplePropertyAccess {
18 field: PropertyAccessField,
20 },
21 PrivatePropertyAccess {
23 field: PrivateName,
25 },
26 Call {
28 args: Box<[Expression]>,
30 },
31}
32
33impl VisitWith for OptionalOperationKind {
34 fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
35 where
36 V: Visitor<'a>,
37 {
38 match self {
39 Self::SimplePropertyAccess { field } => visitor.visit_property_access_field(field),
40 Self::PrivatePropertyAccess { field } => visitor.visit_private_name(field),
41 Self::Call { args } => {
42 for arg in args {
43 visitor.visit_expression(arg)?;
44 }
45 ControlFlow::Continue(())
46 }
47 }
48 }
49
50 fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
51 where
52 V: VisitorMut<'a>,
53 {
54 match self {
55 Self::SimplePropertyAccess { field } => visitor.visit_property_access_field_mut(field),
56 Self::PrivatePropertyAccess { field } => visitor.visit_private_name_mut(field),
57 Self::Call { args } => {
58 for arg in args.iter_mut() {
59 visitor.visit_expression_mut(arg)?;
60 }
61 ControlFlow::Continue(())
62 }
63 }
64 }
65}
66
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
74#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
75#[derive(Clone, Debug, PartialEq)]
76pub struct OptionalOperation {
77 kind: OptionalOperationKind,
78 shorted: bool,
79 span: Span,
80}
81
82impl OptionalOperation {
83 #[inline]
85 #[must_use]
86 pub const fn new(kind: OptionalOperationKind, shorted: bool, span: Span) -> Self {
87 Self {
88 kind,
89 shorted,
90 span,
91 }
92 }
93 #[inline]
95 #[must_use]
96 pub const fn kind(&self) -> &OptionalOperationKind {
97 &self.kind
98 }
99
100 #[inline]
103 #[must_use]
104 pub const fn shorted(&self) -> bool {
105 self.shorted
106 }
107}
108
109impl Spanned for OptionalOperation {
110 #[inline]
111 fn span(&self) -> Span {
112 self.span
113 }
114}
115
116impl ToInternedString for OptionalOperation {
117 fn to_interned_string(&self, interner: &Interner) -> String {
118 let mut buf = if self.shorted {
119 String::from("?.")
120 } else {
121 if let OptionalOperationKind::SimplePropertyAccess {
122 field: PropertyAccessField::Const(name),
123 } = &self.kind
124 {
125 return format!(".{}", interner.resolve_expect(name.sym()));
126 }
127
128 if let OptionalOperationKind::PrivatePropertyAccess { field } = &self.kind {
129 return format!(".#{}", interner.resolve_expect(field.description()));
130 }
131
132 String::new()
133 };
134 match &self.kind {
135 OptionalOperationKind::SimplePropertyAccess { field } => match field {
136 PropertyAccessField::Const(name) => {
137 buf.push_str(&interner.resolve_expect(name.sym()).to_string());
138 }
139 PropertyAccessField::Expr(expr) => {
140 let _ = write!(buf, "[{}]", expr.to_interned_string(interner));
141 }
142 },
143 OptionalOperationKind::PrivatePropertyAccess { field } => {
144 let _ = write!(buf, "#{}", interner.resolve_expect(field.description()));
145 }
146 OptionalOperationKind::Call { args } => {
147 let _ = write!(buf, "({})", join_nodes(interner, args));
148 }
149 }
150 buf
151 }
152}
153
154impl VisitWith for OptionalOperation {
155 fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
156 where
157 V: Visitor<'a>,
158 {
159 visitor.visit_optional_operation_kind(&self.kind)
160 }
161
162 fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
163 where
164 V: VisitorMut<'a>,
165 {
166 visitor.visit_optional_operation_kind_mut(&mut self.kind)
167 }
168}
169
170#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
193#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
194#[derive(Clone, Debug, PartialEq)]
195pub struct Optional {
196 target: Box<Expression>,
197 chain: Box<[OptionalOperation]>,
198 span: Span,
199}
200
201impl Optional {
202 #[inline]
204 #[must_use]
205 pub fn new(target: Expression, chain: Box<[OptionalOperation]>, span: Span) -> Self {
206 Self {
207 target: Box::new(target),
208 chain,
209 span,
210 }
211 }
212
213 #[inline]
215 #[must_use]
216 pub fn target(&self) -> &Expression {
217 self.target.as_ref()
218 }
219
220 #[inline]
222 #[must_use]
223 pub fn chain(&self) -> &[OptionalOperation] {
224 self.chain.as_ref()
225 }
226}
227
228impl Spanned for Optional {
229 #[inline]
230 fn span(&self) -> Span {
231 self.span
232 }
233}
234
235impl From<Optional> for Expression {
236 fn from(opt: Optional) -> Self {
237 Self::Optional(opt)
238 }
239}
240
241impl ToInternedString for Optional {
242 fn to_interned_string(&self, interner: &Interner) -> String {
243 let mut buf = self.target.to_interned_string(interner);
244
245 for item in &*self.chain {
246 buf.push_str(&item.to_interned_string(interner));
247 }
248
249 buf
250 }
251}
252
253impl VisitWith for Optional {
254 fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
255 where
256 V: Visitor<'a>,
257 {
258 visitor.visit_expression(&self.target)?;
259 for op in &*self.chain {
260 visitor.visit_optional_operation(op)?;
261 }
262 ControlFlow::Continue(())
263 }
264
265 fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
266 where
267 V: VisitorMut<'a>,
268 {
269 visitor.visit_expression_mut(&mut self.target)?;
270 for op in &mut *self.chain {
271 visitor.visit_optional_operation_mut(op)?;
272 }
273 ControlFlow::Continue(())
274 }
275}