lemma/
engine.rs

1use crate::evaluator::Evaluator;
2use crate::{parse, LemmaResult, Response, Validator};
3use std::collections::HashMap;
4
5/// The Lemma evaluation engine.
6///
7/// Pure Rust implementation that evaluates Lemma documents directly from the AST.
8pub struct Engine {
9    documents: HashMap<String, crate::LemmaDoc>,
10    sources: HashMap<String, String>,
11    validator: Validator,
12    evaluator: Evaluator,
13}
14
15impl Default for Engine {
16    fn default() -> Self {
17        Self::new()
18    }
19}
20
21impl Engine {
22    pub fn new() -> Self {
23        Self {
24            documents: HashMap::new(),
25            sources: HashMap::new(),
26            validator: Validator::new(),
27            evaluator: Evaluator::new(),
28        }
29    }
30
31    pub fn add_lemma_code(&mut self, lemma_code: &str, source: &str) -> LemmaResult<()> {
32        // Parse the documents
33        let new_docs = parse(lemma_code, Some(source.to_string()))?;
34
35        // Store source text for all new documents
36        for doc in &new_docs {
37            let source_id = doc.source.clone().unwrap_or_else(|| "<input>".to_string());
38            self.sources.insert(source_id, lemma_code.to_string());
39        }
40
41        // Combine existing documents with new documents for semantic validation
42        let mut all_docs: Vec<crate::LemmaDoc> = self.documents.values().cloned().collect();
43        all_docs.extend(new_docs);
44
45        // Run semantic validation on all documents
46        let validated = self.validator.validate_all(all_docs)?;
47
48        // Store the validated documents
49        for doc in validated.documents {
50            self.documents.insert(doc.name.clone(), doc);
51        }
52
53        Ok(())
54    }
55
56    pub fn remove_document(&mut self, doc_name: &str) {
57        self.documents.remove(doc_name);
58    }
59
60    pub fn list_documents(&self) -> Vec<String> {
61        self.documents.keys().cloned().collect()
62    }
63
64    pub fn get_document(&self, doc_name: &str) -> Option<&crate::LemmaDoc> {
65        self.documents.get(doc_name)
66    }
67
68    pub fn get_document_facts(&self, doc_name: &str) -> Vec<&crate::LemmaFact> {
69        if let Some(doc) = self.documents.get(doc_name) {
70            doc.facts.iter().collect()
71        } else {
72            Vec::new()
73        }
74    }
75
76    pub fn get_document_rules(&self, doc_name: &str) -> Vec<&crate::LemmaRule> {
77        if let Some(doc) = self.documents.get(doc_name) {
78            doc.rules.iter().collect()
79        } else {
80            Vec::new()
81        }
82    }
83
84    /// Evaluate rules in a document with optional fact overrides
85    ///
86    /// If `rule_names` is None, evaluates all rules.
87    /// If `rule_names` is Some, only returns results for the specified rules,
88    /// but still computes their dependencies.
89    ///
90    /// Fact overrides must be pre-parsed using `parse_facts()`.
91    pub fn evaluate(
92        &self,
93        doc_name: &str,
94        rule_names: Option<Vec<String>>,
95        fact_overrides: Option<Vec<crate::LemmaFact>>,
96    ) -> LemmaResult<Response> {
97        let overrides = fact_overrides.unwrap_or_default();
98        self.evaluator.evaluate_document(
99            doc_name,
100            &self.documents,
101            &self.sources,
102            overrides,
103            rule_names,
104        )
105    }
106
107    /// Get all documents (needed by serializers for schema resolution)
108    pub fn get_all_documents(&self) -> &HashMap<String, crate::LemmaDoc> {
109        &self.documents
110    }
111}