#[derive(Debug, PartialEq)]
pub enum Value {
Dict(Vec<Object>),
Str(String),
ZStr(String),
Int(i32),
Float(f64),
Bool(bool),
}
impl Value {
pub fn to_number<T>(&self) -> Option<T>
where
T: std::convert::From<i32> + std::convert::From<f64>,
{
match *self {
Self::Int(i) => Some(i.into()),
Self::Float(f) => Some(f.into()),
_ => None,
}
}
}
#[derive(Debug, PartialEq)]
pub struct Pair {
pub key: String,
pub value: Value,
}
#[derive(Debug, PartialEq)]
pub enum Object {
Dict(Vec<Object>),
Pair(Pair),
Str(String),
ZStr(String),
Int(i32),
}
type Result<T> = std::result::Result<T, peg::error::ParseError<peg::str::LineCol>>;
impl Object {
pub fn from_string(s: &str) -> Result<Object> {
lron::root(s)
}
}
peg::parser! {grammar lron() for str {
use std::str::FromStr;
pub rule root() -> Object
= key:identifier() _() "=" _() value:array() _()
{ Object::Pair(Pair{key, value: Value::Dict(value)}) }
rule array() -> Vec<Object>
= "{" _() v:(object() ** (_() "," _())) _()(",")? _() "}" { v }
rule object() -> Object
= a:array() { Object::Dict(a) } /
p:pair() { Object::Pair(p) } /
s:string_literal() { Object::Str(s) } /
z:zstr() { Object::ZStr(z) } /
n:int() { Object::Int(n) }
rule pair() -> Pair
= key:identifier() _() "=" _() value:value() { Pair { key, value } } /
"[" key:string_literal() "]" _() "=" _() value:value()
{ Pair { key, value } }
rule value() -> Value
= i:int() { Value::Int(i) } /
b:bool() { Value::Bool(b) } /
f:float() { Value::Float(f) } /
s:string_literal() { Value::Str(s) } /
a:array() { Value::Dict(a) } /
z:zstr() { Value::ZStr(z) }
rule int() -> i32
= n:$("-"? ['0'..='9']+) !"." { i32::from_str(n).unwrap() } / expected!("integer")
rule bool() -> bool
= "true" { true } / "false" { false }
rule float() -> f64
= f:$("-"? ['0'..='9']+ "." ['0'..='9']+) { f64::from_str(f).unwrap() } / expected!("floating point")
rule identifier() -> String
= s:$(['a'..='z' | 'A'..='Z' | '0'..='9' | '_']+) { s.to_owned() } / expected!("identifier")
rule string_literal() -> String
= "\"" s:$((!['"'][_])*) "\"" { s.to_owned() }
rule zstr() -> String
= "ZSTR" _() s:string_literal() { s }
rule _() = quiet!{[' ' | '\r' | '\n' | '\t']*}
}}
#[test]
fn test_parser() {
const DATA: &'static str = "s = { \
{ \
criteria = \"rating\", \
operation = \">\", \
value = 0, \
value2 = 0, \
}, \
combine = \"intersect\", \
}";
let r = Object::from_string(DATA);
assert!(r.is_ok());
let o = r.unwrap();
println!("{:?}", o);
assert!(matches!(o, Object::Pair(_)));
if let Object::Pair(ref p) = o {
assert_eq!(p.key, "s");
assert!(matches!(p.value, Value::Dict(_)));
if let Value::Dict(ref d) = p.value {
assert_eq!(d.len(), 2);
assert!(matches!(d[0], Object::Dict(_)));
if let Object::Dict(ref d) = d[0] {
assert_eq!(d.len(), 4);
assert!(matches!(d[0], Object::Pair(_)));
assert!(matches!(d[1], Object::Pair(_)));
assert!(matches!(d[2], Object::Pair(_)));
assert!(matches!(d[3], Object::Pair(_)));
}
assert!(matches!(d[1], Object::Pair(_)));
if let Object::Pair(ref p) = d[1] {
assert_eq!(p.key, "combine");
assert_eq!(p.value, Value::Str("intersect".to_owned()));
}
}
} else {
assert!(false);
}
}