1use anyhow::Result;
7use std::collections::HashMap;
8
9use crate::{DomainHierarchy, DomainInfo, PredicateConstraints, PredicateInfo, SymbolTable};
10
11pub struct CompilerExport;
13
14impl CompilerExport {
15 pub fn export_domains(table: &SymbolTable) -> HashMap<String, usize> {
34 table
35 .domains
36 .iter()
37 .map(|(name, info)| (name.clone(), info.cardinality))
38 .collect()
39 }
40
41 pub fn export_predicate_signatures(table: &SymbolTable) -> HashMap<String, Vec<String>> {
61 table
62 .predicates
63 .iter()
64 .map(|(name, info)| (name.clone(), info.arg_domains.clone()))
65 .collect()
66 }
67
68 pub fn export_variable_bindings(table: &SymbolTable) -> HashMap<String, String> {
87 table
88 .variables
89 .iter()
90 .map(|(var, domain)| (var.clone(), domain.clone()))
91 .collect()
92 }
93
94 pub fn export_all(table: &SymbolTable) -> CompilerExportBundle {
98 CompilerExportBundle {
99 domains: Self::export_domains(table),
100 predicate_signatures: Self::export_predicate_signatures(table),
101 variable_bindings: Self::export_variable_bindings(table),
102 }
103 }
104}
105
106#[derive(Clone, Debug)]
111pub struct CompilerExportBundle {
112 pub domains: HashMap<String, usize>,
114 pub predicate_signatures: HashMap<String, Vec<String>>,
116 pub variable_bindings: HashMap<String, String>,
118}
119
120impl CompilerExportBundle {
121 pub fn new() -> Self {
123 Self {
124 domains: HashMap::new(),
125 predicate_signatures: HashMap::new(),
126 variable_bindings: HashMap::new(),
127 }
128 }
129
130 pub fn is_empty(&self) -> bool {
132 self.domains.is_empty()
133 && self.predicate_signatures.is_empty()
134 && self.variable_bindings.is_empty()
135 }
136}
137
138impl Default for CompilerExportBundle {
139 fn default() -> Self {
140 Self::new()
141 }
142}
143
144pub struct CompilerImport;
146
147impl CompilerImport {
148 pub fn import_domains(table: &mut SymbolTable, domains: &HashMap<String, usize>) -> Result<()> {
169 for (name, cardinality) in domains {
170 table.add_domain(DomainInfo::new(name.clone(), *cardinality))?;
171 }
172 Ok(())
173 }
174
175 pub fn import_predicates(
194 table: &mut SymbolTable,
195 signatures: &HashMap<String, Vec<String>>,
196 ) -> Result<()> {
197 for (name, arg_domains) in signatures {
198 table.add_predicate(PredicateInfo::new(name.clone(), arg_domains.clone()))?;
199 }
200 Ok(())
201 }
202
203 pub fn import_variables(
224 table: &mut SymbolTable,
225 bindings: &HashMap<String, String>,
226 ) -> Result<()> {
227 for (var, domain) in bindings {
228 table.bind_variable(var, domain)?;
229 }
230 Ok(())
231 }
232
233 pub fn import_all(table: &mut SymbolTable, bundle: &CompilerExportBundle) -> Result<()> {
235 Self::import_domains(table, &bundle.domains)?;
236 Self::import_predicates(table, &bundle.predicate_signatures)?;
237 Self::import_variables(table, &bundle.variable_bindings)?;
238 Ok(())
239 }
240}
241
242pub struct SymbolTableSync;
244
245impl SymbolTableSync {
246 pub fn sync_with_compiler(
253 table: &mut SymbolTable,
254 compiler_bundle: &CompilerExportBundle,
255 ) -> Result<CompilerExportBundle> {
256 CompilerImport::import_all(table, compiler_bundle)?;
258
259 Ok(CompilerExport::export_all(table))
261 }
262
263 pub fn validate_bundle(
267 table: &SymbolTable,
268 bundle: &CompilerExportBundle,
269 ) -> Result<ValidationResult> {
270 let mut errors = Vec::new();
271 let mut warnings = Vec::new();
272
273 for (pred_name, arg_domains) in &bundle.predicate_signatures {
275 for domain in arg_domains {
276 if !table.domains.contains_key(domain) && !bundle.domains.contains_key(domain) {
277 errors.push(format!(
278 "Predicate '{}' references unknown domain '{}'",
279 pred_name, domain
280 ));
281 }
282 }
283 }
284
285 for (var_name, domain) in &bundle.variable_bindings {
287 if !table.domains.contains_key(domain) && !bundle.domains.contains_key(domain) {
288 errors.push(format!(
289 "Variable '{}' references unknown domain '{}'",
290 var_name, domain
291 ));
292 }
293 }
294
295 for domain_name in bundle.domains.keys() {
297 let used_in_predicates = bundle
298 .predicate_signatures
299 .values()
300 .any(|args| args.contains(domain_name));
301 let used_in_variables = bundle.variable_bindings.values().any(|d| d == domain_name);
302
303 if !used_in_predicates && !used_in_variables {
304 warnings.push(format!(
305 "Domain '{}' is defined but never used",
306 domain_name
307 ));
308 }
309 }
310
311 Ok(ValidationResult { errors, warnings })
312 }
313}
314
315#[derive(Clone, Debug)]
317pub struct ValidationResult {
318 pub errors: Vec<String>,
320 pub warnings: Vec<String>,
322}
323
324impl ValidationResult {
325 pub fn is_valid(&self) -> bool {
327 self.errors.is_empty()
328 }
329}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334
335 #[test]
336 fn test_export_domains() {
337 let mut table = SymbolTable::new();
338 table
339 .add_domain(DomainInfo::new("Person", 100))
340 .expect("unwrap");
341 table
342 .add_domain(DomainInfo::new("Location", 50))
343 .expect("unwrap");
344
345 let domains = CompilerExport::export_domains(&table);
346 assert_eq!(domains.get("Person"), Some(&100));
347 assert_eq!(domains.get("Location"), Some(&50));
348 }
349
350 #[test]
351 fn test_export_predicate_signatures() {
352 let mut table = SymbolTable::new();
353 table
354 .add_domain(DomainInfo::new("Person", 100))
355 .expect("unwrap");
356 table
357 .add_predicate(PredicateInfo::new(
358 "knows",
359 vec!["Person".to_string(), "Person".to_string()],
360 ))
361 .expect("unwrap");
362
363 let signatures = CompilerExport::export_predicate_signatures(&table);
364 assert_eq!(
365 signatures.get("knows"),
366 Some(&vec!["Person".to_string(), "Person".to_string()])
367 );
368 }
369
370 #[test]
371 fn test_export_all() {
372 let mut table = SymbolTable::new();
373 table
374 .add_domain(DomainInfo::new("Person", 100))
375 .expect("unwrap");
376 table
377 .add_predicate(PredicateInfo::new("knows", vec!["Person".to_string()]))
378 .expect("unwrap");
379 table.bind_variable("x", "Person").expect("unwrap");
380
381 let bundle = CompilerExport::export_all(&table);
382 assert_eq!(bundle.domains.len(), 1);
383 assert_eq!(bundle.predicate_signatures.len(), 1);
384 assert_eq!(bundle.variable_bindings.len(), 1);
385 }
386
387 #[test]
388 fn test_import_domains() {
389 let mut domains = HashMap::new();
390 domains.insert("Person".to_string(), 100);
391
392 let mut table = SymbolTable::new();
393 CompilerImport::import_domains(&mut table, &domains).expect("unwrap");
394
395 assert!(table.get_domain("Person").is_some());
396 }
397
398 #[test]
399 fn test_import_predicates() {
400 let mut table = SymbolTable::new();
401 table
402 .add_domain(DomainInfo::new("Person", 100))
403 .expect("unwrap");
404
405 let mut signatures = HashMap::new();
406 signatures.insert("knows".to_string(), vec!["Person".to_string()]);
407
408 CompilerImport::import_predicates(&mut table, &signatures).expect("unwrap");
409 assert!(table.get_predicate("knows").is_some());
410 }
411
412 #[test]
413 fn test_validation_invalid_domain_reference() {
414 let table = SymbolTable::new();
415 let mut bundle = CompilerExportBundle::new();
416 bundle
417 .predicate_signatures
418 .insert("knows".to_string(), vec!["UnknownDomain".to_string()]);
419
420 let result = SymbolTableSync::validate_bundle(&table, &bundle).expect("unwrap");
421 assert!(!result.is_valid());
422 assert!(!result.errors.is_empty());
423 }
424
425 #[test]
426 fn test_validation_unused_domain_warning() {
427 let table = SymbolTable::new();
428 let mut bundle = CompilerExportBundle::new();
429 bundle.domains.insert("UnusedDomain".to_string(), 100);
430
431 let result = SymbolTableSync::validate_bundle(&table, &bundle).expect("unwrap");
432 assert!(result.is_valid()); assert!(!result.warnings.is_empty());
434 }
435
436 #[test]
437 fn test_sync_with_compiler() {
438 let mut table = SymbolTable::new();
439 table
440 .add_domain(DomainInfo::new("Person", 100))
441 .expect("unwrap");
442
443 let mut bundle = CompilerExportBundle::new();
444 bundle.domains.insert("Location".to_string(), 50);
445
446 let result = SymbolTableSync::sync_with_compiler(&mut table, &bundle).expect("unwrap");
447
448 assert!(table.get_domain("Person").is_some());
450 assert!(table.get_domain("Location").is_some());
451
452 assert_eq!(result.domains.len(), 2);
454 }
455}
456
457pub struct CompilerExportAdvanced;
462
463impl CompilerExportAdvanced {
464 pub fn export_hierarchy(hierarchy: &DomainHierarchy) -> HashMap<String, Vec<String>> {
481 let mut result = HashMap::new();
482
483 for domain in hierarchy.all_domains() {
484 let ancestors = hierarchy.get_ancestors(&domain);
486 if !ancestors.is_empty() {
487 result.insert(domain, ancestors);
488 }
489 }
490
491 result
492 }
493
494 pub fn export_constraints(table: &SymbolTable) -> HashMap<String, PredicateConstraints> {
514 table
515 .predicates
516 .iter()
517 .filter_map(|(name, info)| info.constraints.as_ref().map(|c| (name.clone(), c.clone())))
518 .collect()
519 }
520
521 pub fn export_refinement_types() -> HashMap<String, String> {
526 let mut types = HashMap::new();
529 types.insert("PositiveInt".to_string(), "{ x: Int | x > 0 }".to_string());
530 types.insert(
531 "Probability".to_string(),
532 "{ x: Float | 0.0 <= x <= 1.0 }".to_string(),
533 );
534 types.insert(
535 "NonZeroFloat".to_string(),
536 "{ x: Float | x != 0.0 }".to_string(),
537 );
538 types
539 }
540
541 pub fn export_dependent_types() -> HashMap<String, String> {
545 let mut types = HashMap::new();
547 types.insert("Vector".to_string(), "Vector<T, n>".to_string());
548 types.insert("Matrix".to_string(), "Matrix<T, m, n>".to_string());
549 types.insert("Tensor3D".to_string(), "Tensor<T, i, j, k>".to_string());
550 types.insert("SquareMatrix".to_string(), "Matrix<T, n, n>".to_string());
551 types
552 }
553
554 pub fn export_linear_types() -> HashMap<String, String> {
558 let mut types = HashMap::new();
560 types.insert("GpuTensor".to_string(), "Linear".to_string());
561 types.insert("FileHandle".to_string(), "Linear".to_string());
562 types.insert("NetworkSocket".to_string(), "Linear".to_string());
563 types.insert("MutableReference".to_string(), "Affine".to_string());
564 types.insert("LogMessage".to_string(), "Relevant".to_string());
565 types
566 }
567
568 pub fn export_effects() -> HashMap<String, Vec<String>> {
572 let mut effects = HashMap::new();
574 effects.insert("read_file".to_string(), vec!["IO".to_string()]);
575 effects.insert(
576 "allocate_gpu".to_string(),
577 vec!["GPU".to_string(), "State".to_string()],
578 );
579 effects.insert(
580 "train_model".to_string(),
581 vec!["State".to_string(), "NonDet".to_string()],
582 );
583 effects.insert(
584 "fetch_data".to_string(),
585 vec!["IO".to_string(), "Exception".to_string()],
586 );
587 effects
588 }
589
590 pub fn export_all_advanced(
594 table: &SymbolTable,
595 hierarchy: Option<&DomainHierarchy>,
596 ) -> AdvancedExportBundle {
597 AdvancedExportBundle {
598 hierarchy: hierarchy.map(Self::export_hierarchy),
599 constraints: Self::export_constraints(table),
600 refinement_types: Self::export_refinement_types(),
601 dependent_types: Self::export_dependent_types(),
602 linear_types: Self::export_linear_types(),
603 effects: Self::export_effects(),
604 }
605 }
606}
607
608#[derive(Clone, Debug)]
610pub struct AdvancedExportBundle {
611 pub hierarchy: Option<HashMap<String, Vec<String>>>,
613 pub constraints: HashMap<String, PredicateConstraints>,
615 pub refinement_types: HashMap<String, String>,
617 pub dependent_types: HashMap<String, String>,
619 pub linear_types: HashMap<String, String>,
621 pub effects: HashMap<String, Vec<String>>,
623}
624
625impl AdvancedExportBundle {
626 pub fn new() -> Self {
628 Self {
629 hierarchy: None,
630 constraints: HashMap::new(),
631 refinement_types: HashMap::new(),
632 dependent_types: HashMap::new(),
633 linear_types: HashMap::new(),
634 effects: HashMap::new(),
635 }
636 }
637
638 pub fn is_empty(&self) -> bool {
640 self.hierarchy.is_none()
641 && self.constraints.is_empty()
642 && self.refinement_types.is_empty()
643 && self.dependent_types.is_empty()
644 && self.linear_types.is_empty()
645 && self.effects.is_empty()
646 }
647}
648
649impl Default for AdvancedExportBundle {
650 fn default() -> Self {
651 Self::new()
652 }
653}
654
655#[derive(Clone, Debug)]
657pub struct CompleteExportBundle {
658 pub basic: CompilerExportBundle,
660 pub advanced: AdvancedExportBundle,
662}
663
664impl CompleteExportBundle {
665 pub fn new(basic: CompilerExportBundle, advanced: AdvancedExportBundle) -> Self {
667 Self { basic, advanced }
668 }
669
670 pub fn from_symbol_table(table: &SymbolTable, hierarchy: Option<&DomainHierarchy>) -> Self {
672 Self {
673 basic: CompilerExport::export_all(table),
674 advanced: CompilerExportAdvanced::export_all_advanced(table, hierarchy),
675 }
676 }
677
678 pub fn is_empty(&self) -> bool {
680 self.basic.is_empty() && self.advanced.is_empty()
681 }
682}
683
684#[cfg(test)]
685mod advanced_tests {
686 use super::*;
687
688 #[test]
689 fn test_export_hierarchy() {
690 let mut hierarchy = DomainHierarchy::new();
691 hierarchy.add_subtype("Student", "Person");
692 hierarchy.add_subtype("Person", "Agent");
693
694 let relationships = CompilerExportAdvanced::export_hierarchy(&hierarchy);
695
696 assert!(relationships.contains_key("Student"));
697 assert!(relationships.contains_key("Person"));
698 }
699
700 #[test]
701 fn test_export_constraints() {
702 let mut table = SymbolTable::new();
703 table
704 .add_domain(DomainInfo::new("Person", 100))
705 .expect("unwrap");
706
707 let mut pred = PredicateInfo::new("age", vec!["Person".to_string()]);
708 pred.constraints = Some(PredicateConstraints::new());
709 table.add_predicate(pred).expect("unwrap");
710
711 let constraints = CompilerExportAdvanced::export_constraints(&table);
712 assert!(constraints.contains_key("age"));
713 }
714
715 #[test]
716 fn test_export_refinement_types() {
717 let types = CompilerExportAdvanced::export_refinement_types();
718 assert!(types.contains_key("PositiveInt"));
719 assert!(types.contains_key("Probability"));
720 }
721
722 #[test]
723 fn test_export_dependent_types() {
724 let types = CompilerExportAdvanced::export_dependent_types();
725 assert!(types.contains_key("Vector"));
726 assert!(types.contains_key("Matrix"));
727 }
728
729 #[test]
730 fn test_export_linear_types() {
731 let types = CompilerExportAdvanced::export_linear_types();
732 assert!(types.contains_key("GpuTensor"));
733 assert!(types.contains_key("FileHandle"));
734 }
735
736 #[test]
737 fn test_export_effects() {
738 let effects = CompilerExportAdvanced::export_effects();
739 assert!(effects.contains_key("read_file"));
740 assert!(effects.contains_key("allocate_gpu"));
741 }
742
743 #[test]
744 fn test_export_all_advanced() {
745 let mut table = SymbolTable::new();
746 table
747 .add_domain(DomainInfo::new("Person", 100))
748 .expect("unwrap");
749
750 let mut hierarchy = DomainHierarchy::new();
751 hierarchy.add_subtype("Student", "Person");
752
753 let bundle = CompilerExportAdvanced::export_all_advanced(&table, Some(&hierarchy));
754
755 assert!(bundle.hierarchy.is_some());
756 assert!(!bundle.refinement_types.is_empty());
757 assert!(!bundle.dependent_types.is_empty());
758 }
759
760 #[test]
761 fn test_complete_export_bundle() {
762 let mut table = SymbolTable::new();
763 table
764 .add_domain(DomainInfo::new("Person", 100))
765 .expect("unwrap");
766
767 let mut hierarchy = DomainHierarchy::new();
768 hierarchy.add_subtype("Student", "Person");
769
770 let bundle = CompleteExportBundle::from_symbol_table(&table, Some(&hierarchy));
771
772 assert!(!bundle.is_empty());
773 assert_eq!(bundle.basic.domains.len(), 1);
774 assert!(bundle.advanced.hierarchy.is_some());
775 }
776
777 #[test]
778 fn test_advanced_bundle_empty() {
779 let bundle = AdvancedExportBundle::new();
780 assert!(bundle.is_empty());
781 }
782}