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::VecBag;
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 multi set of action labels
34
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
35
pub struct LtsMultiAction<L: Ord> {
36
    actions: VecBag<L>,
37
}
38

            
39
impl<L: Ord> LtsMultiAction<L> {
40
    /// Creates a new multi-action with the given set of action labels.
41
    pub fn new(actions: VecBag<L>) -> Self {
42
        LtsMultiAction { actions }
43
    }
44

            
45
    /// Returns true iff the multi-action is a tau action, i.e., it contains no action labels.
46
    pub fn is_tau_label(&self) -> bool {
47
        self.actions.is_empty()
48
    }
49

            
50
    /// Returns the set of action labels contained in the multi-action.
51
    pub fn actions(&self) -> &VecBag<L> {
52
        &self.actions
53
    }
54

            
55
    /// Consumes the multi-action and returns the set of action labels contained in it.
56
    pub fn into_actions(self) -> VecBag<L> {
57
        self.actions
58
    }
59

            
60
    /// Retains only the actions in the multi-action that satisfy the given predicate.
61
    pub fn retain<F>(&mut self, mut f: F)
62
    where
63
        F: FnMut(&L) -> bool,
64
    {
65
        self.actions.retain(|action| f(action));
66
    }
67
}
68

            
69
/// A single action label with a name and string-valued arguments, used for the
70
/// mCRL2 textual `.aut` format where argument values are not yet interpreted as
71
/// data expressions.
72
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
73
pub struct SimpleAction {
74
    label: String,
75
    arguments: Vec<String>,
76
}
77

            
78
impl SimpleAction {
79
    /// Creates a new simple action with the given label and arguments.
80
    pub fn new(label: String, arguments: Vec<String>) -> Self {
81
        SimpleAction { label, arguments }
82
    }
83

            
84
    /// Returns the name of the action label.
85
    pub fn label(&self) -> &str {
86
        &self.label
87
    }
88

            
89
    /// Returns the string arguments of the action.
90
    pub fn arguments(&self) -> &[String] {
91
        &self.arguments
92
    }
93
}
94

            
95
impl fmt::Display for SimpleAction {
96
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97
        if self.arguments.is_empty() {
98
            write!(f, "{}", self.label)
99
        } else {
100
            write!(f, "{}({})", self.label, self.arguments.iter().format(", "))
101
        }
102
    }
103
}
104

            
105
impl TransitionLabel for SimpleAction {
106
    fn is_tau_label(&self) -> bool {
107
        false
108
    }
109

            
110
    fn tau_label() -> Self {
111
        panic!("SimpleAction does not support tau labels; use LtsMultiAction for that")
112
    }
113

            
114
    fn matches_label(&self, label: &str) -> bool {
115
        self.label == label
116
    }
117

            
118
    fn from_index(i: usize) -> Self {
119
        SimpleAction::new(
120
            char::from_digit(i as u32, 36)
121
                .expect("Radix is less than 37, so should not panic")
122
                .to_string(),
123
            Vec::new(),
124
        )
125
    }
126
}
127

            
128
impl LtsMultiAction<SimpleAction> {
129
    /// Parses a multi-action from the mCRL2 textual `.aut` label format.
130
    ///
131
    /// Handles labels of the form `"a(1, 2) | b | c(x)"`: each `|`-separated
132
    /// part is split into a label name and optional parenthesised argument list.
133
    pub fn from_string(input: &str) -> Result<Self, MercError> {
134
        let mut actions = VecBag::new();
135

            
136
        for part in input.split('|') {
137
            let part = part.trim();
138
            if part.is_empty() {
139
                return Err("Empty action label in multi-action.".into());
140
            }
141

            
142
            if let Some(open) = part.find('(') {
143
                if !part.ends_with(')') {
144
                    return Err(format!("Malformed action with arguments: {part}").into());
145
                }
146
                let label = part[..open].trim().to_string();
147
                let args_str = &part[open + 1..part.len() - 1];
148
                let arguments = args_str.split(',').map(|s| s.trim().to_string()).collect();
149
                actions.insert(SimpleAction::new(label, arguments));
150
            } else {
151
                actions.insert(SimpleAction::new(part.to_string(), Vec::new()));
152
            }
153
        }
154

            
155
        Ok(LtsMultiAction { actions })
156
    }
157
}
158

            
159
impl LtsMultiAction<LtsAction> {
160
    /// Converts the MultiAction into its mCRL2 ATerm representation.
161
300
    pub fn to_mcrl2_aterm(&self) -> Result<ATerm, MercError> {
162
300
        let action_terms: Vec<MCRL2Action> = self
163
300
            .actions
164
300
            .iter()
165
300
            .map(|action| -> Result<MCRL2Action, MercError> {
166
200
                let label_term = MCRL2ActionLabel::new(
167
200
                    ATermString::new(&action.label).copy(),
168
200
                    ATermList::<DataExpression>::empty(),
169
                );
170

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

            
173
200
                Ok(MCRL2Action::new(label_term.copy(), arguments_term))
174
200
            })
175
300
            .collect::<Result<Vec<_>, _>>()?;
176

            
177
300
        let actions_list = ATermList::<MCRL2Action>::from_double_iter(action_terms.into_iter());
178
300
        let time_term: DataExpression = DataVariable::new("@undefined_real").into();
179
300
        Ok(MCRL2TimedMultiAction::new(actions_list, time_term.copy()).into())
180
300
    }
181

            
182
    /// Constructs a MultiAction from an mCRL2 ATerm representation.
183
3667
    pub fn from_mcrl2_aterm(term: ATerm) -> Result<Self, MercError> {
184
3667
        if is_mcrl2_timed_multi_action_symbol(&term.get_head_symbol()) {
185
3667
            let multi_action = MCRL2TimedMultiAction::from(term);
186

            
187
3667
            if is_data_variable(&multi_action.time()) {
188
3667
                let variable: DataVariableRef = multi_action.time().into();
189
3667
                if variable.name() != "@undefined_real" {
190
                    return Err("Timed multi-actions are not supported.".into());
191
3667
                }
192
            } else {
193
                return Err("Timed multi-actions are not supported.".into());
194
            }
195

            
196
3667
            let mut actions = VecBag::new();
197
3667
            for action in multi_action.actions() {
198
3555
                let arguments = action.arguments().to_vec();
199
3555

            
200
3555
                // Convert the label to string
201
3555
                actions.insert(LtsAction {
202
3555
                    label: action.label().name().to_string(),
203
3555
                    arguments,
204
3555
                });
205
3555
            }
206

            
207
3667
            Ok(LtsMultiAction { actions })
208
        } else {
209
            Err(format!("Expected TimedMultAction symbol, got {}.", term).into())
210
        }
211
3667
    }
212
}
213

            
214
#[merc_derive_terms]
215
mod inner {
216
    use merc_aterm::ATermStringRef;
217
    use merc_aterm::Symbol;
218
    use merc_data::DataExpression;
219
    use merc_data::DataExpressionRef;
220
    use merc_macros::merc_ignore;
221

            
222
    use super::*;
223

            
224
    /// Represents a TimedMultiAction in mCRL2, which is a multi-action with an associated time.
225
    #[merc_term(is_mcrl2_timed_multi_action)]
226
    pub struct MCRL2TimedMultiAction {
227
        term: ATerm,
228
    }
229

            
230
    impl MCRL2TimedMultiAction {
231
        /// Creates a new TimedMultiAction with the given actions and time.
232
        #[merc_ignore]
233
300
        pub fn new(actions: ATermList<MCRL2Action>, time: DataExpressionRef<'_>) -> Self {
234
300
            let args: &[ATermRef<'_>] = &[actions.copy(), time.into()];
235
300
            let term = ATerm::with_args(&Symbol::new("TimedMultAct", 2), args);
236
300
            MCRL2TimedMultiAction { term: term.protect() }
237
300
        }
238

            
239
        /// Returns the actions contained in the multi-action.
240
3667
        pub fn actions(&self) -> ATermList<MCRL2Action> {
241
3667
            self.term.arg(0).into()
242
3667
        }
243

            
244
        /// Returns the time at which the multi-action occurs.
245
7334
        pub fn time(&self) -> DataExpressionRef<'_> {
246
7334
            self.term.arg(1).into()
247
7334
        }
248
    }
249

            
250
    #[merc_term(is_mcrl2_action)]
251
    pub struct MCRL2Action {
252
        term: ATerm,
253
    }
254

            
255
    impl MCRL2Action {
256
        /// Creates a new Action with the given label and arguments.
257
        #[merc_ignore]
258
200
        pub fn new(label: MCRL2ActionLabelRef<'_>, arguments: ATermList<DataExpression>) -> Self {
259
200
            let args: &[ATermRef<'_>] = &[label.into(), arguments.copy()];
260
200
            let term = ATerm::with_args(&Symbol::new("Action", 2), args);
261
200
            MCRL2Action { term: term.protect() }
262
200
        }
263

            
264
        /// Returns the label of the action.
265
3555
        pub fn label(&self) -> MCRL2ActionLabelRef<'_> {
266
3555
            self.term.arg(0).into()
267
3555
        }
268

            
269
        /// Returns the data arguments of the action.
270
3555
        pub fn arguments(&self) -> ATermList<DataExpression> {
271
3555
            self.term.arg(1).into()
272
3555
        }
273
    }
274

            
275
    #[merc_term(is_mcrl2_action_label)]
276
    pub struct MCRL2ActionLabel {
277
        term: ATerm,
278
    }
279

            
280
    impl MCRL2ActionLabel {
281
        /// Constructs a new action label with the given name and arguments.
282
        #[merc_ignore]
283
200
        pub fn new(name: ATermStringRef<'_>, args: ATermList<DataExpression>) -> Self {
284
200
            let args: &[ATermRef<'_>] = &[name.into(), args.copy()];
285
200
            let term = ATerm::with_args(&Symbol::new("ActId", 2), args);
286
200
            MCRL2ActionLabel { term: term.protect() }
287
200
        }
288

            
289
        /// Obtain the name of the action label.
290
3555
        pub fn name(&self) -> ATermStringRef<'_> {
291
3555
            self.term.arg(0).into()
292
3555
        }
293
    }
294
}
295

            
296
pub use inner::*;
297

            
298
/// See [`is_mcrl2_timed_multi_action_symbol`]
299
3667
fn is_mcrl2_timed_multi_action<'a, 'b, T: Term<'a, 'b>>(term: &'b T) -> bool {
300
3667
    is_mcrl2_timed_multi_action_symbol(&term.get_head_symbol())
301
3667
}
302

            
303
/// See [`is_mcrl2_action_symbol`]
304
3555
fn is_mcrl2_action<'a, 'b, T: Term<'a, 'b>>(term: &'b T) -> bool {
305
3555
    is_mcrl2_action_symbol(&term.get_head_symbol())
306
3555
}
307

            
308
/// See [`is_mcrl2_action_label_symbol`]
309
3755
fn is_mcrl2_action_label<'a, 'b, T: Term<'a, 'b>>(term: &'b T) -> bool {
310
3755
    is_mcrl2_action_label_symbol(&term.get_head_symbol())
311
3755
}
312

            
313
/// Checks if the given symbol represents a TimedMultiAction in mCRL2.
314
7334
fn is_mcrl2_timed_multi_action_symbol(symbol: &SymbolRef<'_>) -> bool {
315
7334
    symbol.name() == "TimedMultAct" && symbol.arity() == 2
316
7334
}
317

            
318
/// Checks if the given symbol represents an Action in mCRL2.
319
3555
fn is_mcrl2_action_symbol(symbol: &SymbolRef<'_>) -> bool {
320
3555
    symbol.name() == "Action" && symbol.arity() == 2
321
3555
}
322

            
323
/// Checks if the given symbol represents an ActionLabel in mCRL2.
324
3755
fn is_mcrl2_action_label_symbol(symbol: &SymbolRef<'_>) -> bool {
325
3755
    symbol.name() == "ActId" && symbol.arity() == 2
326
3755
}
327

            
328
/// Represents a single action label, with its (data) arguments
329
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
330
pub struct LtsAction {
331
    label: String,
332
    arguments: Vec<DataExpression>,
333
}
334

            
335
impl LtsAction {
336
    /// Creates a new action label with the given name and arguments.
337
18200
    pub fn new(label: String, arguments: Vec<DataExpression>) -> Self {
338
18200
        LtsAction { label, arguments }
339
18200
    }
340

            
341
    /// Returns the name of the action label.
342
    pub fn label(&self) -> &str {
343
        &self.label
344
    }
345

            
346
    /// Returns the data arguments of the action.
347
    pub fn arguments(&self) -> &[DataExpression] {
348
        &self.arguments
349
    }
350
}
351

            
352
impl TransitionLabel for LtsAction {
353
    fn is_tau_label(&self) -> bool {
354
        false
355
    }
356

            
357
    fn tau_label() -> Self {
358
        panic!("LtsAction does not support tau labels. Use LtsMultiAction for that.")
359
    }
360

            
361
    fn matches_label(&self, label: &str) -> bool {
362
        self.label == label
363
    }
364

            
365
18200
    fn from_index(i: usize) -> Self {
366
18200
        LtsAction::new(
367
18200
            char::from_digit(i as u32, 36)
368
18200
                .expect("Radix is less than 37, so should not panic")
369
18200
                .to_string(),
370
18200
            Vec::new(),
371
        )
372
18200
    }
373
}
374

            
375
impl<L: TransitionLabel> TransitionLabel for LtsMultiAction<L> {
376
301
    fn is_tau_label(&self) -> bool {
377
301
        self.actions.is_empty()
378
301
    }
379

            
380
302
    fn tau_label() -> Self {
381
302
        LtsMultiAction { actions: VecBag::new() }
382
302
    }
383

            
384
    fn matches_label(&self, label: &str) -> bool {
385
        // TODO: Is this correct, now a|b matches a?
386
        self.actions.iter().any(|action| action.matches_label(label))
387
    }
388

            
389
1700
    fn from_index(i: usize) -> Self {
390
        // For now we only generate single actions, but these could become multiactions as well
391
1700
        LtsMultiAction {
392
1700
            actions: VecBag::singleton(L::from_index(i)),
393
1700
        }
394
1700
    }
395
}
396

            
397
impl<L: Ord + fmt::Display> fmt::Display for LtsMultiAction<L> {
398
2379
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
399
2379
        if self.actions.is_empty() {
400
201
            write!(f, "τ")
401
        } else {
402
2178
            write!(f, "{}", self.actions.iter().format("|"))
403
        }
404
2379
    }
405
}
406

            
407
impl fmt::Display for LtsAction {
408
21736
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
409
21736
        if self.arguments.is_empty() {
410
18460
            write!(f, "{}", self.label)
411
        } else {
412
3276
            write!(f, "{}({})", self.label, self.arguments.iter().format(", "))
413
        }
414
21736
    }
415
}
416

            
417
impl<L: Ord + fmt::Display> fmt::Debug for LtsMultiAction<L> {
418
600
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
419
        // Use the debug format to print the display format
420
600
        write!(f, "{}", self)
421
600
    }
422
}