1
use thiserror::Error;
2

            
3
use merc_syntax::SortExpression;
4
use merc_syntax::UntypedDataSpecification;
5
use merc_utilities::MercError;
6

            
7
use crate::is_nonempty_sort;
8
use crate::target_sort;
9

            
10
/// Checks if a signature is well-typed, i.e. it satisfies the conditions of 15.1.7.
11
2
pub fn is_well_typed(spec: &UntypedDataSpecification) -> Result<(), WellTypedError> {
12
2
    are_constructors_and_mappings_disjoint(spec)?;
13

            
14
    // Check that there are no constructors defined for the basic sorts.
15
2
    for constructor in &spec.constructor_declarations {
16
2
        let sort = target_sort(&constructor.sort);
17

            
18
        // There are not more constructors for basic sorts.
19
2
        if is_basic_sort(sort) {
20
1
            return Err(WellTypedError::ConstructorForBasicSort {
21
1
                constructor: constructor.identifier.clone(),
22
1
                sort: sort.to_string(),
23
1
            }
24
1
            .into());
25
1
        }
26

            
27
        // Function sorts are not constructor sorts
28
1
        if matches!(sort, SortExpression::Function { domain: _, range: _ }) {
29
            return Err(WellTypedError::ConstructorForFunctionSort {
30
                constructor: constructor.identifier.clone(),
31
                sort: sort.to_string(),
32
            });
33
1
        }
34
    }
35

            
36
    // Check that all sorts are syntactically non-empty.
37
1
    for sort in &spec.sort_declarations {
38
1
        if !is_nonempty_sort(&sort.identifier, spec) {
39
1
            return Err(WellTypedError::EmptySort {
40
1
                sort: sort.identifier.clone(),
41
1
            });
42
        }
43
    }
44

            
45
    Ok(())
46
2
}
47

            
48
#[derive(Debug, Error)]
49
pub enum WellTypedError {
50
    #[error("Constructor '{}' and mapping '{}' have the same identifier", constructor, map)]
51
    ConstructorAndMappingConflict { constructor: String, map: String },
52

            
53
    #[error(
54
        "Constructors cannot be defined for basic sorts, but constructor '{}' is defined for sort '{}'",
55
        constructor,
56
        sort
57
    )]
58
    ConstructorForBasicSort { constructor: String, sort: String },
59

            
60
    #[error(
61
        "Constructors cannot be defined for function sorts, but constructor '{}' is defined for sort '{}'",
62
        constructor,
63
        sort
64
    )]
65
    ConstructorForFunctionSort { constructor: String, sort: String },
66

            
67
    #[error("Sort '{}' is syntactically empty", sort)]
68
    EmptySort { sort: String },
69

            
70
    #[error("Alias cycle detected: {:?}", sorts)]
71
    AliasCycle { sorts: Vec<String> },
72

            
73
    #[error("Error: '{}'", 0)]
74
    Custom(MercError),
75

            
76
    // These are name resolution errors, but we include them here to avoid having to define a separate error type for name resolution.
77
    #[error("Duplicate sort declaration: '{}'", sort)]
78
    DuplicateSortDeclaration { sort: String },
79

            
80
    #[error("Undefined sort: '{}'", sort)]
81
    UndefinedSort { sort: String },
82
}
83

            
84
/// Checks that the constructors and mappings are disjoint, i.e. that no identifier is both a constructor and a mapping.
85
2
fn are_constructors_and_mappings_disjoint(spec: &UntypedDataSpecification) -> Result<(), WellTypedError> {
86
2
    for constructor in &spec.constructor_declarations {
87
2
        if let Some(map) = spec
88
2
            .map_declarations
89
2
            .iter()
90
2
            .find(|map| map.identifier == constructor.identifier)
91
        {
92
            return Err(WellTypedError::ConstructorAndMappingConflict {
93
                constructor: constructor.identifier.clone(),
94
                map: map.identifier.clone(),
95
            });
96
2
        }
97
    }
98

            
99
2
    Ok(())
100
2
}
101

            
102
/// The set of basic sorts `BS` are exactly the sorts Bool, Pos, Int, Nat, and Real. Definition 15.1.2.
103
2
fn is_basic_sort(sort: &SortExpression) -> bool {
104
2
    matches!(sort, SortExpression::Simple(_))
105
2
}
106

            
107
#[cfg(test)]
108
mod tests {
109
    use merc_syntax::UntypedDataSpecification;
110

            
111
    use crate::DataSpecification;
112
    use crate::WellTypedError;
113

            
114
    #[test]
115
1
    fn test_well_typed_spec() {
116
1
        let spec = UntypedDataSpecification::parse(
117
1
            "
118
1
            sort D;
119
1
            cons f: D -> Nat;
120
1
        ",
121
        )
122
1
        .unwrap();
123

            
124
1
        match DataSpecification::from_untyped(spec) {
125
1
            Err(WellTypedError::ConstructorForBasicSort { constructor, sort })
126
1
                if constructor == "f" && sort == "Nat" => {}
127
            Err(other) => panic!("Unexpected error {:?}", other),
128
            _ => panic!("Expected from_untyped to fail"),
129
        }
130
1
    }
131
}