1
use super::atom::parse_int;
2
use super::util::named_children;
3
use crate::diagnostics::diagnostics_api::SymbolKind;
4
use crate::diagnostics::source_map::{HoverInfo, span_with_hover};
5
use crate::errors::FatalParseError;
6
use crate::expression::parse_expression;
7
use crate::parser::ParseContext;
8
use crate::util::TypecheckingContext;
9
use crate::{RecoverableParseError, child};
10
use conjure_cp_core::ast::Typeable;
11
use 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
};
16
use tree_sitter::Node;
17

            
18
use crate::field;
19

            
20
/// Parse an Essence variable domain into its Conjure AST representation.
21
6733891
pub fn parse_domain(
22
6733891
    ctx: &mut ParseContext,
23
6733891
    domain: Node,
24
6733891
) -> Result<Option<DomainPtr>, FatalParseError> {
25
6733891
    match domain.kind() {
26
        "domain" | "annotation_domain" => {
27
3313617
            let inner = match domain.child(0) {
28
3313617
                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
3313617
            parse_domain(ctx, inner)
38
        }
39
4
        "enum_domain" => parse_enum_domain(ctx, domain),
40
        "bool_domain" => {
41
258963
            ctx.add_span_and_doc_hover(&domain, "L_bool", SymbolKind::Domain, None, None);
42
258963
            Ok(Some(Domain::bool()))
43
        }
44
2216893
        "int_domain" | "annotation_int_domain" => parse_int_domain(ctx, domain),
45
        "identifier" => {
46
159500
            let Some(decl) = get_declaration_ptr_from_identifier(ctx, domain)? else {
47
96
                return Ok(None);
48
            };
49
159404
            let is_unnamed = decl.as_unnamed_type().is_some();
50
159404
            let type_domain = if is_unnamed {
51
28
                decl.domain()
52
            } else {
53
159376
                decl.as_enumerated_type().map(|ty| ty.to_domain())
54
            };
55
159404
            let Some(dom) = type_domain.or_else(|| Domain::reference(decl)) else {
56
24
                ctx.record_error(crate::errors::RecoverableParseError::new(
57
24
                    format!(
58
24
                        "The identifier '{}' is not a valid domain",
59
24
                        &ctx.source_code[domain.start_byte()..domain.end_byte()]
60
24
                    ),
61
24
                    Some(domain.range()),
62
24
                ));
63
24
                return Ok(None);
64
            };
65
159380
            let name = &ctx.source_code[domain.start_byte()..domain.end_byte()];
66

            
67
            // Not form docs, because we need context specific hover info
68
159380
            span_with_hover(
69
159380
                &domain,
70
159380
                ctx.source_code,
71
159380
                ctx.source_map,
72
159380
                HoverInfo {
73
159380
                    description: format!("Domain reference: {name}"),
74
159380
                    doc_key: None,
75
159380
                    kind: Some(SymbolKind::Variable),
76
159380
                    ty: None,
77
159380
                    decl_span: None, // could link to the declaration span if we wanted
78
159380
                },
79
159380
            );
80
159380
            Ok(Some(dom))
81
        }
82
71450
        "tuple_domain" | "annotation_tuple_domain" => parse_tuple_domain(ctx, domain),
83
257930
        "matrix_domain" | "annotation_matrix_domain" => parse_matrix_domain(ctx, domain),
84
9169
        "record_domain" | "annotation_record_domain" => parse_record_domain(ctx, domain),
85
19272
        "variant_domain" | "annotation_variant_domain" => parse_variant_domain(ctx, domain),
86
229566
        "set_domain" | "annotation_set_domain" => parse_set_domain(ctx, domain),
87
102241
        "mset_domain" | "annotation_mset_domain" => parse_mset_domain(ctx, domain),
88
42820
        "sequence_domain" | "annotation_sequence_domain" => parse_sequence_domain(ctx, domain),
89
39456
        "function_domain" | "annotation_function_domain" => parse_function_domain(ctx, domain),
90
2592
        "relation_domain" => parse_relation_domain(ctx, domain),
91
480
        "partition_domain" => parse_partition_domain(ctx, domain),
92
9914
        "permutation_domain" => parse_permutation_domain(ctx, domain),
93
        _ => {
94
24
            ctx.record_error(RecoverableParseError::new(
95
24
                format!("{} is not a supported domain type", domain.kind()),
96
24
                Some(domain.range()),
97
24
            ));
98
24
            Ok(None)
99
        }
100
    }
101
6733891
}
102

            
103
102241
fn parse_mset_domain(
104
102241
    ctx: &mut ParseContext,
105
102241
    mset_domain: Node,
106
102241
) -> Result<Option<DomainPtr>, FatalParseError> {
107
102241
    let mut representation = None;
108
102241
    let mut size = Range::Unbounded;
109
102241
    let mut occurrence = Range::Unbounded;
110
102241
    let mut min_size = None;
111
102241
    let mut max_size = None;
112
102241
    let mut min_occurrence = None;
113
102241
    let mut max_occurrence = None;
114
102241
    let mut value_domain = None;
115

            
116
102241
    for child in named_children(&mset_domain) {
117
204434
        match child.kind() {
118
            "identifier" => {}
119
            "mset_attributes" => {
120
102193
                for attribute in named_children(&child) {
121
187418
                    let Some(value_node) = attribute.child_by_field_name("value") else {
122
                        return Ok(None);
123
                    };
124
187418
                    let name = attribute
125
187418
                        .child_by_field_name("attribute")
126
187418
                        .map(|node| &ctx.source_code[node.start_byte()..node.end_byte()])
127
                        .unwrap_or_default();
128
187418
                    if name == "representation" {
129
49
                        representation = Some(
130
49
                            ctx.source_code[value_node.start_byte()..value_node.end_byte()]
131
49
                                .to_string(),
132
49
                        );
133
49
                        continue;
134
187369
                    }
135
                    // Attribute values may be expressions over givens, e.g. `maxSize |nums|`,
136
                    // which only become ground once the parameters are instantiated.
137
187369
                    let Some(value) = parse_int_val(ctx, value_node)? else {
138
                        return Ok(None);
139
                    };
140
187369
                    match name {
141
16992
                        "size" => size = Range::Single(value),
142
8544
                        "minSize" => min_size = Some(value),
143
76561
                        "maxSize" => max_size = Some(value),
144
144
                        "minOccur" => min_occurrence = Some(value),
145
85128
                        "maxOccur" => max_occurrence = Some(value),
146
                        _ => return Ok(None),
147
                    }
148
                }
149
            }
150
102241
            "domain" | "annotation_domain" => value_domain = parse_domain(ctx, child)?,
151
            _ => {}
152
        }
153
    }
154

            
155
102241
    if !matches!(size, Range::Single(_)) {
156
85249
        size = match (min_size, max_size) {
157
8544
            (Some(min), Some(max)) => Range::Bounded(min, max),
158
            (Some(min), None) => Range::UnboundedR(min),
159
68017
            (None, Some(max)) => Range::UnboundedL(max),
160
8688
            (None, None) => Range::Unbounded,
161
        };
162
16992
    }
163
102241
    occurrence = match (min_occurrence, max_occurrence) {
164
120
        (Some(min), Some(max)) => Range::Bounded(min, max),
165
24
        (Some(min), None) => Range::UnboundedR(min),
166
85008
        (None, Some(max)) => Range::UnboundedL(max),
167
17089
        (None, None) => occurrence,
168
    };
169

            
170
102241
    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
102241
    let mut attrs = MSetAttr::new(size, occurrence);
178
102241
    if let Some(representation) = representation {
179
49
        attrs = attrs.with_representation(representation);
180
102192
    }
181
102241
    Ok(Some(Domain::mset(attrs, value_domain)))
182
102241
}
183

            
184
159504
fn get_declaration_ptr_from_identifier(
185
159504
    ctx: &mut ParseContext,
186
159504
    identifier: Node,
187
159504
) -> Result<Option<DeclarationPtr>, FatalParseError> {
188
159504
    let name = Name::user(&ctx.source_code[identifier.start_byte()..identifier.end_byte()]);
189
159504
    let decl = ctx.symbols.as_ref().unwrap().read().lookup(&name);
190

            
191
159504
    if decl.is_none() {
192
96
        ctx.record_error(crate::errors::RecoverableParseError::new(
193
96
            format!("The identifier '{}' is not defined", name),
194
96
            Some(identifier.range()),
195
96
        ));
196
96
        return Ok(None);
197
159408
    }
198
159408
    match decl {
199
159408
        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
159504
}
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 ...`.
212
2574714
fn representation_field(ctx: &ParseContext, domain: Node) -> Option<String> {
213
2574714
    let value = domain
214
2574714
        .child_by_field_name("representation")?
215
564
        .child_by_field_name("value")?;
216
564
    Some(ctx.source_code[value.start_byte()..value.end_byte()].to_string())
217
2574714
}
218

            
219
/// The value of an attribute node in an attribute list, e.g. `packed` in `representation packed`.
220
240
fn attribute_value_text(ctx: &ParseContext, attribute: Node) -> Option<String> {
221
240
    let value = attribute.child_by_field_name("value")?;
222
240
    Some(ctx.source_code[value.start_byte()..value.end_byte()].to_string())
223
240
}
224

            
225
/// Apply the preference written on a domain, if any, to the domain just built from it.
226
357797
fn with_representation(domain: DomainPtr, representation: Option<String>) -> DomainPtr {
227
357797
    match representation {
228
336
        Some(representation) => domain.with_representation_preference(&representation),
229
357461
        None => domain,
230
    }
231
357797
}
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)`.
235
2216893
fn parse_int_domain(
236
2216893
    ctx: &mut ParseContext,
237
2216893
    int_domain: Node,
238
2216893
) -> Result<Option<DomainPtr>, FatalParseError> {
239
2216893
    let representation = representation_field(ctx, int_domain);
240
2216893
    let Some(domain) = parse_int_domain_ranges(ctx, int_domain, representation.is_some())? else {
241
48
        return Ok(None);
242
    };
243
2216845
    Ok(Some(match representation {
244
228
        Some(representation) => domain.with_representation_preference(&representation),
245
2216617
        None => domain,
246
    }))
247
2216893
}
248

            
249
2216893
fn parse_int_domain_ranges(
250
2216893
    ctx: &mut ParseContext,
251
2216893
    int_domain: Node,
252
2216893
    has_representation: bool,
253
2216893
) -> Result<Option<DomainPtr>, FatalParseError> {
254
2216893
    let int_keyword_node = child!(int_domain, 0, "int");
255

            
256
2216893
    let Some(range_list) = int_domain.child_by_field_name("ranges") else {
257
98140
        ctx.add_span_and_doc_hover(&int_keyword_node, "L_int", SymbolKind::Domain, None, None);
258
98140
        let int_text = ctx.source_code[int_domain.start_byte()..int_domain.end_byte()].trim();
259
98140
        return if int_text == "int" || has_representation {
260
98087
            Ok(Some(Domain::int_ground(vec![Range::Unbounded])))
261
        } else {
262
53
            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
2118753
    let components: Vec<_> = named_children(&range_list).collect();
268
2118753
    if let [only] = components.as_slice()
269
1897578
        && matches!(only.kind(), "atom" | "arithmetic_expr")
270
77348
        && 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
2118753
    }
275

            
276
2118753
    let mut ranges_unresolved: Vec<Range<IntVal>> = Vec::new();
277
2118753
    let mut all_resolved = true;
278

            
279
2118753
    for domain_component in named_children(&range_list) {
280
2463795
        match domain_component.kind() {
281
            "atom" | "arithmetic_expr" | "integer" | "identifier" => {
282
588989
                let Some(int_val) = parse_int_val(ctx, domain_component)? else {
283
                    return Ok(None);
284
                };
285

            
286
588989
                if !matches!(int_val, IntVal::Const(_)) {
287
3452
                    all_resolved = false;
288
585537
                }
289
588989
                ranges_unresolved.push(Range::Single(int_val));
290
            }
291
            "int_range" | "annotation_int_range" => {
292
1874806
                let lower_bound = match domain_component.child_by_field_name("lower") {
293
                    Some(node) => {
294
1874778
                        match parse_int_val(ctx, node)? {
295
1874778
                            Some(val) => Some(val),
296
                            None => return Ok(None), // semantic error occurred
297
                        }
298
                    }
299
28
                    None => None,
300
                };
301
1874806
                let upper_bound = match domain_component.child_by_field_name("upper") {
302
                    Some(node) => {
303
1858019
                        match parse_int_val(ctx, node)? {
304
1857971
                            Some(val) => Some(val),
305
48
                            None => return Ok(None), // semantic error occurred
306
                        }
307
                    }
308
16787
                    None => None,
309
                };
310

            
311
1874758
                match (lower_bound, upper_bound) {
312
                    (Some(lower), Some(upper)) => {
313
1857943
                        if !matches!((&lower, &upper), (IntVal::Const(_), IntVal::Const(_))) {
314
187386
                            all_resolved = false;
315
1670557
                        }
316
1857943
                        ranges_unresolved.push(Range::Bounded(lower, upper));
317
                    }
318
                    (Some(lower), None) => {
319
16787
                        if !matches!(lower, IntVal::Const(_)) {
320
                            all_resolved = false;
321
16787
                        }
322
16787
                        ranges_unresolved.push(Range::UnboundedR(lower));
323
                    }
324
                    (None, Some(upper)) => {
325
28
                        if !matches!(upper, IntVal::Const(_)) {
326
                            all_resolved = false;
327
28
                        }
328
28
                        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
2118705
    if all_resolved {
355
1931467
        let ranges: Vec<Range<i32>> = ranges_unresolved
356
1931467
            .into_iter()
357
2272861
            .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
2272861
            .filter(|range| !matches!(range, Range::Bounded(lower, upper) if lower > upper))
367
            .collect();
368

            
369
1931467
        ctx.add_span_and_doc_hover(&int_keyword_node, "L_int", SymbolKind::Domain, None, None);
370
1931467
        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
187238
        ctx.add_span_and_doc_hover(&int_keyword_node, "L_int", SymbolKind::Domain, None, None);
376
187238
        Ok(Some(Domain::int(ranges_unresolved)))
377
    }
378
2216893
}
379

            
380
// Helper function to parse a node into an IntVal
381
// Handles constants, references, and arbitrary expressions
382
4691323
fn parse_int_val(ctx: &mut ParseContext, node: Node) -> Result<Option<IntVal>, FatalParseError> {
383
4691323
    if matches!(node.kind(), "atom" | "integer") {
384
4356937
        let text = &ctx.source_code[node.start_byte()..node.end_byte()];
385
4356937
        if let Ok(integer) = text.parse::<i32>() {
386
4195572
            return Ok(Some(IntVal::new_const(integer)));
387
161365
        }
388
334386
    }
389

            
390
495751
    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
495751
    }
397

            
398
495751
    let saved_context = ctx.typechecking_context;
399
495751
    let saved_inner_context = ctx.inner_typechecking_context;
400
495751
    ctx.typechecking_context = TypecheckingContext::Arithmetic;
401
495751
    ctx.inner_typechecking_context = TypecheckingContext::Arithmetic;
402
495751
    let expr = parse_expression(ctx, node)?;
403
495751
    ctx.typechecking_context = saved_context;
404
495751
    ctx.inner_typechecking_context = saved_inner_context;
405

            
406
495751
    let Some(expr) = expr else {
407
72
        return Ok(None);
408
    };
409

            
410
495679
    if let Expression::Atomic(_, Atom::Reference(reference)) = &expr
411
161245
        && let Ok(reference_val) = IntVal::new_ref(reference)
412
    {
413
161245
        return Ok(Some(reference_val));
414
334434
    }
415

            
416
334434
    if let Expression::Atomic(_, Atom::Literal(Literal::Int(value))) = expr {
417
280077
        return Ok(Some(IntVal::new_const(value)));
418
54357
    }
419

            
420
54357
    Ok(IntVal::new_expr(Moo::new(expr)).ok())
421
4691323
}
422

            
423
71450
fn parse_tuple_domain(
424
71450
    ctx: &mut ParseContext,
425
71450
    tuple_domain: Node,
426
71450
) -> Result<Option<DomainPtr>, FatalParseError> {
427
71450
    let mut domains: Vec<DomainPtr> = Vec::new();
428
71450
    let representation = representation_field(ctx, tuple_domain);
429
71450
    for domain in named_children(&tuple_domain) {
430
142996
        if domain.kind() == "representation_attribute" {
431
96
            continue;
432
142900
        }
433
142900
        let Some(parsed_domain) = parse_domain(ctx, domain)? else {
434
24
            return Ok(None);
435
        };
436
142876
        domains.push(parsed_domain);
437
    }
438

            
439
    // extract the first child node which should be the 'tuple' keyword for hover info
440
71426
    if let Some(first) = tuple_domain.child(0)
441
71426
        && first.kind() == "tuple"
442
    {
443
        // Adding tuple to the source map with hover info from documentation
444
17400
        ctx.add_span_and_doc_hover(&first, "L_tuple", SymbolKind::Domain, None, None);
445
54026
    }
446

            
447
71426
    Ok(Some(with_representation(
448
71426
        Domain::tuple(domains),
449
71426
        representation,
450
71426
    )))
451
71450
}
452

            
453
257930
fn parse_matrix_domain(
454
257930
    ctx: &mut ParseContext,
455
257930
    matrix_domain: Node,
456
257930
) -> Result<Option<DomainPtr>, FatalParseError> {
457
257930
    let mut domains: Vec<DomainPtr> = Vec::new();
458
257930
    let representation = representation_field(ctx, matrix_domain);
459
257930
    let Some(index_domain_list) = field!(recover, ctx, matrix_domain, "index_domain_list") else {
460
        return Ok(None);
461
    };
462
257930
    for domain in named_children(&index_domain_list) {
463
305979
        let Some(parsed_domain) = parse_domain(ctx, domain)? else {
464
            return Ok(None);
465
        };
466
305979
        domains.push(parsed_domain);
467
    }
468
257930
    let Some(value_domain_node) = field!(recover, ctx, matrix_domain, "value_domain") else {
469
        return Ok(None);
470
    };
471
257930
    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
257930
    let matrix_keyword_node = child!(matrix_domain, 0, "matrix");
477
257930
    ctx.add_span_and_doc_hover(
478
257930
        &matrix_keyword_node,
479
257930
        "matrix",
480
257930
        SymbolKind::Domain,
481
257930
        None,
482
257930
        None,
483
257930
    );
484
257930
    Ok(Some(with_representation(
485
257930
        Domain::matrix(value_domain, domains),
486
257930
        representation,
487
257930
    )))
488
257930
}
489

            
490
9169
fn parse_record_domain(
491
9169
    ctx: &mut ParseContext,
492
9169
    record_domain: Node,
493
9169
) -> Result<Option<DomainPtr>, FatalParseError> {
494
9169
    let mut record_entries: Vec<Field<DomainPtr>> = Vec::new();
495
9169
    let representation = representation_field(ctx, record_domain);
496
9169
    for record_entry in named_children(&record_domain) {
497
18433
        if record_entry.kind() == "representation_attribute" {
498
96
            continue;
499
18337
        }
500
18337
        let Some(name_node) = field!(recover, ctx, record_entry, "name") else {
501
            return Ok(None);
502
        };
503
18337
        let name = Name::user(&ctx.source_code[name_node.start_byte()..name_node.end_byte()]);
504
18337
        let Some(domain_node) = field!(recover, ctx, record_entry, "domain") else {
505
            return Ok(None);
506
        };
507
18337
        let Some(value) = parse_domain(ctx, domain_node)? else {
508
            return Ok(None);
509
        };
510
18337
        record_entries.push(Field { name, value });
511
    }
512

            
513
    // Adding record keyword to the source map with hover info from documentation
514
9169
    let record_keyword_node = child!(record_domain, 0, "record");
515
9169
    ctx.add_span_and_doc_hover(
516
9169
        &record_keyword_node,
517
9169
        "L_record",
518
9169
        SymbolKind::Domain,
519
9169
        None,
520
9169
        None,
521
9169
    );
522
9169
    Ok(Some(with_representation(
523
9169
        Domain::record(record_entries),
524
9169
        representation,
525
9169
    )))
526
9169
}
527

            
528
19272
fn parse_variant_domain(
529
19272
    ctx: &mut ParseContext,
530
19272
    variant_domain: Node,
531
19272
) -> Result<Option<DomainPtr>, FatalParseError> {
532
19272
    let mut entries = Vec::new();
533
19272
    let representation = representation_field(ctx, variant_domain);
534
19272
    for entry in named_children(&variant_domain) {
535
38592
        if entry.kind() == "representation_attribute" {
536
48
            continue;
537
38544
        }
538
38544
        let Some(name_node) = field!(recover, ctx, entry, "name") else {
539
            return Ok(None);
540
        };
541
38544
        let name = Name::user(&ctx.source_code[name_node.start_byte()..name_node.end_byte()]);
542
38544
        let Some(domain_node) = field!(recover, ctx, entry, "domain") else {
543
            return Ok(None);
544
        };
545
38544
        let Some(value) = parse_domain(ctx, domain_node)? else {
546
            return Ok(None);
547
        };
548
38544
        entries.push(Field { name, value });
549
    }
550

            
551
19272
    let keyword = child!(variant_domain, 0, "variant");
552
19272
    ctx.add_span_and_doc_hover(&keyword, "variant", SymbolKind::Domain, None, None);
553
19272
    Ok(Some(with_representation(
554
19272
        Domain::variant(entries),
555
19272
        representation,
556
19272
    )))
557
19272
}
558

            
559
42820
fn parse_sequence_domain(
560
42820
    ctx: &mut ParseContext,
561
42820
    sequence_domain: Node,
562
42820
) -> Result<Option<DomainPtr>, FatalParseError> {
563
42820
    let mut size = Range::Unbounded;
564
42820
    let mut min_size = None;
565
42820
    let mut max_size = None;
566
42820
    let mut jectivity = JectivityAttr::None;
567
42820
    let mut representation = None;
568
42820
    let mut value_domain: Option<DomainPtr> = None;
569

            
570
42820
    for child in named_children(&sequence_domain) {
571
85640
        match child.kind() {
572
            "sequence_attributes" => {
573
42820
                for attribute in named_children(&child) {
574
50284
                    let name = attribute
575
50284
                        .child_by_field_name("attribute")
576
50284
                        .map(|node| &ctx.source_code[node.start_byte()..node.end_byte()])
577
                        .unwrap_or_default();
578
50284
                    match name {
579
                        "size" | "minSize" | "maxSize" => {
580
45436
                            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
45436
                            let Some(value) = parse_int_val(ctx, value_node)? else {
587
                                return Ok(None);
588
                            };
589
45436
                            match name {
590
26760
                                "size" => size = Range::Single(value),
591
2616
                                "minSize" => min_size = Some(value),
592
16060
                                "maxSize" => max_size = Some(value),
593
                                _ => unreachable!(),
594
                            }
595
                        }
596
4752
                        "injective" => jectivity = JectivityAttr::Injective,
597
24
                        "surjective" => jectivity = JectivityAttr::Surjective,
598
24
                        "bijective" => jectivity = JectivityAttr::Bijective,
599
48
                        "representation" => representation = attribute_value_text(ctx, attribute),
600
                        _ => return Ok(None),
601
                    }
602
                }
603
            }
604
42820
            "domain" | "annotation_domain" => value_domain = parse_domain(ctx, child)?,
605
            _ => {}
606
        }
607
    }
608

            
609
42820
    if !matches!(size, Range::Single(_)) {
610
16060
        size = match (min_size, max_size) {
611
2616
            (Some(min), Some(max)) => Range::Bounded(min, max),
612
            (Some(min), None) => Range::UnboundedR(min),
613
13444
            (None, Some(max)) => Range::UnboundedL(max),
614
            (None, None) => Range::Unbounded,
615
        };
616
26760
    }
617

            
618
42820
    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
42820
    let sequence_keyword_node = child!(sequence_domain, 0, "sequence");
628
42820
    ctx.add_span_and_doc_hover(
629
42820
        &sequence_keyword_node,
630
42820
        "sequence",
631
42820
        SymbolKind::Domain,
632
42820
        None,
633
42820
        None,
634
42820
    );
635

            
636
42820
    let attrs = SequenceAttr {
637
42820
        size,
638
42820
        jectivity,
639
42820
        representation,
640
42820
    };
641
42820
    Ok(Some(Domain::sequence(attrs, value_domain)))
642
42820
}
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.
647
39456
fn parse_function_domain(
648
39456
    ctx: &mut ParseContext,
649
39456
    function_domain: Node,
650
39456
) -> Result<Option<DomainPtr>, FatalParseError> {
651
39456
    let mut size = Range::Unbounded;
652
39456
    let mut min_size = None;
653
39456
    let mut max_size = None;
654
39456
    let mut partiality = PartialityAttr::Partial;
655
39456
    let mut jectivity = JectivityAttr::None;
656
39456
    let mut representation = None;
657

            
658
39456
    for child in named_children(&function_domain) {
659
117336
        if child.kind() == "function_attributes" {
660
38424
            for attribute in named_children(&child) {
661
42816
                let name = attribute
662
42816
                    .child_by_field_name("attribute")
663
42816
                    .map(|node| &ctx.source_code[node.start_byte()..node.end_byte()])
664
                    .unwrap_or_default();
665
42816
                match name {
666
                    "size" | "minSize" | "maxSize" => {
667
1296
                        let Some(value_node) = attribute.child_by_field_name("value") else {
668
                            return Ok(None);
669
                        };
670
1296
                        let Some(value) = parse_int(ctx, &value_node) else {
671
                            return Ok(None);
672
                        };
673
1296
                        match name {
674
96
                            "size" => size = Range::Single(value),
675
432
                            "minSize" => min_size = Some(value),
676
768
                            "maxSize" => max_size = Some(value),
677
                            _ => unreachable!(),
678
                        }
679
                    }
680
37584
                    "total" => partiality = PartialityAttr::Total,
681
1896
                    "injective" => jectivity = JectivityAttr::Injective,
682
672
                    "surjective" => jectivity = JectivityAttr::Surjective,
683
1320
                    "bijective" => jectivity = JectivityAttr::Bijective,
684
48
                    "representation" => representation = attribute_value_text(ctx, attribute),
685
                    _ => return Ok(None),
686
                }
687
            }
688
78912
        }
689
    }
690

            
691
39456
    if !matches!(size, Range::Single(_)) {
692
39360
        size = match (min_size, max_size) {
693
336
            (Some(min), Some(max)) => Range::Bounded(min, max),
694
96
            (Some(min), None) => Range::UnboundedR(min),
695
432
            (None, Some(max)) => Range::UnboundedL(max),
696
38496
            (None, None) => Range::Unbounded,
697
        };
698
96
    }
699

            
700
39456
    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
39456
    let Some(domain_from) = parse_domain(ctx, domain_from_node)? else {
708
        return Ok(None);
709
    };
710

            
711
39456
    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
39456
    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
39456
    let function_keyword_node = child!(function_domain, 0, "function");
724
39456
    ctx.add_span_and_doc_hover(
725
39456
        &function_keyword_node,
726
39456
        "function",
727
39456
        SymbolKind::Domain,
728
39456
        None,
729
39456
        None,
730
39456
    );
731

            
732
39456
    let attrs = FuncAttr {
733
39456
        size,
734
39456
        partiality,
735
39456
        jectivity,
736
39456
        representation,
737
39456
    };
738
39456
    Ok(Some(Domain::function(attrs, domain_from, domain_to)))
739
39456
}
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.
744
2592
fn parse_relation_domain(
745
2592
    ctx: &mut ParseContext,
746
2592
    relation_domain: Node,
747
2592
) -> Result<Option<DomainPtr>, FatalParseError> {
748
2592
    let mut size = Range::Unbounded;
749
2592
    let mut min_size = None;
750
2592
    let mut max_size = None;
751
2592
    let mut binary = Vec::new();
752
2592
    let mut representation = None;
753

            
754
2592
    for child in named_children(&relation_domain) {
755
3312
        if child.kind() == "relation_attributes" {
756
720
            for attribute in named_children(&child) {
757
1224
                let name = attribute
758
1224
                    .child_by_field_name("attribute")
759
1224
                    .map(|node| &ctx.source_code[node.start_byte()..node.end_byte()])
760
                    .unwrap_or_default();
761
1224
                match name {
762
                    "size" | "minSize" | "maxSize" => {
763
168
                        let Some(value_node) = attribute.child_by_field_name("value") else {
764
                            return Ok(None);
765
                        };
766
168
                        let Some(value) = parse_int(ctx, &value_node) else {
767
                            return Ok(None);
768
                        };
769
168
                        match name {
770
                            "size" => size = Range::Single(value),
771
                            "minSize" => min_size = Some(value),
772
168
                            "maxSize" => max_size = Some(value),
773
                            _ => unreachable!(),
774
                        }
775
                    }
776
48
                    "representation" => representation = attribute_value_text(ctx, attribute),
777
                    _ => {
778
1008
                        let Some(bin_attr) = BinaryAttr::from_keyword(name) else {
779
                            return Ok(None);
780
                        };
781
1008
                        binary.push(bin_attr);
782
                    }
783
                }
784
            }
785
2592
        }
786
    }
787

            
788
2592
    if !matches!(size, Range::Single(_)) {
789
2592
        size = match (min_size, max_size) {
790
            (Some(min), Some(max)) => Range::Bounded(min, max),
791
            (Some(min), None) => Range::UnboundedR(min),
792
168
            (None, Some(max)) => Range::UnboundedL(max),
793
2424
            (None, None) => Range::Unbounded,
794
        };
795
    }
796

            
797
2592
    let Some(column_list) = field!(recover, ctx, relation_domain, "relation_domain_list") else {
798
        return Ok(None);
799
    };
800
2592
    let mut columns: Vec<DomainPtr> = Vec::new();
801
2592
    for column in named_children(&column_list) {
802
5304
        let Some(parsed) = parse_domain(ctx, column)? else {
803
            return Ok(None);
804
        };
805
5304
        columns.push(parsed);
806
    }
807

            
808
    // Adding relation keyword to the source map with hover info from documentation
809
2592
    let relation_keyword_node = child!(relation_domain, 0, "relation");
810
2592
    ctx.add_span_and_doc_hover(
811
2592
        &relation_keyword_node,
812
2592
        "relation",
813
2592
        SymbolKind::Domain,
814
2592
        None,
815
2592
        None,
816
2592
    );
817

            
818
2592
    let attrs = RelAttr {
819
2592
        size,
820
2592
        binary,
821
2592
        representation,
822
2592
    };
823
2592
    Ok(Some(Domain::relation(attrs, columns)))
824
2592
}
825

            
826
480
fn parse_partition_domain(
827
480
    ctx: &mut ParseContext,
828
480
    partition_domain: Node,
829
480
) -> Result<Option<DomainPtr>, FatalParseError> {
830
480
    let mut num_parts = Range::Unbounded;
831
480
    let mut min_num_parts = None;
832
480
    let mut max_num_parts = None;
833
480
    let mut part_len = Range::Unbounded;
834
480
    let mut min_part_len = None;
835
480
    let mut max_part_len = None;
836
480
    let mut is_regular = false;
837
480
    let mut representation = None;
838

            
839
480
    for child in named_children(&partition_domain) {
840
672
        if child.kind() == "partition_attributes" {
841
192
            for attribute in named_children(&child) {
842
480
                let name = attribute
843
480
                    .child_by_field_name("attribute")
844
480
                    .map(|node| &ctx.source_code[node.start_byte()..node.end_byte()])
845
                    .unwrap_or_default();
846
480
                match name {
847
                    "numParts" | "minNumParts" | "maxNumParts" | "partSize" | "minPartSize"
848
                    | "maxPartSize" => {
849
384
                        let Some(value_node) = attribute.child_by_field_name("value") else {
850
                            return Ok(None);
851
                        };
852
384
                        let Some(value) = parse_int(ctx, &value_node) else {
853
                            return Ok(None);
854
                        };
855
384
                        match name {
856
48
                            "numParts" => num_parts = Range::Single(value),
857
48
                            "minNumParts" => min_num_parts = Some(value),
858
96
                            "maxNumParts" => max_num_parts = Some(value),
859
48
                            "partSize" => part_len = Range::Single(value),
860
96
                            "minPartSize" => min_part_len = Some(value),
861
48
                            "maxPartSize" => max_part_len = Some(value),
862
                            _ => unreachable!(),
863
                        }
864
                    }
865
48
                    "regular" => is_regular = true,
866
48
                    "representation" => representation = attribute_value_text(ctx, attribute),
867
                    _ => return Ok(None),
868
                }
869
            }
870
480
        }
871
    }
872

            
873
480
    if !matches!(num_parts, Range::Single(_)) {
874
432
        num_parts = match (min_num_parts, max_num_parts) {
875
48
            (Some(min), Some(max)) => Range::Bounded(min, max),
876
            (Some(min), None) => Range::UnboundedR(min),
877
48
            (None, Some(max)) => Range::UnboundedL(max),
878
336
            (None, None) => Range::Unbounded,
879
        };
880
48
    }
881
480
    if !matches!(part_len, Range::Single(_)) {
882
432
        part_len = match (min_part_len, max_part_len) {
883
48
            (Some(min), Some(max)) => Range::Bounded(min, max),
884
48
            (Some(min), None) => Range::UnboundedR(min),
885
            (None, Some(max)) => Range::UnboundedL(max),
886
336
            (None, None) => Range::Unbounded,
887
        };
888
48
    }
889

            
890
480
    let Some(value_domain) = field!(recover, ctx, partition_domain, "value_domain") else {
891
        return Ok(None);
892
    };
893
480
    let Some(inner) = parse_domain(ctx, value_domain)? else {
894
        return Ok(None);
895
    };
896

            
897
480
    let partition_keyword_node = child!(partition_domain, 0, "partition");
898
480
    ctx.add_span_and_doc_hover(
899
480
        &partition_keyword_node,
900
480
        "partition",
901
480
        SymbolKind::Domain,
902
480
        None,
903
480
        None,
904
480
    );
905

            
906
480
    let attrs = PartitionAttr {
907
480
        num_parts,
908
480
        part_len,
909
480
        is_regular,
910
480
        representation,
911
480
    };
912
480
    Ok(Some(Domain::partition(attrs, inner)))
913
480
}
914

            
915
9914
fn parse_permutation_domain(
916
9914
    ctx: &mut ParseContext,
917
9914
    permutation_domain: Node,
918
9914
) -> Result<Option<DomainPtr>, FatalParseError> {
919
9914
    let mut num_moved = Range::Unbounded;
920
9914
    let mut min_num_moved = None;
921
9914
    let mut max_num_moved = None;
922
9914
    let mut representation = None;
923

            
924
9914
    for child in named_children(&permutation_domain) {
925
10107
        if child.kind() == "permutation_attributes" {
926
193
            for attribute in named_children(&child) {
927
241
                let name = attribute
928
241
                    .child_by_field_name("attribute")
929
241
                    .map(|node| &ctx.source_code[node.start_byte()..node.end_byte()])
930
                    .unwrap_or_default();
931
241
                if name == "representation" {
932
48
                    representation = attribute_value_text(ctx, attribute);
933
48
                    continue;
934
193
                }
935
193
                let Some(value_node) = attribute.child_by_field_name("value") else {
936
                    return Ok(None);
937
                };
938
193
                let Some(value) = parse_int(ctx, &value_node) else {
939
                    return Ok(None);
940
                };
941
193
                match name {
942
97
                    "numMoved" => num_moved = Range::Single(value),
943
48
                    "minNumMoved" => min_num_moved = Some(value),
944
48
                    "maxNumMoved" => max_num_moved = Some(value),
945
                    _ => return Ok(None),
946
                }
947
            }
948
9914
        }
949
    }
950

            
951
9914
    if !matches!(num_moved, Range::Single(_)) {
952
9817
        num_moved = match (min_num_moved, max_num_moved) {
953
48
            (Some(min), Some(max)) => Range::Bounded(min, max),
954
            (Some(min), None) => Range::UnboundedR(min),
955
            (None, Some(max)) => Range::UnboundedL(max),
956
9769
            (None, None) => Range::Unbounded,
957
        };
958
97
    }
959

            
960
9914
    let Some(value_domain) = field!(recover, ctx, permutation_domain, "value_domain") else {
961
        return Ok(None);
962
    };
963
9914
    let Some(inner) = parse_domain(ctx, value_domain)? else {
964
        return Ok(None);
965
    };
966

            
967
9914
    let permutation_keyword_node = child!(permutation_domain, 0, "permutation");
968
9914
    ctx.add_span_and_doc_hover(
969
9914
        &permutation_keyword_node,
970
9914
        "permutation",
971
9914
        SymbolKind::Domain,
972
9914
        None,
973
9914
        None,
974
9914
    );
975

            
976
9914
    let attrs = PermutationAttr {
977
9914
        num_moved,
978
9914
        representation,
979
9914
    };
980
9914
    Ok(Some(Domain::permutation(attrs, inner)))
981
9914
}
982

            
983
229566
pub fn parse_set_domain(
984
229566
    ctx: &mut ParseContext,
985
229566
    set_domain: Node,
986
229566
) -> Result<Option<DomainPtr>, FatalParseError> {
987
229566
    let mut size = Range::Unbounded;
988
229566
    let mut min_size = None;
989
229566
    let mut max_size = None;
990
229566
    let mut representation = None;
991
229566
    let mut value_domain: Option<DomainPtr> = None;
992

            
993
229566
    for child in named_children(&set_domain) {
994
366212
        match child.kind() {
995
            "set_attributes" => {
996
136670
                for attribute in named_children(&child) {
997
137110
                    let Some(value_node) = attribute.child_by_field_name("value") else {
998
                        return Ok(None);
999
                    };
137110
                    let name = attribute
137110
                        .child_by_field_name("attribute")
137110
                        .map(|node| &ctx.source_code[node.start_byte()..node.end_byte()])
                        .unwrap_or_default();
137110
                    if name == "representation" {
378
                        representation = Some(
378
                            ctx.source_code[value_node.start_byte()..value_node.end_byte()]
378
                                .to_string(),
378
                        );
378
                        continue;
136732
                    }
                    // Attribute values may be expressions over givens, e.g. `maxSize |nums|`,
                    // which only become ground once the parameters are instantiated.
136732
                    let Some(value) = parse_int_val(ctx, value_node)? else {
24
                        return Ok(None);
                    };
136708
                    match name {
70492
                        "size" => size = Range::Single(value),
41448
                        "minSize" => min_size = Some(value),
24768
                        "maxSize" => max_size = Some(value),
                        _ => return Ok(None),
                    }
                }
            }
            "domain" | "annotation_domain" => {
229542
                let Some(parsed_domain) = parse_domain(ctx, child)? else {
                    return Ok(None);
                };
229542
                value_domain = Some(parsed_domain);
            }
            _ => {
                ctx.record_error(RecoverableParseError::new(
                    format!("Unrecognized set domain child kind: {}", child.kind()),
                    Some(child.range()),
                ));
                return Ok(None);
            }
        }
    }
229542
    if let Some(domain) = value_domain {
        // Adding set to the source map with hover info from documentation
229542
        let set_keyword_node = child!(set_domain, 0, "set");
        // No documentation available for set domain, using fallback description
229542
        ctx.add_span_and_doc_hover(&set_keyword_node, "set", SymbolKind::Domain, None, None);
229542
        if !matches!(size, Range::Single(_)) {
159050
            size = match (min_size, max_size) {
288
                (Some(min), Some(max)) => Range::Bounded(min, max),
41160
                (Some(min), None) => Range::UnboundedR(min),
24480
                (None, Some(max)) => Range::UnboundedL(max),
93122
                (None, None) => Range::Unbounded,
            };
70492
        }
229542
        let mut attr = SetAttr::new(size);
229542
        if let Some(repr) = representation {
378
            attr = attr.with_representation(repr);
229164
        }
229542
        Ok(Some(Domain::set(attr, domain)))
    } else {
        ctx.record_error(RecoverableParseError::new(
            "Set domain must have a value domain".to_string(),
            Some(set_domain.range()),
        ));
        Ok(None)
    }
229566
}
/// Parses `node` when it denotes a collection of integers, for `int(<collection>)`.
///
/// Returns `None` without recording an error when it is something else, so the caller can carry on
/// reading the node as an ordinary range bound.
77348
fn parse_collection_valued_expression(
77348
    ctx: &mut ParseContext,
77348
    node: Node,
77348
) -> Result<Option<Expression>, FatalParseError> {
77348
    let saved_context = ctx.typechecking_context;
77348
    let saved_inner_context = ctx.inner_typechecking_context;
77348
    ctx.typechecking_context = TypecheckingContext::Unknown;
77348
    ctx.inner_typechecking_context = TypecheckingContext::Unknown;
77348
    let parsed = parse_expression(ctx, node);
77348
    ctx.typechecking_context = saved_context;
77348
    ctx.inner_typechecking_context = saved_inner_context;
77348
    let Some(expr) = parsed? else {
        return Ok(None);
    };
    use conjure_cp_core::ast::Typeable;
    // A comprehension's return type is its *element* type, so the variant has to be recognised
    // directly rather than asked for its type.
77348
    if matches!(expr, Expression::Comprehension(_, _)) {
        return Ok(Some(expr));
77348
    }
77348
    Ok(matches!(
77348
        expr.return_type(),
        conjure_cp_core::ast::ReturnType::Set(_)
            | conjure_cp_core::ast::ReturnType::MSet(_)
            | conjure_cp_core::ast::ReturnType::Matrix(_)
            | conjure_cp_core::ast::ReturnType::Sequence(_)
    )
77348
    .then_some(expr))
77348
}
4
fn parse_enum_domain(
4
    ctx: &mut ParseContext,
4
    node: Node,
4
) -> Result<Option<DomainPtr>, FatalParseError> {
4
    let Some(name) = field!(recover, ctx, node, "name") else {
        return Ok(None);
    };
4
    let Some(declaration) = get_declaration_ptr_from_identifier(ctx, name)? else {
        return Ok(None);
    };
4
    let Some(ty) = declaration.as_enumerated_type().map(|ty| ty.clone()) else {
        ctx.record_error(RecoverableParseError::new(
            "Expected an enumerated type".to_owned(),
            Some(name.range()),
        ));
        return Ok(None);
    };
4
    let Some(ranges_node) = field!(recover, ctx, node, "ranges") else {
        return Ok(None);
    };
4
    let mut ranges = Vec::new();
4
    for range in named_children(&ranges_node) {
8
        let mut parse_bound = |bound| -> Result<Option<EnumVariantVal>, FatalParseError> {
8
            let saved = ctx.typechecking_context;
8
            ctx.typechecking_context = TypecheckingContext::Unknown;
8
            let result = parse_expression(ctx, bound);
8
            ctx.typechecking_context = saved;
8
            let Some(expression) = result? else {
                return Ok(None);
            };
8
            if let Expression::Atomic(_, Atom::Reference(reference)) = expression
8
                && reference.return_type()
8
                    == conjure_cp_core::ast::ReturnType::EnumeratedType(ty.clone())
            {
8
                return Ok(Some(EnumVariantVal(reference)));
            }
            ctx.record_error(RecoverableParseError::new(
                "Expected a member of this enumerated type".to_owned(),
                Some(bound.range()),
            ));
            Ok(None)
8
        };
4
        if range.kind() == "int_range" {
4
            let lower = match range.child_by_field_name("lower") {
                Some(bound) => {
4
                    let Some(value) = parse_bound(bound)? else {
                        return Ok(None);
                    };
4
                    Some(value)
                }
                None => None,
            };
4
            let upper = match range.child_by_field_name("upper") {
                Some(bound) => {
4
                    let Some(value) = parse_bound(bound)? else {
                        return Ok(None);
                    };
4
                    Some(value)
                }
                None => None,
            };
4
            ranges.push(match (lower, upper) {
4
                (Some(a), Some(b)) => Range::Bounded(a, b),
                (Some(a), None) => Range::UnboundedR(a),
                (None, Some(b)) => Range::UnboundedL(b),
                (None, None) => Range::Unbounded,
            });
        } else {
            let Some(value) = parse_bound(range)? else {
                return Ok(None);
            };
            ranges.push(Range::Single(value));
        }
    }
4
    Ok(Some(Domain::enumerated(ty, ranges)))
4
}