use super::core::Lexer;
use crate::parser::core::Result;
use crate::syntax::TextUnit;
pub fn is_portable_name_char(c: char) -> bool {
matches!(c, '0'..='9' | 'A'..='Z' | '_' | 'a'..='z')
}
pub fn is_special_parameter_char(c: char) -> bool {
matches!(c, '@' | '*' | '#' | '?' | '-' | '$' | '!' | '0')
}
pub fn is_single_char_name(c: char) -> bool {
c.is_ascii_digit() || is_special_parameter_char(c)
}
impl Lexer<'_> {
pub async fn raw_param(&mut self, start_index: usize) -> Result<Option<TextUnit>> {
let name = if let Some(c) = self.consume_char_if(is_single_char_name).await? {
c.value.to_string()
} else if let Some(c) = self.consume_char_if(is_portable_name_char).await? {
let mut name = c.value.to_string();
while let Some(c) = self.consume_char_if(is_portable_name_char).await? {
name.push(c.value);
}
name
} else {
return Ok(None);
};
let location = self.location_range(start_index..self.index());
Ok(Some(TextUnit::RawParam { name, location }))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::source::Source;
use assert_matches::assert_matches;
use futures_executor::block_on;
#[test]
fn lexer_raw_param_special_parameter() {
let mut lexer = Lexer::from_memory("$@;", Source::Unknown);
block_on(lexer.peek_char()).unwrap();
lexer.consume_char();
let result = block_on(lexer.raw_param(0)).unwrap().unwrap();
assert_matches!(result, TextUnit::RawParam { name, location } => {
assert_eq!(name, "@");
assert_eq!(*location.code.value.borrow(), "$@;");
assert_eq!(location.code.start_line_number.get(), 1);
assert_eq!(location.code.source, Source::Unknown);
assert_eq!(location.range, 0..2);
});
assert_eq!(block_on(lexer.peek_char()), Ok(Some(';')));
}
#[test]
fn lexer_raw_param_digit() {
let mut lexer = Lexer::from_memory("$12", Source::Unknown);
block_on(lexer.peek_char()).unwrap();
lexer.consume_char();
let result = block_on(lexer.raw_param(0)).unwrap().unwrap();
assert_matches!(result, TextUnit::RawParam { name, location } => {
assert_eq!(name, "1");
assert_eq!(*location.code.value.borrow(), "$12");
assert_eq!(location.code.start_line_number.get(), 1);
assert_eq!(location.code.source, Source::Unknown);
assert_eq!(location.range, 0..2);
});
assert_eq!(block_on(lexer.peek_char()), Ok(Some('2')));
}
#[test]
fn lexer_raw_param_posix_name() {
let mut lexer = Lexer::from_memory("$az_AZ_019<", Source::Unknown);
block_on(lexer.peek_char()).unwrap();
lexer.consume_char();
let result = block_on(lexer.raw_param(0)).unwrap().unwrap();
assert_matches!(result, TextUnit::RawParam { name, location } => {
assert_eq!(name, "az_AZ_019");
assert_eq!(*location.code.value.borrow(), "$az_AZ_019<");
assert_eq!(location.code.start_line_number.get(), 1);
assert_eq!(location.code.source, Source::Unknown);
assert_eq!(location.range, 0..10);
});
assert_eq!(block_on(lexer.peek_char()), Ok(Some('<')));
}
#[test]
fn lexer_raw_param_posix_name_line_continuations() {
let mut lexer = Lexer::from_memory("$a\\\n\\\nb\\\n\\\nc\\\n>", Source::Unknown);
block_on(lexer.peek_char()).unwrap();
lexer.consume_char();
let result = block_on(lexer.raw_param(0)).unwrap().unwrap();
assert_matches!(result, TextUnit::RawParam { name, location } => {
assert_eq!(name, "abc");
assert_eq!(*location.code.value.borrow(), "$a\\\n\\\nb\\\n\\\nc\\\n>");
assert_eq!(location.code.start_line_number.get(), 1);
assert_eq!(location.code.source, Source::Unknown);
assert_eq!(location.range, 0..14);
});
assert_eq!(block_on(lexer.peek_char()), Ok(Some('>')));
}
#[test]
fn lexer_raw_param_not_parameter() {
let mut lexer = Lexer::from_memory("$;", Source::Unknown);
block_on(lexer.peek_char()).unwrap();
lexer.consume_char();
assert_eq!(block_on(lexer.raw_param(0)), Ok(None));
assert_eq!(block_on(lexer.peek_char()), Ok(Some(';')));
}
}