Skip to main content

ezno_parser/extensions/
decorators.rs

1use get_field_by_type::GetFieldByType;
2use iterator_endiate::EndiateIteratorExt;
3use source_map::Span;
4use tokenizer_lib::{
5	sized_tokens::{TokenReaderWithTokenEnds, TokenStart},
6	Token, TokenReader,
7};
8use visitable_derive::Visitable;
9
10use crate::{
11	derive_ASTNode, tokens::token_as_identifier, ASTNode, Expression, ParseOptions, ParseResult,
12	TSXToken, Visitable,
13};
14
15#[derive(Debug, PartialEq, Clone, Visitable)]
16#[apply(derive_ASTNode)]
17pub struct Decorator {
18	pub name: Vec<String>,
19	pub arguments: Option<Vec<Expression>>,
20	pub position: Span,
21}
22
23impl ASTNode for Decorator {
24	fn get_position(&self) -> Span {
25		self.position
26	}
27
28	fn from_reader(
29		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
30		state: &mut crate::ParsingState,
31		options: &ParseOptions,
32	) -> ParseResult<Self> {
33		let at_pos = reader.expect_next(TSXToken::At)?;
34		Self::from_reader_sub_at_symbol(reader, state, options, at_pos)
35	}
36
37	fn to_string_from_buffer<T: source_map::ToString>(
38		&self,
39		buf: &mut T,
40		options: &crate::ToStringOptions,
41		local: crate::LocalToStringInformation,
42	) {
43		if options.include_decorators {
44			buf.push('@');
45			for (not_at_end, value) in self.name.iter().nendiate() {
46				buf.push_str(value);
47				if not_at_end {
48					buf.push('.');
49				}
50			}
51			if let Some(arguments) = &self.arguments {
52				buf.push('(');
53				for (at_end, argument) in arguments.iter().endiate() {
54					argument.to_string_from_buffer(buf, options, local);
55					if !at_end {
56						buf.push(',');
57						options.push_gap_optionally(buf);
58					}
59				}
60				buf.push(')');
61			}
62		}
63	}
64}
65
66impl Decorator {
67	pub(crate) fn from_reader_sub_at_symbol(
68		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
69		state: &mut crate::ParsingState,
70		options: &ParseOptions,
71		at_pos: TokenStart,
72	) -> ParseResult<Self> {
73		let (name, mut last_position) =
74			token_as_identifier(reader.next().unwrap(), "Decorator name")?;
75
76		let mut names = vec![name];
77		while matches!(reader.peek(), Some(Token(TSXToken::Dot, _))) {
78			let (name, pos) = token_as_identifier(reader.next().unwrap(), "Nested decorator name")?;
79			last_position = pos;
80			names.push(name);
81		}
82
83		let (arguments, position) = if reader
84			.conditional_next(|token| matches!(token, TSXToken::OpenParentheses))
85			.is_some()
86		{
87			let mut arguments = Vec::<_>::new();
88			loop {
89				if let Some(Token(TSXToken::CloseParentheses, _)) = reader.peek() {
90					break;
91				}
92				arguments.push(Expression::from_reader(reader, state, options)?);
93				match reader.peek() {
94					Some(Token(TSXToken::Comma, _)) => {
95						reader.next();
96					}
97					_ => break,
98				}
99			}
100			let end = reader.expect_next_get_end(TSXToken::CloseParentheses)?;
101			(Some(arguments), at_pos.union(end))
102		} else {
103			(None, at_pos.union(last_position))
104		};
105		Ok(Self { name: names, arguments, position })
106	}
107}
108
109/// TODO under cfg if don't want this could just be `type Decorated<T> = T;`
110#[apply(derive_ASTNode)]
111#[derive(Debug, PartialEq, Clone, get_field_by_type::GetFieldByType)]
112#[get_field_by_type_target(Span)]
113pub struct Decorated<T> {
114	pub decorators: Vec<Decorator>,
115	pub on: T,
116	// TODO option and on t
117	pub position: Span,
118}
119
120impl<N: ASTNode> ASTNode for Decorated<N> {
121	fn get_position(&self) -> Span {
122		*self.get()
123	}
124
125	fn from_reader(
126		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
127		state: &mut crate::ParsingState,
128		options: &ParseOptions,
129	) -> ParseResult<Self> {
130		let decorators = decorators_from_reader(reader, state, options)?;
131		N::from_reader(reader, state, options).map(|on| Self::new(decorators, on))
132	}
133
134	fn to_string_from_buffer<T: source_map::ToString>(
135		&self,
136		buf: &mut T,
137		options: &crate::ToStringOptions,
138		local: crate::LocalToStringInformation,
139	) {
140		self.to_string_from_buffer_just_decorators(buf, options, local);
141		self.on.to_string_from_buffer(buf, options, local);
142	}
143}
144
145impl<U: ASTNode> Decorated<U> {
146	pub fn new_empty(on: U) -> Self {
147		Self::new(Default::default(), on)
148	}
149
150	pub fn new(decorators: Vec<Decorator>, on: U) -> Self {
151		let position =
152			decorators.first().map_or(on.get_position(), |d| d.position).union(on.get_position());
153		Self { decorators, on, position }
154	}
155
156	pub(crate) fn to_string_from_buffer_just_decorators<T: source_map::ToString>(
157		&self,
158		buf: &mut T,
159		options: &crate::ToStringOptions,
160		local: crate::LocalToStringInformation,
161	) {
162		if options.include_decorators {
163			for decorator in &self.decorators {
164				decorator.to_string_from_buffer(buf, options, local);
165				if options.pretty {
166					buf.push_new_line();
167				} else {
168					buf.push(' ');
169				}
170			}
171		}
172	}
173}
174
175pub(crate) fn decorators_from_reader(
176	reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
177	state: &mut crate::ParsingState,
178	options: &ParseOptions,
179) -> ParseResult<Vec<Decorator>> {
180	let mut decorators = Vec::new();
181	while let Some(Token(TSXToken::At, _)) = reader.peek() {
182		decorators.push(Decorator::from_reader(reader, state, options)?);
183	}
184	Ok(decorators)
185}
186
187impl<T: Visitable> Visitable for Decorated<T> {
188	fn visit<TData>(
189		&self,
190		visitors: &mut (impl crate::VisitorReceiver<TData> + ?Sized),
191		data: &mut TData,
192		options: &crate::VisitOptions,
193
194		chain: &mut temporary_annex::Annex<crate::Chain>,
195	) {
196		self.on.visit(visitors, data, options, chain);
197	}
198
199	fn visit_mut<TData>(
200		&mut self,
201		visitors: &mut (impl crate::VisitorMutReceiver<TData> + ?Sized),
202		data: &mut TData,
203		options: &crate::VisitOptions,
204
205		chain: &mut temporary_annex::Annex<crate::Chain>,
206	) {
207		self.on.visit_mut(visitors, data, options, chain);
208	}
209}