1
use std::io::BufReader;
2
use std::io::Read;
3

            
4
use log::debug;
5
use log::info;
6
use merc_aterm::ATerm;
7
use merc_aterm::ATermList;
8
use merc_aterm::ATermRead;
9
use merc_aterm::ATermStreamable;
10
use merc_aterm::BinaryATermReader;
11
use merc_aterm::Symbol;
12
use merc_data::DataExpression;
13
use merc_data::DataSpecification;
14
use merc_data::DataVariable;
15
use merc_io::BitStreamRead;
16
use merc_lts::LtsMultiAction;
17
use merc_utilities::MercError;
18
use oxidd::ldd::LDDManagerRef;
19

            
20
use crate::BinaryLddReader;
21
use crate::SummandGroup;
22
use crate::SymbolicLts;
23

            
24
/// Reads a symbolic LTS from a binary stream in the mCRL2 `.sym` format.
25
///
26
/// # Details
27
///
28
/// The stream contains
29
///
30
/// ```plain
31
/// <marker>: ATerm
32
/// <data specification>
33
/// <process parameters>: ATermList<ATerm>
34
///
35
/// <initial state>: LDD
36
/// <states>: LDD
37
///
38
/// For each process parameter:
39
///   <number of entries>: u64
40
///   For each entry:
41
///     <value>: ATerm
42
///
43
/// <number of action labels>: u64
44
/// For each action label:
45
///   <action label>: ATerm
46
///
47
/// <number of summand groups>: u64
48
/// For each summand group:
49
///   <number of read parameters>: u64
50
///   For each read parameter:
51
///     <read parameter>: ATerm
52
///
53
///   <number of write parameters>: u64
54
///   For each write parameter:
55
///     <write parameter>: ATerm
56
/// ```
57
6
pub fn read_symbolic_lts<R: Read>(manager: &LDDManagerRef, reader: R) -> Result<SymbolicLts, MercError> {
58
6
    info!("Reading symbolic LTS in the mCRL2 symbolic format...");
59

            
60
6
    let aterm_stream = BinaryATermReader::new(BufReader::new(reader))?;
61
6
    let mut stream = BinaryLddReader::new(manager, aterm_stream)?;
62

            
63
6
    if ATermRead::read_aterm(&mut stream)? != Some(symbolic_labelled_transition_system_mark()) {
64
        return Err("Expected symbolic labelled transition system stream".into());
65
6
    }
66

            
67
6
    let data_spec = DataSpecification::read(&mut stream)?;
68
6
    let process_parameters: ATermList<DataVariable> = stream.read_aterm()?.ok_or("Expected process parameters")?.into();
69
6
    let process_parameters: Vec<DataVariable> = process_parameters.to_vec();
70

            
71
6
    let initial_state = stream.read_ldd(manager)?;
72
6
    let states = stream.read_ldd(manager)?;
73

            
74
    // Read the values for the process parameters.
75
6
    let mut parameter_values: Vec<Vec<DataExpression>> = Vec::with_capacity(process_parameters.len());
76
86
    for parameter in &process_parameters {
77
86
        let num_of_entries = stream.read_integer()?;
78
86
        debug!(
79
            "Parameter {}: {} has {} entries",
80
            parameter_values.len(),
81
            parameter,
82
            num_of_entries
83
        );
84

            
85
86
        let mut values = Vec::with_capacity(num_of_entries as usize);
86
468
        for i in 0..num_of_entries {
87
468
            let value = stream.read_aterm()?.ok_or("Unexpected end of stream")?;
88

            
89
468
            let expr: DataExpression = value.into();
90
468
            debug!("  {i}:  {}", expr);
91
468
            values.push(expr);
92
        }
93

            
94
86
        parameter_values.push(values);
95
    }
96

            
97
    // Read the action labels.
98
6
    let num_of_action_labels = stream.read_integer()?;
99
6
    let mut action_labels = Vec::with_capacity(num_of_action_labels as usize);
100
6
    for _ in 0..num_of_action_labels {
101
335
        let action_label = stream.read_aterm()?.ok_or("Unexpected end of stream")?;
102
335
        let action = LtsMultiAction::from_mcrl2_aterm(action_label)?;
103

            
104
335
        debug!("Action {}: {}", action_labels.len(), action);
105
335
        action_labels.push(action);
106
    }
107

            
108
    // Read the summand groups.
109
6
    let mut summand_groups = Vec::new();
110
6
    let num_of_groups = stream.read_integer()?;
111
6
    for _ in 0..num_of_groups {
112
        // Note: this is not an ATermInt, as expected by `read_aterm_iter`, but a variable integer.
113
193
        let num_of_reads = stream.read_integer()?;
114
193
        let mut read_parameters: Vec<DataVariable> = Vec::with_capacity(num_of_reads as usize);
115
193
        for _ in 0..num_of_reads {
116
554
            read_parameters.push(stream.read_aterm()?.ok_or("Unexpected end of stream")?.into());
117
        }
118

            
119
193
        let num_of_writes = stream.read_integer()?;
120
193
        let mut write_parameters: Vec<DataVariable> = Vec::with_capacity(num_of_writes as usize);
121
193
        for _ in 0..num_of_writes {
122
672
            write_parameters.push(stream.read_aterm()?.ok_or("Unexpected end of stream")?.into());
123
        }
124

            
125
193
        let relation = stream.read_ldd(manager)?;
126

            
127
193
        summand_groups.push(SummandGroup::new(
128
193
            manager,
129
193
            &process_parameters,
130
193
            read_parameters,
131
193
            write_parameters,
132
193
            relation,
133
        )?);
134
    }
135

            
136
6
    info!("Finished reading symbolic LTS.");
137
6
    Ok(SymbolicLts::new(
138
6
        data_spec,
139
6
        states,
140
6
        initial_state,
141
6
        summand_groups,
142
6
        action_labels,
143
6
        parameter_values,
144
6
    ))
145
6
}
146

            
147
/// Returns the ATerm mark for symbolic labelled transition systems.
148
6
fn symbolic_labelled_transition_system_mark() -> ATerm {
149
6
    ATerm::constant(&Symbol::new("symbolic_labelled_transition_system", 0))
150
6
}
151

            
152
#[cfg(test)]
153
mod tests {
154
    use merc_utilities::test_logger;
155

            
156
    use super::*;
157

            
158
    #[test]
159
    #[cfg_attr(miri, ignore)]
160
1
    fn test_read_symbolic_lts_wms_sym() {
161
1
        test_logger();
162
1
        let input = include_bytes!("../../../../examples/lts/WMS.sym");
163

            
164
1
        let ldd_manager = oxidd::ldd::new_manager(2048, 1024, 1);
165
1
        let _lts = read_symbolic_lts(&ldd_manager, &input[..]).unwrap();
166
1
    }
167
}