parent
08d7497706
commit
d933660ed7
@ -0,0 +1,644 @@ |
|||||||
|
use model::data::*; |
||||||
|
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) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
pub mod ast; |
||||||
|
pub mod model; |
||||||
|
pub mod parser; |
@ -0,0 +1,115 @@ |
|||||||
|
use model::data::*; |
||||||
|
use std::fmt; |
||||||
|
use uuid::Uuid; |
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] |
||||||
|
pub struct Variable { |
||||||
|
name: String, |
||||||
|
} |
||||||
|
|
||||||
|
impl Variable { |
||||||
|
pub fn new(name: impl Into<String>) -> Self { |
||||||
|
Self { name: name.into() } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for Variable { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
write!(f, "?{}", self.name) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for Variable { |
||||||
|
fn default() -> Self { |
||||||
|
Self { |
||||||
|
name: Uuid::new_v4().to_string(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] |
||||||
|
pub enum NamedNodeOrVariable { |
||||||
|
NamedNode(NamedNode), |
||||||
|
Variable(Variable), |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for NamedNodeOrVariable { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
match self { |
||||||
|
NamedNodeOrVariable::NamedNode(node) => write!(f, "{}", node), |
||||||
|
NamedNodeOrVariable::Variable(var) => write!(f, "{}", var), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<NamedNode> for NamedNodeOrVariable { |
||||||
|
fn from(node: NamedNode) -> Self { |
||||||
|
NamedNodeOrVariable::NamedNode(node) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<Variable> for NamedNodeOrVariable { |
||||||
|
fn from(var: Variable) -> Self { |
||||||
|
NamedNodeOrVariable::Variable(var) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] |
||||||
|
pub enum TermOrVariable { |
||||||
|
Term(Term), |
||||||
|
Variable(Variable), |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for TermOrVariable { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
match self { |
||||||
|
TermOrVariable::Term(node) => write!(f, "{}", node), |
||||||
|
TermOrVariable::Variable(var) => write!(f, "{}", var), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<NamedNode> for TermOrVariable { |
||||||
|
fn from(node: NamedNode) -> Self { |
||||||
|
TermOrVariable::Term(node.into()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<BlankNode> for TermOrVariable { |
||||||
|
fn from(node: BlankNode) -> Self { |
||||||
|
TermOrVariable::Term(node.into()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<Literal> for TermOrVariable { |
||||||
|
fn from(literal: Literal) -> Self { |
||||||
|
TermOrVariable::Term(literal.into()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<NamedOrBlankNode> for TermOrVariable { |
||||||
|
fn from(node: NamedOrBlankNode) -> Self { |
||||||
|
TermOrVariable::Term(node.into()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<Term> for TermOrVariable { |
||||||
|
fn from(node: Term) -> Self { |
||||||
|
TermOrVariable::Term(node) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<Variable> for TermOrVariable { |
||||||
|
fn from(var: Variable) -> Self { |
||||||
|
TermOrVariable::Variable(var) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<NamedNodeOrVariable> for TermOrVariable { |
||||||
|
fn from(element: NamedNodeOrVariable) -> Self { |
||||||
|
match element { |
||||||
|
NamedNodeOrVariable::NamedNode(node) => TermOrVariable::Term(node.into()), |
||||||
|
NamedNodeOrVariable::Variable(var) => TermOrVariable::Variable(var), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,247 @@ |
|||||||
|
use std::borrow::Cow; |
||||||
|
use std::char; |
||||||
|
use std::str::Chars; |
||||||
|
|
||||||
|
mod grammar { |
||||||
|
use model::data::*; |
||||||
|
use rio::RioError; |
||||||
|
use rio::RioResult; |
||||||
|
use sparql::ast::*; |
||||||
|
use sparql::model::*; |
||||||
|
use sparql::parser::unescape_unicode_codepoints; |
||||||
|
use std::borrow::Cow; |
||||||
|
use std::collections::BTreeMap; |
||||||
|
use std::collections::HashMap; |
||||||
|
use std::io::BufReader; |
||||||
|
use std::io::Read; |
||||||
|
use url::ParseOptions; |
||||||
|
use url::Url; |
||||||
|
|
||||||
|
struct FocusedTriplePattern<F> { |
||||||
|
focus: F, |
||||||
|
patterns: Vec<TriplePattern>, |
||||||
|
} |
||||||
|
|
||||||
|
impl<F> FocusedTriplePattern<F> { |
||||||
|
fn new(focus: F) -> Self { |
||||||
|
Self { |
||||||
|
focus, |
||||||
|
patterns: Vec::default(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<F: Default> Default for FocusedTriplePattern<F> { |
||||||
|
fn default() -> Self { |
||||||
|
Self { |
||||||
|
focus: F::default(), |
||||||
|
patterns: Vec::default(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<F> From<FocusedTriplePattern<F>> for FocusedTriplePattern<Vec<F>> { |
||||||
|
fn from(input: FocusedTriplePattern<F>) -> Self { |
||||||
|
Self { |
||||||
|
focus: vec![input.focus], |
||||||
|
patterns: input.patterns, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
struct FocusedPropertyPathPattern<F> { |
||||||
|
focus: F, |
||||||
|
patterns: Vec<PropertyPathPattern>, |
||||||
|
} |
||||||
|
|
||||||
|
impl<F> FocusedPropertyPathPattern<F> { |
||||||
|
fn new(focus: F) -> Self { |
||||||
|
Self { |
||||||
|
focus, |
||||||
|
patterns: Vec::default(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<F: Default> Default for FocusedPropertyPathPattern<F> { |
||||||
|
fn default() -> Self { |
||||||
|
Self { |
||||||
|
focus: F::default(), |
||||||
|
patterns: Vec::default(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<F> From<FocusedPropertyPathPattern<F>> for FocusedPropertyPathPattern<Vec<F>> { |
||||||
|
fn from(input: FocusedPropertyPathPattern<F>) -> Self { |
||||||
|
Self { |
||||||
|
focus: vec![input.focus], |
||||||
|
patterns: input.patterns, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<F, T: From<F>> From<FocusedTriplePattern<F>> for FocusedPropertyPathPattern<T> { |
||||||
|
fn from(input: FocusedTriplePattern<F>) -> Self { |
||||||
|
Self { |
||||||
|
focus: input.focus.into(), |
||||||
|
patterns: input.patterns.into_iter().map(|p| p.into()).collect(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<FocusedPropertyPathPattern<TermOrVariable>> for GraphPattern { |
||||||
|
fn from(input: FocusedPropertyPathPattern<TermOrVariable>) -> Self { |
||||||
|
if input.patterns.len() == 1 { |
||||||
|
input.patterns[0].clone().into() |
||||||
|
} else { |
||||||
|
GraphPattern::GroupPattern(input.patterns.into_iter().map(|p| p.into()).collect()) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn flatten_group_pattern(v: impl Iterator<Item = GraphPattern>) -> GraphPattern { |
||||||
|
let l: Vec<GraphPattern> = 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) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub struct ParserState { |
||||||
|
base_uri: Option<Url>, |
||||||
|
namespaces: HashMap<String, String>, |
||||||
|
bnodes_map: BTreeMap<String, BlankNode>, |
||||||
|
} |
||||||
|
|
||||||
|
impl ParserState { |
||||||
|
fn url_parser<'a>(&'a self) -> ParseOptions<'a> { |
||||||
|
Url::options().base_url(self.base_uri.as_ref()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/sparql_grammar.rs")); |
||||||
|
|
||||||
|
pub fn read_sparql_query<'a, R: Read + 'a>( |
||||||
|
source: R, |
||||||
|
base_uri: impl Into<Option<Url>>, |
||||||
|
) -> RioResult<Query> { |
||||||
|
let mut state = ParserState { |
||||||
|
base_uri: base_uri.into(), |
||||||
|
namespaces: HashMap::default(), |
||||||
|
bnodes_map: BTreeMap::default(), |
||||||
|
}; |
||||||
|
|
||||||
|
let mut string_buffer = String::default(); |
||||||
|
BufReader::new(source).read_to_string(&mut string_buffer)?; |
||||||
|
|
||||||
|
match QueryUnit( |
||||||
|
&unescape_unicode_codepoints(Cow::from(string_buffer)), |
||||||
|
&mut state, |
||||||
|
) { |
||||||
|
Ok(query) => Ok(query), |
||||||
|
Err(error) => Err(RioError::new(error)), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub use sparql::parser::grammar::read_sparql_query; |
||||||
|
|
||||||
|
fn needs_unescape_unicode_codepoints(input: &str) -> bool { |
||||||
|
let bytes = input.as_bytes(); |
||||||
|
for i in 1..bytes.len() { |
||||||
|
if (bytes[i] == ('u' as u8) || bytes[i] == ('U' as u8)) && bytes[i - 1] == ('/' as u8) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
struct UnescapeUnicodeCharIterator<'a> { |
||||||
|
iter: Chars<'a>, |
||||||
|
buffer: String, |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> UnescapeUnicodeCharIterator<'a> { |
||||||
|
fn new(string: &'a str) -> Self { |
||||||
|
Self { |
||||||
|
iter: string.chars(), |
||||||
|
buffer: String::with_capacity(9), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> Iterator for UnescapeUnicodeCharIterator<'a> { |
||||||
|
type Item = char; |
||||||
|
|
||||||
|
fn next(&mut self) -> Option<char> { |
||||||
|
if !self.buffer.is_empty() { |
||||||
|
return Some(self.buffer.remove(0)); |
||||||
|
} |
||||||
|
match self.iter.next()? { |
||||||
|
'\\' => match self.iter.next() { |
||||||
|
Some('u') => { |
||||||
|
self.buffer.push('u'); |
||||||
|
for _ in 0..4 { |
||||||
|
if let Some(c) = self.iter.next() { |
||||||
|
self.buffer.push(c); |
||||||
|
} else { |
||||||
|
return Some('\\'); |
||||||
|
} |
||||||
|
} |
||||||
|
if let Some(c) = u32::from_str_radix(&self.buffer[1..5], 16) |
||||||
|
.ok() |
||||||
|
.and_then(char::from_u32) |
||||||
|
{ |
||||||
|
self.buffer.clear(); |
||||||
|
Some(c) |
||||||
|
} else { |
||||||
|
Some('\\') |
||||||
|
} |
||||||
|
} |
||||||
|
Some('U') => { |
||||||
|
self.buffer.push('U'); |
||||||
|
for _ in 0..8 { |
||||||
|
if let Some(c) = self.iter.next() { |
||||||
|
self.buffer.push(c); |
||||||
|
} else { |
||||||
|
return Some('\\'); |
||||||
|
} |
||||||
|
} |
||||||
|
if let Some(c) = u32::from_str_radix(&self.buffer[1..9], 16) |
||||||
|
.ok() |
||||||
|
.and_then(char::from_u32) |
||||||
|
{ |
||||||
|
self.buffer.clear(); |
||||||
|
Some(c) |
||||||
|
} else { |
||||||
|
Some('\\') |
||||||
|
} |
||||||
|
} |
||||||
|
Some(c) => { |
||||||
|
self.buffer.push(c); |
||||||
|
Some('\\') |
||||||
|
} |
||||||
|
None => Some('\\'), |
||||||
|
}, |
||||||
|
c => Some(c), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn unescape_unicode_codepoints<'a>(input: Cow<'a, str>) -> Cow<'a, str> { |
||||||
|
if needs_unescape_unicode_codepoints(&input) { |
||||||
|
UnescapeUnicodeCharIterator::new(&input).collect() |
||||||
|
} else { |
||||||
|
input |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,977 @@ |
|||||||
|
//See https://www.w3.org/TR/turtle/#sec-grammar |
||||||
|
|
||||||
|
use std::char; |
||||||
|
use model::vocab::rdf; |
||||||
|
use model::vocab::xsd; |
||||||
|
use std::iter; |
||||||
|
use std::iter::once; |
||||||
|
|
||||||
|
#![arguments(state: &mut ParserState)] |
||||||
|
|
||||||
|
|
||||||
|
//[1] |
||||||
|
#[pub] |
||||||
|
QueryUnit -> Query = Query |
||||||
|
|
||||||
|
//[2] |
||||||
|
Query -> Query = _ Prologue _ q:(SelectQuery / ConstructQuery / DescribeQuery / AskQuery) _ ValuesClause _ { //TODO: ValuesClause |
||||||
|
q |
||||||
|
} |
||||||
|
|
||||||
|
//[4] |
||||||
|
Prologue -> () = (BaseDecl _ / PrefixDecl _)* |
||||||
|
|
||||||
|
//[5] |
||||||
|
BaseDecl -> () = "BASE"i _ i:IRIREF {? |
||||||
|
match state.url_parser().parse(&i) { |
||||||
|
Ok(url) => { |
||||||
|
state.base_uri = Some(url); |
||||||
|
Ok(()) |
||||||
|
}, |
||||||
|
Err(error) => Err("IRI parsing failed") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//[6] |
||||||
|
PrefixDecl -> () = "PREFIX"i _ ns:PNAME_NS _ i:IRIREF { |
||||||
|
state.namespaces.insert(ns.into(), i.into()); |
||||||
|
} |
||||||
|
|
||||||
|
//[7] |
||||||
|
SelectQuery -> Query = s:SelectClause _ d:DatasetClauses _ f:WhereClause _ SolutionModifier { //TODO: Modifier |
||||||
|
Query::SelectQuery { |
||||||
|
selection: s, |
||||||
|
dataset: d, |
||||||
|
filter: f |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//[8] |
||||||
|
SubSelect -> GraphPattern = s:SelectClause _ f:WhereClause _ SolutionModifier _ ValuesClause { //TODO: Modifiers |
||||||
|
GraphPattern::SubSelectPattern { |
||||||
|
selection: s, |
||||||
|
filter: Box::new(f) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//[9] |
||||||
|
SelectClause -> Selection = "SELECT"i _ o:SelectClause_option _ v:SelectClause_variables { |
||||||
|
Selection { |
||||||
|
option: o, |
||||||
|
variables: v |
||||||
|
} |
||||||
|
} |
||||||
|
SelectClause_option -> SelectionOption = |
||||||
|
"DISTINCT"i { SelectionOption::Distinct } / |
||||||
|
"REDUCED"i { SelectionOption::Reduced } / |
||||||
|
{ SelectionOption::Default } |
||||||
|
SelectClause_variables -> Option<Vec<SelectionMember>> = |
||||||
|
'*' { None } / |
||||||
|
p:SelectClause_member+ { Some(p) } |
||||||
|
SelectClause_member -> SelectionMember = |
||||||
|
v:Var _ { SelectionMember::Variable(v) } / |
||||||
|
'(' _ e:Expression _ "AS"i _ v:Var _ ')' _ { SelectionMember::Expression(e, v) } |
||||||
|
|
||||||
|
//[10] |
||||||
|
ConstructQuery -> Query = |
||||||
|
"CONSTRUCT"i _ c:ConstructTemplate _ d:DatasetClauses _ f:WhereClause _ SolutionModifier { |
||||||
|
Query::ConstructQuery { construct: c, dataset: d, filter: f } |
||||||
|
} / |
||||||
|
"CONSTRUCT"i _ d:DatasetClauses _ "WHERE"i _ '{' _ c:ConstructQuery_optional_triple_template _ '}' _ SolutionModifier { |
||||||
|
Query::ConstructQuery { |
||||||
|
construct: c.clone(), |
||||||
|
dataset: d, |
||||||
|
filter: flatten_group_pattern(c.into_iter().map(|p| PropertyPathPattern::from(p).into())) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ConstructQuery_optional_triple_template -> Vec<TriplePattern> = TriplesTemplate / { Vec::default() } |
||||||
|
|
||||||
|
//[11] |
||||||
|
DescribeQuery -> Query = "DESCRIBE"i _ ('*' / (VarOrIri _)+) _ d:DatasetClauses f:WhereClause? _ SolutionModifier { |
||||||
|
Query::DescribeQuery { |
||||||
|
dataset: d, |
||||||
|
filter: f.unwrap_or_else(GraphPattern::default) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//[12] |
||||||
|
AskQuery -> Query = "ASK"i _ d:DatasetClauses f:WhereClause _ SolutionModifier { |
||||||
|
Query::AskQuery { |
||||||
|
dataset: d, |
||||||
|
filter: f |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//[13] |
||||||
|
DatasetClause -> Dataset = "FROM"i _ d:(DefaultGraphClause / NamedGraphClause) { d } |
||||||
|
DatasetClauses -> Dataset = d:DatasetClauses_item* { |
||||||
|
d.into_iter().fold(Dataset::default(), |mut a, b| a + b) |
||||||
|
} |
||||||
|
DatasetClauses_item -> Dataset = d:DatasetClause _ { d } |
||||||
|
//[14] |
||||||
|
DefaultGraphClause -> Dataset = s:SourceSelector { |
||||||
|
Dataset::new_with_default(s) |
||||||
|
} |
||||||
|
|
||||||
|
//[15] |
||||||
|
NamedGraphClause -> Dataset = "NAMED"i _ s:SourceSelector { |
||||||
|
Dataset::new_with_named(s) |
||||||
|
} |
||||||
|
|
||||||
|
//[16] |
||||||
|
SourceSelector -> NamedNode = iri |
||||||
|
|
||||||
|
//[17] |
||||||
|
WhereClause -> GraphPattern = "WHERE"i? _ p:GroupGraphPattern { p } |
||||||
|
|
||||||
|
//[18] |
||||||
|
SolutionModifier -> () = GroupClause? _ HavingClause? _ OrderClause? _ LimitOffsetClauses? |
||||||
|
|
||||||
|
//[19] |
||||||
|
GroupClause -> () = "GROUP"i _ "BY"i _ (GroupCondition _)+ |
||||||
|
|
||||||
|
//[20] |
||||||
|
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) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//[22] |
||||||
|
HavingCondition -> Expression = Constraint |
||||||
|
|
||||||
|
//[23] |
||||||
|
OrderClause -> () = "ORDER"i "BY"i _ OrderCondition+ |
||||||
|
|
||||||
|
//[24] |
||||||
|
OrderCondition -> () = (( "ASC"i / "DESC"i) _ BrackettedExpression) / (Constraint / Var) |
||||||
|
|
||||||
|
//[25] |
||||||
|
LimitOffsetClauses -> () = LimitClause _ OffsetClause? / OffsetClause _ LimitClause? |
||||||
|
|
||||||
|
//[26] |
||||||
|
LimitClause -> () = "LIMIT"i _ INTEGER |
||||||
|
|
||||||
|
//[27] |
||||||
|
OffsetClause -> () = "OFFSET"i _ INTEGER |
||||||
|
|
||||||
|
//[28] |
||||||
|
ValuesClause -> Option<GraphPattern> = |
||||||
|
"VALUES"i _ p:DataBlock { Some(p) } / |
||||||
|
{ None } |
||||||
|
|
||||||
|
//[52] |
||||||
|
TriplesTemplate -> Vec<TriplePattern> = p:TriplesTemplate_item **<1,> ('.' _) '.'? { |
||||||
|
p.into_iter().flat_map(|c| c.into_iter()).collect() |
||||||
|
} |
||||||
|
TriplesTemplate_item -> Vec<TriplePattern> = p:TriplesSameSubject _ { p } |
||||||
|
|
||||||
|
//[53] |
||||||
|
GroupGraphPattern -> GraphPattern = |
||||||
|
'{' _ p:GroupGraphPatternSub _ '}' { p } / |
||||||
|
'{' _ p:SubSelect _ '}' { p } |
||||||
|
|
||||||
|
//[54] |
||||||
|
GroupGraphPatternSub -> GraphPattern = a:TriplesBlock? _ b:GroupGraphPatternSub_item* { |
||||||
|
let mut list = a.map(|v| vec![v]).unwrap_or_else(|| vec![]); |
||||||
|
for v in b { |
||||||
|
list.extend_from_slice(&v) |
||||||
|
} |
||||||
|
flatten_group_pattern(list.into_iter()) |
||||||
|
} |
||||||
|
GroupGraphPatternSub_item -> Vec<GraphPattern> = a:GraphPatternNotTriples _ ('.' _)? b:TriplesBlock? _ { |
||||||
|
let mut result = vec![a]; |
||||||
|
b.map(|v| result.push(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_tail -> GraphPattern = '.' _ t:TriplesBlock? _ { |
||||||
|
t.unwrap_or_else(|| GraphPattern::GroupPattern(Vec::default())) |
||||||
|
} |
||||||
|
|
||||||
|
//[56] |
||||||
|
GraphPatternNotTriples -> GraphPattern = GroupOrUnionGraphPattern / OptionalGraphPattern / MinusGraphPattern / GraphGraphPattern / ServiceGraphPattern / Filter / Bind / InlineData |
||||||
|
|
||||||
|
//[57] |
||||||
|
OptionalGraphPattern -> GraphPattern = "OPTIONAL"i _ p:GroupGraphPattern { |
||||||
|
GraphPattern::OptionalPattern(Box::new(p)) |
||||||
|
} |
||||||
|
|
||||||
|
//[58] |
||||||
|
GraphGraphPattern -> GraphPattern = "GRAPH"i _ g:VarOrIri _ p:GroupGraphPattern { |
||||||
|
GraphPattern::GraphPattern(g, Box::new(p)) |
||||||
|
} |
||||||
|
|
||||||
|
//[59] |
||||||
|
ServiceGraphPattern -> GraphPattern = "SERVICE"i _ "SILENT"i? _ s:VarOrIri _ p:GroupGraphPattern { |
||||||
|
GraphPattern::ServicePattern(s, Box::new(p)) |
||||||
|
} |
||||||
|
|
||||||
|
//[60] |
||||||
|
Bind -> GraphPattern = "BIND"i _ '(' _ e:Expression _ "AS"i _ v:Var _ ')' { |
||||||
|
GraphPattern::BindPattern(e, v) |
||||||
|
} |
||||||
|
|
||||||
|
//[61] |
||||||
|
InlineData -> GraphPattern = "VALUES"i _ p:DataBlock { p } |
||||||
|
|
||||||
|
//[62] |
||||||
|
DataBlock -> GraphPattern = InlineDataOneVar / InlineDataFull |
||||||
|
|
||||||
|
//[63] |
||||||
|
InlineDataOneVar -> GraphPattern = v:Var _ '{' _ d:InlineDataOneVar_value* '}' { |
||||||
|
GraphPattern::ValuesPattern(vec![v], d) |
||||||
|
} |
||||||
|
InlineDataOneVar_value -> Vec<Option<Term>> = t:DataBlockValue { vec![t] } |
||||||
|
|
||||||
|
//[64] |
||||||
|
InlineDataFull -> GraphPattern = '(' _ var:InlineDataFull_var* _ ')' _ '{' _ val:InlineDataFull_values* '}' { |
||||||
|
GraphPattern::ValuesPattern(var, val) |
||||||
|
} |
||||||
|
InlineDataFull_var -> Variable = v:Var _ { v } |
||||||
|
InlineDataFull_values -> Vec<Option<Term>> = '(' _ v:InlineDataFull_value* _ ')' _ { v } |
||||||
|
InlineDataFull_value -> Option<Term> = v:DataBlockValue _ { v } |
||||||
|
|
||||||
|
//[65] |
||||||
|
DataBlockValue -> Option<Term> = |
||||||
|
i:iri { Some(i.into()) } / |
||||||
|
l:RDFLiteral { Some(l.into()) } / |
||||||
|
l:NumericLiteral { Some(l.into()) } / |
||||||
|
l:BooleanLiteral { Some(l.into()) } / |
||||||
|
"UNDEF"i { None } |
||||||
|
|
||||||
|
//[66] |
||||||
|
MinusGraphPattern -> GraphPattern = "MINUS"i _ p: GroupGraphPattern { |
||||||
|
GraphPattern::MinusPattern(Box::new(p)) |
||||||
|
} |
||||||
|
|
||||||
|
//[67] |
||||||
|
GroupOrUnionGraphPattern -> GraphPattern = p:GroupOrUnionGraphPattern_item **<1,> ("UNION"i _) { |
||||||
|
if p.len() == 1 { |
||||||
|
p[0].clone() |
||||||
|
} else { |
||||||
|
GraphPattern::UnionPattern(p) |
||||||
|
} |
||||||
|
} |
||||||
|
GroupOrUnionGraphPattern_item -> GraphPattern = p:GroupGraphPattern _ { p } |
||||||
|
|
||||||
|
//[68] |
||||||
|
Filter -> GraphPattern = "FILTER"i _ c:Constraint { GraphPattern::FilterPattern(c) } |
||||||
|
|
||||||
|
//[69] |
||||||
|
Constraint -> Expression = BrackettedExpression / BuiltInCall / FunctionCall |
||||||
|
|
||||||
|
//[70] |
||||||
|
FunctionCall -> Expression = f: iri _ a: ArgList { |
||||||
|
Expression::CustomFunctionCall(f, a.into()) |
||||||
|
} |
||||||
|
|
||||||
|
//[71] |
||||||
|
ArgList -> Vec<Expression> = //TODO: support DISTINCT |
||||||
|
NIL { Vec::new() } / |
||||||
|
'(' _ 'DISTINCT'? _ e:ArgList_item **<1,> (',' _) _ ')' { e } |
||||||
|
ArgList_item -> Expression = e:Expression _ { e } |
||||||
|
|
||||||
|
//[72] |
||||||
|
ExpressionList -> Vec<Expression> = |
||||||
|
NIL { Vec::default() } / |
||||||
|
'(' _ e:ExpressionList_item **<1,> (',' _) ')' { e } |
||||||
|
ExpressionList_item -> Expression = e:Expression _ { e } |
||||||
|
|
||||||
|
//[73] |
||||||
|
ConstructTemplate -> Vec<TriplePattern> = '{' _ t:ConstructTriples _ '}' { t } |
||||||
|
|
||||||
|
//[74] |
||||||
|
ConstructTriples -> Vec<TriplePattern> = p:ConstructTriples_item ** ('.' _) { |
||||||
|
p.into_iter().flat_map(|c| c.into_iter()).collect() |
||||||
|
} |
||||||
|
ConstructTriples_item -> Vec<TriplePattern> = t:TriplesSameSubject _ { t } |
||||||
|
|
||||||
|
//[75] |
||||||
|
TriplesSameSubject -> Vec<TriplePattern> = |
||||||
|
s:VarOrTerm _ po:PropertyListNotEmpty { |
||||||
|
let mut patterns = po.patterns; |
||||||
|
for (p, os) in po.focus { |
||||||
|
for o in os { |
||||||
|
patterns.push(TriplePattern::new(s.clone(), p.clone(), o)) |
||||||
|
} |
||||||
|
} |
||||||
|
patterns.into_iter().map(|p| p.into()).collect() |
||||||
|
} / |
||||||
|
s:TriplesNode _ po:PropertyList { |
||||||
|
let mut patterns = s.patterns; |
||||||
|
patterns.extend_from_slice(&po.patterns); |
||||||
|
for (p, os) in po.focus { |
||||||
|
for o in os { |
||||||
|
patterns.push(TriplePattern::new(s.focus.clone(), p.clone(), o)) |
||||||
|
} |
||||||
|
} |
||||||
|
patterns.into_iter().map(|p| p.into()).collect() |
||||||
|
} |
||||||
|
|
||||||
|
//[76] |
||||||
|
PropertyList -> FocusedTriplePattern<Vec<(NamedNodeOrVariable,Vec<TermOrVariable>)>> = |
||||||
|
PropertyListNotEmpty / |
||||||
|
{ FocusedTriplePattern::default() } |
||||||
|
|
||||||
|
//[77] |
||||||
|
PropertyListNotEmpty -> FocusedTriplePattern<Vec<(NamedNodeOrVariable,Vec<TermOrVariable>)>> = l:PropertyListNotEmpty_item **<1,> (';' _) { |
||||||
|
l.into_iter().fold(FocusedTriplePattern::<Vec<(NamedNodeOrVariable,Vec<TermOrVariable>)>>::default(), |mut a, b| { |
||||||
|
a.focus.push(b.focus); |
||||||
|
a.patterns.extend_from_slice(&b.patterns); |
||||||
|
a |
||||||
|
}) |
||||||
|
} |
||||||
|
PropertyListNotEmpty_item -> FocusedTriplePattern<(NamedNodeOrVariable,Vec<TermOrVariable>)> = p:Verb _ o:ObjectList _ { |
||||||
|
FocusedTriplePattern { |
||||||
|
focus: (p, o.focus), |
||||||
|
patterns: o.patterns |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//[78] |
||||||
|
Verb -> NamedNodeOrVariable = 'a' { rdf::TYPE.clone().into() } / VarOrIri |
||||||
|
|
||||||
|
//[79] |
||||||
|
ObjectList -> FocusedTriplePattern<Vec<TermOrVariable>> = o:ObjectList_item **<1,> (',' _) { |
||||||
|
o.into_iter().fold(FocusedTriplePattern::<Vec<TermOrVariable>>::default(), |mut a, b| { |
||||||
|
a.focus.push(b.focus); |
||||||
|
a.patterns.extend_from_slice(&b.patterns); |
||||||
|
a |
||||||
|
}) |
||||||
|
} |
||||||
|
ObjectList_item -> FocusedTriplePattern<TermOrVariable> = o:Object _ { o } |
||||||
|
|
||||||
|
//[80] |
||||||
|
Object -> FocusedTriplePattern<TermOrVariable> = GraphNode |
||||||
|
|
||||||
|
//[81] |
||||||
|
TriplesSameSubjectPath -> GraphPattern = |
||||||
|
s:VarOrTerm _ po:PropertyListPathNotEmpty { |
||||||
|
let mut patterns = po.patterns; |
||||||
|
for (p, os) in po.focus { |
||||||
|
for o in os { |
||||||
|
patterns.push(PropertyPathPattern::new(s.clone(), p.clone(), o)) |
||||||
|
} |
||||||
|
} |
||||||
|
flatten_group_pattern(patterns.into_iter().map(|p| p.into())) |
||||||
|
} / |
||||||
|
s:TriplesNodePath _ po:PropertyListPath { |
||||||
|
let mut patterns = s.patterns; |
||||||
|
patterns.extend_from_slice(&po.patterns); |
||||||
|
for (p, os) in po.focus { |
||||||
|
for o in os { |
||||||
|
patterns.push(PropertyPathPattern::new(s.focus.clone(), p.clone(), o)) |
||||||
|
} |
||||||
|
} |
||||||
|
flatten_group_pattern(patterns.into_iter().map(|p| p.into())) |
||||||
|
} |
||||||
|
|
||||||
|
//[82] |
||||||
|
PropertyListPath -> FocusedPropertyPathPattern<Vec<(PropertyPath,Vec<TermOrVariable>)>> = |
||||||
|
PropertyListPathNotEmpty / |
||||||
|
{ FocusedPropertyPathPattern::default() } |
||||||
|
|
||||||
|
//[83] |
||||||
|
PropertyListPathNotEmpty -> FocusedPropertyPathPattern<Vec<(PropertyPath,Vec<TermOrVariable>)>> = hp:(VerbPath / VerbSimple) _ ho:ObjectListPath _ t:PropertyListPathNotEmpty_item* { |
||||||
|
t.into_iter().fold(FocusedPropertyPathPattern { |
||||||
|
focus: vec![(hp, ho.focus)], |
||||||
|
patterns: ho.patterns |
||||||
|
}, |mut a, b| { |
||||||
|
a.focus.push(b.focus); |
||||||
|
a.patterns.extend(b.patterns.into_iter().map(|v| v.into())); |
||||||
|
a |
||||||
|
}) |
||||||
|
} |
||||||
|
PropertyListPathNotEmpty_item -> FocusedTriplePattern<(PropertyPath,Vec<TermOrVariable>)> = ';' _ p:(VerbPath / VerbSimple) _ o:ObjectList _ { //TODO: make values after ';' optional |
||||||
|
FocusedTriplePattern { |
||||||
|
focus: (p, o.focus), |
||||||
|
patterns: o.patterns |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//[84] |
||||||
|
VerbPath -> PropertyPath = Path |
||||||
|
|
||||||
|
//[85] |
||||||
|
VerbSimple -> PropertyPath = v:Var { |
||||||
|
v.into() |
||||||
|
} |
||||||
|
|
||||||
|
//[86] |
||||||
|
ObjectListPath -> FocusedPropertyPathPattern<Vec<TermOrVariable>> = o:ObjectPath_item **<1,> (',' _) { |
||||||
|
o.into_iter().fold(FocusedPropertyPathPattern::<Vec<TermOrVariable>>::default(), |mut a, b| { |
||||||
|
a.focus.push(b.focus); |
||||||
|
a.patterns.extend_from_slice(&b.patterns); |
||||||
|
a |
||||||
|
}) |
||||||
|
} |
||||||
|
ObjectPath_item -> FocusedPropertyPathPattern<TermOrVariable> = o:ObjectPath _ { o } |
||||||
|
|
||||||
|
//[87] |
||||||
|
ObjectPath -> FocusedPropertyPathPattern<TermOrVariable> = GraphNodePath |
||||||
|
|
||||||
|
//[88] |
||||||
|
Path -> PropertyPath = PathAlternative |
||||||
|
|
||||||
|
//[89] |
||||||
|
PathAlternative -> PropertyPath = p:PathAlternative_item **<1,> ('|' _) { |
||||||
|
if p.len() == 1 { |
||||||
|
p[0].clone() |
||||||
|
} else { |
||||||
|
PropertyPath::SequencePath(p) |
||||||
|
} |
||||||
|
} |
||||||
|
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_item -> PropertyPath = p:PathEltOrInverse _ { p } |
||||||
|
|
||||||
|
//[91] |
||||||
|
PathElt -> PropertyPath = |
||||||
|
p:PathPrimary '?' { PropertyPath::ZeroOrOnePath(Box::new(p)) } / //TODO: allow space before "?" |
||||||
|
p:PathPrimary _ '*' { PropertyPath::ZeroOrMorePath(Box::new(p)) } / |
||||||
|
p:PathPrimary _ '+' { PropertyPath::OneOrMorePath(Box::new(p)) } / |
||||||
|
PathPrimary |
||||||
|
|
||||||
|
//[92] |
||||||
|
PathEltOrInverse -> PropertyPath = |
||||||
|
'^' _ p:PathElt { PropertyPath::InversePath(Box::new(p)) } / |
||||||
|
PathElt |
||||||
|
|
||||||
|
//[94] |
||||||
|
PathPrimary -> PropertyPath = |
||||||
|
v:Verb { v.into() } / |
||||||
|
'!' _ p:PathNegatedPropertySet { PropertyPath::NegatedPath(Box::new(p)) } / |
||||||
|
'(' _ p:Path _ ')' { p } |
||||||
|
|
||||||
|
//[95] |
||||||
|
PathNegatedPropertySet -> PropertyPath = |
||||||
|
'(' _ p:PathNegatedPropertySet_item **<1,> ('|' _) ')' { PropertyPath::AlternativePath(p) } / |
||||||
|
PathOneInPropertySet |
||||||
|
PathNegatedPropertySet_item -> PropertyPath = p:PathOneInPropertySet _ { p } |
||||||
|
|
||||||
|
//[96] |
||||||
|
PathOneInPropertySet -> PropertyPath = |
||||||
|
'^' _ v:Verb { PropertyPath::InversePath(Box::new(v.into())) } / |
||||||
|
v:Verb { v.into() } |
||||||
|
|
||||||
|
//[98] |
||||||
|
TriplesNode -> FocusedTriplePattern<TermOrVariable> = Collection / BlankNodePropertyList |
||||||
|
|
||||||
|
//[99] |
||||||
|
BlankNodePropertyList -> FocusedTriplePattern<TermOrVariable> = '[' _ po:PropertyListNotEmpty _ ']' { |
||||||
|
let mut patterns: Vec<TriplePattern> = Vec::default(); |
||||||
|
let mut bnode = TermOrVariable::from(BlankNode::default()); |
||||||
|
for (p, os) in po.focus { |
||||||
|
for o in os { |
||||||
|
patterns.push(TriplePattern::new(bnode.clone(), p.clone(), o)); |
||||||
|
} |
||||||
|
} |
||||||
|
FocusedTriplePattern { |
||||||
|
focus: bnode, |
||||||
|
patterns |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//[100] |
||||||
|
TriplesNodePath -> FocusedPropertyPathPattern<TermOrVariable> = CollectionPath / BlankNodePropertyListPath |
||||||
|
|
||||||
|
//[101] |
||||||
|
BlankNodePropertyListPath -> FocusedPropertyPathPattern<TermOrVariable> = '[' _ po:PropertyListPathNotEmpty _ ']' { |
||||||
|
let mut patterns: Vec<PropertyPathPattern> = Vec::default(); |
||||||
|
let mut bnode = TermOrVariable::from(BlankNode::default()); |
||||||
|
for (p, os) in po.focus { |
||||||
|
for o in os { |
||||||
|
patterns.push(PropertyPathPattern::new(bnode.clone(), p.clone(), o)); |
||||||
|
} |
||||||
|
} |
||||||
|
FocusedPropertyPathPattern { |
||||||
|
focus: bnode, |
||||||
|
patterns |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//[102] |
||||||
|
Collection -> FocusedTriplePattern<TermOrVariable> = '(' _ o:Collection_item+ ')' { |
||||||
|
let mut patterns: Vec<TriplePattern> = Vec::default(); |
||||||
|
let mut current_list_node = TermOrVariable::from(rdf::NIL.clone()); |
||||||
|
for objWithPatterns in o.into_iter().rev() { |
||||||
|
let new_blank_node = TermOrVariable::from(BlankNode::default()); |
||||||
|
patterns.push(TriplePattern::new(new_blank_node.clone(), rdf::FIRST.clone(), objWithPatterns.focus.clone())); |
||||||
|
patterns.push(TriplePattern::new(new_blank_node.clone(), rdf::REST.clone(), current_list_node)); |
||||||
|
current_list_node = new_blank_node; |
||||||
|
patterns.extend_from_slice(&objWithPatterns.patterns); |
||||||
|
} |
||||||
|
FocusedTriplePattern { |
||||||
|
focus: current_list_node, |
||||||
|
patterns |
||||||
|
} |
||||||
|
} |
||||||
|
Collection_item -> FocusedTriplePattern<TermOrVariable> = o:GraphNode _ { o } |
||||||
|
|
||||||
|
//[103] |
||||||
|
CollectionPath -> FocusedPropertyPathPattern<TermOrVariable> = '(' _ o:CollectionPath_item+ _ ')' { |
||||||
|
let mut patterns: Vec<PropertyPathPattern> = Vec::default(); |
||||||
|
let mut current_list_node = TermOrVariable::from(rdf::NIL.clone()); |
||||||
|
for objWithPatterns in o.into_iter().rev() { |
||||||
|
let new_blank_node = TermOrVariable::from(BlankNode::default()); |
||||||
|
patterns.push(PropertyPathPattern::new(new_blank_node.clone(), rdf::FIRST.clone(), objWithPatterns.focus.clone())); |
||||||
|
patterns.push(PropertyPathPattern::new(new_blank_node.clone(), rdf::REST.clone(), current_list_node)); |
||||||
|
current_list_node = new_blank_node; |
||||||
|
patterns.extend_from_slice(&objWithPatterns.patterns); |
||||||
|
} |
||||||
|
FocusedPropertyPathPattern { |
||||||
|
focus: current_list_node, |
||||||
|
patterns |
||||||
|
} |
||||||
|
} |
||||||
|
CollectionPath_item -> FocusedPropertyPathPattern<TermOrVariable> = p:GraphNodePath _ { p } |
||||||
|
|
||||||
|
//[104] |
||||||
|
GraphNode -> FocusedTriplePattern<TermOrVariable> = |
||||||
|
t:VarOrTerm { FocusedTriplePattern::new(t) } / |
||||||
|
TriplesNode |
||||||
|
|
||||||
|
//[105] |
||||||
|
GraphNodePath -> FocusedPropertyPathPattern<TermOrVariable> = |
||||||
|
t:VarOrTerm { FocusedPropertyPathPattern::new(t.into()) } / |
||||||
|
TriplesNodePath |
||||||
|
|
||||||
|
//[106] |
||||||
|
VarOrTerm -> TermOrVariable = |
||||||
|
v:Var { v.into() } / |
||||||
|
t:GraphTerm { t.into() } |
||||||
|
|
||||||
|
//[107] |
||||||
|
VarOrIri -> NamedNodeOrVariable = |
||||||
|
v:Var { v.into() } / |
||||||
|
i:iri { i.into() } |
||||||
|
|
||||||
|
//[108] |
||||||
|
Var -> Variable = v:(VAR1 / VAR2) { Variable::new(v) } |
||||||
|
|
||||||
|
//[109] |
||||||
|
GraphTerm -> Term = |
||||||
|
i:iri { i.into() } / |
||||||
|
l:RDFLiteral { l.into() } / |
||||||
|
l:NumericLiteral { l.into() } / |
||||||
|
l:BooleanLiteral { l.into() } / |
||||||
|
b:BlankNode { b.into() } / |
||||||
|
NIL { BlankNode::default().into() } |
||||||
|
|
||||||
|
//[110] |
||||||
|
Expression -> Expression = e:ConditionalOrExpression {e} |
||||||
|
|
||||||
|
//[111] |
||||||
|
ConditionalOrExpression -> Expression = e:ConditionalOrExpression_item **<1,> ("||" _) { |
||||||
|
if e.len() == 1 { |
||||||
|
e[0].clone() |
||||||
|
} else { |
||||||
|
Expression::OrExpression(e) |
||||||
|
} |
||||||
|
} |
||||||
|
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_item -> Expression = e:ValueLogical _ { e } |
||||||
|
|
||||||
|
//[113] |
||||||
|
ValueLogical -> Expression = RelationalExpression |
||||||
|
|
||||||
|
//[114] |
||||||
|
RelationalExpression -> Expression = |
||||||
|
a:NumericExpression _ "=" _ b:NumericExpression { Expression::EqualExpression(Box::new(a), Box::new(b)) } / |
||||||
|
a:NumericExpression _ "!=" _ b:NumericExpression { Expression::NotEqualExpression(Box::new(a), Box::new(b)) } / |
||||||
|
a:NumericExpression _ ">" _ b:NumericExpression { Expression::GreaterExpression(Box::new(a), Box::new(b)) } / |
||||||
|
a:NumericExpression _ ">=" _ b:NumericExpression { Expression::GreaterOrEqExpression(Box::new(a), Box::new(b)) } / |
||||||
|
a:NumericExpression _ "<" _ b:NumericExpression { Expression::LowerExpression(Box::new(a), Box::new(b)) } / |
||||||
|
a:NumericExpression _ "<=" _ b:NumericExpression { Expression::LowerOrEqExpression(Box::new(a), Box::new(b)) } / |
||||||
|
a:NumericExpression _ "IN"i _ b:ExpressionList { Expression::InExpression(Box::new(a), b) } / |
||||||
|
a:NumericExpression _ "NOT"i _ "IN"i _ b:ExpressionList { Expression::NotInExpression(Box::new(a), b) } / |
||||||
|
NumericExpression |
||||||
|
|
||||||
|
//[115] |
||||||
|
NumericExpression -> Expression = AdditiveExpression |
||||||
|
|
||||||
|
//[116] |
||||||
|
AdditiveExpression -> Expression = |
||||||
|
a:MultiplicativeExpression _ '+' _ b:AdditiveExpression { Expression::AddExpression(Box::new(a), Box::new(b)) } / |
||||||
|
a:MultiplicativeExpression _ '-' _ b:AdditiveExpression { Expression::SubExpression(Box::new(a), Box::new(b)) } / |
||||||
|
MultiplicativeExpression |
||||||
|
|
||||||
|
//[117] |
||||||
|
MultiplicativeExpression -> Expression = |
||||||
|
a:UnaryExpression _ '*' _ b:MultiplicativeExpression { Expression::MulExpression(Box::new(a), Box::new(b)) } / |
||||||
|
a:UnaryExpression _ '/' _ b:MultiplicativeExpression { Expression::DivExpression(Box::new(a), Box::new(b)) } / |
||||||
|
UnaryExpression |
||||||
|
|
||||||
|
//[118] |
||||||
|
UnaryExpression -> Expression = |
||||||
|
'!' _ e:PrimaryExpression { Expression::UnaryNotExpression(Box::new(e)) } / |
||||||
|
'+' _ e:PrimaryExpression { Expression::UnaryPlusExpression(Box::new(e)) } / |
||||||
|
'-' _ e:PrimaryExpression { Expression::UnaryMinusExpression(Box::new(e)) } / |
||||||
|
PrimaryExpression |
||||||
|
|
||||||
|
//[119] |
||||||
|
PrimaryExpression -> Expression = |
||||||
|
BrackettedExpression / |
||||||
|
BuiltInCall / |
||||||
|
iriOrFunction / |
||||||
|
l:RDFLiteral { Expression::ConstantExpression(l.into()) } / |
||||||
|
l:NumericLiteral { Expression::ConstantExpression(l.into()) } / |
||||||
|
l:BooleanLiteral { Expression::ConstantExpression(l.into()) } / |
||||||
|
v:Var { Expression::ConstantExpression(v.into()) } |
||||||
|
|
||||||
|
//[120] |
||||||
|
BrackettedExpression -> Expression = '(' _ e:Expression _ ')' { e } |
||||||
|
|
||||||
|
//[121] |
||||||
|
BuiltInCall -> Expression = |
||||||
|
Aggregate / |
||||||
|
"STR"i _ '(' _ e:Expression _ ')' { Expression::StrFunctionCall(Box::new(e)) } / |
||||||
|
"LANG"i _ '(' _ e:Expression _ ')' { Expression::LangFunctionCall(Box::new(e)) } / |
||||||
|
"LANGMATCHES"i _ '(' _ a:Expression _ ',' _ b:Expression _ ')' { Expression::LangMatchesFunctionCall(Box::new(a), Box::new(b)) } / |
||||||
|
"DATATYPE"i _ '(' _ e:Expression _ ')' { Expression::DatatypeFunctionCall(Box::new(e)) } / |
||||||
|
"BOUND"i _ '(' _ v:Var _ ')' { Expression::BoundFunctionCall(v) } / |
||||||
|
("IRI"i / "URI"i) _ '(' _ e:Expression _ ')' { Expression::IRIFunctionCall(Box::new(e)) } / |
||||||
|
"BNODE"i '(' _ e:Expression _ ')' { Expression::BNodeFunctionCall(Some(Box::new(e))) } / |
||||||
|
"BNODE"i NIL { Expression::BNodeFunctionCall(None) } / |
||||||
|
"RAND"i _ NIL { Expression::RandFunctionCall() } / |
||||||
|
"ABS"i _ '(' _ e:Expression _ ')' { Expression::AbsFunctionCall(Box::new(e)) } / |
||||||
|
"CEIL"i _ '(' _ e:Expression _ ')' { Expression::CeilFunctionCall(Box::new(e)) } / |
||||||
|
"FLOOR"i _ '(' _ e:Expression _ ')' { Expression::FloorFunctionCall(Box::new(e)) } / |
||||||
|
"ROUND"i _ '(' _ e:Expression _ ')' { Expression::RoundFunctionCall(Box::new(e)) } / |
||||||
|
"CONCAT"i e:ExpressionList { Expression::ConcatFunctionCall(e) } / |
||||||
|
SubstringExpression / |
||||||
|
"STRLEN"i _ '(' _ e: Expression _ ')' { Expression::StrLenFunctionCall(Box::new(e)) } / |
||||||
|
StrReplaceExpression / |
||||||
|
"UCASE"i _ '(' _ e:Expression _ ')' { Expression::UCaseFunctionCall(Box::new(e)) } / |
||||||
|
"LCASE"i _ '(' _ e:Expression _ ')' { Expression::LCaseFunctionCall(Box::new(e)) } / |
||||||
|
'ENCODE_FOR_URI' '(' _ e: Expression _ ')' { Expression::EncodeForURIFunctionCall(Box::new(e)) } / |
||||||
|
"CONTAINS"i _ '(' _ a:Expression _ ',' _ b:Expression _ ')' { Expression::ContainsFunctionCall(Box::new(a), Box::new(b)) } / |
||||||
|
"STRSTARTS"i _ '(' _ a:Expression _ ',' _ b:Expression _ ')' { Expression::StrStartsFunctionCall(Box::new(a), Box::new(b)) } / |
||||||
|
"STRENDS"i _ '(' _ a:Expression _ ',' _ b:Expression _ ')' { Expression::StrEndsFunctionCall(Box::new(a), Box::new(b)) } / |
||||||
|
"STRBEFORE"i _ '(' _ a:Expression _ ',' _ b:Expression _ ')' { Expression::StrBeforeFunctionCall(Box::new(a), Box::new(b)) } / |
||||||
|
"STRAFTER"i _ '(' _ a:Expression _ ',' _ b:Expression _ ')' { Expression::StrAfterFunctionCall(Box::new(a), Box::new(b)) } / |
||||||
|
"YEAR"i _ '(' _ e:Expression _ ')' { Expression::YearFunctionCall(Box::new(e)) } / |
||||||
|
"MONTH"i _ '(' _ e:Expression _ ')' { Expression::MonthFunctionCall(Box::new(e)) } / |
||||||
|
"DAY"i _ '(' _ e:Expression _ ')' { Expression::DayFunctionCall(Box::new(e)) } / |
||||||
|
"HOURS"i _ '(' _ e:Expression _ ')' { Expression::HoursFunctionCall(Box::new(e)) } / |
||||||
|
"MINUTES"i _ '(' _ e:Expression _ ')' { Expression::MinutesFunctionCall(Box::new(e)) } / |
||||||
|
"SECONDS"i _ '(' _ e:Expression _ ')' { Expression::SecondsFunctionCall(Box::new(e)) } / |
||||||
|
("TIMEZONE"i / "TZ"i) _ '(' _ e:Expression _ ')' { Expression::TimezoneFunctionCall(Box::new(e)) } / |
||||||
|
"NOW"i _ NIL { Expression::NowFunctionCall() } / |
||||||
|
"UUID"i _ NIL { Expression::UUIDFunctionCall() }/ |
||||||
|
"STRUUID"i _ NIL { Expression::StrUUIDFunctionCall() } / |
||||||
|
"MD5"i '(' _ e:Expression _ ')' { Expression::MD5FunctionCall(Box::new(e)) } / |
||||||
|
"SHA1"i '(' _ e:Expression _ ')' { Expression::SHA1FunctionCall(Box::new(e)) } / |
||||||
|
"SHA256"i '(' _ e:Expression _ ')' { Expression::SHA256FunctionCall(Box::new(e)) } / |
||||||
|
"SHA384"i '(' _ e:Expression _ ')' { Expression::SHA384FunctionCall(Box::new(e)) } / |
||||||
|
"SHA512"i '(' _ e:Expression _ ')' { Expression::SHA512FunctionCall(Box::new(e)) } / |
||||||
|
"COALESCE"i e:ExpressionList { Expression::CoalesceFunctionCall(e) } / |
||||||
|
"IF"i _ '(' _ a:Expression _ ',' _ b:Expression _ ',' _ c:Expression _ ')' { Expression::IfFunctionCall(Box::new(a), Box::new(b), Box::new(c)) } / |
||||||
|
"STRLANG"i _ '(' _ a:Expression _ ',' _ b:Expression _ ')' { Expression::StrLangFunctionCall(Box::new(a), Box::new(b)) } / |
||||||
|
"STRDT"i _ '(' _ a:Expression _ ',' _ b:Expression _ ')' { Expression::StrDTFunctionCall(Box::new(a), Box::new(b)) } / |
||||||
|
"sameTerm"i '(' _ a:Expression _ ',' _ b:Expression _ ')' { Expression::SameTermFunctionCall(Box::new(a), Box::new(b)) } / |
||||||
|
("isIRI"i / "isURI"i) _ '(' _ e:Expression _ ')' { Expression::IsIRIFunctionCall(Box::new(e)) } / |
||||||
|
"isBLANK"i '(' _ e:Expression _ ')' { Expression::IsBlankFunctionCall(Box::new(e)) } / |
||||||
|
"isLITERAL"i '(' _ e:Expression _ ')' { Expression::IsLiteralFunctionCall(Box::new(e)) } / |
||||||
|
"isNUMERIC"i '(' _ e:Expression _ ')' { Expression::IsNumericFunctionCall(Box::new(e)) } / |
||||||
|
RegexExpression / |
||||||
|
ExistsFunc / |
||||||
|
NotExistsFunc |
||||||
|
|
||||||
|
//[122] |
||||||
|
RegexExpression -> Expression = |
||||||
|
"REGEX"i _ '(' _ a:Expression _ ',' _ b:Expression _ ',' _ c:Expression _ ')' { Expression::RegexFunctionCall(Box::new(a), Box::new(b), Some(Box::new(c))) } / |
||||||
|
"REGEX"i _ '(' _ a:Expression _ ',' _ b:Expression _ ')' { Expression::RegexFunctionCall(Box::new(a), Box::new(b), None) } |
||||||
|
|
||||||
|
|
||||||
|
SubstringExpression -> Expression = |
||||||
|
"SUBSTR"i _ '(' _ a:Expression _ ',' _ b:Expression _ ',' _ c:Expression _ ')' { Expression::SubStrFunctionCall(Box::new(a), Box::new(b), Some(Box::new(c))) } / |
||||||
|
"SUBSTR"i _ '(' _ a:Expression _ ',' _ b:Expression _ ')' { Expression::SubStrFunctionCall(Box::new(a), Box::new(b), None) } |
||||||
|
|
||||||
|
|
||||||
|
//[124] |
||||||
|
StrReplaceExpression -> Expression = |
||||||
|
"REPLACE"i _ '(' _ a:Expression _ ',' _ b:Expression _ ',' _ c:Expression _ ',' _ d:Expression _ ')' { Expression::ReplaceFunctionCall(Box::new(a), Box::new(b), Box::new(c), Some(Box::new(d))) } / |
||||||
|
"REPLACE"i _ '(' _ a:Expression _ ',' _ b:Expression _ ',' _ c:Expression _ ')' { Expression::ReplaceFunctionCall(Box::new(a), Box::new(b), Box::new(c), None) } |
||||||
|
|
||||||
|
//[125] |
||||||
|
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)) } |
||||||
|
|
||||||
|
//[127] |
||||||
|
Aggregate -> Expression = |
||||||
|
"COUNT"i _ '(' _ "DISTINCT"i _ '*' _ ')' { Expression::CountAggregate(None, true) } / |
||||||
|
"COUNT"i _ '(' _ "DISTINCT"i _ e:Expression _ ')' { Expression::CountAggregate(Some(Box::new(e)), true) } / |
||||||
|
"COUNT"i _ '(' _ '*' _ ')' { Expression::CountAggregate(None, false) } / |
||||||
|
"COUNT"i _ '(' _ e:Expression _ ')' { Expression::CountAggregate(Some(Box::new(e)), false) } / |
||||||
|
"SUM"i _ '(' _ "DISTINCT"i _ e:Expression _ ')' { Expression::SumAggregate(Box::new(e), true) } / |
||||||
|
"SUM"i _ '(' _ e:Expression _ ')' { Expression::SumAggregate(Box::new(e), false) } / |
||||||
|
"MIN"i _ '(' _ "DISTINCT"i _ e:Expression _ ')' { Expression::MinAggregate(Box::new(e), true) } / |
||||||
|
"MIN"i _ '(' _ e:Expression _ ')' { Expression::MinAggregate(Box::new(e), false) } / |
||||||
|
"MAX"i _ '(' _ "DISTINCT"i _ e:Expression _ ')' { Expression::MaxAggregate(Box::new(e), true) } / |
||||||
|
"MAX"i _ '(' _ e:Expression _ ')' { Expression::MaxAggregate(Box::new(e), false) } / |
||||||
|
"AVG"i _ '(' _ "DISTINCT"i _ e:Expression _ ')' { Expression::AvgAggregate(Box::new(e), true) } / |
||||||
|
"AVG"i _ '(' _ e:Expression _ ')' { Expression::AvgAggregate(Box::new(e), false) } / |
||||||
|
"SAMPLE"i _ '(' _ "DISTINCT"i _ e:Expression _ ')' { Expression::SampleAggregate(Box::new(e), true) } / |
||||||
|
"SAMPLE"i _ '(' _ e:Expression _ ')' { Expression::SampleAggregate(Box::new(e), false) } / |
||||||
|
"GROUP_CONCAT"i _ '(' _ "DISTINCT"i _ e:Expression _ ';' _ 'SEPARATOR'i _ '=' _ s:String _ ')' { Expression::GroupConcatAggregate(Box::new(e), true, Some(s)) } / |
||||||
|
"GROUP_CONCAT"i _ '(' _ "DISTINCT"i _ e:Expression _ ')' { Expression::GroupConcatAggregate(Box::new(e), true, None) } / |
||||||
|
"GROUP_CONCAT"i _ '(' _ e:Expression _ ';' _ 'SEPARATOR'i _ '=' _ s:String _ ')' { Expression::GroupConcatAggregate(Box::new(e), true, Some(s)) } / |
||||||
|
"GROUP_CONCAT"i _ '(' _ e:Expression _ ')' { Expression::GroupConcatAggregate(Box::new(e), false, None) } |
||||||
|
|
||||||
|
//[128] |
||||||
|
iriOrFunction -> Expression = |
||||||
|
FunctionCall / |
||||||
|
i:iri { Expression::ConstantExpression(i.into()) } |
||||||
|
|
||||||
|
//[129] |
||||||
|
RDFLiteral -> Literal = |
||||||
|
v:String _ "^^" _ t:iri { Literal::new_typed_literal(v, t) } / |
||||||
|
v:String _ l:LANGTAG { Literal::new_language_tagged_literal(v, l) } / |
||||||
|
v:String { v.into() } |
||||||
|
|
||||||
|
//[130] |
||||||
|
NumericLiteral -> Literal = NumericLiteralUnsigned / NumericLiteralPositive / NumericLiteralNegative |
||||||
|
|
||||||
|
//[131] |
||||||
|
NumericLiteralUnsigned -> Literal = |
||||||
|
d:$(DOUBLE) { Literal::new_typed_literal(d, xsd::DOUBLE.clone()) } / |
||||||
|
d:$(DECIMAL) { Literal::new_typed_literal(d, xsd::DECIMAL.clone()) } / |
||||||
|
i:$(INTEGER) { Literal::new_typed_literal(i, xsd::INTEGER.clone()) } |
||||||
|
|
||||||
|
//[132] |
||||||
|
NumericLiteralPositive -> Literal = |
||||||
|
d:$(DOUBLE_POSITIVE) { Literal::new_typed_literal(d, xsd::DOUBLE.clone()) } / |
||||||
|
d:$(DECIMAL_POSITIVE) { Literal::new_typed_literal(d, xsd::DECIMAL.clone()) } / |
||||||
|
i:$(INTEGER_POSITIVE) { Literal::new_typed_literal(i, xsd::INTEGER.clone()) } |
||||||
|
|
||||||
|
|
||||||
|
//[133] |
||||||
|
NumericLiteralNegative -> Literal = |
||||||
|
d:$(DOUBLE_NEGATIVE) { Literal::new_typed_literal(d, xsd::DOUBLE.clone()) } / |
||||||
|
d:$(DECIMAL_NEGATIVE) { Literal::new_typed_literal(d, xsd::DECIMAL.clone()) } / |
||||||
|
i:$(INTEGER_NEGATIVE) { Literal::new_typed_literal(i, xsd::INTEGER.clone()) } |
||||||
|
|
||||||
|
//[134] |
||||||
|
BooleanLiteral -> Literal = |
||||||
|
"true" { true.into() } / |
||||||
|
"false" { false.into() } |
||||||
|
|
||||||
|
//[135] |
||||||
|
String -> String = STRING_LITERAL1 / STRING_LITERAL2 / STRING_LITERAL_LONG1 / STRING_LITERAL_LONG2 |
||||||
|
|
||||||
|
//[136] |
||||||
|
iri -> NamedNode = i:(IRIREF / PrefixedName) {? |
||||||
|
match state.url_parser().parse(&i) { |
||||||
|
Ok(url) => Ok(NamedNode::new(url)), |
||||||
|
Err(error) => Err("IRI parsing failed") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//[137] |
||||||
|
PrefixedName -> String = PNAME_LN / |
||||||
|
ns:PNAME_NS {? state.namespaces.get(ns).map(|v| v.clone()).ok_or("Prefix not found") } |
||||||
|
|
||||||
|
//[138] |
||||||
|
BlankNode -> BlankNode = |
||||||
|
b:BLANK_NODE_LABEL { state.bnodes_map.entry(b.to_string()).or_insert_with(BlankNode::default).clone() } / |
||||||
|
ANON { BlankNode::default() } |
||||||
|
|
||||||
|
//[139] |
||||||
|
IRIREF -> String = "<" i:$(([^\u{00}-\u{20}<>"{}|^\u{60}\u{5c}])*) ">" { |
||||||
|
i.to_owned() |
||||||
|
} |
||||||
|
|
||||||
|
//[140] |
||||||
|
PNAME_NS -> &'input str = ns:$(PN_PREFIX? ":") { |
||||||
|
ns |
||||||
|
} |
||||||
|
|
||||||
|
//[141] |
||||||
|
PNAME_LN -> String = ns:$(PNAME_NS) local:PN_LOCAL {? |
||||||
|
state.namespaces.get(ns).map(|v| v.clone() + &local).ok_or("Prefix not found") |
||||||
|
} |
||||||
|
|
||||||
|
//[142] |
||||||
|
BLANK_NODE_LABEL -> &'input str = "_:" b:$(([0-9] / PN_CHARS_U) PN_CHARS* ("."+ PN_CHARS+)*) { |
||||||
|
b |
||||||
|
} |
||||||
|
|
||||||
|
//[143] |
||||||
|
VAR1 -> &'input str = '?' v:$(VARNAME) { v } |
||||||
|
|
||||||
|
//[144] |
||||||
|
VAR2 -> &'input str = '$' v:$(VARNAME) { v } |
||||||
|
|
||||||
|
//[145] |
||||||
|
LANGTAG -> &'input str = "@" l:$([a-zA-Z]+ ("-" [a-zA-Z0-9]+)*) { |
||||||
|
l |
||||||
|
} |
||||||
|
|
||||||
|
//[146] |
||||||
|
INTEGER -> () = [0-9]+ |
||||||
|
|
||||||
|
//[147] |
||||||
|
DECIMAL -> () = [0-9]* '.' [0-9]+ |
||||||
|
|
||||||
|
//[148] |
||||||
|
DOUBLE -> () = ([0-9]+ "." [0-9]* / "."? [0-9]+) EXPONENT |
||||||
|
|
||||||
|
//[149] |
||||||
|
INTEGER_POSITIVE -> () = '+' _ INTEGER |
||||||
|
|
||||||
|
//[150] |
||||||
|
DECIMAL_POSITIVE -> () = '+' _ DECIMAL |
||||||
|
|
||||||
|
//[151] |
||||||
|
DOUBLE_POSITIVE -> () = '+' _ DOUBLE |
||||||
|
|
||||||
|
//[152] |
||||||
|
INTEGER_NEGATIVE -> () = '-' _ INTEGER |
||||||
|
|
||||||
|
//[153] |
||||||
|
DECIMAL_NEGATIVE -> () = '-' _ DECIMAL |
||||||
|
|
||||||
|
//[154] |
||||||
|
DOUBLE_NEGATIVE -> () = '-' _ DOUBLE |
||||||
|
|
||||||
|
//[155] |
||||||
|
EXPONENT -> () = [eE] [+-]? [0-9]+ |
||||||
|
|
||||||
|
//[156] |
||||||
|
STRING_LITERAL1 -> String = "'" l:((STRING_LITERAL1_simple_char / ECHAR)*) "'" { |
||||||
|
l.into_iter().collect() |
||||||
|
} |
||||||
|
STRING_LITERAL1_simple_char -> char = c:$([^'\u{005c}\u{000a}\u{000d}]) { c.chars().next().unwrap() } |
||||||
|
|
||||||
|
|
||||||
|
//[157] |
||||||
|
STRING_LITERAL2 -> String = "\"" l:((STRING_LITERAL2_simple_char / ECHAR)*) "\"" { |
||||||
|
l.into_iter().collect() |
||||||
|
} |
||||||
|
STRING_LITERAL2_simple_char -> char = c:$([^"\u{005c}\u{000a}\u{000d}]) { c.chars().next().unwrap() } |
||||||
|
|
||||||
|
//[158] |
||||||
|
STRING_LITERAL_LONG1 -> String = "'''" l:(STRING_LITERAL_LONG1_inner*) "'''" { |
||||||
|
l.into_iter().collect() |
||||||
|
} |
||||||
|
STRING_LITERAL_LONG1_inner -> String = a:$(("''" / "'")?) b:(STRING_LITERAL_LONG1_simple_char / ECHAR) { |
||||||
|
let mut s = a.to_string(); |
||||||
|
s.push(b); |
||||||
|
s |
||||||
|
} |
||||||
|
STRING_LITERAL_LONG1_simple_char -> char = c:$([^'\u{005c}]) { c.chars().next().unwrap() } |
||||||
|
|
||||||
|
//[159] |
||||||
|
STRING_LITERAL_LONG2 -> String = "\"\"\"" l:(STRING_LITERAL_LONG2_inner*) "\"\"\"" { |
||||||
|
l.into_iter().collect() |
||||||
|
} |
||||||
|
STRING_LITERAL_LONG2_inner -> String = a:$(("\"\"" / "\"")?) b:(STRING_LITERAL_LONG2_simple_char / ECHAR) { |
||||||
|
let mut s = a.to_string(); |
||||||
|
s.push(b); |
||||||
|
s |
||||||
|
} |
||||||
|
STRING_LITERAL_LONG2_simple_char -> char = c:$([^"\u{005c}]) { c.chars().next().unwrap() } |
||||||
|
|
||||||
|
//[160] |
||||||
|
ECHAR -> char = "\\" c:$([tbnrf"'\\]) { |
||||||
|
match c { |
||||||
|
"t" => '\u{0009}', |
||||||
|
"b" => '\u{0008}', |
||||||
|
"n" => '\u{000A}', |
||||||
|
"r" => '\u{000D}', |
||||||
|
"f" => '\u{000C}', |
||||||
|
"\"" => '\u{0022}', |
||||||
|
"'" => '\u{0027}', |
||||||
|
"\\" => '\u{005C}', |
||||||
|
_ => panic!("unexpected escaped char") // not possible |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//[161] |
||||||
|
NIL -> () = "(" WS* ")" |
||||||
|
|
||||||
|
//[162] |
||||||
|
WS -> () = #quiet<[\u{20}\u{9}\u{D}\u{A}]> |
||||||
|
|
||||||
|
//[163] |
||||||
|
ANON -> () = '[' WS* ']' |
||||||
|
|
||||||
|
//[164] |
||||||
|
PN_CHARS_BASE -> () = [A-Za-z\u{00C0}-\u{00D6}\u{00D8}-\u{00F6}\u{00F8}-\u{02FF}\u{0370}-\u{037D}\u{037F}-\u{1FFF}\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFFD}] |
||||||
|
|
||||||
|
//[165] |
||||||
|
PN_CHARS_U -> () = '_' / PN_CHARS_BASE |
||||||
|
|
||||||
|
//[166] |
||||||
|
VARNAME -> () = ([0-9] / PN_CHARS_U) ([0-9\u{00B7}\u{0300}-\u{036F}\u{203F}-\u{2040}] / PN_CHARS_U)* |
||||||
|
|
||||||
|
//[167] |
||||||
|
PN_CHARS -> () = [\-0-9\u{00B7}\u{0300}-\u{036F}\u{203F}-\u{2040}] / PN_CHARS_U |
||||||
|
|
||||||
|
//[168] |
||||||
|
PN_PREFIX -> () = PN_CHARS_BASE PN_CHARS* ("."+ PN_CHARS+)* |
||||||
|
|
||||||
|
//[169] |
||||||
|
PN_LOCAL -> String = f:PN_LOCAL_first c:(PN_LOCAL_next*) e:(PN_LOCAL_next_dot*) { |
||||||
|
f.to_string() + &c.concat() + &e.concat() |
||||||
|
} |
||||||
|
PN_LOCAL_first -> String = |
||||||
|
c:$(":" / [0-9] / PN_CHARS_U) { c.into() } / |
||||||
|
PLX |
||||||
|
PN_LOCAL_next -> String = |
||||||
|
c:$(":" / PN_CHARS) { c.into() } / |
||||||
|
PLX |
||||||
|
PN_LOCAL_next_dot -> String = d:$('.'+) f:PN_LOCAL_next* { d.to_string() + &f.concat()} |
||||||
|
|
||||||
|
//[170] |
||||||
|
PLX -> String = |
||||||
|
p:$(PERCENT) { p.into() } / |
||||||
|
e:PN_LOCAL_ESC { iter::once(e).collect() } |
||||||
|
|
||||||
|
//[171] |
||||||
|
PERCENT -> () = "%" HEX HEX |
||||||
|
|
||||||
|
//[172] |
||||||
|
HEX -> () = ([0-9A-Fa-f]) |
||||||
|
|
||||||
|
//[173] |
||||||
|
PN_LOCAL_ESC -> char = "\\" c:$([_~\.\-!$&'()*+,;=/?#@%:]) { c.chars().next().unwrap() } //TODO: added '/' to make tests pass but is it valid? |
||||||
|
|
||||||
|
//space |
||||||
|
_ = #quiet<([ \t\n\r] / comment)*> |
||||||
|
//comment |
||||||
|
comment = #quiet<"#" [^\r\n]*> |
@ -0,0 +1,52 @@ |
|||||||
|
extern crate reqwest; |
||||||
|
extern crate rudf; |
||||||
|
extern crate url; |
||||||
|
|
||||||
|
use reqwest::Client; |
||||||
|
use reqwest::Response; |
||||||
|
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::parser::read_sparql_query; |
||||||
|
use rudf::store::memory::MemoryGraph; |
||||||
|
use std::error::Error; |
||||||
|
use url::Url; |
||||||
|
|
||||||
|
pub struct RDFClient { |
||||||
|
client: Client, |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for RDFClient { |
||||||
|
fn default() -> Self { |
||||||
|
Self { |
||||||
|
client: Client::new(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl RDFClient { |
||||||
|
pub fn load_turtle(&self, url: Url) -> RioResult<MemoryGraph> { |
||||||
|
Ok(read_turtle(self.get(&url)?, Some(url))?.collect()) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn load_ntriples(&self, url: Url) -> RioResult<MemoryGraph> { |
||||||
|
read_ntriples(self.get(&url)?).collect() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn load_sparql_query(&self, url: Url) -> RioResult<Query> { |
||||||
|
read_sparql_query(self.get(&url)?, Some(url)) |
||||||
|
} |
||||||
|
|
||||||
|
fn get(&self, url: &Url) -> RioResult<Response> { |
||||||
|
match self.client.get(url.clone()).send() { |
||||||
|
Ok(response) => Ok(response), |
||||||
|
Err(error) => if error.description() == "message is incomplete" { |
||||||
|
self.get(url) |
||||||
|
} else { |
||||||
|
Err(RioError::new(error)) |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
///! Integration tests based on [SPARQL 1.1 Test Cases](https://www.w3.org/2009/sparql/docs/tests/)
|
||||||
|
|
||||||
|
#[macro_use] |
||||||
|
extern crate lazy_static; |
||||||
|
extern crate reqwest; |
||||||
|
extern crate rudf; |
||||||
|
extern crate url; |
||||||
|
|
||||||
|
mod client; |
||||||
|
|
||||||
|
use client::RDFClient; |
||||||
|
use rudf::model::data::*; |
||||||
|
use rudf::model::vocab::rdf; |
||||||
|
use rudf::sparql::parser::read_sparql_query; |
||||||
|
use url::Url; |
||||||
|
|
||||||
|
mod mf { |
||||||
|
use rudf::model::data::NamedNode; |
||||||
|
use std::str::FromStr; |
||||||
|
|
||||||
|
lazy_static! { |
||||||
|
pub static ref ACTION: NamedNode = NamedNode::from_str( |
||||||
|
"http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#action" |
||||||
|
).unwrap(); |
||||||
|
pub static ref RESULT: NamedNode = NamedNode::from_str( |
||||||
|
"http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#result" |
||||||
|
).unwrap(); |
||||||
|
pub static ref POSITIVE_SYNTAX_TEST_11: NamedNode = NamedNode::from_str( |
||||||
|
"http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#PositiveSyntaxTest11" |
||||||
|
).unwrap(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn sparql_w3c_syntax_testsuite() { |
||||||
|
let manifest_url = Url::parse( |
||||||
|
"http://www.w3.org/2009/sparql/docs/tests/data-sparql11/syntax-query/manifest.ttl", |
||||||
|
).unwrap(); |
||||||
|
let client = RDFClient::default(); |
||||||
|
let manifest = client.load_turtle(manifest_url.clone()).unwrap(); |
||||||
|
let mf_positive_syntax_test = Term::from(mf::POSITIVE_SYNTAX_TEST_11.clone()); |
||||||
|
|
||||||
|
manifest |
||||||
|
.subjects_for_predicate_object(&rdf::TYPE, &mf_positive_syntax_test) |
||||||
|
.for_each(|test| { |
||||||
|
if let Some(Term::NamedNode(file)) = |
||||||
|
manifest.object_for_subject_predicate(test, &mf::ACTION) |
||||||
|
{ |
||||||
|
match client.load_sparql_query(file.url().clone()) { |
||||||
|
Err(error) => assert!( |
||||||
|
false, |
||||||
|
"Failure on positive syntax file {} with error: {}", |
||||||
|
file, error |
||||||
|
), |
||||||
|
Ok(query) => { |
||||||
|
if let Err(error) = read_sparql_query(query.to_string().as_bytes(), None) { |
||||||
|
assert!( |
||||||
|
false, |
||||||
|
"Failure tu deserialize \"{}\" of file {} with error: {}", |
||||||
|
query.to_string(), |
||||||
|
file, |
||||||
|
error |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
Loading…
Reference in new issue