1use super::atom::parse_int;
2use super::util::named_children;
3use crate::diagnostics::diagnostics_api::SymbolKind;
4use crate::diagnostics::source_map::{HoverInfo, span_with_hover};
5use crate::errors::FatalParseError;
6use crate::expression::parse_expression;
7use crate::parser::ParseContext;
8use crate::{RecoverableParseError, child};
9use conjure_cp_core::ast::{
10 DeclarationPtr, Domain, DomainPtr, IntVal, Moo, Name, Range, RecordEntry, Reference, SetAttr,
11};
12use tree_sitter::Node;
13
14use crate::field;
15
16pub fn parse_domain(
18 ctx: &mut ParseContext,
19 domain: Node,
20) -> Result<Option<DomainPtr>, FatalParseError> {
21 match domain.kind() {
22 "domain" => {
23 let inner = match domain.child(0) {
24 Some(node) => node,
25 None => {
26 ctx.record_error(RecoverableParseError::new(
27 format!("{} in expression of kind '{}'", "domain", domain.kind()),
28 Some(domain.range()),
29 ));
30 return Ok(None);
31 }
32 };
33 parse_domain(ctx, inner)
34 }
35 "bool_domain" => {
36 ctx.add_span_and_doc_hover(&domain, "L_bool", SymbolKind::Domain, None, None);
37 Ok(Some(Domain::bool()))
38 }
39 "int_domain" => parse_int_domain(ctx, domain),
40 "identifier" => {
41 let Some(decl) = get_declaration_ptr_from_identifier(ctx, domain)? else {
42 return Ok(None);
43 };
44 let Some(dom) = Domain::reference(decl) else {
45 ctx.record_error(crate::errors::RecoverableParseError::new(
46 format!(
47 "The identifier '{}' is not a valid domain",
48 &ctx.source_code[domain.start_byte()..domain.end_byte()]
49 ),
50 Some(domain.range()),
51 ));
52 return Ok(None);
53 };
54 let name = &ctx.source_code[domain.start_byte()..domain.end_byte()];
55
56 span_with_hover(
58 &domain,
59 ctx.source_code,
60 ctx.source_map,
61 HoverInfo {
62 description: format!("Domain reference: {name}"),
63 kind: Some(SymbolKind::Variable),
64 ty: None,
65 decl_span: None, },
67 );
68 Ok(Some(dom))
69 }
70 "tuple_domain" => parse_tuple_domain(ctx, domain),
71 "matrix_domain" => parse_matrix_domain(ctx, domain),
72 "record_domain" => parse_record_domain(ctx, domain),
73 "set_domain" => parse_set_domain(ctx, domain),
74 _ => {
75 ctx.record_error(RecoverableParseError::new(
76 format!("{} is not a supported domain type", domain.kind()),
77 Some(domain.range()),
78 ));
79 Ok(None)
80 }
81 }
82}
83
84fn get_declaration_ptr_from_identifier(
85 ctx: &mut ParseContext,
86 identifier: Node,
87) -> Result<Option<DeclarationPtr>, FatalParseError> {
88 let name = Name::user(&ctx.source_code[identifier.start_byte()..identifier.end_byte()]);
89 let decl = ctx.symbols.as_ref().unwrap().read().lookup(&name);
90
91 if decl.is_none() {
92 ctx.record_error(crate::errors::RecoverableParseError::new(
93 format!("The identifier '{}' is not defined", name),
94 Some(identifier.range()),
95 ));
96 return Ok(None);
97 }
98 match decl {
99 Some(decl) => Ok(Some(decl)),
100 None => {
101 ctx.record_error(crate::errors::RecoverableParseError::new(
102 format!("The identifier '{}' is not defined", name),
103 Some(identifier.range()),
104 ));
105 Ok(None)
106 }
107 }
108}
109
110fn parse_int_domain(
112 ctx: &mut ParseContext,
113 int_domain: Node,
114) -> Result<Option<DomainPtr>, FatalParseError> {
115 let int_keyword_node = child!(int_domain, 0, "int");
116 if int_domain.child_count() == 1 {
117 ctx.add_span_and_doc_hover(&int_keyword_node, "L_int", SymbolKind::Domain, None, None);
119 return Ok(Some(Domain::int(vec![Range::Bounded(i32::MIN, i32::MAX)])));
120 }
121
122 let Some(range_list) = field!(recover, ctx, int_domain, "ranges") else {
123 return Ok(None);
124 };
125 let mut ranges_unresolved: Vec<Range<IntVal>> = Vec::new();
126 let mut all_resolved = true;
127
128 for domain_component in named_children(&range_list) {
129 match domain_component.kind() {
130 "atom" | "arithmetic_expr" => {
131 let Some(int_val) = parse_int_val(ctx, domain_component)? else {
132 return Ok(None);
133 };
134
135 if !matches!(int_val, IntVal::Const(_)) {
136 all_resolved = false;
137 }
138 ranges_unresolved.push(Range::Single(int_val));
139 }
140 "int_range" => {
141 let lower_bound = match domain_component.child_by_field_name("lower") {
142 Some(node) => {
143 match parse_int_val(ctx, node)? {
144 Some(val) => Some(val),
145 None => return Ok(None), }
147 }
148 None => None,
149 };
150 let upper_bound = match domain_component.child_by_field_name("upper") {
151 Some(node) => {
152 match parse_int_val(ctx, node)? {
153 Some(val) => Some(val),
154 None => return Ok(None), }
156 }
157 None => None,
158 };
159
160 match (lower_bound, upper_bound) {
161 (Some(lower), Some(upper)) => {
162 if let (IntVal::Const(l), IntVal::Const(u)) = (&lower, &upper) {
164 if l > u {
165 ctx.record_error(crate::errors::RecoverableParseError::new(
166 format!(
167 "Invalid integer range: lower bound {} is greater than upper bound {}",
168 l, u
169 ),
170 Some(domain_component.range()),
171 ));
172 }
173 } else {
174 all_resolved = false;
175 }
176 ranges_unresolved.push(Range::Bounded(lower, upper));
177 }
178 (Some(lower), None) => {
179 if !matches!(lower, IntVal::Const(_)) {
180 all_resolved = false;
181 }
182 ranges_unresolved.push(Range::UnboundedR(lower));
183 }
184 (None, Some(upper)) => {
185 if !matches!(upper, IntVal::Const(_)) {
186 all_resolved = false;
187 }
188 ranges_unresolved.push(Range::UnboundedL(upper));
189 }
190 _ => {
191 ctx.record_error(RecoverableParseError::new(
192 "Invalid int range: must have at least a lower or upper bound"
193 .to_string(),
194 Some(domain_component.range()),
195 ));
196 return Ok(None);
197 }
198 }
199 }
200 _ => {
201 ctx.record_error(RecoverableParseError::new(
202 format!(
203 "Unexpected int domain component: {}",
204 domain_component.kind()
205 ),
206 Some(domain_component.range()),
207 ));
208 return Ok(None);
209 }
210 }
211 }
212
213 if all_resolved {
215 let ranges: Vec<Range<i32>> = ranges_unresolved
216 .into_iter()
217 .map(|r| match r {
218 Range::Single(IntVal::Const(v)) => Range::Single(v),
219 Range::Bounded(IntVal::Const(l), IntVal::Const(u)) => Range::Bounded(l, u),
220 Range::UnboundedR(IntVal::Const(l)) => Range::UnboundedR(l),
221 Range::UnboundedL(IntVal::Const(u)) => Range::UnboundedL(u),
222 Range::Unbounded => Range::Unbounded,
223 _ => unreachable!("all_resolved should be true only if all are Const"),
224 })
225 .collect();
226
227 ctx.add_span_and_doc_hover(&int_keyword_node, "L_int", SymbolKind::Domain, None, None);
228 Ok(Some(Domain::int(ranges)))
229 } else {
230 ctx.add_span_and_doc_hover(&int_keyword_node, "L_int", SymbolKind::Domain, None, None);
234 Ok(Some(Domain::int(ranges_unresolved)))
235 }
236}
237
238fn parse_int_val(ctx: &mut ParseContext, node: Node) -> Result<Option<IntVal>, FatalParseError> {
241 if node.kind() == "atom" {
243 let text = &ctx.source_code[node.start_byte()..node.end_byte()];
244 if let Ok(integer) = text.parse::<i32>() {
245 return Ok(Some(IntVal::Const(integer)));
246 }
247 let Some(decl) = get_declaration_ptr_from_identifier(ctx, node)? else {
249 return Ok(None);
251 };
252 return Ok(Some(IntVal::Reference(Reference::new(decl))));
253 }
254
255 let Some(expr) = parse_expression(ctx, node)? else {
257 return Ok(None);
258 };
259 Ok(Some(IntVal::Expr(Moo::new(expr))))
260}
261
262fn parse_tuple_domain(
263 ctx: &mut ParseContext,
264 tuple_domain: Node,
265) -> Result<Option<DomainPtr>, FatalParseError> {
266 let mut domains: Vec<DomainPtr> = Vec::new();
267 for domain in named_children(&tuple_domain) {
268 let Some(parsed_domain) = parse_domain(ctx, domain)? else {
269 return Ok(None);
270 };
271 domains.push(parsed_domain);
272 }
273
274 if let Some(first) = tuple_domain.child(0)
276 && first.kind() == "tuple"
277 {
278 ctx.add_span_and_doc_hover(&first, "L_tuple", SymbolKind::Domain, None, None);
280 }
281
282 Ok(Some(Domain::tuple(domains)))
283}
284
285fn parse_matrix_domain(
286 ctx: &mut ParseContext,
287 matrix_domain: Node,
288) -> Result<Option<DomainPtr>, FatalParseError> {
289 let mut domains: Vec<DomainPtr> = Vec::new();
290 let Some(index_domain_list) = field!(recover, ctx, matrix_domain, "index_domain_list") else {
291 return Ok(None);
292 };
293 for domain in named_children(&index_domain_list) {
294 let Some(parsed_domain) = parse_domain(ctx, domain)? else {
295 return Ok(None);
296 };
297 domains.push(parsed_domain);
298 }
299 let Some(value_domain_node) = field!(recover, ctx, matrix_domain, "value_domain") else {
300 return Ok(None);
301 };
302 let Some(value_domain) = parse_domain(ctx, value_domain_node)? else {
303 return Ok(None);
304 };
305
306 let matrix_keyword_node = child!(matrix_domain, 0, "matrix");
308 ctx.add_span_and_doc_hover(
309 &matrix_keyword_node,
310 "matrix",
311 SymbolKind::Domain,
312 None,
313 None,
314 );
315 Ok(Some(Domain::matrix(value_domain, domains)))
316}
317
318fn parse_record_domain(
319 ctx: &mut ParseContext,
320 record_domain: Node,
321) -> Result<Option<DomainPtr>, FatalParseError> {
322 let mut record_entries: Vec<RecordEntry> = Vec::new();
323 for record_entry in named_children(&record_domain) {
324 let Some(name_node) = field!(recover, ctx, record_entry, "name") else {
325 return Ok(None);
326 };
327 let name = Name::user(&ctx.source_code[name_node.start_byte()..name_node.end_byte()]);
328 let Some(domain_node) = field!(recover, ctx, record_entry, "domain") else {
329 return Ok(None);
330 };
331 let Some(domain) = parse_domain(ctx, domain_node)? else {
332 return Ok(None);
333 };
334 record_entries.push(RecordEntry { name, domain });
335 }
336
337 let record_keyword_node = child!(record_domain, 0, "record");
339 ctx.add_span_and_doc_hover(
340 &record_keyword_node,
341 "L_record",
342 SymbolKind::Domain,
343 None,
344 None,
345 );
346 Ok(Some(Domain::record(record_entries)))
347}
348
349pub fn parse_set_domain(
350 ctx: &mut ParseContext,
351 set_domain: Node,
352) -> Result<Option<DomainPtr>, FatalParseError> {
353 let mut set_attribute: Option<SetAttr> = None;
354 let mut value_domain: Option<DomainPtr> = None;
355
356 for child in named_children(&set_domain) {
357 match child.kind() {
358 "set_attributes" => {
359 let min_value_node = child.child_by_field_name("min_value");
361 let max_value_node = child.child_by_field_name("max_value");
362 let size_value_node = child.child_by_field_name("size_value");
363
364 if let (Some(min_node), Some(max_node)) = (min_value_node, max_value_node) {
365 let Some(min_val) = parse_int(ctx, &min_node) else {
367 return Ok(None);
368 };
369 let Some(max_val) = parse_int(ctx, &max_node) else {
370 return Ok(None);
371 };
372
373 set_attribute = Some(SetAttr::new_min_max_size(min_val, max_val));
374 } else if let Some(size_node) = size_value_node {
375 let Some(size_val) = parse_int(ctx, &size_node) else {
377 return Ok(None);
378 };
379 set_attribute = Some(SetAttr::new_size(size_val));
380 } else if let Some(min_node) = min_value_node {
381 let Some(min_val) = parse_int(ctx, &min_node) else {
383 return Ok(None);
384 };
385 set_attribute = Some(SetAttr::new_min_size(min_val));
386 } else if let Some(max_node) = max_value_node {
387 let Some(max_val) = parse_int(ctx, &max_node) else {
389 return Ok(None);
390 };
391 set_attribute = Some(SetAttr::new_max_size(max_val));
392 }
393 }
394 "domain" => {
395 let Some(parsed_domain) = parse_domain(ctx, child)? else {
396 return Ok(None);
397 };
398 value_domain = Some(parsed_domain);
399 }
400 _ => {
401 ctx.record_error(RecoverableParseError::new(
402 format!("Unrecognized set domain child kind: {}", child.kind()),
403 Some(child.range()),
404 ));
405 return Ok(None);
406 }
407 }
408 }
409
410 if let Some(domain) = value_domain {
411 let set_keyword_node = child!(set_domain, 0, "set");
413 ctx.add_span_and_doc_hover(&set_keyword_node, "set", SymbolKind::Domain, None, None);
415 Ok(Some(Domain::set(set_attribute.unwrap_or_default(), domain)))
416 } else {
417 ctx.record_error(RecoverableParseError::new(
418 "Set domain must have a value domain".to_string(),
419 Some(set_domain.range()),
420 ));
421 Ok(None)
422 }
423}