scoped dict and spreading

main
Ziyang Hu 2 years ago
parent e4766e574f
commit 8408d62539

@ -5,7 +5,7 @@ use cozorocks::{SlicePtr};
use crate::db::engine::{Session};
use crate::relation::tuple::{OwnTuple, Tuple};
use crate::relation::typing::Typing;
use crate::relation::value::{METHOD_CONCAT, Value};
use crate::relation::value::{Value};
use crate::error::{CozoError, Result};
use crate::error::CozoError::LogicError;
use crate::relation::data::DataKind;
@ -13,7 +13,8 @@ use crate::parser::{Rule};
use crate::parser::text_identifier::build_name_in_def;
use crate::relation::value;
/// layouts for sector 0
/// # layouts for sector 0
///
/// `[Null]`: stores information about table_ids
/// `[Text, Int]`: contains definable data and depth info
/// `[Int, Text]`: inverted index for depth info
@ -358,6 +359,7 @@ impl<'s> Session<'s> {
value::METHOD_IS_NULL => self.is_null_values(args, params, table_bindings)?,
value::METHOD_NOT_NULL => self.not_null_values(args, params, table_bindings)?,
value::METHOD_CONCAT => self.concat_values(args, params, table_bindings)?,
value::METHOD_MERGE => self.merge_values(args, params, table_bindings)?,
_ => { todo!() }
})
}
@ -779,7 +781,47 @@ impl<'s> Session<'s> {
if !cur_ret.is_empty() {
total_ret.push(cur_ret.into());
}
Ok((false, Value::Apply(METHOD_CONCAT.into(), total_ret)))
Ok((false, Value::Apply(value::METHOD_CONCAT.into(), total_ret)))
}
}
fn merge_values<'a>(&self, args: Vec<Value<'a>>, params: &BTreeMap<String, Value<'a>>,
table_bindings: &BTreeMap<String, ()>) -> Result<(bool, Value<'a>)> {
let mut total_ret = vec![];
let mut cur_ret = BTreeMap::new();
let mut evaluated = true;
for val in args.into_iter() {
let (ev, val) = self.partial_eval(val, params, table_bindings)?;
evaluated = ev && evaluated;
match val {
Value::Dict(d) => {
if cur_ret.is_empty() {
cur_ret = d;
} else {
cur_ret.extend(d);
}
}
v @ (Value::Variable(_) |
Value::Apply(_, _) |
Value::FieldAccess(_, _) |
Value::IdxAccess(_, _)) => {
if !cur_ret.is_empty() {
total_ret.push(Value::Dict(cur_ret));
cur_ret = BTreeMap::new();
}
total_ret.push(v);
}
_ => {
return Err(LogicError("Cannot concat incompatible types".to_string()));
}
}
}
if total_ret.is_empty() {
Ok((evaluated, cur_ret.into()))
} else {
if !cur_ret.is_empty() {
total_ret.push(cur_ret.into());
}
Ok((false, Value::Apply(value::METHOD_MERGE.into(), total_ret)))
}
}
fn and_values<'a>(&self, args: Vec<Value<'a>>, params: &BTreeMap<String, Value<'a>>,

@ -122,8 +122,8 @@ list_el = _{spreading | expr}
spreading = {"..." ~ term}
dict = { "{" ~ (dict_entry ~ ",")* ~ dict_entry? ~ "}"}
dict_entry = _{ spreading | dict_accessor | dict_pair }
dict_accessor = { ident? ~ ("." ~ ident)+ }
dict_entry = _{ spreading | dict_pair | scoped_accessor }
scoped_accessor = { ident }
dict_pair = {(ident | string) ~ ":" ~ expr}
scoped_dict = { ident ~ dict }

@ -88,18 +88,22 @@ impl TryFrom<u8> for Tag {
#[derive(Debug, Clone, PartialEq, Ord, PartialOrd, Eq)]
pub enum Value<'a> {
// evaluated
Null,
Bool(bool),
Int(i64),
Float(OrderedFloat<f64>),
Uuid(Uuid),
Text(Cow<'a, str>),
// maybe evaluated
List(Vec<Value<'a>>),
Dict(BTreeMap<Cow<'a, str>, Value<'a>>),
// not evaluated
Variable(Cow<'a, str>),
Apply(Cow<'a, str>, Vec<Value<'a>>),
FieldAccess(Cow<'a, str>, Box<Value<'a>>),
IdxAccess(usize, Box<Value<'a>>),
// cannot exist
EndSentinel,
}
@ -354,6 +358,7 @@ pub const OP_MINUS: &str = "--";
pub const METHOD_IS_NULL: &str = "is_null";
pub const METHOD_NOT_NULL: &str = "not_null";
pub const METHOD_CONCAT: &str = "concat";
pub const METHOD_MERGE: &str = "merge";
fn build_expr_infix<'a>(lhs: Result<Value<'a>>, op: Pair<Rule>, rhs: Result<Value<'a>>) -> Result<Value<'a>> {
@ -443,7 +448,8 @@ fn build_expr_primary(pair: Pair<Rule>) -> Result<Value> {
Rule::spreading => {
let el = p.into_inner().next().unwrap();
let to_concat = build_expr_primary(el)?;
if !matches!(to_concat, Value::List(_) | Value::Variable(_)) {
if !matches!(to_concat, Value::List(_) | Value::Variable(_) |
Value::IdxAccess(_, _) | Value:: FieldAccess(_, _) | Value::Apply(_, _)) {
return Err(CozoError::LogicError("Cannot spread".to_string()));
}
if !collected.is_empty() {
@ -461,20 +467,50 @@ fn build_expr_primary(pair: Pair<Rule>) -> Result<Value> {
if !collected.is_empty() {
spread_collected.push(Value::List(collected));
}
Ok(Value::Apply("concat".into(), spread_collected))
Ok(Value::Apply(METHOD_CONCAT.into(), spread_collected))
}
Rule::dict => {
Ok(pair.into_inner().map(|p| {
let mut spread_collected = vec![];
let mut collected = BTreeMap::new();
for p in pair.into_inner() {
match p.as_rule() {
Rule::dict_pair => {
let mut inner = p.into_inner();
let name = parse_string(inner.next().unwrap())?;
let val = build_expr_primary(inner.next().unwrap())?;
Ok((name.into(), val))
collected.insert(name.into(), val);
}
_ => todo!()
Rule::scoped_accessor => {
let name = parse_string(p.into_inner().next().unwrap())?;
let val = Value::FieldAccess(name.clone().into(),
Value::Variable("_".into()).into());
collected.insert(name.into(), val);
}
Rule::spreading => {
let el = p.into_inner().next().unwrap();
let to_concat = build_expr_primary(el)?;
if !matches!(to_concat, Value::Dict(_) | Value::Variable(_) |
Value::IdxAccess(_, _) | Value:: FieldAccess(_, _) | Value::Apply(_, _)) {
return Err(CozoError::LogicError("Cannot spread".to_string()));
}
if !collected.is_empty() {
spread_collected.push(Value::Dict(collected));
collected = BTreeMap::new();
}
spread_collected.push(to_concat);
}
_ => unreachable!()
}
}).collect::<Result<BTreeMap<Cow<str>, Value>>>()?.into())
}
if spread_collected.is_empty() {
return Ok(Value::Dict(collected));
}
if !collected.is_empty() {
spread_collected.push(Value::Dict(collected));
}
Ok(Value::Apply(METHOD_MERGE.into(), spread_collected))
}
Rule::param => {
Ok(Value::Variable(pair.as_str().into()))
@ -535,4 +571,14 @@ mod tests {
assert_eq!(parse_expr_from_str(r#"'"x"'"#).unwrap(), Value::Text(r##""x""##.into()));
assert_eq!(parse_expr_from_str(r#####"r###"x"yz"###"#####).unwrap(), (Value::Text(r##"x"yz"##.into())));
}
#[test]
fn complex_cases() -> Result<()> {
println!("{}", parse_expr_from_str("{}")?);
println!("{}", parse_expr_from_str("{b:1,a,c:2,d,...e,}")?);
println!("{}", parse_expr_from_str("{...a,...b,c:1,d:2,...e,f:3}")?);
println!("{}", parse_expr_from_str("[]")?);
println!("{}", parse_expr_from_str("[...a,...b,1,2,...e,3]")?);
Ok(())
}
}
Loading…
Cancel
Save