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::with_code("$foo");
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::with_code("${@};");
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::with_code("X${foo_123}<");
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::with_code("${123}<");
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::with_code("${00}<");
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::with_code("${9999999999999999999999999999999999999999}");
320 let mut lexer = WordLexer {
321 lexer: &mut lexer,
322 context: WordContext::Word,
323 };
324 lexer.peek_char().now_or_never().unwrap().unwrap();
325 lexer.consume_char();
326
327 let result = lexer.braced_param(0).now_or_never().unwrap();
328 let param = result.unwrap().unwrap();
329 assert_eq!(param.param.r#type, ParamType::Positional(usize::MAX));
330 }
331
332 #[test]
333 fn lexer_braced_param_invalid_param() {
334 let mut lexer = Lexer::with_code("${0_0}");
335 let mut lexer = WordLexer {
336 lexer: &mut lexer,
337 context: WordContext::Word,
338 };
339 lexer.peek_char().now_or_never().unwrap().unwrap();
340 lexer.consume_char();
341
342 let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
343 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidParam));
344 assert_eq!(*e.location.code.value.borrow(), "${0_0}");
345 assert_eq!(e.location.code.start_line_number.get(), 1);
346 assert_eq!(*e.location.code.source, Source::Unknown);
347 assert_eq!(e.location.range, 2..5);
348 }
349
350 #[test]
353 fn lexer_braced_param_special_zero() {
354 let mut lexer = Lexer::with_code("${0}<");
355 let mut lexer = WordLexer {
356 lexer: &mut lexer,
357 context: WordContext::Word,
358 };
359 lexer.peek_char().now_or_never().unwrap().unwrap();
360 lexer.consume_char();
361
362 let result = lexer.braced_param(0).now_or_never().unwrap();
363 let param = result.unwrap().unwrap();
364 assert_eq!(param.param.id, "0");
365 assert_eq!(param.param.r#type, ParamType::Special(SpecialParam::Zero));
366 assert_eq!(param.modifier, Modifier::None);
367 assert_eq!(*param.location.code.value.borrow(), "${0}<");
369 assert_eq!(param.location.code.start_line_number.get(), 1);
370 assert_eq!(*param.location.code.source, Source::Unknown);
371 assert_eq!(param.location.range, 0..4);
372
373 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
374 }
375
376 #[test]
377 fn lexer_braced_param_special_hash() {
378 let mut lexer = Lexer::with_code("${#}<");
379 let mut lexer = WordLexer {
380 lexer: &mut lexer,
381 context: WordContext::Word,
382 };
383 lexer.peek_char().now_or_never().unwrap().unwrap();
384 lexer.consume_char();
385
386 let result = lexer.braced_param(0).now_or_never().unwrap();
387 let param = result.unwrap().unwrap();
388 assert_eq!(param.param, Param::from(SpecialParam::Number));
389 assert_eq!(param.modifier, Modifier::None);
390 assert_eq!(*param.location.code.value.borrow(), "${#}<");
392 assert_eq!(param.location.code.start_line_number.get(), 1);
393 assert_eq!(*param.location.code.source, Source::Unknown);
394 assert_eq!(param.location.range, 0..4);
395
396 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
397 }
398
399 #[test]
400 fn lexer_braced_param_missing_name() {
401 let mut lexer = Lexer::with_code("${};");
402 let mut lexer = WordLexer {
403 lexer: &mut lexer,
404 context: WordContext::Word,
405 };
406 lexer.peek_char().now_or_never().unwrap().unwrap();
407 lexer.consume_char();
408
409 let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
410 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::EmptyParam));
411 assert_eq!(*e.location.code.value.borrow(), "${};");
412 assert_eq!(e.location.code.start_line_number.get(), 1);
413 assert_eq!(*e.location.code.source, Source::Unknown);
414 assert_eq!(e.location.range, 2..3);
415 }
416
417 #[test]
418 fn lexer_braced_param_unclosed_without_name() {
419 let mut lexer = Lexer::with_code("${;");
420 let mut lexer = WordLexer {
421 lexer: &mut lexer,
422 context: WordContext::Word,
423 };
424 lexer.peek_char().now_or_never().unwrap().unwrap();
425 lexer.consume_char();
426
427 let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
428 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::EmptyParam));
429 assert_eq!(*e.location.code.value.borrow(), "${;");
430 assert_eq!(e.location.code.start_line_number.get(), 1);
431 assert_eq!(*e.location.code.source, Source::Unknown);
432 assert_eq!(e.location.range, 2..3);
433 }
434
435 #[test]
436 fn lexer_braced_param_unclosed_with_name() {
437 let mut lexer = Lexer::with_code("${_;");
438 let mut lexer = WordLexer {
439 lexer: &mut lexer,
440 context: WordContext::Word,
441 };
442 lexer.peek_char().now_or_never().unwrap().unwrap();
443 lexer.consume_char();
444
445 let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
446 assert_matches!(e.cause,
447 ErrorCause::Syntax(SyntaxError::UnclosedParam { opening_location }) => {
448 assert_eq!(*opening_location.code.value.borrow(), "${_;");
449 assert_eq!(opening_location.code.start_line_number.get(), 1);
450 assert_eq!(*opening_location.code.source, Source::Unknown);
451 assert_eq!(opening_location.range, 0..2);
452 });
453 assert_eq!(*e.location.code.value.borrow(), "${_;");
454 assert_eq!(e.location.code.start_line_number.get(), 1);
455 assert_eq!(*e.location.code.source, Source::Unknown);
456 assert_eq!(e.location.range, 3..4);
457 }
458
459 #[test]
460 fn lexer_braced_param_length_alphanumeric_name() {
461 let mut lexer = Lexer::with_code("${#foo_123}<");
462 let mut lexer = WordLexer {
463 lexer: &mut lexer,
464 context: WordContext::Word,
465 };
466 lexer.peek_char().now_or_never().unwrap().unwrap();
467 lexer.consume_char();
468
469 let result = lexer.braced_param(0).now_or_never().unwrap();
470 let param = result.unwrap().unwrap();
471 assert_eq!(param.param, Param::variable("foo_123"));
472 assert_eq!(param.modifier, Modifier::Length);
473 assert_eq!(*param.location.code.value.borrow(), "${#foo_123}<");
475 assert_eq!(param.location.code.start_line_number.get(), 1);
476 assert_eq!(*param.location.code.source, Source::Unknown);
477 assert_eq!(param.location.range, 0..11);
478
479 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
480 }
481
482 #[test]
483 fn lexer_braced_param_length_hash() {
484 let mut lexer = Lexer::with_code("${##}<");
485 let mut lexer = WordLexer {
486 lexer: &mut lexer,
487 context: WordContext::Word,
488 };
489 lexer.peek_char().now_or_never().unwrap().unwrap();
490 lexer.consume_char();
491
492 let result = lexer.braced_param(0).now_or_never().unwrap();
493 let param = result.unwrap().unwrap();
494 assert_eq!(param.param, Param::from(SpecialParam::Number));
495 assert_eq!(param.modifier, Modifier::Length);
496 assert_eq!(*param.location.code.value.borrow(), "${##}<");
498 assert_eq!(param.location.code.start_line_number.get(), 1);
499 assert_eq!(*param.location.code.source, Source::Unknown);
500 assert_eq!(param.location.range, 0..5);
501
502 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
503 }
504
505 #[test]
506 fn lexer_braced_param_length_question() {
507 let mut lexer = Lexer::with_code("${#?}<");
508 let mut lexer = WordLexer {
509 lexer: &mut lexer,
510 context: WordContext::Word,
511 };
512 lexer.peek_char().now_or_never().unwrap().unwrap();
513 lexer.consume_char();
514
515 let result = lexer.braced_param(0).now_or_never().unwrap();
516 let param = result.unwrap().unwrap();
517 assert_eq!(param.param, Param::from(SpecialParam::Question));
518 assert_eq!(param.modifier, Modifier::Length);
519 assert_eq!(*param.location.code.value.borrow(), "${#?}<");
521 assert_eq!(param.location.code.start_line_number.get(), 1);
522 assert_eq!(*param.location.code.source, Source::Unknown);
523 assert_eq!(param.location.range, 0..5);
524
525 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
526 }
527
528 #[test]
529 fn lexer_braced_param_length_hyphen() {
530 let mut lexer = Lexer::with_code("${#-}<");
531 let mut lexer = WordLexer {
532 lexer: &mut lexer,
533 context: WordContext::Word,
534 };
535 lexer.peek_char().now_or_never().unwrap().unwrap();
536 lexer.consume_char();
537
538 let result = lexer.braced_param(0).now_or_never().unwrap();
539 let param = result.unwrap().unwrap();
540 assert_eq!(param.param, Param::from(SpecialParam::Hyphen));
541 assert_eq!(param.modifier, Modifier::Length);
542 assert_eq!(*param.location.code.value.borrow(), "${#-}<");
544 assert_eq!(param.location.code.start_line_number.get(), 1);
545 assert_eq!(*param.location.code.source, Source::Unknown);
546 assert_eq!(param.location.range, 0..5);
547
548 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
549 }
550
551 #[test]
552 fn lexer_braced_param_switch_minimum() {
553 let mut lexer = Lexer::with_code("${x+})");
554 let mut lexer = WordLexer {
555 lexer: &mut lexer,
556 context: WordContext::Word,
557 };
558 lexer.peek_char().now_or_never().unwrap().unwrap();
559 lexer.consume_char();
560
561 let result = lexer.braced_param(0).now_or_never().unwrap();
562 let param = result.unwrap().unwrap();
563 assert_eq!(param.param, Param::variable("x"));
564 assert_matches!(param.modifier, Modifier::Switch(switch) => {
565 assert_eq!(switch.r#type, SwitchType::Alter);
566 assert_eq!(switch.condition, SwitchCondition::Unset);
567 assert_eq!(switch.word.to_string(), "");
568 });
569 assert_eq!(*param.location.code.value.borrow(), "${x+})");
571 assert_eq!(param.location.code.start_line_number.get(), 1);
572 assert_eq!(*param.location.code.source, Source::Unknown);
573 assert_eq!(param.location.range, 0..5);
574
575 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(')')));
576 }
577
578 #[test]
579 fn lexer_braced_param_switch_full() {
580 let mut lexer = Lexer::with_code("${foo:?'!'})");
581 let mut lexer = WordLexer {
582 lexer: &mut lexer,
583 context: WordContext::Word,
584 };
585 lexer.peek_char().now_or_never().unwrap().unwrap();
586 lexer.consume_char();
587
588 let result = lexer.braced_param(0).now_or_never().unwrap();
589 let param = result.unwrap().unwrap();
590 assert_eq!(param.param, Param::variable("foo"));
591 assert_matches!(param.modifier, Modifier::Switch(switch) => {
592 assert_eq!(switch.r#type, SwitchType::Error);
593 assert_eq!(switch.condition, SwitchCondition::UnsetOrEmpty);
594 assert_eq!(switch.word.to_string(), "'!'");
595 });
596 assert_eq!(*param.location.code.value.borrow(), "${foo:?'!'})");
598 assert_eq!(param.location.code.start_line_number.get(), 1);
599 assert_eq!(*param.location.code.source, Source::Unknown);
600 assert_eq!(param.location.range, 0..11);
601
602 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(')')));
603 }
604
605 #[test]
606 fn lexer_braced_param_hash_suffix_alter() {
607 let mut lexer = Lexer::with_code("${#+?}<");
608 let mut lexer = WordLexer {
609 lexer: &mut lexer,
610 context: WordContext::Word,
611 };
612 lexer.peek_char().now_or_never().unwrap().unwrap();
613 lexer.consume_char();
614
615 let result = lexer.braced_param(0).now_or_never().unwrap();
616 let param = result.unwrap().unwrap();
617 assert_eq!(param.param, Param::from(SpecialParam::Number));
618 assert_matches!(param.modifier, Modifier::Switch(switch) => {
619 assert_eq!(switch.r#type, SwitchType::Alter);
620 assert_eq!(switch.condition, SwitchCondition::Unset);
621 assert_eq!(switch.word.to_string(), "?");
622 });
623 assert_eq!(*param.location.code.value.borrow(), "${#+?}<");
625 assert_eq!(param.location.code.start_line_number.get(), 1);
626 assert_eq!(*param.location.code.source, Source::Unknown);
627 assert_eq!(param.location.range, 0..6);
628
629 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
630 }
631
632 #[test]
633 fn lexer_braced_param_hash_suffix_default() {
634 let mut lexer = Lexer::with_code("${#--}<");
635 let mut lexer = WordLexer {
636 lexer: &mut lexer,
637 context: WordContext::Word,
638 };
639 lexer.peek_char().now_or_never().unwrap().unwrap();
640 lexer.consume_char();
641
642 let result = lexer.braced_param(0).now_or_never().unwrap();
643 let param = result.unwrap().unwrap();
644 assert_eq!(param.param, Param::from(SpecialParam::Number));
645 assert_matches!(param.modifier, Modifier::Switch(switch) => {
646 assert_eq!(switch.r#type, SwitchType::Default);
647 assert_eq!(switch.condition, SwitchCondition::Unset);
648 assert_eq!(switch.word.to_string(), "-");
649 });
650 assert_eq!(*param.location.code.value.borrow(), "${#--}<");
652 assert_eq!(param.location.code.start_line_number.get(), 1);
653 assert_eq!(*param.location.code.source, Source::Unknown);
654 assert_eq!(param.location.range, 0..6);
655
656 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
657 }
658
659 #[test]
660 fn lexer_braced_param_hash_suffix_assign() {
661 let mut lexer = Lexer::with_code("${#=?}<");
662 let mut lexer = WordLexer {
663 lexer: &mut lexer,
664 context: WordContext::Word,
665 };
666 lexer.peek_char().now_or_never().unwrap().unwrap();
667 lexer.consume_char();
668
669 let result = lexer.braced_param(0).now_or_never().unwrap();
670 let param = result.unwrap().unwrap();
671 assert_eq!(param.param, Param::from(SpecialParam::Number));
672 assert_matches!(param.modifier, Modifier::Switch(switch) => {
673 assert_eq!(switch.r#type, SwitchType::Assign);
674 assert_eq!(switch.condition, SwitchCondition::Unset);
675 assert_eq!(switch.word.to_string(), "?");
676 });
677 assert_eq!(*param.location.code.value.borrow(), "${#=?}<");
679 assert_eq!(param.location.code.start_line_number.get(), 1);
680 assert_eq!(*param.location.code.source, Source::Unknown);
681 assert_eq!(param.location.range, 0..6);
682
683 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
684 }
685
686 #[test]
687 fn lexer_braced_param_hash_suffix_error() {
688 let mut lexer = Lexer::with_code("${#??}<");
689 let mut lexer = WordLexer {
690 lexer: &mut lexer,
691 context: WordContext::Word,
692 };
693 lexer.peek_char().now_or_never().unwrap().unwrap();
694 lexer.consume_char();
695
696 let result = lexer.braced_param(0).now_or_never().unwrap();
697 let param = result.unwrap().unwrap();
698 assert_eq!(param.param, Param::from(SpecialParam::Number));
699 assert_matches!(param.modifier, Modifier::Switch(switch) => {
700 assert_eq!(switch.r#type, SwitchType::Error);
701 assert_eq!(switch.condition, SwitchCondition::Unset);
702 assert_eq!(switch.word.to_string(), "?");
703 });
704 assert_eq!(*param.location.code.value.borrow(), "${#??}<");
706 assert_eq!(param.location.code.start_line_number.get(), 1);
707 assert_eq!(*param.location.code.source, Source::Unknown);
708 assert_eq!(param.location.range, 0..6);
709
710 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
711 }
712
713 #[test]
714 fn lexer_braced_param_hash_suffix_with_colon() {
715 let mut lexer = Lexer::with_code("${#:-}<");
716 let mut lexer = WordLexer {
717 lexer: &mut lexer,
718 context: WordContext::Word,
719 };
720 lexer.peek_char().now_or_never().unwrap().unwrap();
721 lexer.consume_char();
722
723 let result = lexer.braced_param(0).now_or_never().unwrap();
724 let param = result.unwrap().unwrap();
725 assert_eq!(param.param, Param::from(SpecialParam::Number));
726 assert_matches!(param.modifier, Modifier::Switch(switch) => {
727 assert_eq!(switch.r#type, SwitchType::Default);
728 assert_eq!(switch.condition, SwitchCondition::UnsetOrEmpty);
729 assert_eq!(switch.word.to_string(), "");
730 });
731 assert_eq!(*param.location.code.value.borrow(), "${#:-}<");
733 assert_eq!(param.location.code.start_line_number.get(), 1);
734 assert_eq!(*param.location.code.source, Source::Unknown);
735 assert_eq!(param.location.range, 0..6);
736
737 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
738 }
739
740 #[test]
741 fn lexer_braced_param_hash_with_longest_prefix_trim() {
742 let mut lexer = Lexer::with_code("${###};");
743 let mut lexer = WordLexer {
744 lexer: &mut lexer,
745 context: WordContext::Word,
746 };
747 lexer.peek_char().now_or_never().unwrap().unwrap();
748 lexer.consume_char();
749
750 let result = lexer.braced_param(0).now_or_never().unwrap();
751 let param = result.unwrap().unwrap();
752 assert_eq!(param.param, Param::from(SpecialParam::Number));
753 assert_matches!(param.modifier, Modifier::Trim(trim) => {
754 assert_eq!(trim.side, TrimSide::Prefix);
755 assert_eq!(trim.length, TrimLength::Longest);
756 assert_eq!(trim.pattern.to_string(), "");
757 });
758 assert_eq!(*param.location.code.value.borrow(), "${###};");
760 assert_eq!(param.location.code.start_line_number.get(), 1);
761 assert_eq!(*param.location.code.source, Source::Unknown);
762 assert_eq!(param.location.range, 0..6);
763
764 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(';')));
765 }
766
767 #[test]
768 fn lexer_braced_param_hash_with_suffix_trim() {
769 let mut lexer = Lexer::with_code("${#%};");
770 let mut lexer = WordLexer {
771 lexer: &mut lexer,
772 context: WordContext::Word,
773 };
774 lexer.peek_char().now_or_never().unwrap().unwrap();
775 lexer.consume_char();
776
777 let result = lexer.braced_param(0).now_or_never().unwrap();
778 let param = result.unwrap().unwrap();
779 assert_eq!(param.param, Param::from(SpecialParam::Number));
780 assert_matches!(param.modifier, Modifier::Trim(trim) => {
781 assert_eq!(trim.side, TrimSide::Suffix);
782 assert_eq!(trim.length, TrimLength::Shortest);
783 assert_eq!(trim.pattern.to_string(), "");
784 });
785 assert_eq!(*param.location.code.value.borrow(), "${#%};");
787 assert_eq!(param.location.code.start_line_number.get(), 1);
788 assert_eq!(*param.location.code.source, Source::Unknown);
789 assert_eq!(param.location.range, 0..5);
790
791 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(';')));
792 }
793
794 #[test]
795 fn lexer_braced_param_multiple_modifier() {
796 let mut lexer = Lexer::with_code("${#x+};");
797 let mut lexer = WordLexer {
798 lexer: &mut lexer,
799 context: WordContext::Word,
800 };
801 lexer.peek_char().now_or_never().unwrap().unwrap();
802 lexer.consume_char();
803
804 let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
805 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::MultipleModifier));
806 assert_eq!(*e.location.code.value.borrow(), "${#x+};");
807 assert_eq!(e.location.range, 4..5);
808 }
809
810 #[test]
811 fn lexer_braced_param_line_continuations() {
812 let mut lexer = Lexer::with_code("${\\\n#\\\n\\\na_\\\n1\\\n\\\n}z");
813 let mut lexer = WordLexer {
814 lexer: &mut lexer,
815 context: WordContext::Word,
816 };
817 lexer.peek_char().now_or_never().unwrap().unwrap();
818 lexer.consume_char();
819
820 let result = lexer.braced_param(0).now_or_never().unwrap();
821 let param = result.unwrap().unwrap();
822 assert_eq!(param.param, Param::variable("a_1"));
823 assert_eq!(param.modifier, Modifier::Length);
824 assert_eq!(
826 *param.location.code.value.borrow(),
827 "${\\\n#\\\n\\\na_\\\n1\\\n\\\n}z"
828 );
829 assert_eq!(param.location.code.start_line_number.get(), 1);
830 assert_eq!(*param.location.code.source, Source::Unknown);
831 assert_eq!(param.location.range, 0..19);
832
833 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('z')));
834 }
835
836 #[test]
837 fn lexer_braced_param_line_continuations_hash() {
838 let mut lexer = Lexer::with_code("${#\\\n\\\n}z");
839 let mut lexer = WordLexer {
840 lexer: &mut lexer,
841 context: WordContext::Word,
842 };
843 lexer.peek_char().now_or_never().unwrap().unwrap();
844 lexer.consume_char();
845
846 let result = lexer.braced_param(0).now_or_never().unwrap();
847 let param = result.unwrap().unwrap();
848 assert_eq!(param.param, Param::from(SpecialParam::Number));
849 assert_eq!(param.modifier, Modifier::None);
850 assert_eq!(*param.location.code.value.borrow(), "${#\\\n\\\n}z");
852 assert_eq!(param.location.code.start_line_number.get(), 1);
853 assert_eq!(*param.location.code.source, Source::Unknown);
854 assert_eq!(param.location.range, 0..8);
855
856 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('z')));
857 }
858}