use nom::{
IResult, Parser,
bytes::complete::{take_while, take_while1},
character::complete::char,
combinator::{map, opt, recognize},
number::complete::double,
sequence::{delimited, pair},
};
use crate::types::ErrorKind;
pub fn offset(full: &str, sub: &str) -> usize {
sub.as_ptr() as usize - full.as_ptr() as usize
}
pub fn dollar_cell_ref(i: &str) -> IResult<&str, &str> {
let (rest, matched) = recognize((
opt(char('$')),
take_while1(|c: char| c.is_ascii_alphabetic()),
opt(char('$')),
take_while1(|c: char| c.is_ascii_digit()),
))
.parse(i)?;
if !matched.contains('$') {
return Err(nom::Err::Error(nom::error::Error::new(i, nom::error::ErrorKind::Tag)));
}
Ok((rest, matched))
}
pub fn number_literal(i: &str) -> IResult<&str, f64> {
double(i)
}
pub fn string_literal(i: &str) -> IResult<&str, String> {
let mut parser = map(
delimited(char('"'), take_while(|c| c != '"'), char('"')),
|s: &str| s.to_string(),
);
parser.parse(i)
}
pub fn bool_literal(i: &str) -> IResult<&str, bool> {
let upper: String = i.chars().take(5).collect::<String>().to_uppercase();
if upper.starts_with("FALSE") {
let rest = &i[5..];
let next = rest.chars().next();
if next.map(|c| c.is_alphanumeric() || c == '_' || c == '(').unwrap_or(false) {
return Err(nom::Err::Error(nom::error::Error::new(i, nom::error::ErrorKind::Tag)));
}
return Ok((rest, false));
}
let upper4: String = i.chars().take(4).collect::<String>().to_uppercase();
if upper4 == "TRUE" {
let rest = &i[4..];
let next = rest.chars().next();
if next.map(|c| c.is_alphanumeric() || c == '_' || c == '(').unwrap_or(false) {
return Err(nom::Err::Error(nom::error::Error::new(i, nom::error::ErrorKind::Tag)));
}
return Ok((rest, true));
}
Err(nom::Err::Error(nom::error::Error::new(i, nom::error::ErrorKind::Tag)))
}
pub fn error_literal(i: &str) -> IResult<&str, ErrorKind> {
if !i.starts_with('#') {
return Err(nom::Err::Error(nom::error::Error::new(i, nom::error::ErrorKind::Tag)));
}
let bytes = i.as_bytes();
for kind in ErrorKind::LITERAL_KINDS {
let text = kind.to_string();
let len = text.len();
if bytes.len() >= len && bytes[..len].eq_ignore_ascii_case(text.as_bytes()) {
let rest = &i[len..];
let boundary_ok = !rest.chars().next().map(|c| c.is_alphanumeric() || c == '_').unwrap_or(false);
if boundary_ok {
return Ok((rest, kind));
}
}
}
Err(nom::Err::Error(nom::error::Error::new(i, nom::error::ErrorKind::Tag)))
}
pub fn identifier(i: &str) -> IResult<&str, &str> {
let mut parser = recognize(pair(
take_while1(|c: char| c.is_alphabetic() || c == '_'),
take_while(|c: char| c.is_alphanumeric() || c == '_' || c == '.'),
));
parser.parse(i)
}
#[cfg(test)]
mod tests;