1
use std::fmt;
2
use std::hash::Hash;
3

            
4
use delegate::delegate;
5

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

            
29
use crate::TransitionLabel;
30

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

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

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

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

            
53
1
                let label = &part[..open_paren_index].trim();
54
1
                let args_str = &part[open_paren_index + 1..part.len() - 1];
55
2
                let arguments: Vec<String> = args_str.split(',').map(|s| s.trim().to_string()).collect();
56
1
                actions.insert(Action {
57
1
                    label: label.to_string(),
58
1
                    arguments,
59
1
                });
60
2
            } else {
61
2
                let label = part.trim();
62
2
                actions.insert(Action {
63
2
                    label: label.to_string(),
64
2
                    arguments: Vec::new(),
65
2
                });
66
2
            }
67
        }
68

            
69
1
        Ok(MultiAction { actions })
70
1
    }
71

            
72
    /// Converts the MultiAction into its mCRL2 ATerm representation.
73
300
    pub fn to_mcrl2_aterm(&self) -> Result<ATerm, MercError> {
74
300
        let action_terms: Vec<MCRL2Action> = self
75
300
            .actions
76
300
            .iter()
77
300
            .map(|action| {
78
200
                let label_term = MCRL2ActionLabel::new(
79
200
                    ATermString::new(&action.label).copy(),
80
200
                    ATermList::<DataExpression>::empty(),
81
                );
82
200
                let arguments_term = ATermList::<DataExpression>::empty(); // TODO: Convert arguments if needed
83

            
84
200
                MCRL2Action::new(label_term.copy(), arguments_term)
85
200
            })
86
300
            .collect();
87

            
88
300
        let actions_list = ATermList::<MCRL2Action>::from_double_iter(action_terms.into_iter());
89
300
        let time_term: DataExpression = DataVariable::new("@undefined_real").into();
90
300
        Ok(MCRL2TimedMultiAction::new(actions_list, time_term.copy()).into())
91
300
    }
92

            
93
    /// Constructs a MultiAction from an mCRL2 ATerm representation.
94
319
    pub fn from_mcrl2_aterm(term: ATerm) -> Result<Self, MercError> {
95
319
        if is_mcrl2_timed_multi_action_symbol(&term.get_head_symbol()) {
96
319
            let multi_action = MCRL2TimedMultiAction::from(term);
97

            
98
319
            if is_data_variable(&multi_action.time()) {
99
319
                let variable: DataVariableRef = multi_action.time().into();
100
319
                if variable.name() != "@undefined_real" {
101
                    return Err("Timed multi-actions are not supported.".into());
102
319
                }
103
            } else {
104
                return Err("Timed multi-actions are not supported.".into());
105
            }
106

            
107
319
            let mut actions = VecSet::new();
108
319
            for action in multi_action.actions() {
109
219
                actions.insert(Action {
110
219
                    label: action.label().name().to_string(),
111
219
                    arguments: Vec::new(), // TODO: Extract arguments if needed
112
219
                });
113
219
            }
114

            
115
319
            Ok(MultiAction { actions })
116
        } else {
117
            Err(format!("Expected TimedMultAction symbol, got {}.", term).into())
118
        }
119
319
    }
120
}
121

            
122
#[merc_derive_terms]
123
mod inner {
124
    use merc_aterm::ATermStringRef;
125
    use merc_aterm::Symbol;
126
    use merc_data::DataExpression;
127
    use merc_data::DataExpressionRef;
128
    use merc_macros::merc_ignore;
129

            
130
    use super::*;
131

            
132
    /// Represents a TimedMultiAction in mCRL2, which is a multi-action with an associated time.
133
    #[merc_term(is_mcrl2_timed_multi_action)]
134
    pub struct MCRL2TimedMultiAction {
135
        term: ATerm,
136
    }
137

            
138
    impl MCRL2TimedMultiAction {
139
        /// Creates a new TimedMultiAction with the given actions and time.
140
        #[merc_ignore]
141
300
        pub fn new(actions: ATermList<MCRL2Action>, time: DataExpressionRef<'_>) -> Self {
142
300
            let args: &[ATermRef<'_>] = &[actions.copy(), time.into()];
143
300
            let term = ATerm::with_args(&Symbol::new("TimedMultAct", 2), args);
144
300
            MCRL2TimedMultiAction { term: term.protect() }
145
300
        }
146

            
147
        /// Returns the actions contained in the multi-action.
148
319
        pub fn actions(&self) -> ATermList<MCRL2Action> {
149
319
            self.term.arg(0).into()
150
319
        }
151

            
152
        /// Returns the time at which the multi-action occurs.
153
638
        pub fn time(&self) -> DataExpressionRef<'_> {
154
638
            self.term.arg(1).into()
155
638
        }
156
    }
157

            
158
    #[merc_term(is_mcrl2_action)]
159
    pub struct MCRL2Action {
160
        term: ATerm,
161
    }
162

            
163
    impl MCRL2Action {
164
        /// Creates a new Action with the given label and arguments.
165
        #[merc_ignore]
166
200
        pub fn new(label: MCRL2ActionLabelRef<'_>, arguments: ATermList<DataExpression>) -> Self {
167
200
            let args: &[ATermRef<'_>] = &[label.into(), arguments.copy()];
168
200
            let term = ATerm::with_args(&Symbol::new("Action", 2), args);
169
200
            MCRL2Action { term: term.protect() }
170
200
        }
171

            
172
        /// Returns the label of the action.
173
219
        pub fn label(&self) -> MCRL2ActionLabelRef<'_> {
174
219
            self.term.arg(0).into()
175
219
        }
176

            
177
        /// Returns the data arguments of the action.
178
        pub fn arguments(&self) -> ATermList<DataExpression> {
179
            self.term.arg(1).into()
180
        }
181
    }
182

            
183
    #[merc_term(is_mcrl2_action_label)]
184
    pub struct MCRL2ActionLabel {
185
        term: ATerm,
186
    }
187

            
188
    impl MCRL2ActionLabel {
189
        /// Constructs a new action label with the given name and arguments.
190
        #[merc_ignore]
191
200
        pub fn new(name: ATermStringRef<'_>, args: ATermList<DataExpression>) -> Self {
192
200
            let args: &[ATermRef<'_>] = &[name.into(), args.copy()];
193
200
            let term = ATerm::with_args(&Symbol::new("ActId", 2), args);
194
200
            MCRL2ActionLabel { term: term.protect() }
195
200
        }
196

            
197
        /// Obtain the name of the action label.
198
219
        pub fn name(&self) -> ATermStringRef<'_> {
199
219
            self.term.arg(0).into()
200
219
        }
201
    }
202
}
203

            
204
pub use inner::*;
205

            
206
/// See [`is_mcrl2_timed_multi_action_symbol`]
207
319
fn is_mcrl2_timed_multi_action<'a, 'b>(term: &'b impl Term<'a, 'b>) -> bool {
208
319
    is_mcrl2_timed_multi_action_symbol(&term.get_head_symbol())
209
319
}
210

            
211
/// See [`is_mcrl2_action_symbol`]
212
219
fn is_mcrl2_action<'a, 'b>(term: &'b impl Term<'a, 'b>) -> bool {
213
219
    is_mcrl2_action_symbol(&term.get_head_symbol())
214
219
}
215

            
216
/// See [`is_mcrl2_action_label_symbol`]
217
419
fn is_mcrl2_action_label<'a, 'b>(term: &'b impl Term<'a, 'b>) -> bool {
218
419
    is_mcrl2_action_label_symbol(&term.get_head_symbol())
219
419
}
220

            
221
/// Checks if the given symbol represents a TimedMultiAction in mCRL2.
222
638
fn is_mcrl2_timed_multi_action_symbol(symbol: &SymbolRef<'_>) -> bool {
223
638
    symbol.name() == "TimedMultAct" && symbol.arity() == 2
224
638
}
225

            
226
/// Checks if the given symbol represents an Action in mCRL2.
227
219
fn is_mcrl2_action_symbol(symbol: &SymbolRef<'_>) -> bool {
228
219
    symbol.name() == "Action" && symbol.arity() == 2
229
219
}
230

            
231
/// Checks if the given symbol represents an ActionLabel in mCRL2.
232
419
fn is_mcrl2_action_label_symbol(symbol: &SymbolRef<'_>) -> bool {
233
419
    symbol.name() == "ActId" && symbol.arity() == 2
234
419
}
235

            
236
/// Represents a single action label, with its (data) arguments
237
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
238
pub struct Action {
239
    label: String,
240
    arguments: Vec<String>,
241
}
242

            
243
impl Action {
244
    /// Creates a new action label with the given name and arguments.
245
200
    pub fn new(label: String, arguments: Vec<String>) -> Self {
246
200
        Action { label, arguments }
247
200
    }
248
}
249

            
250
impl TransitionLabel for MultiAction {
251
100
    fn is_tau_label(&self) -> bool {
252
100
        self.actions.is_empty()
253
100
    }
254

            
255
402
    fn tau_label() -> Self {
256
402
        MultiAction { actions: VecSet::new() }
257
402
    }
258

            
259
    fn matches_label(&self, label: &str) -> bool {
260
        // TODO: Is this correct, now a|b matches a?
261
        self.actions.iter().any(|action| action.label == label)
262
    }
263

            
264
200
    fn from_index(i: usize) -> Self {
265
        // For now we only generate single actions, but these could become multiactions as well
266
200
        MultiAction {
267
200
            actions: VecSet::singleton(Action::new(
268
200
                char::from_digit(i as u32, 36)
269
200
                    .expect("Radix is less than 37, so should not panic")
270
200
                    .to_string(),
271
200
                Vec::new(),
272
200
            )),
273
200
        }
274
200
    }
275
}
276

            
277
impl fmt::Display for MultiAction {
278
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279
        if self.actions.is_empty() {
280
            write!(f, "τ")
281
        } else {
282
            write!(f, "{{{}}}", self.actions.iter().format("|"))
283
        }
284
    }
285
}
286

            
287
impl fmt::Display for Action {
288
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289
        if self.arguments.is_empty() {
290
            write!(f, "{}", self.label)
291
        } else {
292
            let args_str = self.arguments.join(", ");
293
            write!(f, "{}({})", self.label, args_str)
294
        }
295
    }
296
}
297

            
298
impl fmt::Debug for MultiAction {
299
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300
        // Use the debug format to print the display format
301
        write!(f, "{}", self)
302
    }
303
}
304

            
305
#[cfg(test)]
306
mod tests {
307
    use crate::MultiAction;
308

            
309
    #[test]
310
1
    fn test_multi_action_parse_string() {
311
1
        let action = MultiAction::from_string("a | b(1, 2) | c").unwrap();
312

            
313
1
        assert_eq!(action.actions.len(), 3);
314
1
        assert!(
315
1
            action
316
1
                .actions
317
1
                .iter()
318
1
                .any(|act| act.label == "a" && act.arguments.is_empty())
319
        );
320
1
        assert!(
321
1
            action
322
1
                .actions
323
1
                .iter()
324
2
                .any(|act| act.label == "b" && act.arguments == vec!["1", "2"])
325
        );
326
1
        assert!(
327
1
            action
328
1
                .actions
329
1
                .iter()
330
3
                .any(|act| act.label == "c" && act.arguments.is_empty())
331
        );
332
1
    }
333
}