Adds SPARQL* support to the SPARQL parser

Nested triple patterns query evaluation is not implemented yet

Annotation syntax is not implemented yet
pull/171/head
Tpt 3 years ago
parent ed4b5fe280
commit 10ee6e17f0
  1. 16
      lib/src/model/triple.rs
  2. 21
      lib/src/sparql/plan_builder.rs
  3. 140
      lib/src/sparql/update.rs
  4. 2
      spargebra/README.md
  5. 56
      spargebra/src/algebra.rs
  6. 3
      spargebra/src/lib.rs
  7. 128
      spargebra/src/parser.rs
  8. 4
      spargebra/src/query.rs
  9. 618
      spargebra/src/term.rs
  10. 4
      spargebra/src/update.rs
  11. 25
      testsuite/tests/sparql.rs

@ -129,11 +129,7 @@ impl fmt::Display for SubjectRef<'_> {
match self { match self {
Self::NamedNode(node) => node.fmt(f), Self::NamedNode(node) => node.fmt(f),
Self::BlankNode(node) => node.fmt(f), Self::BlankNode(node) => node.fmt(f),
Self::Triple(triple) => write!( Self::Triple(triple) => write!(f, "<<{}>>", triple),
f,
"<<{} {} {}>>",
triple.subject, triple.predicate, triple.object
),
} }
} }
} }
@ -371,11 +367,7 @@ impl fmt::Display for TermRef<'_> {
Self::BlankNode(node) => node.fmt(f), Self::BlankNode(node) => node.fmt(f),
Self::Literal(literal) => literal.fmt(f), Self::Literal(literal) => literal.fmt(f),
Self::Triple(triple) => { Self::Triple(triple) => {
write!( write!(f, "<<{}>>", triple)
f,
"<<{} {} {}>>",
triple.subject, triple.predicate, triple.object
)
} }
} }
} }
@ -583,7 +575,7 @@ impl<'a> TripleRef<'a> {
impl fmt::Display for TripleRef<'_> { impl fmt::Display for TripleRef<'_> {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
rio::Triple::from(*self).fmt(f) write!(f, "{} {} {}", self.subject, self.predicate, self.object)
} }
} }
@ -890,7 +882,7 @@ impl<'a> QuadRef<'a> {
impl fmt::Display for QuadRef<'_> { impl fmt::Display for QuadRef<'_> {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
rio::Quad::from(*self).fmt(f) write!(f, "{} {} {}", self.subject, self.predicate, self.object)
} }
} }

@ -3,7 +3,7 @@ use crate::sparql::dataset::DatasetView;
use crate::sparql::error::EvaluationError; use crate::sparql::error::EvaluationError;
use crate::sparql::model::Variable as OxVariable; use crate::sparql::model::Variable as OxVariable;
use crate::sparql::plan::*; use crate::sparql::plan::*;
use crate::storage::numeric_encoder::{EncodedTerm, WriteEncoder}; use crate::storage::numeric_encoder::{EncodedTerm, EncodedTriple, WriteEncoder};
use rand::random; use rand::random;
use spargebra::algebra::*; use spargebra::algebra::*;
use spargebra::term::*; use spargebra::term::*;
@ -807,6 +807,7 @@ impl<'a> PlanBuilder<'a> {
//TODO: very bad hack to convert bnode to variable //TODO: very bad hack to convert bnode to variable
} }
TermPattern::Literal(literal) => PatternValue::Constant(self.build_literal(literal)?), TermPattern::Literal(literal) => PatternValue::Constant(self.build_literal(literal)?),
TermPattern::Triple(_) => unimplemented!(),
}) })
} }
@ -845,6 +846,7 @@ impl<'a> PlanBuilder<'a> {
match term { match term {
GroundTerm::NamedNode(node) => self.build_named_node(node), GroundTerm::NamedNode(node) => self.build_named_node(node),
GroundTerm::Literal(literal) => self.build_literal(literal), GroundTerm::Literal(literal) => self.build_literal(literal),
GroundTerm::Triple(triple) => self.build_triple(triple),
}?, }?,
); );
} }
@ -957,6 +959,7 @@ impl<'a> PlanBuilder<'a> {
TermPattern::Literal(literal) => { TermPattern::Literal(literal) => {
TripleTemplateValue::Constant(self.build_literal(literal)?) TripleTemplateValue::Constant(self.build_literal(literal)?)
} }
TermPattern::Triple(_) => unimplemented!(),
}) })
} }
@ -1091,6 +1094,22 @@ impl<'a> PlanBuilder<'a> {
), ),
}) })
} }
fn build_triple(&mut self, triple: &GroundTriple) -> Result<EncodedTerm, EvaluationError> {
Ok(EncodedTriple::new(
match &triple.subject {
GroundSubject::NamedNode(node) => self.build_named_node(node)?,
GroundSubject::Triple(triple) => self.build_triple(triple)?,
},
self.build_named_node(&triple.predicate)?,
match &triple.object {
GroundTerm::NamedNode(node) => self.build_named_node(node)?,
GroundTerm::Literal(literal) => self.build_literal(literal)?,
GroundTerm::Triple(triple) => self.build_triple(triple)?,
},
)
.into())
}
} }
fn variable_key(variables: &mut Vec<Variable>, variable: &Variable) -> usize { fn variable_key(variables: &mut Vec<Variable>, variable: &Variable) -> usize {

@ -10,7 +10,8 @@ use crate::sparql::plan_builder::PlanBuilder;
use crate::sparql::{EvaluationError, UpdateOptions}; use crate::sparql::{EvaluationError, UpdateOptions};
use crate::storage::io::load_graph; use crate::storage::io::load_graph;
use crate::storage::numeric_encoder::{ use crate::storage::numeric_encoder::{
get_encoded_literal, get_encoded_named_node, EncodedQuad, EncodedTerm, StrLookup, WriteEncoder, get_encoded_literal, get_encoded_named_node, EncodedQuad, EncodedTerm, EncodedTriple,
StrLookup, WriteEncoder,
}; };
use crate::storage::Storage; use crate::storage::Storage;
use http::header::{ACCEPT, CONTENT_TYPE, USER_AGENT}; use http::header::{ACCEPT, CONTENT_TYPE, USER_AGENT};
@ -18,9 +19,10 @@ use http::{Method, Request, StatusCode};
use oxiri::Iri; use oxiri::Iri;
use spargebra::algebra::{GraphPattern, GraphTarget}; use spargebra::algebra::{GraphPattern, GraphTarget};
use spargebra::term::{ use spargebra::term::{
BlankNode, GraphName, GraphNamePattern, GroundQuad, GroundQuadPattern, GroundTerm, BlankNode, GraphName, GraphNamePattern, GroundQuad, GroundQuadPattern, GroundSubject,
GroundTermPattern, Literal, NamedNode, NamedNodePattern, Quad, QuadPattern, Subject, Term, GroundTerm, GroundTermPattern, GroundTriple, GroundTriplePattern, Literal, NamedNode,
TermPattern, Variable, NamedNodePattern, Quad, QuadPattern, Subject, Term, TermPattern, Triple, TriplePattern,
Variable,
}; };
use spargebra::GraphUpdateOperation; use spargebra::GraphUpdateOperation;
use std::collections::HashMap; use std::collections::HashMap;
@ -93,9 +95,8 @@ impl<'a> SimpleUpdateEvaluator<'a> {
fn eval_insert_data(&mut self, data: &[Quad]) -> Result<(), EvaluationError> { fn eval_insert_data(&mut self, data: &[Quad]) -> Result<(), EvaluationError> {
let mut bnodes = HashMap::new(); let mut bnodes = HashMap::new();
for quad in data { for quad in data {
if let Some(quad) = self.encode_quad_for_insertion(quad, &mut bnodes)? { let quad = self.encode_quad_for_insertion(quad, &mut bnodes)?;
self.storage.insert(&quad)?; self.storage.insert(&quad)?;
}
} }
Ok(()) Ok(())
} }
@ -298,13 +299,16 @@ impl<'a> SimpleUpdateEvaluator<'a> {
&mut self, &mut self,
quad: &Quad, quad: &Quad,
bnodes: &mut HashMap<BlankNode, OxBlankNode>, bnodes: &mut HashMap<BlankNode, OxBlankNode>,
) -> Result<Option<EncodedQuad>, EvaluationError> { ) -> Result<EncodedQuad, EvaluationError> {
Ok(Some(EncodedQuad { Ok(EncodedQuad {
subject: match &quad.subject { subject: match &quad.subject {
Subject::NamedNode(subject) => self.encode_named_node_for_insertion(subject)?, Subject::NamedNode(subject) => self.encode_named_node_for_insertion(subject)?,
Subject::BlankNode(subject) => self Subject::BlankNode(subject) => self
.storage .storage
.encode_blank_node(bnodes.entry(subject.clone()).or_default().as_ref())?, .encode_blank_node(bnodes.entry(subject.clone()).or_default().as_ref())?,
Subject::Triple(subject) => {
self.encode_triple_for_insertion(subject, bnodes)?.into()
}
}, },
predicate: self predicate: self
.storage .storage
@ -315,6 +319,7 @@ impl<'a> SimpleUpdateEvaluator<'a> {
.storage .storage
.encode_blank_node(bnodes.entry(object.clone()).or_default().as_ref())?, .encode_blank_node(bnodes.entry(object.clone()).or_default().as_ref())?,
Term::Literal(object) => self.encode_literal_for_insertion(object)?, Term::Literal(object) => self.encode_literal_for_insertion(object)?,
Term::Triple(subject) => self.encode_triple_for_insertion(subject, bnodes)?.into(),
}, },
graph_name: match &quad.graph_name { graph_name: match &quad.graph_name {
GraphName::NamedNode(graph_name) => { GraphName::NamedNode(graph_name) => {
@ -322,7 +327,7 @@ impl<'a> SimpleUpdateEvaluator<'a> {
} }
GraphName::DefaultGraph => EncodedTerm::DefaultGraph, GraphName::DefaultGraph => EncodedTerm::DefaultGraph,
}, },
})) })
} }
fn encode_quad_pattern_for_insertion( fn encode_quad_pattern_for_insertion(
@ -387,6 +392,9 @@ impl<'a> SimpleUpdateEvaluator<'a> {
.encode_blank_node(bnodes.entry(bnode.clone()).or_default().as_ref())?, .encode_blank_node(bnodes.entry(bnode.clone()).or_default().as_ref())?,
), ),
TermPattern::Literal(term) => Some(self.encode_literal_for_insertion(term)?), TermPattern::Literal(term) => Some(self.encode_literal_for_insertion(term)?),
TermPattern::Triple(triple) => self
.encode_triple_pattern_for_insertion(triple, variables, values, bnodes)?
.map(EncodedTerm::from),
TermPattern::Variable(v) => self.lookup_variable(v, variables, values).filter(validate), TermPattern::Variable(v) => self.lookup_variable(v, variables, values).filter(validate),
}) })
} }
@ -420,6 +428,46 @@ impl<'a> SimpleUpdateEvaluator<'a> {
}) })
} }
fn encode_triple_pattern_for_insertion(
&mut self,
triple: &TriplePattern,
variables: &[Variable],
values: &[Option<EncodedTerm>],
bnodes: &mut HashMap<BlankNode, OxBlankNode>,
) -> Result<Option<EncodedTriple>, EvaluationError> {
Ok(Some(EncodedTriple {
subject: if let Some(subject) = self.encode_term_or_var_for_insertion(
&triple.subject,
variables,
values,
bnodes,
|t| t.is_named_node() || t.is_blank_node(),
)? {
subject
} else {
return Ok(None);
},
predicate: if let Some(predicate) =
self.encode_named_node_or_var_for_insertion(&triple.predicate, variables, values)?
{
predicate
} else {
return Ok(None);
},
object: if let Some(object) = self.encode_term_or_var_for_insertion(
&triple.object,
variables,
values,
bnodes,
|t| !t.is_default_graph(),
)? {
object
} else {
return Ok(None);
},
}))
}
fn lookup_variable( fn lookup_variable(
&self, &self,
v: &Variable, v: &Variable,
@ -448,6 +496,35 @@ impl<'a> SimpleUpdateEvaluator<'a> {
.encode_named_node(NamedNodeRef::new_unchecked(&term.iri))?) .encode_named_node(NamedNodeRef::new_unchecked(&term.iri))?)
} }
fn encode_triple_for_insertion(
&mut self,
triple: &Triple,
bnodes: &mut HashMap<BlankNode, OxBlankNode>,
) -> Result<EncodedTriple, EvaluationError> {
Ok(EncodedTriple {
subject: match &triple.subject {
Subject::NamedNode(subject) => self.encode_named_node_for_insertion(subject)?,
Subject::BlankNode(subject) => self
.storage
.encode_blank_node(bnodes.entry(subject.clone()).or_default().as_ref())?,
Subject::Triple(subject) => {
self.encode_triple_for_insertion(subject, bnodes)?.into()
}
},
predicate: self
.storage
.encode_named_node(NamedNodeRef::new_unchecked(&triple.predicate.iri))?,
object: match &triple.object {
Term::NamedNode(object) => self.encode_named_node_for_insertion(object)?,
Term::BlankNode(object) => self
.storage
.encode_blank_node(bnodes.entry(object.clone()).or_default().as_ref())?,
Term::Literal(object) => self.encode_literal_for_insertion(object)?,
Term::Triple(object) => self.encode_triple_for_insertion(object, bnodes)?.into(),
},
})
}
fn encode_literal_for_insertion( fn encode_literal_for_insertion(
&mut self, &mut self,
term: &Literal, term: &Literal,
@ -465,11 +542,15 @@ impl<'a> SimpleUpdateEvaluator<'a> {
fn encode_quad_for_deletion(&mut self, quad: &GroundQuad) -> EncodedQuad { fn encode_quad_for_deletion(&mut self, quad: &GroundQuad) -> EncodedQuad {
EncodedQuad { EncodedQuad {
subject: self.encode_named_node_for_deletion(&quad.subject), subject: match &quad.subject {
GroundSubject::NamedNode(subject) => self.encode_named_node_for_deletion(subject),
GroundSubject::Triple(subject) => self.encode_triple_for_deletion(subject),
},
predicate: self.encode_named_node_for_deletion(&quad.predicate), predicate: self.encode_named_node_for_deletion(&quad.predicate),
object: match &quad.object { object: match &quad.object {
GroundTerm::NamedNode(object) => self.encode_named_node_for_deletion(object), GroundTerm::NamedNode(object) => self.encode_named_node_for_deletion(object),
GroundTerm::Literal(object) => self.encode_literal_for_deletion(object), GroundTerm::Literal(object) => self.encode_literal_for_deletion(object),
GroundTerm::Triple(object) => self.encode_triple_for_deletion(object),
}, },
graph_name: match &quad.graph_name { graph_name: match &quad.graph_name {
GraphName::NamedNode(graph_name) => self.encode_named_node_for_deletion(graph_name), GraphName::NamedNode(graph_name) => self.encode_named_node_for_deletion(graph_name),
@ -510,6 +591,10 @@ impl<'a> SimpleUpdateEvaluator<'a> {
GroundTermPattern::NamedNode(term) => Some(self.encode_named_node_for_deletion(term)), GroundTermPattern::NamedNode(term) => Some(self.encode_named_node_for_deletion(term)),
GroundTermPattern::Literal(term) => Some(self.encode_literal_for_deletion(term)), GroundTermPattern::Literal(term) => Some(self.encode_literal_for_deletion(term)),
GroundTermPattern::Variable(v) => self.lookup_variable(v, variables, values), GroundTermPattern::Variable(v) => self.lookup_variable(v, variables, values),
GroundTermPattern::Triple(triple) => Some(
self.encode_triple_pattern_for_deletion(triple, variables, values)?
.into(),
),
} }
} }
@ -542,6 +627,23 @@ impl<'a> SimpleUpdateEvaluator<'a> {
} }
} }
fn encode_triple_pattern_for_deletion(
&self,
triple: &GroundTriplePattern,
variables: &[Variable],
values: &[Option<EncodedTerm>],
) -> Option<EncodedTriple> {
Some(EncodedTriple {
subject: self.encode_term_or_var_for_deletion(&triple.subject, variables, values)?,
predicate: self.encode_named_node_or_var_for_deletion(
&triple.predicate,
variables,
values,
)?,
object: self.encode_term_or_var_for_deletion(&triple.object, variables, values)?,
})
}
fn encode_named_node_for_deletion(&self, term: &NamedNode) -> EncodedTerm { fn encode_named_node_for_deletion(&self, term: &NamedNode) -> EncodedTerm {
get_encoded_named_node(NamedNodeRef::new_unchecked(&term.iri)) get_encoded_named_node(NamedNodeRef::new_unchecked(&term.iri))
} }
@ -557,4 +659,20 @@ impl<'a> SimpleUpdateEvaluator<'a> {
} }
}) })
} }
fn encode_triple_for_deletion(&self, triple: &GroundTriple) -> EncodedTerm {
EncodedTriple::new(
match &triple.subject {
GroundSubject::NamedNode(subject) => self.encode_named_node_for_deletion(subject),
GroundSubject::Triple(subject) => self.encode_triple_for_deletion(subject),
},
self.encode_named_node_for_deletion(&triple.predicate),
match &triple.object {
GroundTerm::NamedNode(object) => self.encode_named_node_for_deletion(object),
GroundTerm::Literal(object) => self.encode_literal_for_deletion(object),
GroundTerm::Triple(object) => self.encode_triple_for_deletion(object),
},
)
.into()
}
} }

@ -9,7 +9,7 @@ Spargebra
Spargebra is a [SPARQL](https://www.w3.org/TR/sparql11-overview/) parser. Spargebra is a [SPARQL](https://www.w3.org/TR/sparql11-overview/) parser.
It supports both [SPARQL 1.1 Query](https://www.w3.org/TR/sparql11-query/) and [SPARQL 1.1 Update](https://www.w3.org/TR/sparql11-update/). It supports [SPARQL 1.1 Query](https://www.w3.org/TR/sparql11-query/), [SPARQL 1.1 Update](https://www.w3.org/TR/sparql11-update/) and [SPARQL-star](https://w3c.github.io/rdf-star/cg-spec/).
This crate is intended to be a building piece for SPARQL implementations in Rust like [Oxigraph](https://oxigraph.org). This crate is intended to be a building piece for SPARQL implementations in Rust like [Oxigraph](https://oxigraph.org).

@ -5,58 +5,6 @@ use crate::term::*;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::fmt; use std::fmt;
pub(crate) struct SparqlTriplePattern<'a>(pub(crate) &'a TriplePattern);
impl<'a> fmt::Display for SparqlTriplePattern<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {} {} .",
self.0.subject, self.0.predicate, self.0.object
)
}
}
pub(crate) struct SparqlQuadPattern<'a>(pub(crate) &'a QuadPattern);
impl<'a> fmt::Display for SparqlQuadPattern<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0.graph_name == GraphNamePattern::DefaultGraph {
write!(
f,
"{} {} {} .",
self.0.subject, self.0.predicate, self.0.object
)
} else {
write!(
f,
"GRAPH {} {{ {} {} {} }}",
self.0.graph_name, self.0.subject, self.0.predicate, self.0.object
)
}
}
}
pub(crate) struct SparqlGroundQuadPattern<'a>(pub(crate) &'a GroundQuadPattern);
impl<'a> fmt::Display for SparqlGroundQuadPattern<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0.graph_name == GraphNamePattern::DefaultGraph {
write!(
f,
"{} {} {} .",
self.0.subject, self.0.predicate, self.0.object
)
} else {
write!(
f,
"GRAPH {} {{ {} {} {} }}",
self.0.graph_name, self.0.subject, self.0.predicate, self.0.object
)
}
}
}
/// A [property path expression](https://www.w3.org/TR/sparql11-query/#defn_PropertyPathExpr) /// A [property path expression](https://www.w3.org/TR/sparql11-query/#defn_PropertyPathExpr)
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum PropertyPathExpression { pub enum PropertyPathExpression {
@ -563,7 +511,7 @@ impl fmt::Display for GraphPattern {
GraphPattern::Bgp(p) => { GraphPattern::Bgp(p) => {
write!(f, "(bgp")?; write!(f, "(bgp")?;
for pattern in p { for pattern in p {
write!(f, " {}", pattern)?; write!(f, " {} .", pattern)?;
} }
write!(f, ")") write!(f, ")")
} }
@ -754,7 +702,7 @@ impl<'a> fmt::Display for SparqlGraphPattern<'a> {
match self.0 { match self.0 {
GraphPattern::Bgp(p) => { GraphPattern::Bgp(p) => {
for pattern in p { for pattern in p {
write!(f, "{}", SparqlTriplePattern(pattern))? write!(f, "{} .", pattern)?
} }
Ok(()) Ok(())
} }

@ -10,7 +10,7 @@
//! use spargebra::Query; //! use spargebra::Query;
//! //!
//! let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }"; //! let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }";
//! let mut query = Query::parse(query_str, None)?; //! let query = Query::parse(query_str, None)?;
//! assert_eq!(query.to_string(), query_str); //! assert_eq!(query.to_string(), query_str);
//! # Result::Ok::<_, spargebra::ParseError>(()) //! # Result::Ok::<_, spargebra::ParseError>(())
//! ``` //! ```
@ -24,6 +24,7 @@
unsafe_code, unsafe_code,
unused_qualifications unused_qualifications
)] )]
#![doc(test(attr(deny(warnings))))]
pub mod algebra; pub mod algebra;
mod parser; mod parser;

@ -9,6 +9,7 @@ use peg::str::LineCol;
use rand::random; use rand::random;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::convert::{TryFrom, TryInto};
use std::error::Error; use std::error::Error;
use std::str::Chars; use std::str::Chars;
use std::str::FromStr; use std::str::FromStr;
@ -1039,22 +1040,7 @@ parser! {
GraphNamePattern::Variable(graph_name) => GraphPattern::Graph { graph_name: graph_name.clone().into(), inner: Box::new(bgp) }, GraphNamePattern::Variable(graph_name) => GraphPattern::Graph { graph_name: graph_name.clone().into(), inner: Box::new(bgp) },
} }
}).fold(GraphPattern::Bgp(Vec::new()), new_join); }).fold(GraphPattern::Bgp(Vec::new()), new_join);
let delete = d.into_iter().map(|q| Ok(GroundQuadPattern { let delete = d.into_iter().map(GroundQuadPattern::try_from).collect::<Result<Vec<_>,_>>().map_err(|_| "Blank nodes are not allowed in DELETE WHERE")?;
subject: match q.subject {
TermPattern::NamedNode(subject) => subject.into(),
TermPattern::BlankNode(_) => return Err("Blank nodes are not allowed in DELETE WHERE"),
TermPattern::Literal(subject) => subject.into(),
TermPattern::Variable(subject) => subject.into(),
},
predicate: q.predicate,
object: match q.object {
TermPattern::NamedNode(object) => object.into(),
TermPattern::BlankNode(_) => return Err("Blank nodes are not allowed in DELETE WHERE"),
TermPattern::Literal(object) => object.into(),
TermPattern::Variable(object) => object.into(),
},
graph_name: q.graph_name
})).collect::<Result<Vec<_>,_>>()?;
Ok(vec![GraphUpdateOperation::DeleteInsert { Ok(vec![GraphUpdateOperation::DeleteInsert {
delete, delete,
insert: Vec::new(), insert: Vec::new(),
@ -1133,22 +1119,7 @@ parser! {
//[42] //[42]
rule DeleteClause() -> Vec<GroundQuadPattern> = i("DELETE") _ q:QuadPattern() {? rule DeleteClause() -> Vec<GroundQuadPattern> = i("DELETE") _ q:QuadPattern() {?
q.into_iter().map(|q| Ok(GroundQuadPattern { q.into_iter().map(GroundQuadPattern::try_from).collect::<Result<Vec<_>,_>>().map_err(|_| "Blank nodes are not allowed in DELETE WHERE")
subject: match q.subject {
TermPattern::NamedNode(subject) => subject.into(),
TermPattern::BlankNode(_) => return Err("Blank nodes are not allowed in DELETE WHERE"),
TermPattern::Literal(subject) => subject.into(),
TermPattern::Variable(subject) => subject.into(),
},
predicate: q.predicate,
object: match q.object {
TermPattern::NamedNode(object) => object.into(),
TermPattern::BlankNode(_) => return Err("Blank nodes are not allowed in DELETE WHERE"),
TermPattern::Literal(object) => object.into(),
TermPattern::Variable(object) => object.into(),
},
graph_name: q.graph_name
})).collect::<Result<Vec<_>,_>>()
} }
//[43] //[43]
@ -1184,53 +1155,10 @@ parser! {
//[49] //[49]
rule QuadData() -> Vec<Quad> = "{" _ q:Quads() _ "}" {? rule QuadData() -> Vec<Quad> = "{" _ q:Quads() _ "}" {?
q.into_iter().map(|q| Ok(Quad { q.into_iter().map(Quad::try_from).collect::<Result<Vec<_>, ()>>().map_err(|_| "Variables are not allowed in INSERT DATA")
subject: match q.subject {
TermPattern::NamedNode(t) => t.into(),
TermPattern::BlankNode(t) => t.into(),
TermPattern::Literal(_) | TermPattern::Variable(_) => return Err(())
},
predicate: if let NamedNodePattern::NamedNode(t) = q.predicate {
t
} else {
return Err(())
},
object: match q.object {
TermPattern::NamedNode(t) => t.into(),
TermPattern::BlankNode(t) => t.into(),
TermPattern::Literal(t) => t.into(),
TermPattern::Variable(_) => return Err(())
},
graph_name: match q.graph_name {
GraphNamePattern::NamedNode(t) => t.into(),
GraphNamePattern::DefaultGraph => GraphName::DefaultGraph,
GraphNamePattern::Variable(_) => return Err(())
}
})).collect::<Result<Vec<_>, ()>>().map_err(|_| "Variables are not allowed in INSERT DATA and DELETE DATA")
} }
rule GroundQuadData() -> Vec<GroundQuad> = "{" _ q:Quads() _ "}" {? rule GroundQuadData() -> Vec<GroundQuad> = "{" _ q:Quads() _ "}" {?
q.into_iter().map(|q| Ok(GroundQuad { q.into_iter().map(|q| GroundQuad::try_from(Quad::try_from(q)?)).collect::<Result<Vec<_>, ()>>().map_err(|_| "Variables and blank nodes are not allowed in DELETE DATA")
subject: if let TermPattern::NamedNode(t) = q.subject {
t
} else {
return Err(())
},
predicate: if let NamedNodePattern::NamedNode(t) = q.predicate {
t
} else {
return Err(())
},
object: match q.object {
TermPattern::NamedNode(t) => t.into(),
TermPattern::Literal(t) => t.into(),
TermPattern::BlankNode(_) | TermPattern::Variable(_) => return Err(())
},
graph_name: match q.graph_name {
GraphNamePattern::NamedNode(t) => t.into(),
GraphNamePattern::DefaultGraph => GraphName::DefaultGraph,
GraphNamePattern::Variable(_) => return Err(())
}
})).collect::<Result<Vec<_>, ()>>().map_err(|_| "Variables are not allowed in INSERT DATA and DELETE DATA")
} }
//[50] //[50]
@ -1373,6 +1301,7 @@ parser! {
//[65] //[65]
rule DataBlockValue() -> Option<GroundTerm> = rule DataBlockValue() -> Option<GroundTerm> =
t:EmbTriple() { Some(t.into()) } /
i:iri() { Some(i.into()) } / i:iri() { Some(i.into()) } /
l:RDFLiteral() { Some(l.into()) } / l:RDFLiteral() { Some(l.into()) } /
l:NumericLiteral() { Some(l.into()) } / l:NumericLiteral() { Some(l.into()) } /
@ -1428,7 +1357,7 @@ parser! {
//[75] //[75]
rule TriplesSameSubject() -> Vec<TriplePattern> = rule TriplesSameSubject() -> Vec<TriplePattern> =
s:VarOrTerm() _ po:PropertyListNotEmpty() { s:VarOrTermOrEmbTP() _ po:PropertyListNotEmpty() {
let mut patterns = po.patterns; let mut patterns = po.patterns;
for (p, os) in po.focus { for (p, os) in po.focus {
for o in os { for o in os {
@ -1486,7 +1415,7 @@ parser! {
//[81] //[81]
rule TriplesSameSubjectPath() -> Vec<TripleOrPathPattern> = rule TriplesSameSubjectPath() -> Vec<TripleOrPathPattern> =
s:VarOrTerm() _ po:PropertyListPathNotEmpty() { s:VarOrTermOrEmbTP() _ po:PropertyListPathNotEmpty() {
let mut patterns = po.patterns; let mut patterns = po.patterns;
for (p, os) in po.focus { for (p, os) in po.focus {
for o in os { for o in os {
@ -1704,12 +1633,12 @@ parser! {
//[104] //[104]
rule GraphNode() -> FocusedTriplePattern<TermPattern> = rule GraphNode() -> FocusedTriplePattern<TermPattern> =
t:VarOrTerm() { FocusedTriplePattern::new(t) } / t:VarOrTermOrEmbTP() { FocusedTriplePattern::new(t) } /
TriplesNode() TriplesNode()
//[105] //[105]
rule GraphNodePath() -> FocusedTripleOrPathPattern<TermPattern> = rule GraphNodePath() -> FocusedTripleOrPathPattern<TermPattern> =
t:VarOrTerm() { FocusedTripleOrPathPattern::new(t) } / t:VarOrTermOrEmbTP() { FocusedTripleOrPathPattern::new(t) } /
TriplesNodePath() TriplesNodePath()
//[106] //[106]
@ -2128,6 +2057,43 @@ parser! {
//[173] //[173]
rule PN_LOCAL_ESC() = ['\\'] ['_' | '~' | '.' | '-' | '!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | ';' | '=' | '/' | '?' | '#' | '@' | '%'] //TODO: added '/' to make tests pass but is it valid? rule PN_LOCAL_ESC() = ['\\'] ['_' | '~' | '.' | '-' | '!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | ';' | '=' | '/' | '?' | '#' | '@' | '%'] //TODO: added '/' to make tests pass but is it valid?
//[174]
rule EmbTP() -> TriplePattern = "<<" _ s:EmbSubjectOrObject() _ p:Verb() _ o:EmbSubjectOrObject() _ ">>" {
TriplePattern { subject: s, predicate: p, object: o }
}
//[175]
rule EmbTriple() -> GroundTriple = "<<" _ s:DataValueTerm() _ p:EmbTriple_p() _ o:DataValueTerm() _ ">>" {?
Ok(GroundTriple {
subject: s.try_into().map_err(|_| "Literals are not allowed in subject position of nested patterns")?,
predicate: p,
object: o
})
}
rule EmbTriple_p() -> NamedNode = i: iri() { i } / "a" { iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#type") }
//[176]
rule EmbSubjectOrObject() -> TermPattern = v:Var() { v.into() } /
b:BlankNode() { b.into() } /
i:iri() { i.into() } /
l:RDFLiteral() { l.into() } /
l:NumericLiteral() { l.into() } /
l:BooleanLiteral() { l.into() } /
t:EmbTP() { t.into() }
//[177]
rule DataValueTerm() -> GroundTerm = i:iri() { i.into() } /
l:RDFLiteral() { l.into() } /
l:NumericLiteral() { l.into() } /
l:BooleanLiteral() { l.into() } /
t:EmbTriple() { t.into() }
//[178]
rule VarOrTermOrEmbTP() -> TermPattern =
v:Var() { v.into() } /
t:GraphTerm() { t.into() } /
t:EmbTP() { t.into() }
//space //space
rule _() = quiet! { ([' ' | '\t' | '\n' | '\r'] / comment())* } rule _() = quiet! { ([' ' | '\t' | '\n' | '\r'] / comment())* }

@ -12,7 +12,7 @@ use std::str::FromStr;
/// use spargebra::Query; /// use spargebra::Query;
/// ///
/// let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }"; /// let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }";
/// let mut query = Query::parse(query_str, None)?; /// let query = Query::parse(query_str, None)?;
/// assert_eq!(query.to_string(), query_str); /// assert_eq!(query.to_string(), query_str);
/// # Result::Ok::<_, spargebra::ParseError>(()) /// # Result::Ok::<_, spargebra::ParseError>(())
/// ``` /// ```
@ -96,7 +96,7 @@ impl fmt::Display for Query {
} }
write!(f, "CONSTRUCT {{ ")?; write!(f, "CONSTRUCT {{ ")?;
for triple in template.iter() { for triple in template.iter() {
write!(f, "{} ", SparqlTriplePattern(triple))?; write!(f, "{} . ", triple)?;
} }
write!(f, "}}")?; write!(f, "}}")?;
if let Some(dataset) = dataset { if let Some(dataset) = dataset {

@ -1,5 +1,6 @@
//! Data structures for [RDF 1.1 Concepts](https://www.w3.org/TR/rdf11-concepts/) like IRI, literal or triples. //! Data structures for [RDF 1.1 Concepts](https://www.w3.org/TR/rdf11-concepts/) like IRI, literal or triples.
use std::convert::{TryFrom, TryInto};
use std::fmt; use std::fmt;
use std::fmt::Write; use std::fmt::Write;
@ -28,6 +29,18 @@ impl fmt::Display for NamedNode {
} }
} }
impl TryFrom<NamedNodePattern> for NamedNode {
type Error = ();
#[inline]
fn try_from(pattern: NamedNodePattern) -> Result<Self, ()> {
match pattern {
NamedNodePattern::NamedNode(t) => Ok(t),
NamedNodePattern::Variable(_) => Err(()),
}
}
}
/// An RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node). /// An RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node).
/// ///
/// ///
@ -126,14 +139,20 @@ impl fmt::Display for Literal {
pub enum Subject { pub enum Subject {
NamedNode(NamedNode), NamedNode(NamedNode),
BlankNode(BlankNode), BlankNode(BlankNode),
Triple(Box<Triple>),
} }
impl fmt::Display for Subject { impl fmt::Display for Subject {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Subject::NamedNode(node) => node.fmt(f), Self::NamedNode(node) => node.fmt(f),
Subject::BlankNode(node) => node.fmt(f), Self::BlankNode(node) => node.fmt(f),
Self::Triple(triple) => write!(
f,
"<<{} {} {}>>",
triple.subject, triple.predicate, triple.object
),
} }
} }
} }
@ -152,6 +171,104 @@ impl From<BlankNode> for Subject {
} }
} }
impl From<Triple> for Subject {
#[inline]
fn from(triple: Triple) -> Self {
Self::Triple(Box::new(triple))
}
}
impl From<Box<Triple>> for Subject {
#[inline]
fn from(triple: Box<Triple>) -> Self {
Self::Triple(triple)
}
}
impl TryFrom<TermPattern> for Subject {
type Error = ();
#[inline]
fn try_from(term: TermPattern) -> Result<Self, ()> {
match term {
TermPattern::NamedNode(t) => Ok(t.into()),
TermPattern::BlankNode(t) => Ok(t.into()),
TermPattern::Triple(t) => Ok(Triple::try_from(t)?.into()),
TermPattern::Literal(_) | TermPattern::Variable(_) => Err(()),
}
}
}
/// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and [triples](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple).
///
/// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation.
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum GroundSubject {
NamedNode(NamedNode),
Triple(Box<GroundTriple>),
}
impl fmt::Display for GroundSubject {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NamedNode(node) => node.fmt(f),
Self::Triple(triple) => write!(
f,
"<<{} {} {}>>",
triple.subject, triple.predicate, triple.object
),
}
}
}
impl From<NamedNode> for GroundSubject {
#[inline]
fn from(node: NamedNode) -> Self {
Self::NamedNode(node)
}
}
impl From<GroundTriple> for GroundSubject {
#[inline]
fn from(triple: GroundTriple) -> Self {
Self::Triple(Box::new(triple))
}
}
impl From<Box<GroundTriple>> for GroundSubject {
#[inline]
fn from(triple: Box<GroundTriple>) -> Self {
Self::Triple(triple)
}
}
impl TryFrom<Subject> for GroundSubject {
type Error = ();
#[inline]
fn try_from(subject: Subject) -> Result<Self, ()> {
match subject {
Subject::NamedNode(t) => Ok(t.into()),
Subject::BlankNode(_) => Err(()),
Subject::Triple(t) => Ok(GroundTriple::try_from(t)?.into()),
}
}
}
impl TryFrom<GroundTerm> for GroundSubject {
type Error = ();
#[inline]
fn try_from(term: GroundTerm) -> Result<Self, ()> {
match term {
GroundTerm::NamedNode(t) => Ok(t.into()),
GroundTerm::Literal(_) => Err(()),
GroundTerm::Triple(t) => Ok(t.into()),
}
}
}
/// An RDF [term](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-term). /// An RDF [term](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-term).
/// ///
/// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node) and [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal). /// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node) and [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal).
@ -162,15 +279,21 @@ pub enum Term {
NamedNode(NamedNode), NamedNode(NamedNode),
BlankNode(BlankNode), BlankNode(BlankNode),
Literal(Literal), Literal(Literal),
Triple(Box<Triple>),
} }
impl fmt::Display for Term { impl fmt::Display for Term {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Term::NamedNode(node) => node.fmt(f), Self::NamedNode(node) => node.fmt(f),
Term::BlankNode(node) => node.fmt(f), Self::BlankNode(node) => node.fmt(f),
Term::Literal(literal) => literal.fmt(f), Self::Literal(literal) => literal.fmt(f),
Self::Triple(triple) => write!(
f,
"<<{} {} {}>>",
triple.subject, triple.predicate, triple.object
),
} }
} }
} }
@ -196,31 +319,67 @@ impl From<Literal> for Term {
} }
} }
impl From<Triple> for Term {
#[inline]
fn from(triple: Triple) -> Self {
Self::Triple(Box::new(triple))
}
}
impl From<Box<Triple>> for Term {
#[inline]
fn from(triple: Box<Triple>) -> Self {
Self::Triple(triple)
}
}
impl From<Subject> for Term { impl From<Subject> for Term {
#[inline] #[inline]
fn from(resource: Subject) -> Self { fn from(resource: Subject) -> Self {
match resource { match resource {
Subject::NamedNode(node) => Self::NamedNode(node), Subject::NamedNode(node) => node.into(),
Subject::BlankNode(node) => Self::BlankNode(node), Subject::BlankNode(node) => node.into(),
Subject::Triple(t) => t.into(),
} }
} }
} }
/// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal). impl TryFrom<TermPattern> for Term {
type Error = ();
#[inline]
fn try_from(pattern: TermPattern) -> Result<Self, ()> {
match pattern {
TermPattern::NamedNode(t) => Ok(t.into()),
TermPattern::BlankNode(t) => Ok(t.into()),
TermPattern::Literal(t) => Ok(t.into()),
TermPattern::Triple(t) => Ok(Triple::try_from(t)?.into()),
TermPattern::Variable(_) => Err(()),
}
}
}
/// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal) and [triples](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple).
/// ///
/// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation. /// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation.
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum GroundTerm { pub enum GroundTerm {
NamedNode(NamedNode), NamedNode(NamedNode),
Literal(Literal), Literal(Literal),
Triple(Box<GroundTriple>),
} }
impl fmt::Display for GroundTerm { impl fmt::Display for GroundTerm {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
GroundTerm::NamedNode(node) => node.fmt(f), Self::NamedNode(node) => node.fmt(f),
GroundTerm::Literal(literal) => literal.fmt(f), Self::Literal(literal) => literal.fmt(f),
Self::Triple(triple) => write!(
f,
"<<{} {} {}>>",
triple.subject, triple.predicate, triple.object
),
} }
} }
} }
@ -239,6 +398,148 @@ impl From<Literal> for GroundTerm {
} }
} }
impl From<GroundTriple> for GroundTerm {
#[inline]
fn from(triple: GroundTriple) -> Self {
Self::Triple(Box::new(triple))
}
}
impl From<Box<GroundTriple>> for GroundTerm {
#[inline]
fn from(triple: Box<GroundTriple>) -> Self {
Self::Triple(triple)
}
}
impl TryFrom<Term> for GroundTerm {
type Error = ();
#[inline]
fn try_from(term: Term) -> Result<Self, ()> {
match term {
Term::NamedNode(t) => Ok(t.into()),
Term::BlankNode(_) => Err(()),
Term::Literal(t) => Ok(t.into()),
Term::Triple(t) => Ok(GroundTriple::try_from(t)?.into()),
}
}
}
/// A [RDF triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple).
///
/// The default string formatter is returning a N-Quads representation.
///
/// ```
/// use spargebra::term::NamedNode;
/// use spargebra::term::Triple;
///
/// assert_eq!(
/// "<http://example.com/foo> <http://schema.org/sameAs> <http://example.com/foo>",
/// Triple {
/// subject: NamedNode { iri: "http://example.com/foo".into() }.into(),
/// predicate: NamedNode { iri: "http://schema.org/sameAs".into() },
/// object: NamedNode { iri: "http://example.com/foo".into() }.into(),
/// }.to_string()
/// )
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct Triple {
pub subject: Subject,
pub predicate: NamedNode,
pub object: Term,
}
impl fmt::Display for Triple {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {} {}", self.subject, self.predicate, self.object)
}
}
impl TryFrom<TriplePattern> for Triple {
type Error = ();
#[inline]
fn try_from(triple: TriplePattern) -> Result<Self, ()> {
Ok(Self {
subject: triple.subject.try_into()?,
predicate: triple.predicate.try_into()?,
object: triple.object.try_into()?,
})
}
}
impl TryFrom<Box<TriplePattern>> for Triple {
type Error = ();
#[inline]
fn try_from(triple: Box<TriplePattern>) -> Result<Self, ()> {
Ok(Self {
subject: triple.subject.try_into()?,
predicate: triple.predicate.try_into()?,
object: triple.object.try_into()?,
})
}
}
/// A [RDF triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple) without blank nodes.
///
/// The default string formatter is returning a N-Quads representation.
///
/// ```
/// use spargebra::term::NamedNode;
/// use spargebra::term::GroundTriple;
///
/// assert_eq!(
/// "<http://example.com/foo> <http://schema.org/sameAs> <http://example.com/foo>",
/// GroundTriple {
/// subject: NamedNode { iri: "http://example.com/foo".into() }.into(),
/// predicate: NamedNode { iri: "http://schema.org/sameAs".into() },
/// object: NamedNode { iri: "http://example.com/foo".into() }.into(),
/// }.to_string()
/// )
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct GroundTriple {
pub subject: GroundSubject,
pub predicate: NamedNode,
pub object: GroundTerm,
}
impl fmt::Display for GroundTriple {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {} {}", self.subject, self.predicate, self.object)
}
}
impl TryFrom<Triple> for GroundTriple {
type Error = ();
#[inline]
fn try_from(triple: Triple) -> Result<Self, ()> {
Ok(Self {
subject: triple.subject.try_into()?,
predicate: triple.predicate,
object: triple.object.try_into()?,
})
}
}
impl TryFrom<Box<Triple>> for GroundTriple {
type Error = ();
#[inline]
fn try_from(triple: Box<Triple>) -> Result<Self, ()> {
Ok(Self {
subject: triple.subject.try_into()?,
predicate: triple.predicate,
object: triple.object.try_into()?,
})
}
}
/// A possible graph name. /// A possible graph name.
/// ///
/// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and the [default graph name](https://www.w3.org/TR/rdf11-concepts/#dfn-default-graph). /// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and the [default graph name](https://www.w3.org/TR/rdf11-concepts/#dfn-default-graph).
@ -252,8 +553,8 @@ impl fmt::Display for GraphName {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
GraphName::NamedNode(node) => node.fmt(f), Self::NamedNode(node) => node.fmt(f),
GraphName::DefaultGraph => write!(f, "DEFAULT"), Self::DefaultGraph => write!(f, "DEFAULT"),
} }
} }
} }
@ -265,6 +566,19 @@ impl From<NamedNode> for GraphName {
} }
} }
impl TryFrom<GraphNamePattern> for GraphName {
type Error = ();
#[inline]
fn try_from(pattern: GraphNamePattern) -> Result<Self, ()> {
match pattern {
GraphNamePattern::NamedNode(t) => Ok(t.into()),
GraphNamePattern::DefaultGraph => Ok(GraphName::DefaultGraph),
GraphNamePattern::Variable(_) => Err(()),
}
}
}
/// A [RDF triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple) in a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset). /// A [RDF triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple) in a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset).
/// ///
/// The default string formatter is returning a N-Quads representation. /// The default string formatter is returning a N-Quads representation.
@ -274,7 +588,7 @@ impl From<NamedNode> for GraphName {
/// use spargebra::term::Quad; /// use spargebra::term::Quad;
/// ///
/// assert_eq!( /// assert_eq!(
/// "<http://example.com/foo> <http://schema.org/sameAs> <http://example.com/foo> <http://example.com/> .", /// "<http://example.com/foo> <http://schema.org/sameAs> <http://example.com/foo> <http://example.com/>",
/// Quad { /// Quad {
/// subject: NamedNode { iri: "http://example.com/foo".into() }.into(), /// subject: NamedNode { iri: "http://example.com/foo".into() }.into(),
/// predicate: NamedNode { iri: "http://schema.org/sameAs".into() }, /// predicate: NamedNode { iri: "http://schema.org/sameAs".into() },
@ -295,17 +609,31 @@ impl fmt::Display for Quad {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.graph_name == GraphName::DefaultGraph { if self.graph_name == GraphName::DefaultGraph {
write!(f, "{} {} {} .", self.subject, self.predicate, self.object) write!(f, "{} {} {}", self.subject, self.predicate, self.object)
} else { } else {
write!( write!(
f, f,
"{} {} {} {} .", "{} {} {} {}",
self.subject, self.predicate, self.object, self.graph_name self.subject, self.predicate, self.object, self.graph_name
) )
} }
} }
} }
impl TryFrom<QuadPattern> for Quad {
type Error = ();
#[inline]
fn try_from(quad: QuadPattern) -> Result<Self, ()> {
Ok(Self {
subject: quad.subject.try_into()?,
predicate: quad.predicate.try_into()?,
object: quad.object.try_into()?,
graph_name: quad.graph_name.try_into()?,
})
}
}
/// A [RDF triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple) in a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) without blank nodes. /// A [RDF triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple) in a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) without blank nodes.
/// ///
/// The default string formatter is returning a N-Quads representation. /// The default string formatter is returning a N-Quads representation.
@ -315,7 +643,7 @@ impl fmt::Display for Quad {
/// use spargebra::term::GroundQuad; /// use spargebra::term::GroundQuad;
/// ///
/// assert_eq!( /// assert_eq!(
/// "<http://example.com/foo> <http://schema.org/sameAs> <http://example.com/foo> <http://example.com/> .", /// "<http://example.com/foo> <http://schema.org/sameAs> <http://example.com/foo> <http://example.com/>",
/// GroundQuad { /// GroundQuad {
/// subject: NamedNode { iri: "http://example.com/foo".into() }.into(), /// subject: NamedNode { iri: "http://example.com/foo".into() }.into(),
/// predicate: NamedNode { iri: "http://schema.org/sameAs".into() }, /// predicate: NamedNode { iri: "http://schema.org/sameAs".into() },
@ -326,7 +654,7 @@ impl fmt::Display for Quad {
/// ``` /// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct GroundQuad { pub struct GroundQuad {
pub subject: NamedNode, pub subject: GroundSubject,
pub predicate: NamedNode, pub predicate: NamedNode,
pub object: GroundTerm, pub object: GroundTerm,
pub graph_name: GraphName, pub graph_name: GraphName,
@ -336,17 +664,31 @@ impl fmt::Display for GroundQuad {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.graph_name == GraphName::DefaultGraph { if self.graph_name == GraphName::DefaultGraph {
write!(f, "{} {} {} .", self.subject, self.predicate, self.object) write!(f, "{} {} {}", self.subject, self.predicate, self.object)
} else { } else {
write!( write!(
f, f,
"{} {} {} {} .", "{} {} {} {}",
self.subject, self.predicate, self.object, self.graph_name self.subject, self.predicate, self.object, self.graph_name
) )
} }
} }
} }
impl TryFrom<Quad> for GroundQuad {
type Error = ();
#[inline]
fn try_from(quad: Quad) -> Result<Self, ()> {
Ok(Self {
subject: quad.subject.try_into()?,
predicate: quad.predicate,
object: quad.object.try_into()?,
graph_name: quad.graph_name,
})
}
}
/// A [SPARQL query variable](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables). /// A [SPARQL query variable](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables).
/// ///
/// ``` /// ```
@ -380,8 +722,8 @@ impl fmt::Display for NamedNodePattern {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
NamedNodePattern::NamedNode(node) => node.fmt(f), Self::NamedNode(node) => node.fmt(f),
NamedNodePattern::Variable(var) => var.fmt(f), Self::Variable(var) => var.fmt(f),
} }
} }
} }
@ -406,6 +748,7 @@ pub enum TermPattern {
NamedNode(NamedNode), NamedNode(NamedNode),
BlankNode(BlankNode), BlankNode(BlankNode),
Literal(Literal), Literal(Literal),
Triple(Box<TriplePattern>),
Variable(Variable), Variable(Variable),
} }
@ -413,10 +756,11 @@ impl fmt::Display for TermPattern {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
TermPattern::NamedNode(term) => term.fmt(f), Self::NamedNode(term) => term.fmt(f),
TermPattern::BlankNode(term) => term.fmt(f), Self::BlankNode(term) => term.fmt(f),
TermPattern::Literal(term) => term.fmt(f), Self::Literal(term) => term.fmt(f),
TermPattern::Variable(var) => var.fmt(f), Self::Triple(triple) => write!(f, "<<{}>>", triple),
Self::Variable(var) => var.fmt(f),
} }
} }
} }
@ -442,19 +786,45 @@ impl From<Literal> for TermPattern {
} }
} }
impl From<TriplePattern> for TermPattern {
#[inline]
fn from(triple: TriplePattern) -> Self {
Self::Triple(Box::new(triple))
}
}
impl From<Box<TriplePattern>> for TermPattern {
#[inline]
fn from(triple: Box<TriplePattern>) -> Self {
Self::Triple(triple)
}
}
impl From<Variable> for TermPattern { impl From<Variable> for TermPattern {
fn from(var: Variable) -> Self { fn from(var: Variable) -> Self {
Self::Variable(var) Self::Variable(var)
} }
} }
impl From<Subject> for TermPattern {
#[inline]
fn from(subject: Subject) -> Self {
match subject {
Subject::NamedNode(node) => node.into(),
Subject::BlankNode(node) => node.into(),
Subject::Triple(t) => TriplePattern::from(t).into(),
}
}
}
impl From<Term> for TermPattern { impl From<Term> for TermPattern {
#[inline] #[inline]
fn from(term: Term) -> Self { fn from(term: Term) -> Self {
match term { match term {
Term::NamedNode(node) => Self::NamedNode(node), Term::NamedNode(node) => node.into(),
Term::BlankNode(node) => Self::BlankNode(node), Term::BlankNode(node) => node.into(),
Term::Literal(literal) => Self::Literal(literal), Term::Literal(literal) => literal.into(),
Term::Triple(t) => TriplePattern::from(t).into(),
} }
} }
} }
@ -463,8 +833,8 @@ impl From<NamedNodePattern> for TermPattern {
#[inline] #[inline]
fn from(element: NamedNodePattern) -> Self { fn from(element: NamedNodePattern) -> Self {
match element { match element {
NamedNodePattern::NamedNode(node) => Self::NamedNode(node), NamedNodePattern::NamedNode(node) => node.into(),
NamedNodePattern::Variable(var) => Self::Variable(var), NamedNodePattern::Variable(var) => var.into(),
} }
} }
} }
@ -475,15 +845,17 @@ pub enum GroundTermPattern {
NamedNode(NamedNode), NamedNode(NamedNode),
Literal(Literal), Literal(Literal),
Variable(Variable), Variable(Variable),
Triple(Box<GroundTriplePattern>),
} }
impl fmt::Display for GroundTermPattern { impl fmt::Display for GroundTermPattern {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
GroundTermPattern::NamedNode(term) => term.fmt(f), Self::NamedNode(term) => term.fmt(f),
GroundTermPattern::Literal(term) => term.fmt(f), Self::Literal(term) => term.fmt(f),
GroundTermPattern::Variable(var) => var.fmt(f), Self::Variable(var) => var.fmt(f),
Self::Triple(triple) => write!(f, "<<{}>>", triple),
} }
} }
} }
@ -502,6 +874,20 @@ impl From<Literal> for GroundTermPattern {
} }
} }
impl From<GroundTriplePattern> for GroundTermPattern {
#[inline]
fn from(triple: GroundTriplePattern) -> Self {
Self::Triple(Box::new(triple))
}
}
impl From<Box<GroundTriplePattern>> for GroundTermPattern {
#[inline]
fn from(triple: Box<GroundTriplePattern>) -> Self {
Self::Triple(triple)
}
}
impl From<Variable> for GroundTermPattern { impl From<Variable> for GroundTermPattern {
#[inline] #[inline]
fn from(var: Variable) -> Self { fn from(var: Variable) -> Self {
@ -509,12 +895,22 @@ impl From<Variable> for GroundTermPattern {
} }
} }
impl From<GroundSubject> for GroundTermPattern {
#[inline]
fn from(term: GroundSubject) -> Self {
match term {
GroundSubject::NamedNode(node) => node.into(),
GroundSubject::Triple(triple) => GroundTriplePattern::from(triple).into(),
}
}
}
impl From<GroundTerm> for GroundTermPattern { impl From<GroundTerm> for GroundTermPattern {
#[inline] #[inline]
fn from(term: GroundTerm) -> Self { fn from(term: GroundTerm) -> Self {
match term { match term {
GroundTerm::NamedNode(node) => Self::NamedNode(node), GroundTerm::NamedNode(node) => node.into(),
GroundTerm::Literal(literal) => Self::Literal(literal), GroundTerm::Literal(literal) => literal.into(),
GroundTerm::Triple(triple) => GroundTriplePattern::from(triple).into(),
} }
} }
} }
@ -523,12 +919,27 @@ impl From<NamedNodePattern> for GroundTermPattern {
#[inline] #[inline]
fn from(element: NamedNodePattern) -> Self { fn from(element: NamedNodePattern) -> Self {
match element { match element {
NamedNodePattern::NamedNode(node) => Self::NamedNode(node), NamedNodePattern::NamedNode(node) => node.into(),
NamedNodePattern::Variable(var) => Self::Variable(var), NamedNodePattern::Variable(var) => var.into(),
} }
} }
} }
impl TryFrom<TermPattern> for GroundTermPattern {
type Error = ();
#[inline]
fn try_from(pattern: TermPattern) -> Result<Self, ()> {
Ok(match pattern {
TermPattern::NamedNode(named_node) => named_node.into(),
TermPattern::BlankNode(_) => return Err(()),
TermPattern::Literal(literal) => literal.into(),
TermPattern::Triple(triple) => GroundTriplePattern::try_from(triple)?.into(),
TermPattern::Variable(variable) => variable.into(),
})
}
}
/// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [default graph name](https://www.w3.org/TR/rdf11-concepts/#dfn-default-graph) and [variables](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables). /// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [default graph name](https://www.w3.org/TR/rdf11-concepts/#dfn-default-graph) and [variables](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables).
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum GraphNamePattern { pub enum GraphNamePattern {
@ -541,9 +952,9 @@ impl fmt::Display for GraphNamePattern {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
GraphNamePattern::NamedNode(node) => node.fmt(f), Self::NamedNode(node) => node.fmt(f),
GraphNamePattern::DefaultGraph => f.write_str("DEFAULT"), Self::DefaultGraph => f.write_str("DEFAULT"),
GraphNamePattern::Variable(var) => var.fmt(f), Self::Variable(var) => var.fmt(f),
} }
} }
} }
@ -566,7 +977,7 @@ impl From<GraphName> for GraphNamePattern {
#[inline] #[inline]
fn from(graph_name: GraphName) -> Self { fn from(graph_name: GraphName) -> Self {
match graph_name { match graph_name {
GraphName::NamedNode(node) => Self::NamedNode(node), GraphName::NamedNode(node) => node.into(),
GraphName::DefaultGraph => Self::DefaultGraph, GraphName::DefaultGraph => Self::DefaultGraph,
} }
} }
@ -576,8 +987,8 @@ impl From<NamedNodePattern> for GraphNamePattern {
#[inline] #[inline]
fn from(graph_name: NamedNodePattern) -> Self { fn from(graph_name: NamedNodePattern) -> Self {
match graph_name { match graph_name {
NamedNodePattern::NamedNode(node) => Self::NamedNode(node), NamedNodePattern::NamedNode(node) => node.into(),
NamedNodePattern::Variable(var) => Self::Variable(var), NamedNodePattern::Variable(var) => var.into(),
} }
} }
} }
@ -607,11 +1018,92 @@ impl TriplePattern {
impl fmt::Display for TriplePattern { impl fmt::Display for TriplePattern {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(f, "{} {} {}", self.subject, self.predicate, self.object)
f, }
"(triple {} {} {})", }
self.subject, self.predicate, self.object
) impl From<Triple> for TriplePattern {
#[inline]
fn from(triple: Triple) -> Self {
Self {
subject: triple.subject.into(),
predicate: triple.predicate.into(),
object: triple.object.into(),
}
}
}
impl From<Box<Triple>> for TriplePattern {
#[inline]
fn from(triple: Box<Triple>) -> Self {
Self {
subject: triple.subject.into(),
predicate: triple.predicate.into(),
object: triple.object.into(),
}
}
}
/// A [triple pattern](https://www.w3.org/TR/sparql11-query/#defn_TriplePattern) without blank nodes
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct GroundTriplePattern {
pub subject: GroundTermPattern,
pub predicate: NamedNodePattern,
pub object: GroundTermPattern,
}
impl fmt::Display for GroundTriplePattern {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {} {}", self.subject, self.predicate, self.object)
}
}
impl From<GroundTriple> for GroundTriplePattern {
#[inline]
fn from(triple: GroundTriple) -> Self {
Self {
subject: triple.subject.into(),
predicate: triple.predicate.into(),
object: triple.object.into(),
}
}
}
impl From<Box<GroundTriple>> for GroundTriplePattern {
#[inline]
fn from(triple: Box<GroundTriple>) -> Self {
Self {
subject: triple.subject.into(),
predicate: triple.predicate.into(),
object: triple.object.into(),
}
}
}
impl TryFrom<TriplePattern> for GroundTriplePattern {
type Error = ();
#[inline]
fn try_from(triple: TriplePattern) -> Result<Self, Self::Error> {
Ok(Self {
subject: triple.subject.try_into()?,
predicate: triple.predicate,
object: triple.object.try_into()?,
})
}
}
impl TryFrom<Box<TriplePattern>> for GroundTriplePattern {
type Error = ();
#[inline]
fn try_from(triple: Box<TriplePattern>) -> Result<Self, Self::Error> {
Ok(Self {
subject: triple.subject.try_into()?,
predicate: triple.predicate,
object: triple.object.try_into()?,
})
} }
} }
@ -644,15 +1136,11 @@ impl fmt::Display for QuadPattern {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.graph_name == GraphNamePattern::DefaultGraph { if self.graph_name == GraphNamePattern::DefaultGraph {
write!( write!(f, "{} {} {}", self.subject, self.predicate, self.object)
f,
"(triple {} {} {})",
self.subject, self.predicate, self.object
)
} else { } else {
write!( write!(
f, f,
"(graph {} (triple {} {} {}))", "GRAPH {} {{ {} {} {} }}",
self.graph_name, self.subject, self.predicate, self.object self.graph_name, self.subject, self.predicate, self.object
) )
} }
@ -672,21 +1160,31 @@ impl fmt::Display for GroundQuadPattern {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.graph_name == GraphNamePattern::DefaultGraph { if self.graph_name == GraphNamePattern::DefaultGraph {
write!( write!(f, "{} {} {}", self.subject, self.predicate, self.object)
f,
"(triple {} {} {})",
self.subject, self.predicate, self.object
)
} else { } else {
write!( write!(
f, f,
"(graph {} (triple {} {} {}))", "GRAPH {} {{ {} {} {} }}",
self.graph_name, self.subject, self.predicate, self.object self.graph_name, self.subject, self.predicate, self.object
) )
} }
} }
} }
impl TryFrom<QuadPattern> for GroundQuadPattern {
type Error = ();
#[inline]
fn try_from(pattern: QuadPattern) -> Result<Self, ()> {
Ok(GroundQuadPattern {
subject: pattern.subject.try_into()?,
predicate: pattern.predicate,
object: pattern.object.try_into()?,
graph_name: pattern.graph_name,
})
}
}
#[inline] #[inline]
pub(crate) fn print_quoted_str(string: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result { pub(crate) fn print_quoted_str(string: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_char('"')?; f.write_char('"')?;

@ -117,14 +117,14 @@ impl fmt::Display for GraphUpdateOperation {
if !delete.is_empty() { if !delete.is_empty() {
writeln!(f, "DELETE {{")?; writeln!(f, "DELETE {{")?;
for quad in delete { for quad in delete {
writeln!(f, "\t{}", SparqlGroundQuadPattern(quad))?; writeln!(f, "\t{} .", quad)?;
} }
writeln!(f, "}}")?; writeln!(f, "}}")?;
} }
if !insert.is_empty() { if !insert.is_empty() {
writeln!(f, "INSERT {{")?; writeln!(f, "INSERT {{")?;
for quad in insert { for quad in insert {
writeln!(f, "\t{}", SparqlQuadPattern(quad))?; writeln!(f, "\t{} .", quad)?;
} }
writeln!(f, "}}")?; writeln!(f, "}}")?;
} }

@ -134,14 +134,7 @@ fn sparql_star_query_syntax_testsuite() -> Result<()> {
run_testsuite( run_testsuite(
"https://w3c.github.io/rdf-star/tests/sparql/syntax/manifest.ttl", "https://w3c.github.io/rdf-star/tests/sparql/syntax/manifest.ttl",
vec![ vec![
// SPARQL* is not implemented yet // Annotation syntax is not implemented yet
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-1",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-2",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-3",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-4",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-5",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-6",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-7",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-ann-01", "https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-ann-01",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-ann-02", "https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-ann-02",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-ann-03", "https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-ann-03",
@ -151,18 +144,6 @@ fn sparql_star_query_syntax_testsuite() -> Result<()> {
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-ann-07", "https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-ann-07",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-ann-08", "https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-ann-08",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-ann-09", "https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-ann-09",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-bnode-1",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-bnode-2",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-bnode-3",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-compound-1",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-expr-1",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-expr-2",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-expr-6",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-inside-1",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-inside-2",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-nested-1",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-nested-2",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-update-1",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-update-2", "https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-update-2",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-update-3", "https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-update-3",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-update-4", "https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-update-4",
@ -170,6 +151,10 @@ fn sparql_star_query_syntax_testsuite() -> Result<()> {
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-update-6", "https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-update-6",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-update-7", "https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-update-7",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-update-8", "https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-update-8",
// Not covered by grammar yet
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-expr-1",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-expr-2",
"https://w3c.github.io/rdf-star/tests/sparql/syntax#sparql-star-expr-6",
], ],
) )
} }

Loading…
Cancel
Save