1use super::core::Lexer;
20use super::core::WordContext;
21use super::core::WordLexer;
22use crate::parser::core::Result;
23use crate::parser::error::Error;
24use crate::parser::error::SyntaxError;
25use crate::syntax::Modifier;
26use crate::syntax::Switch;
27use crate::syntax::SwitchCondition;
28use crate::syntax::SwitchType;
29use crate::syntax::Trim;
30use crate::syntax::TrimLength;
31use crate::syntax::TrimSide;
32
33impl Lexer<'_> {
34 fn invalid_modifier(&mut self, start_index: usize) -> Result<Modifier> {
38 let cause = SyntaxError::InvalidModifier.into();
39 let location = self.location_range(start_index..self.index());
40 Err(Error { cause, location })
41 }
42
43 fn suffix_modifier_not_found(&mut self, start_index: usize, colon: bool) -> Result<Modifier> {
44 if colon {
45 self.invalid_modifier(start_index)
46 } else {
47 Ok(Modifier::None)
48 }
49 }
50
51 async fn trim(&mut self, start_index: usize, colon: bool, symbol: char) -> Result<Modifier> {
56 self.consume_char();
57 if colon {
58 return self.invalid_modifier(start_index);
59 }
60
61 let side = match symbol {
62 '#' => TrimSide::Prefix,
63 '%' => TrimSide::Suffix,
64 _ => unreachable!(),
65 };
66
67 let length = if self.skip_if(|c| c == symbol).await? {
68 TrimLength::Longest
69 } else {
70 TrimLength::Shortest
71 };
72
73 let mut lexer = WordLexer {
74 lexer: self,
75 context: WordContext::Word,
76 };
77 let mut pattern = Box::pin(lexer.word(|c| c == '}')).await?;
79 pattern.parse_tilde_front();
80
81 Ok(Modifier::Trim(Trim {
82 side,
83 length,
84 pattern,
85 }))
86 }
87}
88
89impl WordLexer<'_, '_> {
90 async fn switch(&mut self, colon: bool, symbol: char) -> Result<Modifier> {
95 self.consume_char();
96 let r#type = match symbol {
97 '+' => SwitchType::Alter,
98 '-' => SwitchType::Default,
99 '=' => SwitchType::Assign,
100 '?' => SwitchType::Error,
101 _ => unreachable!(),
102 };
103
104 let condition = if colon {
105 SwitchCondition::UnsetOrEmpty
106 } else {
107 SwitchCondition::Unset
108 };
109
110 let mut word = Box::pin(self.word(|c| c == '}')).await?;
112 match self.context {
113 WordContext::Text => (),
114 WordContext::Word => word.parse_tilde_front(),
115 }
116
117 Ok(Modifier::Switch(Switch {
118 r#type,
119 condition,
120 word,
121 }))
122 }
123
124 pub async fn suffix_modifier(&mut self) -> Result<Modifier> {
133 let start_index = self.index();
134 let colon = self.skip_if(|c| c == ':').await?;
135
136 if let Some(symbol) = self.peek_char().await? {
137 match symbol {
138 '+' | '-' | '=' | '?' => self.switch(colon, symbol).await,
139 '#' | '%' => self.trim(start_index, colon, symbol).await,
140 _ => self.suffix_modifier_not_found(start_index, colon),
141 }
142 } else {
143 self.suffix_modifier_not_found(start_index, colon)
144 }
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use crate::parser::error::ErrorCause;
152 use crate::source::Source;
153 use crate::syntax::Text;
154 use crate::syntax::TextUnit;
155 use crate::syntax::WordUnit;
156 use assert_matches::assert_matches;
157 use futures_util::FutureExt;
158
159 #[test]
160 fn lexer_suffix_modifier_eof() {
161 let mut lexer = Lexer::from_memory("", Source::Unknown);
162 let mut lexer = WordLexer {
163 lexer: &mut lexer,
164 context: WordContext::Word,
165 };
166
167 let result = lexer.suffix_modifier().now_or_never().unwrap();
168 assert_eq!(result, Ok(Modifier::None));
169 }
170
171 #[test]
172 fn lexer_suffix_modifier_none() {
173 let mut lexer = Lexer::from_memory("}", Source::Unknown);
174 let mut lexer = WordLexer {
175 lexer: &mut lexer,
176 context: WordContext::Word,
177 };
178
179 let result = lexer.suffix_modifier().now_or_never().unwrap();
180 assert_eq!(result, Ok(Modifier::None));
181
182 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
183 }
184
185 #[test]
186 fn lexer_suffix_modifier_alter_empty() {
187 let mut lexer = Lexer::from_memory("+}", Source::Unknown);
188 let mut lexer = WordLexer {
189 lexer: &mut lexer,
190 context: WordContext::Word,
191 };
192
193 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
194 assert_matches!(result, Modifier::Switch(switch) => {
195 assert_eq!(switch.r#type, SwitchType::Alter);
196 assert_eq!(switch.condition, SwitchCondition::Unset);
197 assert_eq!(switch.word.units, []);
198 assert_eq!(*switch.word.location.code.value.borrow(), "+}");
199 assert_eq!(switch.word.location.range, 1..1);
200 });
201
202 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
203 }
204
205 #[test]
206 fn lexer_suffix_modifier_alter_word() {
207 let mut lexer = Lexer::from_memory(r"+a z}", Source::Unknown);
208 let mut lexer = WordLexer {
209 lexer: &mut lexer,
210 context: WordContext::Word,
211 };
212
213 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
214 assert_matches!(result, Modifier::Switch(switch) => {
215 assert_eq!(switch.r#type, SwitchType::Alter);
216 assert_eq!(switch.condition, SwitchCondition::Unset);
217 assert_eq!(
218 switch.word.units,
219 [
220 WordUnit::Unquoted(TextUnit::Literal('a')),
221 WordUnit::Unquoted(TextUnit::Literal(' ')),
222 WordUnit::Unquoted(TextUnit::Literal(' ')),
223 WordUnit::Unquoted(TextUnit::Literal('z')),
224 ]
225 );
226 assert_eq!(*switch.word.location.code.value.borrow(), "+a z}");
227 assert_eq!(switch.word.location.range, 1..5);
228 });
229
230 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
231 }
232
233 #[test]
234 fn lexer_suffix_modifier_colon_alter_empty() {
235 let mut lexer = Lexer::from_memory(":+}", Source::Unknown);
236 let mut lexer = WordLexer {
237 lexer: &mut lexer,
238 context: WordContext::Word,
239 };
240
241 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
242 assert_matches!(result, Modifier::Switch(switch) => {
243 assert_eq!(switch.r#type, SwitchType::Alter);
244 assert_eq!(switch.condition, SwitchCondition::UnsetOrEmpty);
245 assert_eq!(switch.word.units, []);
246 assert_eq!(*switch.word.location.code.value.borrow(), ":+}");
247 assert_eq!(switch.word.location.range, 2..2);
248 });
249
250 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
251 }
252
253 #[test]
254 fn lexer_suffix_modifier_default_empty() {
255 let mut lexer = Lexer::from_memory("-}", Source::Unknown);
256 let mut lexer = WordLexer {
257 lexer: &mut lexer,
258 context: WordContext::Word,
259 };
260
261 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
262 assert_matches!(result, Modifier::Switch(switch) => {
263 assert_eq!(switch.r#type, SwitchType::Default);
264 assert_eq!(switch.condition, SwitchCondition::Unset);
265 assert_eq!(switch.word.units, []);
266 assert_eq!(*switch.word.location.code.value.borrow(), "-}");
267 assert_eq!(switch.word.location.range, 1..1);
268 });
269
270 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
271 }
272
273 #[test]
274 fn lexer_suffix_modifier_colon_default_word() {
275 let mut lexer = Lexer::from_memory(r":-cool}", Source::Unknown);
276 let mut lexer = WordLexer {
277 lexer: &mut lexer,
278 context: WordContext::Word,
279 };
280
281 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
282 assert_matches!(result, Modifier::Switch(switch) => {
283 assert_eq!(switch.r#type, SwitchType::Default);
284 assert_eq!(switch.condition, SwitchCondition::UnsetOrEmpty);
285 assert_eq!(
286 switch.word.units,
287 [
288 WordUnit::Unquoted(TextUnit::Literal('c')),
289 WordUnit::Unquoted(TextUnit::Literal('o')),
290 WordUnit::Unquoted(TextUnit::Literal('o')),
291 WordUnit::Unquoted(TextUnit::Literal('l')),
292 ]
293 );
294 assert_eq!(*switch.word.location.code.value.borrow(), ":-cool}");
295 assert_eq!(switch.word.location.range, 2..6);
296 });
297
298 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
299 }
300
301 #[test]
302 fn lexer_suffix_modifier_colon_assign_empty() {
303 let mut lexer = Lexer::from_memory(":=}", Source::Unknown);
304 let mut lexer = WordLexer {
305 lexer: &mut lexer,
306 context: WordContext::Word,
307 };
308
309 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
310 assert_matches!(result, Modifier::Switch(switch) => {
311 assert_eq!(switch.r#type, SwitchType::Assign);
312 assert_eq!(switch.condition, SwitchCondition::UnsetOrEmpty);
313 assert_eq!(switch.word.units, []);
314 assert_eq!(*switch.word.location.code.value.borrow(), ":=}");
315 assert_eq!(switch.word.location.range, 2..2);
316 });
317
318 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
319 }
320
321 #[test]
322 fn lexer_suffix_modifier_assign_word() {
323 let mut lexer = Lexer::from_memory(r"=Yes}", Source::Unknown);
324 let mut lexer = WordLexer {
325 lexer: &mut lexer,
326 context: WordContext::Word,
327 };
328
329 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
330 assert_matches!(result, Modifier::Switch(switch) => {
331 assert_eq!(switch.r#type, SwitchType::Assign);
332 assert_eq!(switch.condition, SwitchCondition::Unset);
333 assert_eq!(
334 switch.word.units,
335 [
336 WordUnit::Unquoted(TextUnit::Literal('Y')),
337 WordUnit::Unquoted(TextUnit::Literal('e')),
338 WordUnit::Unquoted(TextUnit::Literal('s')),
339 ]
340 );
341 assert_eq!(*switch.word.location.code.value.borrow(), "=Yes}");
342 assert_eq!(switch.word.location.range, 1..4);
343 });
344
345 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
346 }
347
348 #[test]
349 fn lexer_suffix_modifier_error_empty() {
350 let mut lexer = Lexer::from_memory("?}", Source::Unknown);
351 let mut lexer = WordLexer {
352 lexer: &mut lexer,
353 context: WordContext::Word,
354 };
355
356 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
357 assert_matches!(result, Modifier::Switch(switch) => {
358 assert_eq!(switch.r#type, SwitchType::Error);
359 assert_eq!(switch.condition, SwitchCondition::Unset);
360 assert_eq!(switch.word.units, []);
361 assert_eq!(*switch.word.location.code.value.borrow(), "?}");
362 assert_eq!(switch.word.location.range, 1..1);
363 });
364
365 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
366 }
367
368 #[test]
369 fn lexer_suffix_modifier_colon_error_word() {
370 let mut lexer = Lexer::from_memory(r":?No}", Source::Unknown);
371 let mut lexer = WordLexer {
372 lexer: &mut lexer,
373 context: WordContext::Word,
374 };
375
376 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
377 assert_matches!(result, Modifier::Switch(switch) => {
378 assert_eq!(switch.r#type, SwitchType::Error);
379 assert_eq!(switch.condition, SwitchCondition::UnsetOrEmpty);
380 assert_eq!(
381 switch.word.units,
382 [
383 WordUnit::Unquoted(TextUnit::Literal('N')),
384 WordUnit::Unquoted(TextUnit::Literal('o')),
385 ]
386 );
387 assert_eq!(*switch.word.location.code.value.borrow(), ":?No}");
388 assert_eq!(switch.word.location.range, 2..4);
389 });
390
391 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
392 }
393
394 #[test]
395 fn lexer_suffix_modifier_tilde_expansion_in_switch_word_in_word_context() {
396 let mut lexer = Lexer::from_memory(r"-~}", Source::Unknown);
397 let mut lexer = WordLexer {
398 lexer: &mut lexer,
399 context: WordContext::Word,
400 };
401
402 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
403 assert_matches!(result, Modifier::Switch(switch) => {
404 assert_eq!(switch.word.units, [WordUnit::Tilde("".to_string())]);
405 });
406 }
407
408 #[test]
409 fn lexer_suffix_modifier_tilde_expansion_in_switch_word_in_text_context() {
410 let mut lexer = Lexer::from_memory(r"-~}", Source::Unknown);
411 let mut lexer = WordLexer {
412 lexer: &mut lexer,
413 context: WordContext::Text,
414 };
415
416 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
417 assert_matches!(result, Modifier::Switch(switch) => {
418 assert_eq!(
419 switch.word.units,
420 [WordUnit::Unquoted(TextUnit::Literal('~'))]
421 );
422 });
423 }
424
425 #[test]
426 fn lexer_suffix_modifier_trim_shortest_prefix_in_word_context() {
427 let mut lexer = Lexer::from_memory("#'*'}", Source::Unknown);
428 let mut lexer = WordLexer {
429 lexer: &mut lexer,
430 context: WordContext::Word,
431 };
432
433 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
434 assert_matches!(result, Modifier::Trim(trim) => {
435 assert_eq!(trim.side, TrimSide::Prefix);
436 assert_eq!(trim.length, TrimLength::Shortest);
437 assert_eq!(trim.pattern.units, [WordUnit::SingleQuote("*".to_string())]);
438 assert_eq!(*trim.pattern.location.code.value.borrow(), "#'*'}");
439 assert_eq!(trim.pattern.location.range, 1..4);
440 });
441
442 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
443 }
444
445 #[test]
446 fn lexer_suffix_modifier_trim_shortest_prefix_in_text_context() {
447 let mut lexer = Lexer::from_memory("#'*'}", Source::Unknown);
448 let mut lexer = WordLexer {
449 lexer: &mut lexer,
450 context: WordContext::Text,
451 };
452
453 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
454 assert_matches!(result, Modifier::Trim(trim) => {
455 assert_eq!(trim.side, TrimSide::Prefix);
456 assert_eq!(trim.length, TrimLength::Shortest);
457 assert_eq!(trim.pattern.units, [WordUnit::SingleQuote("*".to_string())]);
458 assert_eq!(*trim.pattern.location.code.value.borrow(), "#'*'}");
459 assert_eq!(trim.pattern.location.range, 1..4);
460 });
461
462 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
463 }
464
465 #[test]
466 fn lexer_suffix_modifier_trim_longest_prefix() {
467 let mut lexer = Lexer::from_memory(r#"##"?"}"#, Source::Unknown);
468 let mut lexer = WordLexer {
469 lexer: &mut lexer,
470 context: WordContext::Word,
471 };
472
473 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
474 assert_matches!(result, Modifier::Trim(trim) => {
475 assert_eq!(trim.side, TrimSide::Prefix);
476 assert_eq!(trim.length, TrimLength::Longest);
477 assert_eq!(trim.pattern.units.len(), 1, "{:?}", trim.pattern);
478 assert_matches!(&trim.pattern.units[0], WordUnit::DoubleQuote(Text(units)) => {
479 assert_eq!(units[..], [TextUnit::Literal('?')]);
480 });
481 assert_eq!(*trim.pattern.location.code.value.borrow(), r#"##"?"}"#);
482 assert_eq!(trim.pattern.location.range, 2..5);
483 });
484
485 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
486 }
487
488 #[test]
489 fn lexer_suffix_modifier_trim_shortest_suffix() {
490 let mut lexer = Lexer::from_memory(r"%\%}", Source::Unknown);
491 let mut lexer = WordLexer {
492 lexer: &mut lexer,
493 context: WordContext::Word,
494 };
495
496 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
497 assert_matches!(result, Modifier::Trim(trim) => {
498 assert_eq!(trim.side, TrimSide::Suffix);
499 assert_eq!(trim.length, TrimLength::Shortest);
500 assert_eq!(
501 trim.pattern.units,
502 [WordUnit::Unquoted(TextUnit::Backslashed('%'))]
503 );
504 assert_eq!(*trim.pattern.location.code.value.borrow(), r"%\%}");
505 assert_eq!(trim.pattern.location.range, 1..3);
506 });
507
508 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
509 }
510
511 #[test]
512 fn lexer_suffix_modifier_trim_longest_suffix() {
513 let mut lexer = Lexer::from_memory("%%%}", Source::Unknown);
514 let mut lexer = WordLexer {
515 lexer: &mut lexer,
516 context: WordContext::Word,
517 };
518
519 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
520 assert_matches!(result, Modifier::Trim(trim) => {
521 assert_eq!(trim.side, TrimSide::Suffix);
522 assert_eq!(trim.length, TrimLength::Longest);
523 assert_eq!(
524 trim.pattern.units,
525 [WordUnit::Unquoted(TextUnit::Literal('%'))]
526 );
527 assert_eq!(*trim.pattern.location.code.value.borrow(), "%%%}");
528 assert_eq!(trim.pattern.location.range, 2..3);
529 });
530
531 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
532 }
533
534 #[test]
535 fn lexer_suffix_modifier_tilde_expansion_in_trim_word() {
536 let mut lexer = Lexer::from_memory(r"#~}", Source::Unknown);
537 let mut lexer = WordLexer {
538 lexer: &mut lexer,
539 context: WordContext::Word,
540 };
541
542 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
543 assert_matches!(result, Modifier::Trim(trim) => {
544 assert_eq!(trim.pattern.units, [WordUnit::Tilde("".to_string())]);
545 });
546 }
547
548 #[test]
549 fn lexer_suffix_modifier_orphan_colon_eof() {
550 let mut lexer = Lexer::from_memory(r":", Source::Unknown);
551 let mut lexer = WordLexer {
552 lexer: &mut lexer,
553 context: WordContext::Word,
554 };
555
556 let e = lexer.suffix_modifier().now_or_never().unwrap().unwrap_err();
557 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidModifier));
558 assert_eq!(*e.location.code.value.borrow(), ":");
559 assert_eq!(e.location.range, 0..1);
560 }
561
562 #[test]
563 fn lexer_suffix_modifier_orphan_colon_followed_by_letter() {
564 let mut lexer = Lexer::from_memory(r":x}", Source::Unknown);
565 let mut lexer = WordLexer {
566 lexer: &mut lexer,
567 context: WordContext::Word,
568 };
569
570 let e = lexer.suffix_modifier().now_or_never().unwrap().unwrap_err();
571 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidModifier));
572 assert_eq!(*e.location.code.value.borrow(), ":x}");
573 assert_eq!(e.location.range, 0..1);
574 }
575
576 #[test]
577 fn lexer_suffix_modifier_orphan_colon_followed_by_symbol() {
578 let mut lexer = Lexer::from_memory(r":#}", Source::Unknown);
579 let mut lexer = WordLexer {
580 lexer: &mut lexer,
581 context: WordContext::Word,
582 };
583
584 let e = lexer.suffix_modifier().now_or_never().unwrap().unwrap_err();
585 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidModifier));
586 assert_eq!(*e.location.code.value.borrow(), ":#}");
587 assert_eq!(e.location.range, 0..2);
588 }
589}