1
use std::collections::VecDeque;
2

            
3
use crate::diagnostics::diagnostics_api::SymbolKind;
4
use crate::diagnostics::source_map::{HoverInfo, span_with_hover};
5
use crate::errors::{FatalParseError, RecoverableParseError};
6
use crate::expression::{parse_annotation_expression, parse_binary_expression, parse_expression};
7
use crate::parser::ParseContext;
8
use crate::parser::abstract_literal::parse_abstract;
9
use crate::parser::comprehension::parse_comprehension;
10
use crate::parser::domain::parse_domain;
11
use crate::parser::dominance::parse_pareto_expression;
12
use crate::parser::global_constraints::ELEMENT_ID;
13
use crate::util::{TypecheckingContext, named_children};
14
use crate::{field, named_child};
15

            
16
use conjure_cp_core::ast::{
17
    Atom, DeclarationKind, DeclarationPtr, Expression, GroundDomain, Literal, Metadata, Moo, Name,
18
    ReturnType, Typeable,
19
};
20

            
21
use tree_sitter::Node;
22
use ustr::Ustr;
23

            
24
23832794
pub fn parse_atom(
25
23832794
    ctx: &mut ParseContext,
26
23832794
    node: &Node,
27
23832794
) -> Result<Option<Expression>, FatalParseError> {
28
23832794
    match node.kind() {
29
        "atom" | "primary_atom" | "sub_atom_expr" => {
30
11380495
            let Some(inner) = named_child!(recover, ctx, node) else {
31
                return Ok(None);
32
            };
33
11380495
            parse_atom(ctx, &inner)
34
        }
35
        "metavar" => {
36
2769
            let Some(ident) = field!(recover, ctx, node, "identifier") else {
37
                return Ok(None);
38
            };
39
2769
            let name_str = &ctx.source_code[ident.start_byte()..ident.end_byte()];
40
2769
            Ok(Some(Expression::Metavar(
41
2769
                Metadata::new(),
42
2769
                Ustr::from(name_str),
43
2769
            )))
44
        }
45
        "identifier" => {
46
5241752
            let Some(var) = parse_variable(ctx, node)? else {
47
705
                return Ok(None);
48
            };
49
5241047
            Ok(Some(Expression::Atomic(Metadata::new(), var)))
50
        }
51
        "from_solution" => {
52
24384
            if ctx.root.kind() != "dominance_relation" {
53
                ctx.record_error(RecoverableParseError::new(
54
                    "fromSolution only allowed inside dominance relations".to_string(),
55
                    Some(node.range()),
56
                ));
57
                return Ok(None);
58
24384
            }
59

            
60
24384
            let Some(var_node) = field!(recover, ctx, node, "variable") else {
61
                return Ok(None);
62
            };
63
24384
            let Some(inner) = parse_variable(ctx, &var_node)? else {
64
                return Ok(None);
65
            };
66

            
67
24384
            Ok(Some(Expression::FromSolution(
68
24384
                Metadata::new(),
69
24384
                Moo::new(inner),
70
24384
            )))
71
        }
72
9027
        "pareto_expression" => parse_pareto_expression(ctx, node),
73
        "constant" => {
74
4411828
            let Some(lit) = parse_constant(ctx, node)? else {
75
240
                return Ok(None);
76
            };
77
4411588
            Ok(Some(Expression::Atomic(
78
4411588
                Metadata::new(),
79
4411588
                Atom::Literal(lit),
80
4411588
            )))
81
        }
82
        "matrix"
83
        | "record"
84
        | "variant"
85
        | "tuple"
86
        | "set_literal"
87
        | "mset_literal"
88
        | "sequence_literal"
89
        | "function_literal"
90
        | "relation_literal"
91
        | "partition_literal"
92
        | "permutation_literal" => {
93
1767445
            let Some(abs) = parse_abstract(ctx, node)? else {
94
264
                return Ok(None);
95
            };
96
1767181
            Ok(Some(Expression::AbstractLiteral(Metadata::new(), abs)))
97
        }
98
32208
        "flatten" => parse_flatten(ctx, node),
99
3480
        "element_id" => parse_element_id(ctx, node),
100
143689
        "table" | "negative_table" | "short_table" => parse_table(ctx, node),
101
689050
        "index_or_slice" => parse_index_or_slice(ctx, node),
102
57
        "annotation_expr" => parse_annotation_expression(ctx, node),
103
        // for now, assume is binary since powerset isn't implemented
104
        // TODO: add powerset support under "set_operation"
105
9488
        "set_operation" => parse_binary_expression(ctx, node),
106
61592
        "comprehension" => parse_comprehension(ctx, node),
107
        "defined_expr" | "range_expr" | "to_set_expr" | "to_mset_expr" | "to_relation_expr"
108
3721
        | "perm_inverse_expr" => parse_function_unary_operator(ctx, node),
109
        "enum_operator" => {
110
8
            let Some(argument) = field!(recover, ctx, node, "argument") else {
111
                return Ok(None);
112
            };
113
8
            let saved = ctx.typechecking_context;
114
8
            ctx.typechecking_context = TypecheckingContext::Unknown;
115
8
            let parsed = parse_expression(ctx, argument);
116
8
            ctx.typechecking_context = saved;
117
8
            let Some(expression) = parsed? else {
118
                return Ok(None);
119
            };
120
8
            if !matches!(expression.return_type(), ReturnType::EnumeratedType(_)) {
121
                ctx.record_error(RecoverableParseError::new(
122
                    "Expected an enum member".to_owned(),
123
                    Some(argument.range()),
124
                ));
125
                return Ok(None);
126
8
            }
127
8
            let Some(operator) = field!(recover, ctx, node, "operator") else {
128
                return Ok(None);
129
            };
130
            let constructor =
131
8
                if &ctx.source_code[operator.start_byte()..operator.end_byte()] == "pred" {
132
                    Expression::Pred
133
                } else {
134
8
                    Expression::Succ
135
                };
136
8
            Ok(Some(constructor(Metadata::new(), Moo::new(expression))))
137
        }
138
42316
        "apply_expr" => parse_apply_expr(ctx, node),
139
        "image_expr" | "image_set_expr" | "pre_image_expr" | "inverse_expr" | "compose_expr" => {
140
9125
            parse_function_binary_operator(ctx, node)
141
        }
142
72
        "restrict_expr" => parse_restrict_expr(ctx, node),
143
        "attribute_as_constraint_expr" => parse_attribute_as_constraint(ctx, node),
144
120
        "parts_expr" | "participants_expr" => parse_partition_unary_operator(ctx, node),
145
168
        "together_expr" | "apart_expr" | "party_expr" => parse_partition_binary_operator(ctx, node),
146
        _ => {
147
            ctx.record_error(RecoverableParseError::new(
148
                format!("Expected atom, got: {}", node.kind()),
149
                Some(node.range()),
150
            ));
151
            Ok(None)
152
        }
153
    }
154
23832794
}
155

            
156
32208
fn parse_flatten(
157
32208
    ctx: &mut ParseContext,
158
32208
    node: &Node,
159
32208
) -> Result<Option<Expression>, FatalParseError> {
160
    // add error and return early if we're in a set context, since flatten doesn't produce sets
161
32208
    if ctx.typechecking_context == TypecheckingContext::Set {
162
        ctx.record_error(RecoverableParseError::new(
163
            format!(
164
                "Type error: {}\n\tExpected: set\n\tGot: flatten",
165
                ctx.source_code[node.start_byte()..node.end_byte()].trim()
166
            ),
167
            Some(node.range()),
168
        ));
169
        return Ok(None);
170
32208
    }
171

            
172
32208
    let Some(expr_node) = field!(recover, ctx, node, "expression") else {
173
        return Ok(None);
174
    };
175
    // TODO: verify the atom is a matrix
176
32208
    let Some(expr) = parse_atom(ctx, &expr_node)? else {
177
        return Ok(None);
178
    };
179

            
180
32208
    if let Some(depth_node) = node.child_by_field_name("depth") {
181
        let Some(depth) = parse_int(ctx, &depth_node) else {
182
            return Ok(None);
183
        };
184
        let depth_expression =
185
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(depth)));
186
        Ok(Some(Expression::Flatten(
187
            Metadata::new(),
188
            Some(Moo::new(depth_expression)),
189
            Moo::new(expr),
190
        )))
191
    } else {
192
32208
        Ok(Some(Expression::Flatten(
193
32208
            Metadata::new(),
194
32208
            None,
195
32208
            Moo::new(expr),
196
32208
        )))
197
    }
198
32208
}
199

            
200
143689
fn parse_table(ctx: &mut ParseContext, node: &Node) -> Result<Option<Expression>, FatalParseError> {
201
    // add error and return early if we're in a set context, since tables aren't allowed there
202
143689
    if ctx.typechecking_context == TypecheckingContext::Set {
203
        ctx.record_error(RecoverableParseError::new(
204
            format!(
205
                "Type error: {}\n\tExpected: set\n\tGot: table",
206
                ctx.source_code[node.start_byte()..node.end_byte()].trim()
207
            ),
208
            Some(node.range()),
209
        ));
210
        return Ok(None);
211
143689
    }
212

            
213
    // the variables and rows can contain arbitrary expressions, so we temporarily set the context to Unknown to avoid typechecking errors
214
143689
    let saved_context = ctx.typechecking_context;
215
143689
    ctx.typechecking_context = TypecheckingContext::Unknown;
216

            
217
143689
    let Some(variables_node) = field!(recover, ctx, node, "variables") else {
218
        return Ok(None);
219
    };
220
143689
    let Some(variables) = parse_table_operand(ctx, &variables_node)? else {
221
        return Ok(None);
222
    };
223

            
224
143689
    let Some(rows_node) = field!(recover, ctx, node, "rows") else {
225
        return Ok(None);
226
    };
227
143689
    let Some(rows) = parse_table_operand(ctx, &rows_node)? else {
228
        return Ok(None);
229
    };
230

            
231
143689
    ctx.typechecking_context = saved_context;
232

            
233
143689
    match node.kind() {
234
60961
        "table" => Ok(Some(Expression::Table(
235
60961
            Metadata::new(),
236
60961
            Moo::new(variables),
237
60961
            Moo::new(rows),
238
60961
        ))),
239
50664
        "short_table" => Ok(Some(Expression::ShortTable(
240
50664
            Metadata::new(),
241
50664
            Moo::new(variables),
242
50664
            Moo::new(rows),
243
50664
        ))),
244
32064
        "negative_table" => Ok(Some(Expression::NegativeTable(
245
32064
            Metadata::new(),
246
32064
            Moo::new(variables),
247
32064
            Moo::new(rows),
248
32064
        ))),
249
        _ => {
250
            ctx.record_error(RecoverableParseError::new(
251
                format!(
252
                    "Expected 'table' or 'negative_table', got: '{}'",
253
                    node.kind()
254
                ),
255
                Some(node.range()),
256
            ));
257
            Ok(None)
258
        }
259
    }
260
143689
}
261

            
262
420122
pub fn parse_table_operand(
263
420122
    ctx: &mut ParseContext,
264
420122
    node: &Node,
265
420122
) -> Result<Option<Expression>, FatalParseError> {
266
420122
    match node.kind() {
267
        "identifier" | "constant" | "matrix" | "index_or_slice" | "flatten" => {
268
137186
            parse_atom(ctx, node)
269
        }
270
        "sub_arith_expr" | "negative_expr" | "abs_value" | "factorial_expr" | "arithmetic_expr"
271
        | "product_expr" | "sum_expr" | "exponent" | "bool_expr" | "comparison_expr" => {
272
            parse_expression(ctx, *node)
273
        }
274
282936
        _ => parse_expression(ctx, *node),
275
    }
276
420122
}
277

            
278
3480
fn parse_element_id(
279
3480
    ctx: &mut ParseContext,
280
3480
    node: &Node,
281
3480
) -> Result<Option<Expression>, FatalParseError> {
282
3480
    if ctx.typechecking_context == TypecheckingContext::Set {
283
        ctx.record_error(RecoverableParseError::new(
284
            format!(
285
                "Type error: {}\n\tExpected: set\n\tGot: elementId",
286
                ctx.source_code[node.start_byte()..node.end_byte()].trim()
287
            ),
288
            Some(node.range()),
289
        ));
290
        return Ok(None);
291
3480
    }
292

            
293
3480
    let saved_context = ctx.typechecking_context;
294
3480
    ctx.typechecking_context = TypecheckingContext::Unknown;
295

            
296
3480
    let Some(matrix_node) = field!(recover, ctx, node, "matrix") else {
297
        return Ok(None);
298
    };
299
3480
    let Some(matrix) = parse_table_operand(ctx, &matrix_node)? else {
300
        ctx.typechecking_context = saved_context;
301
        return Ok(None);
302
    };
303

            
304
3480
    let Some(value_node) = field!(recover, ctx, node, "value") else {
305
        ctx.typechecking_context = saved_context;
306
        return Ok(None);
307
    };
308
3480
    let Some(value) = parse_expression(ctx, value_node)? else {
309
        ctx.typechecking_context = saved_context;
310
        return Ok(None);
311
    };
312

            
313
3480
    ctx.typechecking_context = saved_context;
314

            
315
3480
    let operator_node = node.child(0).unwrap();
316
3480
    ctx.add_span_and_doc_hover(&operator_node, ELEMENT_ID, SymbolKind::Function, None, None);
317

            
318
3480
    Ok(Some(Expression::ElementId(
319
3480
        Metadata::new(),
320
3480
        Moo::new(matrix),
321
3480
        Moo::new(value),
322
3480
    )))
323
3480
}
324

            
325
/// Parses the single argument of a unary function-call-style operator (`defined`, `range`,
326
/// `toSet`, `toMSet`, `toRelation`): keyword immediately followed by a parenthesised argument.
327
/// The argument's type varies by operator (function, set, mset, relation), so typechecking
328
/// context is relaxed to `Unknown` while parsing it, mirroring `parse_flatten`/`parse_element_id`.
329
3721
fn parse_function_unary_operator(
330
3721
    ctx: &mut ParseContext,
331
3721
    node: &Node,
332
3721
) -> Result<Option<Expression>, FatalParseError> {
333
3721
    let field_name = if node.kind() == "perm_inverse_expr" {
334
1
        "permutation"
335
    } else {
336
3720
        "function"
337
    };
338

            
339
3721
    let saved_context = ctx.typechecking_context;
340
3721
    ctx.typechecking_context = TypecheckingContext::Unknown;
341

            
342
3721
    let Some(function_node) = field!(recover, ctx, node, field_name) else {
343
        ctx.typechecking_context = saved_context;
344
        return Ok(None);
345
    };
346
3721
    let Some(function) = parse_expression(ctx, function_node)? else {
347
        ctx.typechecking_context = saved_context;
348
        return Ok(None);
349
    };
350

            
351
3721
    ctx.typechecking_context = saved_context;
352

            
353
3721
    match node.kind() {
354
1104
        "defined_expr" => Ok(Some(Expression::Defined(
355
1104
            Metadata::new(),
356
1104
            Moo::new(function),
357
1104
        ))),
358
2472
        "range_expr" => Ok(Some(Expression::Range(Metadata::new(), Moo::new(function)))),
359
72
        "to_set_expr" => Ok(Some(Expression::ToSet(Metadata::new(), Moo::new(function)))),
360
24
        "to_mset_expr" => Ok(Some(Expression::ToMSet(
361
24
            Metadata::new(),
362
24
            Moo::new(function),
363
24
        ))),
364
48
        "to_relation_expr" => Ok(Some(Expression::ToRelation(
365
48
            Metadata::new(),
366
48
            Moo::new(function),
367
48
        ))),
368
1
        "perm_inverse_expr" => Ok(Some(Expression::PermInverse(
369
1
            Metadata::new(),
370
1
            Moo::new(function),
371
1
        ))),
372
        _ => {
373
            ctx.record_error(RecoverableParseError::new(
374
                format!("Expected a function operator, got: {}", node.kind()),
375
                Some(node.range()),
376
            ));
377
            Ok(None)
378
        }
379
    }
380
3721
}
381

            
382
/// Parses an attribute-as-constraint predicate, e.g. `reflexive(r)` or `size(s, 3)`: a keyword
383
/// naming the attribute, immediately followed by a parenthesised target and an optional value.
384
/// Arity is not checked here (the grammar allows any attribute name with or without a value); a
385
/// later pass validates it against the target's actual domain kind.
386
fn parse_attribute_as_constraint(
387
    ctx: &mut ParseContext,
388
    node: &Node,
389
) -> Result<Option<Expression>, FatalParseError> {
390
    let Some(attr_node) = field!(recover, ctx, node, "attribute") else {
391
        return Ok(None);
392
    };
393
    let attr = Ustr::from(&ctx.source_code[attr_node.start_byte()..attr_node.end_byte()]);
394

            
395
    let saved_context = ctx.typechecking_context;
396
    ctx.typechecking_context = TypecheckingContext::Unknown;
397

            
398
    let Some(target_node) = field!(recover, ctx, node, "target") else {
399
        ctx.typechecking_context = saved_context;
400
        return Ok(None);
401
    };
402
    let Some(target) = parse_expression(ctx, target_node)? else {
403
        ctx.typechecking_context = saved_context;
404
        return Ok(None);
405
    };
406

            
407
    let value = match node.child_by_field_name("value") {
408
        Some(value_node) => match parse_expression(ctx, value_node)? {
409
            Some(value_expr) => Some(Moo::new(value_expr)),
410
            None => {
411
                ctx.typechecking_context = saved_context;
412
                return Ok(None);
413
            }
414
        },
415
        None => None,
416
    };
417

            
418
    ctx.typechecking_context = saved_context;
419

            
420
    Ok(Some(Expression::AttributeAsConstraint(
421
        Metadata::new(),
422
        Moo::new(target),
423
        attr,
424
        value,
425
    )))
426
}
427

            
428
/// Parses a unary partition operator (`parts`, `participants`): keyword immediately followed by
429
/// a parenthesised partition expression.
430
120
fn parse_partition_unary_operator(
431
120
    ctx: &mut ParseContext,
432
120
    node: &Node,
433
120
) -> Result<Option<Expression>, FatalParseError> {
434
120
    let saved_context = ctx.typechecking_context;
435
120
    ctx.typechecking_context = TypecheckingContext::Unknown;
436

            
437
120
    let Some(partition_node) = field!(recover, ctx, node, "partition") else {
438
        ctx.typechecking_context = saved_context;
439
        return Ok(None);
440
    };
441
120
    let Some(partition) = parse_expression(ctx, partition_node)? else {
442
24
        ctx.typechecking_context = saved_context;
443
24
        return Ok(None);
444
    };
445

            
446
96
    ctx.typechecking_context = saved_context;
447

            
448
96
    match node.kind() {
449
48
        "parts_expr" => Ok(Some(Expression::Parts(
450
48
            Metadata::new(),
451
48
            Moo::new(partition),
452
48
        ))),
453
48
        "participants_expr" => Ok(Some(Expression::Participants(
454
48
            Metadata::new(),
455
48
            Moo::new(partition),
456
48
        ))),
457
        _ => {
458
            ctx.record_error(RecoverableParseError::new(
459
                format!("Expected a partition operator, got: {}", node.kind()),
460
                Some(node.range()),
461
            ));
462
            Ok(None)
463
        }
464
    }
465
120
}
466

            
467
/// Parses the two arguments of a binary partition operator (`together`, `apart`, `party`):
468
/// keyword immediately followed by a parenthesised, comma-separated argument pair. `party_expr`
469
/// uses an `element`/`partition` field pair; `together_expr`/`apart_expr` use `elements`/
470
/// `partition`.
471
168
fn parse_partition_binary_operator(
472
168
    ctx: &mut ParseContext,
473
168
    node: &Node,
474
168
) -> Result<Option<Expression>, FatalParseError> {
475
168
    let first_field = if node.kind() == "party_expr" {
476
48
        "element"
477
    } else {
478
120
        "elements"
479
    };
480

            
481
168
    let saved_context = ctx.typechecking_context;
482
168
    ctx.typechecking_context = TypecheckingContext::Unknown;
483

            
484
168
    let Some(first_node) = field!(recover, ctx, node, first_field) else {
485
        ctx.typechecking_context = saved_context;
486
        return Ok(None);
487
    };
488
168
    let Some(first) = parse_expression(ctx, first_node)? else {
489
        ctx.typechecking_context = saved_context;
490
        return Ok(None);
491
    };
492

            
493
168
    let Some(partition_node) = field!(recover, ctx, node, "partition") else {
494
        ctx.typechecking_context = saved_context;
495
        return Ok(None);
496
    };
497
168
    let Some(partition) = parse_expression(ctx, partition_node)? else {
498
24
        ctx.typechecking_context = saved_context;
499
24
        return Ok(None);
500
    };
501

            
502
144
    ctx.typechecking_context = saved_context;
503

            
504
144
    match node.kind() {
505
48
        "together_expr" => Ok(Some(Expression::Together(
506
48
            Metadata::new(),
507
48
            Moo::new(first),
508
48
            Moo::new(partition),
509
48
        ))),
510
48
        "apart_expr" => Ok(Some(Expression::Apart(
511
48
            Metadata::new(),
512
48
            Moo::new(first),
513
48
            Moo::new(partition),
514
48
        ))),
515
48
        "party_expr" => Ok(Some(Expression::Party(
516
48
            Metadata::new(),
517
48
            Moo::new(first),
518
48
            Moo::new(partition),
519
48
        ))),
520
        _ => {
521
            ctx.record_error(RecoverableParseError::new(
522
                format!("Expected a partition operator, got: {}", node.kind()),
523
                Some(node.range()),
524
            ));
525
            Ok(None)
526
        }
527
    }
528
168
}
529

            
530
/// Parses the two arguments of a binary function-call-style operator (`image`, `imageSet`,
531
/// `preImage`, `inverse`): keyword immediately followed by a parenthesised, comma-separated
532
/// argument pair. `inverse_expr` uses `left`/`right` fields; the rest use `function`/`argument`.
533
/// Parses function or sequence application, `f(x)`, into the `image(f, x)` it is sugar for.
534
///
535
/// Several arguments make a tuple, so `f(1, 2)` and `f((1, 2))` mean the same thing, as in Conjure.
536
42316
fn parse_apply_expr(
537
42316
    ctx: &mut ParseContext,
538
42316
    node: &Node,
539
42316
) -> Result<Option<Expression>, FatalParseError> {
540
42316
    let saved_context = ctx.typechecking_context;
541
42316
    ctx.typechecking_context = TypecheckingContext::Unknown;
542

            
543
42316
    let parsed = (|ctx: &mut ParseContext| {
544
42316
        let Some(function_node) = field!(recover, ctx, node, "function") else {
545
            return Ok(None);
546
        };
547
42316
        let Some(function) = parse_expression(ctx, function_node)? else {
548
24
            return Ok(None);
549
        };
550

            
551
42292
        let mut cursor = node.walk();
552
42292
        let mut arguments = Vec::new();
553
42292
        for argument_node in node.children_by_field_name("argument", &mut cursor) {
554
42292
            let Some(argument) = parse_expression(ctx, argument_node)? else {
555
                return Ok(None);
556
            };
557
42292
            arguments.push(argument);
558
        }
559

            
560
42292
        let argument = match <[Expression; 1]>::try_from(arguments) {
561
42292
            Ok([argument]) => argument,
562
            Err(arguments) => Expression::AbstractLiteral(
563
                Metadata::new(),
564
                conjure_cp_core::ast::AbstractLiteral::Tuple(arguments),
565
            ),
566
        };
567

            
568
42292
        Ok(Some(Expression::Image(
569
42292
            Metadata::new(),
570
42292
            Moo::new(function),
571
42292
            Moo::new(argument),
572
42292
        )))
573
42316
    })(ctx);
574

            
575
42316
    ctx.typechecking_context = saved_context;
576
42316
    parsed
577
42316
}
578

            
579
9125
fn parse_function_binary_operator(
580
9125
    ctx: &mut ParseContext,
581
9125
    node: &Node,
582
9125
) -> Result<Option<Expression>, FatalParseError> {
583
9125
    let (first_field, second_field) = if matches!(node.kind(), "inverse_expr" | "compose_expr") {
584
122
        ("left", "right")
585
    } else {
586
9003
        ("function", "argument")
587
    };
588

            
589
9125
    let saved_context = ctx.typechecking_context;
590
9125
    ctx.typechecking_context = TypecheckingContext::Unknown;
591

            
592
9125
    let Some(first_node) = field!(recover, ctx, node, first_field) else {
593
        ctx.typechecking_context = saved_context;
594
        return Ok(None);
595
    };
596
9125
    let Some(first) = parse_expression(ctx, first_node)? else {
597
        ctx.typechecking_context = saved_context;
598
        return Ok(None);
599
    };
600

            
601
9125
    let Some(second_node) = field!(recover, ctx, node, second_field) else {
602
        ctx.typechecking_context = saved_context;
603
        return Ok(None);
604
    };
605
9125
    let Some(second) = parse_expression(ctx, second_node)? else {
606
        ctx.typechecking_context = saved_context;
607
        return Ok(None);
608
    };
609

            
610
9125
    ctx.typechecking_context = saved_context;
611

            
612
9125
    match node.kind() {
613
8859
        "image_expr" => Ok(Some(Expression::Image(
614
8859
            Metadata::new(),
615
8859
            Moo::new(first),
616
8859
            Moo::new(second),
617
8859
        ))),
618
72
        "image_set_expr" => Ok(Some(Expression::ImageSet(
619
72
            Metadata::new(),
620
72
            Moo::new(first),
621
72
            Moo::new(second),
622
72
        ))),
623
72
        "pre_image_expr" => Ok(Some(Expression::PreImage(
624
72
            Metadata::new(),
625
72
            Moo::new(first),
626
72
            Moo::new(second),
627
72
        ))),
628
121
        "inverse_expr" => Ok(Some(Expression::Inverse(
629
121
            Metadata::new(),
630
121
            Moo::new(first),
631
121
            Moo::new(second),
632
121
        ))),
633
1
        "compose_expr" => Ok(Some(Expression::Compose(
634
1
            Metadata::new(),
635
1
            Moo::new(first),
636
1
            Moo::new(second),
637
1
        ))),
638
        _ => {
639
            ctx.record_error(RecoverableParseError::new(
640
                format!("Expected a function operator, got: {}", node.kind()),
641
                Some(node.range()),
642
            ));
643
            Ok(None)
644
        }
645
    }
646
9125
}
647

            
648
/// Parses `restrict(f, dom)`. Unlike the other function operators, the second argument is
649
/// syntactically a domain rather than an expression. `Expression::Restrict` still stores it as
650
/// an `Expression` though (its `domain_of()` is called directly on that operand): a bare domain
651
/// name (e.g. `letting D be domain int(0,2)` used as `restrict(f, D)`) becomes a plain reference
652
/// to that declaration, matching the shape the via-conjure/JSON import path produces. An inline
653
/// domain literal (not just a name) is wrapped in a `DomainAnnotation` so `domain_of()` still
654
/// resolves correctly, though it will not print back out identically (no exhaustive/roundtrip
655
/// case in this codebase currently needs that form).
656
72
fn parse_restrict_expr(
657
72
    ctx: &mut ParseContext,
658
72
    node: &Node,
659
72
) -> Result<Option<Expression>, FatalParseError> {
660
72
    let saved_context = ctx.typechecking_context;
661
72
    ctx.typechecking_context = TypecheckingContext::Unknown;
662

            
663
72
    let Some(function_node) = field!(recover, ctx, node, "function") else {
664
        ctx.typechecking_context = saved_context;
665
        return Ok(None);
666
    };
667
72
    let Some(function) = parse_expression(ctx, function_node)? else {
668
        ctx.typechecking_context = saved_context;
669
        return Ok(None);
670
    };
671

            
672
72
    let Some(domain_node) = field!(recover, ctx, node, "domain") else {
673
        ctx.typechecking_context = saved_context;
674
        return Ok(None);
675
    };
676
72
    let Some(domain_expr) = parse_restrict_domain_argument(ctx, domain_node)? else {
677
        ctx.typechecking_context = saved_context;
678
        return Ok(None);
679
    };
680

            
681
72
    ctx.typechecking_context = saved_context;
682

            
683
72
    Ok(Some(Expression::Restrict(
684
72
        Metadata::new(),
685
72
        Moo::new(function),
686
72
        Moo::new(domain_expr),
687
72
    )))
688
72
}
689

            
690
72
fn parse_restrict_domain_argument(
691
72
    ctx: &mut ParseContext,
692
72
    domain_node: Node,
693
72
) -> Result<Option<Expression>, FatalParseError> {
694
    // Unwrap the generic "domain"/"annotation_domain" wrapper node(s) to see what's underneath.
695
72
    let mut inner = domain_node;
696
144
    while matches!(inner.kind(), "domain" | "annotation_domain") {
697
72
        let Some(child) = inner.child(0) else {
698
            break;
699
        };
700
72
        inner = child;
701
    }
702

            
703
72
    if inner.kind() == "identifier" {
704
        // A name referring to a domain: keep it as a plain reference expression, so it prints
705
        // back out as just the name (e.g. `restrict(f,D)`).
706
72
        return parse_atom(ctx, &inner);
707
    }
708

            
709
    let Some(domain) = parse_domain(ctx, domain_node)? else {
710
        return Ok(None);
711
    };
712
    Ok(Some(Expression::DomainAnnotation(
713
        Metadata::new(),
714
        Moo::new(Expression::Atomic(
715
            Metadata::new(),
716
            Atom::Literal(Literal::Bool(true)),
717
        )),
718
        domain,
719
    )))
720
72
}
721

            
722
689050
fn parse_index_or_slice(
723
689050
    ctx: &mut ParseContext,
724
689050
    node: &Node,
725
689050
) -> Result<Option<Expression>, FatalParseError> {
726
    // add error and return early if we're in a set context, since indexing/slicing doesn't produce sets
727
689050
    if ctx.typechecking_context == TypecheckingContext::Set {
728
        ctx.record_error(RecoverableParseError::new(
729
            format!(
730
                "Type error: {}\n\tExpected: set\n\tGot: index or slice",
731
                ctx.source_code[node.start_byte()..node.end_byte()].trim()
732
            ),
733
            Some(node.range()),
734
        ));
735
        return Ok(None);
736
689050
    }
737

            
738
    // Save current context and temporarily set to Unknown for the collection
739
689050
    let saved_context = ctx.typechecking_context;
740
689050
    let saved_inner_context = ctx.inner_typechecking_context;
741
689050
    ctx.typechecking_context = TypecheckingContext::Unknown;
742
    let mut collection: Expression;
743
689050
    match parse_atom(ctx, &field!(node, "collection"))? {
744
689026
        Some(expr) => collection = expr,
745
24
        None => return Ok(None),
746
    }
747
689026
    ctx.typechecking_context = saved_context;
748

            
749
689026
    let indices_field = field!(node, "indices");
750
689026
    let mut idx_nodes = named_children(&indices_field).collect::<VecDeque<_>>();
751

            
752
    // If LHS is a record, parse first index as a record field
753
689026
    if let ReturnType::Record(ents) | ReturnType::Variant(ents) = collection.return_type() {
754
38664
        let idx = idx_nodes
755
38664
            .pop_front()
756
38664
            .ok_or(FatalParseError::internal_error(
757
38664
                "Expected at least one indexing expression".into(),
758
38664
                Some(indices_field.range()),
759
38664
            ))?;
760
38664
        let name = Name::user(ctx.source_code[idx.start_byte()..idx.end_byte()].trim());
761
57984
        let has_name = ents.iter().any(|e| e.name == name);
762
38664
        if !has_name {
763
            return Err(FatalParseError::internal_error(
764
                format!("`{name}` is not a valid field name for `{collection}`"),
765
                Some(idx.range()),
766
            ));
767
38664
        }
768
38664
        collection = Expression::RecordField(Metadata::new(), Moo::new(collection), name);
769

            
770
        // If there are no more indices, return the record field directly
771
38664
        if idx_nodes.is_empty() {
772
38664
            return Ok(typecheck_index_or_slice(
773
38664
                ctx,
774
38664
                node,
775
38664
                collection,
776
38664
                saved_context,
777
38664
                saved_inner_context,
778
38664
            ));
779
        }
780
650362
    }
781

            
782
    // Parse the rest of the indices as normal
783
650362
    let first_index_range = idx_nodes.front().map(|idx| idx.range());
784
650362
    let mut indices = Vec::new();
785
650362
    for idx_node in idx_nodes {
786
824246
        indices.push(parse_index(ctx, &idx_node)?);
787
    }
788

            
789
817470
    let has_null_idx = indices.iter().any(|idx| idx.is_none());
790
650362
    if let Some((_, index_domains)) = collection.domain_of().and_then(|domain| domain.as_matrix())
791
575739
        && index_domains.len() != indices.len()
792
    {
793
16
        let operation = if has_null_idx { "slice" } else { "index" };
794
16
        ctx.record_error(RecoverableParseError::new(
795
16
            format!(
796
16
                "Invalid matrix {operation}: expected {} indices (one per dimension), but got {}",
797
16
                index_domains.len(),
798
16
                indices.len()
799
16
            ),
800
16
            Some(indices_field.range()),
801
16
        ));
802
16
        return Ok(None);
803
650346
    }
804

            
805
650346
    if !has_null_idx
806
623329
        && let ReturnType::Tuple(fields) = collection.return_type()
807
74119
        && let Some(Some(Expression::Atomic(_, Atom::Literal(Literal::Int(index))))) =
808
74119
            indices.first()
809
    {
810
74119
        let valid_index = index
811
74119
            .checked_sub(1)
812
74119
            .and_then(|index| usize::try_from(index).ok())
813
74119
            .is_some_and(|index| index < fields.len());
814

            
815
74119
        if !valid_index {
816
4
            ctx.record_error(RecoverableParseError::new(
817
4
                format!(
818
4
                    "Tuple index {index} is out of bounds; valid indices are 1..{}",
819
4
                    fields.len()
820
4
                ),
821
4
                Some(first_index_range.unwrap_or(indices_field.range())),
822
4
            ));
823
4
            return Ok(None);
824
74115
        }
825
576227
    }
826

            
827
650342
    let expression = if has_null_idx {
828
        // It's a slice
829
27017
        Expression::UnsafeSlice(Metadata::new(), Moo::new(collection), indices)
830
    } else {
831
        // It's an index
832
775320
        let idx_exprs: Vec<Expression> = indices.into_iter().map(|idx| idx.unwrap()).collect();
833
623325
        Expression::UnsafeIndex(Metadata::new(), Moo::new(collection), idx_exprs)
834
    };
835

            
836
650342
    Ok(typecheck_index_or_slice(
837
650342
        ctx,
838
650342
        node,
839
650342
        expression,
840
650342
        saved_context,
841
650342
        saved_inner_context,
842
650342
    ))
843
689050
}
844

            
845
689006
fn typecheck_index_or_slice(
846
689006
    ctx: &mut ParseContext,
847
689006
    node: &Node,
848
689006
    expression: Expression,
849
689006
    context: TypecheckingContext,
850
689006
    inner_context: TypecheckingContext,
851
689006
) -> Option<Expression> {
852
689006
    let return_type = expression.return_type();
853
689006
    let mismatch = if let Some(expected) = expected_scalar_type(context)
854
135593
        && return_type != expected
855
14024
        && return_type != ReturnType::Unknown
856
    {
857
8
        Some((expected, return_type))
858
688998
    } else if let Some(expected) = expected_scalar_type(inner_context)
859
11624
        && let Some(element_type) = return_type.elem_type()
860
8
        && element_type != expected
861
8
        && element_type != ReturnType::Unknown
862
    {
863
8
        Some((expected, element_type))
864
    } else {
865
688990
        None
866
    };
867

            
868
689006
    if let Some((expected, actual)) = mismatch {
869
16
        ctx.record_error(RecoverableParseError::new(
870
16
            format!(
871
16
                "Type error: {}\n\tExpected: {expected}\n\tGot: {actual}",
872
16
                ctx.source_code[node.start_byte()..node.end_byte()].trim()
873
16
            ),
874
16
            Some(node.range()),
875
16
        ));
876
16
        None
877
    } else {
878
688990
        Some(expression)
879
    }
880
689006
}
881

            
882
1378004
fn expected_scalar_type(context: TypecheckingContext) -> Option<ReturnType> {
883
1378004
    match context {
884
26184
        TypecheckingContext::Boolean => Some(ReturnType::Bool),
885
121033
        TypecheckingContext::Arithmetic => Some(ReturnType::Int),
886
1230787
        _ => None,
887
    }
888
1378004
}
889

            
890
824246
fn parse_index(ctx: &mut ParseContext, node: &Node) -> Result<Option<Expression>, FatalParseError> {
891
824246
    match node.kind() {
892
        "arithmetic_expr" | "atom" => {
893
796349
            let saved_context = ctx.typechecking_context;
894
796349
            ctx.typechecking_context = TypecheckingContext::Unknown;
895

            
896
            // TODO: add collection-aware index typechecking.
897
            // For tuple/matrix/set-like indexing, indices should be arithmetic.
898
            // For record field access, index atoms should resolve to valid field names.
899
            // This requires checking index expression together with the indexed collection type.
900

            
901
796349
            let Some(expr) = parse_expression(ctx, *node)? else {
902
                return Ok(None);
903
            };
904

            
905
796349
            ctx.typechecking_context = saved_context;
906
796349
            Ok(Some(expr))
907
        }
908
27897
        "null_index" => Ok(None),
909
        _ => {
910
            ctx.record_error(RecoverableParseError::new(
911
                format!("Expected an index, got: '{}'", node.kind()),
912
                Some(node.range()),
913
            ));
914
            Ok(None)
915
        }
916
    }
917
824246
}
918

            
919
5266136
fn parse_variable(ctx: &mut ParseContext, node: &Node) -> Result<Option<Atom>, FatalParseError> {
920
5266136
    let raw_name = &ctx.source_code[node.start_byte()..node.end_byte()];
921

            
922
5266136
    let name = Name::user(raw_name.trim());
923
5266136
    if let Some(symbols) = &ctx.symbols {
924
        let lookup_result = {
925
5266136
            let symbols_read = symbols.read();
926
5266136
            symbols_read.lookup(&name)
927
        };
928

            
929
5266136
        if let Some(decl) = lookup_result {
930
5265887
            let symbol_kind = match &decl.kind().clone() as &DeclarationKind {
931
4220561
                DeclarationKind::Find(_) | DeclarationKind::FindAuxiliary(_) => SymbolKind::FindVar,
932
308686
                DeclarationKind::Given(_) => SymbolKind::GivenVar,
933
220496
                DeclarationKind::ValueLetting(_, _) => SymbolKind::LettingVar,
934
                DeclarationKind::TemporaryValueLetting(_) => SymbolKind::LettingVar,
935
120
                DeclarationKind::DomainLetting(_) => SymbolKind::LettingVar,
936
450276
                DeclarationKind::Quantified(..) => SymbolKind::FindVar,
937
65748
                DeclarationKind::QuantifiedExpr(..) => SymbolKind::FindVar,
938
                DeclarationKind::EnumeratedType(_) | DeclarationKind::UnnamedType(_) => {
939
                    ctx.record_error(RecoverableParseError::new(
940
                        format!("The identifier '{name}' is a type, not a value"),
941
                        Some(node.range()),
942
                    ));
943
                    return Ok(None);
944
                }
945
                _ => SymbolKind::LettingVar,
946
            };
947

            
948
            let hover = HoverInfo {
949
5265887
                description: format!("Variable: {name}"),
950
5265887
                doc_key: None,
951
5265887
                kind: Some(symbol_kind),
952
5265887
                ty: decl.domain().map(|d| d.to_string()),
953
5265887
                decl_span: ctx.lookup_decl_span(&name),
954
            };
955
5265887
            span_with_hover(node, ctx.source_code, ctx.source_map, hover);
956

            
957
            // Type check the variable against the expected context
958
5265887
            if let Some(error_msg) = typecheck_variable(&decl, ctx.typechecking_context, raw_name) {
959
456
                ctx.record_error(RecoverableParseError::new(error_msg, Some(node.range())));
960
456
                return Ok(None);
961
5265431
            }
962

            
963
5265431
            Ok(Some(Atom::Reference(conjure_cp_core::ast::Reference::new(
964
5265431
                decl,
965
5265431
            ))))
966
        } else {
967
249
            ctx.record_error(RecoverableParseError::new(
968
249
                format!("The identifier '{}' is not defined", raw_name),
969
249
                Some(node.range()),
970
249
            ));
971
249
            Ok(None)
972
        }
973
    } else {
974
        ctx.record_error(RecoverableParseError::new(
975
            format!("Symbol table missing when parsing variable '{raw_name}'"),
976
            Some(node.range()),
977
        ));
978
        Ok(None)
979
    }
980
5266136
}
981

            
982
/// Type check a variable declaration against the expected expression context.
983
/// Returns an error message if the variable type doesn't match the context.
984
5265887
fn typecheck_variable(
985
5265887
    decl: &DeclarationPtr,
986
5265887
    context: TypecheckingContext,
987
5265887
    raw_name: &str,
988
5265887
) -> Option<String> {
989
    // Only type check when context is known
990
5265887
    if context == TypecheckingContext::Unknown {
991
3587847
        return None;
992
1678040
    }
993

            
994
    // Get the variable's domain and resolve it
995
1678040
    let domain = decl.domain()?;
996
1674488
    let ground_domain = domain.resolve().ok()?;
997

            
998
    // Determine what type is expected
999
1649266
    let expected = match context {
438541
        TypecheckingContext::Boolean => "bool",
1031805
        TypecheckingContext::Arithmetic => "int",
50280
        TypecheckingContext::Set => "set",
33816
        TypecheckingContext::SetOrMatrix => "set or matrix",
25488
        TypecheckingContext::MSet => "mset",
64656
        TypecheckingContext::Matrix => "matrix",
        TypecheckingContext::Tuple => "tuple",
3504
        TypecheckingContext::Record => "record",
        TypecheckingContext::Partition => "partition",
480
        TypecheckingContext::Permutation => "permutation",
696
        TypecheckingContext::Sequence => "sequence",
        TypecheckingContext::Function => "function",
        TypecheckingContext::Relation => "relation",
        TypecheckingContext::Unknown => return None, // shouldn't reach here
    };
    // Determine what type we actually have
1649266
    let actual = match ground_domain.as_ref() {
        GroundDomain::Empty(_) => "empty",
438517
        GroundDomain::Bool => "bool",
1031829
        GroundDomain::Int(_, _) => "int",
        GroundDomain::Tuple(_, _) => "tuple",
3528
        GroundDomain::Record(_, _) => "record",
        GroundDomain::Variant(_, _) => "variant",
69840
        GroundDomain::Matrix(_, _, _) => "matrix",
696
        GroundDomain::Sequence(_, _) => "sequence",
78888
        GroundDomain::Set(_, _) => "set",
25488
        GroundDomain::MSet(_, _) => "mset",
        GroundDomain::Function(_, _, _) => "function",
        GroundDomain::Relation(_, _) => "relation",
        GroundDomain::Partition(_, _) => "partition",
        GroundDomain::EnumeratedType(_, _) => "enum",
        GroundDomain::UnnamedType(_) => "unnamed",
480
        GroundDomain::Permutation(_, _) => "permutation",
    };
    // If types match, no error
1649266
    if expected == actual
34248
        || (context == TypecheckingContext::SetOrMatrix && matches!(actual, "set" | "matrix"))
    {
1648810
        return None;
456
    }
    // Otherwise, report the type mismatch
456
    Some(format!(
456
        "Type error: {}\n\tExpected: {}\n\tGot: {}",
456
        raw_name, expected, actual
456
    ))
5265887
}
4411828
fn parse_constant(ctx: &mut ParseContext, node: &Node) -> Result<Option<Literal>, FatalParseError> {
4411828
    let Some(inner) = named_child!(recover, ctx, node) else {
        return Ok(None);
    };
4411828
    let raw_value = &ctx.source_code[inner.start_byte()..inner.end_byte()];
4411828
    let lit = match inner.kind() {
        "integer" => {
4276122
            let Some(value) = parse_int(ctx, &inner) else {
48
                return Ok(None);
            };
4276074
            Literal::Int(value)
        }
        "TRUE" => {
66678
            let hover = HoverInfo {
66678
                description: format!("Boolean constant: {raw_value}"),
66678
                doc_key: None,
66678
                kind: None,
66678
                ty: None,
66678
                decl_span: None,
66678
            };
66678
            span_with_hover(&inner, ctx.source_code, ctx.source_map, hover);
66678
            Literal::Bool(true)
        }
        "FALSE" => {
69028
            let hover = HoverInfo {
69028
                description: format!("Boolean constant: {raw_value}"),
69028
                doc_key: None,
69028
                kind: None,
69028
                ty: None,
69028
                decl_span: None,
69028
            };
69028
            span_with_hover(&inner, ctx.source_code, ctx.source_map, hover);
69028
            Literal::Bool(false)
        }
        _ => {
            ctx.record_error(RecoverableParseError::new(
                format!(
                    "'{}' (kind: '{}') is not a valid constant",
                    raw_value,
                    inner.kind()
                ),
                Some(inner.range()),
            ));
            return Ok(None);
        }
    };
    // Type check the constant against the expected context
4411780
    if ctx.typechecking_context != TypecheckingContext::Unknown {
1576043
        let expected = match ctx.typechecking_context {
24650
            TypecheckingContext::Boolean => "bool",
1551345
            TypecheckingContext::Arithmetic => "int",
24
            TypecheckingContext::Set => "set",
            TypecheckingContext::SetOrMatrix => "set or matrix",
            TypecheckingContext::MSet => "mset",
24
            TypecheckingContext::Matrix => "matrix",
            TypecheckingContext::Tuple => "tuple",
            TypecheckingContext::Record => "record",
            TypecheckingContext::Partition => "partition",
            TypecheckingContext::Permutation => "permutation",
            TypecheckingContext::Sequence => "sequence",
            TypecheckingContext::Function => "function",
            TypecheckingContext::Relation => "relation",
            TypecheckingContext::Unknown => "",
        };
1576043
        let actual = match &lit {
24578
            Literal::Bool(_) => "bool",
1551465
            Literal::Int(_) => "int",
            Literal::EnumVariant(_) => "enum",
            Literal::Unnamed(_) => "unnamed",
            Literal::AbstractLiteral(_) => return Ok(None), // Abstract literals aren't type-checked here
        };
1576043
        if expected != actual {
192
            ctx.record_error(RecoverableParseError::new(
192
                format!(
192
                    "Type error: {}\n\tExpected: {}\n\tGot: {}",
192
                    raw_value, expected, actual
192
                ),
192
                Some(node.range()),
192
            ));
192
            return Ok(None);
1575851
        }
2835737
    }
4411588
    Ok(Some(lit))
4411828
}
4278163
pub(crate) fn parse_int(ctx: &mut ParseContext, node: &Node) -> Option<i32> {
4278163
    let raw_value = &ctx.source_code[node.start_byte()..node.end_byte()];
4278163
    if let Ok(v) = raw_value.parse::<i32>() {
4278115
        Some(v)
    } else {
48
        ctx.record_error(RecoverableParseError::new(
48
            "Expected an integer here".to_string(),
48
            Some(node.range()),
48
        ));
48
        None
    }
4278163
}