diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 003ba740..2fc958b5 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -44,7 +44,7 @@ http = "0.2" httparse = { version = "1", optional = true } native-tls = { version = "0.2", optional = true } json-event-parser = "0.1" -spargebra = { version = "0.1", path="../spargebra" } +spargebra = { version = "0.1", path="../spargebra", features = ["rdf-star"] } [dev-dependencies] diff --git a/spargebra/Cargo.toml b/spargebra/Cargo.toml index 5935ff2c..35af6c72 100644 --- a/spargebra/Cargo.toml +++ b/spargebra/Cargo.toml @@ -12,8 +12,15 @@ A SPARQL parser """ edition = "2018" +[features] +default = [] +rdf-star = [] + [dependencies] peg = "0.7" rand = "0.8" oxiri = "0.1" oxilangtag = "0.1" + +[package.metadata.docs.rs] +all-features = true diff --git a/spargebra/README.md b/spargebra/README.md index 8854f3a1..85d32777 100644 --- a/spargebra/README.md +++ b/spargebra/README.md @@ -9,7 +9,9 @@ Spargebra Spargebra is a [SPARQL](https://www.w3.org/TR/sparql11-overview/) parser. -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/). +It supports [SPARQL 1.1 Query](https://www.w3.org/TR/sparql11-query/) and [SPARQL 1.1 Update](https://www.w3.org/TR/sparql11-update/). + +Support for [SPARQL-star](https://w3c.github.io/rdf-star/cg-spec/#sparql-star) is also available behind the `rdf-star` feature. 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 5714eb16..f375c07f 100644 --- a/spargebra/src/algebra.rs +++ b/spargebra/src/algebra.rs @@ -361,10 +361,15 @@ pub enum Function { IsLiteral, IsNumeric, Regex, + #[cfg(feature = "rdf-star")] Triple, + #[cfg(feature = "rdf-star")] Subject, + #[cfg(feature = "rdf-star")] Predicate, + #[cfg(feature = "rdf-star")] Object, + #[cfg(feature = "rdf-star")] IsTriple, Custom(NamedNode), } @@ -418,10 +423,15 @@ impl fmt::Display for Function { Function::IsLiteral => write!(f, "isLITERAL"), Function::IsNumeric => write!(f, "isNUMERIC"), Function::Regex => write!(f, "REGEX"), + #[cfg(feature = "rdf-star")] Function::Triple => write!(f, "TRIPLE"), + #[cfg(feature = "rdf-star")] Function::Subject => write!(f, "SUBJECT"), + #[cfg(feature = "rdf-star")] Function::Predicate => write!(f, "PREDICATE"), + #[cfg(feature = "rdf-star")] Function::Object => write!(f, "OBJECT"), + #[cfg(feature = "rdf-star")] Function::IsTriple => write!(f, "isTRIPLE"), Function::Custom(iri) => iri.fmt(f), } @@ -656,12 +666,16 @@ impl GraphPattern { } => { if let TermPattern::Variable(s) = subject { vars.insert(s); - } else if let TermPattern::Triple(s) = subject { + } + #[cfg(feature = "rdf-star")] + if let TermPattern::Triple(s) = subject { add_triple_pattern_variables(s, vars) } if let TermPattern::Variable(o) = object { vars.insert(o); - } else if let TermPattern::Triple(o) = object { + } + #[cfg(feature = "rdf-star")] + if let TermPattern::Triple(o) = object { add_triple_pattern_variables(o, vars) } } @@ -703,7 +717,9 @@ impl GraphPattern { fn add_triple_pattern_variables<'a>(pattern: &'a TriplePattern, vars: &mut BTreeSet<&'a Variable>) { if let TermPattern::Variable(s) = &pattern.subject { vars.insert(s); - } else if let TermPattern::Triple(s) = &pattern.subject { + } + #[cfg(feature = "rdf-star")] + if let TermPattern::Triple(s) = &pattern.subject { add_triple_pattern_variables(s, vars) } if let NamedNodePattern::Variable(p) = &pattern.predicate { @@ -711,7 +727,9 @@ fn add_triple_pattern_variables<'a>(pattern: &'a TriplePattern, vars: &mut BTree } if let TermPattern::Variable(o) = &pattern.object { vars.insert(o); - } else if let TermPattern::Triple(o) = &pattern.object { + } + #[cfg(feature = "rdf-star")] + if let TermPattern::Triple(o) = &pattern.object { add_triple_pattern_variables(o, vars) } } diff --git a/spargebra/src/lib.rs b/spargebra/src/lib.rs index 8f1b5613..3672f1b6 100644 --- a/spargebra/src/lib.rs +++ b/spargebra/src/lib.rs @@ -5,6 +5,8 @@ //! //! This crate is intended to be a building piece for SPARQL implementations in Rust like [Oxigraph](https://oxigraph.org). //! +//! Support for [SPARQL-star](https://w3c.github.io/rdf-star/cg-spec/) is available behind the `rdf-star` feature. +//! //! Usage example: //! ``` //! use spargebra::Query; diff --git a/spargebra/src/parser.rs b/spargebra/src/parser.rs index 441f31da..65ec9692 100644 --- a/spargebra/src/parser.rs +++ b/spargebra/src/parser.rs @@ -150,14 +150,20 @@ fn add_to_triple_patterns( predicate: NamedNodePattern, object: AnnotatedTerm, patterns: &mut Vec, -) { +) -> Result<(), &'static str> { let triple = TriplePattern::new(subject, predicate, object.term); + #[cfg(feature = "rdf-star")] for (p, os) in object.annotations { for o in os { - add_to_triple_patterns(triple.clone().into(), p.clone(), o, patterns) + add_to_triple_patterns(triple.clone().into(), p.clone(), o, patterns)? } } - patterns.push(triple) + #[cfg(not(feature = "rdf-star"))] + if !object.annotations.is_empty() { + return Err("Embedded triples are only available in SPARQL-star"); + } + patterns.push(triple); + Ok(()) } fn add_to_triple_or_path_patterns( @@ -229,11 +235,16 @@ fn add_triple_to_triple_or_path_patterns( patterns: &mut Vec, ) -> Result<(), &'static str> { let triple = TriplePattern::new(subject, predicate, object.term); + #[cfg(feature = "rdf-star")] for (p, os) in object.annotations { for o in os { add_to_triple_or_path_patterns(triple.clone().into(), p.clone(), o, patterns)? } } + #[cfg(not(feature = "rdf-star"))] + if !object.annotations.is_empty() { + return Err("Embedded triples are only available in SPARQL-star"); + } patterns.push(triple.into()); Ok(()) } @@ -1115,7 +1126,7 @@ parser! { //[40] rule DeleteWhere() -> Vec = i("DELETE") _ i("WHERE") _ d:QuadPattern() {? - let pattern = d.iter().map(|q| { + let pattern = d.iter().map(|q| { let bgp = GraphPattern::Bgp(vec![TriplePattern::new(q.subject.clone(), q.predicate.clone(), q.object.clone())]); match &q.graph_name { GraphNamePattern::NamedNode(graph_name) => GraphPattern::Graph { graph_name: graph_name.clone().into(), inner: Box::new(bgp) }, @@ -1384,7 +1395,10 @@ parser! { //[65] rule DataBlockValue() -> Option = - t:EmbTriple() { Some(t.into()) } / + t:EmbTriple() {? + #[cfg(feature = "rdf-star")]{Ok(Some(t.into()))} + #[cfg(not(feature = "rdf-star"))]{Err("Embedded triples are only available in SPARQL-star")} + } / i:iri() { Some(i.into()) } / l:RDFLiteral() { Some(l.into()) } / l:NumericLiteral() { Some(l.into()) } / @@ -1440,24 +1454,24 @@ parser! { //[75] rule TriplesSameSubject() -> Vec = - s:VarOrTermOrEmbTP() _ po:PropertyListNotEmpty() { + s:VarOrTermOrEmbTP() _ po:PropertyListNotEmpty() {? let mut patterns = po.patterns; for (p, os) in po.focus { for o in os { - add_to_triple_patterns(s.clone(), p.clone(), o, &mut patterns) + add_to_triple_patterns(s.clone(), p.clone(), o, &mut patterns)? } } - patterns + Ok(patterns) } / - s:TriplesNode() _ po:PropertyList() { + s:TriplesNode() _ po:PropertyList() {? let mut patterns = s.patterns; patterns.extend(po.patterns); for (p, os) in po.focus { for o in os { - add_to_triple_patterns(s.focus.clone(), p.clone(), o, &mut patterns) + add_to_triple_patterns(s.focus.clone(), p.clone(), o, &mut patterns)? } } - patterns + Ok(patterns) } //[76] @@ -1686,18 +1700,18 @@ parser! { rule TriplesNode() -> FocusedTriplePattern = Collection() / BlankNodePropertyList() //[99] - rule BlankNodePropertyList() -> FocusedTriplePattern = "[" _ po:PropertyListNotEmpty() _ "]" { + rule BlankNodePropertyList() -> FocusedTriplePattern = "[" _ po:PropertyListNotEmpty() _ "]" {? let mut patterns = po.patterns; let mut bnode = TermPattern::from(bnode()); for (p, os) in po.focus { for o in os { - add_to_triple_patterns(bnode.clone(), p.clone(), o, &mut patterns) + add_to_triple_patterns(bnode.clone(), p.clone(), o, &mut patterns)?; } } - FocusedTriplePattern { + Ok(FocusedTriplePattern { focus: bnode, patterns - } + }) } //[100] @@ -1926,11 +1940,26 @@ parser! { RegexExpression() / ExistsFunc() / NotExistsFunc() / - i("TRIPLE") "(" _ s:Expression() _ "," _ p:Expression() "," _ o:Expression() ")" { Expression::FunctionCall(Function::Triple, vec![s, p, o]) } / - i("SUBJECT") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Subject, vec![e]) } / - i("PREDICATE") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Predicate, vec![e]) } / - i("OBJECT") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Object, vec![e]) } / - i("isTriple") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::IsTriple, vec![e]) } + i("TRIPLE") "(" _ s:Expression() _ "," _ p:Expression() "," _ o:Expression() ")" {? + #[cfg(feature = "rdf-star")]{Ok(Expression::FunctionCall(Function::Triple, vec![s, p, o]))} + #[cfg(not(feature = "rdf-star"))]{Err("The TRIPLE function is only available in SPARQL-star")} + } / + i("SUBJECT") "(" _ e:Expression() _ ")" {? + #[cfg(feature = "rdf-star")]{Ok(Expression::FunctionCall(Function::Subject, vec![e]))} + #[cfg(not(feature = "rdf-star"))]{Err("The SUBJECT function is only available in SPARQL-star")} + } / + i("PREDICATE") "(" _ e:Expression() _ ")" {? + #[cfg(feature = "rdf-star")]{Ok(Expression::FunctionCall(Function::Predicate, vec![e]))} + #[cfg(not(feature = "rdf-star"))]{Err("The PREDICATE function is only available in SPARQL-star")} + } / + i("OBJECT") "(" _ e:Expression() _ ")" {? + #[cfg(feature = "rdf-star")]{Ok(Expression::FunctionCall(Function::Object, vec![e]))} + #[cfg(not(feature = "rdf-star"))]{Err("The OBJECT function is only available in SPARQL-star")} + } / + i("isTriple") "(" _ e:Expression() _ ")" {? + #[cfg(feature = "rdf-star")]{Ok(Expression::FunctionCall(Function::IsTriple, vec![e]))} + #[cfg(not(feature = "rdf-star"))]{Err("The isTriple function is only available in SPARQL-star")} + } //[122] rule RegexExpression() -> Expression = @@ -2198,7 +2227,10 @@ parser! { //[176] rule EmbSubjectOrObject() -> TermPattern = - t:EmbTP() { t.into() } / + t:EmbTP() {? + #[cfg(feature = "rdf-star")]{Ok(t.into())} + #[cfg(not(feature = "rdf-star"))]{Err("Embedded triple patterns are only available in SPARQL-star")} + } / v:Var() { v.into() } / b:BlankNode() { b.into() } / i:iri() { i.into() } / @@ -2211,11 +2243,17 @@ parser! { l:RDFLiteral() { l.into() } / l:NumericLiteral() { l.into() } / l:BooleanLiteral() { l.into() } / - t:EmbTriple() { t.into() } + t:EmbTriple() {? + #[cfg(feature = "rdf-star")]{Ok(t.into())} + #[cfg(not(feature = "rdf-star"))]{Err("Embedded triples are only available in SPARQL-star")} + } //[178] rule VarOrTermOrEmbTP() -> TermPattern = - t:EmbTP() { t.into() } / + t:EmbTP() {? + #[cfg(feature = "rdf-star")]{Ok(t.into())} + #[cfg(not(feature = "rdf-star"))]{Err("Embedded triple patterns are only available in SPARQL-star")} + } / v:Var() { v.into() } / t:GraphTerm() { t.into() } @@ -2226,8 +2264,9 @@ parser! { rule AnnotationPatternPath() -> FocusedTripleOrPathPattern)>> = "{|" _ a: PropertyListPathNotEmpty() _ "|}" { a } //[181] - rule ExprEmbTP() -> Expression = "<<" _ s:ExprVarOrTerm() _ p:Verb() _ o:ExprVarOrTerm() _ ">>" { - Expression::FunctionCall(Function::Triple, vec![s, p.into(), o]) + rule ExprEmbTP() -> Expression = "<<" _ s:ExprVarOrTerm() _ p:Verb() _ o:ExprVarOrTerm() _ ">>" {? + #[cfg(feature = "rdf-star")]{Ok(Expression::FunctionCall(Function::Triple, vec![s, p.into(), o]))} + #[cfg(not(feature = "rdf-star"))]{Err("Embedded triples are only available in SPARQL-star")} } //[182] diff --git a/spargebra/src/term.rs b/spargebra/src/term.rs index a9658166..fd4a2a30 100644 --- a/spargebra/src/term.rs +++ b/spargebra/src/term.rs @@ -139,6 +139,7 @@ impl fmt::Display for Literal { pub enum Subject { NamedNode(NamedNode), BlankNode(BlankNode), + #[cfg(feature = "rdf-star")] Triple(Box), } @@ -148,6 +149,7 @@ impl fmt::Display for Subject { match self { Self::NamedNode(node) => node.fmt(f), Self::BlankNode(node) => node.fmt(f), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => write!( f, "<<{} {} {}>>", @@ -171,6 +173,7 @@ impl From for Subject { } } +#[cfg(feature = "rdf-star")] impl From for Subject { #[inline] fn from(triple: Triple) -> Self { @@ -186,6 +189,7 @@ impl TryFrom for Subject { match term { TermPattern::NamedNode(t) => Ok(t.into()), TermPattern::BlankNode(t) => Ok(t.into()), + #[cfg(feature = "rdf-star")] TermPattern::Triple(t) => Ok(Triple::try_from(*t)?.into()), TermPattern::Literal(_) | TermPattern::Variable(_) => Err(()), } @@ -198,6 +202,7 @@ impl TryFrom for Subject { #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum GroundSubject { NamedNode(NamedNode), + #[cfg(feature = "rdf-star")] Triple(Box), } @@ -206,6 +211,7 @@ impl fmt::Display for GroundSubject { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::NamedNode(node) => node.fmt(f), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => write!( f, "<<{} {} {}>>", @@ -222,6 +228,7 @@ impl From for GroundSubject { } } +#[cfg(feature = "rdf-star")] impl From for GroundSubject { #[inline] fn from(triple: GroundTriple) -> Self { @@ -237,6 +244,7 @@ impl TryFrom for GroundSubject { match subject { Subject::NamedNode(t) => Ok(t.into()), Subject::BlankNode(_) => Err(()), + #[cfg(feature = "rdf-star")] Subject::Triple(t) => Ok(GroundTriple::try_from(*t)?.into()), } } @@ -250,6 +258,7 @@ impl TryFrom for GroundSubject { match term { GroundTerm::NamedNode(t) => Ok(t.into()), GroundTerm::Literal(_) => Err(()), + #[cfg(feature = "rdf-star")] GroundTerm::Triple(t) => Ok((*t).into()), } } @@ -265,6 +274,7 @@ pub enum Term { NamedNode(NamedNode), BlankNode(BlankNode), Literal(Literal), + #[cfg(feature = "rdf-star")] Triple(Box), } @@ -275,6 +285,7 @@ impl fmt::Display for Term { Self::NamedNode(node) => node.fmt(f), Self::BlankNode(node) => node.fmt(f), Self::Literal(literal) => literal.fmt(f), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => write!( f, "<<{} {} {}>>", @@ -305,6 +316,7 @@ impl From for Term { } } +#[cfg(feature = "rdf-star")] impl From for Term { #[inline] fn from(triple: Triple) -> Self { @@ -318,6 +330,7 @@ impl From for Term { match resource { Subject::NamedNode(node) => node.into(), Subject::BlankNode(node) => node.into(), + #[cfg(feature = "rdf-star")] Subject::Triple(t) => (*t).into(), } } @@ -332,6 +345,7 @@ impl TryFrom for Term { TermPattern::NamedNode(t) => Ok(t.into()), TermPattern::BlankNode(t) => Ok(t.into()), TermPattern::Literal(t) => Ok(t.into()), + #[cfg(feature = "rdf-star")] TermPattern::Triple(t) => Ok(Triple::try_from(*t)?.into()), TermPattern::Variable(_) => Err(()), } @@ -345,6 +359,7 @@ impl TryFrom for Term { pub enum GroundTerm { NamedNode(NamedNode), Literal(Literal), + #[cfg(feature = "rdf-star")] Triple(Box), } @@ -354,6 +369,7 @@ impl fmt::Display for GroundTerm { match self { Self::NamedNode(node) => node.fmt(f), Self::Literal(literal) => literal.fmt(f), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => write!( f, "<<{} {} {}>>", @@ -377,6 +393,7 @@ impl From for GroundTerm { } } +#[cfg(feature = "rdf-star")] impl From for GroundTerm { #[inline] fn from(triple: GroundTriple) -> Self { @@ -393,6 +410,7 @@ impl TryFrom for GroundTerm { Term::NamedNode(t) => Ok(t.into()), Term::BlankNode(_) => Err(()), Term::Literal(t) => Ok(t.into()), + #[cfg(feature = "rdf-star")] Term::Triple(t) => Ok(GroundTriple::try_from(*t)?.into()), } } @@ -694,6 +712,7 @@ pub enum TermPattern { NamedNode(NamedNode), BlankNode(BlankNode), Literal(Literal), + #[cfg(feature = "rdf-star")] Triple(Box), Variable(Variable), } @@ -705,6 +724,7 @@ impl fmt::Display for TermPattern { Self::NamedNode(term) => term.fmt(f), Self::BlankNode(term) => term.fmt(f), Self::Literal(term) => term.fmt(f), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => write!(f, "<<{}>>", triple), Self::Variable(var) => var.fmt(f), } @@ -732,6 +752,7 @@ impl From for TermPattern { } } +#[cfg(feature = "rdf-star")] impl From for TermPattern { #[inline] fn from(triple: TriplePattern) -> Self { @@ -751,6 +772,7 @@ impl From for TermPattern { match subject { Subject::NamedNode(node) => node.into(), Subject::BlankNode(node) => node.into(), + #[cfg(feature = "rdf-star")] Subject::Triple(t) => TriplePattern::from(*t).into(), } } @@ -763,6 +785,7 @@ impl From for TermPattern { Term::NamedNode(node) => node.into(), Term::BlankNode(node) => node.into(), Term::Literal(literal) => literal.into(), + #[cfg(feature = "rdf-star")] Term::Triple(t) => TriplePattern::from(*t).into(), } } @@ -832,6 +855,7 @@ impl From for GroundTermPattern { fn from(term: GroundSubject) -> Self { match term { GroundSubject::NamedNode(node) => node.into(), + #[cfg(feature = "rdf-star")] GroundSubject::Triple(triple) => GroundTriplePattern::from(*triple).into(), } } @@ -842,6 +866,7 @@ impl From for GroundTermPattern { match term { GroundTerm::NamedNode(node) => node.into(), GroundTerm::Literal(literal) => literal.into(), + #[cfg(feature = "rdf-star")] GroundTerm::Triple(triple) => GroundTriplePattern::from(*triple).into(), } } @@ -866,6 +891,7 @@ impl TryFrom for GroundTermPattern { TermPattern::NamedNode(named_node) => named_node.into(), TermPattern::BlankNode(_) => return Err(()), TermPattern::Literal(literal) => literal.into(), + #[cfg(feature = "rdf-star")] TermPattern::Triple(triple) => GroundTriplePattern::try_from(*triple)?.into(), TermPattern::Variable(variable) => variable.into(), })