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 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 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}