1
use crate::diagnostics::diagnostics_api::SymbolKind;
2
use crate::errors::{FatalParseError, RecoverableParseError};
3
use crate::parser::ParseContext;
4
use crate::parser::atom::parse_atom;
5
use crate::parser::comprehension::parse_quantifier_or_aggregate_expr;
6
use crate::util::TypecheckingContext;
7
use crate::{child, field, named_child};
8
use conjure_cp_core::ast::{Expression, GroundDomain, Metadata, Moo};
9
use conjure_cp_core::{domain_int, matrix_expr, range};
10
use tree_sitter::Node;
11

            
12
391847
pub fn parse_expression(
13
391847
    ctx: &mut ParseContext,
14
391847
    node: Node,
15
391847
) -> Result<Option<Expression>, FatalParseError> {
16
391847
    match node.kind() {
17
391847
        "atom" => parse_atom(ctx, &node),
18
212180
        "bool_expr" => {
19
171461
            if ctx.typechecking_context == TypecheckingContext::Arithmetic {
20
                ctx.record_error(RecoverableParseError::new(
21
                    format!(
22
                        "Type error: {}\n\tExepected: int\n\tGot: boolean expression",
23
                        &ctx.source_code[node.start_byte()..node.end_byte()]
24
                    ),
25
                    Some(node.range()),
26
                ));
27
                return Ok(None);
28
171461
            }
29
171461
            parse_boolean_expression(ctx, &node)
30
        }
31
40719
        "arithmetic_expr" => {
32
17207
            if ctx.typechecking_context == TypecheckingContext::Boolean {
33
26
                ctx.record_error(RecoverableParseError::new(
34
26
                    format!(
35
                        "Type error: {}\n\tExepected: bool\n\tGot: arithmetic expression",
36
26
                        &ctx.source_code[node.start_byte()..node.end_byte()]
37
                    ),
38
26
                    Some(node.range()),
39
                ));
40
26
                return Ok(None);
41
17181
            }
42
17181
            parse_arithmetic_expression(ctx, &node)
43
        }
44
23512
        "comparison_expr" => {
45
23343
            if ctx.typechecking_context == TypecheckingContext::Arithmetic {
46
13
                ctx.record_error(RecoverableParseError::new(
47
13
                    format!(
48
                        "Type error: {}\n\tExepected: int\n\tGot: comparison expression",
49
13
                        &ctx.source_code[node.start_byte()..node.end_byte()]
50
                    ),
51
13
                    Some(node.range()),
52
                ));
53
13
                return Ok(None);
54
23330
            }
55
23330
            parse_comparison_expression(ctx, &node)
56
        }
57
169
        "all_diff_comparison" => {
58
143
            if ctx.typechecking_context == TypecheckingContext::Arithmetic {
59
                ctx.record_error(RecoverableParseError::new(
60
                    format!("Type error: {}\n\tExepected: arithmetic expression\n\tFound: comparison expression", &ctx.source_code[node.start_byte()..node.end_byte()]),
61
                    Some(node.range()),
62
                ));
63
                return Ok(None);
64
143
            }
65
143
            ctx.typechecking_context = TypecheckingContext::Matrix;
66
143
            parse_all_diff_comparison(ctx, &node)
67
        }
68
        _ => {
69
26
            ctx.record_error(RecoverableParseError::new(
70
26
                format!("Unexpected expression type: '{}'", node.kind()),
71
26
                Some(node.range()),
72
            ));
73
26
            Ok(None)
74
        }
75
    }
76
391847
}
77

            
78
17181
fn parse_arithmetic_expression(
79
17181
    ctx: &mut ParseContext,
80
17181
    node: &Node,
81
17181
) -> Result<Option<Expression>, FatalParseError> {
82
17181
    ctx.typechecking_context = TypecheckingContext::Arithmetic;
83
17181
    ctx.inner_typechecking_context = TypecheckingContext::Unknown;
84
17181
    let Some(inner) = named_child!(recover, ctx, node) else {
85
        return Ok(None);
86
    };
87
17181
    match inner.kind() {
88
17181
        "atom" => parse_atom(ctx, &inner),
89
17181
        "negative_expr" | "abs_value" | "sub_arith_expr" | "factorial_expr" => {
90
3860
            parse_unary_expression(ctx, &inner)
91
        }
92
13321
        "toInt_expr" => {
93
            // add special handling for toInt, as it is arithmetic but takes a non-arithmetic operand
94
140
            ctx.typechecking_context = TypecheckingContext::Unknown;
95
140
            parse_unary_expression(ctx, &inner)
96
        }
97
13181
        "exponent" | "product_expr" | "sum_expr" => parse_binary_expression(ctx, &inner),
98
1916
        "list_combining_expr_arith" => {
99
            // list-combining arithmetic operators accept either set or matrix operands
100
1682
            ctx.typechecking_context = TypecheckingContext::SetOrMatrix;
101

            
102
            // set inner context to arithmetic to ensure elements of list are arithmetic expressions
103
1682
            ctx.inner_typechecking_context = TypecheckingContext::Arithmetic;
104
1682
            parse_list_combining_expression(ctx, &inner)
105
        }
106
234
        "aggregate_expr" => {
107
234
            ctx.inner_typechecking_context = TypecheckingContext::Arithmetic;
108
234
            parse_quantifier_or_aggregate_expr(ctx, &inner)
109
        }
110
        _ => {
111
            ctx.record_error(RecoverableParseError::new(
112
                format!("Expected arithmetic expression, found: {}", inner.kind()),
113
                Some(inner.range()),
114
            ));
115
            Ok(None)
116
        }
117
    }
118
17181
}
119

            
120
23330
fn parse_comparison_expression(
121
23330
    ctx: &mut ParseContext,
122
23330
    node: &Node,
123
23330
) -> Result<Option<Expression>, FatalParseError> {
124
23330
    let Some(inner) = named_child!(recover, ctx, node) else {
125
        return Ok(None);
126
    };
127
23330
    match inner.kind() {
128
23330
        "arithmetic_comparison" => {
129
            // Arithmetic comparisons require arithmetic operands
130
10174
            ctx.typechecking_context = TypecheckingContext::Arithmetic;
131
10174
            parse_binary_expression(ctx, &inner)
132
        }
133
13156
        "lex_comparison" => {
134
            // TODO: check that both operands are comparable collections.
135
494
            ctx.typechecking_context = TypecheckingContext::Unknown;
136
494
            parse_binary_expression(ctx, &inner)
137
        }
138
12662
        "equality_comparison" => {
139
            // Equality works on any type, typechecking of operands will be handled within parse_binary_expression
140
10827
            ctx.typechecking_context = TypecheckingContext::Unknown;
141
10827
            parse_binary_expression(ctx, &inner)
142
        }
143
1835
        "set_comparison" => {
144
            // Set comparisons require set operands (except 'in', which is hadled later)
145
677
            ctx.typechecking_context = TypecheckingContext::Set;
146
677
            parse_binary_expression(ctx, &inner)
147
        }
148
1158
        "all_diff_comparison" => {
149
1158
            ctx.typechecking_context = TypecheckingContext::Matrix;
150
1158
            parse_all_diff_comparison(ctx, &inner)
151
        }
152
        _ => {
153
            ctx.record_error(RecoverableParseError::new(
154
                format!("Expected comparison expression, found '{}'", inner.kind()),
155
                Some(inner.range()),
156
            ));
157
            Ok(None)
158
        }
159
    }
160
23330
}
161

            
162
171461
fn parse_boolean_expression(
163
171461
    ctx: &mut ParseContext,
164
171461
    node: &Node,
165
171461
) -> Result<Option<Expression>, FatalParseError> {
166
171461
    ctx.typechecking_context = TypecheckingContext::Boolean;
167
171461
    ctx.inner_typechecking_context = TypecheckingContext::Unknown;
168
171461
    let Some(inner) = named_child!(recover, ctx, node) else {
169
        return Ok(None);
170
    };
171
171461
    match inner.kind() {
172
171461
        "atom" => parse_atom(ctx, &inner),
173
171461
        "not_expr" | "sub_bool_expr" => parse_unary_expression(ctx, &inner),
174
84764
        "and_expr" | "or_expr" | "implication" | "iff_expr" => parse_binary_expression(ctx, &inner),
175
2519
        "list_combining_expr_bool" => {
176
            // list-combining boolean operators accept either set or matrix operands
177
673
            ctx.typechecking_context = TypecheckingContext::SetOrMatrix;
178

            
179
            // set inner context to boolean to ensure elements of list are boolean expressions
180
673
            ctx.inner_typechecking_context = TypecheckingContext::Boolean;
181
673
            parse_list_combining_expression(ctx, &inner)
182
        }
183
1846
        "quantifier_expr" => parse_quantifier_or_aggregate_expr(ctx, &inner),
184
        _ => {
185
            ctx.record_error(RecoverableParseError::new(
186
                format!("Expected boolean expression, found '{}'", inner.kind()),
187
                Some(inner.range()),
188
            ));
189
            Ok(None)
190
        }
191
    }
192
171461
}
193

            
194
2355
fn parse_list_combining_expression(
195
2355
    ctx: &mut ParseContext,
196
2355
    node: &Node,
197
2355
) -> Result<Option<Expression>, FatalParseError> {
198
2355
    let Some(operator_node) = field!(recover, ctx, node, "operator") else {
199
        return Ok(None);
200
    };
201
2355
    let operator_str = &ctx.source_code[operator_node.start_byte()..operator_node.end_byte()];
202

            
203
2355
    let Some(arg_node) = field!(recover, ctx, node, "arg") else {
204
        return Ok(None);
205
    };
206
    // While parsing inner, the typechecking context is SetOrMatrix
207
    // The inner context is either Boolean or Arithmetic so the elements of the set/matrix are typechecked correctly.
208
2355
    let Some(inner) = parse_atom(ctx, &arg_node)? else {
209
91
        return Ok(None);
210
    };
211

            
212
2264
    let expr = match operator_str {
213
2264
        "and" => Ok(Some(Expression::And(Metadata::new(), Moo::new(inner)))),
214
1716
        "or" => Ok(Some(Expression::Or(Metadata::new(), Moo::new(inner)))),
215
1630
        "sum" => Ok(Some(Expression::Sum(Metadata::new(), Moo::new(inner)))),
216
1388
        "product" => Ok(Some(Expression::Product(Metadata::new(), Moo::new(inner)))),
217
1388
        "min" => Ok(Some(Expression::Min(Metadata::new(), Moo::new(inner)))),
218
660
        "max" => Ok(Some(Expression::Max(Metadata::new(), Moo::new(inner)))),
219
        _ => {
220
            ctx.record_error(RecoverableParseError::new(
221
                format!("Invalid operator: '{operator_str}'"),
222
                Some(operator_node.range()),
223
            ));
224
            Ok(None)
225
        }
226
    };
227

            
228
2264
    if expr.is_ok() {
229
2264
        ctx.add_span_and_doc_hover(
230
2264
            &operator_node,
231
2264
            operator_str,
232
2264
            SymbolKind::Function,
233
2264
            None,
234
2264
            None,
235
2264
        );
236
2264
    }
237

            
238
2264
    expr
239
2355
}
240

            
241
1301
fn parse_all_diff_comparison(
242
1301
    ctx: &mut ParseContext,
243
1301
    node: &Node,
244
1301
) -> Result<Option<Expression>, FatalParseError> {
245
1301
    let Some(arg_node) = field!(recover, ctx, node, "arg") else {
246
        return Ok(None);
247
    };
248
1301
    let Some(inner) = parse_expression(ctx, arg_node)? else {
249
13
        return Ok(None);
250
    };
251

            
252
1288
    let all_diff_keyword_node = child!(node, 0, "allDiff");
253
1288
    ctx.add_span_and_doc_hover(
254
1288
        &all_diff_keyword_node,
255
1288
        "allDiff",
256
1288
        SymbolKind::Function,
257
1288
        None,
258
1288
        None,
259
    );
260
1288
    Ok(Some(Expression::AllDiff(Metadata::new(), Moo::new(inner))))
261
1301
}
262

            
263
90697
fn parse_unary_expression(
264
90697
    ctx: &mut ParseContext,
265
90697
    node: &Node,
266
90697
) -> Result<Option<Expression>, FatalParseError> {
267
90697
    let Some(expr_node) = field!(recover, ctx, node, "expression") else {
268
        return Ok(None);
269
    };
270
90697
    let Some(inner) = parse_expression(ctx, expr_node)? else {
271
        return Ok(None);
272
    };
273

            
274
90697
    match node.kind() {
275
90697
        "negative_expr" => Ok(Some(Expression::Neg(Metadata::new(), Moo::new(inner)))),
276
89699
        "abs_value" => Ok(Some(Expression::Abs(Metadata::new(), Moo::new(inner)))),
277
89205
        "not_expr" => Ok(Some(Expression::Not(Metadata::new(), Moo::new(inner)))),
278
66594
        "toInt_expr" => {
279
140
            let to_int_keyword_node = child!(node, 0, "toInt");
280
140
            ctx.add_span_and_doc_hover(
281
140
                &to_int_keyword_node,
282
140
                "toInt",
283
140
                SymbolKind::Function,
284
140
                None,
285
140
                None,
286
            );
287
140
            Ok(Some(Expression::ToInt(Metadata::new(), Moo::new(inner))))
288
        }
289
66454
        "factorial_expr" => {
290
            // looking for the operator node (either '!' at the end or 'factorial' at the start) to add hover info
291
            if let Some(op_node) = (0..node.child_count())
292
                .filter_map(|i| node.child(i.try_into().unwrap()))
293
                .find(|c| matches!(c.kind(), "!" | "factorial"))
294
            {
295
                ctx.add_span_and_doc_hover(
296
                    &op_node,
297
                    "post_factorial",
298
                    SymbolKind::Function,
299
                    None,
300
                    None,
301
                );
302
            }
303

            
304
            Ok(Some(Expression::Factorial(
305
                Metadata::new(),
306
                Moo::new(inner),
307
            )))
308
        }
309
66454
        "sub_bool_expr" | "sub_arith_expr" => Ok(Some(inner)),
310
        _ => {
311
            ctx.record_error(RecoverableParseError::new(
312
                format!("Unrecognised unary operation: '{}'", node.kind()),
313
                Some(node.range()),
314
            ));
315
            Ok(None)
316
        }
317
    }
318
90697
}
319

            
320
115838
pub fn parse_binary_expression(
321
115838
    ctx: &mut ParseContext,
322
115838
    node: &Node,
323
115838
) -> Result<Option<Expression>, FatalParseError> {
324
115838
    let Some(op_node) = field!(recover, ctx, node, "operator") else {
325
        return Ok(None);
326
    };
327
115838
    let op_str = &ctx.source_code[op_node.start_byte()..op_node.end_byte()];
328

            
329
115838
    let saved_ctx = ctx.typechecking_context;
330

            
331
    // Special handling for 'in' operator, as the left operand doesn't have to be a set
332
115838
    if op_str == "in" {
333
196
        ctx.typechecking_context = TypecheckingContext::Unknown
334
115642
    }
335

            
336
    // parse left operand
337
115838
    let Some(left_node) = field!(recover, ctx, node, "left") else {
338
        return Ok(None);
339
    };
340
115838
    let Some(left) = parse_expression(ctx, left_node)? else {
341
286
        return Ok(None);
342
    };
343

            
344
    // reset context, if needed
345
115552
    ctx.typechecking_context = saved_ctx;
346

            
347
    // Equality/inequality: enforce right operand to match left operand type when inferable
348
115552
    if matches!(op_str, "=" | "!=") {
349
10697
        ctx.typechecking_context = inferred_context_from_expression(&left);
350
104855
    }
351

            
352
    // parse right operand
353
115552
    let Some(right_node) = field!(recover, ctx, node, "right") else {
354
        return Ok(None);
355
    };
356
115552
    let Some(right) = parse_expression(ctx, right_node)? else {
357
208
        return Ok(None);
358
    };
359

            
360
    // restore original contexts for parent expression parsing
361
115344
    ctx.typechecking_context = saved_ctx;
362

            
363
115344
    let mut doc_name = "";
364
115344
    let expr = match op_str {
365
        // NB: We are deliberately setting the index domain to 1.., not 1..2.
366
        // Semantically, this means "a list that can grow/shrink arbitrarily".
367
        // This is expected by rules which will modify the terms of the sum expression
368
        // (e.g. by partially evaluating them).
369
115344
        "+" => {
370
6154
            doc_name = "L_Plus";
371
6154
            Ok(Some(Expression::Sum(
372
6154
                Metadata::new(),
373
6154
                Moo::new(matrix_expr![left, right; domain_int!(1..)]),
374
6154
            )))
375
        }
376
109190
        "-" => {
377
1169
            doc_name = "L_Minus";
378
1169
            Ok(Some(Expression::Minus(
379
1169
                Metadata::new(),
380
1169
                Moo::new(left),
381
1169
                Moo::new(right),
382
1169
            )))
383
        }
384
108021
        "*" => {
385
951
            doc_name = "L_Times";
386
951
            Ok(Some(Expression::Product(
387
951
                Metadata::new(),
388
951
                Moo::new(matrix_expr![left, right; domain_int!(1..)]),
389
951
            )))
390
        }
391
107070
        "/\\" => {
392
39577
            doc_name = "and";
393
39577
            Ok(Some(Expression::And(
394
39577
                Metadata::new(),
395
39577
                Moo::new(matrix_expr![left, right; domain_int!(1..)]),
396
39577
            )))
397
        }
398
67493
        "\\/" => {
399
            // No documentation for or in Bits yet
400
24931
            doc_name = "or";
401
24931
            Ok(Some(Expression::Or(
402
24931
                Metadata::new(),
403
24931
                Moo::new(matrix_expr![left, right; domain_int!(1..)]),
404
24931
            )))
405
        }
406
42562
        "**" => {
407
806
            doc_name = "L_Pow";
408
806
            Ok(Some(Expression::UnsafePow(
409
806
                Metadata::new(),
410
806
                Moo::new(left),
411
806
                Moo::new(right),
412
806
            )))
413
        }
414
41756
        "/" => {
415
            //TODO: add checks for if division is safe or not
416
1483
            doc_name = "L_Div";
417
1483
            Ok(Some(Expression::UnsafeDiv(
418
1483
                Metadata::new(),
419
1483
                Moo::new(left),
420
1483
                Moo::new(right),
421
1483
            )))
422
        }
423
40273
        "%" => {
424
            //TODO: add checks for if mod is safe or not
425
650
            doc_name = "L_Mod";
426
650
            Ok(Some(Expression::UnsafeMod(
427
650
                Metadata::new(),
428
650
                Moo::new(left),
429
650
                Moo::new(right),
430
650
            )))
431
        }
432

            
433
39623
        "=" => {
434
8879
            doc_name = "L_Eq"; //no docs yet
435
8879
            Ok(Some(Expression::Eq(
436
8879
                Metadata::new(),
437
8879
                Moo::new(left),
438
8879
                Moo::new(right),
439
8879
            )))
440
        }
441
30744
        "!=" => {
442
1662
            doc_name = "L_Neq"; //no docs yet
443
1662
            Ok(Some(Expression::Neq(
444
1662
                Metadata::new(),
445
1662
                Moo::new(left),
446
1662
                Moo::new(right),
447
1662
            )))
448
        }
449
29082
        "<=" => {
450
3638
            doc_name = "L_Leq"; //no docs yet
451
3638
            Ok(Some(Expression::Leq(
452
3638
                Metadata::new(),
453
3638
                Moo::new(left),
454
3638
                Moo::new(right),
455
3638
            )))
456
        }
457
25444
        ">=" => {
458
2955
            doc_name = "L_Geq"; //no docs yet
459
2955
            Ok(Some(Expression::Geq(
460
2955
                Metadata::new(),
461
2955
                Moo::new(left),
462
2955
                Moo::new(right),
463
2955
            )))
464
        }
465
22489
        "<" => {
466
2580
            doc_name = "L_Lt"; //no docs yet
467
2580
            Ok(Some(Expression::Lt(
468
2580
                Metadata::new(),
469
2580
                Moo::new(left),
470
2580
                Moo::new(right),
471
2580
            )))
472
        }
473
19909
        ">" => {
474
936
            doc_name = "L_Gt"; //no docs yet
475
936
            Ok(Some(Expression::Gt(
476
936
                Metadata::new(),
477
936
                Moo::new(left),
478
936
                Moo::new(right),
479
936
            )))
480
        }
481

            
482
18973
        "->" => {
483
17534
            doc_name = "L_Imply"; //no docs yet
484
17534
            Ok(Some(Expression::Imply(
485
17534
                Metadata::new(),
486
17534
                Moo::new(left),
487
17534
                Moo::new(right),
488
17534
            )))
489
        }
490
1439
        "<->" => {
491
151
            doc_name = "L_Iff"; //no docs yet
492
151
            Ok(Some(Expression::Iff(
493
151
                Metadata::new(),
494
151
                Moo::new(left),
495
151
                Moo::new(right),
496
151
            )))
497
        }
498
1288
        "<lex" => {
499
156
            doc_name = "L_LexLt"; //no docs yet
500
156
            Ok(Some(Expression::LexLt(
501
156
                Metadata::new(),
502
156
                Moo::new(left),
503
156
                Moo::new(right),
504
156
            )))
505
        }
506
1132
        ">lex" => {
507
52
            doc_name = "L_LexGt"; //no docs yet
508
52
            Ok(Some(Expression::LexGt(
509
52
                Metadata::new(),
510
52
                Moo::new(left),
511
52
                Moo::new(right),
512
52
            )))
513
        }
514
1080
        "<=lex" => {
515
208
            doc_name = "L_LexLeq"; //no docs yet
516
208
            Ok(Some(Expression::LexLeq(
517
208
                Metadata::new(),
518
208
                Moo::new(left),
519
208
                Moo::new(right),
520
208
            )))
521
        }
522
872
        ">=lex" => {
523
78
            doc_name = "L_LexGeq"; //no docs yet
524
78
            Ok(Some(Expression::LexGeq(
525
78
                Metadata::new(),
526
78
                Moo::new(left),
527
78
                Moo::new(right),
528
78
            )))
529
        }
530
794
        "in" => {
531
196
            doc_name = "L_in";
532
196
            Ok(Some(Expression::In(
533
196
                Metadata::new(),
534
196
                Moo::new(left),
535
196
                Moo::new(right),
536
196
            )))
537
        }
538
598
        "subset" => {
539
130
            doc_name = "L_subset";
540
130
            Ok(Some(Expression::Subset(
541
130
                Metadata::new(),
542
130
                Moo::new(left),
543
130
                Moo::new(right),
544
130
            )))
545
        }
546
468
        "subsetEq" => {
547
104
            doc_name = "L_subsetEq";
548
104
            Ok(Some(Expression::SubsetEq(
549
104
                Metadata::new(),
550
104
                Moo::new(left),
551
104
                Moo::new(right),
552
104
            )))
553
        }
554
364
        "supset" => {
555
104
            doc_name = "L_supset";
556
104
            Ok(Some(Expression::Supset(
557
104
                Metadata::new(),
558
104
                Moo::new(left),
559
104
                Moo::new(right),
560
104
            )))
561
        }
562
260
        "supsetEq" => {
563
104
            doc_name = "L_supsetEq";
564
104
            Ok(Some(Expression::SupsetEq(
565
104
                Metadata::new(),
566
104
                Moo::new(left),
567
104
                Moo::new(right),
568
104
            )))
569
        }
570
156
        "union" => {
571
78
            doc_name = "L_union";
572
78
            Ok(Some(Expression::Union(
573
78
                Metadata::new(),
574
78
                Moo::new(left),
575
78
                Moo::new(right),
576
78
            )))
577
        }
578
78
        "intersect" => {
579
78
            doc_name = "L_intersect";
580
78
            Ok(Some(Expression::Intersect(
581
78
                Metadata::new(),
582
78
                Moo::new(left),
583
78
                Moo::new(right),
584
78
            )))
585
        }
586
        _ => {
587
            ctx.record_error(RecoverableParseError::new(
588
                format!("Invalid operator: '{op_str}'"),
589
                Some(op_node.range()),
590
            ));
591
            Ok(None)
592
        }
593
    };
594

            
595
115344
    if expr.is_ok() {
596
115344
        ctx.add_span_and_doc_hover(&op_node, doc_name, SymbolKind::Function, None, None);
597
115344
    }
598

            
599
115344
    expr
600
115838
}
601

            
602
10697
fn inferred_context_from_expression(expr: &Expression) -> TypecheckingContext {
603
    // TODO: typechecking for index/slice expressions
604
8438
    if matches!(
605
10697
        expr,
606
        Expression::UnsafeIndex(_, _, _) | Expression::UnsafeSlice(_, _, _)
607
    ) {
608
2259
        return TypecheckingContext::Unknown;
609
8438
    }
610

            
611
8438
    let Some(domain) = expr.domain_of() else {
612
281
        return TypecheckingContext::Unknown;
613
    };
614
8157
    let Some(ground) = domain.resolve() else {
615
12
        return TypecheckingContext::Unknown;
616
    };
617

            
618
8145
    match ground.as_ref() {
619
1054
        GroundDomain::Bool => TypecheckingContext::Boolean,
620
6597
        GroundDomain::Int(_) => TypecheckingContext::Arithmetic,
621
182
        GroundDomain::Set(_, _) => TypecheckingContext::Set,
622
        GroundDomain::MSet(_, _) => TypecheckingContext::MSet,
623
156
        GroundDomain::Matrix(_, _) => TypecheckingContext::Matrix,
624
104
        GroundDomain::Tuple(_) => TypecheckingContext::Tuple,
625
52
        GroundDomain::Record(_) => TypecheckingContext::Record,
626
        GroundDomain::Partition(_, _) => TypecheckingContext::Partition,
627
        GroundDomain::Sequence(_, _) => TypecheckingContext::Sequence,
628
        GroundDomain::Function(_, _, _)
629
        | GroundDomain::Variant(_)
630
        | GroundDomain::Relation(_, _)
631
        | GroundDomain::Empty(_) => TypecheckingContext::Unknown,
632
    }
633
10697
}