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, parse_table_operand};
5
use crate::parser::comprehension::parse_quantifier_or_aggregate_expr;
6
use crate::parser::domain::parse_domain;
7
use crate::parser::global_constraints::{
8
    ALL_DIFFERENT, ALL_DIFFERENT_EXCEPT, AT_LEAST, AT_MOST, GLOBAL_CARDINALITY,
9
    is_all_different_except_operator, is_all_different_operator, is_at_least_operator,
10
    is_at_most_operator, is_global_cardinality_operator,
11
};
12
use crate::util::TypecheckingContext;
13
use crate::{child, field, named_child};
14
use conjure_cp_core::ast::ac_operators::ACOperatorKind;
15
use conjure_cp_core::ast::{
16
    Atom, Expression, GroundDomain, Literal, Metadata, Moo, ReturnType, Typeable,
17
};
18
use conjure_cp_core::{domain_int, matrix_expr, range};
19
use tree_sitter::Node;
20

            
21
17870147
pub fn parse_expression(
22
17870147
    ctx: &mut ParseContext,
23
17870147
    node: Node,
24
17870147
) -> Result<Option<Expression>, FatalParseError> {
25
17870147
    match node.kind() {
26
        "atom"
27
        | "primary_atom"
28
        | "constant"
29
        | "identifier"
30
        | "metavar"
31
        | "matrix"
32
        | "record"
33
        | "variant"
34
        | "tuple"
35
        | "set_literal"
36
        | "mset_literal"
37
        | "sequence_literal"
38
        | "function_literal"
39
        | "relation_literal"
40
        | "partition_literal"
41
        | "comprehension"
42
        | "index_or_slice"
43
        | "flatten"
44
        | "element_id"
45
        | "table"
46
        | "negative_table"
47
        | "short_table"
48
        | "enum_operator"
49
        | "apply_expr"
50
        | "image_expr"
51
        | "image_set_expr"
52
        | "pre_image_expr"
53
        | "inverse_expr"
54
        | "restrict_expr"
55
        | "defined_expr"
56
        | "range_expr"
57
        | "to_set_expr"
58
        | "to_mset_expr"
59
        | "to_relation_expr"
60
        | "attribute_as_constraint_expr"
61
        | "together_expr"
62
        | "apart_expr"
63
        | "party_expr"
64
        | "parts_expr"
65
11593766
        | "participants_expr" => parse_atom(ctx, &node),
66
        "bool_expr" => {
67
1413714
            if ctx.typechecking_context == TypecheckingContext::Arithmetic {
68
                ctx.record_error(RecoverableParseError::new(
69
                    format!(
70
                        "Type error: {}\n\tExpected: int\n\tGot: boolean expression",
71
                        &ctx.source_code[node.start_byte()..node.end_byte()]
72
                    ),
73
                    Some(node.range()),
74
                ));
75
                return Ok(None);
76
1413714
            }
77
1413714
            parse_boolean_expression(ctx, &node)
78
        }
79
        "arithmetic_expr" => {
80
2868709
            if ctx.typechecking_context == TypecheckingContext::Boolean {
81
28
                ctx.record_error(RecoverableParseError::new(
82
28
                    format!(
83
28
                        "Type error: {}\n\tExpected: bool\n\tGot: int",
84
28
                        &ctx.source_code[node.start_byte()..node.end_byte()]
85
28
                    ),
86
28
                    Some(node.range()),
87
28
                ));
88
28
                return Ok(None);
89
2868681
            }
90
2868681
            parse_arithmetic_expression(ctx, &node)
91
        }
92
        "negative_expr" | "abs_value" | "sub_arith_expr" | "factorial_expr" | "toInt_expr" => {
93
            parse_unary_expression(ctx, &node)
94
        }
95
        "product_expr" | "sum_expr" | "exponent" => parse_binary_expression(ctx, &node),
96
        "comparison_expr" => {
97
1986653
            if ctx.typechecking_context == TypecheckingContext::Arithmetic {
98
24
                ctx.record_error(RecoverableParseError::new(
99
24
                    format!(
100
24
                        "Type error: {}\n\tExpected: int\n\tGot: comparison expression",
101
24
                        &ctx.source_code[node.start_byte()..node.end_byte()]
102
24
                    ),
103
24
                    Some(node.range()),
104
24
                ));
105
24
                return Ok(None);
106
1986629
            }
107
1986629
            parse_comparison_expression(ctx, &node)
108
        }
109
        "all_diff_comparison" => {
110
144
            if ctx.typechecking_context == TypecheckingContext::Arithmetic {
111
                ctx.record_error(RecoverableParseError::new(
112
                    format!("Type error: {}\n\tExpected: arithmetic expression\n\tGot: comparison expression", &ctx.source_code[node.start_byte()..node.end_byte()]),
113
                    Some(node.range()),
114
                ));
115
                return Ok(None);
116
144
            }
117
144
            ctx.typechecking_context = TypecheckingContext::Matrix;
118
144
            parse_all_diff_comparison(ctx, &node)
119
        }
120
        "all_different_except_comparison" => {
121
            if ctx.typechecking_context == TypecheckingContext::Arithmetic {
122
                ctx.record_error(RecoverableParseError::new(
123
                    format!("Type error: {}\n\tExpected: arithmetic expression\n\tGot: comparison expression", &ctx.source_code[node.start_byte()..node.end_byte()]),
124
                    Some(node.range()),
125
                ));
126
                return Ok(None);
127
            }
128
            ctx.typechecking_context = TypecheckingContext::Matrix;
129
            parse_all_different_except_comparison(ctx, &node)
130
        }
131
        "global_cardinality_comparison" => {
132
            if ctx.typechecking_context == TypecheckingContext::Arithmetic {
133
                ctx.record_error(RecoverableParseError::new(
134
                    format!("Type error: {}\n\tExpected: arithmetic expression\n\tGot: comparison expression", &ctx.source_code[node.start_byte()..node.end_byte()]),
135
                    Some(node.range()),
136
                ));
137
                return Ok(None);
138
            }
139
            parse_global_cardinality_comparison(ctx, &node)
140
        }
141
        "annotation_expr" | "type_annotation" | "domain_annotation" => {
142
7089
            parse_annotation_expression(ctx, &node)
143
        }
144
        _ => {
145
72
            ctx.record_error(RecoverableParseError::new(
146
72
                format!("Unexpected expression type: '{}'", node.kind()),
147
72
                Some(node.range()),
148
72
            ));
149
72
            Ok(None)
150
        }
151
    }
152
17870147
}
153

            
154
2868681
fn parse_arithmetic_expression(
155
2868681
    ctx: &mut ParseContext,
156
2868681
    node: &Node,
157
2868681
) -> Result<Option<Expression>, FatalParseError> {
158
2868681
    ctx.typechecking_context = TypecheckingContext::Arithmetic;
159
2868681
    ctx.inner_typechecking_context = TypecheckingContext::Unknown;
160
2868681
    let Some(inner) = named_child!(recover, ctx, node) else {
161
        return Ok(None);
162
    };
163
2868681
    match inner.kind() {
164
        "atom" => parse_atom(ctx, &inner),
165
        "negative_expr" | "abs_value" | "sub_arith_expr" | "factorial_expr" => {
166
775582
            parse_unary_expression(ctx, &inner)
167
        }
168
        "toInt_expr" => {
169
            // add special handling for toInt, as it is arithmetic but takes a non-arithmetic operand
170
889542
            ctx.typechecking_context = TypecheckingContext::Unknown;
171
889542
            parse_unary_expression(ctx, &inner)
172
        }
173
10152
        "catch_undef_expr" => parse_catch_undef_expression(ctx, &inner),
174
761912
        "exponent" | "product_expr" | "sum_expr" => parse_binary_expression(ctx, &inner),
175
        "list_combining_expr_arith" => {
176
            // list-combining arithmetic operators accept either set or matrix operands
177
403437
            ctx.typechecking_context = TypecheckingContext::SetOrMatrix;
178

            
179
            // set inner context to arithmetic to ensure elements of list are arithmetic expressions
180
403437
            ctx.inner_typechecking_context = TypecheckingContext::Arithmetic;
181
403437
            parse_list_combining_expression(ctx, &inner)
182
        }
183
        "aggregate_expr" => {
184
28056
            ctx.inner_typechecking_context = TypecheckingContext::Arithmetic;
185
28056
            parse_quantifier_or_aggregate_expr(ctx, &inner)
186
        }
187
        _ => {
188
            ctx.record_error(RecoverableParseError::new(
189
                format!("Expected arithmetic expression, found: {}", inner.kind()),
190
                Some(inner.range()),
191
            ));
192
            Ok(None)
193
        }
194
    }
195
2868681
}
196

            
197
1986629
fn parse_comparison_expression(
198
1986629
    ctx: &mut ParseContext,
199
1986629
    node: &Node,
200
1986629
) -> Result<Option<Expression>, FatalParseError> {
201
1986629
    let Some(inner) = named_child!(recover, ctx, node) else {
202
        return Ok(None);
203
    };
204
1986629
    match inner.kind() {
205
        "arithmetic_comparison" => {
206
            // <, <=, >, >= work on any orderable type (everything except unnamed types, which
207
            // don't support ordering); typechecking of operands is handled within
208
            // parse_binary_expression, same as equality.
209
519329
            ctx.typechecking_context = TypecheckingContext::Unknown;
210
519329
            parse_binary_expression(ctx, &inner)
211
        }
212
        "lex_comparison" => {
213
            // <lex, <=lex, >lex, >=lex are specialised to matrices; other types use the plain
214
            // ordering operators above instead.
215
34272
            ctx.typechecking_context = TypecheckingContext::Matrix;
216
34272
            parse_binary_expression(ctx, &inner)
217
        }
218
        "equality_comparison" => {
219
            // Equality works on any type, typechecking of operands will be handled within parse_binary_expression
220
1133401
            ctx.typechecking_context = TypecheckingContext::Unknown;
221
1133401
            parse_binary_expression(ctx, &inner)
222
        }
223
        "set_comparison" => {
224
            // Set comparisons require set operands (except 'in', which is hadled later)
225
138730
            ctx.typechecking_context = TypecheckingContext::Set;
226
138730
            parse_binary_expression(ctx, &inner)
227
        }
228
        "sequence_comparison" => {
229
            // Sequence comparisons (substring/subsequence) require sequence operands
230
696
            ctx.typechecking_context = TypecheckingContext::Sequence;
231
696
            parse_binary_expression(ctx, &inner)
232
        }
233
        "all_diff_comparison" => {
234
55729
            ctx.typechecking_context = TypecheckingContext::Matrix;
235
55729
            parse_all_diff_comparison(ctx, &inner)
236
        }
237
        "all_different_except_comparison" => {
238
64632
            ctx.typechecking_context = TypecheckingContext::Matrix;
239
64632
            parse_all_different_except_comparison(ctx, &inner)
240
        }
241
39840
        "global_cardinality_comparison" => parse_global_cardinality_comparison(ctx, &inner),
242
        _ => {
243
            ctx.record_error(RecoverableParseError::new(
244
                format!("Expected comparison expression, found '{}'", inner.kind()),
245
                Some(inner.range()),
246
            ));
247
            Ok(None)
248
        }
249
    }
250
1986629
}
251

            
252
1413714
fn parse_boolean_expression(
253
1413714
    ctx: &mut ParseContext,
254
1413714
    node: &Node,
255
1413714
) -> Result<Option<Expression>, FatalParseError> {
256
1413714
    ctx.typechecking_context = TypecheckingContext::Boolean;
257
1413714
    ctx.inner_typechecking_context = TypecheckingContext::Unknown;
258
1413714
    let Some(inner) = named_child!(recover, ctx, node) else {
259
        return Ok(None);
260
    };
261
1413714
    match inner.kind() {
262
        "atom" | "table" | "negative_table" | "short_table" => parse_atom(ctx, &inner),
263
21048
        "active_expr" => parse_active_expression(ctx, &inner),
264
662151
        "not_expr" | "sub_bool_expr" => parse_unary_expression(ctx, &inner),
265
544449
        "and_expr" | "or_expr" | "implication" | "iff_expr" => parse_binary_expression(ctx, &inner),
266
        "list_combining_expr_bool" => {
267
            // list-combining boolean operators accept either set or matrix operands
268
34393
            ctx.typechecking_context = TypecheckingContext::SetOrMatrix;
269

            
270
            // set inner context to boolean to ensure elements of list are boolean expressions
271
34393
            ctx.inner_typechecking_context = TypecheckingContext::Boolean;
272
34393
            parse_list_combining_expression(ctx, &inner)
273
        }
274
151673
        "quantifier_expr" => parse_quantifier_or_aggregate_expr(ctx, &inner),
275
        _ => {
276
            ctx.record_error(RecoverableParseError::new(
277
                format!("Expected boolean expression, found '{}'", inner.kind()),
278
                Some(inner.range()),
279
            ));
280
            Ok(None)
281
        }
282
    }
283
1413714
}
284

            
285
21048
fn parse_active_expression(
286
21048
    ctx: &mut ParseContext,
287
21048
    node: &Node,
288
21048
) -> Result<Option<Expression>, FatalParseError> {
289
21048
    let Some(variant_node) = field!(recover, ctx, node, "variant") else {
290
        return Ok(None);
291
    };
292
21048
    let saved_context = ctx.typechecking_context;
293
21048
    ctx.typechecking_context = TypecheckingContext::Unknown;
294
21048
    let variant = parse_expression(ctx, variant_node)?;
295
21048
    ctx.typechecking_context = saved_context;
296
21048
    let Some(variant) = variant else {
297
        return Ok(None);
298
    };
299
21048
    let Some(name_node) = field!(recover, ctx, node, "name") else {
300
        return Ok(None);
301
    };
302
21048
    let name = conjure_cp_core::ast::Name::user(
303
21048
        &ctx.source_code[name_node.start_byte()..name_node.end_byte()],
304
21048
    );
305
21048
    let ReturnType::Variant(entries) = variant.return_type() else {
306
        ctx.record_error(RecoverableParseError::new(
307
            format!("active expects a variant, got {variant}"),
308
            Some(node.range()),
309
        ));
310
        return Ok(None);
311
    };
312
31560
    if !entries.iter().any(|entry| entry.name == name) {
313
        ctx.record_error(RecoverableParseError::new(
314
            format!("unknown variant field `{name}`"),
315
            Some(name_node.range()),
316
        ));
317
        return Ok(None);
318
21048
    }
319
21048
    Ok(Some(Expression::Active(
320
21048
        Metadata::new(),
321
21048
        Moo::new(variant),
322
21048
        name,
323
21048
    )))
324
21048
}
325

            
326
437830
fn parse_list_combining_expression(
327
437830
    ctx: &mut ParseContext,
328
437830
    node: &Node,
329
437830
) -> Result<Option<Expression>, FatalParseError> {
330
437830
    let Some(operator_node) = field!(recover, ctx, node, "operator") else {
331
        return Ok(None);
332
    };
333
437830
    let operator_str = &ctx.source_code[operator_node.start_byte()..operator_node.end_byte()];
334

            
335
437830
    let Some(arg_node) = field!(recover, ctx, node, "arg") else {
336
        return Ok(None);
337
    };
338
    // While parsing inner, the typechecking context is SetOrMatrix
339
    // The inner context is either Boolean or Arithmetic so the elements of the set/matrix are typechecked correctly.
340
437830
    let Some(inner) = parse_expression(ctx, arg_node)? else {
341
200
        return Ok(None);
342
    };
343

            
344
437630
    let skip_operator = list_combining_skip_operator(operator_str);
345
437630
    let inner = set_comprehension_skip_operator(inner, skip_operator);
346

            
347
437630
    let expr = match operator_str {
348
28217
        "and" => Ok(Some(Expression::And(Metadata::new(), Moo::new(inner)))),
349
6100
        "or" => Ok(Some(Expression::Or(Metadata::new(), Moo::new(inner)))),
350
293585
        "sum" => Ok(Some(Expression::Sum(Metadata::new(), Moo::new(inner)))),
351
96
        "product" => Ok(Some(Expression::Product(Metadata::new(), Moo::new(inner)))),
352
73896
        "min" => Ok(Some(Expression::Min(Metadata::new(), Moo::new(inner)))),
353
35736
        "max" => Ok(Some(Expression::Max(Metadata::new(), Moo::new(inner)))),
354
        _ => {
355
            ctx.record_error(RecoverableParseError::new(
356
                format!("Invalid operator: '{operator_str}'"),
357
                Some(operator_node.range()),
358
            ));
359
            Ok(None)
360
        }
361
    };
362

            
363
437630
    if expr.is_ok() {
364
437630
        ctx.add_span_and_doc_hover(
365
437630
            &operator_node,
366
437630
            operator_str,
367
437630
            SymbolKind::Function,
368
437630
            None,
369
437630
            None,
370
437630
        );
371
    }
372

            
373
437630
    expr
374
437830
}
375

            
376
55873
fn parse_all_diff_comparison(
377
55873
    ctx: &mut ParseContext,
378
55873
    node: &Node,
379
55873
) -> Result<Option<Expression>, FatalParseError> {
380
55873
    let Some(arg_node) = field!(recover, ctx, node, "arg") else {
381
        return Ok(None);
382
    };
383
55873
    let Some(inner) = parse_expression(ctx, arg_node)? else {
384
24
        return Ok(None);
385
    };
386

            
387
55849
    let operator_node = node
388
55849
        .child_by_field_name("operator")
389
        .unwrap_or_else(|| node.child(0).unwrap());
390
    let operator_str =
391
55849
        ctx.source_code[operator_node.start_byte()..operator_node.end_byte()].to_string();
392
55849
    if !is_all_different_operator(&operator_str) {
393
        ctx.record_error(RecoverableParseError::new(
394
            format!("Invalid operator: '{operator_str}'"),
395
            Some(operator_node.range()),
396
        ));
397
        return Ok(None);
398
55849
    }
399

            
400
55849
    ctx.add_span_and_doc_hover(
401
55849
        &operator_node,
402
55849
        ALL_DIFFERENT,
403
55849
        SymbolKind::Function,
404
55849
        None,
405
55849
        None,
406
55849
    );
407
55849
    Ok(Some(Expression::AllDiff(Metadata::new(), Moo::new(inner))))
408
55873
}
409

            
410
64632
fn parse_all_different_except_comparison(
411
64632
    ctx: &mut ParseContext,
412
64632
    node: &Node,
413
64632
) -> Result<Option<Expression>, FatalParseError> {
414
64632
    let operator_node = node
415
64632
        .child_by_field_name("operator")
416
        .unwrap_or_else(|| node.child(0).unwrap());
417
    let operator_str =
418
64632
        ctx.source_code[operator_node.start_byte()..operator_node.end_byte()].to_string();
419
64632
    if !is_all_different_except_operator(&operator_str) {
420
        ctx.record_error(RecoverableParseError::new(
421
            format!("Invalid operator: '{operator_str}'"),
422
            Some(operator_node.range()),
423
        ));
424
        return Ok(None);
425
64632
    }
426

            
427
64632
    let Some(matrix_node) = field!(recover, ctx, node, "matrix") else {
428
        return Ok(None);
429
    };
430
64632
    let Some(except_node) = field!(recover, ctx, node, "except") else {
431
        return Ok(None);
432
    };
433

            
434
64632
    let saved_context = ctx.typechecking_context;
435
64632
    ctx.typechecking_context = TypecheckingContext::Unknown;
436
64632
    let Some(matrix) = parse_table_operand(ctx, &matrix_node)? else {
437
        ctx.typechecking_context = saved_context;
438
        return Ok(None);
439
    };
440
64632
    let Some(except) = parse_table_operand(ctx, &except_node)? else {
441
        ctx.typechecking_context = saved_context;
442
        return Ok(None);
443
    };
444
64632
    ctx.typechecking_context = saved_context;
445

            
446
64632
    ctx.add_span_and_doc_hover(
447
64632
        &operator_node,
448
64632
        ALL_DIFFERENT_EXCEPT,
449
64632
        SymbolKind::Function,
450
64632
        None,
451
64632
        None,
452
64632
    );
453
64632
    Ok(Some(Expression::AllDifferentExcept(
454
64632
        Metadata::new(),
455
64632
        Moo::new(matrix),
456
64632
        Moo::new(except),
457
64632
    )))
458
64632
}
459

            
460
39840
fn parse_global_cardinality_comparison(
461
39840
    ctx: &mut ParseContext,
462
39840
    node: &Node,
463
39840
) -> Result<Option<Expression>, FatalParseError> {
464
39840
    let Some(operator_node) = field!(recover, ctx, node, "operator") else {
465
        return Ok(None);
466
    };
467
    let operator_str =
468
39840
        ctx.source_code[operator_node.start_byte()..operator_node.end_byte()].to_string();
469

            
470
39840
    let Some(variables_node) = field!(recover, ctx, node, "variables") else {
471
        return Ok(None);
472
    };
473
39840
    let Some(arg2_node) = field!(recover, ctx, node, "arg2") else {
474
        return Ok(None);
475
    };
476
39840
    let Some(arg3_node) = field!(recover, ctx, node, "arg3") else {
477
        return Ok(None);
478
    };
479

            
480
39840
    let saved_context = ctx.typechecking_context;
481
39840
    ctx.typechecking_context = TypecheckingContext::Unknown;
482
39840
    let Some(variables) = parse_expression(ctx, variables_node)? else {
483
        ctx.typechecking_context = saved_context;
484
        return Ok(None);
485
    };
486
39840
    let Some(arg2) = parse_expression(ctx, arg2_node)? else {
487
        ctx.typechecking_context = saved_context;
488
        return Ok(None);
489
    };
490
39840
    let Some(arg3) = parse_expression(ctx, arg3_node)? else {
491
        ctx.typechecking_context = saved_context;
492
        return Ok(None);
493
    };
494
39840
    ctx.typechecking_context = saved_context;
495

            
496
39840
    ctx.add_span_and_doc_hover(
497
39840
        &operator_node,
498
39840
        match operator_str.as_str() {
499
39840
            op if is_at_least_operator(op) => AT_LEAST,
500
16704
            op if is_at_most_operator(op) => AT_MOST,
501
192
            _ => GLOBAL_CARDINALITY,
502
        },
503
39840
        SymbolKind::Function,
504
39840
        None,
505
39840
        None,
506
    );
507

            
508
39840
    match operator_str.as_str() {
509
39840
        op if is_at_least_operator(op) => Ok(Some(Expression::AtLeast(
510
23136
            Metadata::new(),
511
23136
            Moo::new(variables),
512
23136
            Moo::new(arg2),
513
23136
            Moo::new(arg3),
514
23136
        ))),
515
16704
        op if is_at_most_operator(op) => Ok(Some(Expression::AtMost(
516
16512
            Metadata::new(),
517
16512
            Moo::new(variables),
518
16512
            Moo::new(arg2),
519
16512
            Moo::new(arg3),
520
16512
        ))),
521
192
        op if is_global_cardinality_operator(op) => Ok(Some(Expression::Gcc(
522
192
            Metadata::new(),
523
192
            Moo::new(variables),
524
192
            Moo::new(arg2),
525
192
            Moo::new(arg3),
526
192
        ))),
527
        _ => {
528
            ctx.record_error(RecoverableParseError::new(
529
                format!("Invalid operator: '{operator_str}'"),
530
                Some(operator_node.range()),
531
            ));
532
            Ok(None)
533
        }
534
    }
535
39840
}
536

            
537
2327275
fn parse_unary_expression(
538
2327275
    ctx: &mut ParseContext,
539
2327275
    node: &Node,
540
2327275
) -> Result<Option<Expression>, FatalParseError> {
541
2327275
    let saved_context = ctx.typechecking_context;
542
2327275
    if node.kind() == "abs_value" {
543
        // Bars are overloaded for numeric absolute value and collection cardinality.
544
57894
        ctx.typechecking_context = TypecheckingContext::Unknown;
545
2269381
    }
546
2327275
    let Some(expr_node) = field!(recover, ctx, node, "expression") else {
547
        ctx.typechecking_context = saved_context;
548
        return Ok(None);
549
    };
550
2327275
    let Some(inner) = parse_expression(ctx, expr_node)? else {
551
        ctx.typechecking_context = saved_context;
552
        return Ok(None);
553
    };
554
2327275
    ctx.typechecking_context = saved_context;
555

            
556
2327275
    match node.kind() {
557
        "negative_expr" => {
558
533220
            if let Expression::Atomic(_, Atom::Literal(Literal::Int(value))) = inner {
559
462639
                Ok(Some(Expression::Atomic(
560
462639
                    Metadata::new(),
561
462639
                    Atom::Literal(Literal::Int(-value)),
562
462639
                )))
563
            } else {
564
70581
                Ok(Some(Expression::Neg(Metadata::new(), Moo::new(inner))))
565
            }
566
        }
567
        "abs_value" => {
568
57894
            let constructor = match inner.return_type() {
569
                ReturnType::Matrix(_)
570
                | ReturnType::Set(_)
571
                | ReturnType::MSet(_)
572
                | ReturnType::Sequence(_)
573
                | ReturnType::Function(_, _)
574
                | ReturnType::Relation(_)
575
                | ReturnType::Partition(_)
576
33245
                | ReturnType::Permutation(_) => Expression::Card,
577
24649
                _ => Expression::Abs,
578
            };
579
57894
            Ok(Some(constructor(Metadata::new(), Moo::new(inner))))
580
        }
581
153385
        "not_expr" => Ok(Some(Expression::Not(Metadata::new(), Moo::new(inner)))),
582
        "toInt_expr" => {
583
889542
            let to_int_keyword_node = child!(node, 0, "toInt");
584
889542
            ctx.add_span_and_doc_hover(
585
889542
                &to_int_keyword_node,
586
889542
                "toInt",
587
889542
                SymbolKind::Function,
588
889542
                None,
589
889542
                None,
590
889542
            );
591
889542
            Ok(Some(Expression::ToInt(Metadata::new(), Moo::new(inner))))
592
        }
593
        "factorial_expr" => {
594
            // looking for the operator node (either '!' at the end or 'factorial' at the start) to add hover info
595
1632
            if let Some(op_node) = (0..node.child_count())
596
2424
                .filter_map(|i| node.child(i))
597
2424
                .find(|c| matches!(c.kind(), "!" | "factorial"))
598
            {
599
1632
                ctx.add_span_and_doc_hover(
600
1632
                    &op_node,
601
1632
                    "post_factorial",
602
1632
                    SymbolKind::Function,
603
1632
                    None,
604
1632
                    None,
605
1632
                );
606
            }
607

            
608
1632
            Ok(Some(Expression::Factorial(
609
1632
                Metadata::new(),
610
1632
                Moo::new(inner),
611
1632
            )))
612
        }
613
691602
        "sub_bool_expr" | "sub_arith_expr" => Ok(Some(inner)),
614
        _ => {
615
            ctx.record_error(RecoverableParseError::new(
616
                format!("Unrecognised unary operation: '{}'", node.kind()),
617
                Some(node.range()),
618
            ));
619
            Ok(None)
620
        }
621
    }
622
2327275
}
623

            
624
3142277
pub fn parse_binary_expression(
625
3142277
    ctx: &mut ParseContext,
626
3142277
    node: &Node,
627
3142277
) -> Result<Option<Expression>, FatalParseError> {
628
3142277
    let Some(op_node) = field!(recover, ctx, node, "operator") else {
629
        return Ok(None);
630
    };
631
3142277
    let op_str = &ctx.source_code[op_node.start_byte()..op_node.end_byte()];
632

            
633
3142277
    let saved_ctx = ctx.typechecking_context;
634

            
635
    // Special handling for 'in' operator, as the left operand doesn't have to be a set
636
3142277
    if op_str == "in" {
637
123478
        ctx.typechecking_context = TypecheckingContext::Unknown
638
3018799
    }
639

            
640
    // Minus spells both arithmetic subtraction and set difference, so neither operand can be held
641
    // to the arithmetic context until the operand types say which one this is.
642
3142277
    if op_str == "-" {
643
85073
        ctx.typechecking_context = TypecheckingContext::Unknown;
644
3057204
    }
645

            
646
    // parse left operand
647
3142277
    let Some(left_node) = field!(recover, ctx, node, "left") else {
648
        return Ok(None);
649
    };
650
3142277
    let Some(left) = parse_expression(ctx, left_node)? else {
651
512
        return Ok(None);
652
    };
653

            
654
    // reset context, if needed
655
3141765
    ctx.typechecking_context = saved_ctx;
656

            
657
    // Equality/inequality/ordering: enforce right operand to match left operand type when
658
    // inferable. Ordering (<, <=, >, >=) works on any orderable type now, the same as equality,
659
    // so it needs the same operand-matching check.
660
3141765
    if matches!(op_str, "=" | "!=" | "<" | "<=" | ">" | ">=") {
661
1652418
        ctx.typechecking_context = inferred_context_from_expression(&left);
662
1489347
    }
663

            
664
    // 'in's right operand is the container, which need not be a set (mset/sequence-via-toSet/
665
    // relation/etc all support membership too); the grammar's "set_comparison" node kind sets
666
    // `saved_ctx` to `Set` unconditionally, which only the left operand's override above escapes.
667
3141765
    if op_str == "in" {
668
123454
        ctx.typechecking_context = TypecheckingContext::Unknown;
669
3018311
    }
670

            
671
3141765
    if op_str == "-" {
672
85049
        ctx.typechecking_context = TypecheckingContext::Unknown;
673
3056716
    }
674

            
675
    // parse right operand
676
3141765
    let Some(right_node) = field!(recover, ctx, node, "right") else {
677
        return Ok(None);
678
    };
679
3141765
    let Some(right) = parse_expression(ctx, right_node)? else {
680
412
        return Ok(None);
681
    };
682

            
683
    // restore original contexts for parent expression parsing
684
3141353
    ctx.typechecking_context = saved_ctx;
685

            
686
3141353
    if matches!(op_str, "=" | "!=" | "<" | "<=" | ">" | ">=") {
687
        // Parameter-dependent local lettings may not have a domain yet. Only check types
688
        // that can already be inferred, without requiring their domains to resolve.
689
1652078
        let left_type = left.try_return_type().unwrap_or(ReturnType::Unknown);
690
1652078
        let right_type = right.try_return_type().unwrap_or(ReturnType::Unknown);
691
1652078
        if matches!(left_type, ReturnType::UnnamedType(_))
692
1652074
            || matches!(right_type, ReturnType::UnnamedType(_))
693
        {
694
4
            if matches!(op_str, "<" | "<=" | ">" | ">=") {
695
                ctx.record_error(RecoverableParseError::new(
696
                    "Unnamed types do not support ordering".to_string(),
697
                    Some(node.range()),
698
                ));
699
                return Ok(None);
700
4
            }
701
4
            if matches!(op_str, "=" | "!=") && left_type != right_type {
702
                ctx.record_error(RecoverableParseError::new(
703
                    format!("Cannot compare different types: {left_type} and {right_type}"),
704
                    Some(node.range()),
705
                ));
706
                return Ok(None);
707
4
            }
708
1652074
        }
709
1489275
    }
710

            
711
3141353
    let mut doc_name = "";
712
3141353
    let expr = match op_str {
713
        // NB: We are deliberately setting the index domain to 1.., not 1..2.
714
        // Semantically, this means "a list that can grow/shrink arbitrarily".
715
        // This is expected by rules which will modify the terms of the sum expression
716
        // (e.g. by partially evaluating them).
717
        "+" => {
718
294964
            doc_name = "L_Plus";
719
294964
            Ok(Some(Expression::Sum(
720
294964
                Metadata::new(),
721
294964
                Moo::new(matrix_expr![left, right; domain_int!(1..)]),
722
294964
            )))
723
        }
724
        "-" => {
725
85049
            doc_name = "L_Minus";
726
85049
            if is_set_valued(&left) || is_set_valued(&right) {
727
3600
                Ok(Some(Expression::Difference(
728
3600
                    Metadata::new(),
729
3600
                    Moo::new(left),
730
3600
                    Moo::new(right),
731
3600
                )))
732
            } else {
733
                // Subtraction, so the operands do have to be integers. The arithmetic context
734
                // could not be imposed while parsing them, since it would have rejected the set
735
                // operands that make this a difference instead.
736
81449
                if !require_int_operand(ctx, &left_node, &left)
737
81377
                    || !require_int_operand(ctx, &right_node, &right)
738
                {
739
72
                    return Ok(None);
740
81377
                }
741
81377
                Ok(Some(Expression::Minus(
742
81377
                    Metadata::new(),
743
81377
                    Moo::new(left),
744
81377
                    Moo::new(right),
745
81377
                )))
746
            }
747
        }
748
        "*" => {
749
183733
            doc_name = "L_Times";
750
183733
            Ok(Some(Expression::Product(
751
183733
                Metadata::new(),
752
183733
                Moo::new(matrix_expr![left, right; domain_int!(1..)]),
753
183733
            )))
754
        }
755
        "/\\" => {
756
60382
            doc_name = "and";
757
60382
            Ok(Some(Expression::And(
758
60382
                Metadata::new(),
759
60382
                Moo::new(matrix_expr![left, right; domain_int!(1..)]),
760
60382
            )))
761
        }
762
        "\\/" => {
763
            // No documentation for or in Bits yet
764
75876
            doc_name = "or";
765
75876
            Ok(Some(Expression::Or(
766
75876
                Metadata::new(),
767
75876
                Moo::new(matrix_expr![left, right; domain_int!(1..)]),
768
75876
            )))
769
        }
770
        "**" => {
771
77341
            doc_name = "L_Pow";
772
77341
            Ok(Some(Expression::UnsafePow(
773
77341
                Metadata::new(),
774
77341
                Moo::new(left),
775
77341
                Moo::new(right),
776
77341
            )))
777
        }
778
        "/" => {
779
            //TODO: add checks for if division is safe or not
780
68461
            doc_name = "L_Div";
781
68461
            Ok(Some(Expression::UnsafeDiv(
782
68461
                Metadata::new(),
783
68461
                Moo::new(left),
784
68461
                Moo::new(right),
785
68461
            )))
786
        }
787
        "%" => {
788
            //TODO: add checks for if mod is safe or not
789
52288
            doc_name = "L_Mod";
790
52288
            Ok(Some(Expression::UnsafeMod(
791
52288
                Metadata::new(),
792
52288
                Moo::new(left),
793
52288
                Moo::new(right),
794
52288
            )))
795
        }
796

            
797
        "=" => {
798
1031547
            doc_name = "L_Eq"; //no docs yet
799
1031547
            Ok(Some(Expression::Eq(
800
1031547
                Metadata::new(),
801
1031547
                Moo::new(left),
802
1031547
                Moo::new(right),
803
1031547
            )))
804
        }
805
        "!=" => {
806
101322
            doc_name = "L_Neq"; //no docs yet
807
101322
            Ok(Some(Expression::Neq(
808
101322
                Metadata::new(),
809
101322
                Moo::new(left),
810
101322
                Moo::new(right),
811
101322
            )))
812
        }
813
        "<=" => {
814
273907
            doc_name = "L_Leq"; //no docs yet
815
273907
            Ok(Some(Expression::Leq(
816
273907
                Metadata::new(),
817
273907
                Moo::new(left),
818
273907
                Moo::new(right),
819
273907
            )))
820
        }
821
        ">=" => {
822
96359
            doc_name = "L_Geq"; //no docs yet
823
96359
            Ok(Some(Expression::Geq(
824
96359
                Metadata::new(),
825
96359
                Moo::new(left),
826
96359
                Moo::new(right),
827
96359
            )))
828
        }
829
        "<" => {
830
106214
            doc_name = "L_Lt"; //no docs yet
831
106214
            Ok(Some(Expression::Lt(
832
106214
                Metadata::new(),
833
106214
                Moo::new(left),
834
106214
                Moo::new(right),
835
106214
            )))
836
        }
837
        ">" => {
838
42729
            doc_name = "L_Gt"; //no docs yet
839
42729
            Ok(Some(Expression::Gt(
840
42729
                Metadata::new(),
841
42729
                Moo::new(left),
842
42729
                Moo::new(right),
843
42729
            )))
844
        }
845

            
846
        "->" => {
847
107366
            doc_name = "L_Imply"; //no docs yet
848
107366
            Ok(Some(Expression::Imply(
849
107366
                Metadata::new(),
850
107366
                Moo::new(left),
851
107366
                Moo::new(right),
852
107366
            )))
853
        }
854
        "<->" => {
855
300725
            doc_name = "L_Iff"; //no docs yet
856
300725
            Ok(Some(Expression::Iff(
857
300725
                Metadata::new(),
858
300725
                Moo::new(left),
859
300725
                Moo::new(right),
860
300725
            )))
861
        }
862
        "<lex" => {
863
9312
            doc_name = "L_LexLt"; //no docs yet
864
9312
            Ok(Some(Expression::LexLt(
865
9312
                Metadata::new(),
866
9312
                Moo::new(left),
867
9312
                Moo::new(right),
868
9312
            )))
869
        }
870
        ">lex" => {
871
1968
            doc_name = "L_LexGt"; //no docs yet
872
1968
            Ok(Some(Expression::LexGt(
873
1968
                Metadata::new(),
874
1968
                Moo::new(left),
875
1968
                Moo::new(right),
876
1968
            )))
877
        }
878
        "<=lex" => {
879
19080
            doc_name = "L_LexLeq"; //no docs yet
880
19080
            Ok(Some(Expression::LexLeq(
881
19080
                Metadata::new(),
882
19080
                Moo::new(left),
883
19080
                Moo::new(right),
884
19080
            )))
885
        }
886
        ">=lex" => {
887
3912
            doc_name = "L_LexGeq"; //no docs yet
888
3912
            Ok(Some(Expression::LexGeq(
889
3912
                Metadata::new(),
890
3912
                Moo::new(left),
891
3912
                Moo::new(right),
892
3912
            )))
893
        }
894
        "in" => {
895
123454
            doc_name = "L_in";
896
123454
            Ok(Some(Expression::In(
897
123454
                Metadata::new(),
898
123454
                Moo::new(left),
899
123454
                Moo::new(right),
900
123454
            )))
901
        }
902
        "subset" => {
903
3840
            doc_name = "L_subset";
904
3840
            Ok(Some(Expression::Subset(
905
3840
                Metadata::new(),
906
3840
                Moo::new(left),
907
3840
                Moo::new(right),
908
3840
            )))
909
        }
910
        "subsetEq" => {
911
10764
            doc_name = "L_subsetEq";
912
10764
            Ok(Some(Expression::SubsetEq(
913
10764
                Metadata::new(),
914
10764
                Moo::new(left),
915
10764
                Moo::new(right),
916
10764
            )))
917
        }
918
        "supset" => {
919
288
            doc_name = "L_supset";
920
288
            Ok(Some(Expression::Supset(
921
288
                Metadata::new(),
922
288
                Moo::new(left),
923
288
                Moo::new(right),
924
288
            )))
925
        }
926
        "supsetEq" => {
927
288
            doc_name = "L_supsetEq";
928
288
            Ok(Some(Expression::SupsetEq(
929
288
                Metadata::new(),
930
288
                Moo::new(left),
931
288
                Moo::new(right),
932
288
            )))
933
        }
934
        "substring" => {
935
456
            doc_name = "L_substring";
936
456
            Ok(Some(Expression::Substring(
937
456
                Metadata::new(),
938
456
                Moo::new(left),
939
456
                Moo::new(right),
940
456
            )))
941
        }
942
        "subsequence" => {
943
240
            doc_name = "L_subsequence";
944
240
            Ok(Some(Expression::Subsequence(
945
240
                Metadata::new(),
946
240
                Moo::new(left),
947
240
                Moo::new(right),
948
240
            )))
949
        }
950
        "union" => {
951
3700
            doc_name = "L_union";
952
3700
            Ok(Some(Expression::Union(
953
3700
                Metadata::new(),
954
3700
                Moo::new(left),
955
3700
                Moo::new(right),
956
3700
            )))
957
        }
958
        "intersect" => {
959
5788
            doc_name = "L_intersect";
960
5788
            Ok(Some(Expression::Intersect(
961
5788
                Metadata::new(),
962
5788
                Moo::new(left),
963
5788
                Moo::new(right),
964
5788
            )))
965
        }
966
        _ => {
967
            ctx.record_error(RecoverableParseError::new(
968
                format!("Invalid operator: '{op_str}'"),
969
                Some(op_node.range()),
970
            ));
971
            Ok(None)
972
        }
973
    };
974

            
975
3141281
    if expr.is_ok() {
976
3141281
        ctx.add_span_and_doc_hover(&op_node, doc_name, SymbolKind::Function, None, None);
977
    }
978

            
979
3141281
    expr
980
3142277
}
981

            
982
7146
pub fn parse_annotation_expression(
983
7146
    ctx: &mut ParseContext,
984
7146
    node: &Node,
985
7146
) -> Result<Option<Expression>, FatalParseError> {
986
7146
    let annotation_node = if node.kind() == "annotation_expr" {
987
7146
        let Some(inner) = named_child!(recover, ctx, node) else {
988
            return Ok(None);
989
        };
990
7146
        inner
991
    } else {
992
        *node
993
    };
994

            
995
7146
    let Some(left_node) = field!(recover, ctx, &annotation_node, "left") else {
996
        return Ok(None);
997
    };
998
7146
    let left_node = if left_node.kind() == "annotation_subject" {
999
7146
        let Some(inner) = named_child!(recover, ctx, &left_node) else {
            return Ok(None);
        };
7146
        inner
    } else {
        left_node
    };
7146
    let left_node = match left_node.kind() {
        "sub_arith_expr" | "sub_bool_expr" | "sub_atom_expr" => {
1
            let Some(inner) = field!(recover, ctx, &left_node, "expression") else {
                return Ok(None);
            };
1
            inner
        }
7145
        _ => left_node,
    };
7146
    let saved_context = ctx.typechecking_context;
7146
    ctx.typechecking_context = TypecheckingContext::Unknown;
7146
    let left = match left_node.kind() {
17
        "constant" | "identifier" | "metavar" => parse_atom(ctx, &left_node)?,
7129
        _ => parse_expression(ctx, left_node)?,
    };
7146
    let Some(left) = left else {
        ctx.typechecking_context = saved_context;
        return Ok(None);
    };
7146
    ctx.typechecking_context = saved_context;
7146
    let Some(operator_node) = field!(recover, ctx, &annotation_node, "operator") else {
        return Ok(None);
    };
7146
    let operator_str = &ctx.source_code[operator_node.start_byte()..operator_node.end_byte()];
7146
    match annotation_node.kind() {
        "type_annotation" => {
7136
            let Some(type_node) = field!(recover, ctx, &annotation_node, "type") else {
                return Ok(None);
            };
7136
            let Some(domain) = parse_domain(ctx, type_node)? else {
                return Ok(None);
            };
7136
            ctx.add_span_and_doc_hover(
7136
                &operator_node,
7136
                operator_str,
7136
                SymbolKind::Function,
7136
                None,
7136
                None,
7136
            );
7136
            Ok(Some(Expression::TypeAnnotation(
7136
                Metadata::new(),
7136
                Moo::new(left),
7136
                domain,
7136
            )))
        }
        "domain_annotation" => {
10
            let Some(domain_node) = field!(recover, ctx, &annotation_node, "domain") else {
                return Ok(None);
            };
10
            let Some(domain) = parse_domain(ctx, domain_node)? else {
                return Ok(None);
            };
10
            ctx.add_span_and_doc_hover(
10
                &operator_node,
10
                operator_str,
10
                SymbolKind::Function,
10
                None,
10
                None,
10
            );
10
            Ok(Some(Expression::DomainAnnotation(
10
                Metadata::new(),
10
                Moo::new(left),
10
                domain,
10
            )))
        }
        _ => {
            ctx.record_error(RecoverableParseError::new(
                format!(
                    "Unrecognised annotation expression: '{}'",
                    annotation_node.kind()
                ),
                Some(annotation_node.range()),
            ));
            Ok(None)
        }
    }
7146
}
1652418
fn inferred_context_from_expression(expr: &Expression) -> TypecheckingContext {
    // TODO: typechecking for index/slice expressions
1652418
    if matches!(
1652418
        expr,
        Expression::UnsafeIndex(_, _, _) | Expression::UnsafeSlice(_, _, _)
    ) {
295183
        return TypecheckingContext::Unknown;
1357235
    }
1357235
    let Some(domain) = expr.domain_of() else {
36272
        return TypecheckingContext::Unknown;
    };
1320963
    let Ok(ground) = domain.resolve() else {
16068
        return TypecheckingContext::Unknown;
    };
1304895
    match ground.as_ref() {
7088
        GroundDomain::Empty(_) => TypecheckingContext::Unknown,
30998
        GroundDomain::Bool => TypecheckingContext::Boolean,
1155072
        GroundDomain::Int(_, _) => TypecheckingContext::Arithmetic,
7008
        GroundDomain::Tuple(_, _) => TypecheckingContext::Tuple,
7008
        GroundDomain::Record(_, _) => TypecheckingContext::Record,
17544
        GroundDomain::Variant(_, _) => TypecheckingContext::Unknown,
21360
        GroundDomain::Matrix(_, _, _) => TypecheckingContext::Matrix,
10656
        GroundDomain::Sequence(_, _) => TypecheckingContext::Sequence,
21848
        GroundDomain::Set(_, _) => TypecheckingContext::Set,
25512
        GroundDomain::MSet(_, _) => TypecheckingContext::MSet,
        GroundDomain::EnumeratedType(_, _) | GroundDomain::UnnamedType(_) => {
8
            TypecheckingContext::Unknown
        }
72
        GroundDomain::Function(_, _, _) => TypecheckingContext::Function,
24
        GroundDomain::Relation(_, _) => TypecheckingContext::Relation,
216
        GroundDomain::Partition(_, _) => TypecheckingContext::Partition,
481
        GroundDomain::Permutation(_, _) => TypecheckingContext::Permutation,
    }
1652418
}
437630
fn list_combining_skip_operator(operator_str: &str) -> Option<ACOperatorKind> {
437630
    match operator_str {
28217
        "and" => Some(ACOperatorKind::And),
6100
        "or" => Some(ACOperatorKind::Or),
293585
        "sum" => Some(ACOperatorKind::Sum),
96
        "product" => Some(ACOperatorKind::Product),
        // min/max are not true AC operators (no universal identity element), but still need
        // their own skip-operator tag so a symbolic guard inside `min([... | ...])` lowers
        // correctly (previously mapped to Sum, which silently substituted 0 for guarded-out
        // elements -- wrong for anything but a min/max whose correct answer happens to be <=/>= 0).
73896
        "min" => Some(ACOperatorKind::Min),
35736
        "max" => Some(ACOperatorKind::Max),
        _ => None,
    }
437630
}
437630
fn set_comprehension_skip_operator(
437630
    inner: Expression,
437630
    skip_operator: Option<ACOperatorKind>,
437630
) -> Expression {
437630
    let Expression::Comprehension(meta, comprehension) = inner else {
384942
        return inner;
    };
52688
    if let Some(skip_operator) = skip_operator {
52688
        let mut comprehension = Moo::unwrap_or_clone(comprehension);
52688
        comprehension.skip_operator = Some(skip_operator);
52688
        Expression::Comprehension(meta, Moo::new(comprehension))
    } else {
        Expression::Comprehension(meta, comprehension)
    }
437630
}
/// Parses `catchUndef(expression, default)`.
10152
fn parse_catch_undef_expression(
10152
    ctx: &mut ParseContext,
10152
    node: &Node,
10152
) -> Result<Option<Expression>, FatalParseError> {
10152
    let saved_context = ctx.typechecking_context;
10152
    ctx.typechecking_context = TypecheckingContext::Unknown;
10152
    let Some(expression_node) = field!(recover, ctx, node, "expression") else {
        return Ok(None);
    };
10152
    let Some(default_node) = field!(recover, ctx, node, "default") else {
        return Ok(None);
    };
10152
    let Some(expression) = parse_expression(ctx, expression_node)? else {
        return Ok(None);
    };
10152
    ctx.typechecking_context = TypecheckingContext::Unknown;
10152
    let Some(default) = parse_expression(ctx, default_node)? else {
        return Ok(None);
    };
10152
    ctx.typechecking_context = saved_context;
10152
    Ok(Some(Expression::CatchUndef(
10152
        Metadata::new(),
10152
        Moo::new(expression),
10152
        Moo::new(default),
10152
    )))
10152
}
/// Whether `expr` is known to be set-valued, used to read `-` as set difference.
166498
fn is_set_valued(expr: &Expression) -> bool {
166498
    matches!(expr.try_return_type(), Some(ReturnType::Set(_)))
166498
}
/// Reports a type error when an arithmetic operand is not an integer.
///
/// Returns whether the operand is acceptable.
162826
fn require_int_operand(ctx: &mut ParseContext, node: &Node, expr: &Expression) -> bool {
    // An operand whose type cannot be worked out yet gets the benefit of the doubt.
162826
    let Some(actual) = expr.try_return_type() else {
        return true;
    };
162826
    if matches!(actual, ReturnType::Int | ReturnType::Unknown) {
162754
        return true;
72
    }
72
    ctx.record_error(RecoverableParseError::new(
72
        format!(
72
            "Type error: {}\n\tExpected: int\n\tGot: {}",
72
            ctx.source_code[node.start_byte()..node.end_byte()].trim(),
72
            type_name(&actual)
72
        ),
72
        Some(node.range()),
72
    ));
72
    false
162826
}
/// The bare name of a type, as type errors spell it: `partition`, not `partition of int`.
72
fn type_name(return_type: &ReturnType) -> &'static str {
72
    match return_type {
        ReturnType::Unknown => "unknown",
        ReturnType::Bool => "bool",
        ReturnType::Int => "int",
        ReturnType::Tuple(_) => "tuple",
        ReturnType::Record(_) => "record",
        ReturnType::Variant(_) => "variant",
        ReturnType::Matrix(_) => "matrix",
        ReturnType::Sequence(_) => "sequence",
        ReturnType::Set(_) => "set",
        ReturnType::MSet(_) => "mset",
        ReturnType::Function(_, _) => "function",
        ReturnType::Relation(_) => "relation",
72
        ReturnType::Partition(_) => "partition",
        ReturnType::Permutation(_) => "permutation",
        ReturnType::EnumeratedType(_) => "enum",
        ReturnType::UnnamedType(_) => "unnamed",
    }
72
}