1
use super::ParseContext;
2
use super::util::{get_expr_tree, query_toplevel};
3
use crate::diagnostics::source_map::SourceMap;
4
use crate::errors::FatalParseError;
5
use crate::expression::parse_expression;
6
use crate::util::TypecheckingContext;
7
use crate::util::node_is_expression;
8
use conjure_cp_core::ast::{Expression, SymbolTablePtr};
9
use std::collections::BTreeMap;
10
#[allow(unused)]
11
use uniplate::Uniplate;
12

            
13
21
pub fn parse_expr(
14
21
    src: &str,
15
21
    symbols_ptr: SymbolTablePtr,
16
21
) -> Result<Option<Expression>, FatalParseError> {
17
21
    let exprs = parse_exprs(src, symbols_ptr)?;
18
21
    if exprs.len() != 1 {
19
1
        return Ok(None);
20
20
    }
21
20
    Ok(Some(exprs[0].clone()))
22
21
}
23

            
24
22
pub fn parse_exprs(
25
22
    src: &str,
26
22
    symbols_ptr: SymbolTablePtr,
27
22
) -> Result<Vec<Expression>, FatalParseError> {
28
22
    let Some((tree, source_code)) = get_expr_tree(src) else {
29
        return Ok(Vec::new());
30
    };
31

            
32
22
    let root = tree.root_node();
33
22
    let mut source_map = SourceMap::default();
34
22
    let mut decl_spans = BTreeMap::new();
35
22
    let mut errors = Vec::new();
36
22
    let mut ctx = ParseContext::new(
37
22
        &source_code,
38
22
        &root,
39
22
        Some(symbols_ptr),
40
22
        &mut errors,
41
22
        &mut source_map,
42
22
        &mut decl_spans,
43
22
    );
44
22
    let mut ans = Vec::new();
45
22
    for expr in query_toplevel(&root, &node_is_expression) {
46
23
        ctx.typechecking_context = TypecheckingContext::Unknown;
47
23
        ctx.inner_typechecking_context = TypecheckingContext::Unknown;
48
23
        let Some(expr) = parse_expression(&mut ctx, expr)? else {
49
1
            continue;
50
        };
51
22
        ans.push(expr);
52
    }
53
22
    Ok(ans)
54
22
}
55

            
56
mod test {
57
    #[allow(unused)]
58
    use super::{parse_expr, parse_exprs};
59
    #[allow(unused)]
60
    use conjure_cp_core::ast::SymbolTablePtr;
61
    #[allow(unused)]
62
    use conjure_cp_core::ast::{
63
        Atom, DeclarationPtr, Domain, Expression, Literal, Metadata, Moo, Name, ReturnType,
64
        SymbolTable, Typeable,
65
    };
66
    #[allow(unused)]
67
    use std::collections::HashMap;
68
    #[allow(unused)]
69
    use std::sync::Arc;
70
    #[allow(unused)]
71
    use tree_sitter::Range;
72

            
73
    #[test]
74
1
    pub fn test_parse_constant() {
75
1
        let symbols = SymbolTablePtr::new();
76

            
77
1
        assert_eq!(
78
1
            parse_expr("42", symbols.clone()).unwrap().unwrap(),
79
1
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(42)))
80
        );
81
1
        assert_eq!(
82
1
            parse_expr("true", symbols.clone()).unwrap().unwrap(),
83
1
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true)))
84
        );
85
1
        assert_eq!(
86
1
            parse_expr("false", symbols).unwrap().unwrap(),
87
1
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(false)))
88
        )
89
1
    }
90

            
91
    #[test]
92
1
    pub fn test_parse_expressions() {
93
1
        let src = "x >= 5, y = a / 2";
94
1
        let symbols = SymbolTablePtr::new();
95
1
        let x = DeclarationPtr::new_find(
96
1
            Name::User("x".into()),
97
1
            Domain::int(vec![conjure_cp_core::ast::Range::Bounded(0, 10)]),
98
1
        );
99

            
100
1
        let y = DeclarationPtr::new_find(
101
1
            Name::User("y".into()),
102
1
            Domain::int(vec![conjure_cp_core::ast::Range::Bounded(0, 10)]),
103
1
        );
104

            
105
1
        let a = DeclarationPtr::new_find(
106
1
            Name::User("a".into()),
107
1
            Domain::int(vec![conjure_cp_core::ast::Range::Bounded(0, 10)]),
108
1
        );
109

            
110
        // Clone the Rc when inserting!
111
1
        symbols
112
1
            .write()
113
1
            .insert(x.clone())
114
1
            .expect("x should not exist in the symbol-table yet, so we should be able to add it");
115

            
116
1
        symbols
117
1
            .write()
118
1
            .insert(y.clone())
119
1
            .expect("y should not exist in the symbol-table yet, so we should be able to add it");
120

            
121
1
        symbols
122
1
            .write()
123
1
            .insert(a.clone())
124
1
            .expect("a should not exist in the symbol-table yet, so we should be able to add it");
125

            
126
1
        let exprs = parse_exprs(src, symbols).unwrap();
127
1
        assert_eq!(exprs.len(), 2);
128

            
129
1
        assert_eq!(
130
1
            exprs[0],
131
1
            Expression::Geq(
132
1
                Metadata::new(),
133
1
                Moo::new(Expression::Atomic(Metadata::new(), Atom::new_ref(x))),
134
1
                Moo::new(Expression::Atomic(Metadata::new(), 5.into()))
135
1
            )
136
        );
137

            
138
1
        assert_eq!(
139
1
            exprs[1],
140
1
            Expression::Eq(
141
1
                Metadata::new(),
142
1
                Moo::new(Expression::Atomic(Metadata::new(), Atom::new_ref(y))),
143
1
                Moo::new(Expression::UnsafeDiv(
144
1
                    Metadata::new(),
145
1
                    Moo::new(Expression::Atomic(Metadata::new(), Atom::new_ref(a))),
146
1
                    Moo::new(Expression::Atomic(Metadata::new(), 2.into()))
147
1
                ))
148
1
            )
149
        );
150
1
    }
151

            
152
    #[test]
153
1
    fn bars_distinguish_set_cardinality_from_integer_absolute_value() {
154
1
        let symbols = SymbolTablePtr::new();
155
1
        let set = DeclarationPtr::new_find(
156
1
            Name::User("s".into()),
157
1
            Domain::set(
158
1
                conjure_cp_core::ast::SetAttr::new_max_size(2),
159
1
                Domain::int(vec![conjure_cp_core::ast::Range::Bounded(1, 2)]),
160
1
            ),
161
1
        );
162
1
        let integer = DeclarationPtr::new_find(
163
1
            Name::User("x".into()),
164
1
            Domain::int(vec![conjure_cp_core::ast::Range::Bounded(-2, 2)]),
165
1
        );
166
1
        symbols.write().insert(set).unwrap();
167
1
        symbols.write().insert(integer).unwrap();
168

            
169
1
        let set_expr = parse_expr("|s| = 1", symbols.clone()).unwrap().unwrap();
170
1
        let Expression::Eq(_, set_left, _) = set_expr else {
171
            panic!("expected set cardinality comparison");
172
        };
173
1
        assert!(matches!(*set_left, Expression::Card(..)));
174

            
175
1
        let int_expr = parse_expr("|x| = 1", symbols).unwrap().unwrap();
176
1
        let Expression::Eq(_, int_left, _) = int_expr else {
177
            panic!("expected integer absolute-value comparison");
178
        };
179
1
        assert!(matches!(*int_left, Expression::Abs(..)));
180
1
    }
181

            
182
    #[test]
183
1
    pub fn test_parse_expression_annotations() {
184
1
        let symbols = SymbolTablePtr::new();
185
1
        let x = DeclarationPtr::new_find(
186
1
            Name::User("x".into()),
187
1
            Domain::int(vec![conjure_cp_core::ast::Range::Bounded(0, 10)]),
188
1
        );
189
1
        symbols
190
1
            .write()
191
1
            .insert(x.clone())
192
1
            .expect("x should not exist in the symbol-table yet, so we should be able to add it");
193

            
194
1
        let domain_annotation = parse_expr("x : int(1..3)", symbols.clone())
195
1
            .unwrap()
196
1
            .unwrap();
197
1
        assert!(matches!(
198
1
            domain_annotation,
199
            Expression::DomainAnnotation(_, _, _)
200
        ));
201
1
        assert_eq!(domain_annotation.to_string(), "x : int(1..3)");
202

            
203
1
        let type_annotation = parse_expr("x :: int", symbols.clone()).unwrap().unwrap();
204
1
        assert!(matches!(
205
1
            type_annotation,
206
            Expression::TypeAnnotation(_, _, _)
207
        ));
208
1
        assert_eq!(type_annotation.return_type(), ReturnType::Int);
209
1
        assert_eq!(type_annotation.to_string(), "x :: int");
210
1
    }
211

            
212
    #[test]
213
1
    pub fn test_parse_set_representation_preference() {
214
1
        let symbols = SymbolTablePtr::new();
215
1
        let x = DeclarationPtr::new_find(
216
1
            Name::User("x".into()),
217
1
            Domain::set(
218
1
                conjure_cp_core::ast::SetAttr::new_max_size(3),
219
1
                Domain::int(vec![conjure_cp_core::ast::Range::Bounded(1, 4)]),
220
1
            ),
221
1
        );
222
1
        symbols.write().insert(x).unwrap();
223

            
224
1
        let find_domain = parse_expr("x : set (representation packed) of int", symbols.clone())
225
1
            .unwrap()
226
1
            .unwrap();
227
1
        let Expression::DomainAnnotation(_, _, domain) = find_domain else {
228
            panic!("expected domain annotation");
229
        };
230
1
        assert_eq!(domain.representation_preference(), Some("packed"));
231
1
        assert_eq!(domain.to_string(), "set (representation packed) of int");
232

            
233
1
        let type_ann = parse_expr(
234
1
            "x :: set (representation occurrence) of int",
235
1
            symbols.clone(),
236
1
        )
237
1
        .unwrap()
238
1
        .unwrap();
239
1
        assert_eq!(
240
1
            type_ann.to_string(),
241
1
            "x :: set (representation occurrence) of int"
242
        );
243
1
        let Expression::TypeAnnotation(_, _, ty_domain) = type_ann else {
244
            panic!("expected type annotation");
245
        };
246
1
        assert_eq!(ty_domain.representation_preference(), Some("occurrence"));
247

            
248
1
        let nested = parse_expr(
249
1
            "x : set (representation explicit) of set (representation occurrence) of int",
250
1
            symbols,
251
1
        )
252
1
        .unwrap()
253
1
        .unwrap();
254
1
        let Expression::DomainAnnotation(_, _, nested_domain) = nested else {
255
            panic!("expected domain annotation");
256
        };
257
1
        assert_eq!(nested_domain.representation_preference(), Some("explicit"));
258
1
        let (_, inner) = nested_domain.as_set().unwrap();
259
1
        assert_eq!(inner.representation_preference(), Some("occurrence"));
260
1
        assert_eq!(
261
1
            nested_domain.to_string(),
262
1
            "set (representation explicit) of set (representation occurrence) of int"
263
        );
264
1
    }
265

            
266
    #[test]
267
1
    pub fn test_parse_nested_mset_representation_preference() {
268
1
        let symbols = SymbolTablePtr::new();
269
1
        let x = DeclarationPtr::new_find(Name::User("x".into()), Domain::bool());
270
1
        symbols.write().insert(x).unwrap();
271

            
272
1
        let annotation = parse_expr(
273
1
            "x : matrix indexed by [int(1..2)] of record { before: mset (representation repetition, maxSize 6) of int(1..9) }",
274
1
            symbols,
275
1
        )
276
1
        .unwrap()
277
1
        .unwrap();
278
1
        let Expression::DomainAnnotation(_, _, domain) = annotation else {
279
            panic!("expected domain annotation");
280
        };
281

            
282
1
        assert!(domain.has_representation_preference());
283
1
        let (record, _) = domain.as_matrix().expect("expected matrix domain");
284
1
        let fields = record.as_record().expect("expected record domain");
285
1
        let before = &fields
286
1
            .iter()
287
1
            .find(|field| field.name == Name::User("before".into()))
288
1
            .expect("expected before field")
289
            .value;
290
1
        assert_eq!(before.representation_preference(), Some("repetition"));
291
1
        assert_eq!(
292
1
            domain.to_string(),
293
1
            "matrix indexed by [int(1..2)] of record {before: mset (representation repetition, maxSize 6) of int(1..9)}"
294
        );
295
1
    }
296

            
297
    #[test]
298
1
    pub fn test_expression_annotations_bind_tighter_than_addition() {
299
1
        let symbols = SymbolTablePtr::new();
300
1
        let x = DeclarationPtr::new_find(
301
1
            Name::User("x".into()),
302
1
            Domain::int(vec![conjure_cp_core::ast::Range::Bounded(0, 10)]),
303
1
        );
304
1
        symbols
305
1
            .write()
306
1
            .insert(x)
307
1
            .expect("x should not exist in the symbol-table yet, so we should be able to add it");
308

            
309
1
        let expr = parse_expr("x + 1 : int", symbols).unwrap().unwrap();
310
1
        let Expression::Sum(_, terms) = expr else {
311
            panic!("expected a sum expression");
312
        };
313
1
        let terms = (*terms).clone().unwrap_list().unwrap();
314
1
        assert_eq!(terms.len(), 2);
315
1
        assert!(matches!(terms[1], Expression::DomainAnnotation(_, _, _)));
316
1
        assert_eq!(terms[1].to_string(), "1 : int");
317

            
318
1
        let symbols = SymbolTablePtr::new();
319
1
        let x = DeclarationPtr::new_find(
320
1
            Name::User("x".into()),
321
1
            Domain::int(vec![conjure_cp_core::ast::Range::Bounded(0, 10)]),
322
1
        );
323
1
        symbols
324
1
            .write()
325
1
            .insert(x)
326
1
            .expect("x should not exist in the symbol-table yet, so we should be able to add it");
327

            
328
1
        let expr = parse_expr("(x + 1) : int", symbols).unwrap().unwrap();
329
1
        let Expression::DomainAnnotation(_, inner, _) = expr else {
330
            panic!("expected a domain annotation");
331
        };
332
1
        assert!(matches!(*inner, Expression::Sum(_, _)));
333
1
    }
334

            
335
    #[test]
336
1
    pub fn test_parse_in_with_repr_annotation() {
337
1
        let symbols = SymbolTablePtr::new();
338
1
        let x = DeclarationPtr::new_find(
339
1
            Name::User("x".into()),
340
1
            Domain::set(
341
1
                conjure_cp_core::ast::SetAttr::new_max_size(3),
342
1
                Domain::int(vec![conjure_cp_core::ast::Range::Bounded(1, 4)]),
343
1
            ),
344
1
        );
345
1
        symbols.write().insert(x).unwrap();
346

            
347
1
        let expr = parse_expr(
348
1
            "1 in x :: set (representation packed) of int",
349
1
            symbols.clone(),
350
1
        )
351
1
        .unwrap()
352
1
        .unwrap();
353
1
        println!("no parens: {expr}");
354
1
        assert!(
355
1
            matches!(expr, Expression::In(_, _, _)),
356
            "expected In, got {expr:?}"
357
        );
358
1
        let Expression::In(_, _, rhs) = &expr else {
359
            unreachable!()
360
        };
361
1
        assert!(
362
1
            matches!(rhs.as_ref(), Expression::TypeAnnotation(_, _, _)),
363
            "expected type annotation on in-rhs, got {rhs:?}"
364
        );
365

            
366
1
        let expr = parse_expr("1 in (x :: set (representation packed) of int)", symbols)
367
1
            .unwrap()
368
1
            .unwrap();
369
1
        println!("with parens: {expr}");
370
1
        let Expression::In(_, _, rhs) = expr else {
371
            panic!("expected In, got {expr:?}");
372
        };
373
        // Parentheses may wrap as Atomic-ish structure; accept TypeAnnotation directly or inside.
374
1
        let rhs_str = rhs.to_string();
375
1
        assert!(
376
1
            rhs_str.contains("set (representation packed)")
377
                || matches!(rhs.as_ref(), Expression::TypeAnnotation(_, _, _)),
378
            "expected annotated set on rhs, got {rhs:?}"
379
        );
380
1
    }
381
}