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
///
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
3
pub fn read_symbolic_lts<R: Read>(storage: &mut Storage, reader: R) -> Result<SymbolicLts, MercError> {
58
3
    info!("Reading symbolic LTS in the mCRL2 symbolic format...");
59

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

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

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

            
71
3
    let initial_state = stream.read_ldd(storage)?;
72
3
    let states = stream.read_ldd(storage)?;
73

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

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

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

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

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

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

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

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

            
125
121
        let relation = stream.read_ldd(storage)?;
126

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

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

            
147
/// Returns the ATerm mark for symbolic labelled transition systems.
148
3
fn symbolic_labelled_transition_system_mark() -> ATerm {
149
3
    ATerm::constant(&Symbol::new("symbolic_labelled_transition_system", 0))
150
3
}
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 mut storage = Storage::new();
165
1
        let _lts = read_symbolic_lts(&mut storage, &input[..]).unwrap();
166
1
    }
167
}