conjure_cp_essence_parser/parser/
find.rs1#![allow(clippy::legacy_numeric_constants)]
2use crate::field;
3
4use std::collections::BTreeMap;
5use tree_sitter::Node;
6
7use super::ParseContext;
8use super::domain::parse_domain;
9use super::util::named_children;
10use crate::diagnostics::diagnostics_api::SymbolKind;
11use crate::diagnostics::source_map::{HoverInfo, span_with_hover};
12use crate::errors::{FatalParseError, RecoverableParseError};
13use conjure_cp_core::ast::{DomainPtr, Name};
14
15pub fn parse_find_statement(
16 ctx: &mut ParseContext,
17 find_statement: Node,
18) -> Result<BTreeMap<Name, DomainPtr>, FatalParseError> {
19 let Some(keyword) = field!(recover, ctx, find_statement, "find_keyword") else {
20 return Ok(BTreeMap::new());
21 };
22 ctx.add_span_and_doc_hover(&keyword, "find", SymbolKind::Find, None, None);
23 let mut var_hashmap = BTreeMap::new();
24 for var_decl in named_children(&find_statement) {
25 if let Ok(mut decls) = parse_declaration_statement(ctx, var_decl, SymbolKind::FindVar) {
26 var_hashmap.append(&mut decls);
27 }
28 }
29 Ok(var_hashmap)
30}
31
32pub fn parse_given_statement(
33 ctx: &mut ParseContext,
34 given_statement: Node,
35) -> Result<BTreeMap<Name, DomainPtr>, FatalParseError> {
36 let Some(keyword) = field!(recover, ctx, given_statement, "given_keyword") else {
37 return Ok(BTreeMap::new());
38 };
39 span_with_hover(
40 &keyword,
41 ctx.source_code,
42 ctx.source_map,
43 HoverInfo {
44 description: "Given keyword".to_string(),
45 doc_key: None,
46 kind: Some(SymbolKind::Given),
47 ty: None,
48 decl_span: None,
49 },
50 );
51
52 let mut var_hashmap = BTreeMap::new();
53 for var_decl in named_children(&given_statement) {
54 if let Ok(mut decls) = parse_declaration_statement(ctx, var_decl, SymbolKind::GivenVar) {
55 var_hashmap.append(&mut decls);
56 }
57 }
58 Ok(var_hashmap)
59}
60
61pub fn parse_declaration_statement(
62 ctx: &mut ParseContext,
63 statement_node: Node,
64 symbol_kind: SymbolKind,
65) -> Result<BTreeMap<Name, DomainPtr>, FatalParseError> {
66 let mut vars = BTreeMap::new();
67
68 let Some(domain_node) = field!(recover, ctx, statement_node, "domain") else {
69 return Ok(vars);
70 };
71
72 let Some(domain) = parse_domain(ctx, domain_node)? else {
73 return Ok(vars);
74 };
75
76 let Some(variable_list) = field!(recover, ctx, statement_node, "variables") else {
77 return Ok(vars);
78 };
79 for variable in named_children(&variable_list) {
80 let start = variable.start_byte();
82 let end = variable.end_byte();
83 if end > ctx.source_code.len() {
84 ctx.record_error(RecoverableParseError::new(
85 "Variable name extends beyond end of source code".to_string(),
86 Some(variable.range()),
87 ));
88 continue;
89 }
90 let variable_name = &ctx.source_code[start..end];
91 let name = Name::user(variable_name);
92
93 if vars.contains_key(&name) {
95 ctx.errors.push(RecoverableParseError::new(
96 format!(
97 "Variable '{}' is already declared in this {} statement",
98 variable_name,
99 match symbol_kind {
100 SymbolKind::FindVar => "find",
101 SymbolKind::GivenVar => "given",
102 _ => "declaration",
103 }
104 ),
105 Some(variable.range()),
106 ));
107 continue;
109 }
110
111 if let Some(symbols) = &ctx.symbols
113 && symbols.read().lookup(&name).is_some()
114 {
115 let previous_line = ctx.lookup_decl_line(&name);
116 ctx.errors.push(RecoverableParseError::new(
117 match previous_line {
118 Some(line) => format!(
119 "Variable '{}' is already declared in a previous statement on line {}",
120 variable_name, line
121 ),
122 None => format!(
123 "Variable '{}' is already declared in a previous statement",
124 variable_name
125 ),
126 },
127 Some(variable.range()),
128 ));
129 continue;
131 }
132
133 vars.insert(name.clone(), domain.clone());
134 let hover = HoverInfo {
135 description: format!(
136 "{} variable: {variable_name}",
137 match symbol_kind {
138 SymbolKind::FindVar => "Find",
139 SymbolKind::GivenVar => "Given",
140 _ => "Declaration",
141 }
142 ),
143 doc_key: None,
144 kind: Some(symbol_kind),
145 ty: Some(domain.to_string()),
146 decl_span: None,
147 };
148 let span_id = span_with_hover(&variable, ctx.source_code, ctx.source_map, hover);
149 ctx.save_decl_span(name, span_id);
150 }
151
152 Ok(vars)
153}