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}