oxirs_ttl/toolkit/
parser.rs1use crate::error::{RuleRecognizerError, TurtleParseError, TurtleResult};
7use crate::toolkit::lexer::{TokenOrLineJump, TokenRecognizer};
8use std::io::{BufRead, Read};
10use std::marker::PhantomData;
11
12pub trait RuleRecognizer {
14 type TokenRecognizer: TokenRecognizer;
16
17 type Output;
19
20 type Context;
22
23 fn recognize_next(
25 self,
26 token: TokenOrLineJump<<Self::TokenRecognizer as TokenRecognizer>::Token<'_>>,
27 context: &mut Self::Context,
28 results: &mut Vec<Self::Output>,
29 errors: &mut Vec<RuleRecognizerError>,
30 ) -> Self;
31}
32
33pub struct StreamingParser<R, T: crate::toolkit::lexer::TokenRecognizer, P: RuleRecognizer> {
35 tokenizer: crate::toolkit::lexer::StreamingTokenizer<R, T>,
36 rule_recognizer: P,
37 context: P::Context,
38 _phantom: PhantomData<P>,
39}
40
41impl<R: BufRead, T: TokenRecognizer, P: RuleRecognizer<TokenRecognizer = T>>
42 StreamingParser<R, T, P>
43{
44 pub fn new(
46 tokenizer: crate::toolkit::lexer::StreamingTokenizer<R, T>,
47 rule_recognizer: P,
48 context: P::Context,
49 ) -> Self {
50 Self {
51 tokenizer,
52 rule_recognizer,
53 context,
54 _phantom: PhantomData,
55 }
56 }
57}
58
59impl<R: BufRead, T: TokenRecognizer, P: RuleRecognizer<TokenRecognizer = T>> Iterator
60 for StreamingParser<R, T, P>
61where
62 P: Clone,
63{
64 type Item = TurtleResult<P::Output>;
65
66 fn next(&mut self) -> Option<Self::Item> {
67 loop {
68 match self.tokenizer.next() {
69 None => return None, Some(Err(e)) => {
71 return Some(Err(TurtleParseError::syntax(
72 crate::error::TurtleSyntaxError::Generic {
73 message: e.to_string(),
74 position: self.tokenizer.position(),
75 },
76 )))
77 }
78 Some(Ok(token)) => {
79 let mut results = Vec::new();
80 let mut errors = Vec::new();
81
82 self.rule_recognizer = self.rule_recognizer.clone().recognize_next(
83 token,
84 &mut self.context,
85 &mut results,
86 &mut errors,
87 );
88
89 if !errors.is_empty() {
91 return Some(Err(TurtleParseError::syntax(
92 crate::error::TurtleSyntaxError::Generic {
93 message: format!("Rule recognition error: {:?}", errors[0]),
94 position: self.tokenizer.position(),
95 },
96 )));
97 }
98
99 if let Some(result) = results.into_iter().next() {
101 return Some(Ok(result));
102 }
103
104 }
106 }
107 }
108 }
109}
110
111pub trait Parser<Output> {
113 fn parse<R: Read>(&self, reader: R) -> TurtleResult<Vec<Output>>;
115
116 fn for_reader<R: BufRead + 'static>(
118 &self,
119 reader: R,
120 ) -> Box<dyn Iterator<Item = TurtleResult<Output>>>;
121}
122
123#[cfg(feature = "async-tokio")]
125pub trait AsyncParser<Output> {
126 fn parse_async<R: tokio::io::AsyncRead + Unpin>(
128 &self,
129 reader: R,
130 ) -> impl std::future::Future<Output = TurtleResult<Vec<Output>>> + Send;
131
132 fn for_async_reader<R: tokio::io::AsyncBufRead + Unpin>(
134 &self,
135 reader: R,
136 ) -> Box<dyn futures::Stream<Item = TurtleResult<Output>> + Unpin>;
137}
138
139#[derive(Debug, Clone, Default)]
141pub struct ParsingContext {
142 pub base_iri: Option<String>,
144 pub prefixes: std::collections::HashMap<String, String>,
146 pub blank_node_counter: usize,
148}
149
150impl ParsingContext {
151 pub fn new() -> Self {
153 Self::default()
154 }
155
156 pub fn with_base_iri(mut self, base_iri: String) -> Self {
158 self.base_iri = Some(base_iri);
159 self
160 }
161
162 pub fn add_prefix(&mut self, prefix: String, iri: String) {
164 self.prefixes.insert(prefix, iri);
165 }
166
167 pub fn resolve_prefixed_name(&self, prefix: &str, local: &str) -> Option<String> {
169 self.prefixes.get(prefix).map(|iri| format!("{iri}{local}"))
170 }
171
172 pub fn generate_blank_node_id(&mut self) -> String {
174 let id = format!("_:b{}", self.blank_node_counter);
175 self.blank_node_counter += 1;
176 id
177 }
178
179 pub fn resolve_iri(&self, iri: &str) -> String {
181 if let Some(ref base) = self.base_iri {
182 if iri.starts_with('#') || iri.starts_with('/') {
184 format!("{base}{iri}")
185 } else {
186 iri.to_string()
187 }
188 } else {
189 iri.to_string()
190 }
191 }
192}