use super::core::WordLexer;
use super::raw_param::is_portable_name_char;
use crate::parser::core::Result;
use crate::parser::error::Error;
use crate::parser::error::SyntaxError;
use crate::syntax::BracedParam;
use crate::syntax::Modifier;
use crate::syntax::Param;
use crate::syntax::ParamType;
use crate::syntax::SpecialParam;
use std::num::IntErrorKind;
pub fn is_name_char(c: char) -> bool {
is_portable_name_char(c)
}
#[must_use]
fn type_of_id(id: &str) -> Option<ParamType> {
if id == "0" {
return Some(ParamType::Special(SpecialParam::Zero));
}
if id.starts_with(|c: char| c.is_ascii_digit()) {
return match id.parse() {
Ok(index) => Some(ParamType::Positional(index)),
Err(e) => match e.kind() {
IntErrorKind::PosOverflow => Some(ParamType::Positional(usize::MAX)),
_ => None,
},
};
}
Some(ParamType::Variable)
}
impl WordLexer<'_, '_> {
async fn has_length_prefix(&mut self) -> Result<bool> {
if !self.skip_if(|c| c == '#').await? {
return Ok(false);
}
if let Some(c) = self.peek_char().await? {
if matches!(c, '}' | '+' | '=' | ':' | '%') {
return Ok(false);
}
if matches!(c, '-' | '?' | '#') {
self.consume_char();
if let Some(c) = self.peek_char().await? {
return Ok(c == '}');
}
}
}
Ok(true)
}
async fn length_prefix(&mut self) -> Result<bool> {
let initial_index = self.index();
let has_length_prefix = self.has_length_prefix().await?;
self.rewind(initial_index);
if has_length_prefix {
self.peek_char().await?;
self.consume_char();
}
Ok(has_length_prefix)
}
pub async fn braced_param(&mut self, start_index: usize) -> Result<Option<BracedParam>> {
if !self.skip_if(|c| c == '{').await? {
return Ok(None);
}
let opening_location = self.location_range(start_index..self.index());
let has_length_prefix = self.length_prefix().await?;
let param_start_index = self.index();
let c = self.peek_char().await?.unwrap();
let param = if is_name_char(c) {
self.consume_char();
let mut id = c.to_string();
while let Some(c) = self.consume_char_if(is_name_char).await? {
id.push(c.value);
}
let Some(r#type) = type_of_id(&id) else {
let cause = SyntaxError::InvalidParam.into();
let location = self.location_range(param_start_index..self.index());
return Err(Error { cause, location });
};
Param { id, r#type }
} else if let Some(special) = SpecialParam::from_char(c) {
self.consume_char();
Param {
id: c.to_string(),
r#type: special.into(),
}
} else {
let cause = SyntaxError::EmptyParam.into();
let location = self.location().await?.clone();
return Err(Error { cause, location });
};
let suffix_location = self.location().await?.clone();
let suffix = self.suffix_modifier().await?;
if !self.skip_if(|c| c == '}').await? {
let cause = SyntaxError::UnclosedParam { opening_location }.into();
let location = self.location().await?.clone();
return Err(Error { cause, location });
}
let modifier = match (has_length_prefix, suffix) {
(true, Modifier::None) => Modifier::Length,
(true, _) => {
let cause = SyntaxError::MultipleModifier.into();
let location = suffix_location;
return Err(Error { cause, location });
}
(false, suffix) => suffix,
};
Ok(Some(BracedParam {
param,
modifier,
location: self.location_range(start_index..self.index()),
}))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::error::ErrorCause;
use crate::parser::lex::Lexer;
use crate::parser::lex::WordContext;
use crate::source::Source;
use crate::syntax::SwitchCondition;
use crate::syntax::SwitchType;
use crate::syntax::TrimLength;
use crate::syntax::TrimSide;
use assert_matches::assert_matches;
use futures_util::FutureExt;
#[test]
fn lexer_braced_param_none() {
let mut lexer = Lexer::from_memory("$foo", Source::Unknown);
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
assert_eq!(lexer.braced_param(0).now_or_never().unwrap(), Ok(None));
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('f')));
}
#[test]
fn lexer_braced_param_minimum() {
let mut lexer = Lexer::from_memory("${@};", Source::Unknown);
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::from(SpecialParam::At));
assert_eq!(param.modifier, Modifier::None);
assert_eq!(*param.location.code.value.borrow(), "${@};");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..4);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(';')));
}
#[test]
fn lexer_braced_param_alphanumeric_name() {
let mut lexer = Lexer::from_memory("X${foo_123}<", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(1).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::variable("foo_123"));
assert_eq!(param.modifier, Modifier::None);
assert_eq!(*param.location.code.value.borrow(), "X${foo_123}<");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 1..11);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
}
#[test]
fn lexer_braced_param_positional() {
let mut lexer = Lexer::from_memory("${123}<", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::from(123));
assert_eq!(param.modifier, Modifier::None);
assert_eq!(*param.location.code.value.borrow(), "${123}<");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..6);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
}
#[test]
fn lexer_braced_param_positional_zero() {
let mut lexer = Lexer::from_memory("${00}<", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param.id, "00");
assert_eq!(param.param.r#type, ParamType::Positional(0));
assert_eq!(param.modifier, Modifier::None);
assert_eq!(*param.location.code.value.borrow(), "${00}<");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..5);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
}
#[test]
fn lexer_braced_param_positional_overflow() {
let mut lexer = Lexer::from_memory(
"${9999999999999999999999999999999999999999}",
Source::Unknown,
);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param.r#type, ParamType::Positional(usize::MAX));
}
#[test]
fn lexer_braced_param_invalid_param() {
let mut lexer = Lexer::from_memory("${0_0}", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidParam));
assert_eq!(*e.location.code.value.borrow(), "${0_0}");
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(*e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 2..5);
}
#[test]
fn lexer_braced_param_special_zero() {
let mut lexer = Lexer::from_memory("${0}<", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param.id, "0");
assert_eq!(param.param.r#type, ParamType::Special(SpecialParam::Zero));
assert_eq!(param.modifier, Modifier::None);
assert_eq!(*param.location.code.value.borrow(), "${0}<");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..4);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
}
#[test]
fn lexer_braced_param_special_hash() {
let mut lexer = Lexer::from_memory("${#}<", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::from(SpecialParam::Number));
assert_eq!(param.modifier, Modifier::None);
assert_eq!(*param.location.code.value.borrow(), "${#}<");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..4);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
}
#[test]
fn lexer_braced_param_missing_name() {
let mut lexer = Lexer::from_memory("${};", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::EmptyParam));
assert_eq!(*e.location.code.value.borrow(), "${};");
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(*e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 2..3);
}
#[test]
fn lexer_braced_param_unclosed_without_name() {
let mut lexer = Lexer::from_memory("${;", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::EmptyParam));
assert_eq!(*e.location.code.value.borrow(), "${;");
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(*e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 2..3);
}
#[test]
fn lexer_braced_param_unclosed_with_name() {
let mut lexer = Lexer::from_memory("${_;", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
assert_matches!(e.cause,
ErrorCause::Syntax(SyntaxError::UnclosedParam { opening_location }) => {
assert_eq!(*opening_location.code.value.borrow(), "${_;");
assert_eq!(opening_location.code.start_line_number.get(), 1);
assert_eq!(*opening_location.code.source, Source::Unknown);
assert_eq!(opening_location.range, 0..2);
});
assert_eq!(*e.location.code.value.borrow(), "${_;");
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(*e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 3..4);
}
#[test]
fn lexer_braced_param_length_alphanumeric_name() {
let mut lexer = Lexer::from_memory("${#foo_123}<", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::variable("foo_123"));
assert_eq!(param.modifier, Modifier::Length);
assert_eq!(*param.location.code.value.borrow(), "${#foo_123}<");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..11);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
}
#[test]
fn lexer_braced_param_length_hash() {
let mut lexer = Lexer::from_memory("${##}<", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::from(SpecialParam::Number));
assert_eq!(param.modifier, Modifier::Length);
assert_eq!(*param.location.code.value.borrow(), "${##}<");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..5);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
}
#[test]
fn lexer_braced_param_length_question() {
let mut lexer = Lexer::from_memory("${#?}<", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::from(SpecialParam::Question));
assert_eq!(param.modifier, Modifier::Length);
assert_eq!(*param.location.code.value.borrow(), "${#?}<");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..5);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
}
#[test]
fn lexer_braced_param_length_hyphen() {
let mut lexer = Lexer::from_memory("${#-}<", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::from(SpecialParam::Hyphen));
assert_eq!(param.modifier, Modifier::Length);
assert_eq!(*param.location.code.value.borrow(), "${#-}<");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..5);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
}
#[test]
fn lexer_braced_param_switch_minimum() {
let mut lexer = Lexer::from_memory("${x+})", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::variable("x"));
assert_matches!(param.modifier, Modifier::Switch(switch) => {
assert_eq!(switch.r#type, SwitchType::Alter);
assert_eq!(switch.condition, SwitchCondition::Unset);
assert_eq!(switch.word.to_string(), "");
});
assert_eq!(*param.location.code.value.borrow(), "${x+})");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..5);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(')')));
}
#[test]
fn lexer_braced_param_switch_full() {
let mut lexer = Lexer::from_memory("${foo:?'!'})", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::variable("foo"));
assert_matches!(param.modifier, Modifier::Switch(switch) => {
assert_eq!(switch.r#type, SwitchType::Error);
assert_eq!(switch.condition, SwitchCondition::UnsetOrEmpty);
assert_eq!(switch.word.to_string(), "'!'");
});
assert_eq!(*param.location.code.value.borrow(), "${foo:?'!'})");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..11);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(')')));
}
#[test]
fn lexer_braced_param_hash_suffix_alter() {
let mut lexer = Lexer::from_memory("${#+?}<", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::from(SpecialParam::Number));
assert_matches!(param.modifier, Modifier::Switch(switch) => {
assert_eq!(switch.r#type, SwitchType::Alter);
assert_eq!(switch.condition, SwitchCondition::Unset);
assert_eq!(switch.word.to_string(), "?");
});
assert_eq!(*param.location.code.value.borrow(), "${#+?}<");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..6);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
}
#[test]
fn lexer_braced_param_hash_suffix_default() {
let mut lexer = Lexer::from_memory("${#--}<", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::from(SpecialParam::Number));
assert_matches!(param.modifier, Modifier::Switch(switch) => {
assert_eq!(switch.r#type, SwitchType::Default);
assert_eq!(switch.condition, SwitchCondition::Unset);
assert_eq!(switch.word.to_string(), "-");
});
assert_eq!(*param.location.code.value.borrow(), "${#--}<");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..6);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
}
#[test]
fn lexer_braced_param_hash_suffix_assign() {
let mut lexer = Lexer::from_memory("${#=?}<", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::from(SpecialParam::Number));
assert_matches!(param.modifier, Modifier::Switch(switch) => {
assert_eq!(switch.r#type, SwitchType::Assign);
assert_eq!(switch.condition, SwitchCondition::Unset);
assert_eq!(switch.word.to_string(), "?");
});
assert_eq!(*param.location.code.value.borrow(), "${#=?}<");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..6);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
}
#[test]
fn lexer_braced_param_hash_suffix_error() {
let mut lexer = Lexer::from_memory("${#??}<", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::from(SpecialParam::Number));
assert_matches!(param.modifier, Modifier::Switch(switch) => {
assert_eq!(switch.r#type, SwitchType::Error);
assert_eq!(switch.condition, SwitchCondition::Unset);
assert_eq!(switch.word.to_string(), "?");
});
assert_eq!(*param.location.code.value.borrow(), "${#??}<");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..6);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
}
#[test]
fn lexer_braced_param_hash_suffix_with_colon() {
let mut lexer = Lexer::from_memory("${#:-}<", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::from(SpecialParam::Number));
assert_matches!(param.modifier, Modifier::Switch(switch) => {
assert_eq!(switch.r#type, SwitchType::Default);
assert_eq!(switch.condition, SwitchCondition::UnsetOrEmpty);
assert_eq!(switch.word.to_string(), "");
});
assert_eq!(*param.location.code.value.borrow(), "${#:-}<");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..6);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
}
#[test]
fn lexer_braced_param_hash_with_longest_prefix_trim() {
let mut lexer = Lexer::from_memory("${###};", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::from(SpecialParam::Number));
assert_matches!(param.modifier, Modifier::Trim(trim) => {
assert_eq!(trim.side, TrimSide::Prefix);
assert_eq!(trim.length, TrimLength::Longest);
assert_eq!(trim.pattern.to_string(), "");
});
assert_eq!(*param.location.code.value.borrow(), "${###};");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..6);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(';')));
}
#[test]
fn lexer_braced_param_hash_with_suffix_trim() {
let mut lexer = Lexer::from_memory("${#%};", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::from(SpecialParam::Number));
assert_matches!(param.modifier, Modifier::Trim(trim) => {
assert_eq!(trim.side, TrimSide::Suffix);
assert_eq!(trim.length, TrimLength::Shortest);
assert_eq!(trim.pattern.to_string(), "");
});
assert_eq!(*param.location.code.value.borrow(), "${#%};");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..5);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(';')));
}
#[test]
fn lexer_braced_param_multiple_modifier() {
let mut lexer = Lexer::from_memory("${#x+};", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let e = lexer.braced_param(0).now_or_never().unwrap().unwrap_err();
assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::MultipleModifier));
assert_eq!(*e.location.code.value.borrow(), "${#x+};");
assert_eq!(e.location.range, 4..5);
}
#[test]
fn lexer_braced_param_line_continuations() {
let mut lexer = Lexer::from_memory("${\\\n#\\\n\\\na_\\\n1\\\n\\\n}z", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::variable("a_1"));
assert_eq!(param.modifier, Modifier::Length);
assert_eq!(
*param.location.code.value.borrow(),
"${\\\n#\\\n\\\na_\\\n1\\\n\\\n}z"
);
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..19);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('z')));
}
#[test]
fn lexer_braced_param_line_continuations_hash() {
let mut lexer = Lexer::from_memory("${#\\\n\\\n}z", Source::Unknown);
let mut lexer = WordLexer {
lexer: &mut lexer,
context: WordContext::Word,
};
lexer.peek_char().now_or_never().unwrap().unwrap();
lexer.consume_char();
let result = lexer.braced_param(0).now_or_never().unwrap();
let param = result.unwrap().unwrap();
assert_eq!(param.param, Param::from(SpecialParam::Number));
assert_eq!(param.modifier, Modifier::None);
assert_eq!(*param.location.code.value.borrow(), "${#\\\n\\\n}z");
assert_eq!(param.location.code.start_line_number.get(), 1);
assert_eq!(*param.location.code.source, Source::Unknown);
assert_eq!(param.location.range, 0..8);
assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('z')));
}
}