use crate::{
ast::*,
parser::{basic::*, combinator::*},
};
use nom::{
branch::alt,
character::complete::{char, digit0, digit1, multispace0, none_of, satisfy},
combinator::opt,
multi::{many0, many1},
sequence::tuple,
Parser,
};
pub fn sign(input: &str) -> ParseResult<char> {
alt((char('+'), char('-'))).parse(input)
}
pub fn integer(input: &str) -> ParseResult<i64> {
tuple((opt(sign), multispace0, digit1))
.map(|(sign, _space, numbers)| {
let num: i64 = numbers.parse().expect("Failed to parse into integer");
match sign {
Some('-') => -num,
_ => num,
}
})
.parse(input)
}
fn exponent(input: &str) -> ParseResult<i64> {
tuple((char('E'), multispace0, opt(sign), multispace0, digit1))
.map(|(_e, _sp1, sign, _sp2, digit)| {
let num: i64 = digit.parse().expect("Failed to parse integer in exponent");
match sign {
Some('-') => -num,
_ => num,
}
})
.parse(input)
}
pub fn real(input: &str) -> ParseResult<f64> {
tuple((
opt(sign),
multispace0,
digit1,
char('.'),
digit0,
opt(exponent),
))
.map(|(sign, _space, integral, _point, fractional, exp)| {
let num: f64 = format!("{}.{}e{}", integral, fractional, exp.unwrap_or(0))
.parse()
.expect("Failed to parse Float");
match sign {
Some('-') => -num,
_ => num,
}
})
.parse(input)
}
pub fn string(input: &str) -> ParseResult<String> {
tuple((char('\''), many0(none_of("'")), char('\'')))
.map(|(_start, s, _end)| s.iter().collect())
.parse(input)
}
pub fn resource(input: &str) -> ParseResult<URI> {
tuple((char('<'), many0(none_of(">")), char('>')))
.map(|(_start, s, _end)| URI(s.iter().collect()))
.parse(input)
}
pub fn enumeration(input: &str) -> ParseResult<String> {
tuple((char('.'), standard_keyword, char('.')))
.map(|(_head, name, _tail)| name)
.parse(input)
}
fn u64_overflow(input: &str) -> nom::Err<nom::error::VerboseError<&str>> {
nom::Err::Failure(nom::error::VerboseError {
errors: vec![(input, nom::error::VerboseErrorKind::Context("u64-overflow"))],
})
}
pub fn entity_instance_name(input: &str) -> ParseResult<u64> {
let (input, name) = tuple((char('#'), digit1))
.map(|(_sharp, name): (_, &str)| name.parse())
.parse(input)?;
if let Ok(name) = name {
Ok((input, name))
} else {
Err(u64_overflow(input))
}
}
pub fn value_instance_name(input: &str) -> ParseResult<u64> {
let (input, name) = tuple((char('@'), digit1))
.map(|(_sharp, name): (_, &str)| name.parse())
.parse(input)?;
if let Ok(name) = name {
Ok((input, name))
} else {
Err(u64_overflow(input))
}
}
pub fn constant_entity_name(input: &str) -> ParseResult<String> {
tuple((char('#'), standard_keyword))
.map(|(_sharp, name)| name)
.parse(input)
}
pub fn constant_value_name(input: &str) -> ParseResult<String> {
tuple((char('@'), standard_keyword))
.map(|(_sharp, name)| name)
.parse(input)
}
pub fn lhs_occurrence_name(input: &str) -> ParseResult<Name> {
alt((
entity_instance_name.map(Name::Entity),
value_instance_name.map(Name::Value),
))
.parse(input)
}
pub fn rhs_occurrence_name(input: &str) -> ParseResult<Name> {
alt((
entity_instance_name.map(Name::Entity),
value_instance_name.map(Name::Value),
constant_entity_name.map(Name::ConstantEntity),
constant_value_name.map(Name::ConstantValue),
))
.parse(input)
}
pub fn anchor_name(input: &str) -> ParseResult<String> {
tuple((char('<'), many0(none_of(">")), char('>')))
.map(|(_start, s, _end)| s.iter().collect())
.parse(input)
}
pub fn keyword(input: &str) -> ParseResult<String> {
alt((user_defined_keyword, standard_keyword)).parse(input)
}
pub fn standard_keyword(input: &str) -> ParseResult<String> {
tuple((upper, many0(alt((upper, digit)))))
.map(|(first, tail)| {
let head = &[first];
head.iter().chain(tail.iter()).collect()
})
.parse(input)
}
pub fn user_defined_keyword(input: &str) -> ParseResult<String> {
tuple((char('!'), standard_keyword))
.map(|(_e, name)| name)
.parse(input)
}
pub fn tag_name(input: &str) -> ParseResult<String> {
tuple((alt((upper, lower)), many0(alt((upper, lower, digit)))))
.map(|(first, tail)| {
let head = &[first];
head.iter().chain(tail.iter()).collect()
})
.parse(input)
}
pub fn signature_content(input: &str) -> ParseResult<String> {
let base_char = satisfy(|c| matches!(c, '0'..='9' | 'a'..='z' | 'A'..='Z' | '+' | '/' | '='));
many1(base_char)
.map(|chars| chars.iter().collect())
.parse(input)
}
#[cfg(test)]
mod tests {
use nom::Finish;
#[test]
fn real() {
let (res, s) = super::real("1.23").finish().unwrap();
assert_eq!(res, "");
assert_eq!(s, 1.23);
let (res, s) = super::real("1.23E4").finish().unwrap();
assert_eq!(res, "");
assert_eq!(s, 1.23e4);
let (res, s) = super::real("1.23E-4").finish().unwrap();
assert_eq!(res, "");
assert_eq!(s, 1.23e-4);
let (res, s) = super::real("-1.23E4").finish().unwrap();
assert_eq!(res, "");
assert_eq!(s, -1.23e4);
let (res, s) = super::real("-1.23E-4").finish().unwrap();
assert_eq!(res, "");
assert_eq!(s, -1.23e-4);
assert!(super::real("123").finish().is_err());
}
#[test]
fn string() {
let (res, s) = super::string("'vim'").finish().unwrap();
assert_eq!(res, "");
assert_eq!(s, "vim");
}
#[test]
fn instance_name() {
let (res, s) = super::entity_instance_name("#18446744073709551615" )
.finish()
.unwrap();
assert_eq!(res, "");
assert_eq!(s, std::u64::MAX);
let (res, s) = super::value_instance_name("@18446744073709551615" )
.finish()
.unwrap();
assert_eq!(res, "");
assert_eq!(s, std::u64::MAX);
assert!(
super::entity_instance_name("#18446744073709551616" )
.finish()
.is_err()
);
assert!(
super::value_instance_name("@18446744073709551616" )
.finish()
.is_err()
);
let (res, s) = super::entity_instance_name("#001").finish().unwrap();
assert_eq!(res, "");
assert_eq!(s, 1);
let (res, s) = super::value_instance_name("@001").finish().unwrap();
assert_eq!(res, "");
assert_eq!(s, 1);
}
}