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, FieldEntry, IntVal, Moo, Name, Range, 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 doc_key: None,
64 kind: Some(SymbolKind::Variable),
65 ty: None,
66 decl_span: None, },
68 );
69 Ok(Some(dom))
70 }
71 "tuple_domain" => parse_tuple_domain(ctx, domain),
72 "matrix_domain" => parse_matrix_domain(ctx, domain),
73 "record_domain" => parse_record_domain(ctx, domain),
74 "set_domain" => parse_set_domain(ctx, domain),
75 _ => {
76 ctx.record_error(RecoverableParseError::new(
77 format!("{} is not a supported domain type", domain.kind()),
78 Some(domain.range()),
79 ));
80 Ok(None)
81 }
82 }
83}
84
85fn get_declaration_ptr_from_identifier(
86 ctx: &mut ParseContext,
87 identifier: Node,
88) -> Result<Option<DeclarationPtr>, FatalParseError> {
89 let name = Name::user(&ctx.source_code[identifier.start_byte()..identifier.end_byte()]);
90 let decl = ctx.symbols.as_ref().unwrap().read().lookup(&name);
91
92 if decl.is_none() {
93 ctx.record_error(crate::errors::RecoverableParseError::new(
94 format!("The identifier '{}' is not defined", name),
95 Some(identifier.range()),
96 ));
97 return Ok(None);
98 }
99 match decl {
100 Some(decl) => Ok(Some(decl)),
101 None => {
102 ctx.record_error(crate::errors::RecoverableParseError::new(
103 format!("The identifier '{}' is not defined", name),
104 Some(identifier.range()),
105 ));
106 Ok(None)
107 }
108 }
109}
110
111fn parse_int_domain(
113 ctx: &mut ParseContext,
114 int_domain: Node,
115) -> Result<Option<DomainPtr>, FatalParseError> {
116 let int_keyword_node = child!(int_domain, 0, "int");
117 if int_domain.child_count() == 1 {
118 ctx.add_span_and_doc_hover(&int_keyword_node, "L_int", SymbolKind::Domain, None, None);
120 return Ok(Some(Domain::int(vec![Range::Bounded(i32::MIN, i32::MAX)])));
121 }
122
123 let Some(range_list) = field!(recover, ctx, int_domain, "ranges") else {
124 return Ok(None);
125 };
126 let mut ranges_unresolved: Vec<Range<IntVal>> = Vec::new();
127 let mut all_resolved = true;
128
129 for domain_component in named_children(&range_list) {
130 match domain_component.kind() {
131 "atom" | "arithmetic_expr" => {
132 let Some(int_val) = parse_int_val(ctx, domain_component)? else {
133 return Ok(None);
134 };
135
136 if !matches!(int_val, IntVal::Const(_)) {
137 all_resolved = false;
138 }
139 ranges_unresolved.push(Range::Single(int_val));
140 }
141 "int_range" => {
142 let lower_bound = match domain_component.child_by_field_name("lower") {
143 Some(node) => {
144 match parse_int_val(ctx, node)? {
145 Some(val) => Some(val),
146 None => return Ok(None), }
148 }
149 None => None,
150 };
151 let upper_bound = match domain_component.child_by_field_name("upper") {
152 Some(node) => {
153 match parse_int_val(ctx, node)? {
154 Some(val) => Some(val),
155 None => return Ok(None), }
157 }
158 None => None,
159 };
160
161 match (lower_bound, upper_bound) {
162 (Some(lower), Some(upper)) => {
163 if let (IntVal::Const(l), IntVal::Const(u)) = (&lower, &upper) {
165 if l > u {
166 ctx.record_error(crate::errors::RecoverableParseError::new(
167 format!(
168 "Invalid integer range: lower bound {} is greater than upper bound {}",
169 l, u
170 ),
171 Some(domain_component.range()),
172 ));
173 }
174 } else {
175 all_resolved = false;
176 }
177 ranges_unresolved.push(Range::Bounded(lower, upper));
178 }
179 (Some(lower), None) => {
180 if !matches!(lower, IntVal::Const(_)) {
181 all_resolved = false;
182 }
183 ranges_unresolved.push(Range::UnboundedR(lower));
184 }
185 (None, Some(upper)) => {
186 if !matches!(upper, IntVal::Const(_)) {
187 all_resolved = false;
188 }
189 ranges_unresolved.push(Range::UnboundedL(upper));
190 }
191 _ => {
192 ctx.record_error(RecoverableParseError::new(
193 "Invalid int range: must have at least a lower or upper bound"
194 .to_string(),
195 Some(domain_component.range()),
196 ));
197 return Ok(None);
198 }
199 }
200 }
201 _ => {
202 ctx.record_error(RecoverableParseError::new(
203 format!(
204 "Unexpected int domain component: {}",
205 domain_component.kind()
206 ),
207 Some(domain_component.range()),
208 ));
209 return Ok(None);
210 }
211 }
212 }
213
214 if all_resolved {
216 let ranges: Vec<Range<i32>> = ranges_unresolved
217 .into_iter()
218 .map(|r| match r {
219 Range::Single(IntVal::Const(v)) => Range::Single(v),
220 Range::Bounded(IntVal::Const(l), IntVal::Const(u)) => Range::Bounded(l, u),
221 Range::UnboundedR(IntVal::Const(l)) => Range::UnboundedR(l),
222 Range::UnboundedL(IntVal::Const(u)) => Range::UnboundedL(u),
223 Range::Unbounded => Range::Unbounded,
224 _ => unreachable!("all_resolved should be true only if all are Const"),
225 })
226 .collect();
227
228 ctx.add_span_and_doc_hover(&int_keyword_node, "L_int", SymbolKind::Domain, None, None);
229 Ok(Some(Domain::int(ranges)))
230 } else {
231 ctx.add_span_and_doc_hover(&int_keyword_node, "L_int", SymbolKind::Domain, None, None);
235 Ok(Some(Domain::int(ranges_unresolved)))
236 }
237}
238
239fn parse_int_val(ctx: &mut ParseContext, node: Node) -> Result<Option<IntVal>, FatalParseError> {
242 if node.kind() == "atom" {
244 let text = &ctx.source_code[node.start_byte()..node.end_byte()];
245 if let Ok(integer) = text.parse::<i32>() {
246 return Ok(Some(IntVal::Const(integer)));
247 }
248 let Some(decl) = get_declaration_ptr_from_identifier(ctx, node)? else {
250 return Ok(None);
252 };
253 return Ok(Some(IntVal::Reference(Reference::new(decl))));
254 }
255
256 let Some(expr) = parse_expression(ctx, node)? else {
258 return Ok(None);
259 };
260 Ok(Some(IntVal::Expr(Moo::new(expr))))
261}
262
263fn parse_tuple_domain(
264 ctx: &mut ParseContext,
265 tuple_domain: Node,
266) -> Result<Option<DomainPtr>, FatalParseError> {
267 let mut domains: Vec<DomainPtr> = Vec::new();
268 for domain in named_children(&tuple_domain) {
269 let Some(parsed_domain) = parse_domain(ctx, domain)? else {
270 return Ok(None);
271 };
272 domains.push(parsed_domain);
273 }
274
275 if let Some(first) = tuple_domain.child(0)
277 && first.kind() == "tuple"
278 {
279 ctx.add_span_and_doc_hover(&first, "L_tuple", SymbolKind::Domain, None, None);
281 }
282
283 Ok(Some(Domain::tuple(domains)))
284}
285
286fn parse_matrix_domain(
287 ctx: &mut ParseContext,
288 matrix_domain: Node,
289) -> Result<Option<DomainPtr>, FatalParseError> {
290 let mut domains: Vec<DomainPtr> = Vec::new();
291 let Some(index_domain_list) = field!(recover, ctx, matrix_domain, "index_domain_list") else {
292 return Ok(None);
293 };
294 for domain in named_children(&index_domain_list) {
295 let Some(parsed_domain) = parse_domain(ctx, domain)? else {
296 return Ok(None);
297 };
298 domains.push(parsed_domain);
299 }
300 let Some(value_domain_node) = field!(recover, ctx, matrix_domain, "value_domain") else {
301 return Ok(None);
302 };
303 let Some(value_domain) = parse_domain(ctx, value_domain_node)? else {
304 return Ok(None);
305 };
306
307 let matrix_keyword_node = child!(matrix_domain, 0, "matrix");
309 ctx.add_span_and_doc_hover(
310 &matrix_keyword_node,
311 "matrix",
312 SymbolKind::Domain,
313 None,
314 None,
315 );
316 Ok(Some(Domain::matrix(value_domain, domains)))
317}
318
319fn parse_record_domain(
320 ctx: &mut ParseContext,
321 record_domain: Node,
322) -> Result<Option<DomainPtr>, FatalParseError> {
323 let mut record_entries: Vec<FieldEntry> = Vec::new();
324 for record_entry in named_children(&record_domain) {
325 let Some(name_node) = field!(recover, ctx, record_entry, "name") else {
326 return Ok(None);
327 };
328 let name = Name::user(&ctx.source_code[name_node.start_byte()..name_node.end_byte()]);
329 let Some(domain_node) = field!(recover, ctx, record_entry, "domain") else {
330 return Ok(None);
331 };
332 let Some(domain) = parse_domain(ctx, domain_node)? else {
333 return Ok(None);
334 };
335 record_entries.push(FieldEntry { name, domain });
336 }
337
338 let record_keyword_node = child!(record_domain, 0, "record");
340 ctx.add_span_and_doc_hover(
341 &record_keyword_node,
342 "L_record",
343 SymbolKind::Domain,
344 None,
345 None,
346 );
347 Ok(Some(Domain::record(record_entries)))
348}
349
350pub fn parse_set_domain(
351 ctx: &mut ParseContext,
352 set_domain: Node,
353) -> Result<Option<DomainPtr>, FatalParseError> {
354 let mut set_attribute: Option<SetAttr> = None;
355 let mut value_domain: Option<DomainPtr> = None;
356
357 for child in named_children(&set_domain) {
358 match child.kind() {
359 "set_attributes" => {
360 let min_value_node = child.child_by_field_name("min_value");
362 let max_value_node = child.child_by_field_name("max_value");
363 let size_value_node = child.child_by_field_name("size_value");
364
365 if let (Some(min_node), Some(max_node)) = (min_value_node, max_value_node) {
366 let Some(min_val) = parse_int(ctx, &min_node) else {
368 return Ok(None);
369 };
370 let Some(max_val) = parse_int(ctx, &max_node) else {
371 return Ok(None);
372 };
373
374 set_attribute = Some(SetAttr::new_min_max_size(min_val, max_val));
375 } else if let Some(size_node) = size_value_node {
376 let Some(size_val) = parse_int(ctx, &size_node) else {
378 return Ok(None);
379 };
380 set_attribute = Some(SetAttr::new_size(size_val));
381 } else if let Some(min_node) = min_value_node {
382 let Some(min_val) = parse_int(ctx, &min_node) else {
384 return Ok(None);
385 };
386 set_attribute = Some(SetAttr::new_min_size(min_val));
387 } else if let Some(max_node) = max_value_node {
388 let Some(max_val) = parse_int(ctx, &max_node) else {
390 return Ok(None);
391 };
392 set_attribute = Some(SetAttr::new_max_size(max_val));
393 }
394 }
395 "domain" => {
396 let Some(parsed_domain) = parse_domain(ctx, child)? else {
397 return Ok(None);
398 };
399 value_domain = Some(parsed_domain);
400 }
401 _ => {
402 ctx.record_error(RecoverableParseError::new(
403 format!("Unrecognized set domain child kind: {}", child.kind()),
404 Some(child.range()),
405 ));
406 return Ok(None);
407 }
408 }
409 }
410
411 if let Some(domain) = value_domain {
412 let set_keyword_node = child!(set_domain, 0, "set");
414 ctx.add_span_and_doc_hover(&set_keyword_node, "set", SymbolKind::Domain, None, None);
416 Ok(Some(Domain::set(set_attribute.unwrap_or_default(), domain)))
417 } else {
418 ctx.record_error(RecoverableParseError::new(
419 "Set domain must have a value domain".to_string(),
420 Some(set_domain.range()),
421 ));
422 Ok(None)
423 }
424}