Merges MultiSetPattern and ListPattern

It would be nice for optimizations to intricate the two together
pull/10/head
Tpt 6 years ago
parent fb152aa3ee
commit 9659e33251
  1. 553
      lib/src/sparql/algebra.rs
  2. 79
      lib/src/sparql/eval.rs
  3. 48
      lib/src/sparql/parser.rs
  4. 48
      lib/src/sparql/sparql_grammar.rustpeg

@ -474,7 +474,7 @@ pub enum Expression {
IsNumericFunctionCall(Box<Expression>), IsNumericFunctionCall(Box<Expression>),
RegexFunctionCall(Box<Expression>, Box<Expression>, Option<Box<Expression>>), RegexFunctionCall(Box<Expression>, Box<Expression>, Option<Box<Expression>>),
CustomFunctionCall(NamedNode, Vec<Expression>), CustomFunctionCall(NamedNode, Vec<Expression>),
ExistsFunctionCall(Box<MultiSetPattern>), ExistsFunctionCall(Box<GraphPattern>),
} }
impl fmt::Display for Expression { impl fmt::Display for Expression {
@ -700,7 +700,7 @@ impl<'a> fmt::Display for SparqlExpression<'a> {
Expression::UnaryMinusExpression(e) => write!(f, "-{}", SparqlExpression(&*e)), Expression::UnaryMinusExpression(e) => write!(f, "-{}", SparqlExpression(&*e)),
Expression::UnaryNotExpression(e) => match e.as_ref() { Expression::UnaryNotExpression(e) => match e.as_ref() {
Expression::ExistsFunctionCall(p) => { Expression::ExistsFunctionCall(p) => {
write!(f, "NOT EXISTS {{ {} }}", SparqlMultiSetPattern(&*p)) write!(f, "NOT EXISTS {{ {} }}", SparqlGraphPattern(&*p))
} }
e => write!(f, "!{}", e), e => write!(f, "!{}", e),
}, },
@ -890,31 +890,36 @@ impl<'a> fmt::Display for SparqlExpression<'a> {
.join(", ") .join(", ")
), ),
Expression::ExistsFunctionCall(p) => { Expression::ExistsFunctionCall(p) => {
write!(f, "EXISTS {{ {} }}", SparqlMultiSetPattern(&*p)) write!(f, "EXISTS {{ {} }}", SparqlGraphPattern(&*p))
} }
} }
} }
} }
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub enum MultiSetPattern { pub enum GraphPattern {
BGP(Vec<TripleOrPathPattern>), BGP(Vec<TripleOrPathPattern>),
Join(Box<MultiSetPattern>, Box<MultiSetPattern>), Join(Box<GraphPattern>, Box<GraphPattern>),
LeftJoin(Box<MultiSetPattern>, Box<MultiSetPattern>, Expression), LeftJoin(Box<GraphPattern>, Box<GraphPattern>, Expression),
Filter(Expression, Box<MultiSetPattern>), Filter(Expression, Box<GraphPattern>),
Union(Box<MultiSetPattern>, Box<MultiSetPattern>), Union(Box<GraphPattern>, Box<GraphPattern>),
Graph(NamedNodeOrVariable, Box<MultiSetPattern>), Graph(NamedNodeOrVariable, Box<GraphPattern>),
Extend(Box<MultiSetPattern>, Variable, Expression), Extend(Box<GraphPattern>, Variable, Expression),
Minus(Box<MultiSetPattern>, Box<MultiSetPattern>), Minus(Box<GraphPattern>, Box<GraphPattern>),
ToMultiSet(Box<ListPattern>), Service(NamedNodeOrVariable, Box<GraphPattern>, bool),
Service(NamedNodeOrVariable, Box<MultiSetPattern>, bool),
AggregateJoin(GroupPattern, BTreeMap<Aggregation, Variable>), AggregateJoin(GroupPattern, BTreeMap<Aggregation, Variable>),
Data(StaticBindings),
OrderBy(Box<GraphPattern>, Vec<OrderComparator>),
Project(Box<GraphPattern>, Vec<Variable>),
Distinct(Box<GraphPattern>),
Reduced(Box<GraphPattern>),
Slice(Box<GraphPattern>, usize, Option<usize>),
} }
impl fmt::Display for MultiSetPattern { impl fmt::Display for GraphPattern {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
MultiSetPattern::BGP(p) => write!( GraphPattern::BGP(p) => write!(
f, f,
"BGP({})", "BGP({})",
p.iter() p.iter()
@ -922,16 +927,15 @@ impl fmt::Display for MultiSetPattern {
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(" . ") .join(" . ")
), ),
MultiSetPattern::Join(a, b) => write!(f, "Join({}, {})", a, b), GraphPattern::Join(a, b) => write!(f, "Join({}, {})", a, b),
MultiSetPattern::LeftJoin(a, b, e) => write!(f, "LeftJoin({}, {}, {})", a, b, e), GraphPattern::LeftJoin(a, b, e) => write!(f, "LeftJoin({}, {}, {})", a, b, e),
MultiSetPattern::Filter(e, p) => write!(f, "Filter({}, {})", e, p), GraphPattern::Filter(e, p) => write!(f, "Filter({}, {})", e, p),
MultiSetPattern::Union(a, b) => write!(f, "Union({}, {})", a, b), GraphPattern::Union(a, b) => write!(f, "Union({}, {})", a, b),
MultiSetPattern::Graph(g, p) => write!(f, "Graph({}, {})", g, p), GraphPattern::Graph(g, p) => write!(f, "Graph({}, {})", g, p),
MultiSetPattern::Extend(p, v, e) => write!(f, "Extend({}), {}, {})", p, v, e), GraphPattern::Extend(p, v, e) => write!(f, "Extend({}), {}, {})", p, v, e),
MultiSetPattern::Minus(a, b) => write!(f, "Minus({}, {})", a, b), GraphPattern::Minus(a, b) => write!(f, "Minus({}, {})", a, b),
MultiSetPattern::ToMultiSet(l) => write!(f, "{}", l), GraphPattern::Service(n, p, s) => write!(f, "Service({}, {}, {})", n, p, s),
MultiSetPattern::Service(n, p, s) => write!(f, "Service({}, {}, {})", n, p, s), GraphPattern::AggregateJoin(g, a) => write!(
MultiSetPattern::AggregateJoin(g, a) => write!(
f, f,
"AggregateJoin({}, {})", "AggregateJoin({}, {})",
g, g,
@ -940,29 +944,66 @@ impl fmt::Display for MultiSetPattern {
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(", ") .join(", ")
), ),
GraphPattern::Data(bs) => {
let variables = bs.variables();
write!(f, "{{ ")?;
for values in bs.values_iter() {
write!(f, "{{")?;
for i in 0..values.len() {
if let Some(ref val) = values[i] {
write!(f, " {} → {} ", variables[i], val)?;
}
}
write!(f, "}}")?;
}
write!(f, "}}")
}
GraphPattern::OrderBy(l, o) => write!(
f,
"OrderBy({}, ({}))",
l,
o.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", ")
),
GraphPattern::Project(l, pv) => write!(
f,
"Project({}, ({}))",
l,
pv.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(", ")
),
GraphPattern::Distinct(l) => write!(f, "Distinct({})", l),
GraphPattern::Reduced(l) => write!(f, "Reduce({})", l),
GraphPattern::Slice(l, start, length) => write!(
f,
"Slice({}, {}, {})",
l,
start,
length
.map(|l| l.to_string())
.unwrap_or_else(|| '?'.to_string())
),
} }
} }
} }
impl Default for MultiSetPattern { impl Default for GraphPattern {
fn default() -> Self { fn default() -> Self {
MultiSetPattern::BGP(Vec::default()) GraphPattern::BGP(Vec::default())
} }
} }
impl From<TripleOrPathPattern> for MultiSetPattern { impl From<TripleOrPathPattern> for GraphPattern {
fn from(p: TripleOrPathPattern) -> Self { fn from(p: TripleOrPathPattern) -> Self {
MultiSetPattern::BGP(vec![p]) GraphPattern::BGP(vec![p])
}
}
impl From<ListPattern> for MultiSetPattern {
fn from(pattern: ListPattern) -> Self {
MultiSetPattern::ToMultiSet(Box::new(pattern))
} }
} }
impl MultiSetPattern { impl GraphPattern {
pub fn visible_variables(&self) -> BTreeSet<&Variable> { pub fn visible_variables(&self) -> BTreeSet<&Variable> {
let mut vars = BTreeSet::default(); let mut vars = BTreeSet::default();
self.add_visible_variables(&mut vars); self.add_visible_variables(&mut vars);
@ -971,7 +1012,7 @@ impl MultiSetPattern {
fn add_visible_variables<'a>(&'a self, vars: &mut BTreeSet<&'a Variable>) { fn add_visible_variables<'a>(&'a self, vars: &mut BTreeSet<&'a Variable>) {
match self { match self {
MultiSetPattern::BGP(p) => { GraphPattern::BGP(p) => {
for pattern in p { for pattern in p {
match pattern { match pattern {
TripleOrPathPattern::Triple(tp) => { TripleOrPathPattern::Triple(tp) => {
@ -996,28 +1037,33 @@ impl MultiSetPattern {
} }
} }
} }
MultiSetPattern::Join(a, b) => { GraphPattern::Join(a, b) => {
a.add_visible_variables(vars); a.add_visible_variables(vars);
b.add_visible_variables(vars); b.add_visible_variables(vars);
} }
MultiSetPattern::LeftJoin(a, b, _) => { GraphPattern::LeftJoin(a, b, _) => {
a.add_visible_variables(vars); a.add_visible_variables(vars);
b.add_visible_variables(vars); b.add_visible_variables(vars);
} }
MultiSetPattern::Filter(_, p) => p.add_visible_variables(vars), GraphPattern::Filter(_, p) => p.add_visible_variables(vars),
MultiSetPattern::Union(a, b) => { GraphPattern::Union(a, b) => {
a.add_visible_variables(vars); a.add_visible_variables(vars);
b.add_visible_variables(vars); b.add_visible_variables(vars);
} }
MultiSetPattern::Graph(_, p) => p.add_visible_variables(vars), GraphPattern::Graph(_, p) => p.add_visible_variables(vars),
MultiSetPattern::Extend(p, v, _) => { GraphPattern::Extend(p, v, _) => {
p.add_visible_variables(vars); p.add_visible_variables(vars);
adds_if_has_name(vars, &v); adds_if_has_name(vars, &v);
} }
MultiSetPattern::Minus(a, _) => a.add_visible_variables(vars), GraphPattern::Minus(a, _) => a.add_visible_variables(vars),
MultiSetPattern::ToMultiSet(l) => l.add_visible_variables(vars), GraphPattern::Service(_, p, _) => p.add_visible_variables(vars),
MultiSetPattern::Service(_, p, _) => p.add_visible_variables(vars), GraphPattern::AggregateJoin(_, a) => vars.extend(a.iter().map(|(_, v)| v)),
MultiSetPattern::AggregateJoin(_, a) => vars.extend(a.iter().map(|(_, v)| v)), GraphPattern::Data(b) => vars.extend(b.variables_iter()),
GraphPattern::OrderBy(l, _) => l.add_visible_variables(vars),
GraphPattern::Project(_, pv) => vars.extend(pv.iter()),
GraphPattern::Distinct(l) => l.add_visible_variables(vars),
GraphPattern::Reduced(l) => l.add_visible_variables(vars),
GraphPattern::Slice(l, _, _) => l.add_visible_variables(vars),
} }
} }
} }
@ -1028,223 +1074,61 @@ fn adds_if_has_name<'a>(vars: &mut BTreeSet<&'a Variable>, var: &'a Variable) {
} }
} }
struct SparqlMultiSetPattern<'a>(&'a MultiSetPattern); struct SparqlGraphPattern<'a>(&'a GraphPattern);
impl<'a> fmt::Display for SparqlMultiSetPattern<'a> { impl<'a> fmt::Display for SparqlGraphPattern<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 { match self.0 {
MultiSetPattern::BGP(p) => { GraphPattern::BGP(p) => {
if p.is_empty() { for pattern in p {
Ok(()) write!(f, "{} .", SparqlTripleOrPathPattern(pattern))?
} else {
write!(
f,
"{}",
p.iter()
.map(|v| SparqlTripleOrPathPattern(v).to_string())
.collect::<Vec<String>>()
.join(" . ")
)
} }
Ok(())
} }
MultiSetPattern::Join(a, b) => write!( GraphPattern::Join(a, b) => {
f, write!(f, "{} {}", SparqlGraphPattern(&*a), SparqlGraphPattern(&*b))
"{} {}", }
SparqlMultiSetPattern(&*a), GraphPattern::LeftJoin(a, b, e) => write!(
SparqlMultiSetPattern(&*b)
),
MultiSetPattern::LeftJoin(a, b, e) => write!(
f, f,
"{} OPTIONAL {{ {} FILTER({}) }}", "{} OPTIONAL {{ {} FILTER({}) }}",
SparqlMultiSetPattern(&*a), SparqlGraphPattern(&*a),
SparqlMultiSetPattern(&*b), SparqlGraphPattern(&*b),
SparqlExpression(e) SparqlExpression(e)
), ),
MultiSetPattern::Filter(e, p) => write!( GraphPattern::Filter(e, p) => write!(
f, f,
"{} FILTER({})", "{} FILTER({})",
SparqlMultiSetPattern(&*p), SparqlGraphPattern(&*p),
SparqlExpression(e) SparqlExpression(e)
), ),
MultiSetPattern::Union(a, b) => write!( GraphPattern::Union(a, b) => write!(
f, f,
"{{ {} }} UNION {{ {} }}", "{{ {} }} UNION {{ {} }}",
SparqlMultiSetPattern(&*a), SparqlGraphPattern(&*a),
SparqlMultiSetPattern(&*b) SparqlGraphPattern(&*b),
), ),
MultiSetPattern::Graph(g, p) => { GraphPattern::Graph(g, p) => {
write!(f, "GRAPH {} {{ {} }}", g, SparqlMultiSetPattern(&*p)) write!(f, "GRAPH {} {{ {} }}", g, SparqlGraphPattern(&*p),)
} }
MultiSetPattern::Extend(p, v, e) => write!( GraphPattern::Extend(p, v, e) => write!(
f, f,
"{} BIND({} AS {})", "{} BIND({} AS {})",
SparqlMultiSetPattern(&*p), SparqlGraphPattern(&*p),
SparqlExpression(e), SparqlExpression(e),
v v
), ),
MultiSetPattern::Minus(a, b) => write!( GraphPattern::Minus(a, b) => write!(
f, f,
"{} MINUS {{ {} }}", "{} MINUS {{ {} }}",
SparqlMultiSetPattern(&*a), SparqlGraphPattern(&*a),
SparqlMultiSetPattern(&*b) SparqlGraphPattern(&*b)
),
MultiSetPattern::ToMultiSet(l) => write!(
f,
"{{ {} }}",
SparqlListPattern {
algebra: &l,
dataset: &EMPTY_DATASET
}
), ),
MultiSetPattern::Service(n, p, s) => if *s { GraphPattern::Service(n, p, s) => if *s {
write!( write!(f, "SERVICE SILENT {} {{ {} }}", n, SparqlGraphPattern(&*p))
f,
"SERVICE SILENT {} {{ {} }}",
n,
SparqlMultiSetPattern(&*p)
)
} else { } else {
write!(f, "SERVICE {} {{ {} }}", n, SparqlMultiSetPattern(&*p)) write!(f, "SERVICE {} {{ {} }}", n, SparqlGraphPattern(&*p))
}, },
MultiSetPattern::AggregateJoin(GroupPattern(group, p), agg) => write!( GraphPattern::Data(bs) => if bs.is_empty() {
f,
"{{ SELECT {} WHERE {{ {} }} GROUP BY {} }}",
agg.iter()
.map(|(a, v)| format!("({} AS {})", SparqlAggregation(&a), v))
.collect::<Vec<String>>()
.join(" "),
SparqlMultiSetPattern(p),
group
.iter()
.map(|e| format!("({})", e.to_string()))
.collect::<Vec<String>>()
.join(" ")
),
}
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct GroupPattern(pub Vec<Expression>, pub Box<MultiSetPattern>);
impl fmt::Display for GroupPattern {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Group(({}), {})",
self.0
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", "),
self.1
)
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub enum ListPattern {
Data(StaticBindings),
ToList(MultiSetPattern),
OrderBy(Box<ListPattern>, Vec<OrderComparator>),
Project(Box<ListPattern>, Vec<Variable>),
Distinct(Box<ListPattern>),
Reduced(Box<ListPattern>),
Slice(Box<ListPattern>, usize, Option<usize>),
}
impl fmt::Display for ListPattern {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ListPattern::Data(bs) => {
let variables = bs.variables();
write!(f, "{{ ")?;
for values in bs.values_iter() {
write!(f, "{{")?;
for i in 0..values.len() {
if let Some(ref val) = values[i] {
write!(f, " {} → {} ", variables[i], val)?;
}
}
write!(f, "}}")?;
}
write!(f, "}}")
}
ListPattern::ToList(l) => write!(f, "{}", l),
ListPattern::OrderBy(l, o) => write!(
f,
"OrderBy({}, ({}))",
l,
o.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", ")
),
ListPattern::Project(l, pv) => write!(
f,
"Project({}, ({}))",
l,
pv.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(", ")
),
ListPattern::Distinct(l) => write!(f, "Distinct({})", l),
ListPattern::Reduced(l) => write!(f, "Reduce({})", l),
ListPattern::Slice(l, start, length) => write!(
f,
"Slice({}, {}, {})",
l,
start,
length
.map(|l| l.to_string())
.unwrap_or_else(|| '?'.to_string())
),
}
}
}
impl Default for ListPattern {
fn default() -> Self {
ListPattern::Data(StaticBindings::default())
}
}
impl From<MultiSetPattern> for ListPattern {
fn from(pattern: MultiSetPattern) -> Self {
ListPattern::ToList(pattern)
}
}
impl ListPattern {
pub fn visible_variables<'a>(&'a self) -> BTreeSet<&'a Variable> {
let mut vars = BTreeSet::default();
self.add_visible_variables(&mut vars);
vars
}
fn add_visible_variables<'a>(&'a self, vars: &mut BTreeSet<&'a Variable>) {
match self {
ListPattern::Data(b) => vars.extend(b.variables_iter()),
ListPattern::ToList(p) => p.add_visible_variables(vars),
ListPattern::OrderBy(l, _) => l.add_visible_variables(vars),
ListPattern::Project(_, pv) => vars.extend(pv.iter()),
ListPattern::Distinct(l) => l.add_visible_variables(vars),
ListPattern::Reduced(l) => l.add_visible_variables(vars),
ListPattern::Slice(l, _, _) => l.add_visible_variables(vars),
}
}
}
struct SparqlListPattern<'a> {
algebra: &'a ListPattern,
dataset: &'a DatasetSpec,
}
impl<'a> fmt::Display for SparqlListPattern<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.algebra {
ListPattern::Data(bs) => if bs.is_empty() {
Ok(()) Ok(())
} else { } else {
write!(f, "VALUES ( ")?; write!(f, "VALUES ( ")?;
@ -1264,96 +1148,127 @@ impl<'a> fmt::Display for SparqlListPattern<'a> {
} }
write!(f, " }}") write!(f, " }}")
}, },
ListPattern::ToList(l) => write!(f, "{{ {} }}", SparqlMultiSetPattern(&*l)), GraphPattern::AggregateJoin(GroupPattern(group, p), agg) => write!(
ListPattern::OrderBy(l, o) => write!(
f, f,
"{} ORDER BY {}", "{{ SELECT {} WHERE {{ {} }} GROUP BY {} }}",
SparqlListPattern { agg.iter()
algebra: &*l, .map(|(a, v)| format!("({} AS {})", SparqlAggregation(&a), v))
dataset: self.dataset .collect::<Vec<String>>()
}, .join(" "),
o.iter() SparqlGraphPattern(&*p),
.map(|c| SparqlOrderComparator(c).to_string()) group
.iter()
.map(|e| format!("({})", e.to_string()))
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(" ") .join(" ")
), ),
ListPattern::Project(l, pv) => write!( p => write!(
f, f,
"SELECT {} {} WHERE {}", "{{ {} }}",
build_sparql_select_arguments(pv), SparqlGraphRootPattern {
self.dataset, algebra: p,
SparqlListPattern {
algebra: &*l,
dataset: &EMPTY_DATASET dataset: &EMPTY_DATASET
} }
), ),
ListPattern::Distinct(l) => match l.as_ref() { }
ListPattern::Project(l, pv) => write!( }
f, }
"SELECT DISTINCT {} {} WHERE {}",
build_sparql_select_arguments(pv), struct SparqlGraphRootPattern<'a> {
self.dataset, algebra: &'a GraphPattern,
SparqlListPattern { dataset: &'a DatasetSpec,
algebra: &*l, }
dataset: &EMPTY_DATASET
} impl<'a> fmt::Display for SparqlGraphRootPattern<'a> {
), fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
l => write!( let mut distinct = false;
f, let mut reduced = false;
"DISTINCT {}", let mut order = None;
SparqlListPattern { let mut start = 0;
algebra: &l, let mut length = None;
dataset: self.dataset let mut project: &[Variable] = &[];
}
), let mut child = self.algebra;
}, loop {
ListPattern::Reduced(l) => match l.as_ref() { match child {
ListPattern::Project(l, pv) => write!( GraphPattern::OrderBy(l, o) => {
f, order = Some(o);
"SELECT REDUCED {} {} WHERE {}", child = &*l;
build_sparql_select_arguments(pv), }
self.dataset, GraphPattern::Project(l, pv) if project.is_empty() => {
SparqlListPattern { project = pv;
algebra: &*l, child = &*l;
dataset: &EMPTY_DATASET }
GraphPattern::Distinct(l) => {
distinct = true;
child = &*l;
}
GraphPattern::Reduced(l) => {
reduced = true;
child = &*l;
}
GraphPattern::Slice(l, s, len) => {
start = *s;
length = *len;
child = l;
}
p => {
write!(f, "SELECT ")?;
if distinct {
write!(f, "DISTINCT ")?;
} }
), if reduced {
l => write!( write!(f, "REDUCED ")?;
f,
"REDUCED {}",
SparqlListPattern {
algebra: &l,
dataset: self.dataset
} }
),
},
ListPattern::Slice(l, start, length) => length
.map(|length| {
write!(
f,
"{} LIMIT {} OFFSET {}",
SparqlListPattern {
algebra: &*l,
dataset: self.dataset
},
start,
length
)
}).unwrap_or_else(|| {
write!( write!(
f, f,
"{} LIMIT {}", "{} {} WHERE {{ {} }}",
SparqlListPattern { build_sparql_select_arguments(project),
algebra: &*l, self.dataset,
dataset: self.dataset SparqlGraphPattern(p)
}, )?;
start if let Some(order) = order {
) write!(
}), f,
" ORDER BY {}",
order
.iter()
.map(|c| SparqlOrderComparator(c).to_string())
.collect::<Vec<String>>()
.join(" ")
)?;
}
if start > 0 {
write!(f, " OFFSET {}", start)?;
}
if let Some(length) = length {
write!(f, " LIMIT {}", length)?;
}
return Ok(());
}
}
} }
} }
} }
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct GroupPattern(pub Vec<Expression>, pub Box<GraphPattern>);
impl fmt::Display for GroupPattern {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Group(({}), {})",
self.0
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", "),
self.1
)
}
}
fn build_sparql_select_arguments(args: &[Variable]) -> String { fn build_sparql_select_arguments(args: &[Variable]) -> String {
if args.is_empty() { if args.is_empty() {
"*".to_owned() "*".to_owned()
@ -1589,20 +1504,20 @@ lazy_static! {
pub enum Query { pub enum Query {
SelectQuery { SelectQuery {
dataset: DatasetSpec, dataset: DatasetSpec,
algebra: ListPattern, algebra: GraphPattern,
}, },
ConstructQuery { ConstructQuery {
construct: Vec<TriplePattern>, construct: Vec<TriplePattern>,
dataset: DatasetSpec, dataset: DatasetSpec,
algebra: ListPattern, algebra: GraphPattern,
}, },
DescribeQuery { DescribeQuery {
dataset: DatasetSpec, dataset: DatasetSpec,
algebra: ListPattern, algebra: GraphPattern,
}, },
AskQuery { AskQuery {
dataset: DatasetSpec, dataset: DatasetSpec,
algebra: ListPattern, algebra: GraphPattern,
}, },
} }
@ -1612,7 +1527,7 @@ impl fmt::Display for Query {
Query::SelectQuery { dataset, algebra } => write!( Query::SelectQuery { dataset, algebra } => write!(
f, f,
"{}", "{}",
SparqlListPattern { SparqlGraphRootPattern {
algebra: &algebra, algebra: &algebra,
dataset: &dataset dataset: &dataset
} }
@ -1630,7 +1545,7 @@ impl fmt::Display for Query {
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(" . "), .join(" . "),
dataset, dataset,
SparqlListPattern { SparqlGraphRootPattern {
algebra: &algebra, algebra: &algebra,
dataset: &EMPTY_DATASET dataset: &EMPTY_DATASET
} }
@ -1639,7 +1554,7 @@ impl fmt::Display for Query {
f, f,
"DESCRIBE * {} WHERE {{ {} }}", "DESCRIBE * {} WHERE {{ {} }}",
dataset, dataset,
SparqlListPattern { SparqlGraphRootPattern {
algebra: &algebra, algebra: &algebra,
dataset: &EMPTY_DATASET dataset: &EMPTY_DATASET
} }
@ -1648,7 +1563,7 @@ impl fmt::Display for Query {
f, f,
"ASK {} WHERE {{ {} }}", "ASK {} WHERE {{ {} }}",
dataset, dataset,
SparqlListPattern { SparqlGraphRootPattern {
algebra: &algebra, algebra: &algebra,
dataset: &EMPTY_DATASET dataset: &EMPTY_DATASET
} }

@ -163,47 +163,20 @@ impl<S: EncodedQuadsStore> SparqlEvaluator<S> {
match query { match query {
Query::SelectQuery { algebra, dataset } => { Query::SelectQuery { algebra, dataset } => {
Ok(QueryResult::Bindings(self.decode_bindings( Ok(QueryResult::Bindings(self.decode_bindings(
self.eval_list_pattern(algebra, EncodedBindingsIterator::default())?, self.eval_graph_pattern(algebra, EncodedBindingsIterator::default())?,
))) )))
} }
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
fn eval_list_pattern( fn eval_graph_pattern(
&self, &self,
pattern: &ListPattern, pattern: &GraphPattern,
from: EncodedBindingsIterator, from: EncodedBindingsIterator,
) -> Result<EncodedBindingsIterator> { ) -> Result<EncodedBindingsIterator> {
match pattern { match pattern {
ListPattern::Data(bs) => Ok(self.encode_bindings(bs)), GraphPattern::BGP(p) => {
ListPattern::ToList(l) => self.eval_multi_set_pattern(l, from),
ListPattern::OrderBy(l, o) => self.eval_list_pattern(l, from), //TODO
ListPattern::Project(l, new_variables) => Ok(self
.eval_list_pattern(l, from)?
.project(new_variables.to_vec())),
ListPattern::Distinct(l) => Ok(self.eval_list_pattern(l, from)?.unique()),
ListPattern::Reduced(l) => self.eval_list_pattern(l, from),
ListPattern::Slice(l, start, length) => {
let mut iter = self.eval_list_pattern(l, from)?;
if *start > 0 {
iter = iter.skip(*start);
}
if let Some(length) = length {
iter = iter.take(*length);
}
Ok(iter)
}
}
}
fn eval_multi_set_pattern(
&self,
pattern: &MultiSetPattern,
from: EncodedBindingsIterator,
) -> Result<EncodedBindingsIterator> {
match pattern {
MultiSetPattern::BGP(p) => {
let mut iter = from; let mut iter = from;
for pattern in p { for pattern in p {
iter = match pattern { iter = match pattern {
@ -215,13 +188,13 @@ impl<S: EncodedQuadsStore> SparqlEvaluator<S> {
} }
Ok(iter) Ok(iter)
} }
MultiSetPattern::Join(a, b) => { GraphPattern::Join(a, b) => {
self.eval_multi_set_pattern(b, self.eval_multi_set_pattern(a, from)?) self.eval_graph_pattern(b, self.eval_graph_pattern(a, from)?)
} }
MultiSetPattern::LeftJoin(a, b, e) => unimplemented!(), GraphPattern::LeftJoin(a, b, e) => unimplemented!(),
MultiSetPattern::Filter(e, p) => { GraphPattern::Filter(e, p) => {
let EncodedBindingsIterator { variables, iter } = let EncodedBindingsIterator { variables, iter } =
self.eval_multi_set_pattern(p, from)?; self.eval_graph_pattern(p, from)?;
let expression = e.clone(); let expression = e.clone();
let evaluator = Self { let evaluator = Self {
store: self.store.clone(), store: self.store.clone(),
@ -239,18 +212,34 @@ impl<S: EncodedQuadsStore> SparqlEvaluator<S> {
})), })),
}) })
} }
MultiSetPattern::Union(a, b) => { GraphPattern::Union(a, b) => {
let (from1, from2) = from.duplicate(); let (from1, from2) = from.duplicate();
Ok(self Ok(self
.eval_multi_set_pattern(a, from1)? .eval_graph_pattern(a, from1)?
.chain(self.eval_multi_set_pattern(b, from2)?)) .chain(self.eval_graph_pattern(b, from2)?))
}
GraphPattern::Graph(g, p) => unimplemented!(),
GraphPattern::Extend(p, v, e) => unimplemented!(),
GraphPattern::Minus(a, b) => unimplemented!(),
GraphPattern::Service(n, p, s) => unimplemented!(),
GraphPattern::AggregateJoin(g, a) => unimplemented!(),
GraphPattern::Data(bs) => Ok(self.encode_bindings(bs)),
GraphPattern::OrderBy(l, o) => self.eval_graph_pattern(l, from), //TODO
GraphPattern::Project(l, new_variables) => Ok(self
.eval_graph_pattern(l, from)?
.project(new_variables.to_vec())),
GraphPattern::Distinct(l) => Ok(self.eval_graph_pattern(l, from)?.unique()),
GraphPattern::Reduced(l) => self.eval_graph_pattern(l, from),
GraphPattern::Slice(l, start, length) => {
let mut iter = self.eval_graph_pattern(l, from)?;
if *start > 0 {
iter = iter.skip(*start);
}
if let Some(length) = length {
iter = iter.take(*length);
}
Ok(iter)
} }
MultiSetPattern::Graph(g, p) => unimplemented!(),
MultiSetPattern::Extend(p, v, e) => unimplemented!(),
MultiSetPattern::Minus(a, b) => unimplemented!(),
MultiSetPattern::ToMultiSet(l) => self.eval_list_pattern(l, from),
MultiSetPattern::Service(n, p, s) => unimplemented!(),
MultiSetPattern::AggregateJoin(g, a) => unimplemented!(),
} }
} }

@ -140,21 +140,21 @@ mod grammar {
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
enum PartialGraphPattern { enum PartialGraphPattern {
Optional(MultiSetPattern), Optional(GraphPattern),
Minus(MultiSetPattern), Minus(GraphPattern),
Bind(Expression, Variable), Bind(Expression, Variable),
Filter(Expression), Filter(Expression),
Other(MultiSetPattern), Other(GraphPattern),
} }
fn new_join(l: MultiSetPattern, r: MultiSetPattern) -> MultiSetPattern { fn new_join(l: GraphPattern, r: GraphPattern) -> GraphPattern {
//Avoid to output empty BGPs //Avoid to output empty BGPs
if let MultiSetPattern::BGP(pl) = &l { if let GraphPattern::BGP(pl) = &l {
if pl.is_empty() { if pl.is_empty() {
return r; return r;
} }
} }
if let MultiSetPattern::BGP(pr) = &r { if let GraphPattern::BGP(pr) = &r {
if pr.is_empty() { if pr.is_empty() {
return l; return l;
} }
@ -162,11 +162,11 @@ mod grammar {
//Merge BGPs //Merge BGPs
match (l, r) { match (l, r) {
(MultiSetPattern::BGP(mut pl), MultiSetPattern::BGP(pr)) => { (GraphPattern::BGP(mut pl), GraphPattern::BGP(pr)) => {
pl.extend_from_slice(&pr); pl.extend_from_slice(&pr);
MultiSetPattern::BGP(pl) GraphPattern::BGP(pl)
} }
(l, r) => MultiSetPattern::Join(Box::new(l), Box::new(r)), (l, r) => GraphPattern::Join(Box::new(l), Box::new(r)),
} }
} }
@ -207,28 +207,28 @@ mod grammar {
fn build_select( fn build_select(
select: Selection, select: Selection,
wher: MultiSetPattern, wher: GraphPattern,
group: Option<(Vec<Expression>, Vec<(Expression, Variable)>)>, group: Option<(Vec<Expression>, Vec<(Expression, Variable)>)>,
having: Option<Expression>, having: Option<Expression>,
order_by: Option<Vec<OrderComparator>>, order_by: Option<Vec<OrderComparator>>,
offset_limit: Option<(usize, Option<usize>)>, offset_limit: Option<(usize, Option<usize>)>,
values: Option<MultiSetPattern>, values: Option<GraphPattern>,
state: &mut ParserState, state: &mut ParserState,
) -> ListPattern { ) -> GraphPattern {
let mut p = wher; let mut p = wher;
//GROUP BY //GROUP BY
if let Some((clauses, binds)) = group { if let Some((clauses, binds)) = group {
for (e, v) in binds { for (e, v) in binds {
p = MultiSetPattern::Extend(Box::new(p), v, e); p = GraphPattern::Extend(Box::new(p), v, e);
} }
let g = GroupPattern(clauses, Box::new(p)); let g = GroupPattern(clauses, Box::new(p));
p = MultiSetPattern::AggregateJoin(g, state.aggregations.clone()); p = GraphPattern::AggregateJoin(g, state.aggregations.clone());
state.aggregations = BTreeMap::default(); state.aggregations = BTreeMap::default();
} }
if !state.aggregations.is_empty() { if !state.aggregations.is_empty() {
let g = GroupPattern(vec![Literal::from(1).into()], Box::new(p)); let g = GroupPattern(vec![Literal::from(1).into()], Box::new(p));
p = MultiSetPattern::AggregateJoin(g, state.aggregations.clone()); p = GraphPattern::AggregateJoin(g, state.aggregations.clone());
state.aggregations = BTreeMap::default(); state.aggregations = BTreeMap::default();
} }
@ -236,12 +236,12 @@ mod grammar {
//HAVING //HAVING
if let Some(ex) = having { if let Some(ex) = having {
p = MultiSetPattern::Filter(ex, Box::new(p)); p = GraphPattern::Filter(ex, Box::new(p));
} }
//VALUES //VALUES
if let Some(data) = values { if let Some(data) = values {
p = MultiSetPattern::Join(Box::new(p), Box::new(data)); p = new_join(p, data);
} }
//SELECT //SELECT
@ -254,7 +254,7 @@ mod grammar {
SelectionMember::Expression(e, v) => if pv.contains(&v) { SelectionMember::Expression(e, v) => if pv.contains(&v) {
//TODO: fail //TODO: fail
} else { } else {
p = MultiSetPattern::Extend(Box::new(p), v.clone(), e); p = GraphPattern::Extend(Box::new(p), v.clone(), e);
pv.push(v); pv.push(v);
}, },
} }
@ -264,24 +264,24 @@ mod grammar {
pv.extend(p.visible_variables().into_iter().cloned()) //TODO: is it really useful to do a projection? pv.extend(p.visible_variables().into_iter().cloned()) //TODO: is it really useful to do a projection?
} }
} }
let mut m = ListPattern::from(p); let mut m = GraphPattern::from(p);
//ORDER BY //ORDER BY
if let Some(order) = order_by { if let Some(order) = order_by {
m = ListPattern::OrderBy(Box::new(m), order); m = GraphPattern::OrderBy(Box::new(m), order);
} }
//PROJECT //PROJECT
m = ListPattern::Project(Box::new(m), pv); m = GraphPattern::Project(Box::new(m), pv);
match select.option { match select.option {
SelectionOption::Distinct => m = ListPattern::Distinct(Box::new(m)), SelectionOption::Distinct => m = GraphPattern::Distinct(Box::new(m)),
SelectionOption::Reduced => m = ListPattern::Reduced(Box::new(m)), SelectionOption::Reduced => m = GraphPattern::Reduced(Box::new(m)),
SelectionOption::Default => (), SelectionOption::Default => (),
} }
//OFFSET LIMIT //OFFSET LIMIT
if let Some((offset, limit)) = offset_limit { if let Some((offset, limit)) = offset_limit {
m = ListPattern::Slice(Box::new(m), offset, limit) m = GraphPattern::Slice(Box::new(m), offset, limit)
} }
m m
} }

@ -45,7 +45,7 @@ SelectQuery -> Query = s:SelectClause _ d:DatasetClauses _ w:WhereClause _ g:Gro
} }
//[8] //[8]
SubSelect -> MultiSetPattern = s:SelectClause _ w:WhereClause _ g:GroupClause? _ h:HavingClause? _ o:OrderClause? _ l:LimitOffsetClauses? _ v:ValuesClause { //TODO: Modifiers SubSelect -> GraphPattern = s:SelectClause _ w:WhereClause _ g:GroupClause? _ h:HavingClause? _ o:OrderClause? _ l:LimitOffsetClauses? _ v:ValuesClause { //TODO: Modifiers
build_select(s, w, g, h, o, l, v, state).into() build_select(s, w, g, h, o, l, v, state).into()
} }
@ -82,7 +82,7 @@ ConstructQuery -> Query =
dataset: d, dataset: d,
algebra: build_select( algebra: build_select(
Selection::default(), Selection::default(),
MultiSetPattern::BGP(c.into_iter().map(TripleOrPathPattern::from).collect()), GraphPattern::BGP(c.into_iter().map(TripleOrPathPattern::from).collect()),
g, h, o, l, v, state g, h, o, l, v, state
) )
} }
@ -95,7 +95,7 @@ DescribeQuery -> Query =
"DESCRIBE"i _ '*' _ d:DatasetClauses w:WhereClause? _ g:GroupClause? _ h:HavingClause? _ o:OrderClause? _ l:LimitOffsetClauses? _ v:ValuesClause { "DESCRIBE"i _ '*' _ d:DatasetClauses w:WhereClause? _ g:GroupClause? _ h:HavingClause? _ o:OrderClause? _ l:LimitOffsetClauses? _ v:ValuesClause {
Query::DescribeQuery { Query::DescribeQuery {
dataset: d, dataset: d,
algebra: build_select(Selection::default(), w.unwrap_or_else(MultiSetPattern::default), g, h, o, l, v, state) algebra: build_select(Selection::default(), w.unwrap_or_else(GraphPattern::default), g, h, o, l, v, state)
} }
} / } /
"DESCRIBE"i _ p:DescribeQuery_item+ _ d:DatasetClauses w:WhereClause? _ g:GroupClause? _ h:HavingClause? _ o:OrderClause? _ l:LimitOffsetClauses? _ v:ValuesClause { "DESCRIBE"i _ p:DescribeQuery_item+ _ d:DatasetClauses w:WhereClause? _ g:GroupClause? _ h:HavingClause? _ o:OrderClause? _ l:LimitOffsetClauses? _ v:ValuesClause {
@ -107,7 +107,7 @@ DescribeQuery -> Query =
NamedNodeOrVariable::NamedNode(n) => SelectionMember::Expression(n.into(), Variable::default()), NamedNodeOrVariable::NamedNode(n) => SelectionMember::Expression(n.into(), Variable::default()),
NamedNodeOrVariable::Variable(v) => SelectionMember::Variable(v) NamedNodeOrVariable::Variable(v) => SelectionMember::Variable(v)
}).collect()) }).collect())
}, w.unwrap_or_else(MultiSetPattern::default), g, h, o, l, v, state) }, w.unwrap_or_else(GraphPattern::default), g, h, o, l, v, state)
} }
} }
DescribeQuery_item -> NamedNodeOrVariable = i:VarOrIri _ { i } DescribeQuery_item -> NamedNodeOrVariable = i:VarOrIri _ { i }
@ -140,7 +140,7 @@ NamedGraphClause -> DatasetSpec = "NAMED"i _ s:SourceSelector {
SourceSelector -> NamedNode = iri SourceSelector -> NamedNode = iri
//[17] //[17]
WhereClause -> MultiSetPattern = "WHERE"i? _ p:GroupGraphPattern { WhereClause -> GraphPattern = "WHERE"i? _ p:GroupGraphPattern {
p p
} }
@ -203,7 +203,7 @@ OffsetClause -> usize = "OFFSET"i _ o:$(INTEGER) {?
} }
//[28] //[28]
ValuesClause -> Option<MultiSetPattern> = ValuesClause -> Option<GraphPattern> =
"VALUES"i _ p:DataBlock { Some(p) } / "VALUES"i _ p:DataBlock { Some(p) } /
{ None } { None }
@ -220,33 +220,33 @@ TriplesTemplate_tail -> Vec<TriplePattern> = '.' _ t:TriplesTemplate? _ {
} }
//[53] //[53]
GroupGraphPattern -> MultiSetPattern = GroupGraphPattern -> GraphPattern =
'{' _ p:GroupGraphPatternSub _ '}' { p } / '{' _ p:GroupGraphPatternSub _ '}' { p } /
'{' _ p:SubSelect _ '}' { p } '{' _ p:SubSelect _ '}' { p }
//[54] //[54]
GroupGraphPatternSub -> MultiSetPattern = a:TriplesBlock? _ b:GroupGraphPatternSub_item* { GroupGraphPatternSub -> GraphPattern = a:TriplesBlock? _ b:GroupGraphPatternSub_item* {
let mut p = a.map(|v| vec![PartialGraphPattern::Other(MultiSetPattern::BGP(v))]).unwrap_or_else(|| vec![]); let mut p = a.map(|v| vec![PartialGraphPattern::Other(GraphPattern::BGP(v))]).unwrap_or_else(|| vec![]);
for v in b { for v in b {
p.extend_from_slice(&v) p.extend_from_slice(&v)
} }
let mut filter: Option<Expression> = None; let mut filter: Option<Expression> = None;
let mut g = MultiSetPattern::default(); let mut g = GraphPattern::default();
for e in p { for e in p {
match e { match e {
PartialGraphPattern::Optional(p) => match p { PartialGraphPattern::Optional(p) => match p {
MultiSetPattern::Filter(f, a2) => { GraphPattern::Filter(f, a2) => {
g = MultiSetPattern::LeftJoin(Box::new(g), a2, f) g = GraphPattern::LeftJoin(Box::new(g), a2, f)
} }
a => { a => {
g = MultiSetPattern::LeftJoin(Box::new(g), Box::new(a), Literal::from(true).into()) g = GraphPattern::LeftJoin(Box::new(g), Box::new(a), Literal::from(true).into())
} }
} }
PartialGraphPattern::Minus(p) => { PartialGraphPattern::Minus(p) => {
g = MultiSetPattern::Minus(Box::new(g), Box::new(p)) g = GraphPattern::Minus(Box::new(g), Box::new(p))
} }
PartialGraphPattern::Bind(expr, var) => { PartialGraphPattern::Bind(expr, var) => {
g = MultiSetPattern::Extend(Box::new(g), var, expr) g = GraphPattern::Extend(Box::new(g), var, expr)
} }
PartialGraphPattern::Filter(expr) => match filter { PartialGraphPattern::Filter(expr) => match filter {
Some(f) => { filter = Some(Expression::AndExpression(Box::new(f), Box::new(expr))) }, Some(f) => { filter = Some(Expression::AndExpression(Box::new(f), Box::new(expr))) },
@ -256,14 +256,14 @@ GroupGraphPatternSub -> MultiSetPattern = a:TriplesBlock? _ b:GroupGraphPatternS
} }
} }
match filter { match filter {
Some(filter) => MultiSetPattern::Filter(filter, Box::new(g)), Some(filter) => GraphPattern::Filter(filter, Box::new(g)),
None => g None => g
} }
} }
GroupGraphPatternSub_item -> Vec<PartialGraphPattern> = a:GraphPatternNotTriples _ ('.' _)? b:TriplesBlock? _ { GroupGraphPatternSub_item -> Vec<PartialGraphPattern> = a:GraphPatternNotTriples _ ('.' _)? b:TriplesBlock? _ {
let mut result = vec![a]; let mut result = vec![a];
if let Some(v) = b { if let Some(v) = b {
result.push(PartialGraphPattern::Other(MultiSetPattern::BGP(v))); result.push(PartialGraphPattern::Other(GraphPattern::BGP(v)));
} }
result result
} }
@ -290,13 +290,13 @@ OptionalGraphPattern -> PartialGraphPattern = "OPTIONAL"i _ p:GroupGraphPattern
//[58] //[58]
GraphGraphPattern -> PartialGraphPattern = "GRAPH"i _ g:VarOrIri _ p:GroupGraphPattern { GraphGraphPattern -> PartialGraphPattern = "GRAPH"i _ g:VarOrIri _ p:GroupGraphPattern {
PartialGraphPattern::Other(MultiSetPattern::Graph(g, Box::new(p))) PartialGraphPattern::Other(GraphPattern::Graph(g, Box::new(p)))
} }
//[59] //[59]
ServiceGraphPattern -> PartialGraphPattern = ServiceGraphPattern -> PartialGraphPattern =
"SERVICE"i _ "SILENT"i _ s:VarOrIri _ p:GroupGraphPattern { PartialGraphPattern::Other(MultiSetPattern::Service(s, Box::new(p), true)) } / "SERVICE"i _ "SILENT"i _ s:VarOrIri _ p:GroupGraphPattern { PartialGraphPattern::Other(GraphPattern::Service(s, Box::new(p), true)) } /
"SERVICE"i _ s:VarOrIri _ p:GroupGraphPattern { PartialGraphPattern::Other(MultiSetPattern::Service(s, Box::new(p), false)) } "SERVICE"i _ s:VarOrIri _ p:GroupGraphPattern { PartialGraphPattern::Other(GraphPattern::Service(s, Box::new(p), false)) }
//[60] //[60]
Bind -> PartialGraphPattern = "BIND"i _ '(' _ e:Expression _ "AS"i _ v:Var _ ')' { Bind -> PartialGraphPattern = "BIND"i _ '(' _ e:Expression _ "AS"i _ v:Var _ ')' {
@ -307,8 +307,8 @@ Bind -> PartialGraphPattern = "BIND"i _ '(' _ e:Expression _ "AS"i _ v:Var _ ')'
InlineData -> PartialGraphPattern = "VALUES"i _ p:DataBlock { PartialGraphPattern::Other(p) } InlineData -> PartialGraphPattern = "VALUES"i _ p:DataBlock { PartialGraphPattern::Other(p) }
//[62] //[62]
DataBlock -> MultiSetPattern = l:(InlineDataOneVar / InlineDataFull) { DataBlock -> GraphPattern = l:(InlineDataOneVar / InlineDataFull) {
ListPattern::Data(l).into() GraphPattern::Data(l).into()
} }
//[63] //[63]
@ -341,10 +341,10 @@ MinusGraphPattern -> PartialGraphPattern = "MINUS"i _ p: GroupGraphPattern {
//[67] //[67]
GroupOrUnionGraphPattern -> PartialGraphPattern = p:GroupOrUnionGraphPattern_item **<1,> ("UNION"i _) {? GroupOrUnionGraphPattern -> PartialGraphPattern = p:GroupOrUnionGraphPattern_item **<1,> ("UNION"i _) {?
not_empty_fold(p.into_iter(), |a, b| { not_empty_fold(p.into_iter(), |a, b| {
MultiSetPattern::Union(Box::new(a), Box::new(b)) GraphPattern::Union(Box::new(a), Box::new(b))
}).map(PartialGraphPattern::Other) }).map(PartialGraphPattern::Other)
} }
GroupOrUnionGraphPattern_item -> MultiSetPattern = p:GroupGraphPattern _ { p } GroupOrUnionGraphPattern_item -> GraphPattern = p:GroupGraphPattern _ { p }
//[68] //[68]
Filter -> PartialGraphPattern = "FILTER"i _ c:Constraint { Filter -> PartialGraphPattern = "FILTER"i _ c:Constraint {

Loading…
Cancel
Save