1use super::core::WordLexer;
20use super::raw_param::is_portable_name_char;
21use crate::parser::core::Result;
22use crate::parser::error::Error;
23use crate::parser::error::SyntaxError;
24use crate::syntax::BracedParam;
25use crate::syntax::Modifier;
26use crate::syntax::Param;
27use crate::syntax::ParamType;
28use crate::syntax::SpecialParam;
29use std::num::IntErrorKind;
30
31pub fn is_name_char(c: char) -> bool {
36 is_portable_name_char(c)
38}
39
40#[must_use]
53fn type_of_id(id: &str) -> Option<ParamType> {
54 if id == "0" {
55 return Some(ParamType::Special(SpecialParam::Zero));
56 }
57 if id.starts_with(|c: char| c.is_ascii_digit()) {
58 return match id.parse() {
59 Ok(index) => Some(ParamType::Positional(index)),
60 Err(e) => match e.kind() {
61 IntErrorKind::PosOverflow => Some(ParamType::Positional(usize::MAX)),
62 _ => None,
63 },
64 };
65 }
66 Some(ParamType::Variable)
67}
68
69impl WordLexer<'_, '_> {
70 async fn has_length_prefix(&mut self) -> Result<bool> {
76 if !self.skip_if(|c| c == '#').await? {
77 return Ok(false);
78 }
79
80 if let Some(c) = self.peek_char().await? {
84 if matches!(c, '}' | '+' | '=' | ':' | '%') {
86 return Ok(false);
87 }
88
89 if matches!(c, '-' | '?' | '#') {
92 self.consume_char();
93 if let Some(c) = self.peek_char().await? {
94 return Ok(c == '}');
95 }
96 }
97 }
98
99 Ok(true)
100 }
101
102 async fn length_prefix(&mut self) -> Result<bool> {
104 let initial_index = self.index();
105 let has_length_prefix = self.has_length_prefix().await?;
106 self.rewind(initial_index);
107 if has_length_prefix {
108 self.peek_char().await?;
109 self.consume_char();
110 }
111 Ok(has_length_prefix)
112 }
113
114 pub async fn braced_param(&mut self, start_index: usize) -> Result<Option<BracedParam>> {
126 if !self.skip_if(|c| c == '{').await? {
127 return Ok(None);
128 }
129
130 let opening_location = self.location_range(start_index..self.index());
131
132 let has_length_prefix = self.length_prefix().await?;
133
134 let param_start_index = self.index();
135
136 let c = self.peek_char().await?.unwrap();
137 let param = if is_name_char(c) {
138 self.consume_char();
139
140 let mut id = c.to_string();
142 while let Some(c) = self.consume_char_if(is_name_char).await? {
143 id.push(c.value);
144 }
145
146 let Some(r#type) = type_of_id(&id) else {
147 let cause = SyntaxError::InvalidParam.into();
148 let location = self.location_range(param_start_index..self.index());
149 return Err(Error { cause, location });
150 };
151 Param { id, r#type }
152 } else if let Some(special) = SpecialParam::from_char(c) {
153 self.consume_char();
154 Param {
155 id: c.to_string(),
156 r#type: special.into(),
157 }
158 } else {
159 let cause = SyntaxError::EmptyParam.into();
160 let location = self.location().await?.clone();
161 return Err(Error { cause, location });
162 };
163
164 let suffix_location = self.location().await?.clone();
165 let suffix = self.suffix_modifier().await?;
166
167 if !self.skip_if(|c| c == '}').await? {
168 let cause = SyntaxError::UnclosedParam { opening_location }.into();
169 let location = self.location().await?.clone();
170 return Err(Error { cause, location });
171 }
172
173 let modifier = match (has_length_prefix, suffix) {
174 (true, Modifier::None) => Modifier::Length,
175 (true, _) => {
176 let cause = SyntaxError::MultipleModifier.into();
177 let location = suffix_location;
178 return Err(Error { cause, location });
179 }
180 (false, suffix) => suffix,
181 };
182
183 Ok(Some(BracedParam {
184 param,
185 modifier,
186 location: self.location_range(start_index..self.index()),
187 }))
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194 use crate::parser::error::ErrorCause;
195 use crate::parser::lex::Lexer;
196 use crate::parser::lex::WordContext;
197 use crate::source::Source;
198 use crate::syntax::SwitchCondition;
199 use crate::syntax::SwitchType;
200 use crate::syntax::TrimLength;
201 use crate::syntax::TrimSide;
202 use assert_matches::assert_matches;
203 use futures_util::FutureExt;
204
205 #[test]
206 fn lexer_braced_param_none() {
207 let mut lexer = Lexer::from_memory("$foo", Source::Unknown);
208 lexer.peek_char().now_or_never().unwrap().unwrap();
209 lexer.consume_char();
210 let mut lexer = WordLexer {
211 lexer: &mut lexer,
212 context: WordContext::Word,
213 };
214 assert_eq!(lexer.braced_param(0).now_or_never().unwrap(), Ok(None));
215 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('f')));
216 }
217
218 #[test]
219 fn lexer_braced_param_minimum() {
220 let mut lexer = Lexer::from_memory("${@};", Source::Unknown);
221 lexer.peek_char().now_or_never().unwrap().unwrap();
222 lexer.consume_char();
223 let mut lexer = WordLexer {
224 lexer: &mut lexer,
225 context: WordContext::Word,
226 };
227
228 let result = lexer.braced_param(0).now_or_never().unwrap();
229 let param = result.unwrap().unwrap();
230 assert_eq!(param.param, Param::from(SpecialParam::At));
231 assert_eq!(param.modifier, Modifier::None);
232 assert_eq!(*param.location.code.value.borrow(), "${@};");
234 assert_eq!(param.location.code.start_line_number.get(), 1);
235 assert_eq!(*param.location.code.source, Source::Unknown);
236 assert_eq!(param.location.range, 0..4);
237
238 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(';')));
239 }
240
241 #[test]
242 fn lexer_braced_param_alphanumeric_name() {
243 let mut lexer = Lexer::from_memory("X${foo_123}<", Source::Unknown);
244 let mut lexer = WordLexer {
245 lexer: &mut lexer,
246 context: WordContext::Word,
247 };
248 lexer.peek_char().now_or_never().unwrap().unwrap();
249 lexer.consume_char();
250 lexer.peek_char().now_or_never().unwrap().unwrap();
251 lexer.consume_char();
252
253 let result = lexer.braced_param(1).now_or_never().unwrap();
254 let param = result.unwrap().unwrap();
255 assert_eq!(param.param, Param::variable("foo_123"));
256 assert_eq!(param.modifier, Modifier::None);
257 assert_eq!(*param.location.code.value.borrow(), "X${foo_123}<");
259 assert_eq!(param.location.code.start_line_number.get(), 1);
260 assert_eq!(*param.location.code.source, Source::Unknown);
261 assert_eq!(param.location.range, 1..11);
262
263 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
264 }
265
266 #[test]
267 fn lexer_braced_param_positional() {
268 let mut lexer = Lexer::from_memory("${123}<", Source::Unknown);
269 let mut lexer = WordLexer {
270 lexer: &mut lexer,
271 context: WordContext::Word,
272 };
273 lexer.peek_char().now_or_never().unwrap().unwrap();
274 lexer.consume_char();
275
276 let result = lexer.braced_param(0).now_or_never().unwrap();
277 let param = result.unwrap().unwrap();
278 assert_eq!(param.param, Param::from(123));
279 assert_eq!(param.modifier, Modifier::None);
280 assert_eq!(*param.location.code.value.borrow(), "${123}<");
282 assert_eq!(param.location.code.start_line_number.get(), 1);
283 assert_eq!(*param.location.code.source, Source::Unknown);
284 assert_eq!(param.location.range, 0..6);
285
286 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
287 }
288
289 #[test]
292 fn lexer_braced_param_positional_zero() {
293 let mut lexer = Lexer::from_memory("${00}<", Source::Unknown);
294 let mut lexer = WordLexer {
295 lexer: &mut lexer,
296 context: WordContext::Word,
297 };
298 lexer.peek_char().now_or_never().unwrap().unwrap();
299 lexer.consume_char();
300
301 let result = lexer.braced_param(0).now_or_never().unwrap();
302 let param = result.unwrap().unwrap();
303 assert_eq!(param.param.id, "00");
304 assert_eq!(param.param.r#type, ParamType::Positional(0));
305 assert_eq!(param.modifier, Modifier::None);
306 assert_eq!(*param.location.code.value.borrow(), "${00}<");
308 assert_eq!(param.location.code.start_line_number.get(), 1);
309 assert_eq!(*param.location.code.source, Source::Unknown);
310 assert_eq!(param.location.range, 0..5);
311
312 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
313 }
314
315 #[test]
316 fn lexer_braced_param_positional_overflow() {
317 let mut lexer = Lexer::from_memory(
320 "${9999999999999999999999999999999999999999}",
321 Source::Unknown,
322 );
323 let mut lexer = WordLexer {
324 lexer: &mut lexer,
325 context: WordContext::Word,
326 };
327 lexer.peek_char().now_or_never().unwrap().unwrap();
328 lexer.consume_char();
329
330 let result = lexer.braced_param(0).now_or_never().unwrap();
331 let param = result.unwrap().unwrap();
332 assert_eq!(param.param.r#type, ParamType::Positional(usize::MAX));
333 }
334
335 #[test]
336 fn lexer_braced_param_invalid_param() {
337 let mut lexer = Lexer::from_memory("${0_0}", Source::Unknown);
338 let mut lexer = WordLexer {
339 lexer: &mut lexer,
340 context: WordContext::Word,
341 };
342 lexer.peek_char().now_or_never().unwrap().unwrap();
343 lexer.consume_char();
344
345 let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
346 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidParam));
347 assert_eq!(*e.location.code.value.borrow(), "${0_0}");
348 assert_eq!(e.location.code.start_line_number.get(), 1);
349 assert_eq!(*e.location.code.source, Source::Unknown);
350 assert_eq!(e.location.range, 2..5);
351 }
352
353 #[test]
356 fn lexer_braced_param_special_zero() {
357 let mut lexer = Lexer::from_memory("${0}<", Source::Unknown);
358 let mut lexer = WordLexer {
359 lexer: &mut lexer,
360 context: WordContext::Word,
361 };
362 lexer.peek_char().now_or_never().unwrap().unwrap();
363 lexer.consume_char();
364
365 let result = lexer.braced_param(0).now_or_never().unwrap();
366 let param = result.unwrap().unwrap();
367 assert_eq!(param.param.id, "0");
368 assert_eq!(param.param.r#type, ParamType::Special(SpecialParam::Zero));
369 assert_eq!(param.modifier, Modifier::None);
370 assert_eq!(*param.location.code.value.borrow(), "${0}<");
372 assert_eq!(param.location.code.start_line_number.get(), 1);
373 assert_eq!(*param.location.code.source, Source::Unknown);
374 assert_eq!(param.location.range, 0..4);
375
376 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
377 }
378
379 #[test]
380 fn lexer_braced_param_special_hash() {
381 let mut lexer = Lexer::from_memory("${#}<", Source::Unknown);
382 let mut lexer = WordLexer {
383 lexer: &mut lexer,
384 context: WordContext::Word,
385 };
386 lexer.peek_char().now_or_never().unwrap().unwrap();
387 lexer.consume_char();
388
389 let result = lexer.braced_param(0).now_or_never().unwrap();
390 let param = result.unwrap().unwrap();
391 assert_eq!(param.param, Param::from(SpecialParam::Number));
392 assert_eq!(param.modifier, Modifier::None);
393 assert_eq!(*param.location.code.value.borrow(), "${#}<");
395 assert_eq!(param.location.code.start_line_number.get(), 1);
396 assert_eq!(*param.location.code.source, Source::Unknown);
397 assert_eq!(param.location.range, 0..4);
398
399 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
400 }
401
402 #[test]
403 fn lexer_braced_param_missing_name() {
404 let mut lexer = Lexer::from_memory("${};", Source::Unknown);
405 let mut lexer = WordLexer {
406 lexer: &mut lexer,
407 context: WordContext::Word,
408 };
409 lexer.peek_char().now_or_never().unwrap().unwrap();
410 lexer.consume_char();
411
412 let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
413 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::EmptyParam));
414 assert_eq!(*e.location.code.value.borrow(), "${};");
415 assert_eq!(e.location.code.start_line_number.get(), 1);
416 assert_eq!(*e.location.code.source, Source::Unknown);
417 assert_eq!(e.location.range, 2..3);
418 }
419
420 #[test]
421 fn lexer_braced_param_unclosed_without_name() {
422 let mut lexer = Lexer::from_memory("${;", Source::Unknown);
423 let mut lexer = WordLexer {
424 lexer: &mut lexer,
425 context: WordContext::Word,
426 };
427 lexer.peek_char().now_or_never().unwrap().unwrap();
428 lexer.consume_char();
429
430 let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
431 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::EmptyParam));
432 assert_eq!(*e.location.code.value.borrow(), "${;");
433 assert_eq!(e.location.code.start_line_number.get(), 1);
434 assert_eq!(*e.location.code.source, Source::Unknown);
435 assert_eq!(e.location.range, 2..3);
436 }
437
438 #[test]
439 fn lexer_braced_param_unclosed_with_name() {
440 let mut lexer = Lexer::from_memory("${_;", Source::Unknown);
441 let mut lexer = WordLexer {
442 lexer: &mut lexer,
443 context: WordContext::Word,
444 };
445 lexer.peek_char().now_or_never().unwrap().unwrap();
446 lexer.consume_char();
447
448 let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
449 assert_matches!(e.cause,
450 ErrorCause::Syntax(SyntaxError::UnclosedParam { opening_location }) => {
451 assert_eq!(*opening_location.code.value.borrow(), "${_;");
452 assert_eq!(opening_location.code.start_line_number.get(), 1);
453 assert_eq!(*opening_location.code.source, Source::Unknown);
454 assert_eq!(opening_location.range, 0..2);
455 });
456 assert_eq!(*e.location.code.value.borrow(), "${_;");
457 assert_eq!(e.location.code.start_line_number.get(), 1);
458 assert_eq!(*e.location.code.source, Source::Unknown);
459 assert_eq!(e.location.range, 3..4);
460 }
461
462 #[test]
463 fn lexer_braced_param_length_alphanumeric_name() {
464 let mut lexer = Lexer::from_memory("${#foo_123}<", Source::Unknown);
465 let mut lexer = WordLexer {
466 lexer: &mut lexer,
467 context: WordContext::Word,
468 };
469 lexer.peek_char().now_or_never().unwrap().unwrap();
470 lexer.consume_char();
471
472 let result = lexer.braced_param(0).now_or_never().unwrap();
473 let param = result.unwrap().unwrap();
474 assert_eq!(param.param, Param::variable("foo_123"));
475 assert_eq!(param.modifier, Modifier::Length);
476 assert_eq!(*param.location.code.value.borrow(), "${#foo_123}<");
478 assert_eq!(param.location.code.start_line_number.get(), 1);
479 assert_eq!(*param.location.code.source, Source::Unknown);
480 assert_eq!(param.location.range, 0..11);
481
482 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
483 }
484
485 #[test]
486 fn lexer_braced_param_length_hash() {
487 let mut lexer = Lexer::from_memory("${##}<", Source::Unknown);
488 let mut lexer = WordLexer {
489 lexer: &mut lexer,
490 context: WordContext::Word,
491 };
492 lexer.peek_char().now_or_never().unwrap().unwrap();
493 lexer.consume_char();
494
495 let result = lexer.braced_param(0).now_or_never().unwrap();
496 let param = result.unwrap().unwrap();
497 assert_eq!(param.param, Param::from(SpecialParam::Number));
498 assert_eq!(param.modifier, Modifier::Length);
499 assert_eq!(*param.location.code.value.borrow(), "${##}<");
501 assert_eq!(param.location.code.start_line_number.get(), 1);
502 assert_eq!(*param.location.code.source, Source::Unknown);
503 assert_eq!(param.location.range, 0..5);
504
505 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
506 }
507
508 #[test]
509 fn lexer_braced_param_length_question() {
510 let mut lexer = Lexer::from_memory("${#?}<", Source::Unknown);
511 let mut lexer = WordLexer {
512 lexer: &mut lexer,
513 context: WordContext::Word,
514 };
515 lexer.peek_char().now_or_never().unwrap().unwrap();
516 lexer.consume_char();
517
518 let result = lexer.braced_param(0).now_or_never().unwrap();
519 let param = result.unwrap().unwrap();
520 assert_eq!(param.param, Param::from(SpecialParam::Question));
521 assert_eq!(param.modifier, Modifier::Length);
522 assert_eq!(*param.location.code.value.borrow(), "${#?}<");
524 assert_eq!(param.location.code.start_line_number.get(), 1);
525 assert_eq!(*param.location.code.source, Source::Unknown);
526 assert_eq!(param.location.range, 0..5);
527
528 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
529 }
530
531 #[test]
532 fn lexer_braced_param_length_hyphen() {
533 let mut lexer = Lexer::from_memory("${#-}<", Source::Unknown);
534 let mut lexer = WordLexer {
535 lexer: &mut lexer,
536 context: WordContext::Word,
537 };
538 lexer.peek_char().now_or_never().unwrap().unwrap();
539 lexer.consume_char();
540
541 let result = lexer.braced_param(0).now_or_never().unwrap();
542 let param = result.unwrap().unwrap();
543 assert_eq!(param.param, Param::from(SpecialParam::Hyphen));
544 assert_eq!(param.modifier, Modifier::Length);
545 assert_eq!(*param.location.code.value.borrow(), "${#-}<");
547 assert_eq!(param.location.code.start_line_number.get(), 1);
548 assert_eq!(*param.location.code.source, Source::Unknown);
549 assert_eq!(param.location.range, 0..5);
550
551 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
552 }
553
554 #[test]
555 fn lexer_braced_param_switch_minimum() {
556 let mut lexer = Lexer::from_memory("${x+})", Source::Unknown);
557 let mut lexer = WordLexer {
558 lexer: &mut lexer,
559 context: WordContext::Word,
560 };
561 lexer.peek_char().now_or_never().unwrap().unwrap();
562 lexer.consume_char();
563
564 let result = lexer.braced_param(0).now_or_never().unwrap();
565 let param = result.unwrap().unwrap();
566 assert_eq!(param.param, Param::variable("x"));
567 assert_matches!(param.modifier, Modifier::Switch(switch) => {
568 assert_eq!(switch.r#type, SwitchType::Alter);
569 assert_eq!(switch.condition, SwitchCondition::Unset);
570 assert_eq!(switch.word.to_string(), "");
571 });
572 assert_eq!(*param.location.code.value.borrow(), "${x+})");
574 assert_eq!(param.location.code.start_line_number.get(), 1);
575 assert_eq!(*param.location.code.source, Source::Unknown);
576 assert_eq!(param.location.range, 0..5);
577
578 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(')')));
579 }
580
581 #[test]
582 fn lexer_braced_param_switch_full() {
583 let mut lexer = Lexer::from_memory("${foo:?'!'})", Source::Unknown);
584 let mut lexer = WordLexer {
585 lexer: &mut lexer,
586 context: WordContext::Word,
587 };
588 lexer.peek_char().now_or_never().unwrap().unwrap();
589 lexer.consume_char();
590
591 let result = lexer.braced_param(0).now_or_never().unwrap();
592 let param = result.unwrap().unwrap();
593 assert_eq!(param.param, Param::variable("foo"));
594 assert_matches!(param.modifier, Modifier::Switch(switch) => {
595 assert_eq!(switch.r#type, SwitchType::Error);
596 assert_eq!(switch.condition, SwitchCondition::UnsetOrEmpty);
597 assert_eq!(switch.word.to_string(), "'!'");
598 });
599 assert_eq!(*param.location.code.value.borrow(), "${foo:?'!'})");
601 assert_eq!(param.location.code.start_line_number.get(), 1);
602 assert_eq!(*param.location.code.source, Source::Unknown);
603 assert_eq!(param.location.range, 0..11);
604
605 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(')')));
606 }
607
608 #[test]
609 fn lexer_braced_param_hash_suffix_alter() {
610 let mut lexer = Lexer::from_memory("${#+?}<", Source::Unknown);
611 let mut lexer = WordLexer {
612 lexer: &mut lexer,
613 context: WordContext::Word,
614 };
615 lexer.peek_char().now_or_never().unwrap().unwrap();
616 lexer.consume_char();
617
618 let result = lexer.braced_param(0).now_or_never().unwrap();
619 let param = result.unwrap().unwrap();
620 assert_eq!(param.param, Param::from(SpecialParam::Number));
621 assert_matches!(param.modifier, Modifier::Switch(switch) => {
622 assert_eq!(switch.r#type, SwitchType::Alter);
623 assert_eq!(switch.condition, SwitchCondition::Unset);
624 assert_eq!(switch.word.to_string(), "?");
625 });
626 assert_eq!(*param.location.code.value.borrow(), "${#+?}<");
628 assert_eq!(param.location.code.start_line_number.get(), 1);
629 assert_eq!(*param.location.code.source, Source::Unknown);
630 assert_eq!(param.location.range, 0..6);
631
632 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
633 }
634
635 #[test]
636 fn lexer_braced_param_hash_suffix_default() {
637 let mut lexer = Lexer::from_memory("${#--}<", Source::Unknown);
638 let mut lexer = WordLexer {
639 lexer: &mut lexer,
640 context: WordContext::Word,
641 };
642 lexer.peek_char().now_or_never().unwrap().unwrap();
643 lexer.consume_char();
644
645 let result = lexer.braced_param(0).now_or_never().unwrap();
646 let param = result.unwrap().unwrap();
647 assert_eq!(param.param, Param::from(SpecialParam::Number));
648 assert_matches!(param.modifier, Modifier::Switch(switch) => {
649 assert_eq!(switch.r#type, SwitchType::Default);
650 assert_eq!(switch.condition, SwitchCondition::Unset);
651 assert_eq!(switch.word.to_string(), "-");
652 });
653 assert_eq!(*param.location.code.value.borrow(), "${#--}<");
655 assert_eq!(param.location.code.start_line_number.get(), 1);
656 assert_eq!(*param.location.code.source, Source::Unknown);
657 assert_eq!(param.location.range, 0..6);
658
659 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
660 }
661
662 #[test]
663 fn lexer_braced_param_hash_suffix_assign() {
664 let mut lexer = Lexer::from_memory("${#=?}<", Source::Unknown);
665 let mut lexer = WordLexer {
666 lexer: &mut lexer,
667 context: WordContext::Word,
668 };
669 lexer.peek_char().now_or_never().unwrap().unwrap();
670 lexer.consume_char();
671
672 let result = lexer.braced_param(0).now_or_never().unwrap();
673 let param = result.unwrap().unwrap();
674 assert_eq!(param.param, Param::from(SpecialParam::Number));
675 assert_matches!(param.modifier, Modifier::Switch(switch) => {
676 assert_eq!(switch.r#type, SwitchType::Assign);
677 assert_eq!(switch.condition, SwitchCondition::Unset);
678 assert_eq!(switch.word.to_string(), "?");
679 });
680 assert_eq!(*param.location.code.value.borrow(), "${#=?}<");
682 assert_eq!(param.location.code.start_line_number.get(), 1);
683 assert_eq!(*param.location.code.source, Source::Unknown);
684 assert_eq!(param.location.range, 0..6);
685
686 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
687 }
688
689 #[test]
690 fn lexer_braced_param_hash_suffix_error() {
691 let mut lexer = Lexer::from_memory("${#??}<", Source::Unknown);
692 let mut lexer = WordLexer {
693 lexer: &mut lexer,
694 context: WordContext::Word,
695 };
696 lexer.peek_char().now_or_never().unwrap().unwrap();
697 lexer.consume_char();
698
699 let result = lexer.braced_param(0).now_or_never().unwrap();
700 let param = result.unwrap().unwrap();
701 assert_eq!(param.param, Param::from(SpecialParam::Number));
702 assert_matches!(param.modifier, Modifier::Switch(switch) => {
703 assert_eq!(switch.r#type, SwitchType::Error);
704 assert_eq!(switch.condition, SwitchCondition::Unset);
705 assert_eq!(switch.word.to_string(), "?");
706 });
707 assert_eq!(*param.location.code.value.borrow(), "${#??}<");
709 assert_eq!(param.location.code.start_line_number.get(), 1);
710 assert_eq!(*param.location.code.source, Source::Unknown);
711 assert_eq!(param.location.range, 0..6);
712
713 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
714 }
715
716 #[test]
717 fn lexer_braced_param_hash_suffix_with_colon() {
718 let mut lexer = Lexer::from_memory("${#:-}<", Source::Unknown);
719 let mut lexer = WordLexer {
720 lexer: &mut lexer,
721 context: WordContext::Word,
722 };
723 lexer.peek_char().now_or_never().unwrap().unwrap();
724 lexer.consume_char();
725
726 let result = lexer.braced_param(0).now_or_never().unwrap();
727 let param = result.unwrap().unwrap();
728 assert_eq!(param.param, Param::from(SpecialParam::Number));
729 assert_matches!(param.modifier, Modifier::Switch(switch) => {
730 assert_eq!(switch.r#type, SwitchType::Default);
731 assert_eq!(switch.condition, SwitchCondition::UnsetOrEmpty);
732 assert_eq!(switch.word.to_string(), "");
733 });
734 assert_eq!(*param.location.code.value.borrow(), "${#:-}<");
736 assert_eq!(param.location.code.start_line_number.get(), 1);
737 assert_eq!(*param.location.code.source, Source::Unknown);
738 assert_eq!(param.location.range, 0..6);
739
740 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
741 }
742
743 #[test]
744 fn lexer_braced_param_hash_with_longest_prefix_trim() {
745 let mut lexer = Lexer::from_memory("${###};", Source::Unknown);
746 let mut lexer = WordLexer {
747 lexer: &mut lexer,
748 context: WordContext::Word,
749 };
750 lexer.peek_char().now_or_never().unwrap().unwrap();
751 lexer.consume_char();
752
753 let result = lexer.braced_param(0).now_or_never().unwrap();
754 let param = result.unwrap().unwrap();
755 assert_eq!(param.param, Param::from(SpecialParam::Number));
756 assert_matches!(param.modifier, Modifier::Trim(trim) => {
757 assert_eq!(trim.side, TrimSide::Prefix);
758 assert_eq!(trim.length, TrimLength::Longest);
759 assert_eq!(trim.pattern.to_string(), "");
760 });
761 assert_eq!(*param.location.code.value.borrow(), "${###};");
763 assert_eq!(param.location.code.start_line_number.get(), 1);
764 assert_eq!(*param.location.code.source, Source::Unknown);
765 assert_eq!(param.location.range, 0..6);
766
767 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(';')));
768 }
769
770 #[test]
771 fn lexer_braced_param_hash_with_suffix_trim() {
772 let mut lexer = Lexer::from_memory("${#%};", Source::Unknown);
773 let mut lexer = WordLexer {
774 lexer: &mut lexer,
775 context: WordContext::Word,
776 };
777 lexer.peek_char().now_or_never().unwrap().unwrap();
778 lexer.consume_char();
779
780 let result = lexer.braced_param(0).now_or_never().unwrap();
781 let param = result.unwrap().unwrap();
782 assert_eq!(param.param, Param::from(SpecialParam::Number));
783 assert_matches!(param.modifier, Modifier::Trim(trim) => {
784 assert_eq!(trim.side, TrimSide::Suffix);
785 assert_eq!(trim.length, TrimLength::Shortest);
786 assert_eq!(trim.pattern.to_string(), "");
787 });
788 assert_eq!(*param.location.code.value.borrow(), "${#%};");
790 assert_eq!(param.location.code.start_line_number.get(), 1);
791 assert_eq!(*param.location.code.source, Source::Unknown);
792 assert_eq!(param.location.range, 0..5);
793
794 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(';')));
795 }
796
797 #[test]
798 fn lexer_braced_param_multiple_modifier() {
799 let mut lexer = Lexer::from_memory("${#x+};", Source::Unknown);
800 let mut lexer = WordLexer {
801 lexer: &mut lexer,
802 context: WordContext::Word,
803 };
804 lexer.peek_char().now_or_never().unwrap().unwrap();
805 lexer.consume_char();
806
807 let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
808 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::MultipleModifier));
809 assert_eq!(*e.location.code.value.borrow(), "${#x+};");
810 assert_eq!(e.location.range, 4..5);
811 }
812
813 #[test]
814 fn lexer_braced_param_line_continuations() {
815 let mut lexer = Lexer::from_memory("${\\\n#\\\n\\\na_\\\n1\\\n\\\n}z", Source::Unknown);
816 let mut lexer = WordLexer {
817 lexer: &mut lexer,
818 context: WordContext::Word,
819 };
820 lexer.peek_char().now_or_never().unwrap().unwrap();
821 lexer.consume_char();
822
823 let result = lexer.braced_param(0).now_or_never().unwrap();
824 let param = result.unwrap().unwrap();
825 assert_eq!(param.param, Param::variable("a_1"));
826 assert_eq!(param.modifier, Modifier::Length);
827 assert_eq!(
829 *param.location.code.value.borrow(),
830 "${\\\n#\\\n\\\na_\\\n1\\\n\\\n}z"
831 );
832 assert_eq!(param.location.code.start_line_number.get(), 1);
833 assert_eq!(*param.location.code.source, Source::Unknown);
834 assert_eq!(param.location.range, 0..19);
835
836 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('z')));
837 }
838
839 #[test]
840 fn lexer_braced_param_line_continuations_hash() {
841 let mut lexer = Lexer::from_memory("${#\\\n\\\n}z", Source::Unknown);
842 let mut lexer = WordLexer {
843 lexer: &mut lexer,
844 context: WordContext::Word,
845 };
846 lexer.peek_char().now_or_never().unwrap().unwrap();
847 lexer.consume_char();
848
849 let result = lexer.braced_param(0).now_or_never().unwrap();
850 let param = result.unwrap().unwrap();
851 assert_eq!(param.param, Param::from(SpecialParam::Number));
852 assert_eq!(param.modifier, Modifier::None);
853 assert_eq!(*param.location.code.value.borrow(), "${#\\\n\\\n}z");
855 assert_eq!(param.location.code.start_line_number.get(), 1);
856 assert_eq!(*param.location.code.source, Source::Unknown);
857 assert_eq!(param.location.range, 0..8);
858
859 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('z')));
860 }
861}