use winnow::{
combinator::{alt, opt},
error::{ErrorKind, FromExternalError, ParserError},
token::{literal, one_of, take_while},
PResult, Parser,
};
use crate::comments::space_or_comment0;
pub fn identifier<'a, E: ParserError<&'a str>>(input: &mut &'a str) -> PResult<String, E> {
let first = one_of([
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
])
.parse_next(input)?;
let rest = take_while(0.., is_identifier_rest).parse_next(input)?;
let combine = format!("{}{}", first, rest);
if is_reserved_key_word(&combine) {
Err(winnow::error::ErrMode::Cut(ParserError::from_error_kind(
input,
ErrorKind::Token,
)))
} else {
Ok(combine)
}
}
pub fn var_par_identifier<'a, E: ParserError<&'a str>>(input: &mut &'a str) -> PResult<String, E> {
let first = one_of([
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_',
])
.parse_next(input)?;
let rest = take_while(0.., is_identifier_rest).parse_next(input)?;
let combine = format!("{}{}", first, rest);
if is_reserved_key_word(&combine) {
Err(winnow::error::ErrMode::Backtrack(
ParserError::from_error_kind(input, ErrorKind::Token),
))
} else {
Ok(combine)
}
}
fn is_reserved_key_word(string: &str) -> bool {
matches!(
string,
"annotation"
| "any"
| "array"
| "bool"
| "case"
| "constraint"
| "diff"
| "div"
| "else"
| "elseif"
| "endif"
| "enum"
| "false"
| "float"
| "function"
| "if"
| "in"
| "include"
| "int"
| "intersect"
| "let"
| "list"
| "maximize"
| "minimize"
| "mod"
| "not"
| "of"
| "satisfy"
| "subset"
| "superset"
| "output"
| "par"
| "predicate"
| "record"
| "set"
| "solve"
| "string"
| "symdiff"
| "test"
| "then"
| "true"
| "tuple"
| "union"
| "type"
| "var"
| "where"
| "xor"
)
}
fn is_identifier_rest(c: char) -> bool {
matches!(
c,
'a' | 'b'
| 'c'
| 'd'
| 'e'
| 'f'
| 'g'
| 'h'
| 'i'
| 'j'
| 'k'
| 'l'
| 'm'
| 'n'
| 'o'
| 'p'
| 'q'
| 'r'
| 's'
| 't'
| 'u'
| 'v'
| 'w'
| 'x'
| 'y'
| 'z'
| 'A'
| 'B'
| 'C'
| 'D'
| 'E'
| 'F'
| 'G'
| 'H'
| 'I'
| 'J'
| 'K'
| 'L'
| 'M'
| 'N'
| 'O'
| 'P'
| 'Q'
| 'R'
| 'S'
| 'T'
| 'U'
| 'V'
| 'W'
| 'X'
| 'Y'
| 'Z'
| '_'
| '0'
| '1'
| '2'
| '3'
| '4'
| '5'
| '6'
| '7'
| '8'
| '9'
)
}
pub fn bool_literal<'a, E: ParserError<&'a str>>(input: &mut &'a str) -> PResult<bool, E> {
alt((literal("true").value(true), literal("false").value(false))).parse_next(input)
}
pub fn int_literal<'a, E>(input: &mut &'a str) -> PResult<i128, E>
where
E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>,
{
alt((decimal, hexadecimal, octal)).parse_next(input)
}
#[test]
fn test_int_literal() {
use winnow::error::ContextError;
let mut input = "1";
assert_eq!(int_literal::<ContextError>(&mut input), Ok(1));
}
fn decimal<'a, E>(input: &mut &'a str) -> PResult<i128, E>
where
E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>,
{
let negation = opt('-').parse_next(input)?;
let int = take_while(1.., is_dec_digit)
.parse_next(input)?
.parse::<i128>()
.map_err(|e| winnow::error::ErrMode::from_external_error(input, ErrorKind::Verify, e))?;
if negation.is_some() {
Ok(-int)
} else {
Ok(int)
}
}
#[test]
fn test_decimal() {
use winnow::error::ContextError;
let mut input = "170141183460469231731687303715884105727";
assert_eq!(
decimal::<ContextError>(&mut input),
Ok(170141183460469231731687303715884105727)
);
}
#[test]
fn test_decimal2() {
use winnow::error::ContextError;
let mut input = "-170141183460469231731687303715884105727";
assert_eq!(
decimal::<ContextError>(&mut input),
Ok(-170141183460469231731687303715884105727)
);
}
#[test]
fn test_decimal3() {
use winnow::error::ContextError;
let mut input = "170141183460469231731687303715884105728";
assert!(decimal::<ContextError>(&mut input).is_err());
}
fn hexadecimal<'a, E>(input: &mut &'a str) -> PResult<i128, E>
where
E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>,
{
let negation = opt('-').parse_next(input)?;
"0x".parse_next(input)?;
let int = take_while(1.., is_hex_digit).parse_next(input)?;
let int = i128::from_str_radix(int, 16)
.map_err(|e| winnow::error::ErrMode::from_external_error(input, ErrorKind::Verify, e))?;
if negation.is_some() {
Ok(-int)
} else {
Ok(int)
}
}
#[test]
fn test_hex() {
use winnow::error::ContextError;
let mut input = "-0x2f";
assert_eq!(hexadecimal::<ContextError>(&mut input), Ok(-47));
}
fn octal<'a, E>(input: &mut &'a str) -> PResult<i128, E>
where
E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>,
{
let negation = opt('-').parse_next(input)?;
"0o".parse_next(input)?;
let int = take_while(1.., is_oct_digit).parse_next(input)?;
let int = i128::from_str_radix(int, 8)
.map_err(|e| winnow::error::ErrMode::from_external_error(input, ErrorKind::Verify, e))?;
if negation.is_some() {
Ok(-int)
} else {
Ok(int)
}
}
#[test]
fn test_oct() {
use winnow::error::ContextError;
let mut input = "0o200000000000000000000000000000000001";
assert_eq!(
octal::<ContextError>(&mut input),
Ok(81129638414606681695789005144065)
);
}
fn is_hex_digit(c: char) -> bool {
c.is_ascii_hexdigit()
}
fn is_oct_digit(c: char) -> bool {
c.is_digit(8)
}
fn is_dec_digit(c: char) -> bool {
c.is_ascii_digit()
}
pub fn float_literal<'a, E>(input: &mut &'a str) -> PResult<f64, E>
where
E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseFloatError>,
{
fz_float(input)
}
#[test]
fn test_float_literal() {
use winnow::error::ContextError;
let mut input = "023.21";
assert_eq!(float_literal::<ContextError>(&mut input), Ok(023.21));
let mut input = "0023.21E-098";
assert_eq!(float_literal::<ContextError>(&mut input), Ok(0023.21E-098));
let mut input = "0023.21e+098";
assert_eq!(float_literal::<ContextError>(&mut input), Ok(0023.21e+098));
let mut input = "002e+098";
assert_eq!(float_literal::<ContextError>(&mut input), Ok(002e+098));
let mut input = "0.21";
assert_eq!(float_literal::<ContextError>(&mut input), Ok(0.21));
let mut input = "1.0,";
assert_eq!(float_literal::<ContextError>(&mut input), Ok(1.0));
let mut input = "0.000000000000000000000000000000007609999999000000000000000000000000760999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999900000000000000000000000764DD4DDDDDDDDD%
";
assert_eq!(
float_literal::<ContextError>(&mut input),
Ok(0.000000000000000000000000000000007609999999)
);
}
fn fz_float<'a, E>(input: &mut &'a str) -> PResult<f64, E>
where
E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseFloatError>,
{
let mut fl = alt((fz_float1, fz_float2)).parse_next(input)?;
winnow::ascii::float.parse_next(&mut fl)
}
fn fz_float1<'a, E: ParserError<&'a str>>(input: &mut &'a str) -> PResult<&'a str, E> {
let sign = opt('-');
let pre = take_while(1.., is_dec_digit);
let post = take_while(1.., is_dec_digit);
let rest = opt(bpart);
(sign, pre, '.', post, rest).take().parse_next(input)
}
fn fz_float2<'a, E: ParserError<&'a str>>(input: &mut &'a str) -> PResult<&'a str, E> {
let sign = opt('-');
let digits = take_while(1.., is_dec_digit);
let e = alt(('e', 'E'));
let sign2 = opt(alt(("-", "+")));
let digits2 = take_while(1.., is_dec_digit);
(sign, digits, e, sign2, digits2).take().parse_next(input)
}
fn bpart<'a, E: ParserError<&'a str>>(input: &mut &'a str) -> PResult<String, E> {
let e = alt(('e', 'E')).parse_next(input)?;
let sign = opt(alt(("-", "+"))).parse_next(input)?;
let digits = take_while(1.., is_dec_digit).parse_next(input)?;
if let Some(sign) = sign {
Ok(format!("{}{}{}", e, sign, digits))
} else {
Ok(format!("{}{}", e, digits))
}
}
#[derive(PartialEq, Clone, Debug)]
pub struct IndexSet(pub i128);
pub fn index_set<'a, E>(input: &mut &'a str) -> PResult<IndexSet, E>
where
E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>,
{
'1'.parse_next(input)?;
space_or_comment0(input)?;
"..".parse_next(input)?;
space_or_comment0(input)?;
let int = int_literal.parse_next(input)?;
Ok(IndexSet(int))
}