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_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::dominance::parse_pareto_expression;
11
use crate::util::{TypecheckingContext, named_children};
12
use crate::{field, named_child};
13

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

            
19
use tree_sitter::Node;
20
use ustr::Ustr;
21

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

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

            
65
34632
            Ok(Some(Expression::FromSolution(
66
34632
                Metadata::new(),
67
34632
                Moo::new(inner),
68
34632
            )))
69
        }
70
42086
        "pareto_expression" => parse_pareto_expression(ctx, node),
71
40991
        "constant" => {
72
28711
            let Some(lit) = parse_constant(ctx, node)? else {
73
117
                return Ok(None);
74
            };
75
28594
            Ok(Some(Expression::Atomic(
76
28594
                Metadata::new(),
77
28594
                Atom::Literal(lit),
78
28594
            )))
79
        }
80
12280
        "matrix" | "record" | "tuple" | "set_literal" => {
81
5209
            let Some(abs) = parse_abstract(ctx, node)? else {
82
143
                return Ok(None);
83
            };
84
5066
            Ok(Some(Expression::AbstractLiteral(Metadata::new(), abs)))
85
        }
86
7071
        "flatten" => parse_flatten(ctx, node),
87
6811
        "table" | "negative_table" => parse_table(ctx, node),
88
6707
        "index_or_slice" => parse_index_or_slice(ctx, node),
89
        // for now, assume is binary since powerset isn't implemented
90
        // TODO: add powerset support under "set_operation"
91
1045
        "set_operation" => parse_binary_expression(ctx, node),
92
889
        "comprehension" => parse_comprehension(ctx, node),
93
        _ => {
94
            ctx.record_error(RecoverableParseError::new(
95
                format!("Expected atom, got: {}", node.kind()),
96
                Some(node.range()),
97
            ));
98
            Ok(None)
99
        }
100
    }
101
376310
}
102

            
103
260
fn parse_flatten(
104
260
    ctx: &mut ParseContext,
105
260
    node: &Node,
106
260
) -> Result<Option<Expression>, FatalParseError> {
107
    // add error and return early if we're in a set context, since flatten doesn't produce sets
108
260
    if ctx.typechecking_context == TypecheckingContext::Set {
109
        ctx.record_error(RecoverableParseError::new(
110
            format!(
111
                "Type error: {}\n\tExpected: set\n\tGot: flatten",
112
                ctx.source_code[node.start_byte()..node.end_byte()].trim()
113
            ),
114
            Some(node.range()),
115
        ));
116
        return Ok(None);
117
260
    }
118

            
119
260
    let Some(expr_node) = field!(recover, ctx, node, "expression") else {
120
        return Ok(None);
121
    };
122
    // TODO: verify the atom is a matrix
123
260
    let Some(expr) = parse_atom(ctx, &expr_node)? else {
124
        return Ok(None);
125
    };
126

            
127
260
    if let Some(depth_node) = node.child_by_field_name("depth") {
128
        let Some(depth) = parse_int(ctx, &depth_node) else {
129
            return Ok(None);
130
        };
131
        let depth_expression =
132
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(depth)));
133
        Ok(Some(Expression::Flatten(
134
            Metadata::new(),
135
            Some(Moo::new(depth_expression)),
136
            Moo::new(expr),
137
        )))
138
    } else {
139
260
        Ok(Some(Expression::Flatten(
140
260
            Metadata::new(),
141
260
            None,
142
260
            Moo::new(expr),
143
260
        )))
144
    }
145
260
}
146

            
147
104
fn parse_table(ctx: &mut ParseContext, node: &Node) -> Result<Option<Expression>, FatalParseError> {
148
    // add error and return early if we're in a set context, since tables aren't allowed there
149
104
    if ctx.typechecking_context == TypecheckingContext::Set {
150
        ctx.record_error(RecoverableParseError::new(
151
            format!(
152
                "Type error: {}\n\tExpected: set\n\tGot: table",
153
                ctx.source_code[node.start_byte()..node.end_byte()].trim()
154
            ),
155
            Some(node.range()),
156
        ));
157
        return Ok(None);
158
104
    }
159

            
160
    // the variables and rows can contain arbitrary expressions, so we temporarily set the context to Unknown to avoid typechecking errors
161
104
    let saved_context = ctx.typechecking_context;
162
104
    ctx.typechecking_context = TypecheckingContext::Unknown;
163

            
164
104
    let Some(variables_node) = field!(recover, ctx, node, "variables") else {
165
        return Ok(None);
166
    };
167
104
    let Some(variables) = parse_atom(ctx, &variables_node)? else {
168
        return Ok(None);
169
    };
170

            
171
104
    let Some(rows_node) = field!(recover, ctx, node, "rows") else {
172
        return Ok(None);
173
    };
174
104
    let Some(rows) = parse_atom(ctx, &rows_node)? else {
175
        return Ok(None);
176
    };
177

            
178
104
    ctx.typechecking_context = saved_context;
179

            
180
104
    match node.kind() {
181
104
        "table" => Ok(Some(Expression::Table(
182
78
            Metadata::new(),
183
78
            Moo::new(variables),
184
78
            Moo::new(rows),
185
78
        ))),
186
26
        "negative_table" => Ok(Some(Expression::NegativeTable(
187
26
            Metadata::new(),
188
26
            Moo::new(variables),
189
26
            Moo::new(rows),
190
26
        ))),
191
        _ => {
192
            ctx.record_error(RecoverableParseError::new(
193
                format!(
194
                    "Expected 'table' or 'negative_table', got: '{}'",
195
                    node.kind()
196
                ),
197
                Some(node.range()),
198
            ));
199
            Ok(None)
200
        }
201
    }
202
104
}
203

            
204
5662
fn parse_index_or_slice(
205
5662
    ctx: &mut ParseContext,
206
5662
    node: &Node,
207
5662
) -> Result<Option<Expression>, FatalParseError> {
208
    // add error and return early if we're in a set context, since indexing/slicing doesn't produce sets
209
5662
    if ctx.typechecking_context == TypecheckingContext::Set {
210
        ctx.record_error(RecoverableParseError::new(
211
            format!(
212
                "Type error: {}\n\tExpected: set\n\tGot: index or slice",
213
                ctx.source_code[node.start_byte()..node.end_byte()].trim()
214
            ),
215
            Some(node.range()),
216
        ));
217
        return Ok(None);
218
5662
    }
219

            
220
    // Save current context and temporarily set to Unknown for the collection
221
5662
    let saved_context = ctx.typechecking_context;
222
5662
    ctx.typechecking_context = TypecheckingContext::Unknown;
223
    let mut collection: Expression;
224
5662
    match parse_atom(ctx, &field!(node, "collection"))? {
225
5649
        Some(expr) => collection = expr,
226
13
        None => return Ok(None),
227
    }
228
5649
    ctx.typechecking_context = saved_context;
229

            
230
5649
    let indices_field = field!(node, "indices");
231
5649
    let mut idx_nodes = named_children(&indices_field).collect::<VecDeque<_>>();
232

            
233
    // If LHS is a record, parse first index as a record field
234
5649
    if let ReturnType::Record(ents) = collection.return_type() {
235
52
        let idx = idx_nodes
236
52
            .pop_front()
237
52
            .ok_or(FatalParseError::internal_error(
238
52
                "Expected at least one indexing expression".into(),
239
52
                Some(indices_field.range()),
240
            ))?;
241
52
        let name = Name::user(ctx.source_code[idx.start_byte()..idx.end_byte()].trim());
242
78
        let has_name = ents.iter().any(|e| e.name == name);
243
52
        if !has_name {
244
            return Err(FatalParseError::internal_error(
245
                format!("`{name}` is not a valid field name for `{collection}`"),
246
                Some(idx.range()),
247
            ));
248
52
        }
249
52
        collection = Expression::RecordField(Metadata::new(), Moo::new(collection), name);
250

            
251
        // If there are no more indices, return the record field directly
252
52
        if idx_nodes.is_empty() {
253
52
            return Ok(Some(collection));
254
        }
255
5597
    }
256

            
257
    // Parse the rest of the indices as normal
258
5597
    let mut indices = Vec::new();
259
8173
    for idx_node in idx_nodes {
260
8173
        indices.push(parse_index(ctx, &idx_node)?);
261
    }
262

            
263
7861
    let has_null_idx = indices.iter().any(|idx| idx.is_none());
264
    // TODO: We could check whether the slice/index is safe here
265
5597
    if has_null_idx {
266
        // It's a slice
267
885
        Ok(Some(Expression::UnsafeSlice(
268
885
            Metadata::new(),
269
885
            Moo::new(collection),
270
885
            indices,
271
885
        )))
272
    } else {
273
        // It's an index
274
6455
        let idx_exprs: Vec<Expression> = indices.into_iter().map(|idx| idx.unwrap()).collect();
275
4712
        Ok(Some(Expression::UnsafeIndex(
276
4712
            Metadata::new(),
277
4712
            Moo::new(collection),
278
4712
            idx_exprs,
279
4712
        )))
280
    }
281
5662
}
282

            
283
8173
fn parse_index(ctx: &mut ParseContext, node: &Node) -> Result<Option<Expression>, FatalParseError> {
284
8173
    match node.kind() {
285
8173
        "arithmetic_expr" | "atom" => {
286
7288
            let saved_context = ctx.typechecking_context;
287
7288
            ctx.typechecking_context = TypecheckingContext::Unknown;
288

            
289
            // TODO: add collection-aware index typechecking.
290
            // For tuple/matrix/set-like indexing, indices should be arithmetic.
291
            // For record field access, index atoms should resolve to valid field names.
292
            // This requires checking index expression together with the indexed collection type.
293

            
294
7288
            let Some(expr) = parse_expression(ctx, *node)? else {
295
                return Ok(None);
296
            };
297

            
298
7288
            ctx.typechecking_context = saved_context;
299
7288
            Ok(Some(expr))
300
        }
301
885
        "null_index" => Ok(None),
302
        _ => {
303
            ctx.record_error(RecoverableParseError::new(
304
                format!("Expected an index, got: '{}'", node.kind()),
305
                Some(node.range()),
306
            ));
307
            Ok(None)
308
        }
309
    }
310
8173
}
311

            
312
147798
fn parse_variable(ctx: &mut ParseContext, node: &Node) -> Result<Option<Atom>, FatalParseError> {
313
147798
    let raw_name = &ctx.source_code[node.start_byte()..node.end_byte()];
314

            
315
147798
    let name = Name::user(raw_name.trim());
316
147798
    if let Some(symbols) = &ctx.symbols {
317
147798
        let lookup_result = {
318
147798
            let symbols_read = symbols.read();
319
147798
            symbols_read.lookup(&name)
320
        };
321

            
322
147798
        if let Some(decl) = lookup_result {
323
147667
            let symbol_kind = match &decl.kind().clone() as &DeclarationKind {
324
137848
                DeclarationKind::Find(_) => SymbolKind::FindVar,
325
528
                DeclarationKind::Given(_) => SymbolKind::GivenVar,
326
2212
                DeclarationKind::ValueLetting(_, _) => SymbolKind::LettingVar,
327
                DeclarationKind::TemporaryValueLetting(_) => SymbolKind::LettingVar,
328
26
                DeclarationKind::DomainLetting(_) => SymbolKind::LettingVar,
329
7053
                DeclarationKind::Quantified(..) => SymbolKind::FindVar,
330
                DeclarationKind::QuantifiedExpr(..) => SymbolKind::FindVar,
331
                &_ => todo!(),
332
            };
333

            
334
147667
            let hover = HoverInfo {
335
147667
                description: format!("Variable: {name}"),
336
147667
                doc_key: None,
337
147667
                kind: Some(symbol_kind),
338
147667
                ty: decl.domain().map(|d| d.to_string()),
339
147667
                decl_span: ctx.lookup_decl_span(&name),
340
            };
341
147667
            span_with_hover(node, ctx.source_code, ctx.source_map, hover);
342

            
343
            // Type check the variable against the expected context
344
147667
            if let Some(error_msg) = typecheck_variable(&decl, ctx.typechecking_context, raw_name) {
345
260
                ctx.record_error(RecoverableParseError::new(error_msg, Some(node.range())));
346
260
                return Ok(None);
347
147407
            }
348

            
349
147407
            Ok(Some(Atom::Reference(conjure_cp_core::ast::Reference::new(
350
147407
                decl,
351
147407
            ))))
352
        } else {
353
131
            ctx.record_error(RecoverableParseError::new(
354
131
                format!("The identifier '{}' is not defined", raw_name),
355
131
                Some(node.range()),
356
            ));
357
131
            Ok(None)
358
        }
359
    } else {
360
        ctx.record_error(RecoverableParseError::new(
361
            format!("Symbol table missing when parsing variable '{raw_name}'"),
362
            Some(node.range()),
363
        ));
364
        Ok(None)
365
    }
366
147798
}
367

            
368
/// Type check a variable declaration against the expected expression context.
369
/// Returns an error message if the variable type doesn't match the context.
370
147667
fn typecheck_variable(
371
147667
    decl: &DeclarationPtr,
372
147667
    context: TypecheckingContext,
373
147667
    raw_name: &str,
374
147667
) -> Option<String> {
375
    // Only type check when context is known
376
147667
    if context == TypecheckingContext::Unknown {
377
17407
        return None;
378
130260
    }
379

            
380
    // Get the variable's domain and resolve it
381
130260
    let domain = decl.domain()?;
382
130260
    let ground_domain = domain.resolve().ok()?;
383

            
384
    // Determine what type is expected
385
129656
    let expected = match context {
386
99411
        TypecheckingContext::Boolean => "bool",
387
29192
        TypecheckingContext::Arithmetic => "int",
388
585
        TypecheckingContext::Set => "set",
389
13
        TypecheckingContext::SetOrMatrix => "set or matrix",
390
        TypecheckingContext::MSet => "mset",
391
377
        TypecheckingContext::Matrix => "matrix",
392
52
        TypecheckingContext::Tuple => "tuple",
393
26
        TypecheckingContext::Record => "record",
394
        TypecheckingContext::Partition => "partition",
395
        TypecheckingContext::Sequence => "sequence",
396
        TypecheckingContext::Unknown => return None, // shouldn't reach here
397
    };
398

            
399
    // Determine what type we actually have
400
129656
    let actual = match ground_domain.as_ref() {
401
99411
        GroundDomain::Bool => "bool",
402
29192
        GroundDomain::Int(_) => "int",
403
429
        GroundDomain::Matrix(_, _) => "matrix",
404
533
        GroundDomain::Set(_, _) => "set",
405
        GroundDomain::MSet(_, _) => "mset",
406
52
        GroundDomain::Tuple(_) => "tuple",
407
39
        GroundDomain::Record(_) => "record",
408
        GroundDomain::Function(_, _, _) => "function",
409
        GroundDomain::Variant(_) => "variant",
410
        GroundDomain::Relation(_, _) => "relation",
411
        GroundDomain::Partition(_, _) => "partition",
412
        GroundDomain::Sequence(_, _) => "sequence",
413
        GroundDomain::Empty(_) => "empty",
414
    };
415

            
416
    // If types match, no error
417
129656
    if expected == actual
418
260
        || (context == TypecheckingContext::SetOrMatrix && matches!(actual, "set" | "matrix"))
419
    {
420
129396
        return None;
421
260
    }
422

            
423
    // Otherwise, report the type mismatch
424
260
    Some(format!(
425
260
        "Type error: {}\n\tExpected: {}\n\tGot: {}",
426
260
        raw_name, expected, actual
427
260
    ))
428
147667
}
429

            
430
28711
fn parse_constant(ctx: &mut ParseContext, node: &Node) -> Result<Option<Literal>, FatalParseError> {
431
28711
    let Some(inner) = named_child!(recover, ctx, node) else {
432
        return Ok(None);
433
    };
434
28711
    let raw_value = &ctx.source_code[inner.start_byte()..inner.end_byte()];
435
28711
    let lit = match inner.kind() {
436
28711
        "integer" => {
437
28002
            let Some(value) = parse_int(ctx, &inner) else {
438
26
                return Ok(None);
439
            };
440
27976
            Literal::Int(value)
441
        }
442
709
        "TRUE" => {
443
415
            let hover = HoverInfo {
444
415
                description: format!("Boolean constant: {raw_value}"),
445
415
                doc_key: None,
446
415
                kind: None,
447
415
                ty: None,
448
415
                decl_span: None,
449
415
            };
450
415
            span_with_hover(&inner, ctx.source_code, ctx.source_map, hover);
451
415
            Literal::Bool(true)
452
        }
453
294
        "FALSE" => {
454
294
            let hover = HoverInfo {
455
294
                description: format!("Boolean constant: {raw_value}"),
456
294
                doc_key: None,
457
294
                kind: None,
458
294
                ty: None,
459
294
                decl_span: None,
460
294
            };
461
294
            span_with_hover(&inner, ctx.source_code, ctx.source_map, hover);
462
294
            Literal::Bool(false)
463
        }
464
        _ => {
465
            ctx.record_error(RecoverableParseError::new(
466
                format!(
467
                    "'{}' (kind: '{}') is not a valid constant",
468
                    raw_value,
469
                    inner.kind()
470
                ),
471
                Some(inner.range()),
472
            ));
473
            return Ok(None);
474
        }
475
    };
476

            
477
    // Type check the constant against the expected context
478
28685
    if ctx.typechecking_context != TypecheckingContext::Unknown {
479
14725
        let expected = match ctx.typechecking_context {
480
312
            TypecheckingContext::Boolean => "bool",
481
14387
            TypecheckingContext::Arithmetic => "int",
482
13
            TypecheckingContext::Set => "set",
483
            TypecheckingContext::SetOrMatrix => "set or matrix",
484
            TypecheckingContext::MSet => "mset",
485
13
            TypecheckingContext::Matrix => "matrix",
486
            TypecheckingContext::Tuple => "tuple",
487
            TypecheckingContext::Record => "record",
488
            TypecheckingContext::Partition => "partition",
489
            TypecheckingContext::Sequence => "sequence",
490
            TypecheckingContext::Unknown => "",
491
        };
492

            
493
14725
        let actual = match &lit {
494
286
            Literal::Bool(_) => "bool",
495
14439
            Literal::Int(_) => "int",
496
            Literal::AbstractLiteral(_) => return Ok(None), // Abstract literals aren't type-checked here
497
        };
498

            
499
14725
        if expected != actual {
500
91
            ctx.record_error(RecoverableParseError::new(
501
91
                format!(
502
                    "Type error: {}\n\tExpected: {}\n\tGot: {}",
503
                    raw_value, expected, actual
504
                ),
505
91
                Some(node.range()),
506
            ));
507
91
            return Ok(None);
508
14634
        }
509
13960
    }
510
28594
    Ok(Some(lit))
511
28711
}
512

            
513
28080
pub(crate) fn parse_int(ctx: &mut ParseContext, node: &Node) -> Option<i32> {
514
28080
    let raw_value = &ctx.source_code[node.start_byte()..node.end_byte()];
515
28080
    if let Ok(v) = raw_value.parse::<i32>() {
516
28054
        Some(v)
517
    } else {
518
26
        ctx.record_error(RecoverableParseError::new(
519
26
            "Expected an integer here".to_string(),
520
26
            Some(node.range()),
521
        ));
522
26
        None
523
    }
524
28080
}