diff --git a/lib/src/error.rs b/lib/src/error.rs index cd6aae79..dccdf55f 100644 --- a/lib/src/error.rs +++ b/lib/src/error.rs @@ -1,7 +1,6 @@ +use crate::sparql::SparqlParseError; use oxilangtag::LanguageTagParseError; use oxiri::IriParseError; -use peg::error::ParseError; -use peg::str::LineCol; use rio_turtle::TurtleError; use rio_xml::RdfXmlError; use std::error; @@ -120,8 +119,8 @@ impl From for Error { } } -impl From> for Error { - fn from(error: ParseError) -> Self { +impl From for Error { + fn from(error: SparqlParseError) -> Self { Self::wrap(error) } } diff --git a/lib/src/sparql/mod.rs b/lib/src/sparql/mod.rs index 28b02125..b9f4c722 100644 --- a/lib/src/sparql/mod.rs +++ b/lib/src/sparql/mod.rs @@ -12,7 +12,6 @@ mod xml_results; use crate::model::NamedNode; use crate::sparql::algebra::QueryVariants; use crate::sparql::eval::SimpleEvaluator; -use crate::sparql::parser::read_sparql_query; use crate::sparql::plan::TripleTemplate; use crate::sparql::plan::{DatasetView, PlanNode}; use crate::sparql::plan_builder::PlanBuilder; @@ -20,13 +19,14 @@ use crate::store::ReadableEncodedStore; use crate::Error; use crate::Result; use oxiri::Iri; -use std::fmt; pub use crate::sparql::algebra::GraphPattern; pub use crate::sparql::model::BindingsIterator; pub use crate::sparql::model::QueryResult; pub use crate::sparql::model::QueryResultSyntax; pub use crate::sparql::model::Variable; +pub use crate::sparql::parser::Query; +pub use crate::sparql::parser::SparqlParseError; /// A prepared [SPARQL query](https://www.w3.org/TR/sparql11-query/) pub trait PreparedQuery { @@ -61,7 +61,7 @@ enum SimplePreparedQueryAction { impl<'a, S: ReadableEncodedStore + 'a> SimplePreparedQuery { pub(crate) fn new(store: S, query: &str, options: QueryOptions<'_>) -> Result { let dataset = DatasetView::new(store, options.default_graph_as_union); - Ok(Self(match read_sparql_query(query, options.base_iri)? { + Ok(Self(match Query::parse(query, options.base_iri)?.0 { QueryVariants::Select { algebra, base_iri, .. } => { @@ -219,20 +219,3 @@ impl<'a> QueryOptions<'a> { self } } - -/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/) -#[derive(Eq, PartialEq, Debug, Clone, Hash)] -pub struct Query(QueryVariants); - -impl fmt::Display for Query { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl Query { - /// Parses a SPARQL query - pub fn parse(query: &str, base_iri: Option<&str>) -> Result { - Ok(Query(read_sparql_query(query, base_iri)?)) - } -} diff --git a/lib/src/sparql/parser.rs b/lib/src/sparql/parser.rs index 4fb6fd19..26fe8dcf 100644 --- a/lib/src/sparql/parser.rs +++ b/lib/src/sparql/parser.rs @@ -4,13 +4,80 @@ use crate::model::*; use crate::sparql::algebra::*; use crate::sparql::model::*; use oxiri::{Iri, IriParseError}; +use peg::error::ParseError; use peg::parser; +use peg::str::LineCol; use std::borrow::Cow; -use std::char; use std::collections::HashMap; use std::collections::{BTreeMap, BTreeSet}; +use std::error::Error; use std::str::Chars; use std::str::FromStr; +use std::{char, fmt}; + +/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/) +#[derive(Eq, PartialEq, Debug, Clone, Hash)] +pub struct Query(pub(crate) QueryVariants); + +impl fmt::Display for Query { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Query { + /// Parses a SPARQL query + pub fn parse(query: &str, base_iri: Option<&str>) -> Result { + let mut state = ParserState { + base_iri: if let Some(base_iri) = base_iri { + Some( + Iri::parse(base_iri.to_owned()).map_err(|e| SparqlParseError { + inner: SparqlParseErrorKind::InvalidBaseIri(e), + })?, + ) + } else { + None + }, + namespaces: HashMap::default(), + bnodes_map: BTreeMap::default(), + used_bnodes: BTreeSet::default(), + aggregations: Vec::default(), + }; + + Ok(Self( + parser::QueryUnit(&unescape_unicode_codepoints(query), &mut state).map_err(|e| { + SparqlParseError { + inner: SparqlParseErrorKind::Parser(e), + } + })?, + )) + } +} + +/// Error returned during SPARQL parsing. +#[derive(Debug)] +pub struct SparqlParseError { + inner: SparqlParseErrorKind, +} + +#[derive(Debug)] +enum SparqlParseErrorKind { + InvalidBaseIri(IriParseError), + Parser(ParseError), +} + +impl fmt::Display for SparqlParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.inner { + SparqlParseErrorKind::InvalidBaseIri(e) => { + write!(f, "Invalid SPARQL base IRI provided: {}", e) + } + SparqlParseErrorKind::Parser(e) => e.fmt(f), + } + } +} + +impl Error for SparqlParseError {} struct FocusedTriplePattern { focus: F, @@ -521,30 +588,6 @@ pub fn unescape_pn_local(input: &str) -> Cow<'_, str> { unescape_characters(input, &UNESCAPE_PN_CHARACTERS, &UNESCAPE_PN_REPLACEMENT) } -//include!(concat!(env!("OUT_DIR"), "/sparql_grammar.rs")); - -pub fn read_sparql_query( - query: &str, - base_iri: Option<&str>, -) -> super::super::Result { - let mut state = ParserState { - base_iri: if let Some(base_iri) = base_iri { - Some(Iri::parse(base_iri.to_owned())?) - } else { - None - }, - namespaces: HashMap::default(), - bnodes_map: BTreeMap::default(), - used_bnodes: BTreeSet::default(), - aggregations: Vec::default(), - }; - - Ok(parser::QueryUnit( - &unescape_unicode_codepoints(query), - &mut state, - )?) -} - parser! { //See https://www.w3.org/TR/turtle/#sec-grammar grammar parser(state: &mut ParserState) for str {