1
#![forbid(unsafe_code)]
2

            
3
use std::fmt;
4
use std::hash::Hash;
5

            
6
use delegate::delegate;
7
use itertools::Itertools;
8

            
9
use merc_aterm::ATerm;
10
use merc_aterm::ATermArgs;
11
use merc_aterm::ATermIndex;
12
use merc_aterm::ATermList;
13
use merc_aterm::ATermRef;
14
use merc_aterm::ATermString;
15
use merc_aterm::Markable;
16
use merc_aterm::Symb;
17
use merc_aterm::SymbolRef;
18
use merc_aterm::Term;
19
use merc_aterm::TermIterator;
20
use merc_aterm::Transmutable;
21
use merc_aterm::storage::Marker;
22
use merc_collections::VecSet;
23
use merc_data::DataExpression;
24
use merc_data::DataVariable;
25
use merc_data::DataVariableRef;
26
use merc_data::is_data_variable;
27
use merc_macros::merc_derive_terms;
28
use merc_macros::merc_term;
29
use merc_utilities::MercError;
30

            
31
use crate::TransitionLabel;
32

            
33
/// Represents a multi-action, i.e., a set of action labels
34
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
35
pub struct LtsMultiAction {
36
    actions: VecSet<LtsAction>,
37
}
38

            
39
impl LtsMultiAction {
40
    /// Parses a multi-action from a string representation, typically found in the Aldebaran format.
41
1
    pub fn from_string(input: &str) -> Result<Self, MercError> {
42
1
        let mut actions = VecSet::new();
43

            
44
3
        for part in input.split('|') {
45
3
            let part = part.trim();
46
3
            if part.is_empty() {
47
                return Err("Empty action label in multi-action.".into());
48
3
            }
49

            
50
3
            if let Some(open_paren_index) = part.find('(') {
51
1
                if !part.ends_with(')') {
52
                    return Err(format!("Malformed action with arguments: {}", part).into());
53
1
                }
54

            
55
1
                let label = &part[..open_paren_index].trim();
56
1
                let args_str = &part[open_paren_index + 1..part.len() - 1];
57
1
                let arguments: Vec<DataExpression> = args_str
58
1
                    .split(',')
59
2
                    .map(|s| DataExpression::from_string(s.trim()))
60
1
                    .collect::<Result<_, MercError>>()?;
61
1
                actions.insert(LtsAction {
62
1
                    label: label.to_string(),
63
1
                    arguments,
64
1
                });
65
2
            } else {
66
2
                let label = part.trim();
67
2
                actions.insert(LtsAction {
68
2
                    label: label.to_string(),
69
2
                    arguments: Vec::new(),
70
2
                });
71
2
            }
72
        }
73

            
74
1
        Ok(LtsMultiAction { actions })
75
1
    }
76

            
77
    /// Converts the MultiAction into its mCRL2 ATerm representation.
78
300
    pub fn to_mcrl2_aterm(&self) -> Result<ATerm, MercError> {
79
300
        let action_terms: Vec<MCRL2Action> = self
80
300
            .actions
81
300
            .iter()
82
300
            .map(|action| -> Result<MCRL2Action, MercError> {
83
200
                let label_term = MCRL2ActionLabel::new(
84
200
                    ATermString::new(&action.label).copy(),
85
200
                    ATermList::<DataExpression>::empty(),
86
                );
87

            
88
200
                let arguments_term = ATermList::<DataExpression>::from_double_iter(action.arguments.iter().cloned());
89

            
90
200
                Ok(MCRL2Action::new(label_term.copy(), arguments_term))
91
200
            })
92
300
            .collect::<Result<Vec<_>, _>>()?;
93

            
94
300
        let actions_list = ATermList::<MCRL2Action>::from_double_iter(action_terms.into_iter());
95
300
        let time_term: DataExpression = DataVariable::new("@undefined_real").into();
96
300
        Ok(MCRL2TimedMultiAction::new(actions_list, time_term.copy()).into())
97
300
    }
98

            
99
    /// Constructs a MultiAction from an mCRL2 ATerm representation.
100
1283
    pub fn from_mcrl2_aterm(term: ATerm) -> Result<Self, MercError> {
101
1283
        if is_mcrl2_timed_multi_action_symbol(&term.get_head_symbol()) {
102
1283
            let multi_action = MCRL2TimedMultiAction::from(term);
103

            
104
1283
            if is_data_variable(&multi_action.time()) {
105
1283
                let variable: DataVariableRef = multi_action.time().into();
106
1283
                if variable.name() != "@undefined_real" {
107
                    return Err("Timed multi-actions are not supported.".into());
108
1283
                }
109
            } else {
110
                return Err("Timed multi-actions are not supported.".into());
111
            }
112

            
113
1283
            let mut actions = VecSet::new();
114
1283
            for action in multi_action.actions() {
115
1179
                let arguments = action.arguments().to_vec();
116
1179

            
117
1179
                // Convert the label to string
118
1179
                actions.insert(LtsAction {
119
1179
                    label: action.label().name().to_string(),
120
1179
                    arguments,
121
1179
                });
122
1179
            }
123

            
124
1283
            Ok(LtsMultiAction { actions })
125
        } else {
126
            Err(format!("Expected TimedMultAction symbol, got {}.", term).into())
127
        }
128
1283
    }
129
}
130

            
131
#[merc_derive_terms]
132
mod inner {
133
    use merc_aterm::ATermStringRef;
134
    use merc_aterm::Symbol;
135
    use merc_data::DataExpression;
136
    use merc_data::DataExpressionRef;
137
    use merc_macros::merc_ignore;
138

            
139
    use super::*;
140

            
141
    /// Represents a TimedMultiAction in mCRL2, which is a multi-action with an associated time.
142
    #[merc_term(is_mcrl2_timed_multi_action)]
143
    pub struct MCRL2TimedMultiAction {
144
        term: ATerm,
145
    }
146

            
147
    impl MCRL2TimedMultiAction {
148
        /// Creates a new TimedMultiAction with the given actions and time.
149
        #[merc_ignore]
150
300
        pub fn new(actions: ATermList<MCRL2Action>, time: DataExpressionRef<'_>) -> Self {
151
300
            let args: &[ATermRef<'_>] = &[actions.copy(), time.into()];
152
300
            let term = ATerm::with_args(&Symbol::new("TimedMultAct", 2), args);
153
300
            MCRL2TimedMultiAction { term: term.protect() }
154
300
        }
155

            
156
        /// Returns the actions contained in the multi-action.
157
1283
        pub fn actions(&self) -> ATermList<MCRL2Action> {
158
1283
            self.term.arg(0).into()
159
1283
        }
160

            
161
        /// Returns the time at which the multi-action occurs.
162
2566
        pub fn time(&self) -> DataExpressionRef<'_> {
163
2566
            self.term.arg(1).into()
164
2566
        }
165
    }
166

            
167
    #[merc_term(is_mcrl2_action)]
168
    pub struct MCRL2Action {
169
        term: ATerm,
170
    }
171

            
172
    impl MCRL2Action {
173
        /// Creates a new Action with the given label and arguments.
174
        #[merc_ignore]
175
200
        pub fn new(label: MCRL2ActionLabelRef<'_>, arguments: ATermList<DataExpression>) -> Self {
176
200
            let args: &[ATermRef<'_>] = &[label.into(), arguments.copy()];
177
200
            let term = ATerm::with_args(&Symbol::new("Action", 2), args);
178
200
            MCRL2Action { term: term.protect() }
179
200
        }
180

            
181
        /// Returns the label of the action.
182
1179
        pub fn label(&self) -> MCRL2ActionLabelRef<'_> {
183
1179
            self.term.arg(0).into()
184
1179
        }
185

            
186
        /// Returns the data arguments of the action.
187
1179
        pub fn arguments(&self) -> ATermList<DataExpression> {
188
1179
            self.term.arg(1).into()
189
1179
        }
190
    }
191

            
192
    #[merc_term(is_mcrl2_action_label)]
193
    pub struct MCRL2ActionLabel {
194
        term: ATerm,
195
    }
196

            
197
    impl MCRL2ActionLabel {
198
        /// Constructs a new action label with the given name and arguments.
199
        #[merc_ignore]
200
200
        pub fn new(name: ATermStringRef<'_>, args: ATermList<DataExpression>) -> Self {
201
200
            let args: &[ATermRef<'_>] = &[name.into(), args.copy()];
202
200
            let term = ATerm::with_args(&Symbol::new("ActId", 2), args);
203
200
            MCRL2ActionLabel { term: term.protect() }
204
200
        }
205

            
206
        /// Obtain the name of the action label.
207
1179
        pub fn name(&self) -> ATermStringRef<'_> {
208
1179
            self.term.arg(0).into()
209
1179
        }
210
    }
211
}
212

            
213
pub use inner::*;
214

            
215
/// See [`is_mcrl2_timed_multi_action_symbol`]
216
1283
fn is_mcrl2_timed_multi_action<'a, 'b>(term: &'b impl Term<'a, 'b>) -> bool {
217
1283
    is_mcrl2_timed_multi_action_symbol(&term.get_head_symbol())
218
1283
}
219

            
220
/// See [`is_mcrl2_action_symbol`]
221
1179
fn is_mcrl2_action<'a, 'b>(term: &'b impl Term<'a, 'b>) -> bool {
222
1179
    is_mcrl2_action_symbol(&term.get_head_symbol())
223
1179
}
224

            
225
/// See [`is_mcrl2_action_label_symbol`]
226
1379
fn is_mcrl2_action_label<'a, 'b>(term: &'b impl Term<'a, 'b>) -> bool {
227
1379
    is_mcrl2_action_label_symbol(&term.get_head_symbol())
228
1379
}
229

            
230
/// Checks if the given symbol represents a TimedMultiAction in mCRL2.
231
2566
fn is_mcrl2_timed_multi_action_symbol(symbol: &SymbolRef<'_>) -> bool {
232
2566
    symbol.name() == "TimedMultAct" && symbol.arity() == 2
233
2566
}
234

            
235
/// Checks if the given symbol represents an Action in mCRL2.
236
1179
fn is_mcrl2_action_symbol(symbol: &SymbolRef<'_>) -> bool {
237
1179
    symbol.name() == "Action" && symbol.arity() == 2
238
1179
}
239

            
240
/// Checks if the given symbol represents an ActionLabel in mCRL2.
241
1379
fn is_mcrl2_action_label_symbol(symbol: &SymbolRef<'_>) -> bool {
242
1379
    symbol.name() == "ActId" && symbol.arity() == 2
243
1379
}
244

            
245
/// Represents a single action label, with its (data) arguments
246
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
247
pub struct LtsAction {
248
    label: String,
249
    arguments: Vec<DataExpression>,
250
}
251

            
252
impl LtsAction {
253
    /// Creates a new action label with the given name and arguments.
254
4200
    pub fn new(label: String, arguments: Vec<DataExpression>) -> Self {
255
4200
        LtsAction { label, arguments }
256
4200
    }
257
}
258

            
259
impl TransitionLabel for LtsMultiAction {
260
100
    fn is_tau_label(&self) -> bool {
261
100
        self.actions.is_empty()
262
100
    }
263

            
264
402
    fn tau_label() -> Self {
265
402
        LtsMultiAction { actions: VecSet::new() }
266
402
    }
267

            
268
    fn matches_label(&self, label: &str) -> bool {
269
        // TODO: Is this correct, now a|b matches a?
270
        self.actions.iter().any(|action| action.label == label)
271
    }
272

            
273
4200
    fn from_index(i: usize) -> Self {
274
        // For now we only generate single actions, but these could become multiactions as well
275
4200
        LtsMultiAction {
276
4200
            actions: VecSet::singleton(LtsAction::new(
277
4200
                char::from_digit(i as u32, 36)
278
4200
                    .expect("Radix is less than 37, so should not panic")
279
4200
                    .to_string(),
280
4200
                Vec::new(),
281
4200
            )),
282
4200
        }
283
4200
    }
284
}
285

            
286
impl fmt::Display for LtsMultiAction {
287
5564
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288
5564
        if self.actions.is_empty() {
289
204
            write!(f, "τ")
290
        } else {
291
5360
            write!(f, "{}", self.actions.iter().format("|"))
292
        }
293
5564
    }
294
}
295

            
296
impl fmt::Display for LtsAction {
297
5360
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298
5360
        if self.arguments.is_empty() {
299
4412
            write!(f, "{}", self.label)
300
        } else {
301
948
            write!(f, "{}({})", self.label, self.arguments.iter().format(", "))
302
        }
303
5360
    }
304
}
305

            
306
impl fmt::Debug for LtsMultiAction {
307
600
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308
        // Use the debug format to print the display format
309
600
        write!(f, "{}", self)
310
600
    }
311
}
312

            
313
#[cfg(test)]
314
mod tests {
315
    use merc_data::DataExpression;
316

            
317
    use crate::LtsMultiAction;
318

            
319
    #[test]
320
1
    fn test_multi_action_parse_string() {
321
1
        let action = LtsMultiAction::from_string("a | b(1, 2) | c").unwrap();
322

            
323
1
        assert_eq!(action.actions.len(), 3);
324
1
        assert!(
325
1
            action
326
1
                .actions
327
1
                .iter()
328
1
                .any(|act| act.label == "a" && act.arguments.is_empty())
329
        );
330
2
        assert!(action.actions.iter().any(|act| act.label == "b"
331
1
            && act.arguments
332
1
                == vec![
333
1
                    DataExpression::from_string("1").unwrap(),
334
1
                    DataExpression::from_string("2").unwrap()
335
1
                ]));
336
1
        assert!(
337
1
            action
338
1
                .actions
339
1
                .iter()
340
3
                .any(|act| act.label == "c" && act.arguments.is_empty())
341
        );
342
1
    }
343
}