patch_prolog_core/
database.rs1use crate::index::{build_index, lookup_clauses, PredicateIndex};
2use crate::term::{Clause, StringInterner, Term};
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct CompiledDatabase {
9 pub interner: StringInterner,
10 pub clauses: Vec<Clause>,
11 pub predicate_index: PredicateIndex,
12}
13
14impl CompiledDatabase {
15 pub fn new(mut interner: StringInterner, clauses: Vec<Clause>) -> Self {
17 interner.intern("[]");
19 interner.intern("!");
20 let predicate_index = build_index(&clauses);
21 CompiledDatabase {
22 interner,
23 clauses,
24 predicate_index,
25 }
26 }
27
28 pub fn lookup(&self, goal: &Term) -> Vec<usize> {
30 lookup_clauses(&self.predicate_index, goal, &self.clauses)
31 }
32
33 pub fn to_bytes(&self) -> Result<Vec<u8>, String> {
35 bincode::serialize(self).map_err(|e| format!("Serialization error: {}", e))
36 }
37
38 pub fn from_bytes(data: &[u8]) -> Result<Self, String> {
40 bincode::deserialize(data).map_err(|e| format!("Deserialization error: {}", e))
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47 use crate::parser::Parser;
48
49 fn build_db(source: &str) -> CompiledDatabase {
50 let mut interner = StringInterner::new();
51 let clauses = Parser::parse_program(source, &mut interner).unwrap();
52 CompiledDatabase::new(interner, clauses)
53 }
54
55 #[test]
56 fn test_roundtrip_serialization() {
57 let db = build_db("parent(tom, mary). parent(tom, james).");
58 let bytes = db.to_bytes().unwrap();
59 let restored = CompiledDatabase::from_bytes(&bytes).unwrap();
60 assert_eq!(restored.clauses.len(), 2);
61 assert_eq!(restored.interner.resolve(0), db.interner.resolve(0));
62 }
63
64 #[test]
65 fn test_lookup_indexed() {
66 let db = build_db("color(red). color(blue). color(green). shape(circle). shape(square).");
67 let color_id = db.interner.lookup("color").unwrap();
69 let goal = Term::Compound {
70 functor: color_id,
71 args: vec![Term::Var(0)],
72 };
73 let results = db.lookup(&goal);
74 assert_eq!(results.len(), 3);
75
76 let shape_id = db.interner.lookup("shape").unwrap();
78 let goal = Term::Compound {
79 functor: shape_id,
80 args: vec![Term::Var(0)],
81 };
82 let results = db.lookup(&goal);
83 assert_eq!(results.len(), 2);
84 }
85
86 #[test]
87 fn test_lookup_specific_first_arg() {
88 let db = build_db(
89 "component(engine, piston). component(engine, crankshaft). component(brake, pad).",
90 );
91 let comp_id = db.interner.lookup("component").unwrap();
92 let brake_id = db.interner.lookup("brake").unwrap();
93
94 let goal = Term::Compound {
95 functor: comp_id,
96 args: vec![Term::Atom(brake_id), Term::Var(0)],
97 };
98 let results = db.lookup(&goal);
99 assert_eq!(results.len(), 1);
100 }
101}