#![allow(clippy::arithmetic_side_effects)]
use std::collections::HashMap;
use nom::{
branch::alt,
bytes::streaming::{escaped, is_not, tag, take, take_until, take_while},
character::streaming::{char, one_of},
combinator::{map, map_opt, opt},
error::{ContextError, Error, ErrorKind, FromExternalError, ParseError},
multi::{many0, separated_list0},
sequence::{delimited, pair, preceded, terminated, tuple},
IResult, InputTakeAtPosition,
};
use crate::{ast::GroupWrapper, ArcStr};
#[inline]
fn comment_single(i: &str) -> IResult<&str, usize, Error<&str>> {
map(
tuple((
alt((tag("*"), tag("//"))),
take_while(move |c: char| c != '\n'),
take(1_usize),
space,
)),
|_| 1,
)(i)
}
#[inline]
fn comment_multi(i: &str) -> IResult<&str, usize, Error<&str>> {
map(tuple((tag("/*"), take_until("*/"), take(2_usize), space)), |(_, s, _, _)| {
s.chars().filter(|&x| x == '\n').count()
})(i)
}
#[cfg(test)]
mod test_comment {
use super::*;
#[test]
fn test1() {
println!("{:?}", comment_single("iwww\" \n \nw"));
println!("{:?}", comment_single("*iwww\" \n \nw"));
println!("{:?}", comment_single("//iwww\" \n \nw"));
println!("{:?}", comment_multi("//iwww\" \n \nw"));
println!(
"{:?}",
comment_multi(
r#"/*iwww\
*/
w"#
)
);
println!(
"{:?}",
comment_multi(
r#"/*iwww\ */
w"#
)
);
}
}
#[inline]
fn space(i: &str) -> IResult<&str, (), Error<&str>> {
map(take_while(move |c: char| matches!(c, '\t' | '\r' | ' ')), |_| ())(i)
}
#[inline]
fn space_newline(i: &str) -> IResult<&str, usize, Error<&str>> {
map(take_while(move |c: char| matches!(c, '\t' | '\n' | '\r' | ' ')), |s: &str| {
s.chars().filter(|&x| x == '\n').count()
})(i)
}
#[inline]
pub(crate) fn comment_space_newline_many1(i: &str) -> IResult<&str, usize, Error<&str>> {
match map(
pair(many0(pair(space_newline, alt((comment_single, comment_multi)))), space_newline),
|(v, n3)| v.iter().map(|(n1, n2)| n1 + n2).sum::<usize>() + n3,
)(i)
{
Ok((s, n)) => {
if n == 0 {
Err(nom::Err::Error(Error::new(i, ErrorKind::Many1)))
} else {
Ok((s, n))
}
}
Err(e) => Err(e),
}
}
#[inline]
pub(crate) fn comment_space_newline(i: &str) -> IResult<&str, usize, Error<&str>> {
map(
pair(many0(pair(space_newline, alt((comment_single, comment_multi)))), space_newline),
|(v, n3)| v.iter().map(|(n1, n2)| n1 + n2).sum::<usize>() + n3,
)(i)
}
#[inline]
fn comment_space_newline_slash(i: &str) -> IResult<&str, usize, Error<&str>> {
map(
pair(
space,
opt(preceded(
preceded(char('\\'), space),
alt((
map_opt(
pair(opt(comment_multi), space_newline),
|(n_comment, n_newline)| match (n_comment, n_newline) {
(_, 0) => None,
(None | Some(0), _) => Some(n_newline),
(_, _) => None,
},
),
comment_single,
)),
)),
),
|(_, n)| n.unwrap_or(0),
)(i)
}
#[cfg(test)]
mod test_space {
use super::*;
#[test]
fn space_test() {
println!("{:?}", comment_space_newline_slash(r#" w"#));
println!("{:?}", comment_space_newline_slash(r#" );"#));
println!(
"{:?}",
comment_space_newline_slash(
r#"/*iwww\
*/
w"#
)
);
println!(
"{:?}",
comment_space_newline_slash(
r#"\ /*iwww\*/
w"#
)
);
println!(
"{:?}",
comment_space_newline_slash(
r#"\
w"#
)
);
println!(
"{:?}",
comment_space_newline_slash(
r#"\ //www
w"#
)
);
}
}
#[inline]
pub(crate) fn undefine<'a>(
i: &'a str,
group_name: &str,
scope: &mut super::ParseScope,
) -> IResult<&'a str, super::UndefinedAttriValue, Error<&'a str>> {
let line_num_back: usize = scope.line_num;
if let Ok((input, res)) = simple(i, &mut scope.line_num) {
return Ok((input, super::UndefinedAttriValue::Simple(ArcStr::from(res))));
}
scope.line_num = line_num_back;
if let Ok((input, res)) = complex(i, &mut scope.line_num) {
return Ok((
input,
super::UndefinedAttriValue::Complex(super::ComplexWrapper(
res.into_iter().map(ArcStr::from).collect(),
)),
));
}
scope.line_num = line_num_back;
match title(i, &mut scope.line_num) {
Ok((mut input, title)) => {
let mut res = GroupWrapper {
title: title.into_iter().map(ArcStr::from).collect(),
attri_map: HashMap::new(),
};
loop {
match key(input) {
Err(nom::Err::Error(_)) => {
(input, _) = end_group(input)?;
let (new_input, n) = comment_space_newline(input)?;
input = new_input;
scope.line_num += n;
return Ok((input, super::UndefinedAttriValue::Group(res)));
}
Err(e) => return Err(e),
Ok((input1, key)) => {
let (new_input, undefined) = undefine(input1, group_name, scope)?;
input = new_input;
super::attributs_set_undefined_attri(
&mut res.attri_map,
key,
group_name,
scope,
undefined,
);
}
}
}
}
Err(e) => Err(e),
}
}
#[inline]
fn unquote<'a, E>(i: &'a str) -> IResult<&'a str, &'a str, E>
where
E: ParseError<&'a str> + ContextError<&'a str> + FromExternalError<&'a str, E>,
{
delimited(
char('"'),
escaped(opt(alt((tag(r#"\""#), is_not(r#"\""#)))), '\\', one_of(r#"\"rnt"#)),
char('"'),
)(i)
}
#[cfg(test)]
mod test_unquote {
use super::*;
#[test]
fn test1() {
use nom::error::VerboseError;
println!("{:?}", unquote::<VerboseError<&str>>("\"iwww\" "));
println!("{:?}", key::<VerboseError<&str>>("iw_ww "));
println!("{:?}", key::<VerboseError<&str>>("iw_w2w "));
println!("{:?}", key::<VerboseError<&str>>("iw_w2w';"));
println!("{:?}", formula::<VerboseError<&str>>("0.3 * VDD ;"));
}
}
#[inline]
pub(crate) fn key<'a, E>(i: &'a str) -> IResult<&'a str, &'a str, E>
where
E: ParseError<&'a str> + ContextError<&'a str> + FromExternalError<&'a str, E>,
{
i.split_at_position1(|item| !(item.is_alphanumeric() || item == '_'), ErrorKind::Alpha)
}
#[inline]
pub(super) fn char_in_word(c: char) -> bool {
c.is_alphanumeric() || "/_.+-:".contains(c)
}
#[inline]
pub(crate) fn word<'a, E>(i: &'a str) -> IResult<&'a str, &'a str, E>
where
E: ParseError<&'a str> + ContextError<&'a str> + FromExternalError<&'a str, E>,
{
i.split_at_position1(|item| !char_in_word(item), ErrorKind::Alpha)
}
#[inline]
pub(super) fn char_in_formula(c: char) -> bool {
c.is_ascii_alphanumeric() || " /_.+-*^:".contains(c)
}
#[inline]
pub(crate) fn formula<'a, E>(i: &'a str) -> IResult<&'a str, &'a str, E>
where
E: ParseError<&'a str> + ContextError<&'a str> + FromExternalError<&'a str, E>,
{
i.split_at_position1(|item| !char_in_formula(item), ErrorKind::Alpha)
}
#[inline]
pub(crate) fn simple_multi<'a>(
i: &'a str,
line_num: &mut usize,
) -> IResult<&'a str, &'a str, Error<&'a str>> {
map(
tuple((
space,
char(':'),
space,
char('"'),
take_while(move |c| c != '"'),
take(1_usize),
space,
char(';'),
comment_space_newline,
)),
|(_, _, _, _, s, _, _, _, n)| {
*line_num += n + s.chars().filter(|&x| x == '\n').count();
s
},
)(i)
}
#[inline]
pub(crate) fn simple_custom<'a, T>(
i: &'a str,
line_num: &mut usize,
func: fn(&'a str) -> IResult<&'a str, T, Error<&'a str>>,
) -> super::SimpleParseRes<'a, T> {
map(
tuple((
space,
char(':'),
space,
alt((
map(alt((func, delimited(char('"'), func, char('"')))), Ok),
map(alt((word, unquote)), |s| Err(ArcStr::from(s))),
)),
alt((
preceded(terminated(space, char(';')), comment_space_newline),
comment_space_newline_many1,
)),
)),
|(_, _, _, s, n)| {
*line_num += n;
s
},
)(i)
}
#[inline]
pub(crate) fn simple<'a>(
i: &'a str,
line_num: &mut usize,
) -> IResult<&'a str, &'a str, Error<&'a str>> {
map(
tuple((
space,
char(':'),
space,
alt((unquote, word)),
alt((
preceded(terminated(space, char(';')), comment_space_newline),
comment_space_newline_many1,
)),
)),
|(_, _, _, s, n)| {
*line_num += n;
s
},
)(i)
}
#[inline]
fn complex_complex(i: &str) -> IResult<&str, Vec<&str>, Error<&str>> {
let (input, words) = unquote(i)?;
Ok((
input,
words
.split(',')
.filter_map(|s| {
let _s = s.trim();
if _s.is_empty() {
None
} else {
Some(_s)
}
})
.collect(),
))
}
#[inline]
pub(crate) fn complex<'a>(
i: &'a str,
line_num: &mut usize,
) -> IResult<&'a str, Vec<&'a str>, Error<&'a str>> {
map(
tuple((
space,
char('('),
comment_space_newline_slash,
many0(pair(
alt((map(word, |s| vec![s]), complex_complex)),
tuple((space, char(','), comment_space_newline_slash)),
)),
opt(pair(
alt((complex_complex, map(word, |s| vec![s]))),
comment_space_newline_slash,
)),
char(')'),
space,
alt((preceded(char(';'), comment_space_newline), comment_space_newline_many1)),
)),
|(_, _, n0, res, last, _, _, n1)| {
*line_num += n0 + n1;
let mut vec: Vec<&'a str> = res
.into_iter()
.flat_map(|(v, (_, _, n))| {
*line_num += n;
v
})
.collect();
if let Some((last_vec, n)) = last {
*line_num += n;
vec.extend(last_vec);
}
vec
},
)(i)
}
#[cfg(test)]
mod test_key {
use super::*;
#[test]
fn test1() {
use nom::error::VerboseError;
println!("{:?}", comment_space_newline("\n\r\t\n : b ; "));
println!("{:?}", simple(" : b; }", &mut 1));
println!("{:?}", simple(" : iwww ; ", &mut 1));
println!("{:?}", simple(" : 0.3 * VDD ;", &mut 1));
println!("{:?}", key::<VerboseError<&str>>("iwww "));
println!("{:?}", key::<VerboseError<&str>>("iw_ww "));
println!("{:?}", key::<VerboseError<&str>>("iw_w2w "));
println!("{:?}", key::<VerboseError<&str>>("iw_w2w';"));
}
}
#[inline]
pub(crate) fn title<'a>(
i: &'a str,
line_num: &mut usize,
) -> IResult<&'a str, Vec<&'a str>, Error<&'a str>> {
map(
tuple((
space,
char('('),
separated_list0(char(','), delimited(space, alt((unquote, word)), space)),
char(')'),
space,
char('{'),
comment_space_newline,
)),
|(_, _, v, _, _, _, n)| {
*line_num += n;
v
},
)(i)
}
#[inline]
pub(crate) fn end_group(i: &str) -> IResult<&str, (), Error<&str>> {
map(char('}'), |_| ())(i)
}
#[cfg(test)]
mod test_end_group {
use super::*;
#[test]
fn test1() {
println!("{:?}", end_group("}"));
println!("{:?}", end_group("}\n"));
}
}