tests for expression parsing

main
Ziyang Hu 2 years ago
parent 7b00b81a2d
commit 2f4368fd9b

@ -2,7 +2,7 @@ use crate::data::op::{AggOp, Op, UnresolvedOp};
use crate::data::tuple_set::{ColId, TableId, TupleSetIdx};
use crate::data::value::{StaticValue, Value};
use std::collections::BTreeMap;
use std::fmt::{Debug, Formatter, write};
use std::fmt::{Debug, format, Formatter, write};
use std::result;
use std::sync::Arc;
@ -34,6 +34,26 @@ pub(crate) enum Expr<'a> {
IdxAcc(usize, Box<Expr<'a>>),
}
impl<'a> PartialEq for Expr<'a> {
fn eq(&self, other: &Self) -> bool {
use Expr::*;
match (self, other) {
(Const(l), Const(r)) => l == r,
(List(l), List(r)) => l == r,
(Dict(l), Dict(r)) => l == r,
(Variable(l), Variable(r)) => l == r,
(TableCol(lt, lc), TableCol(rt, rc)) => (lt == rt) && (lc == rc),
(TupleSetIdx(l), TupleSetIdx(r)) => l == r,
(Apply(lo, la), Apply(ro, ra)) => (lo.name() == ro.name()) && (la == ra),
(ApplyAgg(lo, laa, la), ApplyAgg(ro, raa, ra)) => (lo.name() == ro.name()) && (laa == raa) && (la == ra),
(FieldAcc(lf, la), FieldAcc(rf, ra)) => (lf == rf) && (la == ra),
(IdxAcc(li, la), IdxAcc(ri, ra)) => (li == ri) && (la == ra),
_ => false
}
}
}
impl<'a> Debug for Expr<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
@ -43,8 +63,15 @@ impl<'a> Debug for Expr<'a> {
Expr::Variable(v) => write!(f, "`{}`", v),
Expr::TableCol(tid, cid) => write!(f, "{:?}{:?}", tid, cid),
Expr::TupleSetIdx(sid) => write!(f, "{:?}", sid),
Expr::Apply(op, args) => write!(f, "({} {:?})", op.name(), args),
Expr::ApplyAgg(op, a_args, args) => write!(f, "({} {:?} {:?})", op.name(), a_args, args),
Expr::Apply(op, args) => write!(
f, "({} {})",
op.name(),
args.iter().map(|v| format!("{:?}", v)).collect::<Vec<_>>().join(" ")),
Expr::ApplyAgg(op, a_args, args) => write!(
f, "[|{} {} | {}|]",
op.name(),
a_args.iter().map(|v| format!("{:?}", v)).collect::<Vec<_>>().join(" "),
args.iter().map(|v| format!("{:?}", v)).collect::<Vec<_>>().join(" ")),
Expr::FieldAcc(field, arg) => write!(f, "(.{} {:?})", field, arg),
Expr::IdxAcc(i, arg) => write!(f, "(.{} {:?})", i, arg)
}

@ -242,4 +242,97 @@ fn build_expr_infix<'a>(
_ => unreachable!(),
};
Ok(Expr::Apply(op, vec![lhs, rhs]))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::CozoParser;
use pest::Parser;
use crate::data::expr::StaticExpr;
fn parse_expr_from_str(s: &str) -> Result<Expr> {
let pair = CozoParser::parse(Rule::expr, s.as_ref())
.unwrap()
.next()
.unwrap();
Expr::try_from(pair)
}
#[test]
fn raw_string() {
assert!(dbg!(parse_expr_from_str(r#####"r#"x"#"#####)).is_ok());
}
#[test]
fn unevaluated() {
assert!(dbg!(parse_expr_from_str("a+b*c+d")).is_ok());
}
#[test]
fn parse_literals() {
assert_eq!(parse_expr_from_str("1").unwrap(), Expr::Const(Value::Int(1)));
assert_eq!(parse_expr_from_str("12_3").unwrap(), Expr::Const(Value::Int(123)));
assert_eq!(parse_expr_from_str("0xaf").unwrap(), Expr::Const(Value::Int(0xaf)));
assert_eq!(
parse_expr_from_str("0xafcE_f").unwrap(),
Expr::Const(Value::Int(0xafcef)
));
assert_eq!(
parse_expr_from_str("0o1234_567").unwrap(),
Expr::Const(Value::Int(0o1234567)
));
assert_eq!(
parse_expr_from_str("0o0001234_567").unwrap(),
Expr::Const(Value::Int(0o1234567)
));
assert_eq!(
parse_expr_from_str("0b101010").unwrap(),
Expr::Const(Value::Int(0b101010)
));
assert_eq!(
parse_expr_from_str("0.0").unwrap(),
Expr::Const(Value::Float((0.).into())
));
assert_eq!(
parse_expr_from_str("10.022_3").unwrap(),
Expr::Const(Value::Float(10.0223.into())
));
assert_eq!(
parse_expr_from_str("10.022_3e-100").unwrap(),
Expr::Const(Value::Float(10.0223e-100.into())
));
assert_eq!(parse_expr_from_str("null").unwrap(), Expr::Const(Value::Null));
assert_eq!(parse_expr_from_str("true").unwrap(), Expr::Const(Value::Bool(true)));
assert_eq!(parse_expr_from_str("false").unwrap(), Expr::Const(Value::Bool(false)));
assert_eq!(
parse_expr_from_str(r#""x \n \ty \"""#).unwrap(),
Expr::Const(Value::Text(Cow::Borrowed("x \n \ty \""))
));
assert_eq!(
parse_expr_from_str(r#""x'""#).unwrap(),
Expr::Const(Value::Text("x'".into())
));
assert_eq!(
parse_expr_from_str(r#"'"x"'"#).unwrap(),
Expr::Const(Value::Text(r##""x""##.into())
));
assert_eq!(
parse_expr_from_str(r#####"r###"x"yz"###"#####).unwrap(),
(Expr::Const(Value::Text(r##"x"yz"##.into()))
));
}
#[test]
fn complex_cases() -> Result<()> {
dbg!(parse_expr_from_str("{}")?);
dbg!(parse_expr_from_str("{b:1,a,c:2,d,...e,}")?);
dbg!(parse_expr_from_str("{...a,...b,c:1,d:2,...e,f:3}")?);
dbg!(parse_expr_from_str("[]")?);
dbg!(parse_expr_from_str("[...a,...b,1,2,...e,3]")?);
Ok(())
}
}

@ -3,7 +3,7 @@ use crate::parser::text_identifier::build_name_in_def;
use crate::parser::{CozoParser, Rule};
use pest::iterators::Pair;
use pest::Parser;
use std::fmt::{Display, Formatter};
use std::fmt::{Debug, Display, Formatter};
use std::result;
#[derive(thiserror::Error, Debug)]
@ -26,7 +26,7 @@ pub(crate) enum TypingError {
type Result<T> = result::Result<T, TypingError>;
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone)]
pub(crate) enum Typing {
Any,
Bool,
@ -70,6 +70,12 @@ impl Display for Typing {
}
}
impl Debug for Typing {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Typing<{}>", self)
}
}
impl Typing {
pub(crate) fn coerce<'a>(&self, v: Value<'a>) -> Result<Value<'a>> {
if *self == Typing::Any {
@ -148,7 +154,7 @@ impl TryFrom<&str> for Typing {
fn try_from(value: &str) -> Result<Self> {
let pair = CozoParser::parse(Rule::typing, value)?.next().unwrap();
Typing::from_pair(pair)
Typing::try_from(pair)
}
}
@ -160,8 +166,10 @@ impl<'a> TryFrom<Value<'a>> for Typing {
}
}
impl Typing {
pub fn from_pair<'a>(pair: Pair<Rule>) -> Result<Self> {
impl TryFrom<Pair<'_, Rule>> for Typing {
type Error = TypingError;
fn try_from(pair: Pair<Rule>) -> Result<Self> {
Ok(match pair.as_rule() {
Rule::simple_type => match pair.as_str() {
"Any" => Typing::Any,
@ -172,16 +180,16 @@ impl Typing {
"Uuid" => Typing::Uuid,
t => return Err(TypingError::UndefinedType(t.to_string())),
},
Rule::nullable_type => Typing::Nullable(Box::new(Typing::from_pair(
Rule::nullable_type => Typing::Nullable(Box::new(Typing::try_from(
pair.into_inner().next().unwrap(),
)?)),
Rule::homogeneous_list_type => Typing::Homogeneous(Box::new(Typing::from_pair(
Rule::homogeneous_list_type => Typing::Homogeneous(Box::new(Typing::try_from(
pair.into_inner().next().unwrap(),
)?)),
Rule::unnamed_tuple_type => {
let types = pair
.into_inner()
.map(|p| Typing::from_pair(p))
.map(Typing::try_from)
.collect::<Result<Vec<Typing>>>()?;
Typing::UnnamedTuple(types)
}
@ -193,7 +201,7 @@ impl Typing {
let name_pair = ps.next().unwrap();
let name = build_name_in_def(name_pair, true)?;
let typ_pair = ps.next().unwrap();
let typ = Typing::from_pair(typ_pair)?;
let typ = Typing::try_from(typ_pair)?;
Ok((name, typ))
})
.collect::<Result<Vec<(String, Typing)>>>()?;
@ -203,3 +211,29 @@ impl Typing {
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn to_string() {
assert_eq!(
format!(
"{}",
Typing::Nullable(Box::new(Typing::Homogeneous(Box::new(Typing::Text))))
),
"?[Text]"
);
}
#[test]
fn from_string() {
assert!(dbg!(Typing::try_from("?[Text]")).is_ok());
assert!(dbg!(Typing::try_from("?(Text, [Int], ?Uuid)")).is_ok());
assert!(dbg!(Typing::try_from("{xzzx : Text}")).is_ok());
assert!(dbg!(Typing::try_from("?({x : Text, ppqp: ?Int}, [Int], ?Uuid)")).is_ok());
assert!(dbg!(Typing::try_from("??Int")).is_err());
}
}

@ -257,4 +257,15 @@ mod tests {
dbg!(size_of::<HashMap<(), ()>>());
dbg!(size_of::<Value>());
}
#[test]
fn conversions() {
assert!(matches!(Value::from(()), Value::Null));
assert!(matches!(Value::from(10i64), Value::Int(_)));
assert!(matches!(Value::from(10.1f64), Value::Float(_)));
assert!(matches!(Value::from("abc"), Value::Text(_)));
assert!(matches!(Value::from("abc".to_string()), Value::Text(_)));
assert!(matches!(Value::from(vec![Value::Null]), Value::List(_)));
assert!(matches!(Value::from(BTreeMap::new()), Value::Dict(_)));
}
}

@ -2,43 +2,43 @@ use crate::error::{CozoError, Result};
use crate::relation::tuple::Tuple;
use crate::relation::typing::Typing;
use std::borrow::Borrow;
#[repr(u32)]
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Clone)]
pub enum DataKind {
Data = 0,
Node = 1,
Edge = 2,
Assoc = 3,
Index = 4,
Val = 5,
Type = 6,
Empty = u32::MAX,
}
// In storage, key layout is `[0, name, stack_depth]` where stack_depth is a non-positive number as zigzag
// Also has inverted index `[0, stack_depth, name]` for easy popping of stacks
pub const EMPTY_DATA: [u8; 4] = u32::MAX.to_be_bytes();
impl<T: AsRef<[u8]>> Tuple<T> {
pub fn data_kind(&self) -> Result<DataKind> {
use DataKind::*;
Ok(match self.get_prefix() {
0 => Data,
1 => Node,
2 => Edge,
3 => Assoc,
4 => Index,
5 => Val,
6 => Type,
u32::MAX => Empty,
v => return Err(CozoError::UndefinedDataKind(v)),
})
}
pub fn interpret_as_type(&self) -> Result<Typing> {
let text = self
.get_text(0)
.ok_or_else(|| CozoError::BadDataFormat(self.as_ref().to_vec()))?;
Typing::try_from(text.borrow())
}
}
//
// #[repr(u32)]
// #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Clone)]
// pub enum DataKind {
// Data = 0,
// Node = 1,
// Edge = 2,
// Assoc = 3,
// Index = 4,
// Val = 5,
// Type = 6,
// Empty = u32::MAX,
// }
// // In storage, key layout is `[0, name, stack_depth]` where stack_depth is a non-positive number as zigzag
// // Also has inverted index `[0, stack_depth, name]` for easy popping of stacks
//
// pub const EMPTY_DATA: [u8; 4] = u32::MAX.to_be_bytes();
//
// impl<T: AsRef<[u8]>> Tuple<T> {
// pub fn data_kind(&self) -> Result<DataKind> {
// use DataKind::*;
// Ok(match self.get_prefix() {
// 0 => Data,
// 1 => Node,
// 2 => Edge,
// 3 => Assoc,
// 4 => Index,
// 5 => Val,
// 6 => Type,
// u32::MAX => Empty,
// v => return Err(CozoError::UndefinedDataKind(v)),
// })
// }
// pub fn interpret_as_type(&self) -> Result<Typing> {
// let text = self
// .get_text(0)
// .ok_or_else(|| CozoError::BadDataFormat(self.as_ref().to_vec()))?;
// Typing::try_from(text.borrow())
// }
// }

@ -230,45 +230,45 @@ impl TryFrom<&str> for Typing {
Typing::from_pair(pair, None)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Result;
#[test]
fn to_string() {
assert_eq!(
format!(
"{}",
Typing::Nullable(Box::new(Typing::Homogeneous(Box::new(Typing::Text))))
),
"?[Text]"
);
}
#[test]
fn from_string() {
let res: Result<Typing> = "?[Text]".try_into();
println!("{:#?}", res);
assert!(res.is_ok());
let res: Result<Typing> = "?(Text, [Int], ?Uuid)".try_into();
println!("{:#?}", res);
assert!(res.is_ok());
let res: Result<Typing> = "{xzzx : Text}".try_into();
println!("{:#?}", res);
assert!(res.is_ok());
let res: Result<Typing> = "?({x : Text, ppqp: ?Int}, [Int], ?Uuid)".try_into();
println!("{:#?}", res);
assert!(res.is_ok());
let res: Result<Typing> = "??Int".try_into();
println!("{:#?}", res);
assert!(res.is_err());
let res: Result<Typing> = "<Int, Int, ?Int>->Any".try_into();
println!("{:#?}", res);
assert!(res.is_ok());
let res: Result<Typing> = "<>->Any".try_into();
println!("{:#?}", res);
assert!(res.is_ok());
}
}
//
// #[cfg(test)]
// mod tests {
// use super::*;
// use crate::error::Result;
//
// #[test]
// fn to_string() {
// assert_eq!(
// format!(
// "{}",
// Typing::Nullable(Box::new(Typing::Homogeneous(Box::new(Typing::Text))))
// ),
// "?[Text]"
// );
// }
//
// #[test]
// fn from_string() {
// let res: Result<Typing> = "?[Text]".try_into();
// println!("{:#?}", res);
// assert!(res.is_ok());
// let res: Result<Typing> = "?(Text, [Int], ?Uuid)".try_into();
// println!("{:#?}", res);
// assert!(res.is_ok());
// let res: Result<Typing> = "{xzzx : Text}".try_into();
// println!("{:#?}", res);
// assert!(res.is_ok());
// let res: Result<Typing> = "?({x : Text, ppqp: ?Int}, [Int], ?Uuid)".try_into();
// println!("{:#?}", res);
// assert!(res.is_ok());
// let res: Result<Typing> = "??Int".try_into();
// println!("{:#?}", res);
// assert!(res.is_err());
// let res: Result<Typing> = "<Int, Int, ?Int>->Any".try_into();
// println!("{:#?}", res);
// assert!(res.is_ok());
// let res: Result<Typing> = "<>->Any".try_into();
// println!("{:#?}", res);
// assert!(res.is_ok());
// }
// }

Loading…
Cancel
Save