1
#![allow(clippy::legacy_numeric_constants)]
2

            
3
use std::collections::BTreeMap;
4
use tree_sitter::Node;
5

            
6
use super::ParseContext;
7
use super::domain::parse_domain;
8
use super::util::named_children;
9
use crate::diagnostics::diagnostics_api::SymbolKind;
10
use crate::diagnostics::source_map::{HoverInfo, span_with_hover};
11
use crate::errors::{FatalParseError, RecoverableParseError};
12
use crate::field;
13
use conjure_cp_core::ast::{DomainPtr, Name};
14

            
15
/// Parse a find statement into a map of decision variable names to their domains.
16
1576
pub fn parse_find_statement(
17
1576
    ctx: &mut ParseContext,
18
1576
    find_statement: Node,
19
1576
) -> Result<BTreeMap<Name, DomainPtr>, FatalParseError> {
20
1576
    let mut vars = BTreeMap::new();
21

            
22
1576
    let domain = field!(find_statement, "domain");
23
1576
    let Some(domain) = parse_domain(ctx, domain)? else {
24
33
        return Ok(vars);
25
    };
26

            
27
1543
    let variable_list = field!(find_statement, "variables");
28
1791
    for variable in named_children(&variable_list) {
29
1791
        let variable_name = &ctx.source_code[variable.start_byte()..variable.end_byte()];
30
1791
        let name = Name::user(variable_name);
31

            
32
        // Check for duplicate within the same statement
33
1791
        if vars.contains_key(&name) {
34
11
            ctx.errors.push(RecoverableParseError::new(
35
11
                format!(
36
                    "Variable '{}' is already declared in this find statement",
37
                    variable_name
38
                ),
39
11
                Some(variable.range()),
40
            ));
41
            // don't return here, as we can still add the other variables to the symbol table
42
11
            continue;
43
1780
        }
44

            
45
        // Check for duplicate declaration across statements
46
1780
        if let Some(symbols) = &ctx.symbols
47
1780
            && symbols.read().lookup(&name).is_some()
48
        {
49
11
            ctx.errors.push(RecoverableParseError::new(
50
11
                format!(
51
                    "Variable '{}' is already declared in a previous statement",
52
                    variable_name
53
                ),
54
11
                Some(variable.range()),
55
            ));
56
            // don't return here, as we can still add the other variables to the symbol table
57
11
            continue;
58
1769
        }
59

            
60
1769
        vars.insert(name, domain.clone());
61
1769
        let hover = HoverInfo {
62
1769
            description: format!("Find variable: {variable_name}"),
63
1769
            kind: Some(SymbolKind::Find),
64
1769
            ty: Some(domain.to_string()),
65
1769
            decl_span: None,
66
1769
        };
67
1769
        span_with_hover(&variable, ctx.source_code, ctx.source_map, hover);
68
    }
69

            
70
1543
    Ok(vars)
71
1576
}