Skip to main content

conjure_cp_essence_parser/parser/
find.rs

1#![allow(clippy::legacy_numeric_constants)]
2
3use std::collections::BTreeMap;
4use tree_sitter::Node;
5
6use super::ParseContext;
7use super::domain::parse_domain;
8use super::util::named_children;
9use crate::diagnostics::diagnostics_api::SymbolKind;
10use crate::diagnostics::source_map::{HoverInfo, span_with_hover};
11use crate::errors::{FatalParseError, RecoverableParseError};
12use crate::field;
13use conjure_cp_core::ast::{DomainPtr, Name};
14
15/// Parse a find statement into a map of decision variable names to their domains.
16pub fn parse_find_statement(
17    ctx: &mut ParseContext,
18    find_statement: Node,
19) -> Result<BTreeMap<Name, DomainPtr>, FatalParseError> {
20    let mut vars = BTreeMap::new();
21
22    let domain = field!(find_statement, "domain");
23    let Some(domain) = parse_domain(ctx, domain)? else {
24        return Ok(vars);
25    };
26
27    let variable_list = field!(find_statement, "variables");
28    for variable in named_children(&variable_list) {
29        let variable_name = &ctx.source_code[variable.start_byte()..variable.end_byte()];
30        let name = Name::user(variable_name);
31
32        // Check for duplicate within the same statement
33        if vars.contains_key(&name) {
34            ctx.errors.push(RecoverableParseError::new(
35                format!(
36                    "Variable '{}' is already declared in this find statement",
37                    variable_name
38                ),
39                Some(variable.range()),
40            ));
41            // don't return here, as we can still add the other variables to the symbol table
42            continue;
43        }
44
45        // Check for duplicate declaration across statements
46        if let Some(symbols) = &ctx.symbols
47            && symbols.read().lookup(&name).is_some()
48        {
49            ctx.errors.push(RecoverableParseError::new(
50                format!(
51                    "Variable '{}' is already declared in a previous statement",
52                    variable_name
53                ),
54                Some(variable.range()),
55            ));
56            // don't return here, as we can still add the other variables to the symbol table
57            continue;
58        }
59
60        vars.insert(name, domain.clone());
61        let hover = HoverInfo {
62            description: format!("Find variable: {variable_name}"),
63            kind: Some(SymbolKind::Find),
64            ty: Some(domain.to_string()),
65            decl_span: None,
66        };
67        span_with_hover(&variable, ctx.source_code, ctx.source_map, hover);
68    }
69
70    Ok(vars)
71}