Skip to main content

oxttl/
n3.rs

1//! A [N3](https://w3c.github.io/N3/spec/) streaming parser implemented by [`N3Parser`].
2
3use crate::lexer::{N3Lexer, N3LexerMode, N3LexerOptions, N3Token, resolve_local_name};
4#[cfg(feature = "async-tokio")]
5use crate::toolkit::TokioAsyncReaderIterator;
6use crate::toolkit::{
7    Lexer, Parser, ReaderIterator, RuleRecognizer, RuleRecognizerError, SliceIterator,
8    TokenOrLineJump, TurtleSyntaxError,
9};
10use crate::{MAX_BUFFER_SIZE, MIN_BUFFER_SIZE, TurtleParseError};
11use oxiri::{Iri, IriParseError};
12#[cfg(feature = "rdf-12")]
13use oxrdf::Triple;
14use oxrdf::vocab::{rdf, xsd};
15use oxrdf::{
16    BlankNode, GraphName, Literal, NamedNode, NamedNodeRef, NamedOrBlankNode, Quad, Term, Variable,
17};
18use std::collections::HashMap;
19use std::collections::hash_map::Iter;
20use std::fmt;
21use std::io::Read;
22#[cfg(feature = "async-tokio")]
23use tokio::io::AsyncRead;
24
25/// A N3 term i.e. a RDF `Term` or a `Variable`.
26#[derive(Eq, PartialEq, Debug, Clone, Hash)]
27pub enum N3Term {
28    NamedNode(NamedNode),
29    BlankNode(BlankNode),
30    Literal(Literal),
31    #[cfg(feature = "rdf-12")]
32    Triple(Box<Triple>),
33    Variable(Variable),
34}
35
36impl fmt::Display for N3Term {
37    #[inline]
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        match self {
40            Self::NamedNode(term) => term.fmt(f),
41            Self::BlankNode(term) => term.fmt(f),
42            Self::Literal(term) => term.fmt(f),
43            #[cfg(feature = "rdf-12")]
44            Self::Triple(term) => term.fmt(f),
45            Self::Variable(term) => term.fmt(f),
46        }
47    }
48}
49
50impl From<NamedNode> for N3Term {
51    #[inline]
52    fn from(node: NamedNode) -> Self {
53        Self::NamedNode(node)
54    }
55}
56
57impl From<NamedNodeRef<'_>> for N3Term {
58    #[inline]
59    fn from(node: NamedNodeRef<'_>) -> Self {
60        Self::NamedNode(node.into_owned())
61    }
62}
63
64impl From<BlankNode> for N3Term {
65    #[inline]
66    fn from(node: BlankNode) -> Self {
67        Self::BlankNode(node)
68    }
69}
70
71impl From<Literal> for N3Term {
72    #[inline]
73    fn from(literal: Literal) -> Self {
74        Self::Literal(literal)
75    }
76}
77
78#[cfg(feature = "rdf-12")]
79impl From<Triple> for N3Term {
80    #[inline]
81    fn from(triple: Triple) -> Self {
82        Self::Triple(Box::new(triple))
83    }
84}
85
86#[cfg(feature = "rdf-12")]
87impl From<Box<Triple>> for N3Term {
88    #[inline]
89    fn from(node: Box<Triple>) -> Self {
90        Self::Triple(node)
91    }
92}
93
94impl From<NamedOrBlankNode> for N3Term {
95    #[inline]
96    fn from(node: NamedOrBlankNode) -> Self {
97        match node {
98            NamedOrBlankNode::NamedNode(node) => node.into(),
99            NamedOrBlankNode::BlankNode(node) => node.into(),
100        }
101    }
102}
103
104impl From<Term> for N3Term {
105    #[inline]
106    fn from(node: Term) -> Self {
107        match node {
108            Term::NamedNode(node) => node.into(),
109            Term::BlankNode(node) => node.into(),
110            Term::Literal(node) => node.into(),
111            #[cfg(feature = "rdf-12")]
112            Term::Triple(triple) => Self::Triple(triple),
113        }
114    }
115}
116
117impl From<Variable> for N3Term {
118    #[inline]
119    fn from(variable: Variable) -> Self {
120        Self::Variable(variable)
121    }
122}
123
124/// A N3 quad i.e. a quad composed of [`N3Term`].
125///
126/// The `graph_name` is used to encode the formula where the triple is in.
127/// In this case the formula is encoded by a blank node.
128#[derive(Eq, PartialEq, Debug, Clone, Hash)]
129pub struct N3Quad {
130    /// The [subject](https://www.w3.org/TR/rdf11-concepts/#dfn-subject) of this triple.
131    pub subject: N3Term,
132
133    /// The [predicate](https://www.w3.org/TR/rdf11-concepts/#dfn-predicate) of this triple.
134    pub predicate: N3Term,
135
136    /// The [object](https://www.w3.org/TR/rdf11-concepts/#dfn-object) of this triple.
137    pub object: N3Term,
138
139    /// The name of the RDF [graph](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-graph) in which the triple is.
140    pub graph_name: GraphName,
141}
142
143impl fmt::Display for N3Quad {
144    #[inline]
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146        if self.graph_name == GraphName::DefaultGraph {
147            write!(f, "{} {} {}", self.subject, self.predicate, self.object)
148        } else {
149            write!(
150                f,
151                "{} {} {} {}",
152                self.subject, self.predicate, self.object, self.graph_name
153            )
154        }
155    }
156}
157
158impl From<Quad> for N3Quad {
159    fn from(quad: Quad) -> Self {
160        Self {
161            subject: quad.subject.into(),
162            predicate: quad.predicate.into(),
163            object: quad.object.into(),
164            graph_name: quad.graph_name,
165        }
166    }
167}
168
169/// A [N3](https://w3c.github.io/N3/spec/) streaming parser.
170///
171/// Count the number of people:
172/// ```
173/// use oxrdf::NamedNode;
174/// use oxrdf::vocab::rdf;
175/// use oxttl::n3::{N3Parser, N3Term};
176///
177/// let file = r#"@base <http://example.com/> .
178/// @prefix schema: <http://schema.org/> .
179/// <foo> a schema:Person ;
180///     schema:name "Foo" .
181/// <bar> a schema:Person ;
182///     schema:name "Bar" ."#;
183///
184/// let rdf_type = N3Term::NamedNode(rdf::TYPE.into_owned());
185/// let schema_person = N3Term::NamedNode(NamedNode::new("http://schema.org/Person")?);
186/// let mut count = 0;
187/// for triple in N3Parser::new().for_reader(file.as_bytes()) {
188///     let triple = triple?;
189///     if triple.predicate == rdf_type && triple.object == schema_person {
190///         count += 1;
191///     }
192/// }
193/// assert_eq!(2, count);
194/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
195/// ```
196#[derive(Default, Clone)]
197#[must_use]
198pub struct N3Parser {
199    lenient: bool,
200    base: Option<Iri<String>>,
201    prefixes: HashMap<String, Iri<String>>,
202}
203
204impl N3Parser {
205    /// Builds a new [`N3Parser`].
206    #[inline]
207    pub fn new() -> Self {
208        Self::default()
209    }
210
211    /// Assumes the file is valid to make parsing faster.
212    ///
213    /// It will skip some validations.
214    ///
215    /// Note that if the file is actually not valid, the parser might emit broken RDF.
216    #[inline]
217    pub fn lenient(mut self) -> Self {
218        self.lenient = true;
219        self
220    }
221
222    #[deprecated(note = "Use `lenient()` instead", since = "0.2.0")]
223    #[inline]
224    pub fn unchecked(self) -> Self {
225        self.lenient()
226    }
227
228    #[inline]
229    pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Result<Self, IriParseError> {
230        self.base = Some(Iri::parse(base_iri.into())?);
231        Ok(self)
232    }
233
234    #[inline]
235    pub fn with_prefix(
236        mut self,
237        prefix_name: impl Into<String>,
238        prefix_iri: impl Into<String>,
239    ) -> Result<Self, IriParseError> {
240        self.prefixes
241            .insert(prefix_name.into(), Iri::parse(prefix_iri.into())?);
242        Ok(self)
243    }
244
245    /// Parses a N3 file from a [`Read`] implementation.
246    ///
247    /// Count the number of people:
248    /// ```
249    /// use oxrdf::NamedNode;
250    /// use oxttl::n3::{N3Parser, N3Term};
251    ///
252    /// let file = r#"@base <http://example.com/> .
253    /// @prefix schema: <http://schema.org/> .
254    /// <foo> a schema:Person ;
255    ///     schema:name "Foo" .
256    /// <bar> a schema:Person ;
257    ///     schema:name "Bar" ."#;
258    ///
259    /// let rdf_type = N3Term::NamedNode(NamedNode::new(
260    ///     "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
261    /// )?);
262    /// let schema_person = N3Term::NamedNode(NamedNode::new("http://schema.org/Person")?);
263    /// let mut count = 0;
264    /// for triple in N3Parser::new().for_reader(file.as_bytes()) {
265    ///     let triple = triple?;
266    ///     if triple.predicate == rdf_type && triple.object == schema_person {
267    ///         count += 1;
268    ///     }
269    /// }
270    /// assert_eq!(2, count);
271    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
272    /// ```
273    pub fn for_reader<R: Read>(self, reader: R) -> ReaderN3Parser<R> {
274        ReaderN3Parser {
275            inner: self.low_level().parser.for_reader(reader),
276        }
277    }
278
279    /// Parses a N3 file from a [`AsyncRead`] implementation.
280    ///
281    /// Count the number of people:
282    /// ```
283    /// # #[tokio::main(flavor = "current_thread")]
284    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
285    /// use oxrdf::NamedNode;
286    /// use oxrdf::vocab::rdf;
287    /// use oxttl::n3::{N3Parser, N3Term};
288    ///
289    /// let file = r#"@base <http://example.com/> .
290    /// @prefix schema: <http://schema.org/> .
291    /// <foo> a schema:Person ;
292    ///     schema:name "Foo" .
293    /// <bar> a schema:Person ;
294    ///     schema:name "Bar" ."#;
295    ///
296    /// let rdf_type = N3Term::NamedNode(rdf::TYPE.into_owned());
297    /// let schema_person = N3Term::NamedNode(NamedNode::new("http://schema.org/Person")?);
298    /// let mut count = 0;
299    /// let mut parser = N3Parser::new().for_tokio_async_reader(file.as_bytes());
300    /// while let Some(triple) = parser.next().await {
301    ///     let triple = triple?;
302    ///     if triple.predicate == rdf_type && triple.object == schema_person {
303    ///         count += 1;
304    ///     }
305    /// }
306    /// assert_eq!(2, count);
307    /// # Ok(())
308    /// # }
309    /// ```
310    #[cfg(feature = "async-tokio")]
311    pub fn for_tokio_async_reader<R: AsyncRead + Unpin>(
312        self,
313        reader: R,
314    ) -> TokioAsyncReaderN3Parser<R> {
315        TokioAsyncReaderN3Parser {
316            inner: self.low_level().parser.for_tokio_async_reader(reader),
317        }
318    }
319
320    /// Parses a N3 file from a byte slice.
321    ///
322    /// Count the number of people:
323    /// ```
324    /// use oxrdf::NamedNode;
325    /// use oxrdf::vocab::rdf;
326    /// use oxttl::n3::{N3Parser, N3Term};
327    ///
328    /// let file = r#"@base <http://example.com/> .
329    /// @prefix schema: <http://schema.org/> .
330    /// <foo> a schema:Person ;
331    ///     schema:name "Foo" .
332    /// <bar> a schema:Person ;
333    ///     schema:name "Bar" ."#;
334    ///
335    /// let rdf_type = N3Term::NamedNode(rdf::TYPE.into_owned());
336    /// let schema_person = N3Term::NamedNode(NamedNode::new("http://schema.org/Person")?);
337    /// let mut count = 0;
338    /// for triple in N3Parser::new().for_slice(file) {
339    ///     let triple = triple?;
340    ///     if triple.predicate == rdf_type && triple.object == schema_person {
341    ///         count += 1;
342    ///     }
343    /// }
344    /// assert_eq!(2, count);
345    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
346    /// ```
347    pub fn for_slice(self, slice: &(impl AsRef<[u8]> + ?Sized)) -> SliceN3Parser<'_> {
348        SliceN3Parser {
349            inner: N3Recognizer::new_parser(slice.as_ref(), true, false, self.base, self.prefixes)
350                .into_iter(),
351        }
352    }
353
354    /// Allows to parse a N3 file by using a low-level API.
355    ///
356    /// Count the number of people:
357    /// ```
358    /// use oxrdf::NamedNode;
359    /// use oxrdf::vocab::rdf;
360    /// use oxttl::n3::{N3Parser, N3Term};
361    ///
362    /// let file: [&[u8]; 5] = [
363    ///     b"@base <http://example.com/>",
364    ///     b". @prefix schema: <http://schema.org/> .",
365    ///     b"<foo> a schema:Person",
366    ///     b" ; schema:name \"Foo\" . <bar>",
367    ///     b" a schema:Person ; schema:name \"Bar\" .",
368    /// ];
369    ///
370    /// let rdf_type = N3Term::NamedNode(rdf::TYPE.into_owned());
371    /// let schema_person = N3Term::NamedNode(NamedNode::new("http://schema.org/Person")?);
372    /// let mut count = 0;
373    /// let mut parser = N3Parser::new().low_level();
374    /// let mut file_chunks = file.iter();
375    /// while !parser.is_end() {
376    ///     // We feed more data to the parser
377    ///     if let Some(chunk) = file_chunks.next() {
378    ///         parser.extend_from_slice(chunk);
379    ///     } else {
380    ///         parser.end(); // It's finished
381    ///     }
382    ///     // We read as many triples from the parser as possible
383    ///     while let Some(triple) = parser.parse_next() {
384    ///         let triple = triple?;
385    ///         if triple.predicate == rdf_type && triple.object == schema_person {
386    ///             count += 1;
387    ///         }
388    ///     }
389    /// }
390    /// assert_eq!(2, count);
391    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
392    /// ```
393    pub fn low_level(self) -> LowLevelN3Parser {
394        LowLevelN3Parser {
395            parser: N3Recognizer::new_parser(
396                Vec::new(),
397                false,
398                self.lenient,
399                self.base,
400                self.prefixes,
401            ),
402        }
403    }
404}
405
406/// Parses a N3 file from a [`Read`] implementation.
407///
408/// Can be built using [`N3Parser::for_reader`].
409///
410/// Count the number of people:
411/// ```
412/// use oxrdf::NamedNode;
413/// use oxrdf::vocab::rdf;
414/// use oxttl::n3::{N3Parser, N3Term};
415///
416/// let file = r#"@base <http://example.com/> .
417/// @prefix schema: <http://schema.org/> .
418/// <foo> a schema:Person ;
419///     schema:name "Foo" .
420/// <bar> a schema:Person ;
421///     schema:name "Bar" ."#;
422///
423/// let rdf_type = N3Term::NamedNode(rdf::TYPE.into_owned());
424/// let schema_person = N3Term::NamedNode(NamedNode::new("http://schema.org/Person")?);
425/// let mut count = 0;
426/// for triple in N3Parser::new().for_reader(file.as_bytes()) {
427///     let triple = triple?;
428///     if triple.predicate == rdf_type && triple.object == schema_person {
429///         count += 1;
430///     }
431/// }
432/// assert_eq!(2, count);
433/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
434/// ```
435#[must_use]
436pub struct ReaderN3Parser<R: Read> {
437    inner: ReaderIterator<R, N3Recognizer>,
438}
439
440impl<R: Read> ReaderN3Parser<R> {
441    /// The list of IRI prefixes considered at the current step of the parsing.
442    ///
443    /// This method returns (prefix name, prefix value) tuples.
444    /// It is empty at the beginning of the parsing and gets updated when prefixes are encountered.
445    /// It should be full at the end of the parsing (but if a prefix is overridden, only the latest version will be returned).
446    ///
447    /// ```
448    /// use oxttl::N3Parser;
449    ///
450    /// let file = r#"@base <http://example.com/> .
451    /// @prefix schema: <http://schema.org/> .
452    /// <foo> a schema:Person ;
453    ///     schema:name "Foo" ."#;
454    ///
455    /// let mut parser = N3Parser::new().for_reader(file.as_bytes());
456    /// assert_eq!(parser.prefixes().collect::<Vec<_>>(), []); // No prefix at the beginning
457    ///
458    /// parser.next().unwrap()?; // We read the first triple
459    /// assert_eq!(
460    ///     parser.prefixes().collect::<Vec<_>>(),
461    ///     [("schema", "http://schema.org/")]
462    /// ); // There are now prefixes
463    /// //
464    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
465    /// ```
466    pub fn prefixes(&self) -> N3PrefixesIter<'_> {
467        N3PrefixesIter {
468            inner: self.inner.parser.context.prefixes.iter(),
469        }
470    }
471
472    /// The base IRI considered at the current step of the parsing.
473    ///
474    /// ```
475    /// use oxttl::N3Parser;
476    ///
477    /// let file = r#"@base <http://example.com/> .
478    /// @prefix schema: <http://schema.org/> .
479    /// <foo> a schema:Person ;
480    ///     schema:name "Foo" ."#;
481    ///
482    /// let mut parser = N3Parser::new().for_reader(file.as_bytes());
483    /// assert!(parser.base_iri().is_none()); // No base at the beginning because none has been given to the parser.
484    ///
485    /// parser.next().unwrap()?; // We read the first triple
486    /// assert_eq!(parser.base_iri(), Some("http://example.com/")); // There is now a base IRI.
487    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
488    /// ```
489    pub fn base_iri(&self) -> Option<&str> {
490        self.inner
491            .parser
492            .context
493            .lexer_options
494            .base_iri
495            .as_ref()
496            .map(Iri::as_str)
497    }
498}
499
500impl<R: Read> Iterator for ReaderN3Parser<R> {
501    type Item = Result<N3Quad, TurtleParseError>;
502
503    fn next(&mut self) -> Option<Self::Item> {
504        self.inner.next()
505    }
506}
507
508/// Parses a N3 file from a [`AsyncRead`] implementation.
509///
510/// Can be built using [`N3Parser::for_tokio_async_reader`].
511///
512/// Count the number of people:
513/// ```
514/// # #[tokio::main(flavor = "current_thread")]
515/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
516/// use oxrdf::NamedNode;
517/// use oxrdf::vocab::rdf;
518/// use oxttl::n3::{N3Parser, N3Term};
519///
520/// let file = r#"@base <http://example.com/> .
521/// @prefix schema: <http://schema.org/> .
522/// <foo> a schema:Person ;
523///     schema:name "Foo" .
524/// <bar> a schema:Person ;
525///     schema:name "Bar" ."#;
526///
527/// let rdf_type = N3Term::NamedNode(rdf::TYPE.into_owned());
528/// let schema_person = N3Term::NamedNode(NamedNode::new("http://schema.org/Person")?);
529/// let mut count = 0;
530/// let mut parser = N3Parser::new().for_tokio_async_reader(file.as_bytes());
531/// while let Some(triple) = parser.next().await {
532///     let triple = triple?;
533///     if triple.predicate == rdf_type && triple.object == schema_person {
534///         count += 1;
535///     }
536/// }
537/// assert_eq!(2, count);
538/// # Ok(())
539/// # }
540/// ```
541#[cfg(feature = "async-tokio")]
542#[must_use]
543pub struct TokioAsyncReaderN3Parser<R: AsyncRead + Unpin> {
544    inner: TokioAsyncReaderIterator<R, N3Recognizer>,
545}
546
547#[cfg(feature = "async-tokio")]
548impl<R: AsyncRead + Unpin> TokioAsyncReaderN3Parser<R> {
549    /// Reads the next triple or returns `None` if the file is finished.
550    pub async fn next(&mut self) -> Option<Result<N3Quad, TurtleParseError>> {
551        self.inner.next().await
552    }
553
554    /// The list of IRI prefixes considered at the current step of the parsing.
555    ///
556    /// This method returns (prefix name, prefix value) tuples.
557    /// It is empty at the beginning of the parsing and gets updated when prefixes are encountered.
558    /// It should be full at the end of the parsing (but if a prefix is overridden, only the latest version will be returned).
559    ///
560    /// ```
561    /// # #[tokio::main(flavor = "current_thread")]
562    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
563    /// use oxttl::N3Parser;
564    ///
565    /// let file = r#"@base <http://example.com/> .
566    /// @prefix schema: <http://schema.org/> .
567    /// <foo> a schema:Person ;
568    ///     schema:name "Foo" ."#;
569    ///
570    /// let mut parser = N3Parser::new().for_tokio_async_reader(file.as_bytes());
571    /// assert_eq!(parser.prefixes().collect::<Vec<_>>(), []); // No prefix at the beginning
572    ///
573    /// parser.next().await.unwrap()?; // We read the first triple
574    /// assert_eq!(
575    ///     parser.prefixes().collect::<Vec<_>>(),
576    ///     [("schema", "http://schema.org/")]
577    /// ); // There are now prefixes
578    /// //
579    /// # Ok(())
580    /// # }
581    /// ```
582    pub fn prefixes(&self) -> N3PrefixesIter<'_> {
583        N3PrefixesIter {
584            inner: self.inner.parser.context.prefixes.iter(),
585        }
586    }
587
588    /// The base IRI considered at the current step of the parsing.
589    ///
590    /// ```
591    /// # #[tokio::main(flavor = "current_thread")]
592    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
593    /// use oxttl::N3Parser;
594    ///
595    /// let file = r#"@base <http://example.com/> .
596    /// @prefix schema: <http://schema.org/> .
597    /// <foo> a schema:Person ;
598    ///     schema:name "Foo" ."#;
599    ///
600    /// let mut parser = N3Parser::new().for_tokio_async_reader(file.as_bytes());
601    /// assert!(parser.base_iri().is_none()); // No base IRI at the beginning
602    ///
603    /// parser.next().await.unwrap()?; // We read the first triple
604    /// assert_eq!(parser.base_iri(), Some("http://example.com/")); // There is now a base IRI
605    /// //
606    /// # Ok(())
607    /// # }
608    /// ```
609    pub fn base_iri(&self) -> Option<&str> {
610        self.inner
611            .parser
612            .context
613            .lexer_options
614            .base_iri
615            .as_ref()
616            .map(Iri::as_str)
617    }
618}
619
620/// Parses a N3 file from a byte slice.
621///
622/// Can be built using [`N3Parser::for_slice`].
623///
624/// Count the number of people:
625/// ```
626/// use oxrdf::NamedNode;
627/// use oxrdf::vocab::rdf;
628/// use oxttl::n3::{N3Parser, N3Term};
629///
630/// let file = r#"@base <http://example.com/> .
631/// @prefix schema: <http://schema.org/> .
632/// <foo> a schema:Person ;
633///     schema:name "Foo" .
634/// <bar> a schema:Person ;
635///     schema:name "Bar" ."#;
636///
637/// let rdf_type = N3Term::NamedNode(rdf::TYPE.into_owned());
638/// let schema_person = N3Term::NamedNode(NamedNode::new("http://schema.org/Person")?);
639/// let mut count = 0;
640/// for triple in N3Parser::new().for_slice(file) {
641///     let triple = triple?;
642///     if triple.predicate == rdf_type && triple.object == schema_person {
643///         count += 1;
644///     }
645/// }
646/// assert_eq!(2, count);
647/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
648/// ```
649#[must_use]
650pub struct SliceN3Parser<'a> {
651    inner: SliceIterator<'a, N3Recognizer>,
652}
653
654impl SliceN3Parser<'_> {
655    /// The list of IRI prefixes considered at the current step of the parsing.
656    ///
657    /// This method returns (prefix name, prefix value) tuples.
658    /// It is empty at the beginning of the parsing and gets updated when prefixes are encountered.
659    /// It should be full at the end of the parsing (but if a prefix is overridden, only the latest version will be returned).
660    ///
661    /// ```
662    /// use oxttl::N3Parser;
663    ///
664    /// let file = r#"@base <http://example.com/> .
665    /// @prefix schema: <http://schema.org/> .
666    /// <foo> a schema:Person ;
667    ///     schema:name "Foo" ."#;
668    ///
669    /// let mut parser = N3Parser::new().for_slice(file);
670    /// assert_eq!(parser.prefixes().collect::<Vec<_>>(), []); // No prefix at the beginning
671    ///
672    /// parser.next().unwrap()?; // We read the first triple
673    /// assert_eq!(
674    ///     parser.prefixes().collect::<Vec<_>>(),
675    ///     [("schema", "http://schema.org/")]
676    /// ); // There are now prefixes
677    /// //
678    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
679    /// ```
680    pub fn prefixes(&self) -> N3PrefixesIter<'_> {
681        N3PrefixesIter {
682            inner: self.inner.parser.context.prefixes.iter(),
683        }
684    }
685
686    /// The base IRI considered at the current step of the parsing.
687    ///
688    /// ```
689    /// use oxttl::N3Parser;
690    ///
691    /// let file = r#"@base <http://example.com/> .
692    /// @prefix schema: <http://schema.org/> .
693    /// <foo> a schema:Person ;
694    ///     schema:name "Foo" ."#;
695    ///
696    /// let mut parser = N3Parser::new().for_slice(file);
697    /// assert!(parser.base_iri().is_none()); // No base at the beginning because none has been given to the parser.
698    ///
699    /// parser.next().unwrap()?; // We read the first triple
700    /// assert_eq!(parser.base_iri(), Some("http://example.com/")); // There is now a base IRI.
701    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
702    /// ```
703    pub fn base_iri(&self) -> Option<&str> {
704        self.inner
705            .parser
706            .context
707            .lexer_options
708            .base_iri
709            .as_ref()
710            .map(Iri::as_str)
711    }
712}
713
714impl Iterator for SliceN3Parser<'_> {
715    type Item = Result<N3Quad, TurtleSyntaxError>;
716
717    fn next(&mut self) -> Option<Self::Item> {
718        self.inner.next()
719    }
720}
721
722/// Parses a N3 file by using a low-level API.
723///
724/// Can be built using [`N3Parser::low_level`].
725///
726/// Count the number of people:
727/// ```
728/// use oxrdf::NamedNode;
729/// use oxrdf::vocab::rdf;
730/// use oxttl::n3::{N3Parser, N3Term};
731///
732/// let file: [&[u8]; 5] = [
733///     b"@base <http://example.com/>",
734///     b". @prefix schema: <http://schema.org/> .",
735///     b"<foo> a schema:Person",
736///     b" ; schema:name \"Foo\" . <bar>",
737///     b" a schema:Person ; schema:name \"Bar\" .",
738/// ];
739///
740/// let rdf_type = N3Term::NamedNode(rdf::TYPE.into_owned());
741/// let schema_person = N3Term::NamedNode(NamedNode::new("http://schema.org/Person")?);
742/// let mut count = 0;
743/// let mut parser = N3Parser::new().low_level();
744/// let mut file_chunks = file.iter();
745/// while !parser.is_end() {
746///     // We feed more data to the parser
747///     if let Some(chunk) = file_chunks.next() {
748///         parser.extend_from_slice(chunk);
749///     } else {
750///         parser.end(); // It's finished
751///     }
752///     // We read as many triples from the parser as possible
753///     while let Some(triple) = parser.parse_next() {
754///         let triple = triple?;
755///         if triple.predicate == rdf_type && triple.object == schema_person {
756///             count += 1;
757///         }
758///     }
759/// }
760/// assert_eq!(2, count);
761/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
762/// ```
763pub struct LowLevelN3Parser {
764    parser: Parser<Vec<u8>, N3Recognizer>,
765}
766
767impl LowLevelN3Parser {
768    /// Adds some extra bytes to the parser. Should be called when [`parse_next`](Self::parse_next) returns [`None`] and there is still unread data.
769    pub fn extend_from_slice(&mut self, other: &[u8]) {
770        self.parser.extend_from_slice(other)
771    }
772
773    /// Tell the parser that the file is finished.
774    ///
775    /// This triggers the parsing of the final bytes and might lead [`parse_next`](Self::parse_next) to return some extra values.
776    pub fn end(&mut self) {
777        self.parser.end()
778    }
779
780    /// Returns if the parsing is finished i.e. [`end`](Self::end) has been called and [`parse_next`](Self::parse_next) is always going to return `None`.
781    pub fn is_end(&self) -> bool {
782        self.parser.is_end()
783    }
784
785    /// Attempt to parse a new quad from the already provided data.
786    ///
787    /// Returns [`None`] if the parsing is finished or more data is required.
788    /// If it is the case more data should be fed using [`extend_from_slice`](Self::extend_from_slice).
789    pub fn parse_next(&mut self) -> Option<Result<N3Quad, TurtleSyntaxError>> {
790        self.parser.parse_next()
791    }
792
793    /// The list of IRI prefixes considered at the current step of the parsing.
794    ///
795    /// This method returns (prefix name, prefix value) tuples.
796    /// It is empty at the beginning of the parsing and gets updated when prefixes are encountered.
797    /// It should be full at the end of the parsing (but if a prefix is overridden, only the latest version will be returned).
798    ///
799    /// ```
800    /// use oxttl::N3Parser;
801    ///
802    /// let file = r#"@base <http://example.com/> .
803    /// @prefix schema: <http://schema.org/> .
804    /// <foo> a schema:Person ;
805    ///     schema:name "Foo" ."#;
806    ///
807    /// let mut parser = N3Parser::new().low_level();
808    /// parser.extend_from_slice(file.as_bytes());
809    /// assert_eq!(parser.prefixes().collect::<Vec<_>>(), []); // No prefix at the beginning
810    ///
811    /// parser.parse_next().unwrap()?; // We read the first triple
812    /// assert_eq!(
813    ///     parser.prefixes().collect::<Vec<_>>(),
814    ///     [("schema", "http://schema.org/")]
815    /// ); // There are now prefixes
816    /// //
817    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
818    /// ```
819    pub fn prefixes(&self) -> N3PrefixesIter<'_> {
820        N3PrefixesIter {
821            inner: self.parser.context.prefixes.iter(),
822        }
823    }
824
825    /// The base IRI considered at the current step of the parsing.
826    ///
827    /// ```
828    /// use oxttl::N3Parser;
829    ///
830    /// let file = r#"@base <http://example.com/> .
831    /// @prefix schema: <http://schema.org/> .
832    /// <foo> a schema:Person ;
833    ///     schema:name "Foo" ."#;
834    ///
835    /// let mut parser = N3Parser::new().low_level();
836    /// parser.extend_from_slice(file.as_bytes());
837    /// assert!(parser.base_iri().is_none()); // No base IRI at the beginning
838    ///
839    /// parser.parse_next().unwrap()?; // We read the first triple
840    /// assert_eq!(parser.base_iri(), Some("http://example.com/")); // There is now a base IRI
841    /// //
842    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
843    /// ```
844    pub fn base_iri(&self) -> Option<&str> {
845        self.parser
846            .context
847            .lexer_options
848            .base_iri
849            .as_ref()
850            .map(Iri::as_str)
851    }
852}
853
854#[derive(Clone)]
855enum Predicate {
856    Regular(N3Term),
857    Inverted(N3Term),
858}
859
860struct N3Recognizer {
861    stack: Vec<N3State>,
862    terms: Vec<N3Term>,
863    predicates: Vec<Predicate>,
864    contexts: Vec<BlankNode>,
865}
866
867struct N3RecognizerContext {
868    lexer_options: N3LexerOptions,
869    prefixes: HashMap<String, Iri<String>>,
870}
871
872impl RuleRecognizer for N3Recognizer {
873    type TokenRecognizer = N3Lexer;
874    type Output = N3Quad;
875    type Context = N3RecognizerContext;
876
877    fn error_recovery_state(mut self) -> Self {
878        self.stack.clear();
879        self.terms.clear();
880        self.predicates.clear();
881        self.contexts.clear();
882        self
883    }
884
885    fn recognize_next(
886        mut self,
887        token: TokenOrLineJump<N3Token<'_>>,
888        context: &mut N3RecognizerContext,
889        results: &mut Vec<N3Quad>,
890        errors: &mut Vec<RuleRecognizerError>,
891    ) -> Self {
892        let TokenOrLineJump::Token(token) = token else {
893            return self;
894        };
895        while let Some(rule) = self.stack.pop() {
896            match rule {
897                // [1]  n3Doc            ::=  ( ( n3Statement ".") | sparqlDirective) *
898                // [2]  n3Statement      ::=  n3Directive | triples
899                // [3]  n3Directive      ::=  prefixID | base
900                // [4]  sparqlDirective  ::=  sparqlBase | sparqlPrefix
901                // [5]  sparqlBase       ::=  BASE IRIREF
902                // [6]  sparqlPrefix     ::=  PREFIX PNAME_NS IRIREF
903                // [7]  prefixID         ::=  "@prefix" PNAME_NS IRIREF
904                // [8]  base             ::=  "@base" IRIREF
905                N3State::N3Doc => {
906                    self.stack.push(N3State::N3Doc);
907                    match token {
908                        N3Token::PlainKeyword(k) if k.eq_ignore_ascii_case("base") => {
909                            self.stack.push(N3State::BaseExpectIri);
910                            return self;
911                        }
912                        N3Token::PlainKeyword(k) if k.eq_ignore_ascii_case("prefix") => {
913                            self.stack.push(N3State::PrefixExpectPrefix);
914                            return self;
915                        }
916                        N3Token::LangTag {
917                            language: "prefix", #[cfg(
918                            feature = "rdf-12"
919                        )] direction: None
920                        } => {
921                            self.stack.push(N3State::N3DocExpectDot);
922                            self.stack.push(N3State::PrefixExpectPrefix);
923                            return self;
924                        }
925                        N3Token::LangTag {
926                            language: "base", #[cfg(
927                            feature = "rdf-12"
928                        )] direction: None
929                        } => {
930                            self.stack.push(N3State::N3DocExpectDot);
931                            self.stack.push(N3State::BaseExpectIri);
932                            return self;
933                        }
934                        _ => {
935                            self.stack.push(N3State::N3DocExpectDot);
936                            self.stack.push(N3State::Triples);
937                        }
938                    }
939                }
940                N3State::N3DocExpectDot => {
941                    if token == N3Token::Punctuation(".") {
942                        return self;
943                    }
944                    errors.push("A dot is expected at the end of N3 statements".into());
945                }
946                N3State::BaseExpectIri => return if let N3Token::IriRef(iri) = token {
947                    context.lexer_options.base_iri = Some(Iri::parse_unchecked(iri));
948                    self
949                } else {
950                    self.error(errors, "The BASE keyword should be followed by an IRI")
951                },
952                N3State::PrefixExpectPrefix => return match token {
953                    N3Token::PrefixedName { prefix, local, .. } if local.is_empty() => {
954                        self.stack.push(N3State::PrefixExpectIri { name: prefix.to_owned() });
955                        self
956                    }
957                    _ => {
958                        self.error(errors, "The PREFIX keyword should be followed by a prefix like 'ex:'")
959                    }
960                },
961                N3State::PrefixExpectIri { name } => return if let N3Token::IriRef(iri) = token {
962                    context.prefixes.insert(name, Iri::parse_unchecked(iri));
963                    self
964                } else {
965                    self.error(errors, "The PREFIX declaration should be followed by a prefix and its value as an IRI")
966                },
967                // [9]  triples  ::=  subject predicateObjectList?
968                N3State::Triples => {
969                    self.stack.push(N3State::TriplesMiddle);
970                    self.stack.push(N3State::Path);
971                }
972                N3State::TriplesMiddle => if matches!(token, N3Token::Punctuation("." | "]" | "}" | ")")) {} else {
973                    self.stack.push(N3State::TriplesEnd);
974                    self.stack.push(N3State::PredicateObjectList);
975                },
976                N3State::TriplesEnd => {
977                    self.terms.pop();
978                }
979                // [10]  predicateObjectList  ::=  verb objectList ( ";" ( verb objectList) ? ) *
980                N3State::PredicateObjectList => {
981                    self.stack.push(N3State::PredicateObjectListEnd);
982                    self.stack.push(N3State::ObjectsList);
983                    self.stack.push(N3State::Verb);
984                }
985                N3State::PredicateObjectListEnd => {
986                    self.predicates.pop();
987                    if token == N3Token::Punctuation(";") {
988                        self.stack.push(N3State::PredicateObjectListPossibleContinuation);
989                        return self;
990                    }
991                }
992                N3State::PredicateObjectListPossibleContinuation => if token == N3Token::Punctuation(";") {
993                    self.stack.push(N3State::PredicateObjectListPossibleContinuation);
994                    return self;
995                } else if matches!(token, N3Token::Punctuation(";" | "." | "}" | "]" | ")")) {} else {
996                    self.stack.push(N3State::PredicateObjectListEnd);
997                    self.stack.push(N3State::ObjectsList);
998                    self.stack.push(N3State::Verb);
999                },
1000                // [11]  objectList  ::=  object ( "," object) *
1001                N3State::ObjectsList => {
1002                    self.stack.push(N3State::ObjectsListEnd);
1003                    self.stack.push(N3State::Path);
1004                }
1005                N3State::ObjectsListEnd => {
1006                    let object = self.terms.pop().unwrap();
1007                    let subject = self.terms.last().unwrap().clone();
1008                    results.push(match self.predicates.last().unwrap().clone() {
1009                        Predicate::Regular(predicate) => self.quad(
1010                            subject,
1011                            predicate,
1012                            object,
1013                        ),
1014                        Predicate::Inverted(predicate) => self.quad(
1015                            object,
1016                            predicate,
1017                            subject,
1018                        )
1019                    });
1020                    if token == N3Token::Punctuation(",") {
1021                        self.stack.push(N3State::ObjectsListEnd);
1022                        self.stack.push(N3State::Path);
1023                        return self;
1024                    }
1025                }
1026                // [12]  verb       ::=  predicate | "a" | ( "has" expression) | ( "is" expression "of") | "=" | "<=" | "=>"
1027                // [14]  predicate  ::=  expression | ( "<-" expression)
1028                N3State::Verb => match token {
1029                    N3Token::PlainKeyword("a") => {
1030                        self.predicates.push(Predicate::Regular(rdf::TYPE.into()));
1031                        return self;
1032                    }
1033                    N3Token::PlainKeyword("has") => {
1034                        self.stack.push(N3State::AfterRegularVerb);
1035                        self.stack.push(N3State::Path);
1036                        return self;
1037                    }
1038                    N3Token::PlainKeyword("is") => {
1039                        self.stack.push(N3State::AfterVerbIs);
1040                        self.stack.push(N3State::Path);
1041                        return self;
1042                    }
1043                    N3Token::Punctuation("=") => {
1044                        self.predicates.push(Predicate::Regular(NamedNode::new_unchecked("http://www.w3.org/2002/07/owl#sameAs").into()));
1045                        return self;
1046                    }
1047                    N3Token::Punctuation("=>") => {
1048                        self.predicates.push(Predicate::Regular(NamedNode::new_unchecked("http://www.w3.org/2000/10/swap/log#implies").into()));
1049                        return self;
1050                    }
1051                    N3Token::Punctuation("<=") => {
1052                        self.predicates.push(Predicate::Inverted(NamedNode::new_unchecked("http://www.w3.org/2000/10/swap/log#implies").into()));
1053                        return self;
1054                    }
1055                    N3Token::Punctuation("<-") => {
1056                        self.stack.push(N3State::AfterInvertedVerb);
1057                        self.stack.push(N3State::Path);
1058                        return self;
1059                    }
1060                    _ => {
1061                        self.stack.push(N3State::AfterRegularVerb);
1062                        self.stack.push(N3State::Path);
1063                    }
1064                }
1065                N3State::AfterRegularVerb => {
1066                    self.predicates.push(Predicate::Regular(self.terms.pop().unwrap()));
1067                }
1068                N3State::AfterInvertedVerb => {
1069                    self.predicates.push(Predicate::Inverted(self.terms.pop().unwrap()));
1070                }
1071                N3State::AfterVerbIs => return match token {
1072                    N3Token::PlainKeyword("of") => {
1073                        self.predicates.push(Predicate::Inverted(self.terms.pop().unwrap()));
1074                        self
1075                    }
1076                    _ => {
1077                        self.error(errors, "The keyword 'is' should be followed by a predicate then by the keyword 'of'")
1078                    }
1079                },
1080                // [13]  subject     ::=  expression
1081                // [15]  object      ::=  expression
1082                // [16]  expression  ::=  path
1083                // [17]  path        ::=  pathItem ( ( "!" path) | ( "^" path) ) ?
1084                N3State::Path => {
1085                    self.stack.push(N3State::PathFollowUp);
1086                    self.stack.push(N3State::PathItem);
1087                }
1088                N3State::PathFollowUp => match token {
1089                    N3Token::Punctuation("!") => {
1090                        self.stack.push(N3State::PathAfterIndicator { is_inverse: false });
1091                        self.stack.push(N3State::PathItem);
1092                        return self;
1093                    }
1094                    N3Token::Punctuation("^") => {
1095                        self.stack.push(N3State::PathAfterIndicator { is_inverse: true });
1096                        self.stack.push(N3State::PathItem);
1097                        return self;
1098                    }
1099                    _ => ()
1100                },
1101                N3State::PathAfterIndicator { is_inverse } => {
1102                    let predicate = self.terms.pop().unwrap();
1103                    let previous = self.terms.pop().unwrap();
1104                    let current = BlankNode::default();
1105                    results.push(if is_inverse { self.quad(current.clone(), predicate, previous) } else { self.quad(previous, predicate, current.clone()) });
1106                    self.terms.push(current.into());
1107                    self.stack.push(N3State::PathFollowUp);
1108                }
1109                // [18]  pathItem               ::=  iri | blankNode | quickVar | collection | blankNodePropertyList | iriPropertyList | literal | formula
1110                // [19]  literal                ::=  rdfLiteral | numericLiteral | BOOLEAN_LITERAL
1111                // [20]  blankNodePropertyList  ::=  "[" predicateObjectList "]"
1112                // [21]  iriPropertyList        ::=  IPLSTART iri predicateObjectList "]"
1113                // [22]  collection             ::=  "(" object* ")"
1114                // [23]  formula                ::=  "{" formulaContent? "}"
1115                // [25]  numericLiteral         ::=  DOUBLE | DECIMAL | INTEGER
1116                // [26]  rdfLiteral             ::=  STRING ( LANGTAG | ( "^^" iri) ) ?
1117                // [27]  iri                    ::=  IRIREF | prefixedName
1118                // [28]  prefixedName           ::=  PNAME_LN | PNAME_NS
1119                // [29]  blankNode              ::=  BLANK_NODE_LABEL | ANON
1120                // [30]  quickVar               ::=  QUICK_VAR_NAME
1121                N3State::PathItem => {
1122                    return match token {
1123                        N3Token::IriRef(iri) => {
1124                            self.terms.push(NamedNode::new_unchecked(iri).into());
1125                            self
1126                        }
1127                        N3Token::PrefixedName { prefix, local, might_be_invalid_iri } => match resolve_local_name(prefix, &local, might_be_invalid_iri, &context.prefixes) {
1128                            Ok(t) => {
1129                                self.terms.push(t.into());
1130                                self
1131                            }
1132                            Err(e) => self.error(errors, e)
1133                        }
1134                        N3Token::BlankNodeLabel(bnode) => {
1135                            self.terms.push(BlankNode::new_unchecked(bnode).into());
1136                            self
1137                        }
1138                        N3Token::Variable(name) => {
1139                            self.terms.push(Variable::new_unchecked(name).into());
1140                            self
1141                        }
1142                        N3Token::Punctuation("[") => {
1143                            self.stack.push(N3State::PropertyListMiddle);
1144                            self
1145                        }
1146                        N3Token::Punctuation("(") => {
1147                            self.stack.push(N3State::CollectionBeginning);
1148                            self
1149                        }
1150                        N3Token::String(value) | N3Token::LongString(value) => {
1151                            self.stack.push(N3State::LiteralPossibleSuffix { value });
1152                            self
1153                        }
1154                        N3Token::Integer(v) => {
1155                            self.terms.push(Literal::new_typed_literal(v, xsd::INTEGER).into());
1156                            self
1157                        }
1158                        N3Token::Decimal(v) => {
1159                            self.terms.push(Literal::new_typed_literal(v, xsd::DECIMAL).into());
1160                            self
1161                        }
1162                        N3Token::Double(v) => {
1163                            self.terms.push(Literal::new_typed_literal(v, xsd::DOUBLE).into());
1164                            self
1165                        }
1166                        N3Token::PlainKeyword("true") => {
1167                            self.terms.push(Literal::new_typed_literal("true", xsd::BOOLEAN).into());
1168                            self
1169                        }
1170                        N3Token::PlainKeyword("false") => {
1171                            self.terms.push(Literal::new_typed_literal("false", xsd::BOOLEAN).into());
1172                            self
1173                        }
1174                        N3Token::Punctuation("{") => {
1175                            self.contexts.push(BlankNode::default());
1176                            self.stack.push(N3State::FormulaContent);
1177                            self
1178                        }
1179                        _ =>
1180                            self.error(errors, "TOKEN is not a valid RDF value")
1181                    }
1182                }
1183                N3State::PropertyListMiddle => match token {
1184                    N3Token::Punctuation("]") => {
1185                        self.terms.push(BlankNode::default().into());
1186                        return self;
1187                    }
1188                    N3Token::PlainKeyword("id") => {
1189                        self.stack.push(N3State::IriPropertyList);
1190                        return self;
1191                    }
1192                    _ => {
1193                        self.terms.push(BlankNode::default().into());
1194                        self.stack.push(N3State::PropertyListEnd);
1195                        self.stack.push(N3State::PredicateObjectList);
1196                    }
1197                }
1198                N3State::PropertyListEnd => if token == N3Token::Punctuation("]") {
1199                    return self;
1200                } else {
1201                    errors.push("blank node property lists should end with a ']'".into());
1202                }
1203                N3State::IriPropertyList => return match token {
1204                    N3Token::IriRef(id) => {
1205                        self.terms.push(NamedNode::new_unchecked(id).into());
1206                        self.stack.push(N3State::PropertyListEnd);
1207                        self.stack.push(N3State::PredicateObjectList);
1208                        self
1209                    }
1210                    N3Token::PrefixedName { prefix, local, might_be_invalid_iri } => match resolve_local_name(prefix, &local, might_be_invalid_iri, &context.prefixes) {
1211                        Ok(t) => {
1212                            self.terms.push(t.into());
1213                            self.stack.push(N3State::PropertyListEnd);
1214                            self.stack.push(N3State::PredicateObjectList);
1215                            self
1216                        }
1217                        Err(e) => {
1218                            self.error(errors, e)
1219                        }
1220                    }
1221                    _ => {
1222                        self.error(errors, "The '[ id' construction should be followed by an IRI")
1223                    }
1224                },
1225                N3State::CollectionBeginning => if let N3Token::Punctuation(")") = token {
1226                    self.terms.push(rdf::NIL.into());
1227                    return self;
1228                } else {
1229                    let root = BlankNode::default();
1230                    self.terms.push(root.clone().into());
1231                    self.terms.push(root.into());
1232                    self.stack.push(N3State::CollectionPossibleEnd);
1233                    self.stack.push(N3State::Path);
1234                },
1235                N3State::CollectionPossibleEnd => {
1236                    let value = self.terms.pop().unwrap();
1237                    let old = self.terms.pop().unwrap();
1238                    results.push(self.quad(
1239                        old.clone(),
1240                        rdf::FIRST,
1241                        value,
1242                    ));
1243                    if let N3Token::Punctuation(")") = token {
1244                        results.push(self.quad(
1245                            old,
1246                            rdf::REST,
1247                            rdf::NIL,
1248                        ));
1249                        return self;
1250                    }
1251                    let new = BlankNode::default();
1252                    results.push(self.quad(
1253                        old,
1254                        rdf::REST,
1255                        new.clone(),
1256                    ));
1257                    self.terms.push(new.into());
1258                    self.stack.push(N3State::CollectionPossibleEnd);
1259                    self.stack.push(N3State::Path);
1260                }
1261                N3State::LiteralPossibleSuffix { value } => {
1262                    match token {
1263                        N3Token::LangTag { language, #[cfg(feature = "rdf-12")]direction } => {
1264                            #[cfg(feature = "rdf-12")]
1265                            if direction.is_some() {
1266                                return self.error(errors, "rdf:dirLangString is not supported in N3");
1267                            }
1268                            self.terms.push(Literal::new_language_tagged_literal_unchecked(value, language.to_ascii_lowercase()).into());
1269                            return self;
1270                        }
1271                        N3Token::Punctuation("^^") => {
1272                            self.stack.push(N3State::LiteralExpectDatatype { value });
1273                            return self;
1274                        }
1275                        _ => {
1276                            self.terms.push(Literal::new_simple_literal(value).into());
1277                        }
1278                    }
1279                }
1280                N3State::LiteralExpectDatatype { value } => {
1281                    match token {
1282                        N3Token::IriRef(datatype) => {
1283                            self.terms.push(Literal::new_typed_literal(value, NamedNode::new_unchecked(datatype)).into());
1284                            return self;
1285                        }
1286                        N3Token::PrefixedName { prefix, local, might_be_invalid_iri } => match resolve_local_name(prefix, &local, might_be_invalid_iri, &context.prefixes) {
1287                            Ok(datatype) => {
1288                                self.terms.push(Literal::new_typed_literal(value, datatype).into());
1289                                return self;
1290                            }
1291                            Err(e) => {
1292                                return self.error(errors, e);
1293                            }
1294                        }
1295                        _ => {
1296                            errors.push("Expecting a datatype IRI after '^^, found TOKEN".into());
1297                            self.stack.clear();
1298                        }
1299                    }
1300                }
1301                // [24]  formulaContent  ::=  ( n3Statement ( "." formulaContent? ) ? ) | ( sparqlDirective formulaContent? )
1302                N3State::FormulaContent => {
1303                    match token {
1304                        N3Token::Punctuation("}") => {
1305                            self.terms.push(self.contexts.pop().unwrap().into());
1306                            return self;
1307                        }
1308                        N3Token::PlainKeyword(k)if k.eq_ignore_ascii_case("base") => {
1309                            self.stack.push(N3State::FormulaContent);
1310                            self.stack.push(N3State::BaseExpectIri);
1311                            return self;
1312                        }
1313                        N3Token::PlainKeyword(k)if k.eq_ignore_ascii_case("prefix") => {
1314                            self.stack.push(N3State::FormulaContent);
1315                            self.stack.push(N3State::PrefixExpectPrefix);
1316                            return self;
1317                        }
1318                        N3Token::LangTag {
1319                            language: "prefix", #[cfg(
1320                            feature = "rdf-12"
1321                        )] direction: None
1322                        } => {
1323                            self.stack.push(N3State::FormulaContentExpectDot);
1324                            self.stack.push(N3State::PrefixExpectPrefix);
1325                            return self;
1326                        }
1327                        N3Token::LangTag {
1328                            language: "base", #[cfg(
1329                            feature = "rdf-12"
1330                        )] direction: None
1331                        } => {
1332                            self.stack.push(N3State::FormulaContentExpectDot);
1333                            self.stack.push(N3State::BaseExpectIri);
1334                            return self;
1335                        }
1336                        _ => {
1337                            self.stack.push(N3State::FormulaContentExpectDot);
1338                            self.stack.push(N3State::Triples);
1339                        }
1340                    }
1341                }
1342                N3State::FormulaContentExpectDot => {
1343                    match token {
1344                        N3Token::Punctuation("}") => {
1345                            self.terms.push(self.contexts.pop().unwrap().into());
1346                            return self;
1347                        }
1348                        N3Token::Punctuation(".") => {
1349                            self.stack.push(N3State::FormulaContent);
1350                            return self;
1351                        }
1352                        _ => {
1353                            errors.push("A dot is expected at the end of N3 statements".into());
1354                            self.stack.push(N3State::FormulaContent);
1355                        }
1356                    }
1357                }
1358            }
1359        }
1360        // Empty stack
1361        if token == N3Token::Punctuation(".") {
1362            self.stack.push(N3State::N3Doc);
1363            self
1364        } else {
1365            self
1366        }
1367    }
1368
1369    fn recognize_end(
1370        self,
1371        _state: &mut N3RecognizerContext,
1372        _results: &mut Vec<Self::Output>,
1373        errors: &mut Vec<RuleRecognizerError>,
1374    ) {
1375        match &*self.stack {
1376            [] | [N3State::N3Doc] => (),
1377            _ => errors.push("Unexpected end".into()), // TODO
1378        }
1379    }
1380
1381    fn lexer_options(context: &N3RecognizerContext) -> &N3LexerOptions {
1382        &context.lexer_options
1383    }
1384}
1385
1386impl N3Recognizer {
1387    pub fn new_parser<B>(
1388        data: B,
1389        is_ending: bool,
1390        unchecked: bool,
1391        base_iri: Option<Iri<String>>,
1392        prefixes: HashMap<String, Iri<String>>,
1393    ) -> Parser<B, Self> {
1394        Parser::new(
1395            Lexer::new(
1396                N3Lexer::new(N3LexerMode::N3, unchecked),
1397                data,
1398                is_ending,
1399                MIN_BUFFER_SIZE,
1400                MAX_BUFFER_SIZE,
1401                Some(b"#"),
1402            ),
1403            Self {
1404                stack: vec![N3State::N3Doc],
1405                terms: Vec::new(),
1406                predicates: Vec::new(),
1407                contexts: Vec::new(),
1408            },
1409            N3RecognizerContext {
1410                lexer_options: N3LexerOptions { base_iri },
1411                prefixes,
1412            },
1413        )
1414    }
1415
1416    #[must_use]
1417    fn error(
1418        mut self,
1419        errors: &mut Vec<RuleRecognizerError>,
1420        msg: impl Into<RuleRecognizerError>,
1421    ) -> Self {
1422        errors.push(msg.into());
1423        self.stack.clear();
1424        self
1425    }
1426
1427    fn quad(
1428        &self,
1429        subject: impl Into<N3Term>,
1430        predicate: impl Into<N3Term>,
1431        object: impl Into<N3Term>,
1432    ) -> N3Quad {
1433        N3Quad {
1434            subject: subject.into(),
1435            predicate: predicate.into(),
1436            object: object.into(),
1437            graph_name: self
1438                .contexts
1439                .last()
1440                .map_or(GraphName::DefaultGraph, |g| g.clone().into()),
1441        }
1442    }
1443}
1444
1445#[derive(Debug)]
1446enum N3State {
1447    N3Doc,
1448    N3DocExpectDot,
1449    BaseExpectIri,
1450    PrefixExpectPrefix,
1451    PrefixExpectIri { name: String },
1452    Triples,
1453    TriplesMiddle,
1454    TriplesEnd,
1455    PredicateObjectList,
1456    PredicateObjectListEnd,
1457    PredicateObjectListPossibleContinuation,
1458    ObjectsList,
1459    ObjectsListEnd,
1460    Verb,
1461    AfterRegularVerb,
1462    AfterInvertedVerb,
1463    AfterVerbIs,
1464    Path,
1465    PathFollowUp,
1466    PathAfterIndicator { is_inverse: bool },
1467    PathItem,
1468    PropertyListMiddle,
1469    PropertyListEnd,
1470    IriPropertyList,
1471    CollectionBeginning,
1472    CollectionPossibleEnd,
1473    LiteralPossibleSuffix { value: String },
1474    LiteralExpectDatatype { value: String },
1475    FormulaContent,
1476    FormulaContentExpectDot,
1477}
1478
1479/// Iterator on the file prefixes.
1480///
1481/// See [`LowLevelN3Parser::prefixes`].
1482pub struct N3PrefixesIter<'a> {
1483    inner: Iter<'a, String, Iri<String>>,
1484}
1485
1486impl<'a> Iterator for N3PrefixesIter<'a> {
1487    type Item = (&'a str, &'a str);
1488
1489    #[inline]
1490    fn next(&mut self) -> Option<Self::Item> {
1491        let (key, value) = self.inner.next()?;
1492        Some((key.as_str(), value.as_str()))
1493    }
1494
1495    #[inline]
1496    fn size_hint(&self) -> (usize, Option<usize>) {
1497        self.inner.size_hint()
1498    }
1499}