Starts to use the standard SPARQL algebra as SPARQL AST

pull/10/head
Tpt 7 years ago
parent 4b3bc7a3fe
commit 21a4b78105
  1. 1405
      src/sparql/algebra.rs
  2. 644
      src/sparql/ast.rs
  3. 2
      src/sparql/mod.rs
  4. 50
      src/sparql/model.rs
  5. 64
      src/sparql/parser.rs
  6. 210
      src/sparql/sparql_grammar.rustpeg
  7. 4
      tests/rdf_test_cases.rs

File diff suppressed because it is too large Load Diff

@ -1,644 +0,0 @@
use model::*;
use sparql::model::*;
use std::fmt;
use std::ops::Add;
use utils::Escaper;
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct TriplePattern {
subject: TermOrVariable,
predicate: NamedNodeOrVariable,
object: TermOrVariable,
}
impl TriplePattern {
pub fn new(
subject: impl Into<TermOrVariable>,
predicate: impl Into<NamedNodeOrVariable>,
object: impl Into<TermOrVariable>,
) -> Self {
Self {
subject: subject.into(),
predicate: predicate.into(),
object: object.into(),
}
}
}
impl fmt::Display for TriplePattern {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {} {}", self.subject, self.predicate, self.object)
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub enum PropertyPath {
PredicatePath(NamedNodeOrVariable),
InversePath(Box<PropertyPath>),
SequencePath(Vec<PropertyPath>),
AlternativePath(Vec<PropertyPath>),
ZeroOrMorePath(Box<PropertyPath>),
OneOrMorePath(Box<PropertyPath>),
ZeroOrOnePath(Box<PropertyPath>),
NegatedPath(Box<PropertyPath>),
}
impl fmt::Display for PropertyPath {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
PropertyPath::PredicatePath(p) => write!(f, "{}", p),
PropertyPath::InversePath(p) => write!(f, "^{}", p),
PropertyPath::SequencePath(ps) => write!(
f,
"({})",
ps.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(" / ")
),
PropertyPath::AlternativePath(ps) => write!(
f,
"({})",
ps.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(" | ")
),
PropertyPath::ZeroOrMorePath(p) => write!(f, "{}*", p),
PropertyPath::OneOrMorePath(p) => write!(f, "{}+", p),
PropertyPath::ZeroOrOnePath(p) => write!(f, "{}?", p),
PropertyPath::NegatedPath(p) => write!(f, "!{}", p),
}
}
}
impl From<NamedNodeOrVariable> for PropertyPath {
fn from(p: NamedNodeOrVariable) -> Self {
PropertyPath::PredicatePath(p)
}
}
impl From<NamedNode> for PropertyPath {
fn from(p: NamedNode) -> Self {
PropertyPath::PredicatePath(p.into())
}
}
impl From<Variable> for PropertyPath {
fn from(p: Variable) -> Self {
PropertyPath::PredicatePath(p.into())
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct PropertyPathPattern {
subject: TermOrVariable,
path: PropertyPath,
object: TermOrVariable,
}
impl PropertyPathPattern {
pub fn new(
subject: impl Into<TermOrVariable>,
path: impl Into<PropertyPath>,
object: impl Into<TermOrVariable>,
) -> Self {
Self {
subject: subject.into(),
path: path.into(),
object: object.into(),
}
}
}
impl fmt::Display for PropertyPathPattern {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {} {}", self.subject, self.path, self.object)
}
}
impl From<TriplePattern> for PropertyPathPattern {
fn from(p: TriplePattern) -> Self {
Self {
subject: p.subject,
path: p.predicate.into(),
object: p.object,
}
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub enum Expression {
ConstantExpression(TermOrVariable),
OrExpression(Vec<Expression>),
AndExpression(Vec<Expression>),
EqualExpression(Box<Expression>, Box<Expression>),
NotEqualExpression(Box<Expression>, Box<Expression>),
GreaterExpression(Box<Expression>, Box<Expression>),
GreaterOrEqExpression(Box<Expression>, Box<Expression>),
LowerExpression(Box<Expression>, Box<Expression>),
LowerOrEqExpression(Box<Expression>, Box<Expression>),
InExpression(Box<Expression>, Vec<Expression>),
NotInExpression(Box<Expression>, Vec<Expression>),
AddExpression(Box<Expression>, Box<Expression>),
SubExpression(Box<Expression>, Box<Expression>),
MulExpression(Box<Expression>, Box<Expression>),
DivExpression(Box<Expression>, Box<Expression>),
UnaryPlusExpression(Box<Expression>),
UnaryMinusExpression(Box<Expression>),
UnaryNotExpression(Box<Expression>),
StrFunctionCall(Box<Expression>),
LangFunctionCall(Box<Expression>),
LangMatchesFunctionCall(Box<Expression>, Box<Expression>),
DatatypeFunctionCall(Box<Expression>),
BoundFunctionCall(Variable),
IRIFunctionCall(Box<Expression>),
BNodeFunctionCall(Option<Box<Expression>>),
RandFunctionCall(),
AbsFunctionCall(Box<Expression>),
CeilFunctionCall(Box<Expression>),
FloorFunctionCall(Box<Expression>),
RoundFunctionCall(Box<Expression>),
ConcatFunctionCall(Vec<Expression>),
SubStrFunctionCall(Box<Expression>, Box<Expression>, Option<Box<Expression>>),
StrLenFunctionCall(Box<Expression>),
ReplaceFunctionCall(
Box<Expression>,
Box<Expression>,
Box<Expression>,
Option<Box<Expression>>,
),
UCaseFunctionCall(Box<Expression>),
LCaseFunctionCall(Box<Expression>),
EncodeForURIFunctionCall(Box<Expression>),
ContainsFunctionCall(Box<Expression>, Box<Expression>),
StrStartsFunctionCall(Box<Expression>, Box<Expression>),
StrEndsFunctionCall(Box<Expression>, Box<Expression>),
StrBeforeFunctionCall(Box<Expression>, Box<Expression>),
StrAfterFunctionCall(Box<Expression>, Box<Expression>),
YearFunctionCall(Box<Expression>),
MonthFunctionCall(Box<Expression>),
DayFunctionCall(Box<Expression>),
HoursFunctionCall(Box<Expression>),
MinutesFunctionCall(Box<Expression>),
SecondsFunctionCall(Box<Expression>),
TimezoneFunctionCall(Box<Expression>),
NowFunctionCall(),
UUIDFunctionCall(),
StrUUIDFunctionCall(),
MD5FunctionCall(Box<Expression>),
SHA1FunctionCall(Box<Expression>),
SHA256FunctionCall(Box<Expression>),
SHA384FunctionCall(Box<Expression>),
SHA512FunctionCall(Box<Expression>),
CoalesceFunctionCall(Vec<Expression>),
IfFunctionCall(Box<Expression>, Box<Expression>, Box<Expression>),
StrLangFunctionCall(Box<Expression>, Box<Expression>),
StrDTFunctionCall(Box<Expression>, Box<Expression>),
SameTermFunctionCall(Box<Expression>, Box<Expression>),
IsIRIFunctionCall(Box<Expression>),
IsBlankFunctionCall(Box<Expression>),
IsLiteralFunctionCall(Box<Expression>),
IsNumericFunctionCall(Box<Expression>),
RegexFunctionCall(Box<Expression>, Box<Expression>, Option<Box<Expression>>),
CustomFunctionCall(NamedNode, Vec<Expression>),
ExistsFunctionCall(Box<GraphPattern>),
NotExistsFunctionCall(Box<GraphPattern>),
CountAggregate(Option<Box<Expression>>, bool),
SumAggregate(Box<Expression>, bool),
MinAggregate(Box<Expression>, bool),
MaxAggregate(Box<Expression>, bool),
AvgAggregate(Box<Expression>, bool),
SampleAggregate(Box<Expression>, bool),
GroupConcatAggregate(Box<Expression>, bool, Option<String>),
}
impl fmt::Display for Expression {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expression::ConstantExpression(t) => write!(f, "{}", t),
Expression::OrExpression(e) => write!(
f,
"({})",
e.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(" || ")
),
Expression::AndExpression(e) => write!(
f,
"({})",
e.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(" && ")
),
Expression::EqualExpression(a, b) => write!(f, "{} = {}", a, b),
Expression::NotEqualExpression(a, b) => write!(f, "{} != {}", a, b),
Expression::GreaterExpression(a, b) => write!(f, "{} > {}", a, b),
Expression::GreaterOrEqExpression(a, b) => write!(f, "{} >= {}", a, b),
Expression::LowerExpression(a, b) => write!(f, "{} < {}", a, b),
Expression::LowerOrEqExpression(a, b) => write!(f, "{} <= {}", a, b),
Expression::InExpression(a, b) => write!(
f,
"{} IN ({})",
a,
b.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(", ")
),
Expression::NotInExpression(a, b) => write!(
f,
"{} NOT IN ({})",
a,
b.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(", ")
),
Expression::AddExpression(a, b) => write!(f, "{} + {}", a, b),
Expression::SubExpression(a, b) => write!(f, "{} - {}", a, b),
Expression::MulExpression(a, b) => write!(f, "{} * {}", a, b),
Expression::DivExpression(a, b) => write!(f, "{} / {}", a, b),
Expression::UnaryPlusExpression(e) => write!(f, "+{}", e),
Expression::UnaryMinusExpression(e) => write!(f, "-{}", e),
Expression::UnaryNotExpression(e) => write!(f, "!{}", e),
Expression::StrFunctionCall(e) => write!(f, "STR({})", e),
Expression::LangFunctionCall(e) => write!(f, "LANG({})", e),
Expression::LangMatchesFunctionCall(a, b) => write!(f, "LANGMATCHES({}, {})", a, b),
Expression::DatatypeFunctionCall(e) => write!(f, "DATATYPE({})", e),
Expression::BoundFunctionCall(v) => write!(f, "BOUND({})", v),
Expression::IRIFunctionCall(e) => write!(f, "IRI({})", e),
Expression::BNodeFunctionCall(v) => v.as_ref()
.map(|id| write!(f, "BOUND({})", id))
.unwrap_or_else(|| write!(f, "BOUND()")),
Expression::RandFunctionCall() => write!(f, "RAND()"),
Expression::AbsFunctionCall(e) => write!(f, "ABS({})", e),
Expression::CeilFunctionCall(e) => write!(f, "CEIL({})", e),
Expression::FloorFunctionCall(e) => write!(f, "FLOOR({})", e),
Expression::RoundFunctionCall(e) => write!(f, "ROUND({})", e),
Expression::ConcatFunctionCall(e) => write!(
f,
"CONCAT({})",
e.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(", ")
),
Expression::SubStrFunctionCall(a, b, c) => c.as_ref()
.map(|cv| write!(f, "SUBSTR({}, {}, {})", a, b, cv))
.unwrap_or_else(|| write!(f, "SUBSTR({}, {})", a, b)),
Expression::StrLenFunctionCall(e) => write!(f, "STRLEN({})", e),
Expression::ReplaceFunctionCall(a, b, c, d) => d.as_ref()
.map(|dv| write!(f, "REPLACE({}, {}, {}, {})", a, b, c, dv))
.unwrap_or_else(|| write!(f, "REPLACE({}, {}, {})", a, b, c)),
Expression::UCaseFunctionCall(e) => write!(f, "UCASE({})", e),
Expression::LCaseFunctionCall(e) => write!(f, "LCASE({})", e),
Expression::EncodeForURIFunctionCall(e) => write!(f, "ENCODE_FOR_URI({})", e),
Expression::ContainsFunctionCall(a, b) => write!(f, "CONTAINS({}, {})", a, b),
Expression::StrStartsFunctionCall(a, b) => write!(f, "STRSTATS({}, {})", a, b),
Expression::StrEndsFunctionCall(a, b) => write!(f, "STRENDS({}, {})", a, b),
Expression::StrBeforeFunctionCall(a, b) => write!(f, "STRBEFORE({}, {})", a, b),
Expression::StrAfterFunctionCall(a, b) => write!(f, "STRAFTER({}, {})", a, b),
Expression::YearFunctionCall(e) => write!(f, "YEAR({})", e),
Expression::MonthFunctionCall(e) => write!(f, "MONTH({})", e),
Expression::DayFunctionCall(e) => write!(f, "DAY({})", e),
Expression::HoursFunctionCall(e) => write!(f, "HOURS({})", e),
Expression::MinutesFunctionCall(e) => write!(f, "MINUTES({})", e),
Expression::SecondsFunctionCall(e) => write!(f, "SECONDS({})", e),
Expression::TimezoneFunctionCall(e) => write!(f, "TIMEZONE({})", e),
Expression::NowFunctionCall() => write!(f, "NOW()"),
Expression::UUIDFunctionCall() => write!(f, "UUID()"),
Expression::StrUUIDFunctionCall() => write!(f, "STRUUID()"),
Expression::MD5FunctionCall(e) => write!(f, "MD5({})", e),
Expression::SHA1FunctionCall(e) => write!(f, "SHA1({})", e),
Expression::SHA256FunctionCall(e) => write!(f, "SHA256({})", e),
Expression::SHA384FunctionCall(e) => write!(f, "SHA384({})", e),
Expression::SHA512FunctionCall(e) => write!(f, "SHA512({})", e),
Expression::CoalesceFunctionCall(e) => write!(
f,
"COALESCE({})",
e.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(", ")
),
Expression::IfFunctionCall(a, b, c) => write!(f, "IF({}, {}, {})", a, b, c),
Expression::StrLangFunctionCall(a, b) => write!(f, "STRLANG({}, {})", a, b),
Expression::StrDTFunctionCall(a, b) => write!(f, "STRDT({}, {})", a, b),
Expression::SameTermFunctionCall(a, b) => write!(f, "sameTerm({}, {})", a, b),
Expression::IsIRIFunctionCall(e) => write!(f, "isIRI({})", e),
Expression::IsBlankFunctionCall(e) => write!(f, "isBLANK({})", e),
Expression::IsLiteralFunctionCall(e) => write!(f, "isLITERAL({})", e),
Expression::IsNumericFunctionCall(e) => write!(f, "isNUMERIC({})", e),
Expression::RegexFunctionCall(a, b, c) => c.as_ref()
.map(|cv| write!(f, "REGEX({}, {}, {})", a, b, cv))
.unwrap_or_else(|| write!(f, "REGEX({}, {})", a, b)),
Expression::CustomFunctionCall(iri, args) => write!(
f,
"{}({})",
iri,
args.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(", ")
),
Expression::ExistsFunctionCall(p) => write!(f, "EXISTS {{ {} }}", p),
Expression::NotExistsFunctionCall(p) => write!(f, "NOT EXISTS {{ {} }}", p),
Expression::CountAggregate(e, distinct) => if *distinct {
e.as_ref()
.map(|ex| write!(f, "COUNT(DISTINCT {})", ex))
.unwrap_or_else(|| write!(f, "COUNT(DISTINCT *)"))
} else {
e.as_ref()
.map(|ex| write!(f, "COUNT({})", ex))
.unwrap_or_else(|| write!(f, "COUNT(*)"))
},
Expression::SumAggregate(e, distinct) => if *distinct {
write!(f, "SUM(DISTINCT {})", e)
} else {
write!(f, "SUM({})", e)
},
Expression::MinAggregate(e, distinct) => if *distinct {
write!(f, "MIN(DISTINCT {})", e)
} else {
write!(f, "MIN({})", e)
},
Expression::MaxAggregate(e, distinct) => if *distinct {
write!(f, "MAX(DISTINCT {})", e)
} else {
write!(f, "MAX({})", e)
},
Expression::AvgAggregate(e, distinct) => if *distinct {
write!(f, "AVG(DISTINCT {})", e)
} else {
write!(f, "AVG({})", e)
},
Expression::SampleAggregate(e, distinct) => if *distinct {
write!(f, "SAMPLE(DISTINCT {})", e)
} else {
write!(f, "SAMPLE({})", e)
},
Expression::GroupConcatAggregate(e, distinct, sep) => if *distinct {
sep.as_ref()
.map(|s| {
write!(
f,
"GROUP_CONCAT(DISTINCT {}; SEPARATOR = \"{}\")",
e,
s.escape()
)
})
.unwrap_or_else(|| write!(f, "GROUP_CONCAT(DISTINCT {})", e))
} else {
sep.as_ref()
.map(|s| write!(f, "GROUP_CONCAT({}; SEPARATOR = \"{}\")", e, s.escape()))
.unwrap_or_else(|| write!(f, "GROUP_CONCAT({})", e))
},
}
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub enum GraphPattern {
GroupPattern(Vec<GraphPattern>),
PropertyPathPattern(PropertyPathPattern),
OptionalPattern(Box<GraphPattern>),
UnionPattern(Vec<GraphPattern>),
GraphPattern(NamedNodeOrVariable, Box<GraphPattern>),
BindPattern(Expression, Variable),
ValuesPattern(Vec<Variable>, Vec<Vec<Option<Term>>>),
GroupByPattern(Expression),
HavingPattern(Expression),
MinusPattern(Box<GraphPattern>),
FilterPattern(Expression),
SubSelectPattern {
selection: Selection,
filter: Box<GraphPattern>,
},
ServicePattern(NamedNodeOrVariable, Box<GraphPattern>),
}
impl Default for GraphPattern {
fn default() -> Self {
GraphPattern::GroupPattern(Vec::default())
}
}
impl From<PropertyPathPattern> for GraphPattern {
fn from(p: PropertyPathPattern) -> Self {
GraphPattern::PropertyPathPattern(p)
}
}
impl fmt::Display for GraphPattern {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
GraphPattern::GroupPattern(p) => write!(
f,
"{}",
p.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(" . ")
),
GraphPattern::PropertyPathPattern(p) => write!(f, "{}", p),
GraphPattern::OptionalPattern(p) => write!(f, "OPTIONAL {{ {} }}", p),
GraphPattern::UnionPattern(ps) => write!(
f,
"{{ {} }}",
ps.iter()
.map(|p| p.to_string())
.collect::<Vec<String>>()
.join(" } UNION { ")
),
GraphPattern::GraphPattern(g, p) => write!(f, "GRAPH {} {{ {} }}", g, p),
GraphPattern::BindPattern(e, v) => write!(f, "BIND({} AS {})", e, v),
GraphPattern::ValuesPattern(vars, vals) => write!(
f,
"VALUES ({}) {{ {} }}",
vars.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(" "),
vals.iter()
.map(|r| format!(
"({})",
r.iter()
.map(|vop| vop.as_ref()
.map(|v| v.to_string())
.unwrap_or_else(|| "UNDEF".to_string()))
.collect::<Vec<String>>()
.join(" ")
))
.collect::<Vec<String>>()
.join(" ")
),
GraphPattern::GroupByPattern(g) => write!(f, "GROUP BY ({})", g),
GraphPattern::HavingPattern(e) => write!(f, "HAVING({})", e),
GraphPattern::MinusPattern(p) => write!(f, "MINUS {{ {} }}", p),
GraphPattern::FilterPattern(p) => write!(f, "FILTER({})", p),
GraphPattern::SubSelectPattern { selection, filter } => {
write!(f, "{{ SELECT {} WHERE {{ {} }} }}", selection, filter)
}
GraphPattern::ServicePattern(s, p) => write!(f, "SERVICE {} {{ {} }}", s, p),
}
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash, Default)]
pub struct Dataset {
pub default: Vec<NamedNode>,
pub named: Vec<NamedNode>,
}
impl Dataset {
pub fn new_with_default(graph: NamedNode) -> Self {
Self {
default: vec![graph],
named: Vec::default(),
}
}
pub fn new_with_named(graph: NamedNode) -> Self {
Self {
default: Vec::default(),
named: vec![graph],
}
}
}
impl Add for Dataset {
type Output = Self;
fn add(mut self, rhs: Dataset) -> Self {
self.default.extend_from_slice(&rhs.default);
self.named.extend_from_slice(&rhs.named);
self
}
}
impl fmt::Display for Dataset {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for g in &self.default {
write!(f, "FROM {} ", g)?;
}
for g in &self.named {
write!(f, "FROM NAMED {} ", g)?;
}
Ok(())
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub enum SelectionOption {
Distinct,
Reduced,
Default,
}
impl fmt::Display for SelectionOption {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
SelectionOption::Distinct => write!(f, "DISTINCT"),
SelectionOption::Reduced => write!(f, "REDUCED"),
SelectionOption::Default => Ok(()),
}
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub enum SelectionMember {
Variable(Variable),
Expression(Expression, Variable),
}
impl fmt::Display for SelectionMember {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
SelectionMember::Variable(v) => write!(f, "{}", v),
SelectionMember::Expression(e, v) => write!(f, "({} AS {})", e, v),
}
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct Selection {
pub option: SelectionOption,
pub variables: Option<Vec<SelectionMember>>,
}
impl fmt::Display for Selection {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.variables
.as_ref()
.map(|vars| {
write!(
f,
"{} {}",
self.option,
vars.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(" ")
)
})
.unwrap_or_else(|| write!(f, "{} *", self.option))
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub enum Query {
SelectQuery {
selection: Selection,
dataset: Dataset,
filter: GraphPattern,
},
ConstructQuery {
construct: Vec<TriplePattern>,
dataset: Dataset,
filter: GraphPattern,
},
DescribeQuery {
dataset: Dataset,
filter: GraphPattern,
},
AskQuery {
dataset: Dataset,
filter: GraphPattern,
},
}
impl fmt::Display for Query {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Query::SelectQuery {
selection,
dataset,
filter,
} => write!(f, "SELECT {} {} WHERE {{ {} }}", selection, dataset, filter),
Query::ConstructQuery {
construct,
dataset,
filter,
} => write!(
f,
"CONSTRUCT {{ {} }} {} WHERE {{ {} }}",
construct
.iter()
.map(|t| t.to_string())
.collect::<Vec<String>>()
.join(" . "),
dataset,
filter
),
Query::DescribeQuery { dataset, filter } => {
write!(f, "DESCRIBE {} WHERE {{ {} }}", dataset, filter)
}
Query::AskQuery { dataset, filter } => {
write!(f, "ASK {} WHERE {{ {} }}", dataset, filter)
}
}
}
}

@ -1,3 +1,3 @@
pub mod ast; pub mod algebra;
pub mod model; pub mod model;
pub mod parser; pub mod parser;

@ -1,4 +1,5 @@
use model::*; use model::*;
use std::collections::BTreeMap;
use std::fmt; use std::fmt;
use uuid::Uuid; use uuid::Uuid;
@ -113,3 +114,52 @@ impl From<NamedNodeOrVariable> for TermOrVariable {
} }
} }
} }
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct TriplePattern {
pub subject: TermOrVariable,
pub predicate: NamedNodeOrVariable,
pub object: TermOrVariable,
}
impl TriplePattern {
pub fn new(
subject: impl Into<TermOrVariable>,
predicate: impl Into<NamedNodeOrVariable>,
object: impl Into<TermOrVariable>,
) -> Self {
Self {
subject: subject.into(),
predicate: predicate.into(),
object: object.into(),
}
}
}
impl fmt::Display for TriplePattern {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {} {}", self.subject, self.predicate, self.object)
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct Binding(BTreeMap<Variable, Term>);
impl Binding {
pub fn insert(&mut self, var: Variable, value: Term) {
self.0.insert(var, value);
}
pub fn get<'a>(&'a self, key: &'a TermOrVariable) -> Option<&'a Term> {
match key {
TermOrVariable::Term(t) => Some(t),
TermOrVariable::Variable(v) => self.0.get(v),
}
}
}
impl Default for Binding {
fn default() -> Self {
Binding(BTreeMap::default())
}
}

@ -6,7 +6,7 @@ mod grammar {
use model::*; use model::*;
use rio::RioError; use rio::RioError;
use rio::RioResult; use rio::RioResult;
use sparql::ast::*; use sparql::algebra::*;
use sparql::model::*; use sparql::model::*;
use sparql::parser::unescape_unicode_codepoints; use sparql::parser::unescape_unicode_codepoints;
use std::borrow::Cow; use std::borrow::Cow;
@ -90,33 +90,53 @@ mod grammar {
} }
} }
impl From<FocusedPropertyPathPattern<TermOrVariable>> for GraphPattern { #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
fn from(input: FocusedPropertyPathPattern<TermOrVariable>) -> Self { enum PartialGraphPattern {
if input.patterns.len() == 1 { Optional(GraphPattern),
input.patterns[0].clone().into() Minus(GraphPattern),
} else { Bind(Expression, Variable),
GraphPattern::GroupPattern(input.patterns.into_iter().map(|p| p.into()).collect()) Filter(Expression),
Other(GraphPattern),
}
fn new_join(l: GraphPattern, r: GraphPattern) -> GraphPattern {
//Avoid to output empty BGPs
if let GraphPattern::BGP(pl) = &l {
if pl.is_empty() {
return r;
}
}
if let GraphPattern::BGP(pr) = &r {
if pr.is_empty() {
return l;
} }
} }
}
fn flatten_group_pattern(v: impl Iterator<Item = GraphPattern>) -> GraphPattern { //Merge BGPs
let l: Vec<GraphPattern> = v.into_iter() match (l, r) {
.flat_map(|p| { (GraphPattern::BGP(mut pl), GraphPattern::BGP(pr)) => {
if let GraphPattern::GroupPattern(v2) = p { pl.extend_from_slice(&pr);
v2.into_iter() GraphPattern::BGP(pl)
} else { }
vec![p].into_iter() (l, r) => GraphPattern::Join(Box::new(l), Box::new(r)),
}
})
.collect();
if l.len() == 1 {
l[0].clone()
} else {
GraphPattern::GroupPattern(l)
} }
} }
fn not_empty_fold<T>(
iter: impl Iterator<Item = T>,
combine: impl Fn(T, T) -> T,
) -> Result<T, &'static str> {
iter.fold(None, |a, b| match a {
Some(av) => Some(combine(av, b)),
None => Some(b),
}).ok_or("The iterator should not be empty")
}
enum Either<L, R> {
Left(L),
Right(R),
}
pub struct ParserState { pub struct ParserState {
base_uri: Option<Url>, base_uri: Option<Url>,
namespaces: HashMap<String, String>, namespaces: HashMap<String, String>,

@ -4,7 +4,6 @@ use std::char;
use model::vocab::rdf; use model::vocab::rdf;
use model::vocab::xsd; use model::vocab::xsd;
use std::iter; use std::iter;
use std::iter::once;
#![arguments(state: &mut ParserState)] #![arguments(state: &mut ParserState)]
@ -48,10 +47,11 @@ SelectQuery -> Query = s:SelectClause _ d:DatasetClauses _ f:WhereClause _ Solut
//[8] //[8]
SubSelect -> GraphPattern = s:SelectClause _ f:WhereClause _ SolutionModifier _ ValuesClause { //TODO: Modifiers SubSelect -> GraphPattern = s:SelectClause _ f:WhereClause _ SolutionModifier _ ValuesClause { //TODO: Modifiers
GraphPattern::SubSelectPattern { GraphPattern::default()
/*TODO GraphPattern::SubSelectPattern {
selection: s, selection: s,
filter: Box::new(f) filter: Box::new(f)
} }*/
} }
//[9] //[9]
@ -81,7 +81,7 @@ ConstructQuery -> Query =
Query::ConstructQuery { Query::ConstructQuery {
construct: c.clone(), construct: c.clone(),
dataset: d, dataset: d,
filter: flatten_group_pattern(c.into_iter().map(|p| PropertyPathPattern::from(p).into())) filter: GraphPattern::BGP(c.into_iter().map(|p| PropertyPathPattern::from(p)).collect())
} }
} }
@ -135,12 +135,8 @@ GroupClause -> () = "GROUP"i _ "BY"i _ (GroupCondition _)+
GroupCondition -> () = BuiltInCall / FunctionCall / '(' _ Expression _ ("AS"i _ Var _)? ')' / Var GroupCondition -> () = BuiltInCall / FunctionCall / '(' _ Expression _ ("AS"i _ Var _)? ')' / Var
//[21] //[21]
HavingClause -> Expression = "HAVING"i _ c:HavingCondition+ { HavingClause -> Expression = "HAVING"i _ e:HavingCondition+ {?
if c.len() == 1 { not_empty_fold(e.into_iter(), |a, b| Expression::AndExpression(Box::new(a), Box::new(b)))
c[0].clone()
} else {
Expression::AndExpression(c)
}
} }
//[22] //[22]
@ -179,67 +175,107 @@ GroupGraphPattern -> GraphPattern =
//[54] //[54]
GroupGraphPatternSub -> GraphPattern = a:TriplesBlock? _ b:GroupGraphPatternSub_item* { GroupGraphPatternSub -> GraphPattern = a:TriplesBlock? _ b:GroupGraphPatternSub_item* {
let mut list = a.map(|v| vec![v]).unwrap_or_else(|| vec![]); let mut p = a.map(|v| vec![PartialGraphPattern::Other(GraphPattern::BGP(v))]).unwrap_or_else(|| vec![]);
for v in b { for v in b {
list.extend_from_slice(&v) p.extend_from_slice(&v)
}
let mut filter: Option<Expression> = None;
let mut g = GraphPattern::default();
for e in p {
match e {
PartialGraphPattern::Optional(p) => match p {
GraphPattern::Filter(f, a2) => {
g = GraphPattern::LeftJoin(Box::new(g), a2, f)
}
a => {
g = GraphPattern::LeftJoin(Box::new(g), Box::new(a), Literal::from(true).into())
}
}
PartialGraphPattern::Minus(p) => {
g = GraphPattern::Minus(Box::new(g), Box::new(p))
}
PartialGraphPattern::Bind(expr, var) => {
g = GraphPattern::Extend(Box::new(g), var, expr)
}
PartialGraphPattern::Filter(expr) => match filter {
Some(f) => { filter = Some(Expression::AndExpression(Box::new(f), Box::new(expr))) },
None => { filter = Some(expr) }
},
PartialGraphPattern::Other(e) => g = new_join(g, e),
}
}
match filter {
Some(filter) => GraphPattern::Filter(filter, Box::new(g)),
None => g
} }
flatten_group_pattern(list.into_iter())
} }
GroupGraphPatternSub_item -> Vec<GraphPattern> = a:GraphPatternNotTriples _ ('.' _)? b:TriplesBlock? _ { GroupGraphPatternSub_item -> Vec<PartialGraphPattern> = a:GraphPatternNotTriples _ ('.' _)? b:TriplesBlock? _ {
let mut result = vec![a]; let mut result = vec![a];
b.map(|v| result.push(v)); b.map(|v| result.push(PartialGraphPattern::Other(GraphPattern::BGP(v))));
result result
} }
//[55] //[55]
TriplesBlock -> GraphPattern = h:TriplesSameSubjectPath _ t:TriplesBlock_tail? { TriplesBlock -> Vec<PropertyPathPattern> = h:TriplesSameSubjectPath _ t:TriplesBlock_tail? {
match t { let mut triples = h;
Some(l) => flatten_group_pattern(vec![h, l].into_iter()), if let Some(l) = t {
None => flatten_group_pattern(once(h)) triples.extend_from_slice(&l)
} }
triples
} }
TriplesBlock_tail -> GraphPattern = '.' _ t:TriplesBlock? _ { TriplesBlock_tail -> Vec<PropertyPathPattern> = '.' _ t:TriplesBlock? _ {
t.unwrap_or_else(|| GraphPattern::GroupPattern(Vec::default())) t.unwrap_or_else(|| Vec::default())
} }
//[56] //[56]
GraphPatternNotTriples -> GraphPattern = GroupOrUnionGraphPattern / OptionalGraphPattern / MinusGraphPattern / GraphGraphPattern / ServiceGraphPattern / Filter / Bind / InlineData GraphPatternNotTriples -> PartialGraphPattern = GroupOrUnionGraphPattern / OptionalGraphPattern / MinusGraphPattern / GraphGraphPattern / ServiceGraphPattern / Filter / Bind / InlineData
//[57] //[57]
OptionalGraphPattern -> GraphPattern = "OPTIONAL"i _ p:GroupGraphPattern { OptionalGraphPattern -> PartialGraphPattern = "OPTIONAL"i _ p:GroupGraphPattern {
GraphPattern::OptionalPattern(Box::new(p)) PartialGraphPattern::Optional(p)
} }
//[58] //[58]
GraphGraphPattern -> GraphPattern = "GRAPH"i _ g:VarOrIri _ p:GroupGraphPattern { GraphGraphPattern -> PartialGraphPattern = "GRAPH"i _ g:VarOrIri _ p:GroupGraphPattern {
GraphPattern::GraphPattern(g, Box::new(p)) PartialGraphPattern::Other(GraphPattern::Graph(g, Box::new(p)))
} }
//[59] //[59]
ServiceGraphPattern -> GraphPattern = "SERVICE"i _ "SILENT"i? _ s:VarOrIri _ p:GroupGraphPattern { ServiceGraphPattern -> PartialGraphPattern = "SERVICE"i _ "SILENT"i? _ s:VarOrIri _ p:GroupGraphPattern {
GraphPattern::ServicePattern(s, Box::new(p)) PartialGraphPattern::Other(GraphPattern::default())
//TODO PartialGraphPattern::Other(GraphPattern::ServicePattern(s, Box::new(p)))
} }
//[60] //[60]
Bind -> GraphPattern = "BIND"i _ '(' _ e:Expression _ "AS"i _ v:Var _ ')' { Bind -> PartialGraphPattern = "BIND"i _ '(' _ e:Expression _ "AS"i _ v:Var _ ')' {
GraphPattern::BindPattern(e, v) PartialGraphPattern::Bind(e, v)
} }
//[61] //[61]
InlineData -> GraphPattern = "VALUES"i _ p:DataBlock { p } InlineData -> PartialGraphPattern = "VALUES"i _ p:DataBlock { PartialGraphPattern::Other(p) }
//[62] //[62]
DataBlock -> GraphPattern = InlineDataOneVar / InlineDataFull DataBlock -> GraphPattern = InlineDataOneVar / InlineDataFull
//[63] //[63]
InlineDataOneVar -> GraphPattern = v:Var _ '{' _ d:InlineDataOneVar_value* '}' { InlineDataOneVar -> GraphPattern = var:Var _ '{' _ d:InlineDataOneVar_value* '}' {
GraphPattern::ValuesPattern(vec![v], d) GraphPattern::Multiset(d.into_iter().map(|val| {
let mut bindings = Binding::default();
val.map(|v| bindings.insert(var.clone(), v));
bindings
}).collect())
} }
InlineDataOneVar_value -> Vec<Option<Term>> = t:DataBlockValue { vec![t] } InlineDataOneVar_value -> Option<Term> = t:DataBlockValue { t }
//[64] //[64]
InlineDataFull -> GraphPattern = '(' _ var:InlineDataFull_var* _ ')' _ '{' _ val:InlineDataFull_values* '}' { InlineDataFull -> GraphPattern = '(' _ vars:InlineDataFull_var* _ ')' _ '{' _ val:InlineDataFull_values* '}' {
GraphPattern::ValuesPattern(var, val) GraphPattern::Multiset(val.into_iter().map(|vals| {
let mut bindings = Binding::default();
for (var, val) in vars.iter().zip(vals.into_iter()) {
val.map(|v| bindings.insert(var.clone(), v));
}
bindings
}).collect())
} }
InlineDataFull_var -> Variable = v:Var _ { v } InlineDataFull_var -> Variable = v:Var _ { v }
InlineDataFull_values -> Vec<Option<Term>> = '(' _ v:InlineDataFull_value* _ ')' _ { v } InlineDataFull_values -> Vec<Option<Term>> = '(' _ v:InlineDataFull_value* _ ')' _ { v }
@ -254,22 +290,22 @@ DataBlockValue -> Option<Term> =
"UNDEF"i { None } "UNDEF"i { None }
//[66] //[66]
MinusGraphPattern -> GraphPattern = "MINUS"i _ p: GroupGraphPattern { MinusGraphPattern -> PartialGraphPattern = "MINUS"i _ p: GroupGraphPattern {
GraphPattern::MinusPattern(Box::new(p)) PartialGraphPattern::Minus(p)
} }
//[67] //[67]
GroupOrUnionGraphPattern -> GraphPattern = p:GroupOrUnionGraphPattern_item **<1,> ("UNION"i _) { GroupOrUnionGraphPattern -> PartialGraphPattern = p:GroupOrUnionGraphPattern_item **<1,> ("UNION"i _) {?
if p.len() == 1 { not_empty_fold(p.into_iter(), |a, b| {
p[0].clone() GraphPattern::Union(Box::new(a), Box::new(b))
} else { }).map(PartialGraphPattern::Other)
GraphPattern::UnionPattern(p)
}
} }
GroupOrUnionGraphPattern_item -> GraphPattern = p:GroupGraphPattern _ { p } GroupOrUnionGraphPattern_item -> GraphPattern = p:GroupGraphPattern _ { p }
//[68] //[68]
Filter -> GraphPattern = "FILTER"i _ c:Constraint { GraphPattern::FilterPattern(c) } Filter -> PartialGraphPattern = "FILTER"i _ c:Constraint {
PartialGraphPattern::Filter(c)
}
//[69] //[69]
Constraint -> Expression = BrackettedExpression / BuiltInCall / FunctionCall Constraint -> Expression = BrackettedExpression / BuiltInCall / FunctionCall
@ -337,7 +373,7 @@ PropertyListNotEmpty -> FocusedTriplePattern<Vec<(NamedNodeOrVariable,Vec<TermOr
} }
PropertyListNotEmpty_item -> FocusedTriplePattern<(NamedNodeOrVariable,Vec<TermOrVariable>)> = p:Verb _ o:ObjectList _ { PropertyListNotEmpty_item -> FocusedTriplePattern<(NamedNodeOrVariable,Vec<TermOrVariable>)> = p:Verb _ o:ObjectList _ {
FocusedTriplePattern { FocusedTriplePattern {
focus: (p, o.focus), focus: (p.into(), o.focus),
patterns: o.patterns patterns: o.patterns
} }
} }
@ -359,7 +395,7 @@ ObjectList_item -> FocusedTriplePattern<TermOrVariable> = o:Object _ { o }
Object -> FocusedTriplePattern<TermOrVariable> = GraphNode Object -> FocusedTriplePattern<TermOrVariable> = GraphNode
//[81] //[81]
TriplesSameSubjectPath -> GraphPattern = TriplesSameSubjectPath -> Vec<PropertyPathPattern> =
s:VarOrTerm _ po:PropertyListPathNotEmpty { s:VarOrTerm _ po:PropertyListPathNotEmpty {
let mut patterns = po.patterns; let mut patterns = po.patterns;
for (p, os) in po.focus { for (p, os) in po.focus {
@ -367,7 +403,7 @@ TriplesSameSubjectPath -> GraphPattern =
patterns.push(PropertyPathPattern::new(s.clone(), p.clone(), o)) patterns.push(PropertyPathPattern::new(s.clone(), p.clone(), o))
} }
} }
flatten_group_pattern(patterns.into_iter().map(|p| p.into())) patterns
} / } /
s:TriplesNodePath _ po:PropertyListPath { s:TriplesNodePath _ po:PropertyListPath {
let mut patterns = s.patterns; let mut patterns = s.patterns;
@ -377,7 +413,7 @@ TriplesSameSubjectPath -> GraphPattern =
patterns.push(PropertyPathPattern::new(s.focus.clone(), p.clone(), o)) patterns.push(PropertyPathPattern::new(s.focus.clone(), p.clone(), o))
} }
} }
flatten_group_pattern(patterns.into_iter().map(|p| p.into())) patterns
} }
//[82] //[82]
@ -428,22 +464,18 @@ ObjectPath -> FocusedPropertyPathPattern<TermOrVariable> = GraphNodePath
Path -> PropertyPath = PathAlternative Path -> PropertyPath = PathAlternative
//[89] //[89]
PathAlternative -> PropertyPath = p:PathAlternative_item **<1,> ('|' _) { PathAlternative -> PropertyPath = p:PathAlternative_item **<1,> ('|' _) {?
if p.len() == 1 { not_empty_fold(p.into_iter(), |a, b| {
p[0].clone() PropertyPath::AlternativePath(Box::new(a), Box::new(b))
} else { })
PropertyPath::SequencePath(p)
}
} }
PathAlternative_item -> PropertyPath = p:PathSequence _ { p } PathAlternative_item -> PropertyPath = p:PathSequence _ { p }
//[90] //[90]
PathSequence -> PropertyPath = p:PathSequence_item **<1,> ('/' _) { PathSequence -> PropertyPath = p:PathSequence_item **<1,> ('/' _) {?
if p.len() == 1 { not_empty_fold(p.into_iter(), |a, b| {
p[0].clone() PropertyPath::SequencePath(Box::new(a), Box::new(b))
} else { })
PropertyPath::AlternativePath(p)
}
} }
PathSequence_item -> PropertyPath = p:PathEltOrInverse _ { p } PathSequence_item -> PropertyPath = p:PathEltOrInverse _ { p }
@ -462,19 +494,43 @@ PathEltOrInverse -> PropertyPath =
//[94] //[94]
PathPrimary -> PropertyPath = PathPrimary -> PropertyPath =
v:Verb { v.into() } / v:Verb { v.into() } /
'!' _ p:PathNegatedPropertySet { PropertyPath::NegatedPath(Box::new(p)) } / '!' _ p:PathNegatedPropertySet { p } /
'(' _ p:Path _ ')' { p } '(' _ p:Path _ ')' { p }
//[95] //[95]
PathNegatedPropertySet -> PropertyPath = PathNegatedPropertySet -> PropertyPath =
'(' _ p:PathNegatedPropertySet_item **<1,> ('|' _) ')' { PropertyPath::AlternativePath(p) } / '(' _ p:PathNegatedPropertySet_item **<1,> ('|' _) ')' {
PathOneInPropertySet let mut direct = Vec::default();
PathNegatedPropertySet_item -> PropertyPath = p:PathOneInPropertySet _ { p } let mut inverse = Vec::default();
for e in p {
match e {
Either::Left(a) => direct.push(a),
Either::Right(b) => inverse.push(b)
}
}
if inverse.len() == 0 {
PropertyPath::NegatedPropertySet(direct)
} else if direct.len() == 0 {
PropertyPath::InversePath(Box::new(PropertyPath::NegatedPropertySet(inverse)))
} else {
PropertyPath::AlternativePath(
Box::new(PropertyPath::NegatedPropertySet(direct)),
Box::new(PropertyPath::InversePath(Box::new(PropertyPath::NegatedPropertySet(inverse))))
)
}
} /
p:PathOneInPropertySet {
match p {
Either::Left(a) => PropertyPath::NegatedPropertySet(vec![a]),
Either::Right(b) => PropertyPath::InversePath(Box::new(PropertyPath::NegatedPropertySet(vec![b]))),
}
}
PathNegatedPropertySet_item -> Either<NamedNodeOrVariable,NamedNodeOrVariable> = p:PathOneInPropertySet _ { p }
//[96] //[96]
PathOneInPropertySet -> PropertyPath = PathOneInPropertySet -> Either<NamedNodeOrVariable,NamedNodeOrVariable> =
'^' _ v:Verb { PropertyPath::InversePath(Box::new(v.into())) } / '^' _ v:Verb { Either::Right(v) } /
v:Verb { v.into() } v:Verb { Either::Left(v) }
//[98] //[98]
TriplesNode -> FocusedTriplePattern<TermOrVariable> = Collection / BlankNodePropertyList TriplesNode -> FocusedTriplePattern<TermOrVariable> = Collection / BlankNodePropertyList
@ -584,22 +640,14 @@ GraphTerm -> Term =
Expression -> Expression = e:ConditionalOrExpression {e} Expression -> Expression = e:ConditionalOrExpression {e}
//[111] //[111]
ConditionalOrExpression -> Expression = e:ConditionalOrExpression_item **<1,> ("||" _) { ConditionalOrExpression -> Expression = e:ConditionalOrExpression_item **<1,> ("||" _) {?
if e.len() == 1 { not_empty_fold(e.into_iter(), |a, b| Expression::OrExpression(Box::new(a), Box::new(b)))
e[0].clone()
} else {
Expression::OrExpression(e)
}
} }
ConditionalOrExpression_item -> Expression = e:ConditionalAndExpression _ { e } ConditionalOrExpression_item -> Expression = e:ConditionalAndExpression _ { e }
//[112] //[112]
ConditionalAndExpression -> Expression = e:ConditionalAndExpression_item **<1,> ("&&" _) { ConditionalAndExpression -> Expression = e:ConditionalAndExpression_item **<1,> ("&&" _) {?
if e.len() == 1 { not_empty_fold(e.into_iter(), |a, b| Expression::AndExpression(Box::new(a), Box::new(b)))
e[0].clone()
} else {
Expression::AndExpression(e)
}
} }
ConditionalAndExpression_item -> Expression = e:ValueLogical _ { e } ConditionalAndExpression_item -> Expression = e:ValueLogical _ { e }
@ -729,7 +777,7 @@ StrReplaceExpression -> Expression =
ExistsFunc -> Expression = "EXISTS"i _ p:GroupGraphPattern { Expression::ExistsFunctionCall(Box::new(p)) } ExistsFunc -> Expression = "EXISTS"i _ p:GroupGraphPattern { Expression::ExistsFunctionCall(Box::new(p)) }
//[126] //[126]
NotExistsFunc -> Expression = "NOT"i _ "EXISTS"i _ p:GroupGraphPattern { Expression::NotExistsFunctionCall(Box::new(p)) } NotExistsFunc -> Expression = "NOT"i _ "EXISTS"i _ p:GroupGraphPattern { Expression::UnaryNotExpression(Box::new(Expression::ExistsFunctionCall(Box::new(p)))) }
//[127] //[127]
Aggregate -> Expression = Aggregate -> Expression =

@ -15,7 +15,7 @@ use rudf::rio::ntriples::read_ntriples;
use rudf::rio::turtle::read_turtle; use rudf::rio::turtle::read_turtle;
use rudf::rio::RioError; use rudf::rio::RioError;
use rudf::rio::RioResult; use rudf::rio::RioResult;
use rudf::sparql::ast::Query; use rudf::sparql::algebra::Query;
use rudf::sparql::parser::read_sparql_query; use rudf::sparql::parser::read_sparql_query;
use rudf::store::isomorphism::GraphIsomorphism; use rudf::store::isomorphism::GraphIsomorphism;
use rudf::store::memory::MemoryGraph; use rudf::store::memory::MemoryGraph;
@ -144,7 +144,7 @@ fn sparql_w3c_syntax_testsuite() {
if let Err(error) = read_sparql_query(query.to_string().as_bytes(), None) { if let Err(error) = read_sparql_query(query.to_string().as_bytes(), None) {
assert!( assert!(
false, false,
"Failure tu deserialize \"{}\" of {} with error: {}", "Failure to deserialize \"{}\" of {} with error: {}",
query.to_string(), query.to_string(),
test, test,
error error

Loading…
Cancel
Save