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_ldd::BinaryLddReader;
17
use merc_ldd::Storage;
18
use merc_lts::LtsMultiAction;
19
use merc_utilities::MercError;
20

            
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
/// <marker>: ATerm
30
/// <data specification>
31
/// <process parameters>: ATermList<ATerm>
32
///
33
/// <initial state>: LDD
34
/// <states>: LDD
35
///
36
/// For each process parameter:
37
///   <number of entries>: u64
38
///   For each entry:
39
///     <value>: ATerm
40
///
41
/// <number of action labels>: u64
42
/// For each action label:
43
///   <action label>: ATerm
44
///
45
/// <number of summand groups>: u64
46
/// For each summand group:
47
///  <number of read parameters>: u64
48
///  For each read parameter:
49
///    <read parameter>: ATerm
50
///
51
/// <number of write parameters>: u64
52
/// For each write parameter:
53
///  <write parameter>: ATerm
54
3
pub fn read_symbolic_lts<R: Read>(storage: &mut Storage, reader: R) -> Result<SymbolicLts, MercError> {
55
3
    info!("Reading symbolic LTS in the mCRL2 symbolic format...");
56

            
57
3
    let aterm_stream = BinaryATermReader::new(BufReader::new(reader))?;
58
3
    let mut stream = BinaryLddReader::new(storage, aterm_stream)?;
59

            
60
3
    if ATermRead::read_aterm(&mut stream)? != Some(symbolic_labelled_transition_system_mark()) {
61
        return Err("Expected symbolic labelled transition system stream".into());
62
3
    }
63

            
64
3
    let data_spec = DataSpecification::read(&mut stream)?;
65
3
    let process_parameters: ATermList<DataVariable> = stream.read_aterm()?.ok_or("Expected process parameters")?.into();
66
3
    let process_parameters: Vec<DataVariable> = process_parameters.to_vec();
67

            
68
3
    let initial_state = stream.read_ldd(storage)?;
69
3
    let states = stream.read_ldd(storage)?;
70

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

            
82
52
        let mut values = Vec::with_capacity(num_of_entries as usize);
83
251
        for i in 0..num_of_entries {
84
251
            let value = stream.read_aterm()?.ok_or("Unexpected end of stream")?;
85

            
86
251
            let expr: DataExpression = value.into();
87
251
            debug!("  {i}:  {}", expr);
88
251
            values.push(expr);
89
        }
90

            
91
52
        parameter_values.push(values);
92
    }
93

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

            
101
241
        debug!("Action {}: {}", action_labels.len(), action);
102
241
        action_labels.push(action);
103
    }
104

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

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

            
122
121
        let relation = stream.read_ldd(storage)?;
123

            
124
121
        summand_groups.push(SummandGroup::new(
125
121
            storage,
126
121
            &process_parameters,
127
121
            read_parameters,
128
121
            write_parameters,
129
121
            relation,
130
        )?);
131
    }
132

            
133
3
    info!("Finished reading symbolic LTS.");
134
3
    Ok(SymbolicLts::new(
135
3
        data_spec,
136
3
        states,
137
3
        initial_state,
138
3
        summand_groups,
139
3
        action_labels,
140
3
        parameter_values,
141
3
    ))
142
3
}
143

            
144
/// Returns the ATerm mark for symbolic labelled transition systems.
145
3
fn symbolic_labelled_transition_system_mark() -> ATerm {
146
3
    ATerm::constant(&Symbol::new("symbolic_labelled_transition_system", 0))
147
3
}
148

            
149
#[cfg(test)]
150
mod tests {
151
    use merc_utilities::test_logger;
152

            
153
    use super::*;
154

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

            
161
1
        let mut storage = Storage::new();
162
1
        let _lts = read_symbolic_lts(&mut storage, &input[..]).unwrap();
163
1
    }
164
}