1
#![allow(clippy::legacy_numeric_constants)]
2
use crate::field;
3

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

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

            
15
37928
pub fn parse_find_statement(
16
37928
    ctx: &mut ParseContext,
17
37928
    find_statement: Node,
18
37928
) -> Result<BTreeMap<Name, DomainPtr>, FatalParseError> {
19
37928
    let Some(keyword) = field!(recover, ctx, find_statement, "find_keyword") else {
20
        return Ok(BTreeMap::new());
21
    };
22
37928
    ctx.add_span_and_doc_hover(&keyword, "find", SymbolKind::Find, None, None);
23
37928
    let mut var_hashmap = BTreeMap::new();
24
38747
    for var_decl in named_children(&find_statement) {
25
38747
        if let Ok(mut decls) = parse_declaration_statement(ctx, var_decl, SymbolKind::Find) {
26
38747
            var_hashmap.append(&mut decls);
27
38747
        }
28
    }
29
37928
    Ok(var_hashmap)
30
37928
}
31

            
32
142
pub fn parse_given_statement(
33
142
    ctx: &mut ParseContext,
34
142
    given_statement: Node,
35
142
) -> Result<BTreeMap<Name, DomainPtr>, FatalParseError> {
36
142
    let Some(keyword) = field!(recover, ctx, given_statement, "given_keyword") else {
37
        return Ok(BTreeMap::new());
38
    };
39
142
    span_with_hover(
40
142
        &keyword,
41
142
        ctx.source_code,
42
142
        ctx.source_map,
43
142
        HoverInfo {
44
142
            description: "Given keyword".to_string(),
45
142
            kind: Some(SymbolKind::Given),
46
142
            ty: None,
47
142
            decl_span: None,
48
142
        },
49
    );
50

            
51
142
    let mut var_hashmap = BTreeMap::new();
52
142
    for var_decl in named_children(&given_statement) {
53
142
        if let Ok(mut decls) = parse_declaration_statement(ctx, var_decl, SymbolKind::Given) {
54
142
            var_hashmap.append(&mut decls);
55
142
        }
56
    }
57
142
    Ok(var_hashmap)
58
142
}
59

            
60
38889
pub fn parse_declaration_statement(
61
38889
    ctx: &mut ParseContext,
62
38889
    statement_node: Node,
63
38889
    symbol_kind: SymbolKind,
64
38889
) -> Result<BTreeMap<Name, DomainPtr>, FatalParseError> {
65
38889
    let mut vars = BTreeMap::new();
66

            
67
38889
    let Some(domain_node) = field!(recover, ctx, statement_node, "domain") else {
68
13
        return Ok(vars);
69
    };
70

            
71
38876
    let Some(domain) = parse_domain(ctx, domain_node)? else {
72
78
        return Ok(vars);
73
    };
74

            
75
38798
    let Some(variable_list) = field!(recover, ctx, statement_node, "variables") else {
76
        return Ok(vars);
77
    };
78
44836
    for variable in named_children(&variable_list) {
79
        // avoid the _FRAGMENT_EXPRESSION panic by checking range before slicing the source code
80
44836
        let start = variable.start_byte();
81
44836
        let end = variable.end_byte();
82
44836
        if end > ctx.source_code.len() {
83
            ctx.record_error(RecoverableParseError::new(
84
                "Variable name extends beyond end of source code".to_string(),
85
                Some(variable.range()),
86
            ));
87
            continue;
88
44836
        }
89
44836
        let variable_name = &ctx.source_code[start..end];
90
44836
        let name = Name::user(variable_name);
91

            
92
        // Check for duplicate within the same statement
93
44836
        if vars.contains_key(&name) {
94
13
            ctx.errors.push(RecoverableParseError::new(
95
13
                format!(
96
                    "Variable '{}' is already declared in this {} statement",
97
                    variable_name,
98
13
                    match symbol_kind {
99
13
                        SymbolKind::Find => "find",
100
                        SymbolKind::Given => "given",
101
                        _ => "declaration",
102
                    }
103
                ),
104
13
                Some(variable.range()),
105
            ));
106
            // don't return here, as we can still add the other variables to the symbol table
107
13
            continue;
108
44823
        }
109

            
110
        // Check for duplicate declaration across statements
111
44823
        if let Some(symbols) = &ctx.symbols
112
44823
            && symbols.read().lookup(&name).is_some()
113
        {
114
26
            let previous_line = ctx.lookup_decl_line(&name);
115
26
            ctx.errors.push(RecoverableParseError::new(
116
26
                match previous_line {
117
26
                    Some(line) => format!(
118
                        "Variable '{}' is already declared in a previous statement on line {}",
119
                        variable_name, line
120
                    ),
121
                    None => format!(
122
                        "Variable '{}' is already declared in a previous statement",
123
                        variable_name
124
                    ),
125
                },
126
26
                Some(variable.range()),
127
            ));
128
            // don't return here, as we can still add the other variables to the symbol table
129
26
            continue;
130
44797
        }
131

            
132
44797
        vars.insert(name.clone(), domain.clone());
133
44797
        let hover = HoverInfo {
134
44797
            description: format!(
135
                "{} variable: {variable_name}",
136
44797
                match symbol_kind {
137
44668
                    SymbolKind::Find => "Find",
138
129
                    SymbolKind::Given => "Given",
139
                    _ => "Declaration",
140
                }
141
            ),
142
44797
            kind: Some(symbol_kind),
143
44797
            ty: Some(domain.to_string()),
144
44797
            decl_span: None,
145
        };
146
44797
        let span_id = span_with_hover(&variable, ctx.source_code, ctx.source_map, hover);
147
44797
        ctx.save_decl_span(name, span_id);
148
    }
149

            
150
38798
    Ok(vars)
151
38889
}