qualia_core_db/sparql_library/parsers/
trig_star.rs1use crate::lexicon::{generate_60bit_token, generate_embedded_triple_id};
2use crate::rdf_star::{RdfStarParseError, RdfStarParser};
3use crate::NQuin;
7use std::io::BufReader;
9
10pub struct TrigStarParser {
12 context_hash: u64,
14 current_graph: u64,
16}
17
18impl TrigStarParser {
19 pub fn new(context_hash: u64) -> Self {
21 Self {
22 context_hash,
23 current_graph: 0, }
25 }
26
27 pub fn set_current_graph(&mut self, graph_hash: u64) {
29 self.current_graph = graph_hash;
30 }
31
32 pub fn current_graph(&self) -> u64 {
34 self.current_graph
35 }
36
37 pub fn session_context(&self) -> u64 {
39 self.context_hash
40 }
41
42 fn effective_graph(&self) -> u64 {
43 if self.current_graph != 0 {
44 self.current_graph
45 } else {
46 self.context_hash
47 }
48 }
49
50 fn parse_line(&self, line: &str) -> Result<ParseResult, RdfStarParseError> {
52 let line = line.trim();
53 if line.is_empty() || line.starts_with('#') {
54 return Ok(ParseResult::Comment);
55 }
56
57 if line.starts_with("GRAPH") {
59 return self.parse_graph_block(line);
60 }
61
62 if line.contains("<<") {
64 self.parse_embedded_triple_line(line)
65 } else {
66 self.parse_triple_line(line)
67 }
68 }
69
70 fn parse_graph_block(&self, line: &str) -> Result<ParseResult, RdfStarParseError> {
72 let parts: Vec<&str> = line.split_whitespace().collect();
74 if parts.len() < 2 {
75 return Err(RdfStarParseError::InvalidSyntax);
76 }
77
78 let graph_str = parts[1].trim_start_matches('<').trim_end_matches('>');
79 let graph_hash = generate_60bit_token(graph_str.as_bytes());
80
81 Ok(ParseResult::GraphDeclaration { graph_hash })
82 }
83
84 fn parse_triple_line(&self, line: &str) -> Result<ParseResult, RdfStarParseError> {
86 let parts: Vec<&str> = line.split_whitespace().collect();
88 if parts.len() < 3 {
89 return Err(RdfStarParseError::InvalidSyntax);
90 }
91
92 let subject_str = parts[0];
93 let predicate_str = parts[1];
94 let object_str = parts[2];
95
96 let subject = subject_str.trim_start_matches('<').trim_end_matches('>');
98 let predicate = predicate_str.trim_start_matches('<').trim_end_matches('>');
99 let object = object_str
100 .trim_start_matches('<')
101 .trim_end_matches('>')
102 .trim_start_matches('"')
103 .trim_end_matches('"');
104
105 let subject_hash = generate_60bit_token(subject.as_bytes());
106 let predicate_hash = generate_60bit_token(predicate.as_bytes());
107 let object_hash = generate_60bit_token(object.as_bytes());
108
109 Ok(ParseResult::RegularTriple {
110 subject: subject_hash,
111 predicate: predicate_hash,
112 object: object_hash,
113 graph_hash: self.effective_graph(),
114 })
115 }
116
117 fn parse_embedded_triple_line(&self, line: &str) -> Result<ParseResult, RdfStarParseError> {
119 let start = line
122 .find("<<")
123 .ok_or(RdfStarParseError::MalformedEmbeddedTriple)?;
124 let end = line
125 .find(">>")
126 .ok_or(RdfStarParseError::MalformedEmbeddedTriple)?;
127
128 let embedded_part = &line[start + 2..end];
129 let embedded_parts: Vec<&str> = embedded_part.split_whitespace().collect();
130 if embedded_parts.len() < 3 {
131 return Err(RdfStarParseError::MalformedEmbeddedTriple);
132 }
133
134 let subject = embedded_parts[0]
135 .trim_start_matches('<')
136 .trim_end_matches('>');
137 let predicate = embedded_parts[1]
138 .trim_start_matches('<')
139 .trim_end_matches('>');
140 let object = embedded_parts[2]
141 .trim_start_matches('<')
142 .trim_end_matches('>')
143 .trim_start_matches('"')
144 .trim_end_matches('"');
145
146 let subject_hash = generate_60bit_token(subject.as_bytes());
147 let predicate_hash = generate_60bit_token(predicate.as_bytes());
148 let object_hash = generate_60bit_token(object.as_bytes());
149
150 let virtual_id = generate_embedded_triple_id(subject_hash, predicate_hash, object_hash);
151
152 let remaining = &line[end + 2..];
154 let outer_parts: Vec<&str> = remaining.split_whitespace().collect();
155 if outer_parts.len() < 2 {
156 return Err(RdfStarParseError::MalformedEmbeddedTriple);
157 }
158
159 let outer_predicate = outer_parts[0].trim_start_matches('<').trim_end_matches('>');
160 let outer_object = outer_parts[1]
161 .trim_start_matches('<')
162 .trim_end_matches('>')
163 .trim_start_matches('"')
164 .trim_end_matches('"');
165
166 let outer_predicate_hash = generate_60bit_token(outer_predicate.as_bytes());
167 let outer_object_hash = generate_60bit_token(outer_object.as_bytes());
168
169 Ok(ParseResult::EmbeddedTriple {
170 virtual_id,
171 components: [subject_hash, predicate_hash, object_hash],
172 outer_predicate: outer_predicate_hash,
173 outer_object: outer_object_hash,
174 graph_hash: self.effective_graph(),
175 })
176 }
177}
178
179impl RdfStarParser for TrigStarParser {
180 fn parse_embedded_triple(
181 &mut self,
182 input: &[u8],
183 ) -> Result<(u64, [u64; 3]), RdfStarParseError> {
184 let line = std::str::from_utf8(input).map_err(|_| RdfStarParseError::InvalidUtf8)?;
185
186 match self.parse_line(line)? {
187 ParseResult::EmbeddedTriple {
188 virtual_id,
189 components,
190 ..
191 } => Ok((virtual_id, components)),
192 _ => Err(RdfStarParseError::MalformedEmbeddedTriple),
193 }
194 }
195
196 fn parse_triple(&mut self, input: &[u8]) -> Result<(u64, u64, u64), RdfStarParseError> {
197 let line = std::str::from_utf8(input).map_err(|_| RdfStarParseError::InvalidUtf8)?;
198
199 match self.parse_line(line)? {
200 ParseResult::RegularTriple {
201 subject,
202 predicate,
203 object,
204 ..
205 } => Ok((subject, predicate, object)),
206 _ => Err(RdfStarParseError::InvalidSyntax),
207 }
208 }
209
210 fn parse_quad(&mut self, input: &[u8]) -> Result<(u64, u64, u64, u64), RdfStarParseError> {
211 let line = std::str::from_utf8(input).map_err(|_| RdfStarParseError::InvalidUtf8)?;
212
213 match self.parse_line(line)? {
214 ParseResult::RegularTriple {
215 subject,
216 predicate,
217 object,
218 graph_hash,
219 } => Ok((subject, predicate, object, graph_hash)),
220 ParseResult::EmbeddedTriple {
221 outer_predicate,
222 outer_object,
223 graph_hash,
224 ..
225 } => Ok((0, outer_predicate, outer_object, graph_hash)),
226 _ => Err(RdfStarParseError::InvalidSyntax),
227 }
228 }
229
230 fn supports_quads(&self) -> bool {
231 true
232 }
233
234 fn supports_named_graphs(&self) -> bool {
235 true
236 }
237
238 fn format_name(&self) -> &'static str {
239 "Trig-Star"
240 }
241}
242
243enum ParseResult {
245 Comment,
246 GraphDeclaration {
247 graph_hash: u64,
248 },
249 RegularTriple {
250 subject: u64,
251 predicate: u64,
252 object: u64,
253 graph_hash: u64,
254 },
255 EmbeddedTriple {
256 virtual_id: u64,
257 components: [u64; 3],
258 outer_predicate: u64,
259 outer_object: u64,
260 graph_hash: u64,
261 },
262}
263
264pub fn parse_trig_star_into<R: std::io::Read, S: crate::sparql_library::quin_sink::QuinSink>(
266 reader: R,
267 context_hash: u64,
268 sink: &mut S,
269) -> Result<u64, Box<dyn std::error::Error>> {
270 use std::io::BufRead;
271
272 let mut parser = TrigStarParser::new(context_hash);
273 let mut count = 0;
274 let buf_reader = BufReader::new(reader);
275
276 for line in buf_reader.lines() {
277 let line = line?;
278 match parser.parse_line(&line)? {
279 ParseResult::Comment => continue,
280 ParseResult::GraphDeclaration { graph_hash } => {
281 parser.set_current_graph(graph_hash);
282 }
283 ParseResult::RegularTriple {
284 subject,
285 predicate,
286 object,
287 graph_hash,
288 } => {
289 sink.push(NQuin {
290 subject,
291 predicate,
292 object,
293 context: graph_hash,
294 metadata: 0b10 << 61,
295 parity: 0,
296 })?;
297 count += 1;
298 }
299 ParseResult::EmbeddedTriple {
300 virtual_id,
301 components,
302 outer_predicate,
303 outer_object,
304 graph_hash,
305 } => {
306 sink.push(NQuin {
307 subject: virtual_id,
308 predicate: outer_predicate,
309 object: outer_object,
310 context: graph_hash,
311 metadata: 0b10 << 61,
312 parity: 0,
313 })?;
314 count += 1;
315
316 sink.push(NQuin {
317 subject: components[0],
318 predicate: components[1],
319 object: components[2],
320 context: graph_hash,
321 metadata: 0b10 << 61,
322 parity: 0,
323 })?;
324 count += 1;
325 }
326 }
327 }
328
329 Ok(count)
330}
331
332pub fn parse_trig_star_stream<R: std::io::Read>(
333 reader: R,
334 context_hash: u64,
335 sorter: &mut crate::external_sort::ExternalSorter,
336) -> Result<u64, Box<dyn std::error::Error>> {
337 parse_trig_star_into(reader, context_hash, sorter)
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343 use crate::rdf_star::RdfStarParser;
344
345 #[test]
346 fn test_trig_star_parser_creation() {
347 let parser = TrigStarParser::new(0);
348 assert_eq!(parser.format_name(), "Trig-Star");
349 assert!(parser.supports_quads());
350 assert!(parser.supports_named_graphs());
351 }
352
353 #[test]
354 fn test_set_current_graph() {
355 let mut parser = TrigStarParser::new(0);
356 parser.set_current_graph(123);
357 assert_eq!(parser.current_graph(), 123);
358 }
359
360 #[test]
361 fn test_parse_regular_triple() {
362 let mut parser = TrigStarParser::new(0);
363 let input =
364 b"<http://example.org/Alice> <http://example.org/knows> <http://example.org/Bob> .";
365 let result = parser.parse_triple(input);
366 assert!(result.is_ok());
367 }
368
369 #[test]
370 fn test_parse_embedded_triple() {
371 let mut parser = TrigStarParser::new(0);
372 let input = b"<<http://example.org/Alice http://example.org/knows http://example.org/Bob>> http://example.org/saidBy http://example.org/Charlie .";
373 let result = parser.parse_embedded_triple(input);
374 assert!(result.is_ok());
375 }
376}