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::SwitchAction;
28use crate::syntax::SwitchCondition;
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 action = match symbol {
97 '+' => SwitchAction::Alter,
98 '-' => SwitchAction::Default,
99 '=' => SwitchAction::Assign,
100 '?' => SwitchAction::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 action,
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::syntax::Text;
153 use crate::syntax::TextUnit;
154 use crate::syntax::WordUnit;
155 use assert_matches::assert_matches;
156 use futures_util::FutureExt;
157
158 #[test]
159 fn lexer_suffix_modifier_eof() {
160 let mut lexer = Lexer::with_code("");
161 let mut lexer = WordLexer {
162 lexer: &mut lexer,
163 context: WordContext::Word,
164 };
165
166 let result = lexer.suffix_modifier().now_or_never().unwrap();
167 assert_eq!(result, Ok(Modifier::None));
168 }
169
170 #[test]
171 fn lexer_suffix_modifier_none() {
172 let mut lexer = Lexer::with_code("}");
173 let mut lexer = WordLexer {
174 lexer: &mut lexer,
175 context: WordContext::Word,
176 };
177
178 let result = lexer.suffix_modifier().now_or_never().unwrap();
179 assert_eq!(result, Ok(Modifier::None));
180
181 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
182 }
183
184 #[test]
185 fn lexer_suffix_modifier_alter_empty() {
186 let mut lexer = Lexer::with_code("+}");
187 let mut lexer = WordLexer {
188 lexer: &mut lexer,
189 context: WordContext::Word,
190 };
191
192 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
193 assert_matches!(result, Modifier::Switch(switch) => {
194 assert_eq!(switch.action, SwitchAction::Alter);
195 assert_eq!(switch.condition, SwitchCondition::Unset);
196 assert_eq!(switch.word.units, []);
197 assert_eq!(*switch.word.location.code.value.borrow(), "+}");
198 assert_eq!(switch.word.location.range, 1..1);
199 });
200
201 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
202 }
203
204 #[test]
205 fn lexer_suffix_modifier_alter_word() {
206 let mut lexer = Lexer::with_code(r"+a z}");
207 let mut lexer = WordLexer {
208 lexer: &mut lexer,
209 context: WordContext::Word,
210 };
211
212 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
213 assert_matches!(result, Modifier::Switch(switch) => {
214 assert_eq!(switch.action, SwitchAction::Alter);
215 assert_eq!(switch.condition, SwitchCondition::Unset);
216 assert_eq!(
217 switch.word.units,
218 [
219 WordUnit::Unquoted(TextUnit::Literal('a')),
220 WordUnit::Unquoted(TextUnit::Literal(' ')),
221 WordUnit::Unquoted(TextUnit::Literal(' ')),
222 WordUnit::Unquoted(TextUnit::Literal('z')),
223 ]
224 );
225 assert_eq!(*switch.word.location.code.value.borrow(), "+a z}");
226 assert_eq!(switch.word.location.range, 1..5);
227 });
228
229 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
230 }
231
232 #[test]
233 fn lexer_suffix_modifier_colon_alter_empty() {
234 let mut lexer = Lexer::with_code(":+}");
235 let mut lexer = WordLexer {
236 lexer: &mut lexer,
237 context: WordContext::Word,
238 };
239
240 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
241 assert_matches!(result, Modifier::Switch(switch) => {
242 assert_eq!(switch.action, SwitchAction::Alter);
243 assert_eq!(switch.condition, SwitchCondition::UnsetOrEmpty);
244 assert_eq!(switch.word.units, []);
245 assert_eq!(*switch.word.location.code.value.borrow(), ":+}");
246 assert_eq!(switch.word.location.range, 2..2);
247 });
248
249 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
250 }
251
252 #[test]
253 fn lexer_suffix_modifier_default_empty() {
254 let mut lexer = Lexer::with_code("-}");
255 let mut lexer = WordLexer {
256 lexer: &mut lexer,
257 context: WordContext::Word,
258 };
259
260 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
261 assert_matches!(result, Modifier::Switch(switch) => {
262 assert_eq!(switch.action, SwitchAction::Default);
263 assert_eq!(switch.condition, SwitchCondition::Unset);
264 assert_eq!(switch.word.units, []);
265 assert_eq!(*switch.word.location.code.value.borrow(), "-}");
266 assert_eq!(switch.word.location.range, 1..1);
267 });
268
269 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
270 }
271
272 #[test]
273 fn lexer_suffix_modifier_colon_default_word() {
274 let mut lexer = Lexer::with_code(r":-cool}");
275 let mut lexer = WordLexer {
276 lexer: &mut lexer,
277 context: WordContext::Word,
278 };
279
280 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
281 assert_matches!(result, Modifier::Switch(switch) => {
282 assert_eq!(switch.action, SwitchAction::Default);
283 assert_eq!(switch.condition, SwitchCondition::UnsetOrEmpty);
284 assert_eq!(
285 switch.word.units,
286 [
287 WordUnit::Unquoted(TextUnit::Literal('c')),
288 WordUnit::Unquoted(TextUnit::Literal('o')),
289 WordUnit::Unquoted(TextUnit::Literal('o')),
290 WordUnit::Unquoted(TextUnit::Literal('l')),
291 ]
292 );
293 assert_eq!(*switch.word.location.code.value.borrow(), ":-cool}");
294 assert_eq!(switch.word.location.range, 2..6);
295 });
296
297 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
298 }
299
300 #[test]
301 fn lexer_suffix_modifier_colon_assign_empty() {
302 let mut lexer = Lexer::with_code(":=}");
303 let mut lexer = WordLexer {
304 lexer: &mut lexer,
305 context: WordContext::Word,
306 };
307
308 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
309 assert_matches!(result, Modifier::Switch(switch) => {
310 assert_eq!(switch.action, SwitchAction::Assign);
311 assert_eq!(switch.condition, SwitchCondition::UnsetOrEmpty);
312 assert_eq!(switch.word.units, []);
313 assert_eq!(*switch.word.location.code.value.borrow(), ":=}");
314 assert_eq!(switch.word.location.range, 2..2);
315 });
316
317 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
318 }
319
320 #[test]
321 fn lexer_suffix_modifier_assign_word() {
322 let mut lexer = Lexer::with_code(r"=Yes}");
323 let mut lexer = WordLexer {
324 lexer: &mut lexer,
325 context: WordContext::Word,
326 };
327
328 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
329 assert_matches!(result, Modifier::Switch(switch) => {
330 assert_eq!(switch.action, SwitchAction::Assign);
331 assert_eq!(switch.condition, SwitchCondition::Unset);
332 assert_eq!(
333 switch.word.units,
334 [
335 WordUnit::Unquoted(TextUnit::Literal('Y')),
336 WordUnit::Unquoted(TextUnit::Literal('e')),
337 WordUnit::Unquoted(TextUnit::Literal('s')),
338 ]
339 );
340 assert_eq!(*switch.word.location.code.value.borrow(), "=Yes}");
341 assert_eq!(switch.word.location.range, 1..4);
342 });
343
344 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
345 }
346
347 #[test]
348 fn lexer_suffix_modifier_error_empty() {
349 let mut lexer = Lexer::with_code("?}");
350 let mut lexer = WordLexer {
351 lexer: &mut lexer,
352 context: WordContext::Word,
353 };
354
355 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
356 assert_matches!(result, Modifier::Switch(switch) => {
357 assert_eq!(switch.action, SwitchAction::Error);
358 assert_eq!(switch.condition, SwitchCondition::Unset);
359 assert_eq!(switch.word.units, []);
360 assert_eq!(*switch.word.location.code.value.borrow(), "?}");
361 assert_eq!(switch.word.location.range, 1..1);
362 });
363
364 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
365 }
366
367 #[test]
368 fn lexer_suffix_modifier_colon_error_word() {
369 let mut lexer = Lexer::with_code(r":?No}");
370 let mut lexer = WordLexer {
371 lexer: &mut lexer,
372 context: WordContext::Word,
373 };
374
375 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
376 assert_matches!(result, Modifier::Switch(switch) => {
377 assert_eq!(switch.action, SwitchAction::Error);
378 assert_eq!(switch.condition, SwitchCondition::UnsetOrEmpty);
379 assert_eq!(
380 switch.word.units,
381 [
382 WordUnit::Unquoted(TextUnit::Literal('N')),
383 WordUnit::Unquoted(TextUnit::Literal('o')),
384 ]
385 );
386 assert_eq!(*switch.word.location.code.value.borrow(), ":?No}");
387 assert_eq!(switch.word.location.range, 2..4);
388 });
389
390 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
391 }
392
393 #[test]
394 fn lexer_suffix_modifier_tilde_expansion_in_switch_word_in_word_context() {
395 let mut lexer = Lexer::with_code(r"-~}");
396 let mut lexer = WordLexer {
397 lexer: &mut lexer,
398 context: WordContext::Word,
399 };
400
401 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
402 assert_matches!(result, Modifier::Switch(switch) => {
403 assert_eq!(
404 switch.word.units,
405 [WordUnit::Tilde {
406 name: "".to_string(),
407 followed_by_slash: false
408 }]
409 );
410 });
411 }
412
413 #[test]
414 fn lexer_suffix_modifier_tilde_expansion_in_switch_word_in_text_context() {
415 let mut lexer = Lexer::with_code(r"-~}");
416 let mut lexer = WordLexer {
417 lexer: &mut lexer,
418 context: WordContext::Text,
419 };
420
421 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
422 assert_matches!(result, Modifier::Switch(switch) => {
423 assert_eq!(
424 switch.word.units,
425 [WordUnit::Unquoted(TextUnit::Literal('~'))]
426 );
427 });
428 }
429
430 #[test]
431 fn lexer_suffix_modifier_trim_shortest_prefix_in_word_context() {
432 let mut lexer = Lexer::with_code("#'*'}");
433 let mut lexer = WordLexer {
434 lexer: &mut lexer,
435 context: WordContext::Word,
436 };
437
438 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
439 assert_matches!(result, Modifier::Trim(trim) => {
440 assert_eq!(trim.side, TrimSide::Prefix);
441 assert_eq!(trim.length, TrimLength::Shortest);
442 assert_eq!(trim.pattern.units, [WordUnit::SingleQuote("*".to_string())]);
443 assert_eq!(*trim.pattern.location.code.value.borrow(), "#'*'}");
444 assert_eq!(trim.pattern.location.range, 1..4);
445 });
446
447 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
448 }
449
450 #[test]
451 fn lexer_suffix_modifier_trim_shortest_prefix_in_text_context() {
452 let mut lexer = Lexer::with_code("#'*'}");
453 let mut lexer = WordLexer {
454 lexer: &mut lexer,
455 context: WordContext::Text,
456 };
457
458 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
459 assert_matches!(result, Modifier::Trim(trim) => {
460 assert_eq!(trim.side, TrimSide::Prefix);
461 assert_eq!(trim.length, TrimLength::Shortest);
462 assert_eq!(trim.pattern.units, [WordUnit::SingleQuote("*".to_string())]);
463 assert_eq!(*trim.pattern.location.code.value.borrow(), "#'*'}");
464 assert_eq!(trim.pattern.location.range, 1..4);
465 });
466
467 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
468 }
469
470 #[test]
471 fn lexer_suffix_modifier_trim_longest_prefix() {
472 let mut lexer = Lexer::with_code(r#"##"?"}"#);
473 let mut lexer = WordLexer {
474 lexer: &mut lexer,
475 context: WordContext::Word,
476 };
477
478 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
479 assert_matches!(result, Modifier::Trim(trim) => {
480 assert_eq!(trim.side, TrimSide::Prefix);
481 assert_eq!(trim.length, TrimLength::Longest);
482 assert_eq!(trim.pattern.units.len(), 1, "{:?}", trim.pattern);
483 assert_matches!(&trim.pattern.units[0], WordUnit::DoubleQuote(Text(units)) => {
484 assert_eq!(units[..], [TextUnit::Literal('?')]);
485 });
486 assert_eq!(*trim.pattern.location.code.value.borrow(), r#"##"?"}"#);
487 assert_eq!(trim.pattern.location.range, 2..5);
488 });
489
490 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
491 }
492
493 #[test]
494 fn lexer_suffix_modifier_trim_shortest_suffix() {
495 let mut lexer = Lexer::with_code(r"%\%}");
496 let mut lexer = WordLexer {
497 lexer: &mut lexer,
498 context: WordContext::Word,
499 };
500
501 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
502 assert_matches!(result, Modifier::Trim(trim) => {
503 assert_eq!(trim.side, TrimSide::Suffix);
504 assert_eq!(trim.length, TrimLength::Shortest);
505 assert_eq!(
506 trim.pattern.units,
507 [WordUnit::Unquoted(TextUnit::Backslashed('%'))]
508 );
509 assert_eq!(*trim.pattern.location.code.value.borrow(), r"%\%}");
510 assert_eq!(trim.pattern.location.range, 1..3);
511 });
512
513 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
514 }
515
516 #[test]
517 fn lexer_suffix_modifier_trim_longest_suffix() {
518 let mut lexer = Lexer::with_code("%%%}");
519 let mut lexer = WordLexer {
520 lexer: &mut lexer,
521 context: WordContext::Word,
522 };
523
524 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
525 assert_matches!(result, Modifier::Trim(trim) => {
526 assert_eq!(trim.side, TrimSide::Suffix);
527 assert_eq!(trim.length, TrimLength::Longest);
528 assert_eq!(
529 trim.pattern.units,
530 [WordUnit::Unquoted(TextUnit::Literal('%'))]
531 );
532 assert_eq!(*trim.pattern.location.code.value.borrow(), "%%%}");
533 assert_eq!(trim.pattern.location.range, 2..3);
534 });
535
536 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('}')));
537 }
538
539 #[test]
540 fn lexer_suffix_modifier_tilde_expansion_in_trim_word() {
541 let mut lexer = Lexer::with_code(r"#~}");
542 let mut lexer = WordLexer {
543 lexer: &mut lexer,
544 context: WordContext::Word,
545 };
546
547 let result = lexer.suffix_modifier().now_or_never().unwrap().unwrap();
548 assert_matches!(result, Modifier::Trim(trim) => {
549 assert_eq!(
550 trim.pattern.units,
551 [WordUnit::Tilde {
552 name: "".to_string(),
553 followed_by_slash: false
554 }]
555 );
556 });
557 }
558
559 #[test]
560 fn lexer_suffix_modifier_orphan_colon_eof() {
561 let mut lexer = Lexer::with_code(r":");
562 let mut lexer = WordLexer {
563 lexer: &mut lexer,
564 context: WordContext::Word,
565 };
566
567 let e = lexer.suffix_modifier().now_or_never().unwrap().unwrap_err();
568 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidModifier));
569 assert_eq!(*e.location.code.value.borrow(), ":");
570 assert_eq!(e.location.range, 0..1);
571 }
572
573 #[test]
574 fn lexer_suffix_modifier_orphan_colon_followed_by_letter() {
575 let mut lexer = Lexer::with_code(r":x}");
576 let mut lexer = WordLexer {
577 lexer: &mut lexer,
578 context: WordContext::Word,
579 };
580
581 let e = lexer.suffix_modifier().now_or_never().unwrap().unwrap_err();
582 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidModifier));
583 assert_eq!(*e.location.code.value.borrow(), ":x}");
584 assert_eq!(e.location.range, 0..1);
585 }
586
587 #[test]
588 fn lexer_suffix_modifier_orphan_colon_followed_by_symbol() {
589 let mut lexer = Lexer::with_code(r":#}");
590 let mut lexer = WordLexer {
591 lexer: &mut lexer,
592 context: WordContext::Word,
593 };
594
595 let e = lexer.suffix_modifier().now_or_never().unwrap().unwrap_err();
596 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidModifier));
597 assert_eq!(*e.location.code.value.borrow(), ":#}");
598 assert_eq!(e.location.range, 0..2);
599 }
600}