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
694
pub fn parse_find_statement(
17
694
    ctx: &mut ParseContext,
18
694
    find_statement: Node,
19
694
) -> Result<BTreeMap<Name, DomainPtr>, FatalParseError> {
20
694
    let mut vars = BTreeMap::new();
21

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

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

            
32
        // Check for duplicate within the same statement
33
729
        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
718
        }
44

            
45
        // Check for duplicate declaration across statements
46
718
        if let Some(symbols) = &ctx.symbols
47
718
            && 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
707
        }
59

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

            
70
661
    Ok(vars)
71
694
}