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.add_domain(DomainInfo::new("Person", 100)).unwrap();
339 table.add_domain(DomainInfo::new("Location", 50)).unwrap();
340
341 let domains = CompilerExport::export_domains(&table);
342 assert_eq!(domains.get("Person"), Some(&100));
343 assert_eq!(domains.get("Location"), Some(&50));
344 }
345
346 #[test]
347 fn test_export_predicate_signatures() {
348 let mut table = SymbolTable::new();
349 table.add_domain(DomainInfo::new("Person", 100)).unwrap();
350 table
351 .add_predicate(PredicateInfo::new(
352 "knows",
353 vec!["Person".to_string(), "Person".to_string()],
354 ))
355 .unwrap();
356
357 let signatures = CompilerExport::export_predicate_signatures(&table);
358 assert_eq!(
359 signatures.get("knows"),
360 Some(&vec!["Person".to_string(), "Person".to_string()])
361 );
362 }
363
364 #[test]
365 fn test_export_all() {
366 let mut table = SymbolTable::new();
367 table.add_domain(DomainInfo::new("Person", 100)).unwrap();
368 table
369 .add_predicate(PredicateInfo::new("knows", vec!["Person".to_string()]))
370 .unwrap();
371 table.bind_variable("x", "Person").unwrap();
372
373 let bundle = CompilerExport::export_all(&table);
374 assert_eq!(bundle.domains.len(), 1);
375 assert_eq!(bundle.predicate_signatures.len(), 1);
376 assert_eq!(bundle.variable_bindings.len(), 1);
377 }
378
379 #[test]
380 fn test_import_domains() {
381 let mut domains = HashMap::new();
382 domains.insert("Person".to_string(), 100);
383
384 let mut table = SymbolTable::new();
385 CompilerImport::import_domains(&mut table, &domains).unwrap();
386
387 assert!(table.get_domain("Person").is_some());
388 }
389
390 #[test]
391 fn test_import_predicates() {
392 let mut table = SymbolTable::new();
393 table.add_domain(DomainInfo::new("Person", 100)).unwrap();
394
395 let mut signatures = HashMap::new();
396 signatures.insert("knows".to_string(), vec!["Person".to_string()]);
397
398 CompilerImport::import_predicates(&mut table, &signatures).unwrap();
399 assert!(table.get_predicate("knows").is_some());
400 }
401
402 #[test]
403 fn test_validation_invalid_domain_reference() {
404 let table = SymbolTable::new();
405 let mut bundle = CompilerExportBundle::new();
406 bundle
407 .predicate_signatures
408 .insert("knows".to_string(), vec!["UnknownDomain".to_string()]);
409
410 let result = SymbolTableSync::validate_bundle(&table, &bundle).unwrap();
411 assert!(!result.is_valid());
412 assert!(!result.errors.is_empty());
413 }
414
415 #[test]
416 fn test_validation_unused_domain_warning() {
417 let table = SymbolTable::new();
418 let mut bundle = CompilerExportBundle::new();
419 bundle.domains.insert("UnusedDomain".to_string(), 100);
420
421 let result = SymbolTableSync::validate_bundle(&table, &bundle).unwrap();
422 assert!(result.is_valid()); assert!(!result.warnings.is_empty());
424 }
425
426 #[test]
427 fn test_sync_with_compiler() {
428 let mut table = SymbolTable::new();
429 table.add_domain(DomainInfo::new("Person", 100)).unwrap();
430
431 let mut bundle = CompilerExportBundle::new();
432 bundle.domains.insert("Location".to_string(), 50);
433
434 let result = SymbolTableSync::sync_with_compiler(&mut table, &bundle).unwrap();
435
436 assert!(table.get_domain("Person").is_some());
438 assert!(table.get_domain("Location").is_some());
439
440 assert_eq!(result.domains.len(), 2);
442 }
443}
444
445pub struct CompilerExportAdvanced;
450
451impl CompilerExportAdvanced {
452 pub fn export_hierarchy(hierarchy: &DomainHierarchy) -> HashMap<String, Vec<String>> {
469 let mut result = HashMap::new();
470
471 for domain in hierarchy.all_domains() {
472 let ancestors = hierarchy.get_ancestors(&domain);
474 if !ancestors.is_empty() {
475 result.insert(domain, ancestors);
476 }
477 }
478
479 result
480 }
481
482 pub fn export_constraints(table: &SymbolTable) -> HashMap<String, PredicateConstraints> {
502 table
503 .predicates
504 .iter()
505 .filter_map(|(name, info)| info.constraints.as_ref().map(|c| (name.clone(), c.clone())))
506 .collect()
507 }
508
509 pub fn export_refinement_types() -> HashMap<String, String> {
514 let mut types = HashMap::new();
517 types.insert("PositiveInt".to_string(), "{ x: Int | x > 0 }".to_string());
518 types.insert(
519 "Probability".to_string(),
520 "{ x: Float | 0.0 <= x <= 1.0 }".to_string(),
521 );
522 types.insert(
523 "NonZeroFloat".to_string(),
524 "{ x: Float | x != 0.0 }".to_string(),
525 );
526 types
527 }
528
529 pub fn export_dependent_types() -> HashMap<String, String> {
533 let mut types = HashMap::new();
535 types.insert("Vector".to_string(), "Vector<T, n>".to_string());
536 types.insert("Matrix".to_string(), "Matrix<T, m, n>".to_string());
537 types.insert("Tensor3D".to_string(), "Tensor<T, i, j, k>".to_string());
538 types.insert("SquareMatrix".to_string(), "Matrix<T, n, n>".to_string());
539 types
540 }
541
542 pub fn export_linear_types() -> HashMap<String, String> {
546 let mut types = HashMap::new();
548 types.insert("GpuTensor".to_string(), "Linear".to_string());
549 types.insert("FileHandle".to_string(), "Linear".to_string());
550 types.insert("NetworkSocket".to_string(), "Linear".to_string());
551 types.insert("MutableReference".to_string(), "Affine".to_string());
552 types.insert("LogMessage".to_string(), "Relevant".to_string());
553 types
554 }
555
556 pub fn export_effects() -> HashMap<String, Vec<String>> {
560 let mut effects = HashMap::new();
562 effects.insert("read_file".to_string(), vec!["IO".to_string()]);
563 effects.insert(
564 "allocate_gpu".to_string(),
565 vec!["GPU".to_string(), "State".to_string()],
566 );
567 effects.insert(
568 "train_model".to_string(),
569 vec!["State".to_string(), "NonDet".to_string()],
570 );
571 effects.insert(
572 "fetch_data".to_string(),
573 vec!["IO".to_string(), "Exception".to_string()],
574 );
575 effects
576 }
577
578 pub fn export_all_advanced(
582 table: &SymbolTable,
583 hierarchy: Option<&DomainHierarchy>,
584 ) -> AdvancedExportBundle {
585 AdvancedExportBundle {
586 hierarchy: hierarchy.map(Self::export_hierarchy),
587 constraints: Self::export_constraints(table),
588 refinement_types: Self::export_refinement_types(),
589 dependent_types: Self::export_dependent_types(),
590 linear_types: Self::export_linear_types(),
591 effects: Self::export_effects(),
592 }
593 }
594}
595
596#[derive(Clone, Debug)]
598pub struct AdvancedExportBundle {
599 pub hierarchy: Option<HashMap<String, Vec<String>>>,
601 pub constraints: HashMap<String, PredicateConstraints>,
603 pub refinement_types: HashMap<String, String>,
605 pub dependent_types: HashMap<String, String>,
607 pub linear_types: HashMap<String, String>,
609 pub effects: HashMap<String, Vec<String>>,
611}
612
613impl AdvancedExportBundle {
614 pub fn new() -> Self {
616 Self {
617 hierarchy: None,
618 constraints: HashMap::new(),
619 refinement_types: HashMap::new(),
620 dependent_types: HashMap::new(),
621 linear_types: HashMap::new(),
622 effects: HashMap::new(),
623 }
624 }
625
626 pub fn is_empty(&self) -> bool {
628 self.hierarchy.is_none()
629 && self.constraints.is_empty()
630 && self.refinement_types.is_empty()
631 && self.dependent_types.is_empty()
632 && self.linear_types.is_empty()
633 && self.effects.is_empty()
634 }
635}
636
637impl Default for AdvancedExportBundle {
638 fn default() -> Self {
639 Self::new()
640 }
641}
642
643#[derive(Clone, Debug)]
645pub struct CompleteExportBundle {
646 pub basic: CompilerExportBundle,
648 pub advanced: AdvancedExportBundle,
650}
651
652impl CompleteExportBundle {
653 pub fn new(basic: CompilerExportBundle, advanced: AdvancedExportBundle) -> Self {
655 Self { basic, advanced }
656 }
657
658 pub fn from_symbol_table(table: &SymbolTable, hierarchy: Option<&DomainHierarchy>) -> Self {
660 Self {
661 basic: CompilerExport::export_all(table),
662 advanced: CompilerExportAdvanced::export_all_advanced(table, hierarchy),
663 }
664 }
665
666 pub fn is_empty(&self) -> bool {
668 self.basic.is_empty() && self.advanced.is_empty()
669 }
670}
671
672#[cfg(test)]
673mod advanced_tests {
674 use super::*;
675
676 #[test]
677 fn test_export_hierarchy() {
678 let mut hierarchy = DomainHierarchy::new();
679 hierarchy.add_subtype("Student", "Person");
680 hierarchy.add_subtype("Person", "Agent");
681
682 let relationships = CompilerExportAdvanced::export_hierarchy(&hierarchy);
683
684 assert!(relationships.contains_key("Student"));
685 assert!(relationships.contains_key("Person"));
686 }
687
688 #[test]
689 fn test_export_constraints() {
690 let mut table = SymbolTable::new();
691 table.add_domain(DomainInfo::new("Person", 100)).unwrap();
692
693 let mut pred = PredicateInfo::new("age", vec!["Person".to_string()]);
694 pred.constraints = Some(PredicateConstraints::new());
695 table.add_predicate(pred).unwrap();
696
697 let constraints = CompilerExportAdvanced::export_constraints(&table);
698 assert!(constraints.contains_key("age"));
699 }
700
701 #[test]
702 fn test_export_refinement_types() {
703 let types = CompilerExportAdvanced::export_refinement_types();
704 assert!(types.contains_key("PositiveInt"));
705 assert!(types.contains_key("Probability"));
706 }
707
708 #[test]
709 fn test_export_dependent_types() {
710 let types = CompilerExportAdvanced::export_dependent_types();
711 assert!(types.contains_key("Vector"));
712 assert!(types.contains_key("Matrix"));
713 }
714
715 #[test]
716 fn test_export_linear_types() {
717 let types = CompilerExportAdvanced::export_linear_types();
718 assert!(types.contains_key("GpuTensor"));
719 assert!(types.contains_key("FileHandle"));
720 }
721
722 #[test]
723 fn test_export_effects() {
724 let effects = CompilerExportAdvanced::export_effects();
725 assert!(effects.contains_key("read_file"));
726 assert!(effects.contains_key("allocate_gpu"));
727 }
728
729 #[test]
730 fn test_export_all_advanced() {
731 let mut table = SymbolTable::new();
732 table.add_domain(DomainInfo::new("Person", 100)).unwrap();
733
734 let mut hierarchy = DomainHierarchy::new();
735 hierarchy.add_subtype("Student", "Person");
736
737 let bundle = CompilerExportAdvanced::export_all_advanced(&table, Some(&hierarchy));
738
739 assert!(bundle.hierarchy.is_some());
740 assert!(!bundle.refinement_types.is_empty());
741 assert!(!bundle.dependent_types.is_empty());
742 }
743
744 #[test]
745 fn test_complete_export_bundle() {
746 let mut table = SymbolTable::new();
747 table.add_domain(DomainInfo::new("Person", 100)).unwrap();
748
749 let mut hierarchy = DomainHierarchy::new();
750 hierarchy.add_subtype("Student", "Person");
751
752 let bundle = CompleteExportBundle::from_symbol_table(&table, Some(&hierarchy));
753
754 assert!(!bundle.is_empty());
755 assert_eq!(bundle.basic.domains.len(), 1);
756 assert!(bundle.advanced.hierarchy.is_some());
757 }
758
759 #[test]
760 fn test_advanced_bundle_empty() {
761 let bundle = AdvancedExportBundle::new();
762 assert!(bundle.is_empty());
763 }
764}