From 21a4b78105afc7d1b781b5061f9075f3a68b94d5 Mon Sep 17 00:00:00 2001 From: Tpt Date: Fri, 8 Jun 2018 12:13:00 +0200 Subject: [PATCH] Starts to use the standard SPARQL algebra as SPARQL AST --- src/sparql/algebra.rs | 1405 +++++++++++++++++++++++++++++ src/sparql/ast.rs | 644 ------------- src/sparql/mod.rs | 2 +- src/sparql/model.rs | 50 + src/sparql/parser.rs | 64 +- src/sparql/sparql_grammar.rustpeg | 210 +++-- tests/rdf_test_cases.rs | 4 +- 7 files changed, 1629 insertions(+), 750 deletions(-) create mode 100644 src/sparql/algebra.rs delete mode 100644 src/sparql/ast.rs diff --git a/src/sparql/algebra.rs b/src/sparql/algebra.rs new file mode 100644 index 00000000..740f9f6c --- /dev/null +++ b/src/sparql/algebra.rs @@ -0,0 +1,1405 @@ +use model::*; +use sparql::model::*; +use std::fmt; +use std::ops::Add; +use utils::Escaper; + +#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] +pub enum PropertyPath { + PredicatePath(NamedNodeOrVariable), //TODO: restrict to NamedNode + InversePath(Box), + SequencePath(Box, Box), + AlternativePath(Box, Box), + ZeroOrMorePath(Box), + OneOrMorePath(Box), + ZeroOrOnePath(Box), + NegatedPropertySet(Vec), +} + +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, "inv({})", p), + PropertyPath::AlternativePath(a, b) => write!(f, "alt({}, {})", a, b), + PropertyPath::SequencePath(a, b) => write!(f, "seq({}, {})", a, b), + PropertyPath::ZeroOrMorePath(p) => write!(f, "ZeroOrMorePath({})", p), + PropertyPath::OneOrMorePath(p) => write!(f, "OneOrMorePath({})", p), + PropertyPath::ZeroOrOnePath(p) => write!(f, "ZeroOrOnePath({})", p), + PropertyPath::NegatedPropertySet(p) => write!( + f, + "NPS({{ {} }})", + p.iter() + .map(|v| v.to_string()) + .collect::>() + .join(" ") + ), + } + } +} + +struct SparqlPropertyPath<'a>(&'a PropertyPath); + +impl<'a> fmt::Display for SparqlPropertyPath<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.0 { + PropertyPath::PredicatePath(p) => write!(f, "{}", p), + PropertyPath::InversePath(p) => write!(f, "^{}", SparqlPropertyPath(&*p)), + PropertyPath::SequencePath(a, b) => write!( + f, + "({} / {})", + SparqlPropertyPath(&*a), + SparqlPropertyPath(&*b) + ), + PropertyPath::AlternativePath(a, b) => write!( + f, + "({} | {})", + SparqlPropertyPath(&*a), + SparqlPropertyPath(&*b) + ), + PropertyPath::ZeroOrMorePath(p) => write!(f, "{}*", SparqlPropertyPath(&*p)), + PropertyPath::OneOrMorePath(p) => write!(f, "{}+", SparqlPropertyPath(&*p)), + PropertyPath::ZeroOrOnePath(p) => write!(f, "{}?", SparqlPropertyPath(&*p)), + PropertyPath::NegatedPropertySet(p) => write!( + f, + "!({})", + p.iter() + .map(|v| v.to_string()) + .collect::>() + .join(" | ") + ), + } + } +} + +impl From for PropertyPath { + fn from(p: NamedNodeOrVariable) -> Self { + PropertyPath::PredicatePath(p) + } +} + +impl From for PropertyPath { + fn from(p: NamedNode) -> Self { + PropertyPath::PredicatePath(p.into()) + } +} + +impl From for PropertyPath { + fn from(p: Variable) -> Self { + PropertyPath::PredicatePath(p.into()) + } +} + +#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] +pub struct PropertyPathPattern { + pub subject: TermOrVariable, + pub path: PropertyPath, + pub object: TermOrVariable, +} + +impl fmt::Display for PropertyPathPattern { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} {} {}", self.subject, self.path, self.object) + } +} + +impl PropertyPathPattern { + pub fn new( + subject: impl Into, + path: impl Into, + object: impl Into, + ) -> Self { + Self { + subject: subject.into(), + path: path.into(), + object: object.into(), + } + } +} + +impl From for PropertyPathPattern { + fn from(p: TriplePattern) -> Self { + Self { + subject: p.subject, + path: p.predicate.into(), + object: p.object, + } + } +} + +struct SparqlPropertyPathPattern<'a>(&'a PropertyPathPattern); + +impl<'a> fmt::Display for SparqlPropertyPathPattern<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{} {} {}", + self.0.subject, + SparqlPropertyPath(&self.0.path), + self.0.object + ) + } +} + +#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] +pub enum Expression { + ConstantExpression(TermOrVariable), + OrExpression(Box, Box), + AndExpression(Box, Box), + EqualExpression(Box, Box), + NotEqualExpression(Box, Box), + GreaterExpression(Box, Box), + GreaterOrEqExpression(Box, Box), + LowerExpression(Box, Box), + LowerOrEqExpression(Box, Box), + InExpression(Box, Vec), + NotInExpression(Box, Vec), + AddExpression(Box, Box), + SubExpression(Box, Box), + MulExpression(Box, Box), + DivExpression(Box, Box), + UnaryPlusExpression(Box), + UnaryMinusExpression(Box), + UnaryNotExpression(Box), + StrFunctionCall(Box), + LangFunctionCall(Box), + LangMatchesFunctionCall(Box, Box), + DatatypeFunctionCall(Box), + BoundFunctionCall(Variable), + IRIFunctionCall(Box), + BNodeFunctionCall(Option>), + RandFunctionCall(), + AbsFunctionCall(Box), + CeilFunctionCall(Box), + FloorFunctionCall(Box), + RoundFunctionCall(Box), + ConcatFunctionCall(Vec), + SubStrFunctionCall(Box, Box, Option>), + StrLenFunctionCall(Box), + ReplaceFunctionCall( + Box, + Box, + Box, + Option>, + ), + UCaseFunctionCall(Box), + LCaseFunctionCall(Box), + EncodeForURIFunctionCall(Box), + ContainsFunctionCall(Box, Box), + StrStartsFunctionCall(Box, Box), + StrEndsFunctionCall(Box, Box), + StrBeforeFunctionCall(Box, Box), + StrAfterFunctionCall(Box, Box), + YearFunctionCall(Box), + MonthFunctionCall(Box), + DayFunctionCall(Box), + HoursFunctionCall(Box), + MinutesFunctionCall(Box), + SecondsFunctionCall(Box), + TimezoneFunctionCall(Box), + NowFunctionCall(), + UUIDFunctionCall(), + StrUUIDFunctionCall(), + MD5FunctionCall(Box), + SHA1FunctionCall(Box), + SHA256FunctionCall(Box), + SHA384FunctionCall(Box), + SHA512FunctionCall(Box), + CoalesceFunctionCall(Vec), + IfFunctionCall(Box, Box, Box), + StrLangFunctionCall(Box, Box), + StrDTFunctionCall(Box, Box), + SameTermFunctionCall(Box, Box), + IsIRIFunctionCall(Box), + IsBlankFunctionCall(Box), + IsLiteralFunctionCall(Box), + IsNumericFunctionCall(Box), + RegexFunctionCall(Box, Box, Option>), + CustomFunctionCall(NamedNode, Vec), + ExistsFunctionCall(Box), + CountAggregate(Option>, bool), + SumAggregate(Box, bool), + MinAggregate(Box, bool), + MaxAggregate(Box, bool), + AvgAggregate(Box, bool), + SampleAggregate(Box, bool), + GroupConcatAggregate(Box, bool, Option), +} + +impl fmt::Display for Expression { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Expression::ConstantExpression(t) => write!(f, "{}", t), + Expression::OrExpression(a, b) => write!(f, "({} || {})", a, b), + Expression::AndExpression(a, b) => write!(f, "({} && {})", a, b), + 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::>() + .join(", ") + ), + Expression::NotInExpression(a, b) => write!( + f, + "({} NOT IN ({}))", + a, + b.iter() + .map(|v| v.to_string()) + .collect::>() + .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::>() + .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::>() + .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::>() + .join(", ") + ), + Expression::ExistsFunctionCall(p) => write!(f, "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)) + }, + } + } +} + +impl From for Expression { + fn from(p: Literal) -> Self { + Expression::ConstantExpression(p.into()) + } +} + +struct SparqlExpression<'a>(&'a Expression); + +impl<'a> fmt::Display for SparqlExpression<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.0 { + Expression::ConstantExpression(t) => write!(f, "{}", t), + Expression::OrExpression(a, b) => write!( + f, + "({} || {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ), + Expression::AndExpression(a, b) => write!( + f, + "({} && {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ), + Expression::EqualExpression(a, b) => { + write!(f, "({} = {})", SparqlExpression(&*a), SparqlExpression(&*b)) + } + Expression::NotEqualExpression(a, b) => write!( + f, + "({} != {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ), + Expression::GreaterExpression(a, b) => { + write!(f, "({} > {})", SparqlExpression(&*a), SparqlExpression(&*b)) + } + Expression::GreaterOrEqExpression(a, b) => write!( + f, + "({} >= {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ), + Expression::LowerExpression(a, b) => { + write!(f, "({} < {})", SparqlExpression(&*a), SparqlExpression(&*b)) + } + Expression::LowerOrEqExpression(a, b) => write!( + f, + "({} <= {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ), + Expression::InExpression(a, b) => write!( + f, + "({} IN ({}))", + a, + b.iter() + .map(|v| SparqlExpression(v).to_string()) + .collect::>() + .join(", ") + ), + Expression::NotInExpression(a, b) => write!( + f, + "({} NOT IN ({}))", + a, + b.iter() + .map(|v| SparqlExpression(v).to_string()) + .collect::>() + .join(", ") + ), + Expression::AddExpression(a, b) => { + write!(f, "{} + {}", SparqlExpression(&*a), SparqlExpression(&*b)) + } + Expression::SubExpression(a, b) => { + write!(f, "{} - {}", SparqlExpression(&*a), SparqlExpression(&*b)) + } + Expression::MulExpression(a, b) => { + write!(f, "{} * {}", SparqlExpression(&*a), SparqlExpression(&*b)) + } + Expression::DivExpression(a, b) => { + write!(f, "{} / {}", SparqlExpression(&*a), SparqlExpression(&*b)) + } + Expression::UnaryPlusExpression(e) => write!(f, "+{}", SparqlExpression(&*e)), + Expression::UnaryMinusExpression(e) => write!(f, "-{}", SparqlExpression(&*e)), + Expression::UnaryNotExpression(e) => match e.as_ref() { + Expression::ExistsFunctionCall(p) => { + write!(f, "NOT EXISTS {{ {} }}", SparqlGraphPattern(&*p)) + } + e => write!(f, "!{}", e), + }, + Expression::StrFunctionCall(e) => write!(f, "STR({})", SparqlExpression(&*e)), + Expression::LangFunctionCall(e) => write!(f, "LANG({})", SparqlExpression(&*e)), + Expression::LangMatchesFunctionCall(a, b) => write!( + f, + "LANGMATCHES({}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ), + Expression::DatatypeFunctionCall(e) => write!(f, "DATATYPE({})", SparqlExpression(&*e)), + Expression::BoundFunctionCall(v) => write!(f, "BOUND({})", v), + Expression::IRIFunctionCall(e) => write!(f, "IRI({})", SparqlExpression(&*e)), + Expression::BNodeFunctionCall(v) => v.as_ref() + .map(|id| write!(f, "BOUND({})", SparqlExpression(&*id))) + .unwrap_or_else(|| write!(f, "BOUND()")), + Expression::RandFunctionCall() => write!(f, "RAND()"), + Expression::AbsFunctionCall(e) => write!(f, "ABS({})", SparqlExpression(&*e)), + Expression::CeilFunctionCall(e) => write!(f, "CEIL({})", SparqlExpression(&*e)), + Expression::FloorFunctionCall(e) => write!(f, "FLOOR({})", SparqlExpression(&*e)), + Expression::RoundFunctionCall(e) => write!(f, "ROUND({})", SparqlExpression(&*e)), + Expression::ConcatFunctionCall(e) => write!( + f, + "CONCAT({})", + e.iter() + .map(|v| SparqlExpression(v).to_string()) + .collect::>() + .join(", ") + ), + Expression::SubStrFunctionCall(a, b, c) => c.as_ref() + .map(|cv| { + write!( + f, + "SUBSTR({}, {}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b), + SparqlExpression(cv) + ) + }) + .unwrap_or_else(|| { + write!( + f, + "SUBSTR({}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ) + }), + Expression::StrLenFunctionCall(e) => write!(f, "STRLEN({})", SparqlExpression(&*e)), + Expression::ReplaceFunctionCall(a, b, c, d) => d.as_ref() + .map(|dv| { + write!( + f, + "REPLACE({}, {}, {}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b), + SparqlExpression(&*c), + dv + ) + }) + .unwrap_or_else(|| { + write!( + f, + "REPLACE({}, {}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b), + SparqlExpression(&*c) + ) + }), + Expression::UCaseFunctionCall(e) => write!(f, "UCASE({})", SparqlExpression(&*e)), + Expression::LCaseFunctionCall(e) => write!(f, "LCASE({})", SparqlExpression(&*e)), + Expression::EncodeForURIFunctionCall(e) => { + write!(f, "ENCODE_FOR_URI({})", SparqlExpression(&*e)) + } + Expression::ContainsFunctionCall(a, b) => write!( + f, + "CONTAINS({}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ), + Expression::StrStartsFunctionCall(a, b) => write!( + f, + "STRSTATS({}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ), + Expression::StrEndsFunctionCall(a, b) => write!( + f, + "STRENDS({}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ), + Expression::StrBeforeFunctionCall(a, b) => write!( + f, + "STRBEFORE({}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ), + Expression::StrAfterFunctionCall(a, b) => write!( + f, + "STRAFTER({}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ), + Expression::YearFunctionCall(e) => write!(f, "YEAR({})", SparqlExpression(&*e)), + Expression::MonthFunctionCall(e) => write!(f, "MONTH({})", SparqlExpression(&*e)), + Expression::DayFunctionCall(e) => write!(f, "DAY({})", SparqlExpression(&*e)), + Expression::HoursFunctionCall(e) => write!(f, "HOURS({})", SparqlExpression(&*e)), + Expression::MinutesFunctionCall(e) => write!(f, "MINUTES({})", SparqlExpression(&*e)), + Expression::SecondsFunctionCall(e) => write!(f, "SECONDS({})", SparqlExpression(&*e)), + Expression::TimezoneFunctionCall(e) => write!(f, "TIMEZONE({})", SparqlExpression(&*e)), + Expression::NowFunctionCall() => write!(f, "NOW()"), + Expression::UUIDFunctionCall() => write!(f, "UUID()"), + Expression::StrUUIDFunctionCall() => write!(f, "STRUUID()"), + Expression::MD5FunctionCall(e) => write!(f, "MD5({})", SparqlExpression(&*e)), + Expression::SHA1FunctionCall(e) => write!(f, "SHA1({})", SparqlExpression(&*e)), + Expression::SHA256FunctionCall(e) => write!(f, "SHA256({})", SparqlExpression(&*e)), + Expression::SHA384FunctionCall(e) => write!(f, "SHA384({})", SparqlExpression(&*e)), + Expression::SHA512FunctionCall(e) => write!(f, "SHA512({})", SparqlExpression(&*e)), + Expression::CoalesceFunctionCall(e) => write!( + f, + "COALESCE({})", + e.iter() + .map(|v| SparqlExpression(&*v).to_string()) + .collect::>() + .join(", ") + ), + Expression::IfFunctionCall(a, b, c) => write!( + f, + "IF({}, {}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b), + SparqlExpression(&*c) + ), + Expression::StrLangFunctionCall(a, b) => write!( + f, + "STRLANG({}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ), + Expression::StrDTFunctionCall(a, b) => write!( + f, + "STRDT({}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ), + Expression::SameTermFunctionCall(a, b) => write!( + f, + "sameTerm({}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ), + Expression::IsIRIFunctionCall(e) => write!(f, "isIRI({})", SparqlExpression(&*e)), + Expression::IsBlankFunctionCall(e) => write!(f, "isBLANK({})", SparqlExpression(&*e)), + Expression::IsLiteralFunctionCall(e) => { + write!(f, "isLITERAL({})", SparqlExpression(&*e)) + } + Expression::IsNumericFunctionCall(e) => { + write!(f, "isNUMERIC({})", SparqlExpression(&*e)) + } + Expression::RegexFunctionCall(a, b, c) => c.as_ref() + .map(|cv| { + write!( + f, + "REGEX({}, {}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b), + cv + ) + }) + .unwrap_or_else(|| { + write!( + f, + "REGEX({}, {})", + SparqlExpression(&*a), + SparqlExpression(&*b) + ) + }), + Expression::CustomFunctionCall(iri, args) => write!( + f, + "{}({})", + iri, + args.iter() + .map(|v| SparqlExpression(v).to_string()) + .collect::>() + .join(", ") + ), + Expression::ExistsFunctionCall(p) => { + write!(f, "EXISTS {{ {} }}", SparqlGraphPattern(&*p)) + } + Expression::CountAggregate(e, distinct) => if *distinct { + e.as_ref() + .map(|ex| write!(f, "COUNT(DISTINCT {})", SparqlExpression(ex))) + .unwrap_or_else(|| write!(f, "COUNT(DISTINCT *)")) + } else { + e.as_ref() + .map(|ex| write!(f, "COUNT({})", SparqlExpression(ex))) + .unwrap_or_else(|| write!(f, "COUNT(*)")) + }, + Expression::SumAggregate(e, distinct) => if *distinct { + write!(f, "SUM(DISTINCT {})", SparqlExpression(e)) + } else { + write!(f, "SUM({})", SparqlExpression(e)) + }, + Expression::MinAggregate(e, distinct) => if *distinct { + write!(f, "MIN(DISTINCT {})", SparqlExpression(e)) + } else { + write!(f, "MIN({})", SparqlExpression(e)) + }, + Expression::MaxAggregate(e, distinct) => if *distinct { + write!(f, "MAX(DISTINCT {})", SparqlExpression(e)) + } else { + write!(f, "MAX({})", SparqlExpression(e)) + }, + Expression::AvgAggregate(e, distinct) => if *distinct { + write!(f, "AVG(DISTINCT {})", SparqlExpression(e)) + } else { + write!(f, "AVG({})", SparqlExpression(e)) + }, + Expression::SampleAggregate(e, distinct) => if *distinct { + write!(f, "SAMPLE(DISTINCT {})", SparqlExpression(e)) + } else { + write!(f, "SAMPLE({})", SparqlExpression(e)) + }, + Expression::GroupConcatAggregate(e, distinct, sep) => if *distinct { + sep.as_ref() + .map(|s| { + write!( + f, + "GROUP_CONCAT(DISTINCT {}; SEPARATOR = \"{}\")", + SparqlExpression(e), + s.escape() + ) + }) + .unwrap_or_else(|| write!(f, "GROUP_CONCAT(DISTINCT {})", SparqlExpression(e))) + } else { + sep.as_ref() + .map(|s| { + write!( + f, + "GROUP_CONCAT({}; SEPARATOR = \"{}\")", + SparqlExpression(e), + s.escape() + ) + }) + .unwrap_or_else(|| write!(f, "GROUP_CONCAT({})", SparqlExpression(e))) + }, + } + } +} + +#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] +pub enum GraphPattern { + Multiset(Vec), + BGP(Vec), + Join(Box, Box), + LeftJoin(Box, Box, Expression), + Filter(Expression, Box), + Union(Box, Box), + Graph(NamedNodeOrVariable, Box), + Extend(Box, Variable, Expression), + Minus(Box, Box), + Group(), + Aggregation(), + AggregateJoin(), + OrderBy(Box), + Project(Box), + Distinct(Box), + Reduced(Box), + Slice(Box, usize, usize), +} + +impl fmt::Display for GraphPattern { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + GraphPattern::BGP(p) => write!( + f, + "BGP({})", + p.iter() + .map(|v| v.to_string()) + .collect::>() + .join(" . ") + ), + GraphPattern::Join(a, b) => write!(f, "Join({}, {})", a, b), + GraphPattern::LeftJoin(a, b, e) => write!(f, "LeftJoin({}, {}, {})", a, b, e), + GraphPattern::Filter(e, p) => write!(f, "Filter({}, {})", e, p), + GraphPattern::Union(a, b) => write!(f, "Union({}, {})", a, b), + GraphPattern::Graph(g, p) => write!(f, "Graph({}, {})", g, p), + GraphPattern::Extend(p, v, e) => write!(f, "Extend({}), {}, {})", p, v, e), + GraphPattern::Minus(a, b) => write!(f, "Minus({}, {})", a, b), + _ => Ok(()), //TODO + } + } +} + +impl Default for GraphPattern { + fn default() -> Self { + GraphPattern::BGP(Vec::default()) + } +} + +impl From for GraphPattern { + fn from(p: PropertyPathPattern) -> Self { + GraphPattern::BGP(vec![p]) + } +} + +struct SparqlGraphPattern<'a>(&'a GraphPattern); + +impl<'a> fmt::Display for SparqlGraphPattern<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.0 { + GraphPattern::BGP(p) => { + if p.is_empty() { + write!(f, "{{}}") + } else { + write!( + f, + "{}", + p.iter() + .map(|v| SparqlPropertyPathPattern(v).to_string()) + .collect::>() + .join(" . ") + ) + } + } + GraphPattern::Join(a, b) => { + write!(f, "{} {}", SparqlGraphPattern(&*a), SparqlGraphPattern(&*b)) + } + GraphPattern::LeftJoin(a, b, e) => write!( + f, + "{} OPTIONAL {{ {} FILTER({}) }}", + SparqlGraphPattern(&*a), + SparqlGraphPattern(&*b), + SparqlExpression(e) + ), + GraphPattern::Filter(e, p) => write!( + f, + "{} FILTER({})", + SparqlGraphPattern(&*p), + SparqlExpression(e) + ), + GraphPattern::Union(a, b) => write!( + f, + "{{ {} }} UNION {{ {} }}", + SparqlGraphPattern(&*a), + SparqlGraphPattern(&*b) + ), + GraphPattern::Graph(g, p) => write!(f, "GRAPH {} {{ {} }}", g, SparqlGraphPattern(&*p)), + GraphPattern::Extend(p, v, e) => write!( + f, + "{} BIND({} AS {})", + SparqlGraphPattern(&*p), + SparqlExpression(e), + v + ), + GraphPattern::Minus(a, b) => write!( + f, + "{} MINUS {{ {} }}", + SparqlGraphPattern(&*a), + SparqlGraphPattern(&*b) + ), + _ => write!(f, "{{}}"), //TODO + } + } +} + +#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash, Default)] +pub struct Dataset { + pub default: Vec, + pub named: Vec, +} + +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>, +} + +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::>() + .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, + 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, + SparqlGraphPattern(&filter) + ), + Query::ConstructQuery { + construct, + dataset, + filter, + } => write!( + f, + "CONSTRUCT {{ {} }} {} WHERE {{ {} }}", + construct + .iter() + .map(|t| t.to_string()) + .collect::>() + .join(" . "), + dataset, + SparqlGraphPattern(&filter) + ), + Query::DescribeQuery { dataset, filter } => write!( + f, + "DESCRIBE {} WHERE {{ {} }}", + dataset, + SparqlGraphPattern(&filter) + ), + Query::AskQuery { dataset, filter } => write!( + f, + "ASK {} WHERE {{ {} }}", + dataset, + SparqlGraphPattern(&filter) + ), + } + } +} + +/* TODO: tests +/// Implementation of https://www.w3.org/TR/2013/REC-sparql11-query-20130321/#sparqlAlgebraExamples +#[test] +fn test_sparql_algebra_examples() { + assert_eq!( + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p"), + Variable::new("o") + )).try_into(), + Ok(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p"), + Variable::new("o"), + )])) + ); + + assert_eq!( + ast::GraphPattern::GroupPattern(vec![ + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )), + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + )), + ]).try_into(), + Ok(GraphPattern::BGP(vec![ + PropertyPathPattern::new(Variable::new("s"), Variable::new("p1"), Variable::new("v1")), + PropertyPathPattern::new(Variable::new("s"), Variable::new("p2"), Variable::new("v2")), + ])) + ); + + assert_eq!( + ast::GraphPattern::UnionPattern(vec![ + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )), + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + )), + ]).try_into(), + Ok(GraphPattern::Union( + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )])), + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + )])), + )) + ); + + assert_eq!( + ast::GraphPattern::UnionPattern(vec![ + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )), + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + )), + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p3"), + Variable::new("v3"), + )), + ]).try_into(), + Ok(GraphPattern::Union( + Box::new(GraphPattern::Union( + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )])), + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + )])), + )), + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p3"), + Variable::new("v3"), + )])), + )) + ); + + assert_eq!( + ast::GraphPattern::GroupPattern(vec![ + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )), + ast::GraphPattern::OptionalPattern(Box::new(ast::GraphPattern::PropertyPathPattern( + ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + ), + ))), + ]).try_into(), + Ok(GraphPattern::LeftJoin( + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )])), + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + )])), + ast::Expression::ConstantExpression(Literal::from(true).into()), + )) + ); + + assert_eq!( + ast::GraphPattern::GroupPattern(vec![ + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )), + ast::GraphPattern::OptionalPattern(Box::new(ast::GraphPattern::PropertyPathPattern( + ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + ), + ))), + ast::GraphPattern::OptionalPattern(Box::new(ast::GraphPattern::PropertyPathPattern( + ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p3"), + Variable::new("v3"), + ), + ))), + ]).try_into(), + Ok(GraphPattern::LeftJoin( + Box::new(GraphPattern::LeftJoin( + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )])), + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + )])), + ast::Expression::ConstantExpression(Literal::from(true).into()), + )), + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p3"), + Variable::new("v3"), + )])), + ast::Expression::ConstantExpression(Literal::from(true).into()), + )) + ); + + assert_eq!( + ast::GraphPattern::GroupPattern(vec![ + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )), + ast::GraphPattern::OptionalPattern(Box::new(ast::GraphPattern::GroupPattern(vec![ + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + )), + ast::GraphPattern::FilterPattern(ast::Expression::LowerExpression( + Box::new(ast::Expression::ConstantExpression( + Variable::new("v1").into(), + )), + Box::new(ast::Expression::ConstantExpression(Literal::from(3).into())), + )), + ]))), + ]).try_into(), + Ok(GraphPattern::LeftJoin( + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )])), + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + )])), + ast::Expression::LowerExpression( + Box::new(ast::Expression::ConstantExpression( + Variable::new("v1").into(), + )), + Box::new(ast::Expression::ConstantExpression(Literal::from(3).into())), + ), + )) + ); + + assert_eq!( + ast::GraphPattern::GroupPattern(vec![ + ast::GraphPattern::UnionPattern(vec![ + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )), + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + )), + ]), + ast::GraphPattern::OptionalPattern(Box::new(ast::GraphPattern::PropertyPathPattern( + ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p3"), + Variable::new("v3"), + ), + ))), + ]).try_into(), + Ok(GraphPattern::LeftJoin( + Box::new(GraphPattern::Union( + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )])), + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + )])), + )), + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p3"), + Variable::new("v3"), + )])), + ast::Expression::ConstantExpression(Literal::from(true).into()), + )) + ); + + assert_eq!( + ast::GraphPattern::GroupPattern(vec![ + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )), + ast::GraphPattern::FilterPattern(ast::Expression::LowerExpression( + Box::new(ast::Expression::ConstantExpression( + Variable::new("v1").into(), + )), + Box::new(ast::Expression::ConstantExpression(Literal::from(3).into())), + )), + ast::GraphPattern::OptionalPattern(Box::new(ast::GraphPattern::PropertyPathPattern( + ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + ), + ))), + ]).try_into(), + Ok(GraphPattern::Filter( + ast::Expression::LowerExpression( + Box::new(ast::Expression::ConstantExpression( + Variable::new("v1").into(), + )), + Box::new(ast::Expression::ConstantExpression(Literal::from(3).into())), + ), + Box::new(GraphPattern::LeftJoin( + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v1"), + )])), + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p2"), + Variable::new("v2"), + )])), + ast::Expression::ConstantExpression(Literal::from(true).into()), + )), + )) + ); + + assert_eq!( + ast::GraphPattern::GroupPattern(vec![ + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p"), + Variable::new("v"), + )), + ast::GraphPattern::BindPattern( + ast::Expression::MulExpression( + Box::new(ast::Expression::ConstantExpression(Literal::from(2).into())), + Box::new(ast::Expression::ConstantExpression( + Variable::new("v").into(), + )), + ), + Variable::new("v2"), + ), + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v2"), + )), + ]).try_into(), + Ok(GraphPattern::Join( + Box::new(GraphPattern::Extend( + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p"), + Variable::new("v"), + )])), + Variable::new("v2"), + ast::Expression::MulExpression( + Box::new(ast::Expression::ConstantExpression(Literal::from(2).into())), + Box::new(ast::Expression::ConstantExpression( + Variable::new("v").into(), + )), + ), + )), + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v2"), + )])), + )) + ); + + //TODO: { ?s :p ?v . {} BIND (2*?v AS ?v2) } + + assert_eq!( + ast::GraphPattern::GroupPattern(vec![ + ast::GraphPattern::PropertyPathPattern(ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p"), + Variable::new("v"), + )), + ast::GraphPattern::MinusPattern(Box::new(ast::GraphPattern::PropertyPathPattern( + ast::PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v2"), + ), + ))), + ]).try_into(), + Ok(GraphPattern::Minus( + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p"), + Variable::new("v"), + )])), + Box::new(GraphPattern::BGP(vec![PropertyPathPattern::new( + Variable::new("s"), + Variable::new("p1"), + Variable::new("v2"), + )])), + )) + ); + + //TODO { ?s :p ?o . {SELECT DISTINCT ?o {?o ?p ?z} } } +}*/ diff --git a/src/sparql/ast.rs b/src/sparql/ast.rs deleted file mode 100644 index 220116c0..00000000 --- a/src/sparql/ast.rs +++ /dev/null @@ -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, - predicate: impl Into, - object: impl Into, - ) -> 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), - SequencePath(Vec), - AlternativePath(Vec), - ZeroOrMorePath(Box), - OneOrMorePath(Box), - ZeroOrOnePath(Box), - NegatedPath(Box), -} - -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::>() - .join(" / ") - ), - PropertyPath::AlternativePath(ps) => write!( - f, - "({})", - ps.iter() - .map(|v| v.to_string()) - .collect::>() - .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 for PropertyPath { - fn from(p: NamedNodeOrVariable) -> Self { - PropertyPath::PredicatePath(p) - } -} - -impl From for PropertyPath { - fn from(p: NamedNode) -> Self { - PropertyPath::PredicatePath(p.into()) - } -} - -impl From 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, - path: impl Into, - object: impl Into, - ) -> 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 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), - AndExpression(Vec), - EqualExpression(Box, Box), - NotEqualExpression(Box, Box), - GreaterExpression(Box, Box), - GreaterOrEqExpression(Box, Box), - LowerExpression(Box, Box), - LowerOrEqExpression(Box, Box), - InExpression(Box, Vec), - NotInExpression(Box, Vec), - AddExpression(Box, Box), - SubExpression(Box, Box), - MulExpression(Box, Box), - DivExpression(Box, Box), - UnaryPlusExpression(Box), - UnaryMinusExpression(Box), - UnaryNotExpression(Box), - StrFunctionCall(Box), - LangFunctionCall(Box), - LangMatchesFunctionCall(Box, Box), - DatatypeFunctionCall(Box), - BoundFunctionCall(Variable), - IRIFunctionCall(Box), - BNodeFunctionCall(Option>), - RandFunctionCall(), - AbsFunctionCall(Box), - CeilFunctionCall(Box), - FloorFunctionCall(Box), - RoundFunctionCall(Box), - ConcatFunctionCall(Vec), - SubStrFunctionCall(Box, Box, Option>), - StrLenFunctionCall(Box), - ReplaceFunctionCall( - Box, - Box, - Box, - Option>, - ), - UCaseFunctionCall(Box), - LCaseFunctionCall(Box), - EncodeForURIFunctionCall(Box), - ContainsFunctionCall(Box, Box), - StrStartsFunctionCall(Box, Box), - StrEndsFunctionCall(Box, Box), - StrBeforeFunctionCall(Box, Box), - StrAfterFunctionCall(Box, Box), - YearFunctionCall(Box), - MonthFunctionCall(Box), - DayFunctionCall(Box), - HoursFunctionCall(Box), - MinutesFunctionCall(Box), - SecondsFunctionCall(Box), - TimezoneFunctionCall(Box), - NowFunctionCall(), - UUIDFunctionCall(), - StrUUIDFunctionCall(), - MD5FunctionCall(Box), - SHA1FunctionCall(Box), - SHA256FunctionCall(Box), - SHA384FunctionCall(Box), - SHA512FunctionCall(Box), - CoalesceFunctionCall(Vec), - IfFunctionCall(Box, Box, Box), - StrLangFunctionCall(Box, Box), - StrDTFunctionCall(Box, Box), - SameTermFunctionCall(Box, Box), - IsIRIFunctionCall(Box), - IsBlankFunctionCall(Box), - IsLiteralFunctionCall(Box), - IsNumericFunctionCall(Box), - RegexFunctionCall(Box, Box, Option>), - CustomFunctionCall(NamedNode, Vec), - ExistsFunctionCall(Box), - NotExistsFunctionCall(Box), - CountAggregate(Option>, bool), - SumAggregate(Box, bool), - MinAggregate(Box, bool), - MaxAggregate(Box, bool), - AvgAggregate(Box, bool), - SampleAggregate(Box, bool), - GroupConcatAggregate(Box, bool, Option), -} - -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::>() - .join(" || ") - ), - Expression::AndExpression(e) => write!( - f, - "({})", - e.iter() - .map(|v| v.to_string()) - .collect::>() - .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::>() - .join(", ") - ), - Expression::NotInExpression(a, b) => write!( - f, - "{} NOT IN ({})", - a, - b.iter() - .map(|v| v.to_string()) - .collect::>() - .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::>() - .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::>() - .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::>() - .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), - PropertyPathPattern(PropertyPathPattern), - OptionalPattern(Box), - UnionPattern(Vec), - GraphPattern(NamedNodeOrVariable, Box), - BindPattern(Expression, Variable), - ValuesPattern(Vec, Vec>>), - GroupByPattern(Expression), - HavingPattern(Expression), - MinusPattern(Box), - FilterPattern(Expression), - SubSelectPattern { - selection: Selection, - filter: Box, - }, - ServicePattern(NamedNodeOrVariable, Box), -} - -impl Default for GraphPattern { - fn default() -> Self { - GraphPattern::GroupPattern(Vec::default()) - } -} - -impl From 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::>() - .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::>() - .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::>() - .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::>() - .join(" ") - )) - .collect::>() - .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, - pub named: Vec, -} - -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>, -} - -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::>() - .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, - 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::>() - .join(" . "), - dataset, - filter - ), - Query::DescribeQuery { dataset, filter } => { - write!(f, "DESCRIBE {} WHERE {{ {} }}", dataset, filter) - } - Query::AskQuery { dataset, filter } => { - write!(f, "ASK {} WHERE {{ {} }}", dataset, filter) - } - } - } -} diff --git a/src/sparql/mod.rs b/src/sparql/mod.rs index 55886577..47813087 100644 --- a/src/sparql/mod.rs +++ b/src/sparql/mod.rs @@ -1,3 +1,3 @@ -pub mod ast; +pub mod algebra; pub mod model; pub mod parser; diff --git a/src/sparql/model.rs b/src/sparql/model.rs index f64cda9a..4fd1c844 100644 --- a/src/sparql/model.rs +++ b/src/sparql/model.rs @@ -1,4 +1,5 @@ use model::*; +use std::collections::BTreeMap; use std::fmt; use uuid::Uuid; @@ -113,3 +114,52 @@ impl From 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, + predicate: impl Into, + object: impl Into, + ) -> 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); + +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()) + } +} diff --git a/src/sparql/parser.rs b/src/sparql/parser.rs index 4e3bcbf6..86395619 100644 --- a/src/sparql/parser.rs +++ b/src/sparql/parser.rs @@ -6,7 +6,7 @@ mod grammar { use model::*; use rio::RioError; use rio::RioResult; - use sparql::ast::*; + use sparql::algebra::*; use sparql::model::*; use sparql::parser::unescape_unicode_codepoints; use std::borrow::Cow; @@ -90,33 +90,53 @@ mod grammar { } } - impl From> for GraphPattern { - fn from(input: FocusedPropertyPathPattern) -> Self { - if input.patterns.len() == 1 { - input.patterns[0].clone().into() - } else { - GraphPattern::GroupPattern(input.patterns.into_iter().map(|p| p.into()).collect()) + #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] + enum PartialGraphPattern { + Optional(GraphPattern), + Minus(GraphPattern), + Bind(Expression, Variable), + 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) -> GraphPattern { - let l: Vec = v.into_iter() - .flat_map(|p| { - if let GraphPattern::GroupPattern(v2) = p { - v2.into_iter() - } else { - vec![p].into_iter() - } - }) - .collect(); - if l.len() == 1 { - l[0].clone() - } else { - GraphPattern::GroupPattern(l) + //Merge BGPs + match (l, r) { + (GraphPattern::BGP(mut pl), GraphPattern::BGP(pr)) => { + pl.extend_from_slice(&pr); + GraphPattern::BGP(pl) + } + (l, r) => GraphPattern::Join(Box::new(l), Box::new(r)), } } + fn not_empty_fold( + iter: impl Iterator, + combine: impl Fn(T, T) -> T, + ) -> Result { + 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 { + Left(L), + Right(R), + } + pub struct ParserState { base_uri: Option, namespaces: HashMap, diff --git a/src/sparql/sparql_grammar.rustpeg b/src/sparql/sparql_grammar.rustpeg index 12de5577..a6a81299 100644 --- a/src/sparql/sparql_grammar.rustpeg +++ b/src/sparql/sparql_grammar.rustpeg @@ -4,7 +4,6 @@ use std::char; use model::vocab::rdf; use model::vocab::xsd; use std::iter; -use std::iter::once; #![arguments(state: &mut ParserState)] @@ -48,10 +47,11 @@ SelectQuery -> Query = s:SelectClause _ d:DatasetClauses _ f:WhereClause _ Solut //[8] SubSelect -> GraphPattern = s:SelectClause _ f:WhereClause _ SolutionModifier _ ValuesClause { //TODO: Modifiers - GraphPattern::SubSelectPattern { + GraphPattern::default() + /*TODO GraphPattern::SubSelectPattern { selection: s, filter: Box::new(f) - } + }*/ } //[9] @@ -81,7 +81,7 @@ ConstructQuery -> Query = Query::ConstructQuery { construct: c.clone(), 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 //[21] -HavingClause -> Expression = "HAVING"i _ c:HavingCondition+ { - if c.len() == 1 { - c[0].clone() - } else { - Expression::AndExpression(c) - } +HavingClause -> Expression = "HAVING"i _ e:HavingCondition+ {? + not_empty_fold(e.into_iter(), |a, b| Expression::AndExpression(Box::new(a), Box::new(b))) } //[22] @@ -179,67 +175,107 @@ GroupGraphPattern -> GraphPattern = //[54] 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 { - list.extend_from_slice(&v) + p.extend_from_slice(&v) + } + let mut filter: Option = 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 = a:GraphPatternNotTriples _ ('.' _)? b:TriplesBlock? _ { +GroupGraphPatternSub_item -> Vec = a:GraphPatternNotTriples _ ('.' _)? b:TriplesBlock? _ { let mut result = vec![a]; - b.map(|v| result.push(v)); + b.map(|v| result.push(PartialGraphPattern::Other(GraphPattern::BGP(v)))); result } //[55] -TriplesBlock -> GraphPattern = h:TriplesSameSubjectPath _ t:TriplesBlock_tail? { - match t { - Some(l) => flatten_group_pattern(vec![h, l].into_iter()), - None => flatten_group_pattern(once(h)) +TriplesBlock -> Vec = h:TriplesSameSubjectPath _ t:TriplesBlock_tail? { + let mut triples = h; + if let Some(l) = t { + triples.extend_from_slice(&l) } + triples } -TriplesBlock_tail -> GraphPattern = '.' _ t:TriplesBlock? _ { - t.unwrap_or_else(|| GraphPattern::GroupPattern(Vec::default())) +TriplesBlock_tail -> Vec = '.' _ t:TriplesBlock? _ { + t.unwrap_or_else(|| Vec::default()) } //[56] -GraphPatternNotTriples -> GraphPattern = GroupOrUnionGraphPattern / OptionalGraphPattern / MinusGraphPattern / GraphGraphPattern / ServiceGraphPattern / Filter / Bind / InlineData +GraphPatternNotTriples -> PartialGraphPattern = GroupOrUnionGraphPattern / OptionalGraphPattern / MinusGraphPattern / GraphGraphPattern / ServiceGraphPattern / Filter / Bind / InlineData //[57] -OptionalGraphPattern -> GraphPattern = "OPTIONAL"i _ p:GroupGraphPattern { - GraphPattern::OptionalPattern(Box::new(p)) +OptionalGraphPattern -> PartialGraphPattern = "OPTIONAL"i _ p:GroupGraphPattern { + PartialGraphPattern::Optional(p) } //[58] -GraphGraphPattern -> GraphPattern = "GRAPH"i _ g:VarOrIri _ p:GroupGraphPattern { - GraphPattern::GraphPattern(g, Box::new(p)) +GraphGraphPattern -> PartialGraphPattern = "GRAPH"i _ g:VarOrIri _ p:GroupGraphPattern { + PartialGraphPattern::Other(GraphPattern::Graph(g, Box::new(p))) } //[59] -ServiceGraphPattern -> GraphPattern = "SERVICE"i _ "SILENT"i? _ s:VarOrIri _ p:GroupGraphPattern { - GraphPattern::ServicePattern(s, Box::new(p)) +ServiceGraphPattern -> PartialGraphPattern = "SERVICE"i _ "SILENT"i? _ s:VarOrIri _ p:GroupGraphPattern { + PartialGraphPattern::Other(GraphPattern::default()) + //TODO PartialGraphPattern::Other(GraphPattern::ServicePattern(s, Box::new(p))) } //[60] -Bind -> GraphPattern = "BIND"i _ '(' _ e:Expression _ "AS"i _ v:Var _ ')' { - GraphPattern::BindPattern(e, v) +Bind -> PartialGraphPattern = "BIND"i _ '(' _ e:Expression _ "AS"i _ v:Var _ ')' { + PartialGraphPattern::Bind(e, v) } //[61] -InlineData -> GraphPattern = "VALUES"i _ p:DataBlock { p } +InlineData -> PartialGraphPattern = "VALUES"i _ p:DataBlock { PartialGraphPattern::Other(p) } //[62] DataBlock -> GraphPattern = InlineDataOneVar / InlineDataFull //[63] -InlineDataOneVar -> GraphPattern = v:Var _ '{' _ d:InlineDataOneVar_value* '}' { - GraphPattern::ValuesPattern(vec![v], d) +InlineDataOneVar -> GraphPattern = var:Var _ '{' _ d:InlineDataOneVar_value* '}' { + 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> = t:DataBlockValue { vec![t] } +InlineDataOneVar_value -> Option = t:DataBlockValue { t } //[64] -InlineDataFull -> GraphPattern = '(' _ var:InlineDataFull_var* _ ')' _ '{' _ val:InlineDataFull_values* '}' { - GraphPattern::ValuesPattern(var, val) +InlineDataFull -> GraphPattern = '(' _ vars:InlineDataFull_var* _ ')' _ '{' _ val:InlineDataFull_values* '}' { + 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_values -> Vec> = '(' _ v:InlineDataFull_value* _ ')' _ { v } @@ -254,22 +290,22 @@ DataBlockValue -> Option = "UNDEF"i { None } //[66] -MinusGraphPattern -> GraphPattern = "MINUS"i _ p: GroupGraphPattern { - GraphPattern::MinusPattern(Box::new(p)) +MinusGraphPattern -> PartialGraphPattern = "MINUS"i _ p: GroupGraphPattern { + PartialGraphPattern::Minus(p) } //[67] -GroupOrUnionGraphPattern -> GraphPattern = p:GroupOrUnionGraphPattern_item **<1,> ("UNION"i _) { - if p.len() == 1 { - p[0].clone() - } else { - GraphPattern::UnionPattern(p) - } +GroupOrUnionGraphPattern -> PartialGraphPattern = p:GroupOrUnionGraphPattern_item **<1,> ("UNION"i _) {? + not_empty_fold(p.into_iter(), |a, b| { + GraphPattern::Union(Box::new(a), Box::new(b)) + }).map(PartialGraphPattern::Other) } GroupOrUnionGraphPattern_item -> GraphPattern = p:GroupGraphPattern _ { p } //[68] -Filter -> GraphPattern = "FILTER"i _ c:Constraint { GraphPattern::FilterPattern(c) } +Filter -> PartialGraphPattern = "FILTER"i _ c:Constraint { + PartialGraphPattern::Filter(c) +} //[69] Constraint -> Expression = BrackettedExpression / BuiltInCall / FunctionCall @@ -337,7 +373,7 @@ PropertyListNotEmpty -> FocusedTriplePattern FocusedTriplePattern<(NamedNodeOrVariable,Vec)> = p:Verb _ o:ObjectList _ { FocusedTriplePattern { - focus: (p, o.focus), + focus: (p.into(), o.focus), patterns: o.patterns } } @@ -359,7 +395,7 @@ ObjectList_item -> FocusedTriplePattern = o:Object _ { o } Object -> FocusedTriplePattern = GraphNode //[81] -TriplesSameSubjectPath -> GraphPattern = +TriplesSameSubjectPath -> Vec = s:VarOrTerm _ po:PropertyListPathNotEmpty { let mut patterns = po.patterns; for (p, os) in po.focus { @@ -367,7 +403,7 @@ TriplesSameSubjectPath -> GraphPattern = patterns.push(PropertyPathPattern::new(s.clone(), p.clone(), o)) } } - flatten_group_pattern(patterns.into_iter().map(|p| p.into())) + patterns } / s:TriplesNodePath _ po:PropertyListPath { let mut patterns = s.patterns; @@ -377,7 +413,7 @@ TriplesSameSubjectPath -> GraphPattern = patterns.push(PropertyPathPattern::new(s.focus.clone(), p.clone(), o)) } } - flatten_group_pattern(patterns.into_iter().map(|p| p.into())) + patterns } //[82] @@ -428,22 +464,18 @@ ObjectPath -> FocusedPropertyPathPattern = GraphNodePath Path -> PropertyPath = PathAlternative //[89] -PathAlternative -> PropertyPath = p:PathAlternative_item **<1,> ('|' _) { - if p.len() == 1 { - p[0].clone() - } else { - PropertyPath::SequencePath(p) - } +PathAlternative -> PropertyPath = p:PathAlternative_item **<1,> ('|' _) {? + not_empty_fold(p.into_iter(), |a, b| { + PropertyPath::AlternativePath(Box::new(a), Box::new(b)) + }) } PathAlternative_item -> PropertyPath = p:PathSequence _ { p } //[90] -PathSequence -> PropertyPath = p:PathSequence_item **<1,> ('/' _) { - if p.len() == 1 { - p[0].clone() - } else { - PropertyPath::AlternativePath(p) - } +PathSequence -> PropertyPath = p:PathSequence_item **<1,> ('/' _) {? + not_empty_fold(p.into_iter(), |a, b| { + PropertyPath::SequencePath(Box::new(a), Box::new(b)) + }) } PathSequence_item -> PropertyPath = p:PathEltOrInverse _ { p } @@ -462,19 +494,43 @@ PathEltOrInverse -> PropertyPath = //[94] PathPrimary -> PropertyPath = v:Verb { v.into() } / - '!' _ p:PathNegatedPropertySet { PropertyPath::NegatedPath(Box::new(p)) } / + '!' _ p:PathNegatedPropertySet { p } / '(' _ p:Path _ ')' { p } //[95] PathNegatedPropertySet -> PropertyPath = - '(' _ p:PathNegatedPropertySet_item **<1,> ('|' _) ')' { PropertyPath::AlternativePath(p) } / - PathOneInPropertySet -PathNegatedPropertySet_item -> PropertyPath = p:PathOneInPropertySet _ { p } + '(' _ p:PathNegatedPropertySet_item **<1,> ('|' _) ')' { + let mut direct = Vec::default(); + 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 = p:PathOneInPropertySet _ { p } //[96] -PathOneInPropertySet -> PropertyPath = - '^' _ v:Verb { PropertyPath::InversePath(Box::new(v.into())) } / - v:Verb { v.into() } +PathOneInPropertySet -> Either = + '^' _ v:Verb { Either::Right(v) } / + v:Verb { Either::Left(v) } //[98] TriplesNode -> FocusedTriplePattern = Collection / BlankNodePropertyList @@ -584,22 +640,14 @@ GraphTerm -> Term = Expression -> Expression = e:ConditionalOrExpression {e} //[111] -ConditionalOrExpression -> Expression = e:ConditionalOrExpression_item **<1,> ("||" _) { - if e.len() == 1 { - e[0].clone() - } else { - Expression::OrExpression(e) - } +ConditionalOrExpression -> Expression = e:ConditionalOrExpression_item **<1,> ("||" _) {? + not_empty_fold(e.into_iter(), |a, b| Expression::OrExpression(Box::new(a), Box::new(b))) } ConditionalOrExpression_item -> Expression = e:ConditionalAndExpression _ { e } //[112] -ConditionalAndExpression -> Expression = e:ConditionalAndExpression_item **<1,> ("&&" _) { - if e.len() == 1 { - e[0].clone() - } else { - Expression::AndExpression(e) - } +ConditionalAndExpression -> Expression = e:ConditionalAndExpression_item **<1,> ("&&" _) {? + not_empty_fold(e.into_iter(), |a, b| Expression::AndExpression(Box::new(a), Box::new(b))) } ConditionalAndExpression_item -> Expression = e:ValueLogical _ { e } @@ -729,7 +777,7 @@ StrReplaceExpression -> Expression = ExistsFunc -> Expression = "EXISTS"i _ p:GroupGraphPattern { Expression::ExistsFunctionCall(Box::new(p)) } //[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] Aggregate -> Expression = diff --git a/tests/rdf_test_cases.rs b/tests/rdf_test_cases.rs index 97af40ea..f761e27f 100644 --- a/tests/rdf_test_cases.rs +++ b/tests/rdf_test_cases.rs @@ -15,7 +15,7 @@ use rudf::rio::ntriples::read_ntriples; use rudf::rio::turtle::read_turtle; use rudf::rio::RioError; use rudf::rio::RioResult; -use rudf::sparql::ast::Query; +use rudf::sparql::algebra::Query; use rudf::sparql::parser::read_sparql_query; use rudf::store::isomorphism::GraphIsomorphism; 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) { assert!( false, - "Failure tu deserialize \"{}\" of {} with error: {}", + "Failure to deserialize \"{}\" of {} with error: {}", query.to_string(), test, error