ezno_parser/expressions/
assignments.rs

1use crate::{
2	ast::{
3		object_literal::{ObjectLiteral, ObjectLiteralMember},
4		FunctionArgument,
5	},
6	derive_ASTNode, ParseErrors, PropertyKey, PropertyReference, SpreadDestructuringField,
7	TSXToken,
8};
9use derive_partial_eq_extras::PartialEqExtras;
10use get_field_by_type::GetFieldByType;
11use iterator_endiate::EndiateIteratorExt;
12use source_map::Span;
13use tokenizer_lib::TokenReader;
14use visitable_derive::Visitable;
15
16use crate::{
17	ASTNode, ArrayDestructuringField, Expression, ObjectDestructuringField, ParseError,
18	ParseResult, WithComment,
19};
20
21use super::MultipleExpression;
22
23#[apply(derive_ASTNode)]
24#[derive(Debug, Clone, PartialEqExtras, Visitable, get_field_by_type::GetFieldByType)]
25#[get_field_by_type_target(Span)]
26#[partial_eq_ignore_types(Span)]
27pub enum VariableOrPropertyAccess {
28	Variable(String, Span),
29	PropertyAccess {
30		parent: Box<Expression>,
31		property: PropertyReference,
32		position: Span,
33	},
34	/// Using `x[y]`
35	Index {
36		indexee: Box<Expression>,
37		indexer: Box<MultipleExpression>,
38		position: Span,
39	},
40}
41
42impl ASTNode for VariableOrPropertyAccess {
43	fn get_position(&self) -> Span {
44		*self.get()
45	}
46
47	fn from_reader(
48		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
49		state: &mut crate::ParsingState,
50		options: &crate::ParseOptions,
51	) -> ParseResult<Self> {
52		Expression::from_reader(reader, state, options)?.try_into()
53	}
54
55	fn to_string_from_buffer<T: source_map::ToString>(
56		&self,
57		buf: &mut T,
58		options: &crate::ToStringOptions,
59		local: crate::LocalToStringInformation,
60	) {
61		match self {
62			VariableOrPropertyAccess::Variable(name, ..) => {
63				buf.push_str(name);
64			}
65			VariableOrPropertyAccess::PropertyAccess { parent, property, .. } => {
66				if let Expression::NumberLiteral(..)
67				| Expression::ObjectLiteral(..)
68				| Expression::ArrowFunction(..) = parent.get_non_parenthesized()
69				{
70					buf.push('(');
71					parent.to_string_from_buffer(buf, options, local);
72					buf.push(')');
73				} else {
74					parent.to_string_from_buffer(buf, options, local);
75				}
76				buf.push('.');
77				if let PropertyReference::Standard { property, is_private } = property {
78					if *is_private {
79						buf.push('#');
80					}
81					buf.push_str(property);
82				} else if !options.expect_markers {
83					panic!("found marker");
84				}
85			}
86			VariableOrPropertyAccess::Index { indexee, indexer, .. } => {
87				indexee.to_string_from_buffer(buf, options, local);
88				buf.push('[');
89				indexer.to_string_from_buffer(buf, options, local);
90				buf.push(']');
91			}
92		}
93	}
94}
95
96impl VariableOrPropertyAccess {
97	pub(crate) fn from_reader_with_precedence(
98		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
99		state: &mut crate::ParsingState,
100		options: &crate::ParseOptions,
101		return_precedence: u8,
102	) -> ParseResult<Self> {
103		Expression::from_reader_with_precedence(reader, state, options, return_precedence, None)?
104			.try_into()
105	}
106}
107
108impl TryFrom<Expression> for VariableOrPropertyAccess {
109	type Error = ParseError;
110
111	fn try_from(expression: Expression) -> Result<Self, Self::Error> {
112		match expression {
113			Expression::VariableReference(name, position) => Ok(Self::Variable(name, position)),
114			Expression::PropertyAccess { parent, position, property, is_optional } => {
115				if is_optional {
116					// Still a proposal :(
117					Err(ParseError::new(crate::ParseErrors::InvalidLHSAssignment, position))
118				} else {
119					Ok(Self::PropertyAccess { parent, position, property })
120				}
121			}
122			Expression::Index { indexer, position, indexee, is_optional: false } => {
123				Ok(Self::Index { indexer, position, indexee })
124			}
125			// Yah weird. Recursion is fine
126			Expression::ParenthesizedExpression(inner, _) => {
127				if let MultipleExpression::Single(expression) = *inner {
128					TryFrom::try_from(expression)
129				} else {
130					Err(ParseError::new(
131						crate::ParseErrors::InvalidLHSAssignment,
132						inner.get_position(),
133					))
134				}
135			}
136			expression => Err(ParseError::new(
137				crate::ParseErrors::InvalidLHSAssignment,
138				expression.get_position(),
139			)),
140		}
141	}
142}
143
144impl From<VariableOrPropertyAccess> for Expression {
145	fn from(this: VariableOrPropertyAccess) -> Self {
146		match this {
147			VariableOrPropertyAccess::Variable(variable, position) => {
148				Expression::VariableReference(variable, position)
149			}
150			VariableOrPropertyAccess::Index { indexee, indexer, position } => {
151				Expression::Index { indexee, indexer, position, is_optional: false }
152			}
153			VariableOrPropertyAccess::PropertyAccess { parent, position, property } => {
154				Expression::PropertyAccess { parent, position, property, is_optional: false }
155			}
156		}
157	}
158}
159
160impl VariableOrPropertyAccess {
161	#[must_use]
162	pub fn get_parent(&self) -> Option<&Expression> {
163		match self {
164			VariableOrPropertyAccess::Variable(..) => None,
165			VariableOrPropertyAccess::PropertyAccess { parent, .. }
166			| VariableOrPropertyAccess::Index { indexee: parent, .. } => Some(parent),
167		}
168	}
169
170	pub fn get_parent_mut(&mut self) -> Option<&mut Expression> {
171		match self {
172			VariableOrPropertyAccess::Variable(..) => None,
173			VariableOrPropertyAccess::PropertyAccess { parent, .. }
174			| VariableOrPropertyAccess::Index { indexee: parent, .. } => Some(parent),
175		}
176	}
177}
178
179/// TODO visitable is current skipped...
180///
181/// Includes [Destructuring assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
182#[apply(derive_ASTNode)]
183#[derive(PartialEqExtras, Debug, Clone, Visitable, derive_enum_from_into::EnumFrom)]
184#[partial_eq_ignore_types(Span)]
185pub enum LHSOfAssignment {
186	VariableOrPropertyAccess(VariableOrPropertyAccess),
187	ArrayDestructuring {
188		#[visit_skip_field]
189		members: Vec<WithComment<ArrayDestructuringField<LHSOfAssignment>>>,
190		spread: Option<SpreadDestructuringField<LHSOfAssignment>>,
191		position: Span,
192	},
193	ObjectDestructuring {
194		#[visit_skip_field]
195		members: Vec<WithComment<ObjectDestructuringField<LHSOfAssignment>>>,
196		spread: Option<SpreadDestructuringField<LHSOfAssignment>>,
197		position: Span,
198	},
199}
200
201impl ASTNode for LHSOfAssignment {
202	fn get_position(&self) -> Span {
203		match self {
204			LHSOfAssignment::ObjectDestructuring { position, .. }
205			| LHSOfAssignment::ArrayDestructuring { position, .. } => *position,
206			LHSOfAssignment::VariableOrPropertyAccess(var_prop_access) => {
207				var_prop_access.get_position()
208			}
209		}
210	}
211
212	fn from_reader(
213		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
214		state: &mut crate::ParsingState,
215		options: &crate::ParseOptions,
216	) -> ParseResult<Self> {
217		Expression::from_reader(reader, state, options).and_then(TryInto::try_into)
218	}
219
220	fn to_string_from_buffer<T: source_map::ToString>(
221		&self,
222		buf: &mut T,
223		options: &crate::ToStringOptions,
224		local: crate::LocalToStringInformation,
225	) {
226		match self {
227			LHSOfAssignment::ObjectDestructuring { members, spread, position: _ } => {
228				buf.push('{');
229				options.push_gap_optionally(buf);
230				for (at_end, member) in members.iter().endiate() {
231					member.to_string_from_buffer(buf, options, local);
232					if !at_end {
233						buf.push(',');
234						options.push_gap_optionally(buf);
235					}
236				}
237				if let Some(ref spread) = spread {
238					if !members.is_empty() {
239						buf.push(',');
240						options.push_gap_optionally(buf);
241					}
242					buf.push_str("...");
243					spread.0.to_string_from_buffer(buf, options, local);
244				}
245				options.push_gap_optionally(buf);
246				buf.push('}');
247			}
248			LHSOfAssignment::ArrayDestructuring { members, spread, position: _ } => {
249				buf.push('[');
250				for (at_end, member) in members.iter().endiate() {
251					member.to_string_from_buffer(buf, options, local);
252					if !at_end {
253						buf.push(',');
254						options.push_gap_optionally(buf);
255					}
256				}
257				if let Some(ref spread) = spread {
258					if !members.is_empty() {
259						buf.push(',');
260						options.push_gap_optionally(buf);
261					}
262					buf.push_str("...");
263					spread.0.to_string_from_buffer(buf, options, local);
264				}
265				buf.push(']');
266			}
267			LHSOfAssignment::VariableOrPropertyAccess(variable_or_property_access) => {
268				variable_or_property_access.to_string_from_buffer(buf, options, local);
269			}
270		}
271	}
272}
273
274impl TryFrom<Expression> for LHSOfAssignment {
275	type Error = ParseError;
276
277	fn try_from(value: Expression) -> Result<Self, Self::Error> {
278		match value {
279			Expression::ArrayLiteral(members, position) => {
280				let mut new_members = Vec::with_capacity(members.len());
281				let mut iter = members.into_iter();
282				for member in iter.by_ref() {
283					let new_member = match member.0 {
284						Some(FunctionArgument::Comment { content, is_multiline: _, position }) => {
285							WithComment::PrefixComment(
286								content,
287								ArrayDestructuringField::None,
288								position,
289							)
290						}
291						Some(FunctionArgument::Spread(expression, span)) => {
292							return if let Some(next) = iter.next() {
293								Err(ParseError::new(
294									ParseErrors::CannotHaveRegularMemberAfterSpread,
295									next.get_position(),
296								))
297							} else {
298								let inner: LHSOfAssignment = expression.try_into()?;
299								Ok(Self::ArrayDestructuring {
300									members: new_members,
301									spread: Some(SpreadDestructuringField(Box::new(inner), span)),
302									position,
303								})
304							}
305						}
306						Some(FunctionArgument::Standard(expression)) => {
307							WithComment::None(match expression {
308								Expression::Assignment { lhs, rhs, position: _ } => {
309									ArrayDestructuringField::Name(lhs, (), Some(rhs))
310								}
311								expression => {
312									ArrayDestructuringField::Name(expression.try_into()?, (), None)
313								}
314							})
315						}
316						None => WithComment::None(ArrayDestructuringField::None),
317					};
318					new_members.push(new_member);
319				}
320				Ok(Self::ArrayDestructuring { members: new_members, spread: None, position })
321			}
322			Expression::ObjectLiteral(ObjectLiteral { members, position }) => {
323				let mut new_members = Vec::with_capacity(members.len());
324				let mut iter = members.into_iter();
325				for member in iter.by_ref() {
326					let new_member: ObjectDestructuringField<LHSOfAssignment> = match member {
327						ObjectLiteralMember::Spread(expression, span) => {
328							return if let Some(next) = iter.next() {
329								Err(ParseError::new(
330									ParseErrors::CannotHaveRegularMemberAfterSpread,
331									next.get_position(),
332								))
333							} else {
334								let inner: LHSOfAssignment = expression.try_into()?;
335								Ok(Self::ObjectDestructuring {
336									members: new_members,
337									spread: Some(SpreadDestructuringField(Box::new(inner), span)),
338									position,
339								})
340							}
341						}
342						ObjectLiteralMember::Shorthand(name, pos) => {
343							ObjectDestructuringField::Name(
344								crate::VariableIdentifier::Standard(name, pos),
345								(),
346								None,
347								pos,
348							)
349						}
350						ObjectLiteralMember::Property { assignment, key, position, value } => {
351							if assignment {
352								if let PropertyKey::Identifier(name, pos, _) = key.get_ast() {
353									ObjectDestructuringField::Name(
354										crate::VariableIdentifier::Standard(name, pos),
355										(),
356										Some(Box::new(value)),
357										pos,
358									)
359								} else {
360									return Err(ParseError::new(
361										crate::ParseErrors::InvalidLHSAssignment,
362										position,
363									));
364								}
365							} else {
366								let (name, default_value) =
367									if let Expression::Assignment { lhs, rhs, position: _ } = value
368									{
369										(lhs, Some(rhs))
370									} else {
371										(value.try_into()?, None)
372									};
373
374								ObjectDestructuringField::Map {
375									from: key.get_ast(),
376									annotation: (),
377									name: WithComment::None(name),
378									default_value,
379									position,
380								}
381							}
382						}
383						ObjectLiteralMember::Method(_) => {
384							return Err(ParseError::new(
385								crate::ParseErrors::InvalidLHSAssignment,
386								position,
387							))
388						}
389						ObjectLiteralMember::Comment(..) => {
390							continue;
391						}
392					};
393					new_members.push(WithComment::None(new_member));
394				}
395				Ok(Self::ObjectDestructuring { members: new_members, spread: None, position })
396			}
397			expression => VariableOrPropertyAccess::try_from(expression)
398				.map(LHSOfAssignment::VariableOrPropertyAccess),
399		}
400	}
401}