Implement expressions in index operators ()

This commit is contained in:
Mees Delzenne 2024-09-16 22:14:41 +02:00 committed by GitHub
parent 82636d43fa
commit 912cafd3c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 629 additions and 313 deletions

View file

@ -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?;
}
}
}

View file

@ -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()

View file

@ -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> {

View file

@ -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>,

View file

@ -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(()),
}
}
}

View file

@ -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) => {

View file

@ -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(())
}
}

View file

@ -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)?;

View file

@ -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)* )?)
}
}
}};

View file

@ -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]

View file

@ -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(())
}