ezno_parser/functions/
parameters.rs

1use std::fmt::Debug;
2
3use crate::{
4	derive_ASTNode, ASTNode, Expression, ParseError, ParseErrors, ParseResult, TSXKeyword,
5	TSXToken, TypeAnnotation, VariableField, WithComment,
6};
7
8use derive_partial_eq_extras::PartialEqExtras;
9use iterator_endiate::EndiateIteratorExt;
10use source_map::Span;
11use tokenizer_lib::{
12	sized_tokens::{TokenReaderWithTokenEnds, TokenStart},
13	Token, TokenReader,
14};
15use visitable_derive::Visitable;
16
17#[apply(derive_ASTNode)]
18#[derive(Debug, Clone, PartialEq, Visitable, get_field_by_type::GetFieldByType)]
19#[get_field_by_type_target(Span)]
20pub struct Parameter<V> {
21	#[visit_skip_field]
22	pub visibility: V,
23	pub name: WithComment<VariableField>,
24	pub type_annotation: Option<TypeAnnotation>,
25	pub additionally: Option<ParameterData>,
26	pub position: Span,
27}
28
29pub trait ParameterVisibility: Send + Sync + Sized + Debug + PartialEq + Clone + 'static {
30	fn from_reader(
31		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
32		state: &mut crate::ParsingState,
33		options: &crate::ParseOptions,
34	) -> Self;
35}
36
37impl ParameterVisibility for () {
38	fn from_reader(
39		_: &mut impl TokenReader<TSXToken, crate::TokenStart>,
40		_: &mut crate::ParsingState,
41		_: &crate::ParseOptions,
42	) -> Self {
43	}
44}
45
46impl ParameterVisibility for Option<crate::types::Visibility> {
47	fn from_reader(
48		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
49		_: &mut crate::ParsingState,
50		options: &crate::ParseOptions,
51	) -> Option<crate::types::Visibility> {
52		if !options.type_annotations {
53			None
54		} else if let Some(Token(TSXToken::Keyword(t), _)) =
55			reader.conditional_next(crate::types::Visibility::token_is_visibility_specifier)
56		{
57			Some(match t {
58				TSXKeyword::Private => crate::types::Visibility::Private,
59				TSXKeyword::Public => crate::types::Visibility::Public,
60				TSXKeyword::Protected => crate::types::Visibility::Protected,
61				_ => unreachable!(),
62			})
63		} else {
64			None
65		}
66	}
67}
68
69#[derive(Debug, Clone, PartialEq, Visitable)]
70#[apply(derive_ASTNode)]
71pub enum ParameterData {
72	Optional,
73	WithDefaultValue(Box<Expression>),
74}
75
76#[cfg(feature = "extras")]
77#[cfg_attr(target_family = "wasm", tsify::declare)]
78pub type SpreadParameterName = VariableField;
79
80#[cfg(not(feature = "extras"))]
81#[cfg_attr(target_family = "wasm", tsify::declare)]
82pub type SpreadParameterName = crate::VariableIdentifier;
83
84#[apply(derive_ASTNode)]
85#[derive(Debug, Clone, PartialEq, Visitable)]
86pub struct SpreadParameter {
87	pub name: SpreadParameterName,
88	pub type_annotation: Option<TypeAnnotation>,
89	pub position: Span,
90}
91
92#[apply(derive_ASTNode)]
93#[derive(Debug, Clone, PartialEqExtras, Visitable)]
94#[partial_eq_ignore_types(Span)]
95pub struct FunctionParameters<L, V> {
96	#[visit_skip_field]
97	pub leading: L,
98	pub parameters: Vec<Parameter<V>>,
99	pub rest_parameter: Option<Box<SpreadParameter>>,
100	pub position: Span,
101}
102
103pub trait LeadingParameter: Send + Sync + Sized + Debug + PartialEq + Clone + 'static {
104	fn try_make(
105		this_annotation: Option<ThisParameter>,
106		super_annotation: Option<SuperParameter>,
107	) -> ParseResult<Self>;
108
109	fn get_this_parameter(&self) -> Option<&ThisParameter>;
110	fn get_super_parameter(&self) -> Option<&SuperParameter>;
111}
112
113#[apply(derive_ASTNode)]
114#[derive(Debug, Clone, PartialEqExtras, Visitable)]
115#[partial_eq_ignore_types(Span)]
116pub struct ThisParameter {
117	pub constraint: TypeAnnotation,
118	pub position: Span,
119}
120
121/// TODO WIP!
122#[apply(derive_ASTNode)]
123#[derive(Debug, Clone, PartialEqExtras, Visitable)]
124#[partial_eq_ignore_types(Span)]
125pub struct SuperParameter {
126	pub constraint: TypeAnnotation,
127	pub position: Span,
128}
129
130impl LeadingParameter for () {
131	fn try_make(
132		this_annotation: Option<ThisParameter>,
133		super_annotation: Option<SuperParameter>,
134	) -> ParseResult<Self> {
135		if this_annotation.is_some() || super_annotation.is_some() {
136			let position =
137				this_annotation.map_or(super_annotation.unwrap().position, |a| a.position);
138
139			Err(ParseError::new(ParseErrors::CannotUseLeadingParameterHere, position))
140		} else {
141			Ok(())
142		}
143	}
144
145	fn get_this_parameter(&self) -> Option<&ThisParameter> {
146		None
147	}
148	fn get_super_parameter(&self) -> Option<&SuperParameter> {
149		None
150	}
151}
152
153impl LeadingParameter for Option<ThisParameter> {
154	fn try_make(
155		this_annotation: Option<ThisParameter>,
156		super_annotation: Option<SuperParameter>,
157	) -> ParseResult<Self> {
158		if let Some(s) = super_annotation {
159			Err(ParseError::new(ParseErrors::CannotUseLeadingParameterHere, s.position))
160		} else {
161			Ok(this_annotation)
162		}
163	}
164
165	fn get_this_parameter(&self) -> Option<&ThisParameter> {
166		self.as_ref()
167	}
168	fn get_super_parameter(&self) -> Option<&SuperParameter> {
169		None
170	}
171}
172
173impl LeadingParameter for (Option<ThisParameter>, Option<SuperParameter>) {
174	fn try_make(
175		this_annotation: Option<ThisParameter>,
176		super_annotation: Option<SuperParameter>,
177	) -> ParseResult<Self> {
178		Ok((this_annotation, super_annotation))
179	}
180
181	fn get_this_parameter(&self) -> Option<&ThisParameter> {
182		self.0.as_ref()
183	}
184	fn get_super_parameter(&self) -> Option<&SuperParameter> {
185		self.1.as_ref()
186	}
187}
188
189impl<L, V> ASTNode for FunctionParameters<L, V>
190where
191	L: LeadingParameter,
192	V: ParameterVisibility,
193{
194	fn get_position(&self) -> Span {
195		self.position
196	}
197
198	fn from_reader(
199		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
200		state: &mut crate::ParsingState,
201		options: &crate::ParseOptions,
202	) -> ParseResult<Self> {
203		let open_paren_span = reader.expect_next(TSXToken::OpenParentheses)?;
204		Self::from_reader_sub_open_parenthesis(reader, state, options, open_paren_span)
205	}
206
207	fn to_string_from_buffer<T: source_map::ToString>(
208		&self,
209		buf: &mut T,
210		options: &crate::ToStringOptions,
211		local: crate::LocalToStringInformation,
212	) {
213		let FunctionParameters { parameters, rest_parameter, .. } = self;
214		let mut large = false;
215		if options.enforce_limit_length_limit() && local.should_try_pretty_print {
216			let room = options.max_line_length as usize;
217			let mut buf = source_map::StringWithOptionalSourceMap {
218				source: String::new(),
219				source_map: None,
220				quit_after: Some(room),
221				since_new_line: 0,
222			};
223			// Not particularly accurate but does sort of work
224			for parameter in parameters {
225				parameter.name.to_string_from_buffer(&mut buf, options, local);
226				let type_annotation = parameter.type_annotation.as_ref();
227				type_annotation.inspect(|v| v.to_string_from_buffer(&mut buf, options, local));
228				if let Some(ParameterData::WithDefaultValue(ref value)) = parameter.additionally {
229					value.to_string_from_buffer(&mut buf, options, local);
230				}
231				large = buf.source.len() > room;
232				if large {
233					break;
234				}
235			}
236			if let Some(rest_parameter) = rest_parameter {
237				rest_parameter.name.to_string_from_buffer(&mut buf, options, local);
238				let type_annotation = rest_parameter.type_annotation.as_ref();
239				type_annotation.inspect(|v| v.to_string_from_buffer(&mut buf, options, local));
240				large = buf.source.len() > room;
241			}
242		}
243
244		let inner_local = if large { local.next_level() } else { local };
245
246		buf.push('(');
247		// let local = if large { local.next_level() } else { local };
248		for (at_end, Parameter { name, type_annotation, additionally, .. }) in
249			parameters.iter().endiate()
250		{
251			if large {
252				buf.push_new_line();
253				options.add_indent(inner_local.depth, buf);
254			}
255			// decorators_to_string_from_buffer(decorators, buf, options, inner_local);
256			name.to_string_from_buffer(buf, options, inner_local);
257			if let (true, Some(ref type_annotation)) =
258				(options.include_type_annotations, type_annotation)
259			{
260				if let Some(ParameterData::Optional) = additionally {
261					buf.push('?');
262				}
263				buf.push_str(": ");
264				type_annotation.to_string_from_buffer(buf, options, inner_local);
265			}
266			if let Some(ParameterData::WithDefaultValue(value)) = additionally {
267				buf.push_str(if options.pretty { " = " } else { "=" });
268				value.to_string_from_buffer(buf, options, inner_local);
269			}
270			if !at_end || rest_parameter.is_some() {
271				buf.push(',');
272				options.push_gap_optionally(buf);
273			}
274		}
275		if let Some(rest_parameter) = rest_parameter {
276			if large {
277				buf.push_new_line();
278				options.add_indent(inner_local.depth, buf);
279			}
280			buf.push_str("...");
281			rest_parameter.name.to_string_from_buffer(buf, options, inner_local);
282			if let Some(ref type_annotation) = rest_parameter.type_annotation {
283				buf.push_str(": ");
284				type_annotation.to_string_from_buffer(buf, options, inner_local);
285			}
286		}
287		if large {
288			buf.push_new_line();
289			options.add_indent(local.depth, buf);
290		}
291		buf.push(')');
292	}
293}
294
295impl<L, V> FunctionParameters<L, V>
296where
297	L: LeadingParameter,
298	V: ParameterVisibility,
299{
300	pub(crate) fn from_reader_sub_open_parenthesis(
301		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
302		state: &mut crate::ParsingState,
303		options: &crate::ParseOptions,
304		start: TokenStart,
305	) -> ParseResult<Self> {
306		let mut parameters = Vec::new();
307
308		let mut this_type = None::<ThisParameter>;
309		let mut super_type = None::<SuperParameter>;
310		let mut rest_parameter = None;
311
312		loop {
313			if let Some(Token(TSXToken::CloseParentheses, _)) = reader.peek() {
314				break;
315			}
316			// Skip comments
317			while reader.conditional_next(TSXToken::is_comment).is_some() {}
318
319			if let Some(Token(_, spread_pos)) =
320				reader.conditional_next(|tok| matches!(tok, TSXToken::Spread))
321			{
322				let name = SpreadParameterName::from_reader(reader, state, options)?;
323				let name_position = name.get_position();
324
325				let type_annotation = if options.type_annotations
326					&& reader.conditional_next(|tok| matches!(tok, TSXToken::Colon)).is_some()
327				{
328					Some(TypeAnnotation::from_reader(reader, state, options)?)
329				} else {
330					None
331				};
332
333				let position = spread_pos
334					.union(type_annotation.as_ref().map_or(name_position, ASTNode::get_position));
335
336				rest_parameter =
337					Some(Box::new(SpreadParameter { name, type_annotation, position }));
338				break;
339			} else if let Some(Token(_, start)) = reader.conditional_next(|tok| {
340				options.type_annotations
341					&& parameters.is_empty()
342					&& matches!(tok, TSXToken::Keyword(TSXKeyword::This))
343			}) {
344				reader.expect_next(TSXToken::Colon)?;
345				let constraint = TypeAnnotation::from_reader(reader, state, options)?;
346				let position = start.union(constraint.get_position());
347				this_type = Some(ThisParameter { constraint, position });
348			} else if let Some(Token(_, start)) = reader.conditional_next(|tok| {
349				options.type_annotations
350					&& parameters.is_empty()
351					&& matches!(tok, TSXToken::Keyword(TSXKeyword::Super))
352			}) {
353				reader.expect_next(TSXToken::Colon)?;
354				let constraint = TypeAnnotation::from_reader(reader, state, options)?;
355				let position = start.union(constraint.get_position());
356				super_type = Some(SuperParameter { constraint, position });
357			} else {
358				let visibility = V::from_reader(reader, state, options);
359
360				let name = WithComment::<VariableField>::from_reader(reader, state, options)?;
361
362				let (is_optional, type_annotation) = match reader.peek() {
363					Some(Token(TSXToken::Colon, _)) if options.type_annotations => {
364						reader.next();
365						let type_annotation = TypeAnnotation::from_reader(reader, state, options)?;
366						(false, Some(type_annotation))
367					}
368					Some(Token(TSXToken::OptionalMember, _)) if options.type_annotations => {
369						reader.next();
370						let type_annotation = TypeAnnotation::from_reader(reader, state, options)?;
371						(true, Some(type_annotation))
372					}
373					Some(Token(TSXToken::QuestionMark, _)) => {
374						let Token(_, _) = reader.next().unwrap();
375						(true, None)
376					}
377					_ => (false, None),
378				};
379
380				let value = if let Some(token) =
381					reader.conditional_next(|tok| matches!(tok, TSXToken::Assign))
382				{
383					if is_optional {
384						return Err(ParseError::new(
385							crate::ParseErrors::FunctionParameterOptionalAndDefaultValue,
386							token.get_span(),
387						));
388					}
389					Some(Box::new(Expression::from_reader(reader, state, options)?))
390				} else {
391					None
392				};
393
394				let additionally = match (is_optional, value) {
395					(true, Some(_)) => unreachable!("caught earlier by error"),
396					// =
397					(false, Some(value)) => Some(ParameterData::WithDefaultValue(value)),
398					// ?:
399					(true, None) => Some(ParameterData::Optional),
400					(false, None) => None,
401				};
402
403				let end_position = if let Some(ParameterData::WithDefaultValue(e)) = &additionally {
404					e.get_position()
405				} else if let Some(type_annotation) = &type_annotation {
406					type_annotation.get_position()
407				} else {
408					name.get_position()
409				};
410
411				parameters.push(Parameter {
412					visibility,
413					position: name.get_position().union(end_position),
414					name,
415					type_annotation,
416					additionally,
417				});
418			}
419			if let Some(Token(TSXToken::Comma, _)) = reader.peek() {
420				reader.next();
421			} else {
422				break;
423			}
424		}
425
426		let close = reader.expect_next_get_end(TSXToken::CloseParentheses)?;
427
428		let leading = L::try_make(this_type, super_type)?;
429
430		Ok(FunctionParameters { position: start.union(close), parameters, rest_parameter, leading })
431	}
432}