1
use std::collections::HashMap;
2
use std::convert::Infallible;
3

            
4
use merc_syntax::DefId;
5
use merc_syntax::EqnSpec;
6
use merc_syntax::IdDecl;
7
use merc_syntax::SortExpression;
8
use merc_syntax::UntypedDataSpecification;
9
use merc_syntax::apply_sort_expression;
10

            
11
use crate::WellTypedError;
12
use crate::basic_sort_data_specification;
13
use crate::has_alias_cycle;
14
use crate::is_well_typed;
15
use crate::map_sorts_in_spec;
16
use crate::resolve_names;
17

            
18
/// A type checked and well-typed data specification.
19
pub struct DataSpecification {
20
    pub sort_declarations: HashMap<DefId, (Vec<IdDecl>, Vec<EqnSpec>)>,
21
}
22

            
23
impl DataSpecification {
24
    /// Create a completed well-typed data specification from an untyped data specification.
25
3
    pub fn from_untyped(mut spec: UntypedDataSpecification) -> Result<Self, WellTypedError> {
26
5
        map_sorts_in_spec(&mut spec, |sort| -> Result<_, Infallible> {
27
5
            Ok(flatten_function_sorts(sort))
28
5
        })
29
3
        .expect("The inner function never fails");
30

            
31
3
        let sorts = resolve_names(&mut spec)?;
32

            
33
3
        has_alias_cycle(&spec).map_err(|cycle| WellTypedError::AliasCycle {
34
1
            sorts: cycle
35
1
                .iter()
36
3
                .map(|id| sorts.get_by_index(**id).expect("The sort should be declared").clone())
37
1
                .collect(),
38
1
        })?;
39

            
40
2
        is_well_typed(&spec)?;
41

            
42
        // Obtain the definitions for all basic sort.
43
        let _basic_sort_spec = basic_sort_data_specification().map_err(|e| WellTypedError::Custom(e))?;
44

            
45
        // Add all the occurring standard sorts to the specification.
46

            
47
        Ok(Self {
48
            sort_declarations: HashMap::new(), // TODO: Fill this in with the actual sort declarations.
49
        })
50
3
    }
51
}
52

            
53
/// Returns the target sort of a sort expression, i.e. the range of a function
54
/// sort, or the sort itself if it is not a function sort.
55
3
pub fn target_sort(sort: &SortExpression) -> &SortExpression {
56
3
    debug_assert!(
57
3
        !matches!(sort, SortExpression::Function { .. }),
58
        "target_sort should only be called on non-function sorts or flattened function sorts"
59
    );
60

            
61
3
    if let SortExpression::FlattenedFunction { domain: _, range } = sort {
62
3
        range
63
    } else {
64
        sort
65
    }
66
3
}
67

            
68
/// Returns the arguments of a function sort, requires that function sorts are flattened.
69
2
pub fn argument_sorts(sort: &SortExpression) -> &Vec<SortExpression> {
70
2
    if let SortExpression::FlattenedFunction { domain, range: _ } = sort {
71
2
        domain
72
    } else {
73
        unreachable!("argument_sorts should only be called on function sorts")
74
    }
75
2
}
76

            
77
/// Replaces sort references of `identifier` in `sort` by the given `result_sort`.
78
5
fn flatten_function_sorts(sort: &SortExpression) -> SortExpression {
79
5
    apply_sort_expression(sort.clone(), |expr| -> Result<_, Infallible> {
80
5
        if let SortExpression::Function { domain, range } = expr {
81
2
            let mut flattened_domain = Vec::new();
82
2
            flatten_function_domain_rec(domain, &mut flattened_domain);
83

            
84
2
            return Ok(Some(SortExpression::FlattenedFunction {
85
2
                domain: flattened_domain,
86
2
                range: range.clone(),
87
2
            }));
88
3
        }
89

            
90
3
        Ok(None)
91
5
    })
92
5
    .expect("flatten_function_sorts should not fail")
93
5
}
94

            
95
/// Flattens a function sort of the form ((A_0 # A_1) # ... # A_n) -> B into a
96
/// sort of the form A_0 # A_1 # ... # A_n -> B, where B is the original range
97
/// of the function.
98
2
fn flatten_function_domain_rec(sort: &SortExpression, domain: &mut Vec<SortExpression>) {
99
2
    match sort {
100
        SortExpression::Product { lhs, rhs } => {
101
            flatten_function_domain_rec(lhs, domain);
102
            flatten_function_domain_rec(rhs, domain);
103
        }
104
2
        _ => domain.push(sort.clone()),
105
    }
106
2
}