diff --git a/lib/src/model/triple.rs b/lib/src/model/triple.rs index 3fa60d58..786c436c 100644 --- a/lib/src/model/triple.rs +++ b/lib/src/model/triple.rs @@ -129,11 +129,7 @@ impl fmt::Display for SubjectRef<'_> { match self { Self::NamedNode(node) => node.fmt(f), Self::BlankNode(node) => node.fmt(f), - Self::Triple(triple) => write!( - f, - "<<{} {} {}>>", - triple.subject, triple.predicate, triple.object - ), + Self::Triple(triple) => write!(f, "<<{}>>", triple), } } } @@ -371,11 +367,7 @@ impl fmt::Display for TermRef<'_> { Self::BlankNode(node) => node.fmt(f), Self::Literal(literal) => literal.fmt(f), Self::Triple(triple) => { - write!( - f, - "<<{} {} {}>>", - triple.subject, triple.predicate, triple.object - ) + write!(f, "<<{}>>", triple) } } } @@ -583,7 +575,7 @@ impl<'a> TripleRef<'a> { impl fmt::Display for TripleRef<'_> { #[inline] 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<'_> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - rio::Quad::from(*self).fmt(f) + write!(f, "{} {} {}", self.subject, self.predicate, self.object) } } diff --git a/lib/src/sparql/plan_builder.rs b/lib/src/sparql/plan_builder.rs index f30c1b11..4f24f0f7 100644 --- a/lib/src/sparql/plan_builder.rs +++ b/lib/src/sparql/plan_builder.rs @@ -3,7 +3,7 @@ use crate::sparql::dataset::DatasetView; use crate::sparql::error::EvaluationError; use crate::sparql::model::Variable as OxVariable; use crate::sparql::plan::*; -use crate::storage::numeric_encoder::{EncodedTerm, WriteEncoder}; +use crate::storage::numeric_encoder::{EncodedTerm, EncodedTriple, WriteEncoder}; use rand::random; use spargebra::algebra::*; use spargebra::term::*; @@ -807,6 +807,7 @@ impl<'a> PlanBuilder<'a> { //TODO: very bad hack to convert bnode to variable } TermPattern::Literal(literal) => PatternValue::Constant(self.build_literal(literal)?), + TermPattern::Triple(_) => unimplemented!(), }) } @@ -845,6 +846,7 @@ impl<'a> PlanBuilder<'a> { match term { GroundTerm::NamedNode(node) => self.build_named_node(node), 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) => { 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 { + 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) -> usize { diff --git a/lib/src/sparql/update.rs b/lib/src/sparql/update.rs index 60c2b290..78edce17 100644 --- a/lib/src/sparql/update.rs +++ b/lib/src/sparql/update.rs @@ -10,7 +10,8 @@ use crate::sparql::plan_builder::PlanBuilder; use crate::sparql::{EvaluationError, UpdateOptions}; use crate::storage::io::load_graph; 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 http::header::{ACCEPT, CONTENT_TYPE, USER_AGENT}; @@ -18,9 +19,10 @@ use http::{Method, Request, StatusCode}; use oxiri::Iri; use spargebra::algebra::{GraphPattern, GraphTarget}; use spargebra::term::{ - BlankNode, GraphName, GraphNamePattern, GroundQuad, GroundQuadPattern, GroundTerm, - GroundTermPattern, Literal, NamedNode, NamedNodePattern, Quad, QuadPattern, Subject, Term, - TermPattern, Variable, + BlankNode, GraphName, GraphNamePattern, GroundQuad, GroundQuadPattern, GroundSubject, + GroundTerm, GroundTermPattern, GroundTriple, GroundTriplePattern, Literal, NamedNode, + NamedNodePattern, Quad, QuadPattern, Subject, Term, TermPattern, Triple, TriplePattern, + Variable, }; use spargebra::GraphUpdateOperation; use std::collections::HashMap; @@ -93,9 +95,8 @@ impl<'a> SimpleUpdateEvaluator<'a> { fn eval_insert_data(&mut self, data: &[Quad]) -> Result<(), EvaluationError> { let mut bnodes = HashMap::new(); for quad in data { - if let Some(quad) = self.encode_quad_for_insertion(quad, &mut bnodes)? { - self.storage.insert(&quad)?; - } + let quad = self.encode_quad_for_insertion(quad, &mut bnodes)?; + self.storage.insert(&quad)?; } Ok(()) } @@ -298,13 +299,16 @@ impl<'a> SimpleUpdateEvaluator<'a> { &mut self, quad: &Quad, bnodes: &mut HashMap, - ) -> Result, EvaluationError> { - Ok(Some(EncodedQuad { + ) -> Result { + Ok(EncodedQuad { subject: match &quad.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 @@ -315,6 +319,7 @@ impl<'a> SimpleUpdateEvaluator<'a> { .storage .encode_blank_node(bnodes.entry(object.clone()).or_default().as_ref())?, 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 { GraphName::NamedNode(graph_name) => { @@ -322,7 +327,7 @@ impl<'a> SimpleUpdateEvaluator<'a> { } GraphName::DefaultGraph => EncodedTerm::DefaultGraph, }, - })) + }) } 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())?, ), 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), }) } @@ -420,6 +428,46 @@ impl<'a> SimpleUpdateEvaluator<'a> { }) } + fn encode_triple_pattern_for_insertion( + &mut self, + triple: &TriplePattern, + variables: &[Variable], + values: &[Option], + bnodes: &mut HashMap, + ) -> Result, 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( &self, v: &Variable, @@ -448,6 +496,35 @@ impl<'a> SimpleUpdateEvaluator<'a> { .encode_named_node(NamedNodeRef::new_unchecked(&term.iri))?) } + fn encode_triple_for_insertion( + &mut self, + triple: &Triple, + bnodes: &mut HashMap, + ) -> Result { + 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( &mut self, term: &Literal, @@ -465,11 +542,15 @@ impl<'a> SimpleUpdateEvaluator<'a> { fn encode_quad_for_deletion(&mut self, quad: &GroundQuad) -> 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), object: match &quad.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), }, graph_name: match &quad.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::Literal(term) => Some(self.encode_literal_for_deletion(term)), 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], + ) -> Option { + 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 { 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() + } } diff --git a/spargebra/README.md b/spargebra/README.md index 519209c7..8854f3a1 100644 --- a/spargebra/README.md +++ b/spargebra/README.md @@ -9,7 +9,7 @@ Spargebra 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). diff --git a/spargebra/src/algebra.rs b/spargebra/src/algebra.rs index 46f0adfb..64c4c87f 100644 --- a/spargebra/src/algebra.rs +++ b/spargebra/src/algebra.rs @@ -5,58 +5,6 @@ use crate::term::*; use std::collections::BTreeSet; 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) #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum PropertyPathExpression { @@ -563,7 +511,7 @@ impl fmt::Display for GraphPattern { GraphPattern::Bgp(p) => { write!(f, "(bgp")?; for pattern in p { - write!(f, " {}", pattern)?; + write!(f, " {} .", pattern)?; } write!(f, ")") } @@ -754,7 +702,7 @@ impl<'a> fmt::Display for SparqlGraphPattern<'a> { match self.0 { GraphPattern::Bgp(p) => { for pattern in p { - write!(f, "{}", SparqlTriplePattern(pattern))? + write!(f, "{} .", pattern)? } Ok(()) } diff --git a/spargebra/src/lib.rs b/spargebra/src/lib.rs index 77c3f693..8f1b5613 100644 --- a/spargebra/src/lib.rs +++ b/spargebra/src/lib.rs @@ -10,7 +10,7 @@ //! use spargebra::Query; //! //! 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); //! # Result::Ok::<_, spargebra::ParseError>(()) //! ``` @@ -24,6 +24,7 @@ unsafe_code, unused_qualifications )] +#![doc(test(attr(deny(warnings))))] pub mod algebra; mod parser; diff --git a/spargebra/src/parser.rs b/spargebra/src/parser.rs index e84cc04a..a283605f 100644 --- a/spargebra/src/parser.rs +++ b/spargebra/src/parser.rs @@ -9,6 +9,7 @@ use peg::str::LineCol; use rand::random; use std::borrow::Cow; use std::collections::{HashMap, HashSet}; +use std::convert::{TryFrom, TryInto}; use std::error::Error; use std::str::Chars; 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) }, } }).fold(GraphPattern::Bgp(Vec::new()), new_join); - let delete = d.into_iter().map(|q| Ok(GroundQuadPattern { - 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::,_>>()?; + let delete = d.into_iter().map(GroundQuadPattern::try_from).collect::,_>>().map_err(|_| "Blank nodes are not allowed in DELETE WHERE")?; Ok(vec![GraphUpdateOperation::DeleteInsert { delete, insert: Vec::new(), @@ -1133,22 +1119,7 @@ parser! { //[42] rule DeleteClause() -> Vec = i("DELETE") _ q:QuadPattern() {? - q.into_iter().map(|q| Ok(GroundQuadPattern { - 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::,_>>() + q.into_iter().map(GroundQuadPattern::try_from).collect::,_>>().map_err(|_| "Blank nodes are not allowed in DELETE WHERE") } //[43] @@ -1184,53 +1155,10 @@ parser! { //[49] rule QuadData() -> Vec = "{" _ q:Quads() _ "}" {? - q.into_iter().map(|q| Ok(Quad { - 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::, ()>>().map_err(|_| "Variables are not allowed in INSERT DATA and DELETE DATA") + q.into_iter().map(Quad::try_from).collect::, ()>>().map_err(|_| "Variables are not allowed in INSERT DATA") } rule GroundQuadData() -> Vec = "{" _ q:Quads() _ "}" {? - q.into_iter().map(|q| Ok(GroundQuad { - 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::, ()>>().map_err(|_| "Variables are not allowed in INSERT DATA and DELETE DATA") + q.into_iter().map(|q| GroundQuad::try_from(Quad::try_from(q)?)).collect::, ()>>().map_err(|_| "Variables and blank nodes are not allowed in DELETE DATA") } //[50] @@ -1373,6 +1301,7 @@ parser! { //[65] rule DataBlockValue() -> Option = + t:EmbTriple() { Some(t.into()) } / i:iri() { Some(i.into()) } / l:RDFLiteral() { Some(l.into()) } / l:NumericLiteral() { Some(l.into()) } / @@ -1428,7 +1357,7 @@ parser! { //[75] rule TriplesSameSubject() -> Vec = - s:VarOrTerm() _ po:PropertyListNotEmpty() { + s:VarOrTermOrEmbTP() _ po:PropertyListNotEmpty() { let mut patterns = po.patterns; for (p, os) in po.focus { for o in os { @@ -1486,7 +1415,7 @@ parser! { //[81] rule TriplesSameSubjectPath() -> Vec = - s:VarOrTerm() _ po:PropertyListPathNotEmpty() { + s:VarOrTermOrEmbTP() _ po:PropertyListPathNotEmpty() { let mut patterns = po.patterns; for (p, os) in po.focus { for o in os { @@ -1704,12 +1633,12 @@ parser! { //[104] rule GraphNode() -> FocusedTriplePattern = - t:VarOrTerm() { FocusedTriplePattern::new(t) } / + t:VarOrTermOrEmbTP() { FocusedTriplePattern::new(t) } / TriplesNode() //[105] rule GraphNodePath() -> FocusedTripleOrPathPattern = - t:VarOrTerm() { FocusedTripleOrPathPattern::new(t) } / + t:VarOrTermOrEmbTP() { FocusedTripleOrPathPattern::new(t) } / TriplesNodePath() //[106] @@ -2128,6 +2057,43 @@ parser! { //[173] 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 rule _() = quiet! { ([' ' | '\t' | '\n' | '\r'] / comment())* } diff --git a/spargebra/src/query.rs b/spargebra/src/query.rs index 1edc177e..c396d235 100644 --- a/spargebra/src/query.rs +++ b/spargebra/src/query.rs @@ -12,7 +12,7 @@ use std::str::FromStr; /// use spargebra::Query; /// /// 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); /// # Result::Ok::<_, spargebra::ParseError>(()) /// ``` @@ -96,7 +96,7 @@ impl fmt::Display for Query { } write!(f, "CONSTRUCT {{ ")?; for triple in template.iter() { - write!(f, "{} ", SparqlTriplePattern(triple))?; + write!(f, "{} . ", triple)?; } write!(f, "}}")?; if let Some(dataset) = dataset { diff --git a/spargebra/src/term.rs b/spargebra/src/term.rs index ca5992f5..2a8cdfa3 100644 --- a/spargebra/src/term.rs +++ b/spargebra/src/term.rs @@ -1,5 +1,6 @@ //! 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::Write; @@ -28,6 +29,18 @@ impl fmt::Display for NamedNode { } } +impl TryFrom for NamedNode { + type Error = (); + + #[inline] + fn try_from(pattern: NamedNodePattern) -> Result { + match pattern { + NamedNodePattern::NamedNode(t) => Ok(t), + NamedNodePattern::Variable(_) => Err(()), + } + } +} + /// 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 { NamedNode(NamedNode), BlankNode(BlankNode), + Triple(Box), } impl fmt::Display for Subject { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Subject::NamedNode(node) => node.fmt(f), - Subject::BlankNode(node) => node.fmt(f), + Self::NamedNode(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 for Subject { } } +impl From for Subject { + #[inline] + fn from(triple: Triple) -> Self { + Self::Triple(Box::new(triple)) + } +} + +impl From> for Subject { + #[inline] + fn from(triple: Box) -> Self { + Self::Triple(triple) + } +} + +impl TryFrom for Subject { + type Error = (); + + #[inline] + fn try_from(term: TermPattern) -> Result { + 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), +} + +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 for GroundSubject { + #[inline] + fn from(node: NamedNode) -> Self { + Self::NamedNode(node) + } +} + +impl From for GroundSubject { + #[inline] + fn from(triple: GroundTriple) -> Self { + Self::Triple(Box::new(triple)) + } +} + +impl From> for GroundSubject { + #[inline] + fn from(triple: Box) -> Self { + Self::Triple(triple) + } +} + +impl TryFrom for GroundSubject { + type Error = (); + + #[inline] + fn try_from(subject: Subject) -> Result { + match subject { + Subject::NamedNode(t) => Ok(t.into()), + Subject::BlankNode(_) => Err(()), + Subject::Triple(t) => Ok(GroundTriple::try_from(t)?.into()), + } + } +} + +impl TryFrom for GroundSubject { + type Error = (); + + #[inline] + fn try_from(term: GroundTerm) -> Result { + 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). /// /// 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), BlankNode(BlankNode), Literal(Literal), + Triple(Box), } impl fmt::Display for Term { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Term::NamedNode(node) => node.fmt(f), - Term::BlankNode(node) => node.fmt(f), - Term::Literal(literal) => literal.fmt(f), + Self::NamedNode(node) => node.fmt(f), + Self::BlankNode(node) => node.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 for Term { } } +impl From for Term { + #[inline] + fn from(triple: Triple) -> Self { + Self::Triple(Box::new(triple)) + } +} + +impl From> for Term { + #[inline] + fn from(triple: Box) -> Self { + Self::Triple(triple) + } +} + impl From for Term { #[inline] fn from(resource: Subject) -> Self { match resource { - Subject::NamedNode(node) => Self::NamedNode(node), - Subject::BlankNode(node) => Self::BlankNode(node), + Subject::NamedNode(node) => node.into(), + 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 for Term { + type Error = (); + + #[inline] + fn try_from(pattern: TermPattern) -> Result { + 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. #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum GroundTerm { NamedNode(NamedNode), Literal(Literal), + Triple(Box), } impl fmt::Display for GroundTerm { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - GroundTerm::NamedNode(node) => node.fmt(f), - GroundTerm::Literal(literal) => literal.fmt(f), + Self::NamedNode(node) => node.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 for GroundTerm { } } +impl From for GroundTerm { + #[inline] + fn from(triple: GroundTriple) -> Self { + Self::Triple(Box::new(triple)) + } +} + +impl From> for GroundTerm { + #[inline] + fn from(triple: Box) -> Self { + Self::Triple(triple) + } +} + +impl TryFrom for GroundTerm { + type Error = (); + + #[inline] + fn try_from(term: Term) -> Result { + 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!( +/// " ", +/// 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 for Triple { + type Error = (); + + #[inline] + fn try_from(triple: TriplePattern) -> Result { + Ok(Self { + subject: triple.subject.try_into()?, + predicate: triple.predicate.try_into()?, + object: triple.object.try_into()?, + }) + } +} + +impl TryFrom> for Triple { + type Error = (); + + #[inline] + fn try_from(triple: Box) -> Result { + 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!( +/// " ", +/// 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 for GroundTriple { + type Error = (); + + #[inline] + fn try_from(triple: Triple) -> Result { + Ok(Self { + subject: triple.subject.try_into()?, + predicate: triple.predicate, + object: triple.object.try_into()?, + }) + } +} + +impl TryFrom> for GroundTriple { + type Error = (); + + #[inline] + fn try_from(triple: Box) -> Result { + Ok(Self { + subject: triple.subject.try_into()?, + predicate: triple.predicate, + object: triple.object.try_into()?, + }) + } +} + /// 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). @@ -252,8 +553,8 @@ impl fmt::Display for GraphName { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - GraphName::NamedNode(node) => node.fmt(f), - GraphName::DefaultGraph => write!(f, "DEFAULT"), + Self::NamedNode(node) => node.fmt(f), + Self::DefaultGraph => write!(f, "DEFAULT"), } } } @@ -265,6 +566,19 @@ impl From for GraphName { } } +impl TryFrom for GraphName { + type Error = (); + + #[inline] + fn try_from(pattern: GraphNamePattern) -> Result { + 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). /// /// The default string formatter is returning a N-Quads representation. @@ -274,7 +588,7 @@ impl From for GraphName { /// use spargebra::term::Quad; /// /// assert_eq!( -/// " .", +/// " ", /// Quad { /// subject: NamedNode { iri: "http://example.com/foo".into() }.into(), /// predicate: NamedNode { iri: "http://schema.org/sameAs".into() }, @@ -295,17 +609,31 @@ impl fmt::Display for Quad { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.graph_name == GraphName::DefaultGraph { - write!(f, "{} {} {} .", self.subject, self.predicate, self.object) + write!(f, "{} {} {}", self.subject, self.predicate, self.object) } else { write!( f, - "{} {} {} {} .", + "{} {} {} {}", self.subject, self.predicate, self.object, self.graph_name ) } } } +impl TryFrom for Quad { + type Error = (); + + #[inline] + fn try_from(quad: QuadPattern) -> Result { + 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. /// /// The default string formatter is returning a N-Quads representation. @@ -315,7 +643,7 @@ impl fmt::Display for Quad { /// use spargebra::term::GroundQuad; /// /// assert_eq!( -/// " .", +/// " ", /// GroundQuad { /// subject: NamedNode { iri: "http://example.com/foo".into() }.into(), /// predicate: NamedNode { iri: "http://schema.org/sameAs".into() }, @@ -326,7 +654,7 @@ impl fmt::Display for Quad { /// ``` #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct GroundQuad { - pub subject: NamedNode, + pub subject: GroundSubject, pub predicate: NamedNode, pub object: GroundTerm, pub graph_name: GraphName, @@ -336,17 +664,31 @@ impl fmt::Display for GroundQuad { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.graph_name == GraphName::DefaultGraph { - write!(f, "{} {} {} .", self.subject, self.predicate, self.object) + write!(f, "{} {} {}", self.subject, self.predicate, self.object) } else { write!( f, - "{} {} {} {} .", + "{} {} {} {}", self.subject, self.predicate, self.object, self.graph_name ) } } } +impl TryFrom for GroundQuad { + type Error = (); + + #[inline] + fn try_from(quad: Quad) -> Result { + 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). /// /// ``` @@ -380,8 +722,8 @@ impl fmt::Display for NamedNodePattern { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - NamedNodePattern::NamedNode(node) => node.fmt(f), - NamedNodePattern::Variable(var) => var.fmt(f), + Self::NamedNode(node) => node.fmt(f), + Self::Variable(var) => var.fmt(f), } } } @@ -406,6 +748,7 @@ pub enum TermPattern { NamedNode(NamedNode), BlankNode(BlankNode), Literal(Literal), + Triple(Box), Variable(Variable), } @@ -413,10 +756,11 @@ impl fmt::Display for TermPattern { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - TermPattern::NamedNode(term) => term.fmt(f), - TermPattern::BlankNode(term) => term.fmt(f), - TermPattern::Literal(term) => term.fmt(f), - TermPattern::Variable(var) => var.fmt(f), + Self::NamedNode(term) => term.fmt(f), + Self::BlankNode(term) => term.fmt(f), + Self::Literal(term) => term.fmt(f), + Self::Triple(triple) => write!(f, "<<{}>>", triple), + Self::Variable(var) => var.fmt(f), } } } @@ -442,19 +786,45 @@ impl From for TermPattern { } } +impl From for TermPattern { + #[inline] + fn from(triple: TriplePattern) -> Self { + Self::Triple(Box::new(triple)) + } +} + +impl From> for TermPattern { + #[inline] + fn from(triple: Box) -> Self { + Self::Triple(triple) + } +} + impl From for TermPattern { fn from(var: Variable) -> Self { Self::Variable(var) } } +impl From 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 for TermPattern { #[inline] fn from(term: Term) -> Self { match term { - Term::NamedNode(node) => Self::NamedNode(node), - Term::BlankNode(node) => Self::BlankNode(node), - Term::Literal(literal) => Self::Literal(literal), + Term::NamedNode(node) => node.into(), + Term::BlankNode(node) => node.into(), + Term::Literal(literal) => literal.into(), + Term::Triple(t) => TriplePattern::from(t).into(), } } } @@ -463,8 +833,8 @@ impl From for TermPattern { #[inline] fn from(element: NamedNodePattern) -> Self { match element { - NamedNodePattern::NamedNode(node) => Self::NamedNode(node), - NamedNodePattern::Variable(var) => Self::Variable(var), + NamedNodePattern::NamedNode(node) => node.into(), + NamedNodePattern::Variable(var) => var.into(), } } } @@ -475,15 +845,17 @@ pub enum GroundTermPattern { NamedNode(NamedNode), Literal(Literal), Variable(Variable), + Triple(Box), } impl fmt::Display for GroundTermPattern { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - GroundTermPattern::NamedNode(term) => term.fmt(f), - GroundTermPattern::Literal(term) => term.fmt(f), - GroundTermPattern::Variable(var) => var.fmt(f), + Self::NamedNode(term) => term.fmt(f), + Self::Literal(term) => term.fmt(f), + Self::Variable(var) => var.fmt(f), + Self::Triple(triple) => write!(f, "<<{}>>", triple), } } } @@ -502,6 +874,20 @@ impl From for GroundTermPattern { } } +impl From for GroundTermPattern { + #[inline] + fn from(triple: GroundTriplePattern) -> Self { + Self::Triple(Box::new(triple)) + } +} + +impl From> for GroundTermPattern { + #[inline] + fn from(triple: Box) -> Self { + Self::Triple(triple) + } +} + impl From for GroundTermPattern { #[inline] fn from(var: Variable) -> Self { @@ -509,12 +895,22 @@ impl From for GroundTermPattern { } } +impl From for GroundTermPattern { + #[inline] + fn from(term: GroundSubject) -> Self { + match term { + GroundSubject::NamedNode(node) => node.into(), + GroundSubject::Triple(triple) => GroundTriplePattern::from(triple).into(), + } + } +} impl From for GroundTermPattern { #[inline] fn from(term: GroundTerm) -> Self { match term { - GroundTerm::NamedNode(node) => Self::NamedNode(node), - GroundTerm::Literal(literal) => Self::Literal(literal), + GroundTerm::NamedNode(node) => node.into(), + GroundTerm::Literal(literal) => literal.into(), + GroundTerm::Triple(triple) => GroundTriplePattern::from(triple).into(), } } } @@ -523,12 +919,27 @@ impl From for GroundTermPattern { #[inline] fn from(element: NamedNodePattern) -> Self { match element { - NamedNodePattern::NamedNode(node) => Self::NamedNode(node), - NamedNodePattern::Variable(var) => Self::Variable(var), + NamedNodePattern::NamedNode(node) => node.into(), + NamedNodePattern::Variable(var) => var.into(), } } } +impl TryFrom for GroundTermPattern { + type Error = (); + + #[inline] + fn try_from(pattern: TermPattern) -> Result { + 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). #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum GraphNamePattern { @@ -541,9 +952,9 @@ impl fmt::Display for GraphNamePattern { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - GraphNamePattern::NamedNode(node) => node.fmt(f), - GraphNamePattern::DefaultGraph => f.write_str("DEFAULT"), - GraphNamePattern::Variable(var) => var.fmt(f), + Self::NamedNode(node) => node.fmt(f), + Self::DefaultGraph => f.write_str("DEFAULT"), + Self::Variable(var) => var.fmt(f), } } } @@ -566,7 +977,7 @@ impl From for GraphNamePattern { #[inline] fn from(graph_name: GraphName) -> Self { match graph_name { - GraphName::NamedNode(node) => Self::NamedNode(node), + GraphName::NamedNode(node) => node.into(), GraphName::DefaultGraph => Self::DefaultGraph, } } @@ -576,8 +987,8 @@ impl From for GraphNamePattern { #[inline] fn from(graph_name: NamedNodePattern) -> Self { match graph_name { - NamedNodePattern::NamedNode(node) => Self::NamedNode(node), - NamedNodePattern::Variable(var) => Self::Variable(var), + NamedNodePattern::NamedNode(node) => node.into(), + NamedNodePattern::Variable(var) => var.into(), } } } @@ -607,11 +1018,92 @@ impl TriplePattern { impl fmt::Display for TriplePattern { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "(triple {} {} {})", - self.subject, self.predicate, self.object - ) + write!(f, "{} {} {}", self.subject, self.predicate, self.object) + } +} + +impl From for TriplePattern { + #[inline] + fn from(triple: Triple) -> Self { + Self { + subject: triple.subject.into(), + predicate: triple.predicate.into(), + object: triple.object.into(), + } + } +} + +impl From> for TriplePattern { + #[inline] + fn from(triple: Box) -> 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 for GroundTriplePattern { + #[inline] + fn from(triple: GroundTriple) -> Self { + Self { + subject: triple.subject.into(), + predicate: triple.predicate.into(), + object: triple.object.into(), + } + } +} + +impl From> for GroundTriplePattern { + #[inline] + fn from(triple: Box) -> Self { + Self { + subject: triple.subject.into(), + predicate: triple.predicate.into(), + object: triple.object.into(), + } + } +} + +impl TryFrom for GroundTriplePattern { + type Error = (); + + #[inline] + fn try_from(triple: TriplePattern) -> Result { + Ok(Self { + subject: triple.subject.try_into()?, + predicate: triple.predicate, + object: triple.object.try_into()?, + }) + } +} + +impl TryFrom> for GroundTriplePattern { + type Error = (); + + #[inline] + fn try_from(triple: Box) -> Result { + 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] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.graph_name == GraphNamePattern::DefaultGraph { - write!( - f, - "(triple {} {} {})", - self.subject, self.predicate, self.object - ) + write!(f, "{} {} {}", self.subject, self.predicate, self.object) } else { write!( f, - "(graph {} (triple {} {} {}))", + "GRAPH {} {{ {} {} {} }}", self.graph_name, self.subject, self.predicate, self.object ) } @@ -672,21 +1160,31 @@ impl fmt::Display for GroundQuadPattern { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.graph_name == GraphNamePattern::DefaultGraph { - write!( - f, - "(triple {} {} {})", - self.subject, self.predicate, self.object - ) + write!(f, "{} {} {}", self.subject, self.predicate, self.object) } else { write!( f, - "(graph {} (triple {} {} {}))", + "GRAPH {} {{ {} {} {} }}", self.graph_name, self.subject, self.predicate, self.object ) } } } +impl TryFrom for GroundQuadPattern { + type Error = (); + + #[inline] + fn try_from(pattern: QuadPattern) -> Result { + Ok(GroundQuadPattern { + subject: pattern.subject.try_into()?, + predicate: pattern.predicate, + object: pattern.object.try_into()?, + graph_name: pattern.graph_name, + }) + } +} + #[inline] pub(crate) fn print_quoted_str(string: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_char('"')?; diff --git a/spargebra/src/update.rs b/spargebra/src/update.rs index 04318861..f4ac68bc 100644 --- a/spargebra/src/update.rs +++ b/spargebra/src/update.rs @@ -117,14 +117,14 @@ impl fmt::Display for GraphUpdateOperation { if !delete.is_empty() { writeln!(f, "DELETE {{")?; for quad in delete { - writeln!(f, "\t{}", SparqlGroundQuadPattern(quad))?; + writeln!(f, "\t{} .", quad)?; } writeln!(f, "}}")?; } if !insert.is_empty() { writeln!(f, "INSERT {{")?; for quad in insert { - writeln!(f, "\t{}", SparqlQuadPattern(quad))?; + writeln!(f, "\t{} .", quad)?; } writeln!(f, "}}")?; } diff --git a/testsuite/tests/sparql.rs b/testsuite/tests/sparql.rs index 67bcb75e..c7346722 100644 --- a/testsuite/tests/sparql.rs +++ b/testsuite/tests/sparql.rs @@ -134,14 +134,7 @@ fn sparql_star_query_syntax_testsuite() -> Result<()> { run_testsuite( "https://w3c.github.io/rdf-star/tests/sparql/syntax/manifest.ttl", vec![ - // SPARQL* 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", + // Annotation syntax is not implemented yet "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-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-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-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-3", "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-7", "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", ], ) }