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
394548
pub fn parse_expression(
13
394548
    ctx: &mut ParseContext,
14
394548
    node: Node,
15
394548
) -> Result<Option<Expression>, FatalParseError> {
16
394548
    match node.kind() {
17
394548
        "atom" => parse_atom(ctx, &node),
18
212985
        "bool_expr" => {
19
171939
            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
171939
            }
29
171939
            parse_boolean_expression(ctx, &node)
30
        }
31
41046
        "arithmetic_expr" => {
32
17437
            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
17411
            }
42
17411
            parse_arithmetic_expression(ctx, &node)
43
        }
44
23609
        "comparison_expr" => {
45
23440
            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
23427
            }
55
23427
            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
394548
}
77

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

            
102
            // set inner context to arithmetic to ensure elements of list are arithmetic expressions
103
1747
            ctx.inner_typechecking_context = TypecheckingContext::Arithmetic;
104
1747
            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
17411
}
119

            
120
23427
fn parse_comparison_expression(
121
23427
    ctx: &mut ParseContext,
122
23427
    node: &Node,
123
23427
) -> Result<Option<Expression>, FatalParseError> {
124
23427
    let Some(inner) = named_child!(recover, ctx, node) else {
125
        return Ok(None);
126
    };
127
23427
    match inner.kind() {
128
23427
        "arithmetic_comparison" => {
129
            // Arithmetic comparisons require arithmetic operands
130
10074
            ctx.typechecking_context = TypecheckingContext::Arithmetic;
131
10074
            parse_binary_expression(ctx, &inner)
132
        }
133
13353
        "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
12859
        "equality_comparison" => {
139
            // Equality works on any type, typechecking of operands will be handled within parse_binary_expression
140
11024
            ctx.typechecking_context = TypecheckingContext::Unknown;
141
11024
            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
23427
}
161

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

            
179
            // set inner context to boolean to ensure elements of list are boolean expressions
180
847
            ctx.inner_typechecking_context = TypecheckingContext::Boolean;
181
847
            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
171939
}
193

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

            
203
2594
    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
2594
    let Some(inner) = parse_atom(ctx, &arg_node)? else {
209
104
        return Ok(None);
210
    };
211

            
212
2490
    let expr = match operator_str {
213
2490
        "and" => Ok(Some(Expression::And(Metadata::new(), Moo::new(inner)))),
214
1776
        "or" => Ok(Some(Expression::Or(Metadata::new(), Moo::new(inner)))),
215
1682
        "sum" => Ok(Some(Expression::Sum(Metadata::new(), Moo::new(inner)))),
216
1398
        "product" => Ok(Some(Expression::Product(Metadata::new(), Moo::new(inner)))),
217
1398
        "min" => Ok(Some(Expression::Min(Metadata::new(), Moo::new(inner)))),
218
670
        "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
2490
    if expr.is_ok() {
229
2490
        ctx.add_span_and_doc_hover(
230
2490
            &operator_node,
231
2490
            operator_str,
232
2490
            SymbolKind::Function,
233
2490
            None,
234
2490
            None,
235
2490
        );
236
2490
    }
237

            
238
2490
    expr
239
2594
}
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
90893
fn parse_unary_expression(
264
90893
    ctx: &mut ParseContext,
265
90893
    node: &Node,
266
90893
) -> Result<Option<Expression>, FatalParseError> {
267
90893
    let Some(expr_node) = field!(recover, ctx, node, "expression") else {
268
        return Ok(None);
269
    };
270
90893
    let Some(inner) = parse_expression(ctx, expr_node)? else {
271
        return Ok(None);
272
    };
273

            
274
90893
    match node.kind() {
275
90893
        "negative_expr" => Ok(Some(Expression::Neg(Metadata::new(), Moo::new(inner)))),
276
89817
        "abs_value" => Ok(Some(Expression::Abs(Metadata::new(), Moo::new(inner)))),
277
89323
        "not_expr" => Ok(Some(Expression::Not(Metadata::new(), Moo::new(inner)))),
278
66644
        "toInt_expr" => {
279
166
            let to_int_keyword_node = child!(node, 0, "toInt");
280
166
            ctx.add_span_and_doc_hover(
281
166
                &to_int_keyword_node,
282
166
                "toInt",
283
166
                SymbolKind::Function,
284
166
                None,
285
166
                None,
286
            );
287
166
            Ok(Some(Expression::ToInt(Metadata::new(), Moo::new(inner))))
288
        }
289
66478
        "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
66478
        "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
90893
}
319

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

            
329
116208
    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
116208
    if op_str == "in" {
333
196
        ctx.typechecking_context = TypecheckingContext::Unknown
334
116012
    }
335

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

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

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

            
352
    // parse right operand
353
115909
    let Some(right_node) = field!(recover, ctx, node, "right") else {
354
        return Ok(None);
355
    };
356
115909
    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
115701
    ctx.typechecking_context = saved_ctx;
362

            
363
115701
    let mut doc_name = "";
364
115701
    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
115701
        "+" => {
370
6200
            doc_name = "L_Plus";
371
6200
            Ok(Some(Expression::Sum(
372
6200
                Metadata::new(),
373
6200
                Moo::new(matrix_expr![left, right; domain_int!(1..)]),
374
6200
            )))
375
        }
376
109501
        "-" => {
377
1184
            doc_name = "L_Minus";
378
1184
            Ok(Some(Expression::Minus(
379
1184
                Metadata::new(),
380
1184
                Moo::new(left),
381
1184
                Moo::new(right),
382
1184
            )))
383
        }
384
108317
        "*" => {
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
107366
        "/\\" => {
392
39705
            doc_name = "and";
393
39705
            Ok(Some(Expression::And(
394
39705
                Metadata::new(),
395
39705
                Moo::new(matrix_expr![left, right; domain_int!(1..)]),
396
39705
            )))
397
        }
398
67661
        "\\/" => {
399
            // No documentation for or in Bits yet
400
24949
            doc_name = "or";
401
24949
            Ok(Some(Expression::Or(
402
24949
                Metadata::new(),
403
24949
                Moo::new(matrix_expr![left, right; domain_int!(1..)]),
404
24949
            )))
405
        }
406
42712
        "**" => {
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
41906
        "/" => {
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
40423
        "%" => {
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
39773
        "=" => {
434
9023
            doc_name = "L_Eq"; //no docs yet
435
9023
            Ok(Some(Expression::Eq(
436
9023
                Metadata::new(),
437
9023
                Moo::new(left),
438
9023
                Moo::new(right),
439
9023
            )))
440
        }
441
30750
        "!=" => {
442
1702
            doc_name = "L_Neq"; //no docs yet
443
1702
            Ok(Some(Expression::Neq(
444
1702
                Metadata::new(),
445
1702
                Moo::new(left),
446
1702
                Moo::new(right),
447
1702
            )))
448
        }
449
29048
        "<=" => {
450
3772
            doc_name = "L_Leq"; //no docs yet
451
3772
            Ok(Some(Expression::Leq(
452
3772
                Metadata::new(),
453
3772
                Moo::new(left),
454
3772
                Moo::new(right),
455
3772
            )))
456
        }
457
25276
        ">=" => {
458
2877
            doc_name = "L_Geq"; //no docs yet
459
2877
            Ok(Some(Expression::Geq(
460
2877
                Metadata::new(),
461
2877
                Moo::new(left),
462
2877
                Moo::new(right),
463
2877
            )))
464
        }
465
22399
        "<" => {
466
2502
            doc_name = "L_Lt"; //no docs yet
467
2502
            Ok(Some(Expression::Lt(
468
2502
                Metadata::new(),
469
2502
                Moo::new(left),
470
2502
                Moo::new(right),
471
2502
            )))
472
        }
473
19897
        ">" => {
474
858
            doc_name = "L_Gt"; //no docs yet
475
858
            Ok(Some(Expression::Gt(
476
858
                Metadata::new(),
477
858
                Moo::new(left),
478
858
                Moo::new(right),
479
858
            )))
480
        }
481

            
482
19039
        "->" => {
483
17584
            doc_name = "L_Imply"; //no docs yet
484
17584
            Ok(Some(Expression::Imply(
485
17584
                Metadata::new(),
486
17584
                Moo::new(left),
487
17584
                Moo::new(right),
488
17584
            )))
489
        }
490
1455
        "<->" => {
491
167
            doc_name = "L_Iff"; //no docs yet
492
167
            Ok(Some(Expression::Iff(
493
167
                Metadata::new(),
494
167
                Moo::new(left),
495
167
                Moo::new(right),
496
167
            )))
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
115701
    if expr.is_ok() {
596
115701
        ctx.add_span_and_doc_hover(&op_node, doc_name, SymbolKind::Function, None, None);
597
115701
    }
598

            
599
115701
    expr
600
116208
}
601

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

            
611
8612
    let Some(domain) = expr.domain_of() else {
612
335
        return TypecheckingContext::Unknown;
613
    };
614
8277
    let Ok(ground) = domain.resolve() else {
615
24
        return TypecheckingContext::Unknown;
616
    };
617

            
618
8253
    match ground.as_ref() {
619
1054
        GroundDomain::Bool => TypecheckingContext::Boolean,
620
6705
        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
10881
}