Skip to main content

conjure_cp_essence_parser/parser/
domain.rs

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::util::TypecheckingContext;
9use crate::{RecoverableParseError, child};
10use conjure_cp_core::ast::Typeable;
11use conjure_cp_core::ast::{
12    Atom, BinaryAttr, DeclarationPtr, Domain, DomainPtr, EnumVariantVal, Expression, Field,
13    FuncAttr, IntVal, JectivityAttr, Literal, MSetAttr, Moo, Name, PartialityAttr, PartitionAttr,
14    PermutationAttr, Range, Reference, RelAttr, SequenceAttr, SetAttr,
15};
16use tree_sitter::Node;
17
18use crate::field;
19
20/// Parse an Essence variable domain into its Conjure AST representation.
21pub fn parse_domain(
22    ctx: &mut ParseContext,
23    domain: Node,
24) -> Result<Option<DomainPtr>, FatalParseError> {
25    match domain.kind() {
26        "domain" | "annotation_domain" => {
27            let inner = match domain.child(0) {
28                Some(node) => node,
29                None => {
30                    ctx.record_error(RecoverableParseError::new(
31                        format!("{} in expression of kind '{}'", "domain", domain.kind()),
32                        Some(domain.range()),
33                    ));
34                    return Ok(None);
35                }
36            };
37            parse_domain(ctx, inner)
38        }
39        "enum_domain" => parse_enum_domain(ctx, domain),
40        "bool_domain" => {
41            ctx.add_span_and_doc_hover(&domain, "L_bool", SymbolKind::Domain, None, None);
42            Ok(Some(Domain::bool()))
43        }
44        "int_domain" | "annotation_int_domain" => parse_int_domain(ctx, domain),
45        "identifier" => {
46            let Some(decl) = get_declaration_ptr_from_identifier(ctx, domain)? else {
47                return Ok(None);
48            };
49            let is_unnamed = decl.as_unnamed_type().is_some();
50            let type_domain = if is_unnamed {
51                decl.domain()
52            } else {
53                decl.as_enumerated_type().map(|ty| ty.to_domain())
54            };
55            let Some(dom) = type_domain.or_else(|| Domain::reference(decl)) else {
56                ctx.record_error(crate::errors::RecoverableParseError::new(
57                    format!(
58                        "The identifier '{}' is not a valid domain",
59                        &ctx.source_code[domain.start_byte()..domain.end_byte()]
60                    ),
61                    Some(domain.range()),
62                ));
63                return Ok(None);
64            };
65            let name = &ctx.source_code[domain.start_byte()..domain.end_byte()];
66
67            // Not form docs, because we need context specific hover info
68            span_with_hover(
69                &domain,
70                ctx.source_code,
71                ctx.source_map,
72                HoverInfo {
73                    description: format!("Domain reference: {name}"),
74                    doc_key: None,
75                    kind: Some(SymbolKind::Variable),
76                    ty: None,
77                    decl_span: None, // could link to the declaration span if we wanted
78                },
79            );
80            Ok(Some(dom))
81        }
82        "tuple_domain" | "annotation_tuple_domain" => parse_tuple_domain(ctx, domain),
83        "matrix_domain" | "annotation_matrix_domain" => parse_matrix_domain(ctx, domain),
84        "record_domain" | "annotation_record_domain" => parse_record_domain(ctx, domain),
85        "variant_domain" | "annotation_variant_domain" => parse_variant_domain(ctx, domain),
86        "set_domain" | "annotation_set_domain" => parse_set_domain(ctx, domain),
87        "mset_domain" | "annotation_mset_domain" => parse_mset_domain(ctx, domain),
88        "sequence_domain" | "annotation_sequence_domain" => parse_sequence_domain(ctx, domain),
89        "function_domain" | "annotation_function_domain" => parse_function_domain(ctx, domain),
90        "relation_domain" => parse_relation_domain(ctx, domain),
91        "partition_domain" => parse_partition_domain(ctx, domain),
92        "permutation_domain" => parse_permutation_domain(ctx, domain),
93        _ => {
94            ctx.record_error(RecoverableParseError::new(
95                format!("{} is not a supported domain type", domain.kind()),
96                Some(domain.range()),
97            ));
98            Ok(None)
99        }
100    }
101}
102
103fn parse_mset_domain(
104    ctx: &mut ParseContext,
105    mset_domain: Node,
106) -> Result<Option<DomainPtr>, FatalParseError> {
107    let mut representation = None;
108    let mut size = Range::Unbounded;
109    let mut occurrence = Range::Unbounded;
110    let mut min_size = None;
111    let mut max_size = None;
112    let mut min_occurrence = None;
113    let mut max_occurrence = None;
114    let mut value_domain = None;
115
116    for child in named_children(&mset_domain) {
117        match child.kind() {
118            "identifier" => {}
119            "mset_attributes" => {
120                for attribute in named_children(&child) {
121                    let Some(value_node) = attribute.child_by_field_name("value") else {
122                        return Ok(None);
123                    };
124                    let name = attribute
125                        .child_by_field_name("attribute")
126                        .map(|node| &ctx.source_code[node.start_byte()..node.end_byte()])
127                        .unwrap_or_default();
128                    if name == "representation" {
129                        representation = Some(
130                            ctx.source_code[value_node.start_byte()..value_node.end_byte()]
131                                .to_string(),
132                        );
133                        continue;
134                    }
135                    // Attribute values may be expressions over givens, e.g. `maxSize |nums|`,
136                    // which only become ground once the parameters are instantiated.
137                    let Some(value) = parse_int_val(ctx, value_node)? else {
138                        return Ok(None);
139                    };
140                    match name {
141                        "size" => size = Range::Single(value),
142                        "minSize" => min_size = Some(value),
143                        "maxSize" => max_size = Some(value),
144                        "minOccur" => min_occurrence = Some(value),
145                        "maxOccur" => max_occurrence = Some(value),
146                        _ => return Ok(None),
147                    }
148                }
149            }
150            "domain" | "annotation_domain" => value_domain = parse_domain(ctx, child)?,
151            _ => {}
152        }
153    }
154
155    if !matches!(size, Range::Single(_)) {
156        size = match (min_size, max_size) {
157            (Some(min), Some(max)) => Range::Bounded(min, max),
158            (Some(min), None) => Range::UnboundedR(min),
159            (None, Some(max)) => Range::UnboundedL(max),
160            (None, None) => Range::Unbounded,
161        };
162    }
163    occurrence = match (min_occurrence, max_occurrence) {
164        (Some(min), Some(max)) => Range::Bounded(min, max),
165        (Some(min), None) => Range::UnboundedR(min),
166        (None, Some(max)) => Range::UnboundedL(max),
167        (None, None) => occurrence,
168    };
169
170    let Some(value_domain) = value_domain else {
171        ctx.record_error(RecoverableParseError::new(
172            "Multiset domain must have a value domain".to_string(),
173            Some(mset_domain.range()),
174        ));
175        return Ok(None);
176    };
177    let mut attrs = MSetAttr::new(size, occurrence);
178    if let Some(representation) = representation {
179        attrs = attrs.with_representation(representation);
180    }
181    Ok(Some(Domain::mset(attrs, value_domain)))
182}
183
184fn get_declaration_ptr_from_identifier(
185    ctx: &mut ParseContext,
186    identifier: Node,
187) -> Result<Option<DeclarationPtr>, FatalParseError> {
188    let name = Name::user(&ctx.source_code[identifier.start_byte()..identifier.end_byte()]);
189    let decl = ctx.symbols.as_ref().unwrap().read().lookup(&name);
190
191    if decl.is_none() {
192        ctx.record_error(crate::errors::RecoverableParseError::new(
193            format!("The identifier '{}' is not defined", name),
194            Some(identifier.range()),
195        ));
196        return Ok(None);
197    }
198    match decl {
199        Some(decl) => Ok(Some(decl)),
200        None => {
201            ctx.record_error(crate::errors::RecoverableParseError::new(
202                format!("The identifier '{}' is not defined", name),
203                Some(identifier.range()),
204            ));
205            Ok(None)
206        }
207    }
208}
209
210/// The representation named by a domain's `representation` attribute node, as in
211/// `int(representation order, 1..4)` or `matrix (representation packed) indexed by ...`.
212fn representation_field(ctx: &ParseContext, domain: Node) -> Option<String> {
213    let value = domain
214        .child_by_field_name("representation")?
215        .child_by_field_name("value")?;
216    Some(ctx.source_code[value.start_byte()..value.end_byte()].to_string())
217}
218
219/// The value of an attribute node in an attribute list, e.g. `packed` in `representation packed`.
220fn attribute_value_text(ctx: &ParseContext, attribute: Node) -> Option<String> {
221    let value = attribute.child_by_field_name("value")?;
222    Some(ctx.source_code[value.start_byte()..value.end_byte()].to_string())
223}
224
225/// Apply the preference written on a domain, if any, to the domain just built from it.
226fn with_representation(domain: DomainPtr, representation: Option<String>) -> DomainPtr {
227    match representation {
228        Some(representation) => domain.with_representation_preference(&representation),
229        None => domain,
230    }
231}
232
233/// Parse an integer domain. Can be a single integer or a range, optionally preceded by a
234/// representation preference: `int(representation order, 1..4)`.
235fn parse_int_domain(
236    ctx: &mut ParseContext,
237    int_domain: Node,
238) -> Result<Option<DomainPtr>, FatalParseError> {
239    let representation = representation_field(ctx, int_domain);
240    let Some(domain) = parse_int_domain_ranges(ctx, int_domain, representation.is_some())? else {
241        return Ok(None);
242    };
243    Ok(Some(match representation {
244        Some(representation) => domain.with_representation_preference(&representation),
245        None => domain,
246    }))
247}
248
249fn parse_int_domain_ranges(
250    ctx: &mut ParseContext,
251    int_domain: Node,
252    has_representation: bool,
253) -> Result<Option<DomainPtr>, FatalParseError> {
254    let int_keyword_node = child!(int_domain, 0, "int");
255
256    let Some(range_list) = int_domain.child_by_field_name("ranges") else {
257        ctx.add_span_and_doc_hover(&int_keyword_node, "L_int", SymbolKind::Domain, None, None);
258        let int_text = ctx.source_code[int_domain.start_byte()..int_domain.end_byte()].trim();
259        return if int_text == "int" || has_representation {
260            Ok(Some(Domain::int_ground(vec![Range::Unbounded])))
261        } else {
262            Ok(Some(Domain::int_ground(vec![])))
263        };
264    };
265    // `int(<collection>)` takes its values from the collection rather than listing ranges, as in
266    // `int([i | i <- nums])`. One component that is collection-valued means this form.
267    let components: Vec<_> = named_children(&range_list).collect();
268    if let [only] = components.as_slice()
269        && matches!(only.kind(), "atom" | "arithmetic_expr")
270        && let Some(expr) = parse_collection_valued_expression(ctx, *only)?
271    {
272        ctx.add_span_and_doc_hover(&int_keyword_node, "int", SymbolKind::Domain, None, None);
273        return Ok(Some(Domain::int_from_values(expr)));
274    }
275
276    let mut ranges_unresolved: Vec<Range<IntVal>> = Vec::new();
277    let mut all_resolved = true;
278
279    for domain_component in named_children(&range_list) {
280        match domain_component.kind() {
281            "atom" | "arithmetic_expr" | "integer" | "identifier" => {
282                let Some(int_val) = parse_int_val(ctx, domain_component)? else {
283                    return Ok(None);
284                };
285
286                if !matches!(int_val, IntVal::Const(_)) {
287                    all_resolved = false;
288                }
289                ranges_unresolved.push(Range::Single(int_val));
290            }
291            "int_range" | "annotation_int_range" => {
292                let lower_bound = match domain_component.child_by_field_name("lower") {
293                    Some(node) => {
294                        match parse_int_val(ctx, node)? {
295                            Some(val) => Some(val),
296                            None => return Ok(None), // semantic error occurred
297                        }
298                    }
299                    None => None,
300                };
301                let upper_bound = match domain_component.child_by_field_name("upper") {
302                    Some(node) => {
303                        match parse_int_val(ctx, node)? {
304                            Some(val) => Some(val),
305                            None => return Ok(None), // semantic error occurred
306                        }
307                    }
308                    None => None,
309                };
310
311                match (lower_bound, upper_bound) {
312                    (Some(lower), Some(upper)) => {
313                        if !matches!((&lower, &upper), (IntVal::Const(_), IntVal::Const(_))) {
314                            all_resolved = false;
315                        }
316                        ranges_unresolved.push(Range::Bounded(lower, upper));
317                    }
318                    (Some(lower), None) => {
319                        if !matches!(lower, IntVal::Const(_)) {
320                            all_resolved = false;
321                        }
322                        ranges_unresolved.push(Range::UnboundedR(lower));
323                    }
324                    (None, Some(upper)) => {
325                        if !matches!(upper, IntVal::Const(_)) {
326                            all_resolved = false;
327                        }
328                        ranges_unresolved.push(Range::UnboundedL(upper));
329                    }
330                    _ => {
331                        ctx.record_error(RecoverableParseError::new(
332                            "Invalid int range: must have at least a lower or upper bound"
333                                .to_string(),
334                            Some(domain_component.range()),
335                        ));
336                        return Ok(None);
337                    }
338                }
339            }
340            _ => {
341                ctx.record_error(RecoverableParseError::new(
342                    format!(
343                        "Unexpected int domain component: {}",
344                        domain_component.kind()
345                    ),
346                    Some(domain_component.range()),
347                ));
348                return Ok(None);
349            }
350        }
351    }
352
353    // If all values are resolved constants, convert IntVals to raw integers
354    if all_resolved {
355        let ranges: Vec<Range<i32>> = ranges_unresolved
356            .into_iter()
357            .map(|r| r.resolve())
358            .collect::<Result<Vec<_>, _>>()
359            .map_err(|e| {
360                FatalParseError::internal_error(
361                    format!("could not resolve range: {e}"),
362                    Some(int_domain.range()),
363                )
364            })?
365            .into_iter()
366            .filter(|range| !matches!(range, Range::Bounded(lower, upper) if lower > upper))
367            .collect();
368
369        ctx.add_span_and_doc_hover(&int_keyword_node, "L_int", SymbolKind::Domain, None, None);
370        Ok(Some(Domain::int(ranges)))
371    } else {
372        // Otherwise, keep as an expression-based domain
373
374        // Adding int keyword to the source map with hover info from documentation
375        ctx.add_span_and_doc_hover(&int_keyword_node, "L_int", SymbolKind::Domain, None, None);
376        Ok(Some(Domain::int(ranges_unresolved)))
377    }
378}
379
380// Helper function to parse a node into an IntVal
381// Handles constants, references, and arbitrary expressions
382fn parse_int_val(ctx: &mut ParseContext, node: Node) -> Result<Option<IntVal>, FatalParseError> {
383    if matches!(node.kind(), "atom" | "integer") {
384        let text = &ctx.source_code[node.start_byte()..node.end_byte()];
385        if let Ok(integer) = text.parse::<i32>() {
386            return Ok(Some(IntVal::new_const(integer)));
387        }
388    }
389
390    if node.kind() == "identifier" {
391        let Some(decl) = get_declaration_ptr_from_identifier(ctx, node)? else {
392            // If identifier isn't defined, it's a semantic error.
393            return Ok(None);
394        };
395        return Ok(Some(IntVal::Reference(Reference::new(decl))));
396    }
397
398    let saved_context = ctx.typechecking_context;
399    let saved_inner_context = ctx.inner_typechecking_context;
400    ctx.typechecking_context = TypecheckingContext::Arithmetic;
401    ctx.inner_typechecking_context = TypecheckingContext::Arithmetic;
402    let expr = parse_expression(ctx, node)?;
403    ctx.typechecking_context = saved_context;
404    ctx.inner_typechecking_context = saved_inner_context;
405
406    let Some(expr) = expr else {
407        return Ok(None);
408    };
409
410    if let Expression::Atomic(_, Atom::Reference(reference)) = &expr
411        && let Ok(reference_val) = IntVal::new_ref(reference)
412    {
413        return Ok(Some(reference_val));
414    }
415
416    if let Expression::Atomic(_, Atom::Literal(Literal::Int(value))) = expr {
417        return Ok(Some(IntVal::new_const(value)));
418    }
419
420    Ok(IntVal::new_expr(Moo::new(expr)).ok())
421}
422
423fn parse_tuple_domain(
424    ctx: &mut ParseContext,
425    tuple_domain: Node,
426) -> Result<Option<DomainPtr>, FatalParseError> {
427    let mut domains: Vec<DomainPtr> = Vec::new();
428    let representation = representation_field(ctx, tuple_domain);
429    for domain in named_children(&tuple_domain) {
430        if domain.kind() == "representation_attribute" {
431            continue;
432        }
433        let Some(parsed_domain) = parse_domain(ctx, domain)? else {
434            return Ok(None);
435        };
436        domains.push(parsed_domain);
437    }
438
439    // extract the first child node which should be the 'tuple' keyword for hover info
440    if let Some(first) = tuple_domain.child(0)
441        && first.kind() == "tuple"
442    {
443        // Adding tuple to the source map with hover info from documentation
444        ctx.add_span_and_doc_hover(&first, "L_tuple", SymbolKind::Domain, None, None);
445    }
446
447    Ok(Some(with_representation(
448        Domain::tuple(domains),
449        representation,
450    )))
451}
452
453fn parse_matrix_domain(
454    ctx: &mut ParseContext,
455    matrix_domain: Node,
456) -> Result<Option<DomainPtr>, FatalParseError> {
457    let mut domains: Vec<DomainPtr> = Vec::new();
458    let representation = representation_field(ctx, matrix_domain);
459    let Some(index_domain_list) = field!(recover, ctx, matrix_domain, "index_domain_list") else {
460        return Ok(None);
461    };
462    for domain in named_children(&index_domain_list) {
463        let Some(parsed_domain) = parse_domain(ctx, domain)? else {
464            return Ok(None);
465        };
466        domains.push(parsed_domain);
467    }
468    let Some(value_domain_node) = field!(recover, ctx, matrix_domain, "value_domain") else {
469        return Ok(None);
470    };
471    let Some(value_domain) = parse_domain(ctx, value_domain_node)? else {
472        return Ok(None);
473    };
474
475    // Adding matrix to the source map with hover info from documentation
476    let matrix_keyword_node = child!(matrix_domain, 0, "matrix");
477    ctx.add_span_and_doc_hover(
478        &matrix_keyword_node,
479        "matrix",
480        SymbolKind::Domain,
481        None,
482        None,
483    );
484    Ok(Some(with_representation(
485        Domain::matrix(value_domain, domains),
486        representation,
487    )))
488}
489
490fn parse_record_domain(
491    ctx: &mut ParseContext,
492    record_domain: Node,
493) -> Result<Option<DomainPtr>, FatalParseError> {
494    let mut record_entries: Vec<Field<DomainPtr>> = Vec::new();
495    let representation = representation_field(ctx, record_domain);
496    for record_entry in named_children(&record_domain) {
497        if record_entry.kind() == "representation_attribute" {
498            continue;
499        }
500        let Some(name_node) = field!(recover, ctx, record_entry, "name") else {
501            return Ok(None);
502        };
503        let name = Name::user(&ctx.source_code[name_node.start_byte()..name_node.end_byte()]);
504        let Some(domain_node) = field!(recover, ctx, record_entry, "domain") else {
505            return Ok(None);
506        };
507        let Some(value) = parse_domain(ctx, domain_node)? else {
508            return Ok(None);
509        };
510        record_entries.push(Field { name, value });
511    }
512
513    // Adding record keyword to the source map with hover info from documentation
514    let record_keyword_node = child!(record_domain, 0, "record");
515    ctx.add_span_and_doc_hover(
516        &record_keyword_node,
517        "L_record",
518        SymbolKind::Domain,
519        None,
520        None,
521    );
522    Ok(Some(with_representation(
523        Domain::record(record_entries),
524        representation,
525    )))
526}
527
528fn parse_variant_domain(
529    ctx: &mut ParseContext,
530    variant_domain: Node,
531) -> Result<Option<DomainPtr>, FatalParseError> {
532    let mut entries = Vec::new();
533    let representation = representation_field(ctx, variant_domain);
534    for entry in named_children(&variant_domain) {
535        if entry.kind() == "representation_attribute" {
536            continue;
537        }
538        let Some(name_node) = field!(recover, ctx, entry, "name") else {
539            return Ok(None);
540        };
541        let name = Name::user(&ctx.source_code[name_node.start_byte()..name_node.end_byte()]);
542        let Some(domain_node) = field!(recover, ctx, entry, "domain") else {
543            return Ok(None);
544        };
545        let Some(value) = parse_domain(ctx, domain_node)? else {
546            return Ok(None);
547        };
548        entries.push(Field { name, value });
549    }
550
551    let keyword = child!(variant_domain, 0, "variant");
552    ctx.add_span_and_doc_hover(&keyword, "variant", SymbolKind::Domain, None, None);
553    Ok(Some(with_representation(
554        Domain::variant(entries),
555        representation,
556    )))
557}
558
559fn parse_sequence_domain(
560    ctx: &mut ParseContext,
561    sequence_domain: Node,
562) -> Result<Option<DomainPtr>, FatalParseError> {
563    let mut size = Range::Unbounded;
564    let mut min_size = None;
565    let mut max_size = None;
566    let mut jectivity = JectivityAttr::None;
567    let mut representation = None;
568    let mut value_domain: Option<DomainPtr> = None;
569
570    for child in named_children(&sequence_domain) {
571        match child.kind() {
572            "sequence_attributes" => {
573                for attribute in named_children(&child) {
574                    let name = attribute
575                        .child_by_field_name("attribute")
576                        .map(|node| &ctx.source_code[node.start_byte()..node.end_byte()])
577                        .unwrap_or_default();
578                    match name {
579                        "size" | "minSize" | "maxSize" => {
580                            let Some(value_node) = attribute.child_by_field_name("value") else {
581                                return Ok(None);
582                            };
583                            // Attribute values may be expressions over givens, e.g.
584                            // `maxSize n`, which only become ground once the parameters are
585                            // instantiated.
586                            let Some(value) = parse_int_val(ctx, value_node)? else {
587                                return Ok(None);
588                            };
589                            match name {
590                                "size" => size = Range::Single(value),
591                                "minSize" => min_size = Some(value),
592                                "maxSize" => max_size = Some(value),
593                                _ => unreachable!(),
594                            }
595                        }
596                        "injective" => jectivity = JectivityAttr::Injective,
597                        "surjective" => jectivity = JectivityAttr::Surjective,
598                        "bijective" => jectivity = JectivityAttr::Bijective,
599                        "representation" => representation = attribute_value_text(ctx, attribute),
600                        _ => return Ok(None),
601                    }
602                }
603            }
604            "domain" | "annotation_domain" => value_domain = parse_domain(ctx, child)?,
605            _ => {}
606        }
607    }
608
609    if !matches!(size, Range::Single(_)) {
610        size = match (min_size, max_size) {
611            (Some(min), Some(max)) => Range::Bounded(min, max),
612            (Some(min), None) => Range::UnboundedR(min),
613            (None, Some(max)) => Range::UnboundedL(max),
614            (None, None) => Range::Unbounded,
615        };
616    }
617
618    let Some(value_domain) = value_domain else {
619        ctx.record_error(RecoverableParseError::new(
620            "Sequence domain must have a value domain".to_string(),
621            Some(sequence_domain.range()),
622        ));
623        return Ok(None);
624    };
625
626    // Adding sequence keyword to the source map with hover info from documentation
627    let sequence_keyword_node = child!(sequence_domain, 0, "sequence");
628    ctx.add_span_and_doc_hover(
629        &sequence_keyword_node,
630        "sequence",
631        SymbolKind::Domain,
632        None,
633        None,
634    );
635
636    let attrs = SequenceAttr {
637        size,
638        jectivity,
639        representation,
640    };
641    Ok(Some(Domain::sequence(attrs, value_domain)))
642}
643
644// e.g. function int(1..3) --> int(1..3), function (total) bool --> int(13,17),
645// function (minSize 1) int(1..2) --> set (size 1) of int(1..2). Mirrors
646// parse_sequence_domain, plus the extra `partiality` field FuncAttr has.
647fn parse_function_domain(
648    ctx: &mut ParseContext,
649    function_domain: Node,
650) -> Result<Option<DomainPtr>, FatalParseError> {
651    let mut size = Range::Unbounded;
652    let mut min_size = None;
653    let mut max_size = None;
654    let mut partiality = PartialityAttr::Partial;
655    let mut jectivity = JectivityAttr::None;
656    let mut representation = None;
657
658    for child in named_children(&function_domain) {
659        if child.kind() == "function_attributes" {
660            for attribute in named_children(&child) {
661                let name = attribute
662                    .child_by_field_name("attribute")
663                    .map(|node| &ctx.source_code[node.start_byte()..node.end_byte()])
664                    .unwrap_or_default();
665                match name {
666                    "size" | "minSize" | "maxSize" => {
667                        let Some(value_node) = attribute.child_by_field_name("value") else {
668                            return Ok(None);
669                        };
670                        let Some(value) = parse_int(ctx, &value_node) else {
671                            return Ok(None);
672                        };
673                        match name {
674                            "size" => size = Range::Single(value),
675                            "minSize" => min_size = Some(value),
676                            "maxSize" => max_size = Some(value),
677                            _ => unreachable!(),
678                        }
679                    }
680                    "total" => partiality = PartialityAttr::Total,
681                    "injective" => jectivity = JectivityAttr::Injective,
682                    "surjective" => jectivity = JectivityAttr::Surjective,
683                    "bijective" => jectivity = JectivityAttr::Bijective,
684                    "representation" => representation = attribute_value_text(ctx, attribute),
685                    _ => return Ok(None),
686                }
687            }
688        }
689    }
690
691    if !matches!(size, Range::Single(_)) {
692        size = match (min_size, max_size) {
693            (Some(min), Some(max)) => Range::Bounded(min, max),
694            (Some(min), None) => Range::UnboundedR(min),
695            (None, Some(max)) => Range::UnboundedL(max),
696            (None, None) => Range::Unbounded,
697        };
698    }
699
700    let Some(domain_from_node) = function_domain.child_by_field_name("domain_from") else {
701        ctx.record_error(RecoverableParseError::new(
702            "Function domain must have a domain".to_string(),
703            Some(function_domain.range()),
704        ));
705        return Ok(None);
706    };
707    let Some(domain_from) = parse_domain(ctx, domain_from_node)? else {
708        return Ok(None);
709    };
710
711    let Some(domain_to_node) = function_domain.child_by_field_name("domain_to") else {
712        ctx.record_error(RecoverableParseError::new(
713            "Function domain must have a codomain".to_string(),
714            Some(function_domain.range()),
715        ));
716        return Ok(None);
717    };
718    let Some(domain_to) = parse_domain(ctx, domain_to_node)? else {
719        return Ok(None);
720    };
721
722    // Adding function keyword to the source map with hover info from documentation
723    let function_keyword_node = child!(function_domain, 0, "function");
724    ctx.add_span_and_doc_hover(
725        &function_keyword_node,
726        "function",
727        SymbolKind::Domain,
728        None,
729        None,
730    );
731
732    let attrs = FuncAttr {
733        size,
734        partiality,
735        jectivity,
736        representation,
737    };
738    Ok(Some(Domain::function(attrs, domain_from, domain_to)))
739}
740
741// e.g. relation of (int(1..3) * bool), relation (size 2, irreflexive) of (int(0..5) * int(0..5)).
742// Binary relation attributes only make sense for a binary relation whose two columns share a
743// domain; that constraint is enforced by the representations (`init`), not the parser.
744fn parse_relation_domain(
745    ctx: &mut ParseContext,
746    relation_domain: Node,
747) -> Result<Option<DomainPtr>, FatalParseError> {
748    let mut size = Range::Unbounded;
749    let mut min_size = None;
750    let mut max_size = None;
751    let mut binary = Vec::new();
752    let mut representation = None;
753
754    for child in named_children(&relation_domain) {
755        if child.kind() == "relation_attributes" {
756            for attribute in named_children(&child) {
757                let name = attribute
758                    .child_by_field_name("attribute")
759                    .map(|node| &ctx.source_code[node.start_byte()..node.end_byte()])
760                    .unwrap_or_default();
761                match name {
762                    "size" | "minSize" | "maxSize" => {
763                        let Some(value_node) = attribute.child_by_field_name("value") else {
764                            return Ok(None);
765                        };
766                        let Some(value) = parse_int(ctx, &value_node) else {
767                            return Ok(None);
768                        };
769                        match name {
770                            "size" => size = Range::Single(value),
771                            "minSize" => min_size = Some(value),
772                            "maxSize" => max_size = Some(value),
773                            _ => unreachable!(),
774                        }
775                    }
776                    "representation" => representation = attribute_value_text(ctx, attribute),
777                    _ => {
778                        let Some(bin_attr) = BinaryAttr::from_keyword(name) else {
779                            return Ok(None);
780                        };
781                        binary.push(bin_attr);
782                    }
783                }
784            }
785        }
786    }
787
788    if !matches!(size, Range::Single(_)) {
789        size = match (min_size, max_size) {
790            (Some(min), Some(max)) => Range::Bounded(min, max),
791            (Some(min), None) => Range::UnboundedR(min),
792            (None, Some(max)) => Range::UnboundedL(max),
793            (None, None) => Range::Unbounded,
794        };
795    }
796
797    let Some(column_list) = field!(recover, ctx, relation_domain, "relation_domain_list") else {
798        return Ok(None);
799    };
800    let mut columns: Vec<DomainPtr> = Vec::new();
801    for column in named_children(&column_list) {
802        let Some(parsed) = parse_domain(ctx, column)? else {
803            return Ok(None);
804        };
805        columns.push(parsed);
806    }
807
808    // Adding relation keyword to the source map with hover info from documentation
809    let relation_keyword_node = child!(relation_domain, 0, "relation");
810    ctx.add_span_and_doc_hover(
811        &relation_keyword_node,
812        "relation",
813        SymbolKind::Domain,
814        None,
815        None,
816    );
817
818    let attrs = RelAttr {
819        size,
820        binary,
821        representation,
822    };
823    Ok(Some(Domain::relation(attrs, columns)))
824}
825
826fn parse_partition_domain(
827    ctx: &mut ParseContext,
828    partition_domain: Node,
829) -> Result<Option<DomainPtr>, FatalParseError> {
830    let mut num_parts = Range::Unbounded;
831    let mut min_num_parts = None;
832    let mut max_num_parts = None;
833    let mut part_len = Range::Unbounded;
834    let mut min_part_len = None;
835    let mut max_part_len = None;
836    let mut is_regular = false;
837    let mut representation = None;
838
839    for child in named_children(&partition_domain) {
840        if child.kind() == "partition_attributes" {
841            for attribute in named_children(&child) {
842                let name = attribute
843                    .child_by_field_name("attribute")
844                    .map(|node| &ctx.source_code[node.start_byte()..node.end_byte()])
845                    .unwrap_or_default();
846                match name {
847                    "numParts" | "minNumParts" | "maxNumParts" | "partSize" | "minPartSize"
848                    | "maxPartSize" => {
849                        let Some(value_node) = attribute.child_by_field_name("value") else {
850                            return Ok(None);
851                        };
852                        let Some(value) = parse_int(ctx, &value_node) else {
853                            return Ok(None);
854                        };
855                        match name {
856                            "numParts" => num_parts = Range::Single(value),
857                            "minNumParts" => min_num_parts = Some(value),
858                            "maxNumParts" => max_num_parts = Some(value),
859                            "partSize" => part_len = Range::Single(value),
860                            "minPartSize" => min_part_len = Some(value),
861                            "maxPartSize" => max_part_len = Some(value),
862                            _ => unreachable!(),
863                        }
864                    }
865                    "regular" => is_regular = true,
866                    "representation" => representation = attribute_value_text(ctx, attribute),
867                    _ => return Ok(None),
868                }
869            }
870        }
871    }
872
873    if !matches!(num_parts, Range::Single(_)) {
874        num_parts = match (min_num_parts, max_num_parts) {
875            (Some(min), Some(max)) => Range::Bounded(min, max),
876            (Some(min), None) => Range::UnboundedR(min),
877            (None, Some(max)) => Range::UnboundedL(max),
878            (None, None) => Range::Unbounded,
879        };
880    }
881    if !matches!(part_len, Range::Single(_)) {
882        part_len = match (min_part_len, max_part_len) {
883            (Some(min), Some(max)) => Range::Bounded(min, max),
884            (Some(min), None) => Range::UnboundedR(min),
885            (None, Some(max)) => Range::UnboundedL(max),
886            (None, None) => Range::Unbounded,
887        };
888    }
889
890    let Some(value_domain) = field!(recover, ctx, partition_domain, "value_domain") else {
891        return Ok(None);
892    };
893    let Some(inner) = parse_domain(ctx, value_domain)? else {
894        return Ok(None);
895    };
896
897    let partition_keyword_node = child!(partition_domain, 0, "partition");
898    ctx.add_span_and_doc_hover(
899        &partition_keyword_node,
900        "partition",
901        SymbolKind::Domain,
902        None,
903        None,
904    );
905
906    let attrs = PartitionAttr {
907        num_parts,
908        part_len,
909        is_regular,
910        representation,
911    };
912    Ok(Some(Domain::partition(attrs, inner)))
913}
914
915fn parse_permutation_domain(
916    ctx: &mut ParseContext,
917    permutation_domain: Node,
918) -> Result<Option<DomainPtr>, FatalParseError> {
919    let mut num_moved = Range::Unbounded;
920    let mut min_num_moved = None;
921    let mut max_num_moved = None;
922    let mut representation = None;
923
924    for child in named_children(&permutation_domain) {
925        if child.kind() == "permutation_attributes" {
926            for attribute in named_children(&child) {
927                let name = attribute
928                    .child_by_field_name("attribute")
929                    .map(|node| &ctx.source_code[node.start_byte()..node.end_byte()])
930                    .unwrap_or_default();
931                if name == "representation" {
932                    representation = attribute_value_text(ctx, attribute);
933                    continue;
934                }
935                let Some(value_node) = attribute.child_by_field_name("value") else {
936                    return Ok(None);
937                };
938                let Some(value) = parse_int(ctx, &value_node) else {
939                    return Ok(None);
940                };
941                match name {
942                    "numMoved" => num_moved = Range::Single(value),
943                    "minNumMoved" => min_num_moved = Some(value),
944                    "maxNumMoved" => max_num_moved = Some(value),
945                    _ => return Ok(None),
946                }
947            }
948        }
949    }
950
951    if !matches!(num_moved, Range::Single(_)) {
952        num_moved = match (min_num_moved, max_num_moved) {
953            (Some(min), Some(max)) => Range::Bounded(min, max),
954            (Some(min), None) => Range::UnboundedR(min),
955            (None, Some(max)) => Range::UnboundedL(max),
956            (None, None) => Range::Unbounded,
957        };
958    }
959
960    let Some(value_domain) = field!(recover, ctx, permutation_domain, "value_domain") else {
961        return Ok(None);
962    };
963    let Some(inner) = parse_domain(ctx, value_domain)? else {
964        return Ok(None);
965    };
966
967    let permutation_keyword_node = child!(permutation_domain, 0, "permutation");
968    ctx.add_span_and_doc_hover(
969        &permutation_keyword_node,
970        "permutation",
971        SymbolKind::Domain,
972        None,
973        None,
974    );
975
976    let attrs = PermutationAttr {
977        num_moved,
978        representation,
979    };
980    Ok(Some(Domain::permutation(attrs, inner)))
981}
982
983pub fn parse_set_domain(
984    ctx: &mut ParseContext,
985    set_domain: Node,
986) -> Result<Option<DomainPtr>, FatalParseError> {
987    let mut size = Range::Unbounded;
988    let mut min_size = None;
989    let mut max_size = None;
990    let mut representation = None;
991    let mut value_domain: Option<DomainPtr> = None;
992
993    for child in named_children(&set_domain) {
994        match child.kind() {
995            "set_attributes" => {
996                for attribute in named_children(&child) {
997                    let Some(value_node) = attribute.child_by_field_name("value") else {
998                        return Ok(None);
999                    };
1000                    let name = attribute
1001                        .child_by_field_name("attribute")
1002                        .map(|node| &ctx.source_code[node.start_byte()..node.end_byte()])
1003                        .unwrap_or_default();
1004                    if name == "representation" {
1005                        representation = Some(
1006                            ctx.source_code[value_node.start_byte()..value_node.end_byte()]
1007                                .to_string(),
1008                        );
1009                        continue;
1010                    }
1011                    // Attribute values may be expressions over givens, e.g. `maxSize |nums|`,
1012                    // which only become ground once the parameters are instantiated.
1013                    let Some(value) = parse_int_val(ctx, value_node)? else {
1014                        return Ok(None);
1015                    };
1016                    match name {
1017                        "size" => size = Range::Single(value),
1018                        "minSize" => min_size = Some(value),
1019                        "maxSize" => max_size = Some(value),
1020                        _ => return Ok(None),
1021                    }
1022                }
1023            }
1024            "domain" | "annotation_domain" => {
1025                let Some(parsed_domain) = parse_domain(ctx, child)? else {
1026                    return Ok(None);
1027                };
1028                value_domain = Some(parsed_domain);
1029            }
1030            _ => {
1031                ctx.record_error(RecoverableParseError::new(
1032                    format!("Unrecognized set domain child kind: {}", child.kind()),
1033                    Some(child.range()),
1034                ));
1035                return Ok(None);
1036            }
1037        }
1038    }
1039
1040    if let Some(domain) = value_domain {
1041        // Adding set to the source map with hover info from documentation
1042        let set_keyword_node = child!(set_domain, 0, "set");
1043        // No documentation available for set domain, using fallback description
1044        ctx.add_span_and_doc_hover(&set_keyword_node, "set", SymbolKind::Domain, None, None);
1045        if !matches!(size, Range::Single(_)) {
1046            size = match (min_size, max_size) {
1047                (Some(min), Some(max)) => Range::Bounded(min, max),
1048                (Some(min), None) => Range::UnboundedR(min),
1049                (None, Some(max)) => Range::UnboundedL(max),
1050                (None, None) => Range::Unbounded,
1051            };
1052        }
1053        let mut attr = SetAttr::new(size);
1054        if let Some(repr) = representation {
1055            attr = attr.with_representation(repr);
1056        }
1057        Ok(Some(Domain::set(attr, domain)))
1058    } else {
1059        ctx.record_error(RecoverableParseError::new(
1060            "Set domain must have a value domain".to_string(),
1061            Some(set_domain.range()),
1062        ));
1063        Ok(None)
1064    }
1065}
1066
1067/// Parses `node` when it denotes a collection of integers, for `int(<collection>)`.
1068///
1069/// Returns `None` without recording an error when it is something else, so the caller can carry on
1070/// reading the node as an ordinary range bound.
1071fn parse_collection_valued_expression(
1072    ctx: &mut ParseContext,
1073    node: Node,
1074) -> Result<Option<Expression>, FatalParseError> {
1075    let saved_context = ctx.typechecking_context;
1076    let saved_inner_context = ctx.inner_typechecking_context;
1077    ctx.typechecking_context = TypecheckingContext::Unknown;
1078    ctx.inner_typechecking_context = TypecheckingContext::Unknown;
1079    let parsed = parse_expression(ctx, node);
1080    ctx.typechecking_context = saved_context;
1081    ctx.inner_typechecking_context = saved_inner_context;
1082
1083    let Some(expr) = parsed? else {
1084        return Ok(None);
1085    };
1086    use conjure_cp_core::ast::Typeable;
1087
1088    // A comprehension's return type is its *element* type, so the variant has to be recognised
1089    // directly rather than asked for its type.
1090    if matches!(expr, Expression::Comprehension(_, _)) {
1091        return Ok(Some(expr));
1092    }
1093
1094    Ok(matches!(
1095        expr.return_type(),
1096        conjure_cp_core::ast::ReturnType::Set(_)
1097            | conjure_cp_core::ast::ReturnType::MSet(_)
1098            | conjure_cp_core::ast::ReturnType::Matrix(_)
1099            | conjure_cp_core::ast::ReturnType::Sequence(_)
1100    )
1101    .then_some(expr))
1102}
1103
1104fn parse_enum_domain(
1105    ctx: &mut ParseContext,
1106    node: Node,
1107) -> Result<Option<DomainPtr>, FatalParseError> {
1108    let Some(name) = field!(recover, ctx, node, "name") else {
1109        return Ok(None);
1110    };
1111    let Some(declaration) = get_declaration_ptr_from_identifier(ctx, name)? else {
1112        return Ok(None);
1113    };
1114    let Some(ty) = declaration.as_enumerated_type().map(|ty| ty.clone()) else {
1115        ctx.record_error(RecoverableParseError::new(
1116            "Expected an enumerated type".to_owned(),
1117            Some(name.range()),
1118        ));
1119        return Ok(None);
1120    };
1121    let Some(ranges_node) = field!(recover, ctx, node, "ranges") else {
1122        return Ok(None);
1123    };
1124    let mut ranges = Vec::new();
1125    for range in named_children(&ranges_node) {
1126        let mut parse_bound = |bound| -> Result<Option<EnumVariantVal>, FatalParseError> {
1127            let saved = ctx.typechecking_context;
1128            ctx.typechecking_context = TypecheckingContext::Unknown;
1129            let result = parse_expression(ctx, bound);
1130            ctx.typechecking_context = saved;
1131            let Some(expression) = result? else {
1132                return Ok(None);
1133            };
1134            if let Expression::Atomic(_, Atom::Reference(reference)) = expression
1135                && reference.return_type()
1136                    == conjure_cp_core::ast::ReturnType::EnumeratedType(ty.clone())
1137            {
1138                return Ok(Some(EnumVariantVal(reference)));
1139            }
1140            ctx.record_error(RecoverableParseError::new(
1141                "Expected a member of this enumerated type".to_owned(),
1142                Some(bound.range()),
1143            ));
1144            Ok(None)
1145        };
1146        if range.kind() == "int_range" {
1147            let lower = match range.child_by_field_name("lower") {
1148                Some(bound) => {
1149                    let Some(value) = parse_bound(bound)? else {
1150                        return Ok(None);
1151                    };
1152                    Some(value)
1153                }
1154                None => None,
1155            };
1156            let upper = match range.child_by_field_name("upper") {
1157                Some(bound) => {
1158                    let Some(value) = parse_bound(bound)? else {
1159                        return Ok(None);
1160                    };
1161                    Some(value)
1162                }
1163                None => None,
1164            };
1165            ranges.push(match (lower, upper) {
1166                (Some(a), Some(b)) => Range::Bounded(a, b),
1167                (Some(a), None) => Range::UnboundedR(a),
1168                (None, Some(b)) => Range::UnboundedL(b),
1169                (None, None) => Range::Unbounded,
1170            });
1171        } else {
1172            let Some(value) = parse_bound(range)? else {
1173                return Ok(None);
1174            };
1175            ranges.push(Range::Single(value));
1176        }
1177    }
1178    Ok(Some(Domain::enumerated(ty, ranges)))
1179}