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

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

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

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

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

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

            
70
793
    Ok(vars)
71
837
}