use nom::{
bytes::complete::{take_while, take_while1, is_not, tag, take_until},
character::complete::char,
combinator::{map, opt},
error::{Error, ErrorKind, ParseError},
sequence::{pair, preceded, tuple},
Err, IResult, Slice, branch::alt
};
use nom_locate::LocatedSpan;
pub type Span<'a> = LocatedSpan<&'a str>;
const WHITESPACE: &str = " \t\r\n";
pub fn ws(input: Span) -> IResult<Span, Span> {
take_while(move |c| WHITESPACE.contains(c))(input)
}
pub fn ws1(input: Span) -> IResult<Span, Span> {
take_while1(move |c| WHITESPACE.contains(c))(input)
}
pub fn _trailing_comma(input: Span) -> IResult<Span, Option<char>> {
opt(preceded(ws, char(',')))(input)
}
pub fn peol_comment(input: Span) -> IResult<Span, String> {
map(
preceded(
alt((tag("//"), tag("#"))),
is_not("\n\r")
),
|f: LocatedSpan<&str>| {
f.to_string()
}
)(input)
}
pub fn multiline_comment(input: Span) -> IResult<Span, String> {
map(
tuple((
tag("/*"),
take_until("*/"),
tag("*/")
)),
|(_, comment, _): (LocatedSpan<&str>, LocatedSpan<&str>, LocatedSpan<&str>)| {
comment.to_string()
}
)(input)
}
pub fn parse_comment(input: Span) -> IResult<Span, String> {
alt((peol_comment, multiline_comment))(input)
}
const IDENTIFIER_EXTRA: &str = "._";
pub fn parse_identifier(input: Span) -> IResult<Span, String> {
map(
pair(
take_while1(move |c: char| c.is_ascii_alphabetic()),
take_while(move |c: char| c.is_ascii_alphanumeric() || IDENTIFIER_EXTRA.contains(c)),
),
|t| format!("{}{}", t.0, t.1),
)(input)
}
pub fn take_parenth_content<'a>(
opening_bracket: char,
closing_bracket: char,
) -> impl Fn(Span<'a>) -> IResult<Span, Span> {
move |i: Span<'a>| {
let mut index = 0;
let mut bracket_counter = 0;
while let Some(n) = &i[index..].find(&[opening_bracket, closing_bracket, '\\'][..]) {
index += n;
let mut it = i[index..].chars();
match it.next().unwrap_or_default() {
c if c == '\\' => {
index += '\\'.len_utf8();
let c = it.next().unwrap_or_default();
index += c.len_utf8();
}
c if c == opening_bracket => {
bracket_counter += 1;
index += opening_bracket.len_utf8();
}
c if c == closing_bracket => {
bracket_counter -= 1;
index += closing_bracket.len_utf8();
}
_ => unreachable!(),
};
if bracket_counter == 0 {
return Ok((i.slice(index..), i.slice(1..index-1)))
};
}
if bracket_counter == 0 {
return Ok((i.slice(index..), i.slice(1..index-1)))
} else {
Err(Err::Error(Error::from_error_kind(i, ErrorKind::TakeUntil)))
}
}
}
#[cfg(test)]
pub(crate) fn assert_parse<'a, T: std::fmt::Debug + PartialEq>(
output: IResult<LocatedSpan<&'a str>, T>,
expected_value: T,
) {
assert!(output.is_ok(), "{:?}", output);
let output = output.unwrap();
assert_eq!(output.0.fragment(), &"");
assert_eq!(output.1, expected_value);
}