Lower parsing complexity for binary values and idioms (#2475)

This commit is contained in:
Mees Delzenne 2023-08-21 12:33:57 +02:00 committed by GitHub
parent b350f052a7
commit 76b962eb6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 44 deletions

View file

@ -50,7 +50,7 @@ impl Expression {
}
}
/// Augment an existing expression
fn augment(mut self, l: Value, o: Operator) -> Self {
pub(crate) fn augment(mut self, l: Value, o: Operator) -> Self {
match &mut self {
Self::Binary {
l: left,

View file

@ -5,7 +5,7 @@ use crate::err::Error;
use crate::sql::common::commas;
use crate::sql::error::IResult;
use crate::sql::fmt::{fmt_separated_by, Fmt};
use crate::sql::part::{all, field, first, graph, index, last, part, start, Part};
use crate::sql::part::{all, field, first, graph, index, last, part, Part};
use crate::sql::part::{flatten, Next};
use crate::sql::paths::{ID, IN, META, OUT};
use crate::sql::value::Value;
@ -210,8 +210,21 @@ pub fn plain(i: &str) -> IResult<&str, Idiom> {
Ok((i, Idiom::from(v)))
}
/// A complex idiom with graph or many parts
pub fn multi(i: &str) -> IResult<&str, Idiom> {
/// Reparse a value which might part of an idiom.
pub fn reparse_idiom_start(start: Value, i: &str) -> IResult<&str, Value> {
if start.can_start_idiom() {
if let (i, Some(mut parts)) = opt(many1(part))(i)? {
let start = Part::Start(start);
parts.insert(0, start);
let v = Value::from(Idiom::from(parts));
return Ok((i, v));
}
}
Ok((i, start))
}
/// A complex idiom with graph or many parts excluding idioms which start with a value.
pub fn multi_without_start(i: &str) -> IResult<&str, Idiom> {
alt((
|i| {
let (i, p) = graph(i)?;
@ -220,7 +233,7 @@ pub fn multi(i: &str) -> IResult<&str, Idiom> {
Ok((i, Idiom::from(v)))
},
|i| {
let (i, p) = alt((first, start))(i)?;
let (i, p) = first(i)?;
let (i, mut v) = many1(part)(i)?;
v.insert(0, p);
Ok((i, Idiom::from(v)))
@ -239,7 +252,21 @@ pub fn path(i: &str) -> IResult<&str, Idiom> {
/// A full complex idiom with any number of parts
#[cfg(test)]
pub fn idiom(i: &str) -> IResult<&str, Idiom> {
alt((plain, multi))(i)
use nom::combinator::fail;
use crate::sql::value::value;
alt((
plain,
alt((multi_without_start, |i| {
let (i, v) = value(i)?;
let (i, v) = reparse_idiom_start(v, i)?;
if let Value::Idiom(x) = v {
return Ok((i, x));
}
fail(i)
})),
))(i)
}
#[cfg(test)]

View file

@ -217,11 +217,6 @@ pub fn value(i: &str) -> IResult<&str, Part> {
Ok((i, Part::Value(v)))
}
pub fn start(i: &str) -> IResult<&str, Part> {
let (i, v) = value::start(i)?;
Ok((i, Part::Start(v)))
}
pub fn graph(i: &str) -> IResult<&str, Part> {
let (i, v) = graph::graph(i)?;
Ok((i, Part::Graph(v)))

View file

@ -20,11 +20,11 @@ use crate::sql::ending::keyword;
use crate::sql::error::IResult;
use crate::sql::expression::{binary, unary, Expression};
use crate::sql::fmt::{Fmt, Pretty};
use crate::sql::function::{self, function, Function};
use crate::sql::function::{function, Function};
use crate::sql::future::{future, Future};
use crate::sql::geometry::{geometry, Geometry};
use crate::sql::id::{Gen, Id};
use crate::sql::idiom::{self, Idiom};
use crate::sql::idiom::{self, reparse_idiom_start, Idiom};
use crate::sql::kind::Kind;
use crate::sql::model::{model, Model};
use crate::sql::number::decimal_is_integer;
@ -40,7 +40,7 @@ use crate::sql::subquery::{subquery, Subquery};
use crate::sql::table::{table, Table};
use crate::sql::thing::{thing, Thing};
use crate::sql::uuid::{uuid as unique, Uuid};
use crate::sql::Query;
use crate::sql::{operator, Query};
use async_recursion::async_recursion;
use chrono::{DateTime, Utc};
use derive::Store;
@ -1044,6 +1044,25 @@ impl Value {
}
}
/// Returns if this value can be the start of a idiom production.
pub fn can_start_idiom(&self) -> bool {
match self {
Value::Function(x) => !x.is_script(),
Value::Subquery(_)
| Value::Constant(_)
| Value::Datetime(_)
| Value::Duration(_)
| Value::Uuid(_)
| Value::Number(_)
| Value::Object(_)
| Value::Array(_)
| Value::Param(_)
| Value::Edges(_)
| Value::Thing(_) => true,
_ => false,
}
}
/// Try to convert this Value into a set of JSONPatch operations
pub fn to_operations(&self) -> Result<Vec<Operation>, Error> {
match self {
@ -2709,12 +2728,27 @@ impl TryNeg for Value {
/// Parse any `Value` including expressions
pub fn value(i: &str) -> IResult<&str, Value> {
alt((map(binary, Value::from), single))(i)
let (i, start) = single(i)?;
let (i, expr_tail) = opt(|i| {
let (i, o) = operator::binary(i)?;
let (i, r) = value(i)?;
Ok((i, (o, r)))
})(i)?;
let v = if let Some((o, r)) = expr_tail {
let expr = match r {
Value::Expression(r) => r.augment(start, o),
_ => Expression::new(start, o, r),
};
Value::from(expr)
} else {
start
};
Ok((i, v))
}
/// Parse any `Value` excluding binary expressions
pub fn single(i: &str) -> IResult<&str, Value> {
alt((
let (i, v) = alt((
alt((
terminated(
alt((
@ -2725,7 +2759,7 @@ pub fn single(i: &str) -> IResult<&str, Value> {
)),
keyword,
),
map(idiom::multi, Value::from),
map(idiom::multi_without_start, Value::from),
)),
alt((
map(cast, Value::from),
@ -2753,11 +2787,12 @@ pub fn single(i: &str) -> IResult<&str, Value> {
map(strand, Value::from),
map(idiom::path, Value::from),
)),
))(i)
))(i)?;
reparse_idiom_start(v, i)
}
pub fn select(i: &str) -> IResult<&str, Value> {
alt((
let (i, v) = alt((
alt((
map(unary, Value::from),
map(binary, Value::from),
@ -2765,7 +2800,7 @@ pub fn select(i: &str) -> IResult<&str, Value> {
map(tag_no_case("NULL"), |_| Value::Null),
map(tag_no_case("true"), |_| Value::Bool(true)),
map(tag_no_case("false"), |_| Value::Bool(false)),
map(idiom::multi, Value::from),
map(idiom::multi_without_start, Value::from),
)),
alt((
map(cast, Value::from),
@ -2790,33 +2825,14 @@ pub fn select(i: &str) -> IResult<&str, Value> {
map(table, Value::from),
map(strand, Value::from),
)),
))(i)
}
/// Used as the starting part of a complex Idiom
pub fn start(i: &str) -> IResult<&str, Value> {
alt((
map(function::normal, Value::from),
map(function::custom, Value::from),
map(subquery, Value::from),
map(constant, Value::from),
map(datetime, Value::from),
map(duration, Value::from),
map(unique, Value::from),
map(number, Value::from),
map(strand, Value::from),
map(object, Value::from),
map(array, Value::from),
map(param, Value::from),
map(edges, Value::from),
map(thing, Value::from),
))(i)
))(i)?;
reparse_idiom_start(v, i)
}
/// Used in CREATE, UPDATE, and DELETE clauses
pub fn what(i: &str) -> IResult<&str, Value> {
alt((
map(idiom::multi, Value::from),
let (i, v) = alt((
map(idiom::multi_without_start, Value::from),
map(function, Value::from),
map(subquery, Value::from),
map(constant, Value::from),
@ -2830,7 +2846,8 @@ pub fn what(i: &str) -> IResult<&str, Value> {
map(range, Value::from),
map(thing, Value::from),
map(table, Value::from),
))(i)
))(i)?;
reparse_idiom_start(v, i)
}
/// Used to parse any simple JSON-like value