qualia_core_db/sparql_library/parsers/
n3_star.rs1use crate::lexicon::{generate_60bit_token, generate_embedded_triple_id};
7use crate::rdf_star::{RdfStarParseError, RdfStarParser};
8use crate::{q_hash, NQuin};
9
10pub struct N3StarParser {
12 context_hash: u64,
14 variables: std::collections::HashMap<String, u64>,
16}
17
18impl N3StarParser {
19 pub fn new(context_hash: u64) -> Self {
21 Self {
22 context_hash,
23 variables: std::collections::HashMap::new(),
24 }
25 }
26
27 pub fn bind_variable(&mut self, var_name: &str, value: u64) {
29 self.variables.insert(var_name.to_string(), value);
30 }
31
32 pub fn get_variable(&self, var_name: &str) -> Option<u64> {
34 self.variables.get(var_name).copied()
35 }
36
37 fn parse_line(&self, line: &str) -> Result<ParseResult, RdfStarParseError> {
39 let line = line.trim();
40 if line.is_empty() || line.starts_with('#') {
41 return Ok(ParseResult::Comment);
42 }
43
44 if line.contains("=>") || line.contains("~>") || line.contains("^>") || line.contains("-o")
46 {
47 self.parse_rule(line)
48 } else if line.contains("{") {
49 self.parse_formula(line)
50 } else if line.contains("<<") {
51 self.parse_embedded_triple_line(line)
52 } else {
53 self.parse_triple_line(line)
54 }
55 }
56
57 fn parse_rule(&self, line: &str) -> Result<ParseResult, RdfStarParseError> {
59 let rule_type = if line.contains("~>") {
62 RuleType::Defeasible
63 } else if line.contains("^>") {
64 RuleType::Defeater
65 } else if line.contains("-o") {
66 RuleType::Linear
67 } else {
68 RuleType::Strict
69 };
70
71 Ok(ParseResult::Rule { rule_type })
72 }
73
74 fn parse_formula(&self, _line: &str) -> Result<ParseResult, RdfStarParseError> {
76 Ok(ParseResult::Formula)
79 }
80
81 fn parse_triple_line(&self, line: &str) -> Result<ParseResult, RdfStarParseError> {
83 let parts: Vec<&str> = line.split_whitespace().collect();
84 if parts.len() < 3 {
85 return Err(RdfStarParseError::InvalidSyntax);
86 }
87
88 let subject = self.resolve_term(parts[0])?;
89 let predicate = self.resolve_term(parts[1])?;
90 let object = self.resolve_term(parts[2])?;
91
92 Ok(ParseResult::RegularTriple {
93 subject,
94 predicate,
95 object,
96 graph_hash: self.context_hash,
97 })
98 }
99
100 fn parse_embedded_triple_line(&self, line: &str) -> Result<ParseResult, RdfStarParseError> {
102 let start = line
103 .find("<<")
104 .ok_or(RdfStarParseError::MalformedEmbeddedTriple)?;
105 let end = line
106 .find(">>")
107 .ok_or(RdfStarParseError::MalformedEmbeddedTriple)?;
108
109 let embedded_part = &line[start + 2..end];
110 let embedded_parts: Vec<&str> = embedded_part.split_whitespace().collect();
111 if embedded_parts.len() < 3 {
112 return Err(RdfStarParseError::MalformedEmbeddedTriple);
113 }
114
115 let subject = self.resolve_term(embedded_parts[0])?;
116 let predicate = self.resolve_term(embedded_parts[1])?;
117 let object = self.resolve_term(embedded_parts[2])?;
118
119 let virtual_id = generate_embedded_triple_id(subject, predicate, object);
120
121 let remaining = &line[end + 2..];
122 let outer_parts: Vec<&str> = remaining.split_whitespace().collect();
123 if outer_parts.len() < 2 {
124 return Err(RdfStarParseError::MalformedEmbeddedTriple);
125 }
126
127 let outer_predicate = self.resolve_term(outer_parts[0])?;
128 let outer_object = self.resolve_term(outer_parts[1])?;
129
130 Ok(ParseResult::EmbeddedTriple {
131 virtual_id,
132 components: [subject, predicate, object],
133 outer_predicate,
134 outer_object,
135 graph_hash: self.context_hash,
136 })
137 }
138
139 fn resolve_term(&self, term: &str) -> Result<u64, RdfStarParseError> {
141 if term.starts_with('?') {
143 if let Some(value) = self.get_variable(term) {
144 return Ok(value);
145 }
146 return Ok(generate_60bit_token(term.as_bytes()));
148 }
149
150 let term = term
152 .trim_start_matches('<')
153 .trim_end_matches('>')
154 .trim_start_matches('"')
155 .trim_end_matches('"');
156
157 Ok(generate_60bit_token(term.as_bytes()))
158 }
159}
160
161impl RdfStarParser for N3StarParser {
162 fn parse_embedded_triple(
163 &mut self,
164 input: &[u8],
165 ) -> Result<(u64, [u64; 3]), RdfStarParseError> {
166 let line = std::str::from_utf8(input).map_err(|_| RdfStarParseError::InvalidUtf8)?;
167
168 match self.parse_line(line)? {
169 ParseResult::EmbeddedTriple {
170 virtual_id,
171 components,
172 ..
173 } => Ok((virtual_id, components)),
174 _ => Err(RdfStarParseError::MalformedEmbeddedTriple),
175 }
176 }
177
178 fn parse_triple(&mut self, input: &[u8]) -> Result<(u64, u64, u64), RdfStarParseError> {
179 let line = std::str::from_utf8(input).map_err(|_| RdfStarParseError::InvalidUtf8)?;
180
181 match self.parse_line(line)? {
182 ParseResult::RegularTriple {
183 subject,
184 predicate,
185 object,
186 ..
187 } => Ok((subject, predicate, object)),
188 _ => Err(RdfStarParseError::InvalidSyntax),
189 }
190 }
191
192 fn parse_quad(&mut self, input: &[u8]) -> Result<(u64, u64, u64, u64), RdfStarParseError> {
193 let line = std::str::from_utf8(input).map_err(|_| RdfStarParseError::InvalidUtf8)?;
194
195 match self.parse_line(line)? {
196 ParseResult::RegularTriple {
197 subject,
198 predicate,
199 object,
200 graph_hash,
201 } => Ok((subject, predicate, object, graph_hash)),
202 ParseResult::EmbeddedTriple {
203 outer_predicate,
204 outer_object,
205 graph_hash,
206 ..
207 } => Ok((0, outer_predicate, outer_object, graph_hash)),
208 _ => Err(RdfStarParseError::InvalidSyntax),
209 }
210 }
211
212 fn supports_quads(&self) -> bool {
213 true
214 }
215
216 fn supports_named_graphs(&self) -> bool {
217 false }
219
220 fn format_name(&self) -> &'static str {
221 "N3-Star"
222 }
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq)]
227pub enum RuleType {
228 Strict,
230 Defeasible,
232 Defeater,
234 Linear,
236}
237
238enum ParseResult {
240 Comment,
241 Formula,
242 Rule {
243 #[allow(dead_code)]
244 rule_type: RuleType,
245 },
246 RegularTriple {
247 subject: u64,
248 predicate: u64,
249 object: u64,
250 graph_hash: u64,
251 },
252 EmbeddedTriple {
253 virtual_id: u64,
254 components: [u64; 3],
255 outer_predicate: u64,
256 outer_object: u64,
257 graph_hash: u64,
258 },
259}
260
261pub fn parse_n3_star_into<R: std::io::Read, S: crate::sparql_library::quin_sink::QuinSink>(
265 reader: R,
266 context_hash: u64,
267 sink: &mut S,
268) -> Result<u64, Box<dyn std::error::Error>> {
269 use std::io::BufRead;
270
271 let parser = N3StarParser::new(context_hash);
272 let mut count = 0u64;
273 let buf_reader = std::io::BufReader::new(reader);
274
275 for line in buf_reader.lines() {
276 let line = line?;
277 match parser.parse_line(&line)? {
278 ParseResult::Comment | ParseResult::Formula => continue,
279 ParseResult::Rule { rule_type } => {
280 let pred = q_hash("q42:n3RuleType");
281 let tag = rule_type as u64;
282 sink.push(NQuin {
283 subject: context_hash,
284 predicate: pred,
285 object: tag,
286 context: context_hash,
287 metadata: 0,
288 parity: context_hash ^ pred ^ tag ^ context_hash,
289 })?;
290 count += 1;
291 }
292 ParseResult::RegularTriple {
293 subject,
294 predicate,
295 object,
296 ..
297 } => {
298 sink.push(NQuin {
299 subject,
300 predicate,
301 object,
302 context: context_hash,
303 metadata: 0,
304 parity: subject ^ predicate ^ object ^ context_hash,
305 })?;
306 count += 1;
307 }
308 ParseResult::EmbeddedTriple {
309 virtual_id,
310 components,
311 outer_predicate,
312 outer_object,
313 ..
314 } => {
315 sink.push(NQuin {
317 subject: virtual_id,
318 predicate: components[1],
319 object: components[2],
320 context: context_hash,
321 metadata: 0b10 << 61,
322 parity: virtual_id ^ components[1] ^ components[2] ^ context_hash,
323 })?;
324 sink.push(NQuin {
326 subject: components[0],
327 predicate: outer_predicate,
328 object: outer_object,
329 context: context_hash,
330 metadata: 0b10 << 61,
331 parity: components[0] ^ outer_predicate ^ outer_object ^ context_hash,
332 })?;
333 count += 2;
334 }
335 }
336 }
337
338 Ok(count)
339}
340
341pub fn parse_n3_star_stream<R: std::io::Read>(
342 reader: R,
343 context_hash: u64,
344 sorter: &mut crate::external_sort::ExternalSorter,
345) -> Result<u64, Box<dyn std::error::Error>> {
346 parse_n3_star_into(reader, context_hash, sorter)
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352 use crate::rdf_star::RdfStarParser;
353
354 #[test]
355 fn test_n3_star_parser_creation() {
356 let parser = N3StarParser::new(0);
357 assert_eq!(parser.format_name(), "N3-Star");
358 assert!(parser.supports_quads());
359 assert!(!parser.supports_named_graphs());
360 }
361
362 #[test]
363 fn test_variable_binding() {
364 let mut parser = N3StarParser::new(0);
365 parser.bind_variable("?x", 123);
366 assert_eq!(parser.get_variable("?x"), Some(123));
367 }
368
369 #[test]
370 fn test_n3_star_parse_triple() {
371 let mut parser = N3StarParser::new(0);
372 let input = b"<http://example.org/s> <http://example.org/p> <http://example.org/o> .";
373 let result = parser.parse_triple(input);
374 assert!(result.is_ok());
375 }
376
377 #[test]
378 fn test_n3_star_parse_embedded() {
379 let mut parser = N3StarParser::new(0);
380 let input = b"<<http://example.org/Alice http://example.org/knows http://example.org/Bob>> http://example.org/saidBy http://example.org/Charlie .";
381 let result = parser.parse_embedded_triple(input);
382 assert!(result.is_ok());
383 }
384
385 #[test]
386 fn test_parse_regular_triple() {
387 let mut parser = N3StarParser::new(0);
388 let input =
389 b"<http://example.org/Alice> <http://example.org/knows> <http://example.org/Bob> .";
390 let result = parser.parse_triple(input);
391 assert!(result.is_ok());
392 }
393
394 #[test]
395 fn test_parse_rule() {
396 let parser = N3StarParser::new(0);
397 let input = b"{ ?x a :Person } => { ?x :hasName ?name } .";
398 let result = parser.parse_line(std::str::from_utf8(input).unwrap());
399 assert!(result.is_ok());
400 }
401}