diff --git a/core/src/sql/field.rs b/core/src/sql/field.rs index 7badac24..eab2b37a 100644 --- a/core/src/sql/field.rs +++ b/core/src/sql/field.rs @@ -250,9 +250,10 @@ impl Fields { _ => { let expr = expr.compute(stk, ctx, opt, Some(doc)).await?; // Check if this is a single VALUE field expression - match self.single().is_some() { - false => out.set(stk, ctx, opt, name.as_ref(), expr).await?, - true => out = expr, + if self.single().is_some() { + out = expr; + } else { + out.set(stk, ctx, opt, name.as_ref(), expr).await?; } } } diff --git a/core/src/sql/idiom.rs b/core/src/sql/idiom.rs index a93c89ec..0a6489e8 100644 --- a/core/src/sql/idiom.rs +++ b/core/src/sql/idiom.rs @@ -116,9 +116,7 @@ impl Idiom { pub(crate) fn simplify(&self) -> Idiom { self.0 .iter() - .filter(|&p| { - matches!(p, Part::Field(_) | Part::Start(_) | Part::Value(_) | Part::Graph(_)) - }) + .filter(|&p| matches!(p, Part::Field(_) | Part::Start(_) | Part::Graph(_))) .cloned() .collect::<Vec<_>>() .into() diff --git a/core/src/sql/range.rs b/core/src/sql/range.rs index 7c3159d9..f76de184 100644 --- a/core/src/sql/range.rs +++ b/core/src/sql/range.rs @@ -26,6 +26,84 @@ pub struct Range { pub end: Bound<Value>, } +impl Range { + pub fn slice<'a, T>(&self, s: &'a [T]) -> Option<&'a [T]> { + let r = match self.end { + Bound::Included(ref x) => { + let Value::Number(ref x) = x else { + return None; + }; + let x = x.to_usize(); + s.get(..=x)? + } + Bound::Excluded(ref x) => { + let Value::Number(ref x) = x else { + return None; + }; + let x = x.to_usize(); + s.get(..x)? + } + Bound::Unbounded => s, + }; + let r = match self.beg { + Bound::Included(ref x) => { + let Value::Number(ref x) = x else { + return None; + }; + let x = x.to_usize(); + r.get(x..)? + } + Bound::Excluded(ref x) => { + let Value::Number(ref x) = x else { + return None; + }; + let x = x.to_usize().saturating_add(1); + r.get(x..)? + } + Bound::Unbounded => r, + }; + Some(r) + } + + pub fn slice_mut<'a, T>(&self, s: &'a mut [T]) -> Option<&'a mut [T]> { + let r = match self.end { + Bound::Included(ref x) => { + let Value::Number(ref x) = x else { + return None; + }; + let x = x.to_usize(); + s.get_mut(..x)? + } + Bound::Excluded(ref x) => { + let Value::Number(ref x) = x else { + return None; + }; + let x = x.to_usize(); + s.get_mut(..=x)? + } + Bound::Unbounded => s, + }; + let r = match self.beg { + Bound::Included(ref x) => { + let Value::Number(ref x) = x else { + return None; + }; + let x = x.to_usize(); + r.get_mut(x..)? + } + Bound::Excluded(ref x) => { + let Value::Number(ref x) = x else { + return None; + }; + let x = x.to_usize().saturating_add(1); + r.get_mut(x..)? + } + Bound::Unbounded => r, + }; + Some(r) + } +} + impl FromStr for Range { type Err = (); fn from_str(s: &str) -> Result<Self, Self::Err> { diff --git a/core/src/sql/statements/select.rs b/core/src/sql/statements/select.rs index 6693f9c2..e380d34b 100644 --- a/core/src/sql/statements/select.rs +++ b/core/src/sql/statements/select.rs @@ -19,10 +19,12 @@ use std::sync::Arc; #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[non_exhaustive] pub struct SelectStatement { + /// The foo,bar part in SELECT foo,bar FROM baz. pub expr: Fields, pub omit: Option<Idioms>, #[revision(start = 2)] pub only: bool, + /// The baz part in SELECT foo,bar FROM baz. pub what: Values, pub with: Option<With>, pub cond: Option<Cond>, diff --git a/core/src/sql/value/fetch.rs b/core/src/sql/value/fetch.rs index 681f1b42..9ef36e41 100644 --- a/core/src/sql/value/fetch.rs +++ b/core/src/sql/value/fetch.rs @@ -11,7 +11,6 @@ use futures::future::try_join_all; use reblessive::tree::Stk; impl Value { - /// Was marked recursive pub(crate) async fn fetch( &mut self, stk: &mut Stk, @@ -19,152 +18,222 @@ impl Value { opt: &Options, path: &[Part], ) -> Result<(), Error> { - match path.first() { - // Get the current path part - Some(p) => match self { - // Current path part is an object - Value::Object(v) => match p { - Part::Graph(_) => match v.rid() { - Some(v) => { - let mut v = Value::Thing(v); - stk.run(|stk| v.fetch(stk, ctx, opt, path.next())).await + let mut this = self; + let mut iter = path.iter(); + let mut prev = path; + + // Loop over the path. + // If the we just need to select a sub section of a value we update this to point to the + // new subsection of the value. Otherwise we call into fetch again and then immediately + // return. + // If we encounter a idiom application which does not make sense, like `(1).foo` just + // return Ok(()) + while let Some(p) = iter.next() { + match p { + Part::Graph(g) => match this { + Value::Object(o) => { + let Some(v) = o.rid() else { + return Ok(()); + }; + + let mut v = Value::Thing(v); + return stk.run(|stk| v.fetch(stk, ctx, opt, iter.as_slice())).await; + } + Value::Thing(x) => { + let stm = SelectStatement { + expr: Fields(vec![Field::All], false), + what: Values(vec![Value::from(Edges { + from: x.clone(), + dir: g.dir.clone(), + what: g.what.clone(), + })]), + cond: g.cond.clone(), + ..SelectStatement::default() + }; + *this = stm + .compute(stk, ctx, opt, None) + .await? + .all() + .get(stk, ctx, opt, None, path.next()) + .await? + .flatten() + .ok()?; + return Ok(()); + } + Value::Array(x) => { + // apply this path to every entry of the array. + stk.scope(|scope| { + let futs = + x.iter_mut().map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, prev))); + try_join_all(futs) + }) + .await?; + return Ok(()); + } + // break her to be comp + _ => return Ok(()), + }, + Part::Field(f) => match this { + Value::Object(o) => { + let Some(x) = o.get_mut(f.0.as_str()) else { + return Ok(()); + }; + this = x; + } + Value::Array(x) => { + // apply this path to every entry of the array. + stk.scope(|scope| { + let futs = + x.iter_mut().map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, prev))); + try_join_all(futs) + }) + .await?; + return Ok(()); + } + _ => break, + }, + Part::Index(i) => match this { + Value::Object(v) => { + let Some(x) = v.get_mut(&i.to_string()) else { + return Ok(()); + }; + this = x; + } + Value::Array(v) => { + let Some(x) = v.get_mut(i.to_usize()) else { + return Ok(()); + }; + this = x; + } + _ => break, + }, + Part::Value(v) => { + let v = v.compute(stk, ctx, opt, None).await?; + match this { + Value::Object(obj) => { + let Some(x) = obj.get_mut(v.coerce_to_string()?.as_str()) else { + return Ok(()); + }; + this = x; } - None => Ok(()), - }, - Part::Field(f) => match v.get_mut(f as &str) { - Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, path.next())).await, - None => Ok(()), - }, - Part::Index(i) => match v.get_mut(&i.to_string()) { - Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, path.next())).await, - None => Ok(()), - }, - Part::All => stk.run(|stk| self.fetch(stk, ctx, opt, path.next())).await, - Part::Destructure(p) => { + Value::Array(array) => { + if let Value::Range(x) = v { + let Some(range) = x.slice(array) else { + return Ok(()); + }; + let mut range = Value::Array(range.to_vec().into()); + return stk + .run(|stk| range.fetch(stk, ctx, opt, iter.as_slice())) + .await; + } + let Some(x) = array.get_mut(v.coerce_to_u64()? as usize) else { + return Ok(()); + }; + this = x; + } + _ => return Ok(()), + } + } + Part::Destructure(p) => match this { + Value::Array(x) => { + // apply this path to every entry of the array. + stk.scope(|scope| { + let futs = + x.iter_mut().map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, prev))); + try_join_all(futs) + }) + .await?; + } + Value::Object(_) => { for p in p.iter() { - let path = [(p.path().as_slice()), path].concat(); - stk.run(|stk| self.fetch(stk, ctx, opt, &path)).await?; + let mut destructure_path = p.path(); + destructure_path.extend_from_slice(path); + stk.run(|stk| this.fetch(stk, ctx, opt, &destructure_path)).await?; + } + return Ok(()); + } + _ => return Ok(()), + }, + Part::All => match this { + Value::Object(_) => { + continue; + } + Value::Array(x) => { + let next_path = iter.as_slice(); + // no need to spawn all those futures if their is no more paths to + // calculate + if next_path.is_empty() { + break; } - Ok(()) - } - _ => Ok(()), - }, - // Current path part is an array - Value::Array(v) => match p { - Part::All => { - let path = path.next(); stk.scope(|scope| { - let futs = - v.iter_mut().map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, path))); + let futs = x + .iter_mut() + .map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, next_path))); try_join_all(futs) }) .await?; - Ok(()) + return Ok(()); } - Part::First => match v.first_mut() { - Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, path.next())).await, - None => Ok(()), - }, - Part::Last => match v.last_mut() { - Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, path.next())).await, - None => Ok(()), - }, - Part::Index(i) => match v.get_mut(i.to_usize()) { - Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, path.next())).await, - None => Ok(()), - }, - Part::Where(w) => { - let path = path.next(); - for v in v.iter_mut() { - let cur = v.clone().into(); - if w.compute(stk, ctx, opt, Some(&cur)).await?.is_truthy() { - stk.run(|stk| v.fetch(stk, ctx, opt, path)).await?; + _ => break, + }, + Part::First => match this { + Value::Array(x) => { + let Some(x) = x.first_mut() else { + return Ok(()); + }; + this = x; + } + _ => return Ok(()), + }, + Part::Last => match this { + Value::Array(x) => { + let Some(x) = x.last_mut() else { + return Ok(()); + }; + this = x; + } + _ => return Ok(()), + }, + Part::Where(w) => match this { + Value::Array(x) => { + for v in x.iter_mut() { + let doc = v.clone().into(); + if w.compute(stk, ctx, opt, Some(&doc)).await?.is_truthy() { + stk.run(|stk| v.fetch(stk, ctx, opt, iter.as_slice())).await?; } } - Ok(()) - } - _ => { - stk.scope(|scope| { - let futs = - v.iter_mut().map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, path))); - try_join_all(futs) - }) - .await?; - Ok(()) } + _ => return Ok(()), }, - // Current path part is a thing - Value::Thing(v) => { - // Clone the thing - let val = v.clone(); - // Fetch the remote embedded record - match p { - // This is a graph traversal expression - Part::Graph(g) => { - let stm = SelectStatement { - expr: Fields(vec![Field::All], false), - what: Values(vec![Value::from(Edges { - from: val, - dir: g.dir.clone(), - what: g.what.clone(), - })]), - cond: g.cond.clone(), - ..SelectStatement::default() - }; - *self = stm - .compute(stk, ctx, opt, None) - .await? - .all() - .get(stk, ctx, opt, None, path.next()) - .await? - .flatten() - .ok()?; - Ok(()) - } - // This is a remote field expression - _ => { - let stm = SelectStatement { - expr: Fields(vec![Field::All], false), - what: Values(vec![Value::from(val)]), - ..SelectStatement::default() - }; - *self = stm.compute(stk, ctx, opt, None).await?.first(); - Ok(()) - } - } - } - // Ignore everything else - _ => Ok(()), - }, - // No more parts so get the value - None => match self { - // Current path part is an array - Value::Array(v) => { - stk.scope(|scope| { - let futs = - v.iter_mut().map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, path))); - try_join_all(futs) - }) - .await?; - Ok(()) - } - // Current path part is a thing - Value::Thing(v) => { - // Clone the thing - let val = v.clone(); - // Fetch the remote embedded record - let stm = SelectStatement { - expr: Fields(vec![Field::All], false), - what: Values(vec![Value::from(val)]), - ..SelectStatement::default() - }; - *self = stm.compute(stk, ctx, opt, None).await?.first(); - Ok(()) - } - // Ignore everything else - _ => Ok(()), - }, + _ => break, + } + prev = iter.as_slice(); + } + + // If the final value is on of following types we still need to compute it. + match this { + Value::Array(v) => { + stk.scope(|scope| { + let futs = v.iter_mut().map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, path))); + try_join_all(futs) + }) + .await?; + Ok(()) + } + Value::Thing(v) => { + // Clone the thing + let val = v.clone(); + // Fetch the remote embedded record + let stm = SelectStatement { + expr: Fields(vec![Field::All], false), + what: Values(vec![Value::from(val)]), + ..SelectStatement::default() + }; + *this = stm.compute(stk, ctx, opt, None).await?.first(); + Ok(()) + } + _ => Ok(()), } } } diff --git a/core/src/sql/value/get.rs b/core/src/sql/value/get.rs index bf34dace..0ff6c87f 100644 --- a/core/src/sql/value/get.rs +++ b/core/src/sql/value/get.rs @@ -233,6 +233,21 @@ impl Value { Some(v) => stk.run(|stk| v.get(stk, ctx, opt, doc, path.next())).await, None => Ok(Value::None), }, + Value::Range(r) => { + if let Some(range) = r.slice(v.as_slice()) { + let path = path.next(); + stk.scope(|scope| { + let futs = range + .iter() + .map(|v| scope.run(|stk| v.get(stk, ctx, opt, doc, path))); + try_join_all_buffered(futs) + }) + .await + .map(Into::into) + } else { + Ok(Value::None) + } + } _ => stk.run(|stk| Value::None.get(stk, ctx, opt, doc, path.next())).await, }, Part::Method(name, args) => { diff --git a/core/src/sql/value/set.rs b/core/src/sql/value/set.rs index 3d633d74..bb63060c 100644 --- a/core/src/sql/value/set.rs +++ b/core/src/sql/value/set.rs @@ -1,10 +1,12 @@ +use std::collections::btree_map::Entry; + use crate::ctx::Context; use crate::dbs::Options; use crate::err::Error; use crate::exe::try_join_all_buffered; -use crate::sql::part::Next; use crate::sql::part::Part; use crate::sql::value::Value; +use crate::sql::Object; use reblessive::tree::Stk; impl Value { @@ -19,162 +21,254 @@ impl Value { path: &[Part], val: Value, ) -> Result<(), Error> { - match path.first() { - // Get the current value at path - Some(p) => match self { - // Current value at path is an object - Value::Object(v) => match p { - Part::Graph(g) => match v.get_mut(g.to_raw().as_str()) { - Some(v) if v.is_some() => { - stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await - } - _ => { - let mut obj = Value::base(); - stk.run(|stk| obj.set(stk, ctx, opt, path.next(), val)).await?; - v.insert(g.to_raw(), obj); - Ok(()) - } - }, - Part::Field(f) => match v.get_mut(f.as_str()) { - Some(v) if v.is_some() => { - stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await - } - _ => { - let mut obj = Value::base(); - stk.run(|stk| obj.set(stk, ctx, opt, path.next(), val)).await?; - v.insert(f.to_raw(), obj); - Ok(()) - } - }, - Part::Index(i) => match v.get_mut(&i.to_string()) { - Some(v) if v.is_some() => { - stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await - } - _ => { - let mut obj = Value::base(); - stk.run(|stk| obj.set(stk, ctx, opt, path.next(), val)).await?; - v.insert(i.to_string(), obj); - Ok(()) - } - }, - Part::Value(x) => match stk.run(|stk| x.compute(stk, ctx, opt, None)).await? { - Value::Strand(f) => match v.get_mut(f.as_str()) { - Some(v) if v.is_some() => { - stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await - } - _ => { - let mut obj = Value::base(); - stk.run(|stk| obj.set(stk, ctx, opt, path.next(), val)).await?; - v.insert(f.to_raw(), obj); - Ok(()) - } - }, - _ => Ok(()), - }, - _ => Ok(()), - }, - // Current value at path is an array - Value::Array(v) => match p { - Part::All => { - let path = path.next(); - - stk.scope(|scope| { - let futs = v - .iter_mut() - .map(|v| scope.run(|stk| v.set(stk, ctx, opt, path, val.clone()))); - try_join_all_buffered(futs) - }) - .await?; - Ok(()) - } - Part::First => match v.first_mut() { - Some(v) => stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await, - None => Ok(()), - }, - Part::Last => match v.last_mut() { - Some(v) => stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await, - None => Ok(()), - }, - Part::Index(i) => match v.get_mut(i.to_usize()) { - Some(v) => stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await, - None => Ok(()), - }, - Part::Where(w) => match path.next().first() { - Some(Part::Index(_)) => { - let mut a = Vec::new(); - let mut p = Vec::new(); - // Store the elements and positions to update - for (i, o) in v.iter_mut().enumerate() { - let cur = o.clone().into(); - if w.compute(stk, ctx, opt, Some(&cur)).await?.is_truthy() { - a.push(o.clone()); - p.push(i); - } - } - // Convert the matched elements array to a value - let mut a = Value::from(a); - // Set the new value on the matches elements - stk.run(|stk| a.set(stk, ctx, opt, path.next(), val.clone())).await?; - // Push the new values into the original array - for (i, p) in p.into_iter().enumerate() { - v[p] = a.pick(&[Part::Index(i.into())]); - } - Ok(()) - } - _ => { - let path = path.next(); - for v in v.iter_mut() { - let cur = v.clone().into(); - if w.compute(stk, ctx, opt, Some(&cur)).await?.is_truthy() { - stk.run(|stk| v.set(stk, ctx, opt, path, val.clone())).await?; - } - } - Ok(()) - } - }, - Part::Value(x) => match x.compute(stk, ctx, opt, None).await? { - Value::Number(i) => match v.get_mut(i.to_usize()) { - Some(v) => stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await, - None => Ok(()), - }, - _ => Ok(()), - }, - _ => { - stk.scope(|scope| { - let futs = v - .iter_mut() - .map(|v| scope.run(|stk| v.set(stk, ctx, opt, path, val.clone()))); - try_join_all_buffered(futs) - }) - .await?; - - Ok(()) - } - }, - // Current value at path is a record - Value::Thing(_) => { - *self = Value::base(); - stk.run(|stk| self.set(stk, ctx, opt, path, val)).await - } - // Current value at path is empty - Value::Null => { - *self = Value::base(); - stk.run(|stk| self.set(stk, ctx, opt, path, val)).await - } - // Current value at path is empty - Value::None => { - *self = Value::base(); - stk.run(|stk| self.set(stk, ctx, opt, path, val)).await - } - // Ignore everything else - _ => Ok(()), - }, - // No more parts so set the value - None => { - *self = val; - Ok(()) - } + if path.is_empty() { + *self = val; + return Ok(()); } + + let mut iter = path.iter(); + let mut place = self; + let mut prev = path; + + // Index forward trying to find the location where to insert the value + // Whenever we hit an existing path in the value we update place to point to the new value. + // If we hit a dead end, we then assign the into that dead end. If any path is not yet + // matched we use that to create an object to assign. + while let Some(p) = iter.next() { + match place { + Value::Thing(_) | Value::Null | Value::None => { + // any index is guaranteed to fail so just assign to this place. + return Self::assign(stk, ctx, opt, place, val, prev).await; + } + _ => {} + } + + match p { + Part::Graph(g) => { + match place { + Value::Object(obj) => match obj.entry(g.to_raw()) { + Entry::Vacant(x) => { + let v = x.insert(Value::None); + return Self::assign(stk, ctx, opt, v, val, iter.as_slice()).await; + } + Entry::Occupied(x) => { + place = x.into_mut(); + } + }, + Value::Array(arr) => { + // Apply to all entries of the array + stk.scope(|scope| { + let futs = arr.iter_mut().map(|v| { + scope.run(|stk| v.set(stk, ctx, opt, prev, val.clone())) + }); + try_join_all_buffered(futs) + }) + .await?; + return Ok(()); + } + _ => return Ok(()), + }; + } + Part::Field(f) => { + match place { + Value::Object(obj) => match obj.entry(f.0.clone()) { + Entry::Vacant(x) => { + let v = x.insert(Value::None); + return Self::assign(stk, ctx, opt, v, val, iter.as_slice()).await; + } + Entry::Occupied(x) => { + place = x.into_mut(); + } + }, + Value::Array(arr) => { + // Apply to all entries of the array + stk.scope(|scope| { + let futs = arr.iter_mut().map(|v| { + scope.run(|stk| v.set(stk, ctx, opt, prev, val.clone())) + }); + try_join_all_buffered(futs) + }) + .await?; + return Ok(()); + } + _ => return Ok(()), + }; + } + Part::Index(f) => match place { + Value::Object(obj) => match obj.entry(f.to_string()) { + Entry::Vacant(x) => { + let v = x.insert(Value::None); + return Self::assign(stk, ctx, opt, v, val, prev).await; + } + Entry::Occupied(x) => { + place = x.into_mut(); + } + }, + Value::Array(arr) => { + if let Some(x) = arr.get_mut(f.to_usize()) { + place = x + } else { + return Ok(()); + } + } + _ => return Ok(()), + }, + Part::Value(x) => { + let v = stk.run(|stk| x.compute(stk, ctx, opt, None)).await?; + + match place { + Value::Object(obj) => { + let v = match v { + Value::Strand(x) => x.0.clone(), + x => x.to_string(), + }; + + match obj.entry(v) { + Entry::Vacant(x) => { + let v = x.insert(Value::None); + return Self::assign(stk, ctx, opt, v, val, prev).await; + } + Entry::Occupied(x) => { + place = x.into_mut(); + } + } + } + Value::Array(arr) => match v { + Value::Range(x) => { + if let Some(v) = x.slice_mut(arr) { + let path = iter.as_slice(); + stk.scope(|scope| { + let futs = v.iter_mut().map(|v| { + scope.run(|stk| v.set(stk, ctx, opt, path, val.clone())) + }); + try_join_all_buffered(futs) + }) + .await?; + return Ok(()); + } else { + return Ok(()); + } + } + Value::Number(i) => { + if let Some(v) = arr.get_mut(i.to_usize()) { + place = v; + } else { + return Ok(()); + } + } + _ => return Ok(()), + }, + _ => return Ok(()), + } + } + Part::First => { + let Value::Array(arr) = place else { + return Ok(()); + }; + let Some(x) = arr.first_mut() else { + return Ok(()); + }; + place = x + } + Part::Last => { + let Value::Array(arr) = place else { + return Ok(()); + }; + let Some(x) = arr.last_mut() else { + return Ok(()); + }; + place = x + } + Part::All => { + let Value::Array(arr) = place else { + return Ok(()); + }; + + let path = iter.as_slice(); + stk.scope(|scope| { + let futs = arr + .iter_mut() + .map(|v| scope.run(|stk| v.set(stk, ctx, opt, path, val.clone()))); + try_join_all_buffered(futs) + }) + .await?; + return Ok(()); + } + Part::Where(w) => { + let Value::Array(arr) = place else { + return Ok(()); + }; + if let Some(Part::Index(_)) = iter.as_slice().first() { + let mut a = Vec::new(); + let mut p = Vec::new(); + // Store the elements and positions to update + for (i, o) in arr.iter_mut().enumerate() { + let cur = o.clone().into(); + if w.compute(stk, ctx, opt, Some(&cur)).await?.is_truthy() { + a.push(o.clone()); + p.push(i); + } + } + // Convert the matched elements array to a value + let mut a = Value::from(a); + // Set the new value on the matches elements + stk.run(|stk| a.set(stk, ctx, opt, iter.as_slice(), val.clone())).await?; + // Push the new values into the original array + for (i, p) in p.into_iter().enumerate() { + arr[p] = a.pick(&[Part::Index(i.into())]); + } + return Ok(()); + } else { + for v in arr.iter_mut() { + let cur = v.clone().into(); + if w.compute(stk, ctx, opt, Some(&cur)).await?.is_truthy() { + stk.run(|stk| v.set(stk, ctx, opt, iter.as_slice(), val.clone())) + .await?; + } + } + return Ok(()); + } + } + _ => return Ok(()), + } + prev = iter.as_slice(); + } + + *place = val; + Ok(()) + } + + async fn assign( + stk: &mut Stk, + ctx: &Context, + opt: &Options, + place: &mut Value, + mut val: Value, + path: &[Part], + ) -> Result<(), Error> { + for p in path.iter().rev() { + let name = match p { + Part::Graph(x) => x.to_raw(), + Part::Field(f) => f.0.clone(), + Part::Index(i) => i.to_string(), + Part::Value(x) => { + let v = stk.run(|stk| x.compute(stk, ctx, opt, None)).await?; + match v { + Value::Strand(x) => x.0, + Value::Number(x) => x.to_string(), + Value::Range(x) => x.to_string(), + _ => return Ok(()), + } + } + _ => return Ok(()), + }; + let mut object = Object::default(); + object.insert(name, val); + val = object.into(); + } + + *place = val; + Ok(()) } } diff --git a/core/src/syn/parser/idiom.rs b/core/src/syn/parser/idiom.rs index 3de53dde..852f69a6 100644 --- a/core/src/syn/parser/idiom.rs +++ b/core/src/syn/parser/idiom.rs @@ -346,25 +346,18 @@ impl Parser<'_> { self.pop_peek(); Part::Last } - t!("+") | TokenKind::Digits | TokenKind::Glued(Glued::Number) => { - Part::Index(self.next_token_value()?) - } - t!("-") => { - if let TokenKind::Digits = self.peek_whitespace1().kind { - unexpected!(self, peek,"$, * or a number", => "An index can't be negative."); - } - unexpected!(self, peek, "$, * or a number"); - } t!("?") | t!("WHERE") => { self.pop_peek(); let value = ctx.run(|ctx| self.parse_value_field(ctx)).await?; Part::Where(value) } - t!("$param") => Part::Value(Value::Param(self.next_token_value()?)), - TokenKind::Qoute(_x) => Part::Value(Value::Strand(self.next_token_value()?)), _ => { - let idiom = self.parse_basic_idiom(ctx).await?; - Part::Value(Value::Idiom(idiom)) + let value = ctx.run(|ctx| self.parse_value_inherit(ctx)).await?; + if let Value::Number(x) = value { + Part::Index(x) + } else { + Part::Value(value) + } } }; self.expect_closing_delimiter(t!("]"), start)?; diff --git a/core/src/syn/parser/mac.rs b/core/src/syn/parser/mac.rs index 0c981c00..95e65141 100644 --- a/core/src/syn/parser/mac.rs +++ b/core/src/syn/parser/mac.rs @@ -22,7 +22,7 @@ macro_rules! unexpected { $crate::syn::error::bail!("Unexpected whitespace, expected token {} to continue",$expected, @__found.span$( $($t)* )?) } x => { - $crate::syn::error::bail!("Unexpected token {}, expected {}",x,$expected, @__found.span$( $($t)* )?) + $crate::syn::error::bail!("Unexpected token `{}`, expected {}",x,$expected, @__found.span$( $($t)* )?) } } }}; diff --git a/core/src/syn/parser/test/value.rs b/core/src/syn/parser/test/value.rs index 5efb494a..6dcc6ecd 100644 --- a/core/src/syn/parser/test/value.rs +++ b/core/src/syn/parser/test/value.rs @@ -4,15 +4,37 @@ use reblessive::Stack; use crate::{ sql::{ - Array, Constant, Id, Idiom, Number, Object, Part, Query, Statement, Statements, Strand, - Thing, Value, + Array, Constant, Expression, Geometry, Id, Ident, Idiom, Number, Object, Operator, Part, + Query, Statement, Statements, Strand, Thing, Value, }, syn::parser::{mac::test_parse, Parser}, }; +#[test] +fn parse_index_expression() { + let value = test_parse!(parse_value_table, "a[1 + 1]").unwrap(); + let Value::Idiom(x) = value else { + panic!("not the right value type"); + }; + assert_eq!(x.0[0], Part::Field(Ident("a".to_string()))); + assert_eq!( + x.0[1], + Part::Value(Value::Expression(Box::new(Expression::Binary { + l: Value::Number(Number::Int(1)), + o: Operator::Add, + r: Value::Number(Number::Int(1)), + }))) + ) +} + #[test] fn parse_coordinate() { - test_parse!(parse_value_table, "(1.88, -18.0)").unwrap(); + let coord = test_parse!(parse_value_table, "(1.88, -18.0)").unwrap(); + let Value::Geometry(Geometry::Point(x)) = coord else { + panic!("not the right value"); + }; + assert_eq!(x.x(), 1.88); + assert_eq!(x.y(), -18.0); } #[test] diff --git a/sdk/tests/idiom.rs b/sdk/tests/idiom.rs index c6583921..0a7a3352 100644 --- a/sdk/tests/idiom.rs +++ b/sdk/tests/idiom.rs @@ -12,3 +12,47 @@ async fn idiom_chain_part_optional() -> Result<(), Error> { Test::new(sql).await?.expect_val("false")?.expect_val("None")?; Ok(()) } + +#[tokio::test] +async fn idiom_index_expression() -> Result<(), Error> { + let sql = r#" + [1,2,3,4][1 + 1]; + "#; + Test::new(sql).await?.expect_val("3")?; + Ok(()) +} + +#[tokio::test] +async fn idiom_index_call() -> Result<(), Error> { + let sql = r#" + DEFINE FUNCTION fn::foo() { + return 1 + 1; + }; + RETURN [1,2,3,4][fn::foo()]; + "#; + Test::new(sql).await?.expect_val("None")?.expect_val("3")?; + Ok(()) +} + +#[tokio::test] +async fn idiom_index_range() -> Result<(), Error> { + let sql = r#" + [1,2,3,4][1..2]; + [1,2,3,4][1..=2]; + [1,2,3,4][1>..=2]; + [1,2,3,4][1>..]; + [1,2,3,4][1..]; + [1,2,3,4][..2]; + [1,2,3,4][..=2]; + "#; + Test::new(sql) + .await? + .expect_val("[2]")? + .expect_val("[2,3]")? + .expect_val("[3]")? + .expect_val("[3,4]")? + .expect_val("[2,3,4]")? + .expect_val("[1,2]")? + .expect_val("[1,2,3]")?; + Ok(()) +}