use crate::{
GrdfNquadDocument, NquadDocument,
lexing::{Lexer, NquadsLexingError, Token},
};
use decoded_char::DecodedChar;
use locspan::{Span, Spanned};
use rdf_syntax::{Id, IriBuf, Literal, LiteralType, Quad, RdfQuad, Term, XSD_STRING};
use std::fmt;
#[derive(Debug)]
pub enum NquadsError<E = NquadsLexingError> {
Lexer(E),
Unexpected(Option<Token>, Span),
}
impl<E: fmt::Display> fmt::Display for NquadsError<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Unexpected(None, _) => write!(f, "unexpected end of file"),
Self::Unexpected(Some(token), _) => write!(f, "unexpected {token}"),
Self::Lexer(e) => e.fmt(f),
}
}
}
impl<E: 'static + std::error::Error> std::error::Error for NquadsError<E> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Lexer(e) => Some(e),
_ => None,
}
}
}
pub struct NquadsParser<L> {
lexer: L,
position: usize,
pending: Option<Token>,
}
impl<L> NquadsParser<L> {
pub fn new(lexer: L) -> Self {
Self {
lexer,
position: 0,
pending: None,
}
}
}
impl<L, E> NquadsParser<L>
where
L: Iterator<Item = Result<Token, E>>,
{
fn next_token(&mut self) -> Result<Option<Token>, NquadsError<E>> {
let token = match self.pending.take() {
Some(token) => Some(token),
None => self.lexer.next().transpose().map_err(NquadsError::Lexer)?,
};
if let Some(token) = &token {
self.position = token.span().end
}
Ok(token)
}
#[allow(clippy::type_complexity)]
fn peek(&mut self) -> Result<Option<&Token>, NquadsError<E>> {
if self.pending.is_none() {
self.pending = self.lexer.next().transpose().map_err(NquadsError::Lexer)?;
}
Ok(self.pending.as_ref())
}
#[allow(clippy::type_complexity)]
fn parse_literal(
&mut self,
value: String,
mut span: Span,
) -> Result<(Literal, Span), NquadsError<E>> {
match self.peek()? {
Some(Token::LangTag(_, _)) => {
let Some(Token::LangTag(tag, tag_span)) = self.next_token()? else {
unreachable!()
};
span.append(tag_span);
Ok((Literal::new(value, LiteralType::LangString(tag)), span))
}
Some(Token::Carets(_)) => {
self.next_token()?;
match self.next_token()? {
Some(Token::Iri(iri, iri_span)) => {
span.append(iri_span);
Ok((Literal::new(value, LiteralType::Any(iri)), span))
}
Some(token) => {
let span = token.span();
Err(NquadsError::Unexpected(Some(token), span))
}
None => Err(NquadsError::Unexpected(None, self.position.into())),
}
}
_ => Ok((
Literal::new(value, LiteralType::Any(XSD_STRING.to_owned())),
span,
)),
}
}
fn parse_iri(&mut self) -> Result<(IriBuf, Span), NquadsError<E>> {
match self.next_token()? {
Some(Token::Iri(iri, span)) => Ok((iri, span)),
Some(token) => {
let span = token.span();
Err(NquadsError::Unexpected(Some(token), span))
}
None => Err(NquadsError::Unexpected(None, self.position.into())),
}
}
fn parse_id(&mut self) -> Result<(Id, Span), NquadsError<E>> {
match self.next_token()? {
Some(Token::Iri(iri, span)) => Ok((Id::Iri(iri), span)),
Some(Token::BlankNodeLabel(label, span)) => Ok((Id::BlankId(label), span)),
Some(token) => {
let span = token.span();
Err(NquadsError::Unexpected(Some(token), span))
}
None => Err(NquadsError::Unexpected(None, self.position.into())),
}
}
fn parse_id_opt(&mut self) -> Result<Option<(Id, Span)>, NquadsError<E>> {
match self.peek()? {
Some(Token::Dot(_)) => Ok(None),
_ => self.parse_id().map(Some),
}
}
fn parse_term(&mut self) -> Result<(Term, Span), NquadsError<E>> {
match self.next_token()? {
Some(Token::Iri(iri, span)) => Ok((Term::iri(iri), span)),
Some(Token::BlankNodeLabel(label, span)) => Ok((Term::BlankId(label), span)),
Some(Token::StringLiteral(value, span)) => {
let (literal, span) = self.parse_literal(value, span)?;
Ok((Term::literal(literal), span))
}
Some(token) => {
let span = token.span();
Err(NquadsError::Unexpected(Some(token), span))
}
None => Err(NquadsError::Unexpected(None, self.position.into())),
}
}
fn parse_term_opt(&mut self) -> Result<Option<(Term, Span)>, NquadsError<E>> {
match self.peek()? {
Some(Token::Dot(_)) => Ok(None),
_ => self.parse_term().map(Some),
}
}
fn parse_dot(&mut self) -> Result<Span, NquadsError<E>> {
match self.next_token()? {
Some(Token::Dot(span)) => Ok(span),
Some(token) => {
let span = token.span();
Err(NquadsError::Unexpected(Some(token), span))
}
None => Err(NquadsError::Unexpected(None, self.position.into())),
}
}
fn parse_quad(&mut self) -> Result<(RdfQuad, NquadsCodeMapEntry), NquadsError<E>> {
let (s, s_span) = self.parse_id()?;
let (p, p_span) = self.parse_iri()?;
let (o, o_span) = self.parse_term()?;
let (g, g_span) = split_option(self.parse_id_opt()?);
let dot_span = self.parse_dot()?;
let span = s_span.union(dot_span);
Ok((
Quad(s, p, o, g),
NquadsCodeMapEntry {
quad: span,
components: Quad(s_span, p_span, o_span, g_span),
},
))
}
fn parse_grdf_quad(&mut self) -> Result<(Quad<Term>, NquadsCodeMapEntry), NquadsError<E>> {
let (s, s_span) = self.parse_term()?;
let (p, p_span) = self.parse_term()?;
let (o, o_span) = self.parse_term()?;
let (g, g_span) = split_option(self.parse_term_opt()?);
let dot_span = self.parse_dot()?;
let span = s_span.union(dot_span);
Ok((
Quad(s, p, o, g),
NquadsCodeMapEntry {
quad: span,
components: Quad(s_span, p_span, o_span, g_span),
},
))
}
fn parse_document(&mut self) -> Result<(Vec<RdfQuad>, NquadsCodeMap), NquadsError<E>> {
let mut quads = Vec::new();
let mut spans = Vec::new();
while self.peek()?.is_some() {
let (quad, span) = self.parse_quad()?;
quads.push(quad);
spans.push(span);
}
Ok((quads, spans))
}
fn parse_grdf_document(&mut self) -> Result<(Vec<Quad<Term>>, NquadsCodeMap), NquadsError<E>> {
let mut quads = Vec::new();
let mut spans = Vec::new();
while self.peek()?.is_some() {
let (quad, span) = self.parse_grdf_quad()?;
quads.push(quad);
spans.push(span);
}
Ok((quads, spans))
}
}
fn split_option<A, B>(value: Option<(A, B)>) -> (Option<A>, Option<B>) {
match value {
Some((a, b)) => (Some(a), Some(b)),
None => (None, None),
}
}
pub type NquadsCodeMap = Vec<NquadsCodeMapEntry>;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NquadsCodeMapEntry {
pub quad: Span,
pub components: Quad<Span>,
}
pub trait NquadsParse: Sized {
#[allow(clippy::type_complexity)]
fn parse_with<L, E>(
parser: &mut NquadsParser<L>,
) -> Result<(Self, NquadsCodeMap), NquadsError<E>>
where
L: Iterator<Item = Result<Token, E>>;
#[inline(always)]
fn parse<C, E>(chars: C) -> Result<(Self, NquadsCodeMap), NquadsError<NquadsLexingError<E>>>
where
C: Iterator<Item = Result<DecodedChar, E>>,
{
let mut parser = NquadsParser::new(Lexer::new(chars));
Self::parse_with(&mut parser)
}
#[inline(always)]
fn parse_infallible<C>(chars: C) -> Result<(Self, NquadsCodeMap), NquadsError>
where
C: Iterator<Item = DecodedChar>,
{
Self::parse(chars.map(Ok))
}
#[inline(always)]
fn parse_utf8<C, E>(
chars: C,
) -> Result<(Self, NquadsCodeMap), NquadsError<NquadsLexingError<E>>>
where
C: Iterator<Item = Result<char, E>>,
{
Self::parse(decoded_char::FallibleUtf8Decoded::new(chars))
}
#[inline(always)]
fn parse_utf8_infallible<C>(chars: C) -> Result<(Self, NquadsCodeMap), NquadsError>
where
C: Iterator<Item = char>,
{
Self::parse_infallible(decoded_char::Utf8Decoded::new(chars))
}
#[inline(always)]
fn parse_utf16<C, E>(
chars: C,
) -> Result<(Self, NquadsCodeMap), NquadsError<NquadsLexingError<E>>>
where
C: Iterator<Item = Result<char, E>>,
{
Self::parse(decoded_char::FallibleUtf16Decoded::new(chars))
}
#[inline(always)]
fn parse_utf16_infallible<C>(chars: C) -> Result<(Self, NquadsCodeMap), NquadsError>
where
C: Iterator<Item = char>,
{
Self::parse_infallible(decoded_char::Utf16Decoded::new(chars))
}
#[inline(always)]
fn parse_str(string: &str) -> Result<(Self, NquadsCodeMap), NquadsError> {
Self::parse_utf8_infallible(string.chars())
}
}
impl NquadsParse for NquadDocument {
fn parse_with<L, E>(
parser: &mut NquadsParser<L>,
) -> Result<(Self, NquadsCodeMap), NquadsError<E>>
where
L: Iterator<Item = Result<Token, E>>,
{
parser.parse_document()
}
}
impl NquadsParse for GrdfNquadDocument {
fn parse_with<L, E>(
parser: &mut NquadsParser<L>,
) -> Result<(Self, NquadsCodeMap), NquadsError<E>>
where
L: Iterator<Item = Result<Token, E>>,
{
parser.parse_grdf_document()
}
}