Allow strings as an alternative to numbers

This commit is contained in:
Josh Hansen 2023-08-07 12:04:18 -07:00
parent 9cb4e2a465
commit 405c65131e
2 changed files with 93 additions and 73 deletions

View file

@ -3,90 +3,115 @@ use std::ffi::OsStr;
use clap::{builder::TypedValueParser, Arg, Command};
use nom::{
branch::alt,
bytes::complete::{tag, take_while},
character::complete::char,
character::{is_digit, is_space},
combinator::{cut, map},
error::{context, ContextError, ParseError},
bytes::complete::{tag, take_while, take_while1},
character::is_space,
character::{complete::char, is_alphanumeric},
combinator::map,
multi::separated_list0,
number::complete::double,
sequence::{delimited, pair, preceded, terminated},
Compare, IResult, InputLength, InputTake, InputTakeAtPosition,
IResult,
};
use crate::xattr::{parse_xattr, Xattr};
#[derive(Clone, Debug, PartialEq)]
enum Relation {
Eq(f64),
Gt(f64),
Gte(f64),
Lt(f64),
Lte(f64),
In(Vec<f64>),
enum Value {
Number(f64),
String(String),
}
const WHITESPACE: &'static [u8] = b" \t\r\n";
impl From<f64> for Value {
fn from(x: f64) -> Self {
Self::Number(x)
}
}
fn sp<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
let chars = " \t\r\n";
impl From<String> for Value {
fn from(s: String) -> Self {
Self::String(s)
}
}
// nom combinators like `take_while` return a function. That function is the
// parser,to which we can pass the input
take_while(move |c| chars.contains(c))(i)
fn string(i: &[u8]) -> IResult<&[u8], String> {
alt((
delimited(tag("'"), take_while(is_alphanumeric), tag("'")),
delimited(tag("\""), take_while(is_alphanumeric), tag("\"")),
take_while1(is_alphanumeric),
))(i)
.map(|(i, b)| (i, String::from_utf8_lossy(b).to_string()))
}
fn parse_value(i: &[u8]) -> IResult<&[u8], Value> {
alt((map(double, Value::Number), map(string, Value::String)))(i)
}
#[derive(Clone, Debug, PartialEq)]
enum Relation {
Eq(Value),
Gt(Value),
Gte(Value),
Lt(Value),
Lte(Value),
In(Vec<Value>),
}
#[allow(dead_code)]
impl Relation {
fn eq<V: Into<Value>>(x: V) -> Self {
Self::Eq(x.into())
}
fn gt<V: Into<Value>>(x: V) -> Self {
Self::Gt(x.into())
}
fn gte<V: Into<Value>>(x: V) -> Self {
Self::Gte(x.into())
}
fn lt<V: Into<Value>>(x: V) -> Self {
Self::Lt(x.into())
}
fn lte<V: Into<Value>>(x: V) -> Self {
Self::Lte(x.into())
}
fn in_<V: Into<Value>>(x: Vec<V>) -> Self {
Self::In(x.into_iter().map(|x| x.into()).collect())
}
}
fn space(i: &[u8]) -> IResult<&[u8], &[u8]> {
take_while(move |x| is_space(x))(i)
}
// fn array_f64<'a, E: ParseError<&'a str> + ContextError<&'a str>>(
// i: &'a str,
// ) -> IResult<&'a str, Vec<f64>, E> {
// context(
// "array",
// preceded(
// char('['),
// cut(terminated(
// separated_list0(preceded(sp, char(',')), double),
// preceded(sp, char(']')),
// )),
// ),
// )(i)
// }
fn array_f64(i: &[u8]) -> IResult<&[u8], Vec<f64>> {
delimited(char('['), separated_list0(char(','), double), char(']'))(i)
fn array(i: &[u8]) -> IResult<&[u8], Vec<Value>> {
delimited(
char('['),
separated_list0(char(','), parse_value),
char(']'),
)(i)
}
#[test]
fn test_array_f64() {
fn test_array() {
assert_eq!(
array_f64(b"[1,2,3,4]".as_slice()),
Ok((b"".as_slice(), vec![1f64, 2f64, 3f64, 4f64]))
array(b"[1,2,3,4]".as_slice()),
Ok((
b"".as_slice(),
vec![1f64, 2f64, 3f64, 4f64]
.into_iter()
.map(Value::from)
.collect()
))
);
}
// fn parse_relation<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Relation, E> {
// alt((
// preceded(tag(">="), map(double, Relation::Gte)),
// preceded(tag("<="), map(double, Relation::Lte)),
// preceded(tag(" in"), map(array_f64, Relation::In)),
// preceded(tag(">"), map(double, Relation::Gt)),
// preceded(tag("<"), map(double, Relation::Lt)),
// preceded(tag("="), map(double, Relation::Eq)),
// ))(i)
// }
/// E.g. `> 5`
fn parse_relation(i: &[u8]) -> IResult<&[u8], Relation> {
alt((
preceded(pair(tag(">="), space), map(double, Relation::Gte)),
preceded(pair(tag("<="), space), map(double, Relation::Lte)),
preceded(pair(tag("in"), space), map(array_f64, Relation::In)),
preceded(pair(tag(">"), space), map(double, Relation::Gt)),
preceded(pair(tag("<"), space), map(double, Relation::Lt)),
preceded(pair(tag("="), space), map(double, Relation::Eq)),
preceded(pair(tag(">="), space), map(parse_value, Relation::gte)),
preceded(pair(tag("<="), space), map(parse_value, Relation::Lte)),
preceded(pair(tag("in"), space), map(array, Relation::in_)),
preceded(pair(tag(">"), space), map(parse_value, Relation::Gt)),
preceded(pair(tag("<"), space), map(parse_value, Relation::Lt)),
preceded(pair(tag("="), space), map(parse_value, Relation::Eq)),
))(i)
}
@ -95,20 +120,20 @@ fn test_parse_relation() {
assert!(parse_relation(b"a + b").is_err());
assert_eq!(
parse_relation(b">=-2.4343").unwrap().1,
Relation::Gte(-2.4343f64)
Relation::gte(-2.4343f64)
);
assert_eq!(parse_relation(b"<= -2").unwrap().1, Relation::Lte(-2f64));
assert_eq!(parse_relation(b"<= -2").unwrap().1, Relation::lte(-2f64));
assert_eq!(
parse_relation(b"in [1,2,3]"),
Ok((b"".as_slice(), Relation::In(vec![1f64, 2f64, 3f64])))
Ok((b"".as_slice(), Relation::in_(vec![1f64, 2f64, 3f64])))
);
assert_eq!(
parse_relation(b"> 4"),
Ok((b"".as_slice(), Relation::Gt(4f64)))
Ok((b"".as_slice(), Relation::gt(4f64)))
);
assert_eq!(
parse_relation(b"< 4"),
Ok((b"".as_slice(), Relation::Lt(4f64)))
Ok((b"".as_slice(), Relation::lt(4f64)))
);
}
@ -140,7 +165,7 @@ fn test_parse_predicate() {
namespace: b"user".to_vec(),
attr: b"amplitude".to_vec()
},
relation: Relation::Gte(-2.4343f64)
relation: Relation::gte(-2.4343f64)
}
);
assert_eq!(
@ -150,7 +175,7 @@ fn test_parse_predicate() {
namespace: b"system".to_vec(),
attr: b"security".to_vec()
},
relation: Relation::Lte(-2f64)
relation: Relation::lte(-2f64)
}
);
assert_eq!(
@ -162,7 +187,7 @@ fn test_parse_predicate() {
namespace: b"security".to_vec(),
attr: b"lmnop".to_vec()
},
relation: Relation::In(vec![1f64, 2f64, 3f64])
relation: Relation::in_(vec![1f64, 2f64, 3f64])
}
))
);
@ -175,7 +200,7 @@ fn test_parse_predicate() {
namespace: b"trusted".to_vec(),
attr: b"trustfulness".to_vec()
},
relation: Relation::Gt(4f64)
relation: Relation::gt(4f64)
}
))
);
@ -188,7 +213,7 @@ fn test_parse_predicate() {
namespace: b"user".to_vec(),
attr: b"grumpiness".to_vec()
},
relation: Relation::Lt(4f64)
relation: Relation::lt(4f64)
}
))
);

View file

@ -1,11 +1,6 @@
use nom::{
branch::alt,
bytes::complete::{tag, take_while, take_while1},
character::complete::char,
character::is_alphabetic,
error::ParseError,
sequence::{pair, separated_pair, terminated},
IResult,
branch::alt, bytes::complete::take_while1, character::complete::char, character::is_alphabetic,
sequence::separated_pair, IResult,
};
#[derive(Clone, Debug, PartialEq)]