use crate::common::*;
use winnow::error::StrContext;
use winnow::error::{ContextError, ParserError};
use winnow::stream::Stream;
pub use winnow::ascii::{alpha0, alpha1, digit0, digit1, line_ending, space0, space1};
pub use winnow::combinator::cut_err;
pub use winnow::combinator::rest;
pub use winnow::combinator::seq;
pub use winnow::combinator::{delimited, preceded, repeat, repeat_till, separated, terminated};
pub use winnow::prelude::*;
pub use winnow::Parser;
pub use line_ending as eol;
pub use rest_line as read_until_eol;
pub fn label(s: &'static str) -> StrContext {
StrContext::Label(s)
}
pub fn parse_error(e: winnow::error::ParseError<&str, winnow::error::ContextError>, input: &str) -> Error {
anyhow!("found parse error:\n{:}\ninput={input:?}", e.to_string())
}
pub fn not_space<'a>(input: &mut &'a str) -> PResult<&'a str> {
winnow::token::take_till(1.., |c| " \t\r\n".contains(c))
.context(label("not_space"))
.parse_next(input)
}
pub fn read_line<'a>(s: &mut &'a str) -> PResult<&'a str> {
use winnow::ascii::till_line_ending;
use winnow::combinator::opt;
let o = (till_line_ending, opt(line_ending))
.recognize()
.context(label("read_line"))
.parse_next(s)?;
Ok(o)
}
pub fn rest_line<'a>(input: &mut &'a str) -> PResult<&'a str> {
use winnow::ascii::till_line_ending;
terminated(till_line_ending, line_ending).context(label("rest_line")).parse_next(input)
}
pub fn jump_to<'a>(literal: &str) -> impl FnMut(&mut &str) -> PResult<()> + '_ {
use winnow::token::take_until;
move |input: &mut &str| {
let _: (&str, &str) = (take_until(0.., literal), literal).context(label("jump_to")).parse_next(input)?;
Ok(())
}
}
pub fn jump_until<'a>(literal: &str) -> impl FnMut(&mut &str) -> PResult<()> + '_ {
use winnow::token::take_until;
move |input: &mut &str| {
let _: &str = take_until(0.., literal).context(label("jump_until")).parse_next(input)?;
Ok(())
}
}
pub fn ws<'a, ParseInner, Output, Error>(inner: ParseInner) -> impl Parser<&'a str, Output, Error>
where
ParseInner: Parser<&'a str, Output, Error>,
Error: ParserError<&'a str>,
{
delimited(space0, inner, space0)
}
pub fn skip_line_till<'a, O>(inner: impl Parser<&'a str, O, ContextError>) -> impl Parser<&'a str, (), ContextError> {
repeat_till(0.., rest_line, inner).map(|_: (Vec<_>, _)| ())
}
pub fn recognize_integer<'i>(input: &mut &'i str) -> PResult<&'i str> {
use winnow::combinator::opt;
use winnow::token::one_of;
let r = (opt(one_of(['+', '-'])), cut_err(digit1)).recognize().parse_next(input)?;
Ok(r)
}
pub fn unsigned_integer<'a>(input: &mut &'a str) -> PResult<usize> {
digit1.try_map(|x: &str| x.parse()).context(label("usize")).parse_next(input)
}
pub fn signed_integer(s: &mut &str) -> PResult<isize> {
recognize_integer.try_map(|x: &str| x.parse::<isize>()).parse_next(s)
}
pub fn read_usize(s: &mut &str) -> PResult<usize> {
let p = delimited(space0, unsigned_integer, space0);
terminated(p, line_ending).parse_next(s)
}
pub fn read_usize_many(s: &mut &str) -> PResult<Vec<usize>> {
let x = seq! {
_: space0,
separated(1.., unsigned_integer, space1),
_: space0,
_: line_ending,
}
.parse_next(s)?;
Ok(x.0)
}
pub use self::signed_integer as signed_digit;
pub use self::unsigned_integer as unsigned_digit;
pub fn double(input: &mut &str) -> PResult<f64> {
use winnow::ascii::float;
float(input)
}
pub fn sci_double<'i>(input: &mut &'i str) -> PResult<f64> {
use winnow::combinator::alt;
use winnow::combinator::opt;
use winnow::stream::Located;
use winnow::token::one_of;
let pre_exponent = (
opt(one_of(['+', '-'])),
alt(((digit1, opt(('.', opt(digit1)))).map(|_| ()), ('.', digit1).map(|_| ()))),
)
.recognize()
.parse_next(input)?;
let f = if let Some(exponent) = opt(preceded(one_of(['e', 'E', 'D', 'd']), recognize_integer)).parse_next(input)? {
format!("{pre_exponent}E{exponent}")
} else {
format!("{pre_exponent}")
}
.parse()
.unwrap();
Ok(f)
}
pub fn xyz_array(s: &mut &str) -> PResult<[f64; 3]> {
let x = seq! {double, _: space1, double, _: space1, double}.parse_next(s)?;
Ok([x.0, x.1, x.2])
}
pub fn read_double(s: &mut &str) -> PResult<f64> {
let p = delimited(space0, double, space0);
terminated(p, line_ending).parse_next(s)
}
pub fn read_double_many(s: &mut &str) -> PResult<Vec<f64>> {
let x = seq! {
_: space0,
separated(1.., double, space1),
_: space0,
_: line_ending,
}
.parse_next(s)?;
Ok(x.0)
}
pub fn parse_float(s: &str) -> Option<f64> {
if s.chars().all(|x| x == '*') {
std::f64::NAN.into()
} else {
s.parse().ok().or_else(|| s.replacen("D", "E", 1).parse().ok())
}
}
#[test]
fn test_fortran_float() {
let x = parse_float("14");
assert_eq!(x, Some(14.0));
let x = parse_float("14.12E4");
assert_eq!(x, Some(14.12E4));
let x = parse_float("14.12D4");
assert_eq!(x, Some(14.12E4));
let x = parse_float("****");
assert!(x.unwrap().is_nan());
}
#[test]
fn test_sci_double() -> PResult<()> {
let s = "-12.34d-1";
let (_, v) = sci_double.parse_peek(s)?;
assert_eq!(v, -1.234);
let s = "-12";
let (_, v) = sci_double.parse_peek(s)?;
assert_eq!(v, -12.0);
let s = "-12.3E-1";
let (_, v) = sci_double.parse_peek(s)?;
assert_eq!(v, -1.23);
Ok(())
}
#[test]
fn test_ws() -> PResult<()> {
let s = " 123 ";
let (_, x) = ws(digit1).parse_peek(s)?;
assert_eq!(x, "123");
let s = "123 ";
let (_, x) = ws(digit1).parse_peek(s)?;
assert_eq!(x, "123");
let s = "123\n";
let (_, x) = ws(digit1).parse_peek(s)?;
assert_eq!(x, "123");
Ok(())
}
#[test]
fn test_jump() {
let x = "xxbcc aa cc";
let (r, _) = jump_to("aa").parse_peek(x).unwrap();
assert_eq!(r, " cc");
let input = " Leave Link 103 at Fri Apr 19 13:58:11 2019, MaxMem= 33554432 cpu: 0.0
(Enter /home/ybyygu/gaussian/g09/l202.exe)
Input orientation:";
let input_orientation = (space1, "Input orientation:");
let (r, _) = skip_line_till(input_orientation).parse_peek(input).unwrap();
assert!(r.is_empty());
}
#[test]
fn test_read_line() {
let txt = "first line\nsecond line\r\nthird line\n";
let (rest, line) = read_line.parse_peek(txt).unwrap();
assert_eq!(line, "first line\n");
let (rest, line) = read_line.parse_peek(rest).unwrap();
assert_eq!(line, "second line\r\n");
let (rest, line) = read_line.parse_peek(rest).unwrap();
assert_eq!(line, "third line\n");
assert_eq!(rest, "");
let txt = "no newline at the end";
let (rest, line) = read_line.parse_peek(txt).unwrap();
assert_eq!(line, txt);
assert_eq!(rest, "");
let txt = "no";
let (_, line) = not_space.parse_peek(txt).unwrap();
assert_eq!(line, "no");
let txt = "no ";
let (_, line) = not_space.parse_peek(txt).unwrap();
assert_eq!(line, "no");
let txt = "no-a\n";
let (_, line) = not_space.parse_peek(txt).unwrap();
assert_eq!(line, "no-a");
let txt = "no+b\t";
let (_, line) = not_space.parse_peek(txt).unwrap();
assert_eq!(line, "no+b");
let txt = " no-a\n";
let x = not_space.parse_peek(txt);
assert!(x.is_err());
}
#[test]
fn test_read_many() {
let (_, ns) = read_usize_many.parse_peek("11 2 3 4 5\r\n\n").expect("usize parser");
assert_eq!(5, ns.len());
let _ = read_usize_many.parse_peek(" 11 2 3 4 5 \n").expect("usize parser");
let _ = read_usize_many.parse_peek("11 2 3 4 5 \r\n").expect("usize parser");
let line = " 1.2 3.4 -5.7 0.2 \n";
let (_, fs) = read_double_many.parse_peek(line).expect("f64 parser");
assert_eq!(4, fs.len());
}
#[test]
fn test_signed_digit() {
let (_, x) = signed_digit.parse_peek("-123").expect("signed digit, minus");
assert_eq!(x, -123);
let (_, x) = signed_digit.parse_peek("123").expect("signed digit, normal");
assert_eq!(x, 123);
let (_, x) = signed_digit.parse_peek("+123").expect("signed digit, plus");
assert_eq!(x, 123);
let s = "12x";
let (r, n) = unsigned_digit.parse_peek(s).unwrap();
assert_eq!(n, 12);
assert_eq!(r, "x");
let (r, n) = read_usize.parse_peek(" 12 \n").unwrap();
assert_eq!(n, 12);
assert_eq!(r, "");
}