Skip to main content

conjure_cp_essence_parser/parser/
parse_exprs.rs

1use super::ParseContext;
2use super::util::{get_expr_tree, query_toplevel};
3use crate::diagnostics::source_map::SourceMap;
4use crate::errors::FatalParseError;
5use crate::expression::parse_expression;
6use crate::util::TypecheckingContext;
7use crate::util::node_is_expression;
8use conjure_cp_core::ast::{Expression, SymbolTablePtr};
9use std::collections::BTreeMap;
10#[allow(unused)]
11use uniplate::Uniplate;
12
13pub fn parse_expr(
14    src: &str,
15    symbols_ptr: SymbolTablePtr,
16) -> Result<Option<Expression>, FatalParseError> {
17    let exprs = parse_exprs(src, symbols_ptr)?;
18    if exprs.len() != 1 {
19        return Ok(None);
20    }
21    Ok(Some(exprs[0].clone()))
22}
23
24pub fn parse_exprs(
25    src: &str,
26    symbols_ptr: SymbolTablePtr,
27) -> Result<Vec<Expression>, FatalParseError> {
28    let Some((tree, source_code)) = get_expr_tree(src) else {
29        return Ok(Vec::new());
30    };
31
32    let root = tree.root_node();
33    let mut source_map = SourceMap::default();
34    let mut decl_spans = BTreeMap::new();
35    let mut errors = Vec::new();
36    let mut ctx = ParseContext::new(
37        &source_code,
38        &root,
39        Some(symbols_ptr),
40        &mut errors,
41        &mut source_map,
42        &mut decl_spans,
43    );
44    let mut ans = Vec::new();
45    for expr in query_toplevel(&root, &node_is_expression) {
46        ctx.typechecking_context = TypecheckingContext::Unknown;
47        ctx.inner_typechecking_context = TypecheckingContext::Unknown;
48        let Some(expr) = parse_expression(&mut ctx, expr)? else {
49            continue;
50        };
51        ans.push(expr);
52    }
53    Ok(ans)
54}
55
56mod 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    pub fn test_parse_constant() {
75        let symbols = SymbolTablePtr::new();
76
77        assert_eq!(
78            parse_expr("42", symbols.clone()).unwrap().unwrap(),
79            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(42)))
80        );
81        assert_eq!(
82            parse_expr("true", symbols.clone()).unwrap().unwrap(),
83            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true)))
84        );
85        assert_eq!(
86            parse_expr("false", symbols).unwrap().unwrap(),
87            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(false)))
88        )
89    }
90
91    #[test]
92    pub fn test_parse_expressions() {
93        let src = "x >= 5, y = a / 2";
94        let symbols = SymbolTablePtr::new();
95        let x = DeclarationPtr::new_find(
96            Name::User("x".into()),
97            Domain::int(vec![conjure_cp_core::ast::Range::Bounded(0, 10)]),
98        );
99
100        let y = DeclarationPtr::new_find(
101            Name::User("y".into()),
102            Domain::int(vec![conjure_cp_core::ast::Range::Bounded(0, 10)]),
103        );
104
105        let a = DeclarationPtr::new_find(
106            Name::User("a".into()),
107            Domain::int(vec![conjure_cp_core::ast::Range::Bounded(0, 10)]),
108        );
109
110        // Clone the Rc when inserting!
111        symbols
112            .write()
113            .insert(x.clone())
114            .expect("x should not exist in the symbol-table yet, so we should be able to add it");
115
116        symbols
117            .write()
118            .insert(y.clone())
119            .expect("y should not exist in the symbol-table yet, so we should be able to add it");
120
121        symbols
122            .write()
123            .insert(a.clone())
124            .expect("a should not exist in the symbol-table yet, so we should be able to add it");
125
126        let exprs = parse_exprs(src, symbols).unwrap();
127        assert_eq!(exprs.len(), 2);
128
129        assert_eq!(
130            exprs[0],
131            Expression::Geq(
132                Metadata::new(),
133                Moo::new(Expression::Atomic(Metadata::new(), Atom::new_ref(x))),
134                Moo::new(Expression::Atomic(Metadata::new(), 5.into()))
135            )
136        );
137
138        assert_eq!(
139            exprs[1],
140            Expression::Eq(
141                Metadata::new(),
142                Moo::new(Expression::Atomic(Metadata::new(), Atom::new_ref(y))),
143                Moo::new(Expression::UnsafeDiv(
144                    Metadata::new(),
145                    Moo::new(Expression::Atomic(Metadata::new(), Atom::new_ref(a))),
146                    Moo::new(Expression::Atomic(Metadata::new(), 2.into()))
147                ))
148            )
149        );
150    }
151
152    #[test]
153    fn bars_distinguish_set_cardinality_from_integer_absolute_value() {
154        let symbols = SymbolTablePtr::new();
155        let set = DeclarationPtr::new_find(
156            Name::User("s".into()),
157            Domain::set(
158                conjure_cp_core::ast::SetAttr::new_max_size(2),
159                Domain::int(vec![conjure_cp_core::ast::Range::Bounded(1, 2)]),
160            ),
161        );
162        let integer = DeclarationPtr::new_find(
163            Name::User("x".into()),
164            Domain::int(vec![conjure_cp_core::ast::Range::Bounded(-2, 2)]),
165        );
166        symbols.write().insert(set).unwrap();
167        symbols.write().insert(integer).unwrap();
168
169        let set_expr = parse_expr("|s| = 1", symbols.clone()).unwrap().unwrap();
170        let Expression::Eq(_, set_left, _) = set_expr else {
171            panic!("expected set cardinality comparison");
172        };
173        assert!(matches!(*set_left, Expression::Card(..)));
174
175        let int_expr = parse_expr("|x| = 1", symbols).unwrap().unwrap();
176        let Expression::Eq(_, int_left, _) = int_expr else {
177            panic!("expected integer absolute-value comparison");
178        };
179        assert!(matches!(*int_left, Expression::Abs(..)));
180    }
181
182    #[test]
183    pub fn test_parse_expression_annotations() {
184        let symbols = SymbolTablePtr::new();
185        let x = DeclarationPtr::new_find(
186            Name::User("x".into()),
187            Domain::int(vec![conjure_cp_core::ast::Range::Bounded(0, 10)]),
188        );
189        symbols
190            .write()
191            .insert(x.clone())
192            .expect("x should not exist in the symbol-table yet, so we should be able to add it");
193
194        let domain_annotation = parse_expr("x : int(1..3)", symbols.clone())
195            .unwrap()
196            .unwrap();
197        assert!(matches!(
198            domain_annotation,
199            Expression::DomainAnnotation(_, _, _)
200        ));
201        assert_eq!(domain_annotation.to_string(), "x : int(1..3)");
202
203        let type_annotation = parse_expr("x :: int", symbols.clone()).unwrap().unwrap();
204        assert!(matches!(
205            type_annotation,
206            Expression::TypeAnnotation(_, _, _)
207        ));
208        assert_eq!(type_annotation.return_type(), ReturnType::Int);
209        assert_eq!(type_annotation.to_string(), "x :: int");
210    }
211
212    #[test]
213    pub fn test_parse_set_representation_preference() {
214        let symbols = SymbolTablePtr::new();
215        let x = DeclarationPtr::new_find(
216            Name::User("x".into()),
217            Domain::set(
218                conjure_cp_core::ast::SetAttr::new_max_size(3),
219                Domain::int(vec![conjure_cp_core::ast::Range::Bounded(1, 4)]),
220            ),
221        );
222        symbols.write().insert(x).unwrap();
223
224        let find_domain = parse_expr("x : set (representation packed) of int", symbols.clone())
225            .unwrap()
226            .unwrap();
227        let Expression::DomainAnnotation(_, _, domain) = find_domain else {
228            panic!("expected domain annotation");
229        };
230        assert_eq!(domain.representation_preference(), Some("packed"));
231        assert_eq!(domain.to_string(), "set (representation packed) of int");
232
233        let type_ann = parse_expr(
234            "x :: set (representation occurrence) of int",
235            symbols.clone(),
236        )
237        .unwrap()
238        .unwrap();
239        assert_eq!(
240            type_ann.to_string(),
241            "x :: set (representation occurrence) of int"
242        );
243        let Expression::TypeAnnotation(_, _, ty_domain) = type_ann else {
244            panic!("expected type annotation");
245        };
246        assert_eq!(ty_domain.representation_preference(), Some("occurrence"));
247
248        let nested = parse_expr(
249            "x : set (representation explicit) of set (representation occurrence) of int",
250            symbols,
251        )
252        .unwrap()
253        .unwrap();
254        let Expression::DomainAnnotation(_, _, nested_domain) = nested else {
255            panic!("expected domain annotation");
256        };
257        assert_eq!(nested_domain.representation_preference(), Some("explicit"));
258        let (_, inner) = nested_domain.as_set().unwrap();
259        assert_eq!(inner.representation_preference(), Some("occurrence"));
260        assert_eq!(
261            nested_domain.to_string(),
262            "set (representation explicit) of set (representation occurrence) of int"
263        );
264    }
265
266    #[test]
267    pub fn test_parse_nested_mset_representation_preference() {
268        let symbols = SymbolTablePtr::new();
269        let x = DeclarationPtr::new_find(Name::User("x".into()), Domain::bool());
270        symbols.write().insert(x).unwrap();
271
272        let annotation = parse_expr(
273            "x : matrix indexed by [int(1..2)] of record { before: mset (representation repetition, maxSize 6) of int(1..9) }",
274            symbols,
275        )
276        .unwrap()
277        .unwrap();
278        let Expression::DomainAnnotation(_, _, domain) = annotation else {
279            panic!("expected domain annotation");
280        };
281
282        assert!(domain.has_representation_preference());
283        let (record, _) = domain.as_matrix().expect("expected matrix domain");
284        let fields = record.as_record().expect("expected record domain");
285        let before = &fields
286            .iter()
287            .find(|field| field.name == Name::User("before".into()))
288            .expect("expected before field")
289            .value;
290        assert_eq!(before.representation_preference(), Some("repetition"));
291        assert_eq!(
292            domain.to_string(),
293            "matrix indexed by [int(1..2)] of record {before: mset (representation repetition, maxSize 6) of int(1..9)}"
294        );
295    }
296
297    #[test]
298    pub fn test_expression_annotations_bind_tighter_than_addition() {
299        let symbols = SymbolTablePtr::new();
300        let x = DeclarationPtr::new_find(
301            Name::User("x".into()),
302            Domain::int(vec![conjure_cp_core::ast::Range::Bounded(0, 10)]),
303        );
304        symbols
305            .write()
306            .insert(x)
307            .expect("x should not exist in the symbol-table yet, so we should be able to add it");
308
309        let expr = parse_expr("x + 1 : int", symbols).unwrap().unwrap();
310        let Expression::Sum(_, terms) = expr else {
311            panic!("expected a sum expression");
312        };
313        let terms = (*terms).clone().unwrap_list().unwrap();
314        assert_eq!(terms.len(), 2);
315        assert!(matches!(terms[1], Expression::DomainAnnotation(_, _, _)));
316        assert_eq!(terms[1].to_string(), "1 : int");
317
318        let symbols = SymbolTablePtr::new();
319        let x = DeclarationPtr::new_find(
320            Name::User("x".into()),
321            Domain::int(vec![conjure_cp_core::ast::Range::Bounded(0, 10)]),
322        );
323        symbols
324            .write()
325            .insert(x)
326            .expect("x should not exist in the symbol-table yet, so we should be able to add it");
327
328        let expr = parse_expr("(x + 1) : int", symbols).unwrap().unwrap();
329        let Expression::DomainAnnotation(_, inner, _) = expr else {
330            panic!("expected a domain annotation");
331        };
332        assert!(matches!(*inner, Expression::Sum(_, _)));
333    }
334
335    #[test]
336    pub fn test_parse_in_with_repr_annotation() {
337        let symbols = SymbolTablePtr::new();
338        let x = DeclarationPtr::new_find(
339            Name::User("x".into()),
340            Domain::set(
341                conjure_cp_core::ast::SetAttr::new_max_size(3),
342                Domain::int(vec![conjure_cp_core::ast::Range::Bounded(1, 4)]),
343            ),
344        );
345        symbols.write().insert(x).unwrap();
346
347        let expr = parse_expr(
348            "1 in x :: set (representation packed) of int",
349            symbols.clone(),
350        )
351        .unwrap()
352        .unwrap();
353        println!("no parens: {expr}");
354        assert!(
355            matches!(expr, Expression::In(_, _, _)),
356            "expected In, got {expr:?}"
357        );
358        let Expression::In(_, _, rhs) = &expr else {
359            unreachable!()
360        };
361        assert!(
362            matches!(rhs.as_ref(), Expression::TypeAnnotation(_, _, _)),
363            "expected type annotation on in-rhs, got {rhs:?}"
364        );
365
366        let expr = parse_expr("1 in (x :: set (representation packed) of int)", symbols)
367            .unwrap()
368            .unwrap();
369        println!("with parens: {expr}");
370        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        let rhs_str = rhs.to_string();
375        assert!(
376            rhs_str.contains("set (representation packed)")
377                || matches!(rhs.as_ref(), Expression::TypeAnnotation(_, _, _)),
378            "expected annotated set on rhs, got {rhs:?}"
379        );
380    }
381}