1
use indoc::indoc;
2
use merc_syntax::UntypedDataSpecification;
3
use pest::Parser;
4

            
5
use merc_syntax::Mcrl2Parser;
6
use merc_syntax::Rule;
7
use merc_syntax::UntypedProcessSpecification;
8
use merc_syntax::UntypedStateFrmSpec;
9
use merc_syntax::parse_sortexpr;
10
use merc_utilities::test_logger;
11

            
12
/// `DataExprIn`, `DataExprIntDiv`, and `DataExprMod` used to be plain string
13
/// matches without a negative lookahead (`!Id`). This meant that identifiers
14
/// whose names *start* with `in`, `div`, or `mod` were incorrectly tokenised
15
/// as a keyword followed by the rest of the identifier.
16
#[test]
17
1
fn keywords_are_not_prefix_of_identifiers() {
18
    // `index` starts with `in` — must parse as a plain identifier.
19
1
    UntypedProcessSpecification::parse("map index: Bool -> Bool;")
20
1
        .expect("`index` must not be split into keyword `in` + `dex`");
21
    // `divides` starts with `div`.
22
1
    UntypedProcessSpecification::parse("map divides: Bool -> Bool;")
23
1
        .expect("`divides` must not be split into keyword `div` + `ides`");
24
    // `modern` starts with `mod`.
25
1
    UntypedProcessSpecification::parse("map modern: Bool -> Bool;")
26
1
        .expect("`modern` must not be split into keyword `mod` + `ern`");
27
1
}
28

            
29
/// `Number` used to be a non-atomic rule (`{ ASCII_DIGIT+ }`), which in pest
30
/// causes implicit whitespace to be absorbed *after* the digits. That meant
31
/// tokens like `"3 "` could match `Number`, and boundary tests such as
32
/// `"3 div 2"` could mis-parse because the parser consumed the trailing space
33
/// as part of the number and left `div` unrecognised as an operator.
34
#[test]
35
1
fn number_token_does_not_absorb_whitespace() {
36
    // A Number followed immediately by a keyword must still parse correctly.
37
    // Parsing fails on the leading `val(...)` wrapper in a data-specification
38
    // context, so use a minimal expression that forces a Number-then-keyword
39
    // boundary: `3 div 2`.
40
1
    let parsed = Mcrl2Parser::parse(Rule::Number, "3 extra");
41
    // The Number rule must match exactly the digit run, not consume the space.
42
1
    assert!(parsed.is_ok());
43
1
    assert_eq!(
44
1
        parsed.unwrap().as_str(),
45
        "3",
46
        "Number must not consume trailing whitespace"
47
    );
48
1
}
49

            
50
#[test]
51
1
fn test_parse_ifthen() {
52
1
    let expr = "init a -> b <> b;";
53

            
54
1
    match UntypedProcessSpecification::parse(expr) {
55
1
        Ok(result) => {
56
1
            println!("{}", result);
57
1
        }
58
        Err(e) => {
59
            panic!("Failed to parse expression: {}", e);
60
        }
61
    }
62
1
}
63

            
64
#[test]
65
1
fn test_parse_keywords() {
66
1
    let expr = "map or : Boolean # Boolean -> Boolean ;";
67

            
68
1
    match UntypedProcessSpecification::parse(expr) {
69
1
        Ok(result) => {
70
1
            println!("{}", result);
71
1
        }
72
        Err(e) => {
73
            panic!("Failed to parse expression: {}", e);
74
        }
75
    }
76
1
}
77

            
78
#[test]
79
1
fn test_parse_sort_spec() {
80
1
    let sort_spec = indoc! {"
81
1
        sort D = Bool -> Int -> Bool;
82
1
        
83
1

            
84
1
        % Test
85
1
        F     = struct d1 | d2;
86
1
        Error = struct e;
87
1
    "};
88

            
89
1
    match UntypedProcessSpecification::parse(sort_spec) {
90
1
        Ok(result) => {
91
1
            println!("{}", result);
92
1
        }
93
        Err(e) => {
94
            panic!("Failed to parse expression: {}", e);
95
        }
96
    }
97
1
}
98

            
99
#[test]
100
1
fn test_parse_regular_expression() {
101
1
    let spec = "[true++false]true";
102

            
103
1
    match UntypedStateFrmSpec::parse(spec) {
104
1
        Ok(result) => {
105
1
            println!("{}", result);
106
1
        }
107
        Err(e) => {
108
            panic!("Failed to parse expression: {}", e);
109
        }
110
    }
111
1
}
112

            
113
#[test]
114
1
fn test_parse_procexpr() {
115
1
    test_logger();
116

            
117
    use indoc::indoc;
118

            
119
1
    let spec: &str = indoc! {"init
120
1
        true -> delta <> delta;
121
1
    "};
122

            
123
1
    match UntypedProcessSpecification::parse(spec) {
124
1
        Ok(result) => {
125
1
            println!("{}", result);
126
1
        }
127
        Err(e) => {
128
            panic!("Failed to parse expression: {}", e);
129
        }
130
    }
131
1
}
132

            
133
#[test]
134
1
fn test_parse_statefrm() {
135
1
    test_logger();
136

            
137
    use indoc::indoc;
138

            
139
1
    let spec: &str = indoc! {"<b> <a> exists b: Bool . b && !b"};
140

            
141
1
    match UntypedStateFrmSpec::parse(spec) {
142
1
        Ok(result) => {
143
1
            println!("{}", result);
144
1
        }
145
        Err(e) => {
146
            panic!("Failed to parse expression: {}", e);
147
        }
148
    }
149
1
}
150

            
151
#[test]
152
1
fn test_sort_precedence() {
153
1
    let term = "Bool # Int -> Int -> Bool";
154

            
155
1
    match Mcrl2Parser::parse(Rule::SortExpr, term) {
156
1
        Ok(result) => {
157
1
            print!("{}", parse_sortexpr(result).unwrap());
158
1
        }
159
        Err(e) => {
160
            panic!("{}", e);
161
        }
162
    }
163
1
}
164

            
165
#[test]
166
1
fn test_bool_spec() {
167
1
    match UntypedDataSpecification::parse(include_str!("../spec/bool.mcrl2")) {
168
1
        Ok(result) => {
169
1
            println!("{}", result);
170
1
        }
171
        Err(e) => {
172
            panic!("Failed to parse expression: {}", e);
173
        }
174
    }
175
1
}
176

            
177
#[test]
178
1
fn test_int_spec() {
179
1
    match UntypedDataSpecification::parse(include_str!("../spec/int.mcrl2")) {
180
1
        Ok(result) => {
181
1
            println!("{}", result);
182
1
        }
183
        Err(e) => {
184
            panic!("Failed to parse expression: {}", e);
185
        }
186
    }
187
1
}
188

            
189
#[test]
190
1
fn test_nat_spec() {
191
1
    match UntypedDataSpecification::parse(include_str!("../spec/nat.mcrl2")) {
192
1
        Ok(result) => {
193
1
            println!("{}", result);
194
1
        }
195
        Err(e) => {
196
            panic!("Failed to parse expression: {}", e);
197
        }
198
    }
199
1
}
200

            
201
#[test]
202
1
fn test_pos_spec() {
203
1
    match UntypedDataSpecification::parse(include_str!("../spec/pos.mcrl2")) {
204
1
        Ok(result) => {
205
1
            println!("{}", result);
206
1
        }
207
        Err(e) => {
208
            panic!("Failed to parse expression: {}", e);
209
        }
210
    }
211
1
}
212

            
213
#[test]
214
1
fn test_real_spec() {
215
1
    match UntypedDataSpecification::parse(include_str!("../spec/real.mcrl2")) {
216
1
        Ok(result) => {
217
1
            println!("{}", result);
218
1
        }
219
        Err(e) => {
220
            panic!("Failed to parse expression: {}", e);
221
        }
222
    }
223
1
}
224

            
225
#[test]
226
1
fn test_list_spec() {
227
1
    match UntypedDataSpecification::parse(include_str!("../spec/list.mcrl2")) {
228
1
        Ok(result) => {
229
1
            println!("{}", result);
230
1
        }
231
        Err(e) => {
232
            panic!("Failed to parse expression: {}", e);
233
        }
234
    }
235
1
}
236

            
237
#[test]
238
1
fn test_set_spec() {
239
1
    match UntypedDataSpecification::parse(include_str!("../spec/set.mcrl2")) {
240
1
        Ok(result) => {
241
1
            println!("{}", result);
242
1
        }
243
        Err(e) => {
244
            panic!("Failed to parse expression: {}", e);
245
        }
246
    }
247
1
}
248

            
249
#[test]
250
1
fn test_fset_spec() {
251
1
    match UntypedDataSpecification::parse(include_str!("../spec/fset.mcrl2")) {
252
1
        Ok(result) => {
253
1
            println!("{}", result);
254
1
        }
255
        Err(e) => {
256
            panic!("Failed to parse expression: {}", e);
257
        }
258
    }
259
1
}
260

            
261
#[test]
262
1
fn test_bag_spec() {
263
1
    match UntypedDataSpecification::parse(include_str!("../spec/bag.mcrl2")) {
264
1
        Ok(result) => {
265
1
            println!("{}", result);
266
1
        }
267
        Err(e) => {
268
            panic!("Failed to parse expression: {}", e);
269
        }
270
    }
271
1
}
272

            
273
#[test]
274
1
fn test_fbag_spec() {
275
1
    match UntypedDataSpecification::parse(include_str!("../spec/fbag.mcrl2")) {
276
1
        Ok(result) => {
277
1
            println!("{}", result);
278
1
        }
279
        Err(e) => {
280
            panic!("Failed to parse expression: {}", e);
281
        }
282
    }
283
1
}