ezno_parser/expressions/
mod.rs

1use crate::{
2	are_nodes_over_length, declarations::ClassDeclaration, derive_ASTNode,
3	errors::parse_lexing_error, functions, number::NumberRepresentation, parse_bracketed,
4	throw_unexpected_token_with_token, to_string_bracketed,
5	type_annotations::generic_arguments_from_reader_sub_open_angle, ExpressionPosition,
6	FunctionHeader, ListItem, Marker, ParseErrors, ParseResult, Quoted, TSXKeyword,
7};
8
9use self::{
10	assignments::{LHSOfAssignment, VariableOrPropertyAccess},
11	object_literal::ObjectLiteral,
12	operators::{
13		IncrementOrDecrement, Operator, ARROW_FUNCTION_PRECEDENCE, COMMA_PRECEDENCE,
14		CONDITIONAL_TERNARY_PRECEDENCE, CONSTRUCTOR_PRECEDENCE,
15		CONSTRUCTOR_WITHOUT_PARENTHESIS_PRECEDENCE, INDEX_PRECEDENCE, MEMBER_ACCESS_PRECEDENCE,
16		PARENTHESIZED_EXPRESSION_AND_LITERAL_PRECEDENCE,
17	},
18};
19
20use super::{
21	tokens::token_as_identifier, ASTNode, Block, FunctionBase, JSXRoot, ParseError, ParseOptions,
22	Span, TSXToken, Token, TokenReader, TypeAnnotation,
23};
24
25#[cfg(feature = "extras")]
26use crate::extensions::is_expression::{is_expression_from_reader_sub_is_keyword, IsExpression};
27
28use derive_partial_eq_extras::PartialEqExtras;
29use get_field_by_type::GetFieldByType;
30use source_map::{Nullable, ToString};
31use tokenizer_lib::sized_tokens::{SizedToken, TokenEnd, TokenReaderWithTokenEnds, TokenStart};
32use visitable_derive::Visitable;
33
34pub mod arrow_function;
35pub mod assignments;
36pub mod object_literal;
37pub mod operators;
38pub mod template_literal;
39pub use arrow_function::{ArrowFunction, ExpressionOrBlock};
40
41pub use template_literal::TemplateLiteral;
42
43use operators::{
44	AssociativityDirection, BinaryAssignmentOperator, BinaryOperator, UnaryOperator,
45	UnaryPostfixAssignmentOperator, UnaryPrefixAssignmentOperator, ASSIGNMENT_PRECEDENCE,
46	FUNCTION_CALL_PRECEDENCE, RELATION_PRECEDENCE,
47};
48
49pub type ExpressionFunctionBase = functions::GeneralFunctionBase<ExpressionPosition>;
50pub type ExpressionFunction = FunctionBase<ExpressionFunctionBase>;
51
52use std::convert::{TryFrom, TryInto};
53
54/// Expression structures
55///
56/// Comma is implemented as a [`BinaryOperator`]
57#[apply(derive_ASTNode)]
58#[derive(PartialEqExtras, Debug, Clone, Visitable, GetFieldByType)]
59#[get_field_by_type_target(Span)]
60#[partial_eq_ignore_types(Span)]
61#[visit_self]
62pub enum Expression {
63	// Literals:
64	NumberLiteral(NumberRepresentation, Span),
65	StringLiteral(String, #[partial_eq_ignore] Quoted, Span),
66	BooleanLiteral(bool, Span),
67	RegexLiteral {
68		pattern: String,
69		flags: Option<String>,
70		position: Span,
71	},
72	ArrayLiteral(Vec<ArrayElement>, Span),
73	ObjectLiteral(ObjectLiteral),
74	TemplateLiteral(TemplateLiteral),
75	ParenthesizedExpression(Box<MultipleExpression>, Span),
76	// Regular operations:
77	BinaryOperation {
78		lhs: Box<Expression>,
79		operator: BinaryOperator,
80		rhs: Box<Expression>,
81		position: Span,
82	},
83	SpecialOperators(SpecialOperators, Span),
84	UnaryOperation {
85		operator: UnaryOperator,
86		operand: Box<Expression>,
87		position: Span,
88	},
89	// Assignment operations
90	Assignment {
91		lhs: LHSOfAssignment,
92		rhs: Box<Expression>,
93		position: Span,
94	},
95	/// Modified assignment cannot have destructured thingies
96	BinaryAssignmentOperation {
97		lhs: VariableOrPropertyAccess,
98		operator: BinaryAssignmentOperator,
99		rhs: Box<Expression>,
100		position: Span,
101	},
102	UnaryPrefixAssignmentOperation {
103		operator: UnaryPrefixAssignmentOperator,
104		operand: VariableOrPropertyAccess,
105		position: Span,
106	},
107	UnaryPostfixAssignmentOperation {
108		operand: VariableOrPropertyAccess,
109		operator: UnaryPostfixAssignmentOperator,
110		position: Span,
111	},
112	/// e.g `x` or `(...).hi`
113	VariableReference(String, Span),
114	ThisReference(Span),
115	SuperExpression(SuperReference, Span),
116	/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target
117	NewTarget(Span),
118	ImportMeta(Span),
119	DynamicImport {
120		path: Box<Expression>,
121		options: Option<Box<Expression>>,
122		position: Span,
123	},
124	PropertyAccess {
125		parent: Box<Expression>,
126		property: PropertyReference,
127		is_optional: bool,
128		position: Span,
129	},
130	/// e.g `...[4]`
131	Index {
132		indexee: Box<Expression>,
133		indexer: Box<MultipleExpression>,
134		is_optional: bool,
135		position: Span,
136	},
137	// Function calls
138	FunctionCall {
139		function: Box<Expression>,
140		type_arguments: Option<Vec<TypeAnnotation>>,
141		arguments: Vec<FunctionArgument>,
142		is_optional: bool,
143		position: Span,
144	},
145	ConstructorCall {
146		constructor: Box<Expression>,
147		type_arguments: Option<Vec<TypeAnnotation>>,
148		arguments: Option<Vec<FunctionArgument>>,
149		position: Span,
150	},
151	/// e.g `... ? ... ? ...`
152	ConditionalTernary {
153		condition: Box<Expression>,
154		truthy_result: Box<Expression>,
155		falsy_result: Box<Expression>,
156		position: Span,
157	},
158	// Functions
159	ArrowFunction(ArrowFunction),
160	ExpressionFunction(ExpressionFunction),
161	/// Yes classes can exist in expr position :?
162	ClassExpression(ClassDeclaration<ExpressionPosition>),
163	Null(Span),
164	Comment {
165		content: String,
166		on: Box<Expression>,
167		position: Span,
168		is_multiline: bool,
169		prefix: bool,
170	},
171	/// A start of a JSXNode
172	JSXRoot(JSXRoot),
173	/// Not to be confused with binary operator `is`
174	#[cfg(feature = "extras")]
175	IsExpression(IsExpression),
176	#[cfg_attr(feature = "self-rust-tokenize", self_tokenize_field(marker_id))]
177	Marker {
178		#[visit_skip_field]
179		marker_id: Marker<Expression>,
180		position: Span,
181	},
182}
183
184impl Eq for Expression {}
185
186#[derive(PartialEq, Debug, Clone)]
187#[apply(derive_ASTNode)]
188pub enum PropertyReference {
189	Standard {
190		property: String,
191		is_private: bool,
192	},
193	#[cfg_attr(feature = "self-rust-tokenize", self_tokenize_field(0))]
194	Marker(Marker<PropertyReference>),
195}
196
197impl ASTNode for Expression {
198	fn from_reader(
199		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
200		state: &mut crate::ParsingState,
201		options: &ParseOptions,
202	) -> ParseResult<Expression> {
203		Self::from_reader_with_precedence(reader, state, options, COMMA_PRECEDENCE, None)
204	}
205
206	fn to_string_from_buffer<T: source_map::ToString>(
207		&self,
208		buf: &mut T,
209		options: &crate::ToStringOptions,
210		local: crate::LocalToStringInformation,
211	) {
212		self.to_string_using_precedence(
213			buf,
214			options,
215			local,
216			ExpressionToStringArgument { on_left: false, parent_precedence: u8::MAX },
217		);
218	}
219
220	fn get_position(&self) -> Span {
221		*GetFieldByType::get(self)
222	}
223}
224
225impl Expression {
226	#[allow(clippy::similar_names)]
227	pub fn from_reader_with_precedence(
228		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
229		state: &mut crate::ParsingState,
230		options: &ParseOptions,
231		return_precedence: u8,
232		// For partial syntax
233		start: Option<TokenStart>,
234	) -> ParseResult<Self> {
235		if let (true, Some(Token(peek, at))) = (options.partial_syntax, reader.peek()) {
236			let next_is_not_expression_like = peek.is_expression_delimiter()
237				|| start.map_or(false, |start| {
238					peek.is_statement_or_declaration_start()
239						&& state
240							.line_starts
241							.byte_indexes_on_different_lines(start.0 as usize, at.0 as usize)
242				});
243
244			if next_is_not_expression_like {
245				let point = start.unwrap_or(*at);
246				// take up the whole next part for checker suggestions
247				let position = point.union(source_map::End(at.0));
248				return Ok(Expression::Marker {
249					marker_id: state.new_partial_point_marker(point),
250					position,
251				});
252			}
253		}
254
255		let first_expression = match reader.next().ok_or_else(parse_lexing_error)? {
256			Token(TSXToken::StringLiteral(content, quoted), start) => {
257				let position = start.with_length(content.len() + 2);
258				Expression::StringLiteral(content, quoted, position)
259			}
260			Token(TSXToken::NumberLiteral(value), start) => {
261				let position = start.with_length(value.len());
262				match value.parse::<NumberRepresentation>() {
263					Ok(number) => Expression::NumberLiteral(number, position),
264					Err(_) => {
265						// TODO this should never happen
266						return Err(ParseError::new(ParseErrors::InvalidNumberLiteral, position));
267					}
268				}
269			}
270			Token(TSXToken::RegexLiteral(pattern), start) => {
271				let mut position = start.with_length(pattern.len());
272				let flag_token =
273					reader.conditional_next(|t| matches!(t, TSXToken::RegexFlagLiteral(..)));
274				let flags = if let Some(Token(TSXToken::RegexFlagLiteral(flags), start)) =
275					flag_token
276				{
277					if flags.contains(|chr| !matches!(chr, 'd' | 'g' | 'i' | 'm' | 's' | 'u' | 'y'))
278					{
279						return Err(ParseError::new(
280							ParseErrors::InvalidRegexFlag,
281							start.with_length(flags.len()),
282						));
283					}
284					position = position.union(start.get_end_after(flags.len()));
285					Some(flags)
286				} else {
287					None
288				};
289				Expression::RegexLiteral { pattern, flags, position }
290			}
291			t @ Token(TSXToken::Keyword(TSXKeyword::True), _) => {
292				Expression::BooleanLiteral(true, t.get_span())
293			}
294			t @ Token(TSXToken::Keyword(TSXKeyword::False), _) => {
295				Expression::BooleanLiteral(false, t.get_span())
296			}
297			t @ Token(TSXToken::Keyword(TSXKeyword::This), _) => {
298				Expression::ThisReference(t.get_span())
299			}
300			t @ Token(TSXToken::Keyword(TSXKeyword::Import), start) => {
301				let token = reader.next();
302				if let Some(Token(TSXToken::Dot, _)) = token {
303					let meta_start = reader.expect_next(TSXToken::Identifier("meta".into()))?;
304					Expression::ImportMeta(start.union(meta_start.get_end_after("meta".len())))
305				} else if let Some(Token(TSXToken::OpenParentheses, _)) = token {
306					let path = Expression::from_reader(reader, state, options)?;
307					if let Expression::StringLiteral(path, ..) = &path {
308						state.constant_imports.push(path.clone());
309					}
310
311					let options =
312						if reader.conditional_next(|t| matches!(t, TSXToken::Comma)).is_some() {
313							Some(Box::new(Expression::from_reader(reader, state, options)?))
314						} else {
315							None
316						};
317					let end = reader.expect_next(TSXToken::CloseParentheses)?;
318					Expression::DynamicImport {
319						path: Box::new(path),
320						options,
321						position: start.union(end.get_end_after(1)),
322					}
323				} else if let Some(token) = token {
324					return throw_unexpected_token_with_token(
325						token,
326						&[TSXToken::Dot, TSXToken::OpenParentheses],
327					);
328				} else {
329					return Err(ParseError::new(ParseErrors::LexingFailed, t.get_span()));
330				}
331			}
332			t @ Token(TSXToken::Keyword(TSXKeyword::Super), _) => {
333				let _super_position = t.get_span();
334				let token = reader.next().unwrap();
335				let position = token.get_span();
336
337				let (reference, end) = match token {
338					Token(TSXToken::Dot, _) => {
339						let (property, property_pos) =
340							token_as_identifier(reader.next().unwrap(), "super property")?;
341						(
342							SuperReference::PropertyAccess { property },
343							TokenEnd::new(property_pos.end),
344						)
345					}
346					Token(TSXToken::OpenParentheses, _) => {
347						let (arguments, _, end_pos) = parse_bracketed(
348							reader,
349							state,
350							options,
351							None,
352							TSXToken::CloseParentheses,
353						)?;
354						(SuperReference::Call { arguments }, end_pos)
355					}
356					Token(TSXToken::OpenBracket, _) => {
357						let indexer = Expression::from_reader(reader, state, options)?;
358						let end = reader.expect_next(TSXToken::CloseBracket)?;
359						(
360							SuperReference::Index { indexer: Box::new(indexer) },
361							TokenEnd::new(end.0 + 1),
362						)
363					}
364					token => {
365						return throw_unexpected_token_with_token(
366							token,
367							&[TSXToken::Dot, TSXToken::OpenParentheses, TSXToken::OpenBracket],
368						);
369					}
370				};
371				Expression::SuperExpression(reference, position.union(end))
372			}
373			t @ Token(TSXToken::Keyword(TSXKeyword::Null), _) => Expression::Null(t.get_span()),
374			Token(TSXToken::Keyword(kw @ TSXKeyword::Class), start) => {
375				state.append_keyword_at_pos(start.0, kw);
376				ClassDeclaration::from_reader_sub_class_keyword(reader, state, options, start)
377					.map(Expression::ClassExpression)?
378			}
379			Token(TSXToken::Keyword(TSXKeyword::Yield), s) => {
380				// TODO could we do better?
381				let is_delegated =
382					reader.conditional_next(|t| matches!(t, TSXToken::Multiply)).is_some();
383
384				let operator =
385					if is_delegated { UnaryOperator::DelegatedYield } else { UnaryOperator::Yield };
386
387				let operand = Expression::from_reader_with_precedence(
388					reader,
389					state,
390					options,
391					operator.precedence(),
392					Some(s),
393				)?;
394				let position = s.union(operand.get_position());
395				Expression::UnaryOperation { operator, operand: Box::new(operand), position }
396			}
397			Token(TSXToken::OpenBracket, start) => {
398				let (items, _, end) = parse_bracketed::<ArrayElement>(
399					reader,
400					state,
401					options,
402					None,
403					TSXToken::CloseBracket,
404				)?;
405
406				Expression::ArrayLiteral(items, start.union(end))
407			}
408			Token(TSXToken::OpenBrace, start) => {
409				ObjectLiteral::from_reader_sub_open_curly(reader, state, options, start)
410					.map(Expression::ObjectLiteral)?
411			}
412			t @ Token(TSXToken::OpenParentheses, start) => {
413				let mut parentheses_depth = 1;
414				let is_arrow_function = if let Some(Token(
415					TSXToken::Keyword(..)
416					| TSXToken::Identifier(..)
417					| TSXToken::OpenBrace
418					| TSXToken::OpenBracket
419					| TSXToken::CloseParentheses
420					| TSXToken::Spread,
421					_,
422				)) = reader.peek()
423				{
424					let next = reader.scan(|token, _| match token {
425						TSXToken::OpenParentheses => {
426							parentheses_depth += 1;
427							false
428						}
429						TSXToken::CloseParentheses => {
430							parentheses_depth -= 1;
431							parentheses_depth == 0
432						}
433						_ => false,
434					});
435
436					if let Some(Token(token_type, _)) = next {
437						matches!(token_type, TSXToken::Arrow)
438					} else {
439						return Err(ParseError::new(
440							crate::ParseErrors::UnmatchedBrackets,
441							t.get_span(),
442						));
443					}
444				} else {
445					false
446				};
447
448				if is_arrow_function {
449					let arrow_function = ArrowFunction::from_reader_sub_open_paren(
450						reader, state, options, false, start,
451					)?;
452					Expression::ArrowFunction(arrow_function)
453				} else {
454					let parenthesize_expression =
455						MultipleExpression::from_reader(reader, state, options)?;
456					let end = reader.expect_next_get_end(TSXToken::CloseParentheses)?;
457					Expression::ParenthesizedExpression(
458						Box::new(parenthesize_expression),
459						start.union(end),
460					)
461				}
462			}
463			t @ Token(TSXToken::Keyword(TSXKeyword::New), start) => {
464				if let Some(Token(TSXToken::Dot, _)) = reader.peek() {
465					// TODO assert not lonely, else syntax error
466					reader.expect_next(TSXToken::Dot)?;
467					reader.expect_next(TSXToken::Identifier("target".into()))?;
468					Expression::NewTarget(t.get_span())
469				} else {
470					// Pass as a function call and then adds the conversion
471					let constructor_expression = Self::from_reader_with_precedence(
472						reader,
473						state,
474						options,
475						FUNCTION_CALL_PRECEDENCE,
476						Some(start),
477					)?;
478					let position = start.union(constructor_expression.get_position());
479
480					let (type_arguments, end) = if reader
481						.conditional_next(|token| *token == TSXToken::OpenChevron)
482						.is_some()
483					{
484						let (generic_arguments, _, end_pos) =
485							parse_bracketed(reader, state, options, None, TSXToken::CloseChevron)?;
486						(Some(generic_arguments), end_pos)
487					} else {
488						(None, TokenEnd::new(position.end))
489					};
490
491					let (arguments, end) = if reader
492						.conditional_next(|token| *token == TSXToken::OpenParentheses)
493						.is_some()
494					{
495						parse_bracketed(reader, state, options, None, TSXToken::CloseParentheses)
496							.map(|(args, _, end)| (Some(args), end))?
497					} else {
498						// TODO are type arguments not allowed...?
499						(None, end)
500					};
501
502					Expression::ConstructorCall {
503						constructor: constructor_expression.into(),
504						type_arguments,
505						arguments,
506						position: start.union(end),
507					}
508				}
509			}
510			// Yes single line comment can occur here if the line splits
511			// ```
512			// [
513			//     // Hi
514			//     2
515			// ]
516			// ```
517			t @ Token(TSXToken::MultiLineComment(_) | TSXToken::Comment(_), _) => {
518				// TODO discern between multi-line here
519				let (content, is_multiline, position) = TSXToken::try_into_comment(t).unwrap();
520
521				let expression = Self::from_reader_with_precedence(
522					reader,
523					state,
524					options,
525					return_precedence,
526					start,
527				)?;
528				let position = position.union(expression.get_position());
529				Expression::Comment {
530					is_multiline,
531					content,
532					position,
533					on: Box::new(expression),
534					prefix: true,
535				}
536			}
537			// TODO not great
538			Token(TSXToken::DocTypeHTML, _) => {
539				JSXRoot::from_reader(reader, state, options).map(Expression::JSXRoot)?
540			}
541			Token(tok @ (TSXToken::JSXOpeningTagStart | TSXToken::JSXFragmentStart), span) => {
542				let var_name = matches!(tok, TSXToken::JSXFragmentStart);
543				JSXRoot::from_reader_sub_start(reader, state, options, var_name, span)
544					.map(Expression::JSXRoot)?
545			}
546			Token(TSXToken::TemplateLiteralStart, start) => {
547				TemplateLiteral::from_reader_sub_start_with_tag(reader, state, options, None, start)
548					.map(Expression::TemplateLiteral)?
549			}
550			Token(TSXToken::Keyword(kw), start) if function_header_ish(kw, reader) => {
551				// TODO not great to recreate token, but that is how Rust works :)
552				let token = Token(TSXToken::Keyword(kw), start);
553				let (is_async, start, token) =
554					if let Token(TSXToken::Keyword(TSXKeyword::Async), start) = token {
555						(true, start, reader.next().unwrap())
556					} else {
557						(false, start, token)
558					};
559
560				if is_async
561					&& !matches!(token, Token(TSXToken::Keyword(ref kw), _) if kw.is_in_function_header())
562				{
563					if let Token(TSXToken::OpenParentheses, start) = token {
564						let function = ArrowFunction::from_reader_sub_open_paren(
565							reader, state, options, is_async, start,
566						)?;
567						return Ok(Expression::ArrowFunction(function));
568					}
569
570					let (name, position) = token_as_identifier(token, "function parameter")?;
571					ArrowFunction::from_reader_with_first_parameter(
572						reader,
573						state,
574						options,
575						(name, position),
576						is_async,
577					)
578					.map(Expression::ArrowFunction)?
579				} else {
580					#[cfg(feature = "extras")]
581					{
582						use crate::functions::FunctionLocationModifier;
583						let (generator_keyword, token) =
584							if let Token(TSXToken::Keyword(TSXKeyword::Generator), _) = token {
585								(Some(token.get_span()), reader.next().unwrap())
586							} else {
587								(None, token)
588							};
589
590						let (location, token) = match token {
591							Token(TSXToken::Keyword(TSXKeyword::Server), _) => {
592								(Some(FunctionLocationModifier::Server), reader.next().unwrap())
593							}
594							Token(TSXToken::Keyword(TSXKeyword::Worker), _) => {
595								(Some(FunctionLocationModifier::Worker), reader.next().unwrap())
596							}
597							token => (None, token),
598						};
599
600						// Here because `token` (can't use `.expect_next()`)
601						let Token(TSXToken::Keyword(TSXKeyword::Function), function_start) = token
602						else {
603							return throw_unexpected_token_with_token(
604								token,
605								&[TSXToken::Keyword(TSXKeyword::Function)],
606							);
607						};
608
609						let function_end =
610							function_start.get_end_after(TSXKeyword::Function.length() as usize);
611
612						if generator_keyword.is_some() {
613							let position = start.union(function_end);
614
615							let header = FunctionHeader::ChadFunctionHeader {
616								is_async,
617								is_generator: true,
618								location,
619								position,
620							};
621
622							let name = if let Some(Token(TSXToken::OpenParentheses, _)) =
623								reader.peek()
624							{
625								None
626							} else {
627								let (token, span) =
628									token_as_identifier(reader.next().unwrap(), "function name")?;
629								Some(crate::VariableIdentifier::Standard(token, span))
630							};
631
632							FunctionBase::from_reader_with_header_and_name(
633								reader,
634								state,
635								options,
636								(Some(header.get_position().get_start()), header),
637								ExpressionPosition(name),
638							)
639							.map(Expression::ExpressionFunction)?
640						} else {
641							let generator_star_token_position = reader
642								.conditional_next(|tok| matches!(tok, TSXToken::Multiply))
643								.map(|token| token.get_span());
644
645							let end = generator_star_token_position
646								.as_ref()
647								.map_or(function_end, Span::get_end);
648
649							let header = FunctionHeader::VirginFunctionHeader {
650								position: start.union(end),
651								is_async,
652								location,
653								generator_star_token_position,
654							};
655
656							let name = if let Some(Token(TSXToken::OpenParentheses, _)) =
657								reader.peek()
658							{
659								None
660							} else {
661								let (token, span) =
662									token_as_identifier(reader.next().unwrap(), "function name")?;
663								Some(crate::VariableIdentifier::Standard(token, span))
664							};
665
666							FunctionBase::from_reader_with_header_and_name(
667								reader,
668								state,
669								options,
670								(Some(header.get_position().get_start()), header),
671								ExpressionPosition(name),
672							)
673							.map(Expression::ExpressionFunction)?
674						}
675					}
676
677					#[cfg(not(feature = "extras"))]
678					{
679						let Token(TSXToken::Keyword(TSXKeyword::Function), _) = token else {
680							return throw_unexpected_token_with_token(
681								token,
682								&[TSXToken::Keyword(TSXKeyword::Function)],
683							);
684						};
685
686						let generator_star_token_position = reader
687							.conditional_next(|tok| matches!(tok, TSXToken::Multiply))
688							.map(|token| token.get_span());
689
690						let position =
691							start.union(generator_star_token_position.as_ref().unwrap_or(
692								&start.with_length(TSXKeyword::Function.length() as usize),
693							));
694
695						let header = FunctionHeader::VirginFunctionHeader {
696							position,
697							is_async,
698							generator_star_token_position,
699						};
700
701						let name = if let Some(Token(TSXToken::OpenParentheses, _)) = reader.peek()
702						{
703							None
704						} else {
705							let (token, span) =
706								token_as_identifier(reader.next().unwrap(), "function name")?;
707
708							Some(crate::VariableIdentifier::Standard(token, span))
709						};
710						FunctionBase::from_reader_with_header_and_name(
711							reader,
712							state,
713							options,
714							(Some(start), header),
715							ExpressionPosition(name),
716						)
717						.map(Expression::ExpressionFunction)?
718					}
719				}
720			}
721			#[cfg(feature = "extras")]
722			t @ Token(TSXToken::Keyword(TSXKeyword::Is), start) if options.is_expressions => {
723				// Maintains compatibility here
724				let mut parentheses_depth = 0;
725				let next = reader.scan(|token, _| match token {
726					TSXToken::OpenParentheses => {
727						parentheses_depth += 1;
728						false
729					}
730					TSXToken::CloseParentheses => {
731						parentheses_depth -= 1;
732						parentheses_depth == 0
733					}
734					_ => false,
735				});
736				if let Some(Token(token_type, _)) = next {
737					if let TSXToken::OpenBrace = token_type {
738						let is_expression_from_reader_sub_is_keyword =
739							is_expression_from_reader_sub_is_keyword(reader, state, options, start);
740
741						is_expression_from_reader_sub_is_keyword.map(Expression::IsExpression)?
742					} else {
743						Expression::VariableReference("is".to_owned(), start.with_length(2))
744					}
745				} else {
746					return Err(ParseError::new(
747						crate::ParseErrors::UnmatchedBrackets,
748						t.get_span(),
749					));
750				}
751			}
752			Token(TSXToken::HashTag, start) => {
753				let (property_name, _) = token_as_identifier(
754					reader.next().ok_or_else(parse_lexing_error)?,
755					"private in expression",
756				)?;
757				let in_pos = state.expect_keyword(reader, TSXKeyword::In)?;
758				let rhs = Expression::from_reader_with_precedence(
759					reader,
760					state,
761					options,
762					RELATION_PRECEDENCE,
763					Some(in_pos),
764				)?;
765				let position = start.union(rhs.get_position());
766				Self::SpecialOperators(
767					SpecialOperators::In {
768						lhs: InExpressionLHS::PrivateProperty(property_name),
769						rhs: Box::new(rhs),
770					},
771					position,
772				)
773			}
774			token => {
775				if let Ok(unary_operator) = UnaryOperator::try_from(&token.0) {
776					let precedence = unary_operator.precedence();
777					let operand = Self::from_reader_with_precedence(
778						reader,
779						state,
780						options,
781						precedence,
782						Some(token.1),
783					)?;
784					let position = token.get_span().union(operand.get_position());
785					Expression::UnaryOperation {
786						operand: Box::new(operand),
787						operator: unary_operator,
788						position,
789					}
790				} else if let Ok(unary_prefix_operator) =
791					UnaryPrefixAssignmentOperator::try_from(&token.0)
792				{
793					// TODO is precedence needed...?
794					let op_precedence = unary_prefix_operator.precedence();
795					let operand = VariableOrPropertyAccess::from_reader_with_precedence(
796						reader,
797						state,
798						options,
799						op_precedence,
800					)?;
801					let position = token.get_span().union(operand.get_position());
802					Expression::UnaryPrefixAssignmentOperation {
803						operand,
804						operator: unary_prefix_operator,
805						position,
806					}
807				} else {
808					if let TSXToken::Keyword(ref keyword) = token.0 {
809						if keyword.is_invalid_identifier() {
810							return Err(ParseError::new(
811								ParseErrors::ReservedIdentifier,
812								token.get_span(),
813							));
814						}
815					}
816
817					let (name, position) = token_as_identifier(token, "variable reference")?;
818
819					if options.interpolation_points && name == crate::marker::MARKER {
820						let marker_id = state.new_partial_point_marker(position.get_start());
821						Expression::Marker { marker_id, position }
822					} else if let Some(Token(TSXToken::Arrow, _)) = reader.peek() {
823						let function = ArrowFunction::from_reader_with_first_parameter(
824							reader,
825							state,
826							options,
827							(name, position),
828							false,
829						)?;
830						Expression::ArrowFunction(function)
831					} else {
832						Expression::VariableReference(name, position)
833					}
834				}
835			}
836		};
837
838		// Operator precedence == 2, nothing can beat so
839		if let Expression::ArrowFunction(..) = first_expression {
840			return Ok(first_expression);
841		}
842
843		Self::from_reader_sub_first_expression(
844			reader,
845			state,
846			options,
847			return_precedence,
848			first_expression,
849		)
850	}
851
852	pub(crate) fn from_reader_sub_first_expression(
853		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
854		state: &mut crate::ParsingState,
855		options: &ParseOptions,
856		parent_precedence: u8,
857		first_expression: Expression,
858	) -> ParseResult<Self> {
859		let mut top = first_expression;
860		while top.get_precedence() != 2 {
861			let Token(peeked_token, _peeked_pos) = &reader.peek().ok_or_else(parse_lexing_error)?;
862
863			match peeked_token {
864				TSXToken::Comma => {
865					return Ok(top);
866				}
867				TSXToken::QuestionMark => {
868					if AssociativityDirection::RightToLeft
869						.should_return(parent_precedence, CONDITIONAL_TERNARY_PRECEDENCE)
870					{
871						return Ok(top);
872					}
873					reader.next();
874					let condition_position = top.get_position();
875					let condition = Box::new(top);
876					let lhs = Box::new(Self::from_reader(reader, state, options)?);
877					reader.expect_next(TSXToken::Colon)?;
878					let rhs = Self::from_reader(reader, state, options)?;
879					let position = condition_position.union(rhs.get_position());
880					top = Expression::ConditionalTernary {
881						position,
882						condition,
883						truthy_result: lhs,
884						falsy_result: Box::new(rhs),
885					};
886				}
887				TSXToken::OpenParentheses | TSXToken::OptionalCall => {
888					if AssociativityDirection::LeftToRight
889						.should_return(parent_precedence, FUNCTION_CALL_PRECEDENCE)
890					{
891						return Ok(top);
892					}
893					let next = reader.next().unwrap();
894					let is_optional = matches!(next.0, TSXToken::OptionalCall);
895					let (arguments, _, end) =
896						parse_bracketed(reader, state, options, None, TSXToken::CloseParentheses)?;
897					let position = top.get_position().union(end);
898					top = Expression::FunctionCall {
899						function: Box::new(top),
900						type_arguments: None,
901						arguments,
902						position,
903						is_optional,
904					};
905				}
906				TSXToken::OpenBracket | TSXToken::OptionalIndex => {
907					if AssociativityDirection::LeftToRight
908						.should_return(parent_precedence, INDEX_PRECEDENCE)
909					{
910						return Ok(top);
911					}
912					let next = reader.next().unwrap();
913					let is_optional = matches!(next.0, TSXToken::OptionalIndex);
914
915					let indexer = MultipleExpression::from_reader(reader, state, options)?;
916					let end = reader.expect_next_get_end(TSXToken::CloseBracket)?;
917					let position = top.get_position().union(end);
918					top = Expression::Index {
919						position,
920						indexee: Box::new(top),
921						indexer: Box::new(indexer),
922						is_optional,
923					};
924				}
925				TSXToken::TemplateLiteralStart => {
926					// TODO I think this is true
927					if AssociativityDirection::LeftToRight
928						.should_return(parent_precedence, FUNCTION_CALL_PRECEDENCE)
929					{
930						return Ok(top);
931					}
932					reader.next();
933					// TODO should check adjacency
934					let start = TokenStart::new(top.get_position().start);
935					let tag = Some(Box::new(top));
936					let template_literal = TemplateLiteral::from_reader_sub_start_with_tag(
937						reader, state, options, tag, start,
938					);
939					top = template_literal.map(Expression::TemplateLiteral)?;
940				}
941				TSXToken::Dot | TSXToken::OptionalChain => {
942					if AssociativityDirection::LeftToRight
943						.should_return(parent_precedence, MEMBER_ACCESS_PRECEDENCE)
944					{
945						return Ok(top);
946					}
947
948					let Token(accessor, accessor_position) = reader.next().unwrap();
949					// TODO not sure
950					if matches!(top, Self::ObjectLiteral(..)) {
951						return Err(ParseError::new(
952							ParseErrors::CannotAccessObjectLiteralDirectly,
953							accessor_position.with_length(1),
954						));
955					}
956
957					let is_optional = matches!(accessor, TSXToken::OptionalChain);
958
959					let Token(peek, at) = reader.peek().ok_or_else(parse_lexing_error)?;
960					let is_next_not_identifier = peek.is_expression_delimiter()
961						|| (peek.is_statement_or_declaration_start()
962							&& state.line_starts.byte_indexes_on_different_lines(
963								accessor_position.0 as usize,
964								at.0 as usize,
965							));
966
967					let (property, position) = if options.partial_syntax && is_next_not_identifier {
968						let marker = state.new_partial_point_marker(accessor_position);
969						let position = accessor_position.union(source_map::End(at.0));
970						(PropertyReference::Marker(marker), position)
971					} else {
972						let is_private =
973							reader.conditional_next(|t| matches!(t, TSXToken::HashTag)).is_some();
974						let (property, position) = token_as_identifier(
975							reader.next().ok_or_else(parse_lexing_error)?,
976							"variable reference",
977						)?;
978						(PropertyReference::Standard { property, is_private }, position)
979					};
980					let position = top.get_position().union(position);
981					top = Expression::PropertyAccess {
982						parent: Box::new(top),
983						property,
984						position,
985						is_optional,
986					};
987				}
988				TSXToken::Assign => {
989					if AssociativityDirection::RightToLeft
990						.should_return(parent_precedence, ASSIGNMENT_PRECEDENCE)
991					{
992						return Ok(top);
993					}
994
995					let position = top.get_position();
996					let lhs: LHSOfAssignment = top.try_into()?;
997
998					let Token(_assignment, assignment_pos) = reader.next().unwrap();
999					let new_rhs = Self::from_reader_with_precedence(
1000						reader,
1001						state,
1002						options,
1003						parent_precedence,
1004						Some(assignment_pos),
1005					)?;
1006
1007					let position = position.union(new_rhs.get_position());
1008
1009					top = Expression::Assignment { position, lhs, rhs: Box::new(new_rhs) };
1010				}
1011				TSXToken::Comment(_) => {
1012					// TODO while not comment
1013					if reader.peek_n(1).is_some_and(|t| t.0.is_expression_postfix()) {
1014						let (content, is_multiline, position) =
1015							TSXToken::try_into_comment(reader.next().unwrap()).unwrap();
1016						top = Expression::Comment {
1017							content,
1018							on: Box::new(top),
1019							position,
1020							is_multiline,
1021							prefix: false,
1022						};
1023					} else {
1024						return Ok(top);
1025					}
1026				}
1027				TSXToken::MultiLineComment(_) => {
1028					let (content, is_multiline, position) =
1029						TSXToken::try_into_comment(reader.next().unwrap()).unwrap();
1030					top = Expression::Comment {
1031						content,
1032						on: Box::new(top),
1033						position,
1034						is_multiline,
1035						prefix: false,
1036					};
1037				}
1038				TSXToken::Keyword(TSXKeyword::As | TSXKeyword::Satisfies | TSXKeyword::Is)
1039					if options.type_annotations =>
1040				{
1041					if AssociativityDirection::LeftToRight
1042						.should_return(parent_precedence, RELATION_PRECEDENCE)
1043					{
1044						return Ok(top);
1045					}
1046
1047					if (cfg!(not(feature = "extras"))
1048						&& matches!(peeked_token, TSXToken::Keyword(TSXKeyword::Is)))
1049						|| (cfg!(not(feature = "full-typescript"))
1050							&& matches!(peeked_token, TSXToken::Keyword(TSXKeyword::As)))
1051					{
1052						return Ok(top);
1053					}
1054
1055					let token = reader.next().unwrap();
1056					let reference = TypeAnnotation::from_reader(reader, state, options)?;
1057					let position = top.get_position().union(reference.get_position());
1058
1059					let special_operators = match token.0 {
1060						#[cfg(feature = "full-typescript")]
1061						TSXToken::Keyword(TSXKeyword::As) => SpecialOperators::AsCast {
1062							value: top.into(),
1063							rhs: match reference {
1064								// TODO temp :0
1065								TypeAnnotation::Name(
1066									crate::type_annotations::TypeName::Name(name),
1067									span,
1068								) if name == "const" => TypeOrConst::Const(span),
1069								reference => TypeOrConst::Type(Box::new(reference)),
1070							},
1071						},
1072						TSXToken::Keyword(TSXKeyword::Satisfies) => SpecialOperators::Satisfies {
1073							value: top.into(),
1074							type_annotation: Box::new(reference),
1075						},
1076						#[cfg(feature = "extras")]
1077						TSXToken::Keyword(TSXKeyword::Is) if options.is_expressions => SpecialOperators::Is {
1078							value: top.into(),
1079							type_annotation: Box::new(reference),
1080						},
1081						_ => unreachable!(),
1082					};
1083					top = Self::SpecialOperators(special_operators, position);
1084				}
1085				#[cfg(feature = "full-typescript")]
1086				TSXToken::LogicalNot if options.type_annotations => {
1087					let Token(_token, not_pos) = reader.next().unwrap();
1088					let position = top.get_position().union(not_pos.get_end_after(1));
1089					top = Self::SpecialOperators(
1090						SpecialOperators::NonNullAssertion(Box::new(top)),
1091						position,
1092					);
1093				}
1094				TSXToken::Keyword(TSXKeyword::In) => {
1095					if AssociativityDirection::LeftToRight
1096						.should_return(parent_precedence, RELATION_PRECEDENCE)
1097					{
1098						return Ok(top);
1099					}
1100
1101					let Token(_token, in_pos) = reader.next().unwrap();
1102					let rhs = Expression::from_reader_with_precedence(
1103						reader,
1104						state,
1105						options,
1106						RELATION_PRECEDENCE,
1107						Some(in_pos),
1108					)?;
1109					let position = top.get_position().union(rhs.get_position());
1110					top = Self::SpecialOperators(
1111						SpecialOperators::In {
1112							lhs: InExpressionLHS::Expression(Box::new(top)),
1113							rhs: Box::new(rhs),
1114						},
1115						position,
1116					);
1117				}
1118				TSXToken::Keyword(TSXKeyword::InstanceOf) => {
1119					if AssociativityDirection::LeftToRight
1120						.should_return(parent_precedence, RELATION_PRECEDENCE)
1121					{
1122						return Ok(top);
1123					}
1124
1125					let Token(_, instance_of_pos) = reader.next().unwrap();
1126					let rhs = Expression::from_reader_with_precedence(
1127						reader,
1128						state,
1129						options,
1130						RELATION_PRECEDENCE,
1131						Some(instance_of_pos),
1132					)?;
1133					let position = top.get_position().union(rhs.get_position());
1134					top = Self::SpecialOperators(
1135						SpecialOperators::InstanceOf { lhs: Box::new(top), rhs: Box::new(rhs) },
1136						position,
1137					);
1138				}
1139				token => {
1140					// Splitting here side-steps some complaints the borrow checker has with passing
1141					// a mutable reader here
1142					let token = if let TSXToken::OpenChevron = token {
1143						if is_generic_arguments(reader) {
1144							if AssociativityDirection::LeftToRight
1145								.should_return(parent_precedence, FUNCTION_CALL_PRECEDENCE)
1146							{
1147								return Ok(top);
1148							}
1149							let _ = reader.next();
1150							let (type_arguments, _) = generic_arguments_from_reader_sub_open_angle(
1151								reader, state, options, None,
1152							)?;
1153							let (arguments, _, end) = parse_bracketed(
1154								reader,
1155								state,
1156								options,
1157								Some(TSXToken::OpenParentheses),
1158								TSXToken::CloseParentheses,
1159							)?;
1160							top = Expression::FunctionCall {
1161								position: top.get_position().union(end),
1162								function: Box::new(top),
1163								type_arguments: Some(type_arguments),
1164								arguments,
1165								is_optional: false,
1166							};
1167							continue;
1168						}
1169						&TSXToken::OpenChevron
1170					} else {
1171						token
1172					};
1173
1174					if let Ok(operator) = UnaryPostfixAssignmentOperator::try_from(token) {
1175						if operator
1176							.associativity_direction()
1177							.should_return(parent_precedence, operator.precedence())
1178						{
1179							return Ok(top);
1180						}
1181						let token = reader.next().unwrap();
1182
1183						// Increment and decrement are the only two postfix operations
1184						let position = top.get_position().union(token.get_end());
1185						top = Expression::UnaryPostfixAssignmentOperation {
1186							operand: top.try_into()?,
1187							operator,
1188							position,
1189						};
1190					} else if let Ok(operator) = BinaryOperator::try_from(token) {
1191						if operator
1192							.associativity_direction()
1193							.should_return(parent_precedence, operator.precedence())
1194						{
1195							return Ok(top);
1196						}
1197
1198						let Token(token, op_pos) = reader.next().unwrap();
1199
1200						if !options.extra_operators && operator.is_non_standard() {
1201							return Err(ParseError::new(
1202								ParseErrors::NonStandardSyntaxUsedWithoutEnabled,
1203								op_pos.with_length(token.length() as usize),
1204							));
1205						}
1206
1207						// Note precedence is already handled
1208						let rhs = Self::from_reader_with_precedence(
1209							reader,
1210							state,
1211							options,
1212							operator.precedence(),
1213							Some(op_pos),
1214						)?;
1215
1216						top = Expression::BinaryOperation {
1217							position: top.get_position().union(rhs.get_position()),
1218							lhs: Box::new(top),
1219							operator,
1220							rhs: Box::new(rhs),
1221						};
1222					} else if let Ok(operator) = BinaryAssignmentOperator::try_from(token) {
1223						if operator
1224							.associativity_direction()
1225							.should_return(parent_precedence, operator.precedence())
1226						{
1227							return Ok(top);
1228						}
1229						let Token(_, op_pos) = reader.next().unwrap();
1230						let new_rhs = Self::from_reader_with_precedence(
1231							reader,
1232							state,
1233							options,
1234							operator.precedence(),
1235							Some(op_pos),
1236						)?;
1237						top = Expression::BinaryAssignmentOperation {
1238							position: top.get_position().union(new_rhs.get_position()),
1239							lhs: top.try_into()?,
1240							operator,
1241							rhs: Box::new(new_rhs),
1242						};
1243					} else {
1244						return Ok(top);
1245					}
1246				}
1247			}
1248		}
1249
1250		Ok(top)
1251	}
1252
1253	#[must_use]
1254	pub fn get_precedence(&self) -> u8 {
1255		// TODO unsure about some of these
1256		match self {
1257			Self::NumberLiteral(..)
1258			| Self::BooleanLiteral(..)
1259			| Self::StringLiteral(..)
1260			| Self::RegexLiteral { .. }
1261			| Self::ArrayLiteral(..)
1262			| Self::TemplateLiteral(..)
1263			| Self::ParenthesizedExpression(..)
1264			| Self::JSXRoot(..)
1265			| Self::ExpressionFunction(..)
1266			| Self::Null(..)
1267			| Self::ObjectLiteral(..)
1268			| Self::VariableReference(..)
1269			| Self::ThisReference(..)
1270			| Self::SuperExpression(..)
1271			| Self::NewTarget(..)
1272			| Self::ClassExpression(..)
1273			| Self::ImportMeta(..)
1274			| Self::DynamicImport { .. }
1275			| Self::Marker { .. } => PARENTHESIZED_EXPRESSION_AND_LITERAL_PRECEDENCE,
1276			Self::BinaryOperation { operator, .. } => operator.precedence(),
1277			Self::UnaryOperation { operator, .. } => operator.precedence(),
1278			Self::Assignment { .. } => ASSIGNMENT_PRECEDENCE,
1279			Self::BinaryAssignmentOperation { operator, .. } => operator.precedence(),
1280			Self::UnaryPrefixAssignmentOperation { operator, .. } => operator.precedence(),
1281			Self::UnaryPostfixAssignmentOperation { operator, .. } => operator.precedence(),
1282			Self::PropertyAccess { .. } => MEMBER_ACCESS_PRECEDENCE,
1283			Self::FunctionCall { .. } => FUNCTION_CALL_PRECEDENCE,
1284			Self::ConstructorCall { arguments: Some(_), .. } => CONSTRUCTOR_PRECEDENCE,
1285			Self::ConstructorCall { arguments: None, .. } => {
1286				CONSTRUCTOR_WITHOUT_PARENTHESIS_PRECEDENCE
1287			}
1288			Self::ArrowFunction(..) => ARROW_FUNCTION_PRECEDENCE,
1289			Self::Index { .. } => INDEX_PRECEDENCE,
1290			Self::ConditionalTernary { .. } => CONDITIONAL_TERNARY_PRECEDENCE,
1291			Self::Comment { ref on, .. } => on.get_precedence(),
1292			#[cfg(feature = "full-typescript")]
1293			Self::SpecialOperators(SpecialOperators::NonNullAssertion(..), _) => {
1294				PARENTHESIZED_EXPRESSION_AND_LITERAL_PRECEDENCE
1295			}
1296			// All these are relational and have the same precedence
1297			Self::SpecialOperators(..) => RELATION_PRECEDENCE,
1298			// TODO unsure about this one...?
1299			#[cfg(feature = "extras")]
1300			Self::IsExpression(..) => PARENTHESIZED_EXPRESSION_AND_LITERAL_PRECEDENCE,
1301		}
1302	}
1303
1304	pub(crate) fn to_string_using_precedence<T: source_map::ToString>(
1305		&self,
1306		buf: &mut T,
1307		options: &crate::ToStringOptions,
1308		local: crate::LocalToStringInformation,
1309		local2: ExpressionToStringArgument,
1310	) {
1311		let self_precedence = self.get_precedence();
1312		// let inverted = local2.parent_precedence < self_precedence;
1313		// if inverted {
1314		// 	buf.push('(');
1315		// }
1316		match self {
1317			Self::Marker { .. } => {
1318				assert!(options.expect_markers, "marker found");
1319			}
1320			Self::NumberLiteral(num, _) => buf.push_str(&num.to_string()),
1321			Self::StringLiteral(string, quoted, _) => {
1322				buf.push(quoted.as_char());
1323				buf.push_str(string);
1324				buf.push(quoted.as_char());
1325			}
1326			Self::BooleanLiteral(expression, _) => {
1327				buf.push_str(if *expression { "true" } else { "false" });
1328			}
1329			Self::RegexLiteral { pattern, flags, .. } => {
1330				buf.push('/');
1331				buf.push_str(pattern);
1332				buf.push('/');
1333				if let Some(flags) = flags {
1334					buf.push_str(flags);
1335				}
1336			}
1337			Self::BinaryOperation { lhs, operator, rhs, .. } => {
1338				lhs.to_string_using_precedence(
1339					buf,
1340					options,
1341					local,
1342					local2.with_precedence(self_precedence),
1343				);
1344				// TODO not great
1345				if options.pretty
1346					|| matches!(
1347						(operator, &**lhs),
1348						(
1349							BinaryOperator::Subtract,
1350							Expression::UnaryPostfixAssignmentOperation {
1351								operator: UnaryPostfixAssignmentOperator(
1352									IncrementOrDecrement::Decrement
1353								),
1354								..
1355							},
1356						) | (
1357							BinaryOperator::Add,
1358							Expression::UnaryPostfixAssignmentOperation {
1359								operator: UnaryPostfixAssignmentOperator(
1360									IncrementOrDecrement::Increment
1361								),
1362								..
1363							},
1364						)
1365					) {
1366					buf.push(' ');
1367				}
1368				buf.push_str(operator.to_str());
1369				// TODO not great
1370				if options.pretty
1371					|| matches!(
1372						(operator, &**rhs),
1373						(
1374							BinaryOperator::Subtract,
1375							Expression::UnaryPrefixAssignmentOperation {
1376								operator: UnaryPrefixAssignmentOperator::IncrementOrDecrement(
1377									IncrementOrDecrement::Decrement
1378								),
1379								..
1380							} | Expression::UnaryOperation {
1381								operator: UnaryOperator::Negation,
1382								..
1383							},
1384						) | (
1385							BinaryOperator::Add,
1386							Expression::UnaryPrefixAssignmentOperation {
1387								operator: UnaryPrefixAssignmentOperator::IncrementOrDecrement(
1388									IncrementOrDecrement::Increment
1389								),
1390								..
1391							} | Expression::UnaryOperation { operator: UnaryOperator::Plus, .. },
1392						)
1393					) {
1394					buf.push(' ');
1395				}
1396				rhs.to_string_using_precedence(
1397					buf,
1398					options,
1399					local,
1400					local2.with_precedence(self_precedence),
1401				);
1402			}
1403			Self::SpecialOperators(special, _) => match special {
1404				#[cfg(feature = "full-typescript")]
1405				SpecialOperators::AsCast { value, rhs, .. } => {
1406					value.to_string_from_buffer(buf, options, local);
1407					if options.include_type_annotations {
1408						buf.push_str(" as ");
1409						match rhs {
1410							TypeOrConst::Type(type_annotation) => {
1411								type_annotation.to_string_from_buffer(buf, options, local);
1412							}
1413							TypeOrConst::Const(_) => {
1414								buf.push_str("const");
1415							}
1416						}
1417					}
1418				}
1419				SpecialOperators::Satisfies { value, type_annotation, .. } => {
1420					value.to_string_from_buffer(buf, options, local);
1421					if options.include_type_annotations {
1422						buf.push_str(" satisfies ");
1423						type_annotation.to_string_from_buffer(buf, options, local);
1424					}
1425				}
1426				SpecialOperators::In { lhs, rhs } => {
1427					match lhs {
1428						InExpressionLHS::PrivateProperty(property) => {
1429							buf.push('#');
1430							buf.push_str(property);
1431						}
1432						InExpressionLHS::Expression(lhs) => {
1433							lhs.to_string_using_precedence(
1434								buf,
1435								options,
1436								local,
1437								local2.with_precedence(self_precedence),
1438							);
1439						}
1440					}
1441					// TODO whitespace can be dropped depending on LHS and RHS
1442					buf.push_str(" in ");
1443					rhs.to_string_using_precedence(
1444						buf,
1445						options,
1446						local,
1447						local2.with_precedence(self_precedence),
1448					);
1449				}
1450				SpecialOperators::InstanceOf { lhs, rhs } => {
1451					lhs.to_string_using_precedence(
1452						buf,
1453						options,
1454						local,
1455						local2.with_precedence(self_precedence),
1456					);
1457					// TODO whitespace can be dropped depending on LHS and RHS
1458					buf.push_str(" instanceof ");
1459					rhs.to_string_using_precedence(
1460						buf,
1461						options,
1462						local,
1463						local2.with_precedence(self_precedence),
1464					);
1465				}
1466				#[cfg(feature = "full-typescript")]
1467				SpecialOperators::NonNullAssertion(on) => {
1468					on.to_string_using_precedence(
1469						buf,
1470						options,
1471						local,
1472						local2.with_precedence(self_precedence),
1473					);
1474					if options.include_type_annotations {
1475						buf.push('!');
1476					}
1477				}
1478				#[cfg(feature = "extras")]
1479				SpecialOperators::Is { value, type_annotation, .. } => {
1480					value.to_string_from_buffer(buf, options, local);
1481					buf.push_str(" is ");
1482					type_annotation.to_string_from_buffer(buf, options, local);
1483				}
1484			},
1485			Self::UnaryOperation { operand, operator, .. } => {
1486				buf.push_str(operator.to_str());
1487				// TODO not great
1488				if let (
1489					UnaryOperator::Negation,
1490					Expression::UnaryPrefixAssignmentOperation {
1491						operator:
1492							UnaryPrefixAssignmentOperator::IncrementOrDecrement(
1493								IncrementOrDecrement::Decrement,
1494							),
1495						..
1496					}
1497					| Expression::UnaryOperation { operator: UnaryOperator::Negation, .. },
1498				)
1499				| (
1500					UnaryOperator::Plus,
1501					Expression::UnaryPrefixAssignmentOperation {
1502						operator:
1503							UnaryPrefixAssignmentOperator::IncrementOrDecrement(
1504								IncrementOrDecrement::Increment,
1505							),
1506						..
1507					}
1508					| Expression::UnaryOperation { operator: UnaryOperator::Plus, .. },
1509				) = (operator, &**operand)
1510				{
1511					buf.push(' ');
1512				}
1513				let right_argument = local2.with_precedence(self_precedence).on_right();
1514				operand.to_string_using_precedence(buf, options, local, right_argument);
1515			}
1516			Self::Assignment { lhs, rhs, .. } => {
1517				let require_parenthesis =
1518					matches!(lhs, LHSOfAssignment::ObjectDestructuring { .. }) && local2.on_left;
1519
1520				if require_parenthesis {
1521					buf.push('(');
1522				}
1523				lhs.to_string_from_buffer(buf, options, local);
1524				buf.push_str(if options.pretty { " = " } else { "=" });
1525				let right_argument = local2.with_precedence(self_precedence).on_right();
1526				rhs.to_string_using_precedence(buf, options, local, right_argument);
1527				if require_parenthesis {
1528					buf.push(')');
1529				}
1530			}
1531			Self::BinaryAssignmentOperation { lhs, operator, rhs, .. } => {
1532				lhs.to_string_from_buffer(buf, options, local);
1533				options.push_gap_optionally(buf);
1534				buf.push_str(operator.to_str());
1535				options.push_gap_optionally(buf);
1536				let right_argument = local2.with_precedence(self_precedence).on_right();
1537				rhs.to_string_using_precedence(buf, options, local, right_argument);
1538			}
1539			Self::UnaryPrefixAssignmentOperation { operand, operator, .. } => {
1540				buf.push_str(operator.to_str());
1541				operand.to_string_from_buffer(buf, options, local);
1542			}
1543			Self::UnaryPostfixAssignmentOperation { operand, operator, .. } => {
1544				operand.to_string_from_buffer(buf, options, local);
1545				buf.push_str(operator.to_str());
1546			}
1547			Self::VariableReference(name, position) => {
1548				buf.add_mapping(&position.with_source(local.under));
1549				buf.push_str(name);
1550			}
1551			Self::ThisReference(..) => {
1552				buf.push_str("this");
1553			}
1554			Self::NewTarget(..) => {
1555				buf.push_str("new.target");
1556			}
1557			Self::ImportMeta(..) => {
1558				buf.push_str("import.meta");
1559			}
1560			Self::DynamicImport { path, .. } => {
1561				buf.push_str("import(");
1562				path.to_string_from_buffer(buf, options, local);
1563				buf.push(')');
1564			}
1565			Self::PropertyAccess { parent, property, is_optional, position, .. } => {
1566				if options.enforce_limit_length_limit() && local.should_try_pretty_print {
1567					chain_to_string_from_buffer(self, buf, options, local);
1568					return;
1569				}
1570
1571				buf.add_mapping(&position.with_source(local.under));
1572
1573				// TODO number okay, others don't quite get?
1574				if let Self::NumberLiteral(..) | Self::ObjectLiteral(..) | Self::ArrowFunction(..) =
1575					parent.get_non_parenthesized()
1576				{
1577					buf.push('(');
1578					parent.to_string_from_buffer(buf, options, local);
1579					buf.push(')');
1580				} else {
1581					parent.to_string_from_buffer(buf, options, local);
1582				}
1583
1584				if *is_optional {
1585					buf.push_str("?.");
1586				} else {
1587					buf.push('.');
1588				}
1589
1590				match property {
1591					PropertyReference::Standard { property, is_private } => {
1592						if *is_private {
1593							buf.push('#');
1594						}
1595						buf.push_str(property);
1596					}
1597					PropertyReference::Marker(..) => {
1598						assert!(options.expect_markers, "found marker");
1599					}
1600				}
1601			}
1602			Self::ParenthesizedExpression(expr, _) => {
1603				// TODO more expressions could be considered for parenthesis elision
1604				if matches!(&**expr, MultipleExpression::Single(inner) if inner.get_precedence() == PARENTHESIZED_EXPRESSION_AND_LITERAL_PRECEDENCE)
1605				{
1606					expr.to_string_on_left(buf, options, local);
1607				} else {
1608					buf.push('(');
1609					expr.to_string_from_buffer(buf, options, local);
1610					buf.push(')');
1611				}
1612			}
1613			Self::Index { indexee: expression, indexer, is_optional, .. } => {
1614				expression.to_string_using_precedence(buf, options, local, local2);
1615				if *is_optional {
1616					buf.push_str("?.");
1617				}
1618				buf.push('[');
1619				indexer.to_string_from_buffer(buf, options, local);
1620				buf.push(']');
1621			}
1622			Self::FunctionCall { function, type_arguments, arguments, is_optional, .. } => {
1623				// TODO is this okay?
1624				if let Some(ExpressionOrBlock::Expression(expression)) = self.is_iife() {
1625					expression.to_string_from_buffer(buf, options, local);
1626					return;
1627				}
1628
1629				function.to_string_using_precedence(buf, options, local, local2);
1630
1631				if *is_optional {
1632					buf.push_str("?.");
1633				}
1634				if let (true, Some(type_arguments)) =
1635					(options.include_type_annotations, type_arguments)
1636				{
1637					to_string_bracketed(type_arguments, ('<', '>'), buf, options, local);
1638				}
1639				arguments_to_string(arguments, buf, options, local);
1640			}
1641			Self::ConstructorCall { constructor, type_arguments, arguments, .. } => {
1642				buf.push_str("new ");
1643				constructor.to_string_from_buffer(buf, options, local);
1644				if let (true, Some(type_arguments)) =
1645					(options.include_type_annotations, type_arguments)
1646				{
1647					to_string_bracketed(type_arguments, ('<', '>'), buf, options, local);
1648				}
1649				if let Some(arguments) = arguments {
1650					// Constructor calls can drop arguments if none
1651					if !arguments.is_empty() || options.pretty {
1652						arguments_to_string(arguments, buf, options, local);
1653					}
1654				}
1655			}
1656			Self::JSXRoot(root) => root.to_string_from_buffer(buf, options, local),
1657			Self::ArrowFunction(arrow_function) => {
1658				// `async () => {}` looks like async statement declaration when in declaration
1659				if local2.on_left && arrow_function.header {
1660					buf.push('(');
1661				}
1662				arrow_function.to_string_from_buffer(buf, options, local);
1663				if local2.on_left && arrow_function.header {
1664					buf.push(')');
1665				}
1666			}
1667			Self::ExpressionFunction(function) => {
1668				if local2.on_left {
1669					buf.push('(');
1670				}
1671				function.to_string_from_buffer(buf, options, local);
1672				if local2.on_left {
1673					buf.push(')');
1674				}
1675			}
1676			Self::ArrayLiteral(values, _) => {
1677				// Fix, see: https://github.com/kaleidawave/ezno/pull/158#issuecomment-2169621017
1678				if options.pretty && options.enforce_limit_length_limit() {
1679					const MAX_INLINE_OBJECT_LITERAL: u32 = 40;
1680
1681					let values_are_all_booleans_or_numbers =
1682						values.first().and_then(ArrayElement::inner_ref).is_some_and(|e| {
1683							matches!(
1684								e,
1685								Expression::BooleanLiteral(..) | Expression::NumberLiteral(..)
1686							)
1687						}) && values.iter().all(|e| {
1688							e.inner_ref().is_some_and(|e| {
1689								matches!(
1690									e,
1691									Expression::BooleanLiteral(..) | Expression::NumberLiteral(..)
1692								)
1693							})
1694						}) && are_nodes_over_length(
1695							values.iter(),
1696							options,
1697							local,
1698							Some(MAX_INLINE_OBJECT_LITERAL),
1699							true,
1700						);
1701
1702					if values_are_all_booleans_or_numbers {
1703						buf.push('[');
1704						let inner_local = local.next_level();
1705						buf.push_new_line();
1706						options.add_indent(inner_local.depth, buf);
1707						for (at_end, node) in
1708							iterator_endiate::EndiateIteratorExt::endiate(values.iter())
1709						{
1710							if buf.characters_on_current_line() > MAX_INLINE_OBJECT_LITERAL {
1711								buf.push_new_line();
1712								options.add_indent(inner_local.depth, buf);
1713							}
1714							node.to_string_from_buffer(buf, options, inner_local);
1715							if !at_end {
1716								buf.push(',');
1717								options.push_gap_optionally(buf);
1718							}
1719						}
1720						buf.push_new_line();
1721						options.add_indent(local.depth, buf);
1722						buf.push(']');
1723						return;
1724					};
1725				}
1726				to_string_bracketed(values, ('[', ']'), buf, options, local);
1727			}
1728			Self::ObjectLiteral(object_literal) => {
1729				if local2.on_left {
1730					buf.push('(');
1731				}
1732				to_string_bracketed(&object_literal.members, ('{', '}'), buf, options, local);
1733				if local2.on_left {
1734					buf.push(')');
1735				}
1736			}
1737			Self::ClassExpression(class) => {
1738				if local2.on_left {
1739					buf.push('(');
1740				}
1741				class.to_string_from_buffer(buf, options, local);
1742				if local2.on_left {
1743					buf.push(')');
1744				}
1745			}
1746			Self::Comment { content, on, is_multiline, prefix, position: _ } => {
1747				if *prefix && options.should_add_comment(content) {
1748					if *is_multiline {
1749						buf.push_str("/*");
1750						buf.push_str_contains_new_line(content);
1751						buf.push_str("*/ ");
1752					} else {
1753						buf.push_str("//");
1754						buf.push_str(content);
1755						buf.push_new_line();
1756					}
1757				}
1758				on.to_string_using_precedence(buf, options, local, local2);
1759				if !prefix && options.should_add_comment(content) {
1760					if *is_multiline {
1761						buf.push_str("/*");
1762						buf.push_str_contains_new_line(content);
1763						buf.push_str("*/ ");
1764					} else {
1765						buf.push_str("//");
1766						buf.push_str(content);
1767						buf.push_new_line();
1768					}
1769				}
1770			}
1771			Self::TemplateLiteral(template_literal) => {
1772				// Doing here because of tag precedence
1773				if let Some(tag) = &template_literal.tag {
1774					tag.to_string_using_precedence(buf, options, local, local2);
1775				}
1776				buf.push('`');
1777				for (static_part, dynamic_part) in &template_literal.parts {
1778					buf.push_str_contains_new_line(static_part.as_str());
1779
1780					buf.push_str("${");
1781					dynamic_part.to_string_from_buffer(buf, options, local);
1782					buf.push('}');
1783				}
1784				buf.push_str_contains_new_line(template_literal.last.as_str());
1785				buf.push('`');
1786			}
1787			Self::ConditionalTernary { condition, truthy_result, falsy_result, .. } => {
1788				let available_space = u32::from(options.max_line_length)
1789					.saturating_sub(buf.characters_on_current_line());
1790
1791				let split_lines = crate::are_nodes_over_length(
1792					[condition, truthy_result, falsy_result].iter().map(AsRef::as_ref),
1793					options,
1794					local,
1795					Some(available_space),
1796					true,
1797				);
1798				condition.to_string_using_precedence(
1799					buf,
1800					options,
1801					local,
1802					local2.with_precedence(CONDITIONAL_TERNARY_PRECEDENCE),
1803				);
1804				if split_lines {
1805					buf.push_new_line();
1806					options.add_indent(local.depth + 1, buf);
1807					buf.push_str("? ");
1808				} else {
1809					buf.push_str(if options.pretty { " ? " } else { "?" });
1810				}
1811				truthy_result.to_string_using_precedence(
1812					buf,
1813					options,
1814					local,
1815					local2.with_precedence(CONDITIONAL_TERNARY_PRECEDENCE).on_right(),
1816				);
1817				if split_lines {
1818					buf.push_new_line();
1819					options.add_indent(local.depth + 1, buf);
1820					buf.push_str(": ");
1821				} else {
1822					buf.push_str(if options.pretty { " : " } else { ":" });
1823				}
1824				falsy_result.to_string_using_precedence(
1825					buf,
1826					options,
1827					local,
1828					local2.with_precedence(CONDITIONAL_TERNARY_PRECEDENCE).on_right(),
1829				);
1830			}
1831			Self::Null(..) => buf.push_str("null"),
1832			#[cfg(feature = "extras")]
1833			Self::IsExpression(is_expr) => is_expr.to_string_from_buffer(buf, options, local),
1834			Self::SuperExpression(super_expr, _) => {
1835				buf.push_str("super");
1836				match super_expr {
1837					SuperReference::Call { arguments } => {
1838						arguments_to_string(arguments, buf, options, local);
1839					}
1840					SuperReference::PropertyAccess { property } => {
1841						buf.push('.');
1842						buf.push_str(property);
1843					}
1844					SuperReference::Index { indexer: index } => {
1845						buf.push('[');
1846						index.to_string_from_buffer(buf, options, local);
1847						buf.push(']');
1848					}
1849				}
1850			}
1851		}
1852		// if inverted {
1853		// 	buf.push(')');
1854		// }
1855	}
1856}
1857
1858fn function_header_ish(
1859	kw: TSXKeyword,
1860	reader: &mut impl TokenReader<TSXToken, TokenStart>,
1861) -> bool {
1862	kw.is_in_function_header()
1863		|| (kw.is_special_function_header()
1864			&& reader.peek().map_or(
1865				false,
1866				|Token(t, _)| matches!(t, TSXToken::Keyword(kw) if kw.is_in_function_header()),
1867			))
1868}
1869
1870#[derive(Clone, Copy)]
1871pub(crate) struct ExpressionToStringArgument {
1872	/// On left of statement
1873	pub on_left: bool,
1874	pub parent_precedence: u8,
1875}
1876
1877impl ExpressionToStringArgument {
1878	pub fn on_right(self) -> Self {
1879		Self { on_left: false, parent_precedence: self.parent_precedence }
1880	}
1881
1882	pub fn with_precedence(self, precedence: u8) -> Self {
1883		Self { on_left: self.on_left, parent_precedence: precedence }
1884	}
1885}
1886
1887/// Represents expressions under the comma operator
1888#[apply(derive_ASTNode)]
1889#[derive(Debug, Clone, PartialEq, Visitable, get_field_by_type::GetFieldByType)]
1890#[get_field_by_type_target(Span)]
1891pub enum MultipleExpression {
1892	Multiple { lhs: Box<MultipleExpression>, rhs: Expression, position: Span },
1893	Single(Expression),
1894}
1895
1896impl MultipleExpression {
1897	#[must_use]
1898	pub fn is_iife(&self) -> Option<&ExpressionOrBlock> {
1899		if let MultipleExpression::Single(inner) = self {
1900			inner.is_iife()
1901		} else {
1902			None
1903		}
1904	}
1905
1906	#[must_use]
1907	pub fn get_rhs(&self) -> &Expression {
1908		match self {
1909			MultipleExpression::Multiple { rhs, .. } | MultipleExpression::Single(rhs) => rhs,
1910		}
1911	}
1912
1913	pub(crate) fn to_string_on_left<T: source_map::ToString>(
1914		&self,
1915		buf: &mut T,
1916		options: &crate::ToStringOptions,
1917		local: crate::LocalToStringInformation,
1918	) {
1919		match self {
1920			MultipleExpression::Multiple { lhs, rhs, position: _ } => {
1921				lhs.to_string_on_left(buf, options, local);
1922				buf.push(',');
1923				rhs.to_string_from_buffer(buf, options, local);
1924			}
1925			MultipleExpression::Single(single) => {
1926				let local2 =
1927					ExpressionToStringArgument { on_left: true, parent_precedence: u8::MAX };
1928				single.to_string_using_precedence(buf, options, local, local2);
1929			}
1930		}
1931	}
1932}
1933
1934impl ASTNode for MultipleExpression {
1935	fn from_reader(
1936		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
1937		state: &mut crate::ParsingState,
1938		options: &ParseOptions,
1939	) -> ParseResult<Self> {
1940		let first = Expression::from_reader(reader, state, options)?;
1941		if let Some(Token(TSXToken::Comma, _)) = reader.peek() {
1942			let mut top: MultipleExpression = first.into();
1943			while let Some(Token(TSXToken::Comma, _)) = reader.peek() {
1944				reader.next();
1945				let rhs = Expression::from_reader(reader, state, options)?;
1946				let position = top.get_position().union(rhs.get_position());
1947				top = MultipleExpression::Multiple { lhs: Box::new(top), rhs, position };
1948			}
1949			Ok(top)
1950		} else {
1951			Ok(MultipleExpression::Single(first))
1952		}
1953	}
1954
1955	fn to_string_from_buffer<T: source_map::ToString>(
1956		&self,
1957		buf: &mut T,
1958		options: &crate::ToStringOptions,
1959		local: crate::LocalToStringInformation,
1960	) {
1961		match self {
1962			MultipleExpression::Multiple { lhs, rhs, position: _ } => {
1963				lhs.to_string_from_buffer(buf, options, local);
1964				buf.push(',');
1965				rhs.to_string_from_buffer(buf, options, local);
1966			}
1967			MultipleExpression::Single(rhs) => {
1968				rhs.to_string_from_buffer(buf, options, local);
1969			}
1970		}
1971	}
1972
1973	fn get_position(&self) -> Span {
1974		match self {
1975			MultipleExpression::Multiple { position, .. } => *position,
1976			MultipleExpression::Single(expr) => expr.get_position(),
1977		}
1978	}
1979}
1980
1981impl From<Expression> for MultipleExpression {
1982	fn from(expr: Expression) -> Self {
1983		MultipleExpression::Single(expr)
1984	}
1985}
1986
1987/// Determines whether '<' is a comparison or start of generic arguments
1988fn is_generic_arguments(reader: &mut impl TokenReader<TSXToken, crate::TokenStart>) -> bool {
1989	if !matches!(reader.peek(), Some(Token(TSXToken::OpenChevron, _))) {
1990		return false;
1991	}
1992
1993	// Keep a eye on brackets. e.g. for: `if (x<4) {}` should break after the 4
1994	let (mut generic_depth, mut bracket_depth) = (0, 0);
1995	let mut final_generic_position = None::<TokenEnd>;
1996
1997	let next_token = reader.scan(|token, position| {
1998		// Early break if logical operators
1999		if matches!(
2000			token,
2001			TSXToken::StrictEqual
2002				| TSXToken::StrictNotEqual
2003				| TSXToken::LogicalAnd
2004				| TSXToken::LogicalOr
2005				| TSXToken::SemiColon
2006		) {
2007			true
2008		} else {
2009			match token {
2010				TSXToken::OpenChevron => generic_depth += 1,
2011				TSXToken::CloseChevron => generic_depth -= 1,
2012				TSXToken::BitwiseShiftRight => generic_depth -= 2,
2013				TSXToken::BitwiseShiftRightUnsigned => generic_depth -= 3,
2014				TSXToken::OpenParentheses => bracket_depth += 1,
2015				TSXToken::CloseParentheses => bracket_depth -= 1,
2016				_ => {}
2017			}
2018			if generic_depth == 0 {
2019				final_generic_position = Some(TokenEnd::new(
2020					position.0 + tokenizer_lib::sized_tokens::SizedToken::length(token),
2021				));
2022				true
2023			} else {
2024				bracket_depth < 0
2025			}
2026		}
2027	});
2028	if let (Some(last_position), Some(Token(TSXToken::OpenParentheses, open_paren_start))) =
2029		(final_generic_position, next_token)
2030	{
2031		last_position.is_adjacent_to(*open_paren_start)
2032	} else {
2033		false
2034	}
2035}
2036
2037pub(crate) fn arguments_to_string<T: source_map::ToString>(
2038	nodes: &[FunctionArgument],
2039	buf: &mut T,
2040	options: &crate::ToStringOptions,
2041	local: crate::LocalToStringInformation,
2042) {
2043	buf.push('(');
2044	if nodes.is_empty() {
2045		buf.push(')');
2046		return;
2047	}
2048
2049	let add_new_lines = are_nodes_over_length(
2050		nodes.iter(),
2051		options,
2052		local,
2053		Some(u32::from(options.max_line_length).saturating_sub(buf.characters_on_current_line())),
2054		true,
2055	);
2056
2057	if add_new_lines {
2058		buf.push_new_line();
2059		options.add_indent(local.depth + 1, buf);
2060	}
2061	let mut added_last = false;
2062	for node in nodes {
2063		// Hack for arrays, this is just easier for generators and ends up in a smaller output
2064		if let FunctionArgument::Spread(Expression::ArrayLiteral(items, _), _) = node {
2065			if items.is_empty() {
2066				added_last = false;
2067				continue;
2068			}
2069			if added_last {
2070				buf.push(',');
2071				if add_new_lines {
2072					buf.push_new_line();
2073					options.add_indent(local.depth + 1, buf);
2074				} else {
2075					options.push_gap_optionally(buf);
2076				}
2077			}
2078			for (inner_at_end, item) in iterator_endiate::EndiateIteratorExt::endiate(items.iter())
2079			{
2080				if item.0.is_none() {
2081					buf.push_str("undefined");
2082				} else {
2083					item.to_string_from_buffer(buf, options, local);
2084				}
2085				if !inner_at_end {
2086					buf.push(',');
2087					options.push_gap_optionally(buf);
2088				}
2089			}
2090			added_last = true;
2091		} else {
2092			if added_last {
2093				buf.push(',');
2094				if add_new_lines {
2095					buf.push_new_line();
2096					options.add_indent(local.depth + 1, buf);
2097				} else {
2098					options.push_gap_optionally(buf);
2099				}
2100			}
2101			node.to_string_from_buffer(buf, options, local);
2102			added_last = true;
2103		}
2104	}
2105	if add_new_lines {
2106		buf.push_new_line();
2107		options.add_indent(local.depth, buf);
2108	}
2109	buf.push(')');
2110}
2111
2112/// Binary operations whose RHS are types rather than [Expression]s
2113#[apply(derive_ASTNode)]
2114#[derive(PartialEqExtras, Debug, Clone, Visitable)]
2115#[partial_eq_ignore_types(Span)]
2116pub enum SpecialOperators {
2117	/// TS Only
2118	Satisfies {
2119		value: Box<Expression>,
2120		type_annotation: Box<TypeAnnotation>,
2121	},
2122	In {
2123		lhs: InExpressionLHS,
2124		rhs: Box<Expression>,
2125	},
2126	InstanceOf {
2127		lhs: Box<Expression>,
2128		rhs: Box<Expression>,
2129	},
2130	#[cfg(feature = "extras")]
2131	Is {
2132		value: Box<Expression>,
2133		type_annotation: Box<TypeAnnotation>,
2134	},
2135	#[cfg(feature = "full-typescript")]
2136	NonNullAssertion(Box<Expression>),
2137	#[cfg(feature = "full-typescript")]
2138	AsCast {
2139		value: Box<Expression>,
2140		rhs: TypeOrConst,
2141	},
2142}
2143
2144#[cfg(feature = "full-typescript")]
2145#[apply(derive_ASTNode)]
2146#[derive(Debug, Clone, PartialEq, Visitable)]
2147pub enum TypeOrConst {
2148	Type(Box<TypeAnnotation>),
2149	Const(Span),
2150}
2151
2152#[apply(derive_ASTNode)]
2153#[derive(PartialEqExtras, Debug, Clone, Visitable)]
2154#[partial_eq_ignore_types(Span)]
2155pub enum InExpressionLHS {
2156	PrivateProperty(String),
2157	Expression(Box<Expression>),
2158}
2159
2160#[apply(derive_ASTNode)]
2161#[derive(Debug, Clone, PartialEq, Visitable)]
2162pub enum FunctionArgument {
2163	Spread(Expression, Span),
2164	Standard(Expression),
2165	Comment { content: String, is_multiline: bool, position: Span },
2166}
2167
2168impl ListItem for FunctionArgument {
2169	type LAST = ();
2170}
2171
2172impl ASTNode for FunctionArgument {
2173	fn from_reader(
2174		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
2175		state: &mut crate::ParsingState,
2176		options: &ParseOptions,
2177	) -> ParseResult<Self> {
2178		let peek = &reader.peek().ok_or_else(parse_lexing_error)?.0;
2179		match peek {
2180			TSXToken::Spread => {
2181				let start_pos = reader.next().unwrap().1;
2182				let expression = Expression::from_reader(reader, state, options)?;
2183				let position = start_pos.union(expression.get_position());
2184				Ok(Self::Spread(expression, position))
2185			}
2186			t if t.is_comment() => {
2187				let (content, is_multiline, position) =
2188					TSXToken::try_into_comment(reader.next().unwrap()).unwrap();
2189
2190				// Function arguments, JSX interpolated expressions and array elements don't have to have a value
2191				if let Some(Token(
2192					TSXToken::CloseParentheses
2193					| TSXToken::JSXExpressionEnd
2194					| TSXToken::CloseBracket
2195					| TSXToken::Comma,
2196					_,
2197				)) = reader.peek()
2198				{
2199					return Ok(Self::Comment { content, is_multiline, position });
2200				}
2201
2202				let expr = Self::from_reader(reader, state, options)?;
2203				let position = position.union(expr.get_position());
2204
2205				Ok(match expr {
2206					FunctionArgument::Spread(expr, _end) => FunctionArgument::Spread(
2207						Expression::Comment {
2208							content,
2209							on: Box::new(expr),
2210							position,
2211							is_multiline,
2212							prefix: true,
2213						},
2214						position,
2215					),
2216					FunctionArgument::Standard(expr) => {
2217						FunctionArgument::Standard(Expression::Comment {
2218							content,
2219							on: Box::new(expr),
2220							position,
2221							is_multiline,
2222							prefix: true,
2223						})
2224					}
2225					// TODO
2226					c @ FunctionArgument::Comment { .. } => c,
2227				})
2228			}
2229			_ => Ok(Self::Standard(Expression::from_reader(reader, state, options)?)),
2230		}
2231	}
2232
2233	fn to_string_from_buffer<T: source_map::ToString>(
2234		&self,
2235		buf: &mut T,
2236		options: &crate::ToStringOptions,
2237		local: crate::LocalToStringInformation,
2238	) {
2239		match self {
2240			FunctionArgument::Spread(expression, _) => {
2241				buf.push_str("...");
2242				expression.to_string_from_buffer(buf, options, local);
2243			}
2244			FunctionArgument::Standard(expression) => {
2245				expression.to_string_from_buffer(buf, options, local);
2246			}
2247			FunctionArgument::Comment { content, is_multiline: _is_multiline, position: _ } => {
2248				if options.should_add_comment(content) {
2249					buf.push_str("/*");
2250					buf.push_str(content);
2251					buf.push_str("*/");
2252				}
2253			}
2254		}
2255	}
2256
2257	fn get_position(&self) -> Span {
2258		match self {
2259			FunctionArgument::Comment { position, .. } | FunctionArgument::Spread(_, position) => {
2260				*position
2261			}
2262			FunctionArgument::Standard(expr) => expr.get_position(),
2263		}
2264	}
2265}
2266
2267impl From<Expression> for FunctionArgument {
2268	fn from(value: Expression) -> Self {
2269		FunctionArgument::Standard(value)
2270	}
2271}
2272
2273#[derive(Debug, Clone, PartialEq, Visitable)]
2274#[apply(derive_ASTNode)]
2275pub struct ArrayElement(pub Option<FunctionArgument>);
2276
2277impl ASTNode for ArrayElement {
2278	fn get_position(&self) -> Span {
2279		self.0.as_ref().map_or(Span::NULL, ASTNode::get_position)
2280	}
2281
2282	fn from_reader(
2283		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
2284		state: &mut crate::ParsingState,
2285		options: &ParseOptions,
2286	) -> ParseResult<Self> {
2287		Ok(Self(Some(FunctionArgument::from_reader(reader, state, options)?)))
2288	}
2289
2290	fn to_string_from_buffer<T: source_map::ToString>(
2291		&self,
2292		buf: &mut T,
2293		options: &crate::ToStringOptions,
2294		local: crate::LocalToStringInformation,
2295	) {
2296		if let Some(ref s) = self.0 {
2297			s.to_string_from_buffer(buf, options, local);
2298		}
2299	}
2300}
2301
2302impl ArrayElement {
2303	/// For utility purposes! Loses spread information
2304	#[must_use]
2305	pub fn inner_ref(&self) -> Option<&Expression> {
2306		if let Some(ref inner) = self.0 {
2307			match inner {
2308				FunctionArgument::Spread(expr, _) | FunctionArgument::Standard(expr) => Some(expr),
2309				FunctionArgument::Comment { .. } => None,
2310			}
2311		} else {
2312			None
2313		}
2314	}
2315}
2316
2317impl ListItem for ArrayElement {
2318	const EMPTY: Option<Self> = Some(Self(None));
2319
2320	type LAST = ();
2321}
2322
2323// Utils for Expression
2324impl Expression {
2325	/// IIFE = immediate invoked function execution
2326	#[must_use]
2327	pub fn build_iife(block: Block) -> Self {
2328		let position = block.get_position();
2329		Expression::FunctionCall {
2330			function: Expression::ParenthesizedExpression(
2331				Box::new(
2332					Expression::ArrowFunction(ArrowFunction {
2333						// TODO maybe async
2334						header: false,
2335						name: (),
2336						parameters: crate::functions::FunctionParameters {
2337							parameters: Default::default(),
2338							rest_parameter: Default::default(),
2339							position,
2340							leading: (),
2341						},
2342						return_type: None,
2343						type_parameters: None,
2344						position,
2345						body: ExpressionOrBlock::Block(block),
2346					})
2347					.into(),
2348				),
2349				position,
2350			)
2351			.into(),
2352			type_arguments: None,
2353			arguments: Vec::new(),
2354			is_optional: false,
2355			position,
2356		}
2357	}
2358
2359	#[must_use]
2360	pub fn is_iife(&self) -> Option<&ExpressionOrBlock> {
2361		if let Expression::FunctionCall { arguments, function, .. } = self {
2362			if let (true, Expression::ParenthesizedExpression(expression, _)) =
2363				(arguments.is_empty(), &**function)
2364			{
2365				if let MultipleExpression::Single(Expression::ArrowFunction(function)) =
2366					&**expression
2367				{
2368					return Some(&function.body);
2369				}
2370			}
2371		}
2372		None
2373	}
2374
2375	/// Recurses to find first non parenthesized expression
2376	#[must_use]
2377	pub fn get_non_parenthesized(&self) -> &Self {
2378		if let Expression::ParenthesizedExpression(inner_multiple_expr, _) = self {
2379			if let MultipleExpression::Single(expr) = &**inner_multiple_expr {
2380				expr.get_non_parenthesized()
2381			} else {
2382				// TODO could return a variant here...
2383				self
2384			}
2385		} else if let Expression::Comment { on, .. } = self {
2386			on.get_non_parenthesized()
2387		} else {
2388			self
2389		}
2390	}
2391}
2392
2393/// "super" cannot be used alone
2394#[apply(derive_ASTNode)]
2395#[derive(PartialEqExtras, Debug, Clone, Visitable)]
2396#[partial_eq_ignore_types(Span)]
2397pub enum SuperReference {
2398	Call { arguments: Vec<FunctionArgument> },
2399	PropertyAccess { property: String },
2400	Index { indexer: Box<Expression> },
2401}
2402
2403pub(crate) fn chain_to_string_from_buffer<T: source_map::ToString>(
2404	original: &Expression,
2405	buf: &mut T,
2406	options: &crate::ToStringOptions,
2407	local: crate::LocalToStringInformation,
2408) {
2409	let mut chain = Vec::new();
2410
2411	let split_between_lines = if options.enforce_limit_length_limit() {
2412		let room =
2413			u32::from(options.max_line_length).saturating_sub(buf.characters_on_current_line());
2414
2415		let mut buf = source_map::StringWithOptionalSourceMap {
2416			source: String::new(),
2417			source_map: None,
2418			quit_after: Some(room as usize),
2419			since_new_line: 0,
2420		};
2421		let mut over = false;
2422		let mut cur = Some(original);
2423		while let Some(node) = cur {
2424			chain.push(node);
2425			// Just measure the link in change (not the parent)
2426			cur = match node {
2427				Expression::PropertyAccess { parent, property, .. } => {
2428					match property {
2429						PropertyReference::Standard { property, .. } => buf.push_str(property),
2430						PropertyReference::Marker(_) => {}
2431					}
2432					Some(parent)
2433				}
2434				Expression::Index { indexer, indexee, .. } => {
2435					indexer.to_string_from_buffer(&mut buf, options, local);
2436					Some(indexee)
2437				}
2438				Expression::FunctionCall { function, type_arguments, arguments, .. } => {
2439					if let (true, Some(type_arguments)) =
2440						(options.include_type_annotations, type_arguments)
2441					{
2442						to_string_bracketed(type_arguments, ('<', '>'), &mut buf, options, local);
2443					}
2444					arguments_to_string(arguments, &mut buf, options, local);
2445					Some(function)
2446				}
2447				expression => {
2448					expression.to_string_from_buffer(&mut buf, options, local);
2449					None
2450				}
2451			};
2452
2453			if buf.should_halt() {
2454				over = true;
2455				// Continue to build chain
2456			}
2457		}
2458		over
2459	} else {
2460		false
2461	};
2462
2463	if split_between_lines && !chain.is_empty() {
2464		let mut items = chain.into_iter().rev();
2465		items.next().unwrap().to_string_from_buffer(buf, options, local);
2466
2467		for item in items {
2468			// Just measure the link in change (not the parent)
2469			match item {
2470				Expression::PropertyAccess { property, is_optional, .. } => {
2471					buf.push_new_line();
2472					options.add_indent(local.depth + 1, buf);
2473					if *is_optional {
2474						buf.push_str("?.");
2475					} else {
2476						buf.push('.');
2477					}
2478					match property {
2479						PropertyReference::Standard { property, is_private } => {
2480							if *is_private {
2481								buf.push('#');
2482							}
2483							buf.push_str(property);
2484						}
2485						PropertyReference::Marker(..) => {
2486							assert!(options.expect_markers, "found marker");
2487						}
2488					}
2489				}
2490				Expression::Index { indexer, is_optional, .. } => {
2491					buf.push_new_line();
2492					options.add_indent(local.depth + 1, buf);
2493					if *is_optional {
2494						buf.push_str("?.");
2495					}
2496					buf.push('[');
2497					indexer.to_string_from_buffer(buf, options, local);
2498					buf.push(']');
2499				}
2500				Expression::FunctionCall { type_arguments, arguments, is_optional, .. } => {
2501					if *is_optional {
2502						buf.push_str("?.");
2503					}
2504					if let (true, Some(type_arguments)) =
2505						(options.include_type_annotations, type_arguments)
2506					{
2507						to_string_bracketed(type_arguments, ('<', '>'), buf, options, local);
2508					}
2509					arguments_to_string(arguments, buf, options, local);
2510				}
2511				_ => unreachable!(),
2512			}
2513		}
2514	} else {
2515		original.to_string_from_buffer(buf, options, local.do_not_pretty_print());
2516	}
2517}
2518
2519#[cfg(test)]
2520mod tests {
2521	use super::{ASTNode, BinaryOperator, Expression, Expression::*, MultipleExpression};
2522	use crate::{
2523		assert_matches_ast, ast::FunctionArgument, number::NumberRepresentation, span, Quoted,
2524	};
2525
2526	#[test]
2527	fn literal() {
2528		assert_matches_ast!(
2529			"'string'",
2530			StringLiteral(Deref @ "string", Quoted::Single, span!(0, 8))
2531		);
2532		assert_matches_ast!(
2533			"\"string\"",
2534			StringLiteral(Deref @ "string", Quoted::Double, span!(0, 8))
2535		);
2536		// TODO different method
2537		// assert_matches_ast!("45", NumberLiteral(NumberStructure::Number(45.0), span!(0, 2)));
2538		// assert_matches_ast!("45.63", NumberLiteral(NumberStructure::Number(45.63), span!(0, 5)));
2539		assert_matches_ast!("true", BooleanLiteral(true, span!(0, 4)));
2540	}
2541
2542	#[test]
2543	fn parenthesized_expression() {
2544		// Can't match 45 here
2545		assert_matches_ast!(
2546			"(45)",
2547			ParenthesizedExpression(
2548				Deref @ MultipleExpression::Single(NumberLiteral(
2549					NumberRepresentation::Number { .. },
2550					span!(1, 3),
2551				)),
2552				span!(0, 4),
2553			)
2554		);
2555	}
2556
2557	#[test]
2558	fn is_iife() {
2559		let expr = Expression::from_string("(() => 2)()".to_owned(), Default::default()).unwrap();
2560		assert!(expr.is_iife().is_some());
2561	}
2562
2563	#[test]
2564	fn multiple_expression() {
2565		assert_matches_ast!(
2566			"(45,2)",
2567			ParenthesizedExpression(
2568				Deref @ MultipleExpression::Multiple {
2569					lhs:
2570						Deref @ MultipleExpression::Single(NumberLiteral(
2571							NumberRepresentation::Number { .. },
2572							span!(1, 3),
2573						)),
2574					rhs: NumberLiteral(NumberRepresentation::Number { .. }, span!(4, 5)),
2575					position: _,
2576				},
2577				span!(0, 6),
2578			)
2579		);
2580	}
2581
2582	#[test]
2583	fn spread_function_argument() {
2584		assert_matches_ast!(
2585			"console.table(...a)",
2586			FunctionCall { arguments: Deref @ [FunctionArgument::Spread(VariableReference(..), span!(14, 18))], .. }
2587		);
2588	}
2589
2590	#[test]
2591	fn binary_expressions() {
2592		assert_matches_ast!("2 + 3", BinaryOperation {
2593			lhs: Deref @ NumberLiteral(NumberRepresentation::Number { .. }, span!(0, 1)),
2594			operator: BinaryOperator::Add,
2595			rhs: Deref @ NumberLiteral(NumberRepresentation::Number { .. }, span!(4, 5)),
2596			position: _
2597		});
2598		assert_matches_ast!("xt === 3", BinaryOperation {
2599			lhs: Deref @ VariableReference(..),
2600			operator: BinaryOperator::StrictEqual,
2601			rhs: Deref @ NumberLiteral(NumberRepresentation::Number { .. }, span!(7, 8)),
2602			position: _
2603		});
2604		assert_matches_ast!("x << 3", BinaryOperation {
2605			lhs: Deref @ VariableReference(..),
2606			operator: BinaryOperator::BitwiseShiftLeft,
2607			rhs: Deref @ NumberLiteral(NumberRepresentation::Number { .. }, span!(5, 6)),
2608			position: _
2609		});
2610		assert_matches_ast!("x >> 3", BinaryOperation {
2611			lhs: Deref @ VariableReference(..),
2612			operator: BinaryOperator::BitwiseShiftRight,
2613			rhs: Deref @ NumberLiteral(NumberRepresentation::Number { .. }, span!(5, 6)),
2614			position: _
2615		});
2616		assert_matches_ast!("x >>> 3", BinaryOperation {
2617			lhs: Deref @ VariableReference(..),
2618			operator: BinaryOperator::BitwiseShiftRightUnsigned,
2619			rhs: Deref @ NumberLiteral(NumberRepresentation::Number { .. }, span!(6, 7)),
2620			position: _
2621		});
2622	}
2623}