Skip to main content

qualia_core_db/sparql_library/parsers/
chk_parser.rs

1use crate::NQuin;
2use std::hash::{Hash, Hasher};
3use std::io::{BufRead, BufReader, Read};
4
5fn hash_str(s: &str) -> u64 {
6    let mut hasher = std::collections::hash_map::DefaultHasher::new();
7    s.hash(&mut hasher);
8    hasher.finish()
9}
10
11/// Parses the W3C Cognitive AI Community Group `.chk` (Chunks and Rules) format.
12/// Emits 48-byte Quins via the external sorter, completely avoiding the heap.
13pub fn parse_chk_stream<R: Read>(
14    reader: R,
15    context_hash: u64,
16    sorter: &mut crate::external_sort::ExternalSorter,
17) -> Result<u64, Box<dyn std::error::Error>> {
18    let mut count = 0;
19    let buf_reader = BufReader::new(reader);
20
21    // .chk format relies on defining "chunks" with properties like:
22    // chunk_id {
23    //   @condition: ...
24    //   @action: ...
25    //   weight: 0.9
26    // }
27
28    let mut current_subject = 0;
29    let mut current_weight: f32 = 0.0;
30
31    let mut in_chunk = false;
32
33    for line_result in buf_reader.lines() {
34        let line = line_result?;
35        let l = line.trim();
36
37        if l.is_empty() || l.starts_with('#') || l.starts_with("//") {
38            continue;
39        }
40
41        if l.ends_with('{') {
42            // e.g. "my_chunk {"
43            let id_str = l.trim_end_matches('{').trim();
44            current_subject = hash_str(id_str);
45            current_weight = 0.0;
46            in_chunk = true;
47            continue;
48        }
49
50        if l == "}" {
51            in_chunk = false;
52            current_subject = 0;
53            continue;
54        }
55
56        if in_chunk && l.contains(':') {
57            // Split into key and value
58            let mut parts = l.splitn(2, ':');
59            if let (Some(key), Some(val)) = (parts.next(), parts.next()) {
60                let k = key.trim();
61                let v = val.trim().trim_end_matches(';'); // handles optional trailing semicolons
62
63                if k == "weight" || k == "activation" || k == "decay" {
64                    // Update weight memory - will be packed into top 32 bits of metadata
65                    if let Ok(w) = v.parse::<f32>() {
66                        current_weight = w;
67                    }
68                } else {
69                    // It's a standard property (@condition, @action, or custom)
70                    let predicate = hash_str(k);
71                    let object = hash_str(v);
72
73                    // Pack the 32-bit weight float into the upper 32 bits of Metadata
74                    let weight_bits = current_weight.to_bits() as u64;
75                    // Lower 32 bits can hold the lamport clock, set to 0 here.
76                    let metadata = weight_bits << 32;
77
78                    sorter.push(NQuin {
79                        subject: current_subject,
80                        predicate,
81                        object,
82                        context: context_hash,
83                        metadata, // Pack statistical weight here
84                        parity: 0,
85                    })?;
86                    count += 1;
87                }
88            }
89        } else if l.contains("=>") {
90            // Inline rule: A => B
91            let mut parts = l.splitn(2, "=>");
92            if let (Some(ante), Some(cons)) = (parts.next(), parts.next()) {
93                let a = ante.trim();
94                let c = cons.trim();
95
96                let subject = hash_str(a);
97                let predicate = hash_str("=>");
98                let object = hash_str(c);
99
100                sorter.push(NQuin {
101                    subject,
102                    predicate,
103                    object,
104                    context: context_hash,
105                    metadata: 0,
106                    parity: 0,
107                })?;
108                count += 1;
109            }
110        }
111    }
112
113    Ok(count)
114}