1
mod util;
2

            
3
use itertools::Itertools;
4
use proc_macro::TokenStream;
5

            
6
use proc_macro2::Span;
7
use quote::{ToTokens, quote};
8
use std::collections::HashMap;
9
use syn::token::Comma;
10
use syn::{
11
    ExprClosure, GenericParam, Ident, ItemFn, ItemImpl, ItemStruct, LitInt, LitStr, Result, Token,
12
    TypePath, bracketed, parenthesized, parse::Parse, parse::ParseStream, parse_macro_input,
13
};
14

            
15
use crate::util::{rename_ident_in_impl, type_is_ident, type_is_option_of_ident};
16
use util::{build_serde_as_type, rename_fn, rename_ident_in_fn, type_contains_ident};
17

            
18
/// Parsed arguments for `#[register_rule(...)]`.
19
///
20
/// The attribute syntax is:
21
///
22
/// ```text
23
/// #[register_rule("RuleSet", priority)]
24
/// #[register_rule(["RuleSetA", "RuleSetB"], priority)]
25
/// #[register_rule("RuleSet", priority, [prefilter, ...])]
26
/// #[register_rule("RuleSet", priority, [prefilter, ...], symbols_only)]
27
/// ```
28
///
29
/// The optional prefilter list narrows where the scheduler attempts the rule:
30
///
31
/// ```text
32
/// Variant              // focused expression must have this Expression variant
33
/// * / Variant          // focused expression must have an immediate child with this variant
34
/// Variant / Variant    // focused expression must have the left variant and an immediate child
35
///                      // with the right variant
36
/// Atomic / Reference   // focused expression must be an atomic reference
37
/// Atomic / Literal     // focused expression must be an atomic literal
38
/// * / Atomic / Reference  // focused expression must have an immediate atomic-reference child
39
/// * / Atomic / Literal    // focused expression must have an immediate atomic-literal child
40
/// ```
41
///
42
/// Items in the list are alternatives. For example, `[And / Comprehension, Or / Comprehension]`
43
/// means `(And with direct Comprehension child) OR (Or with direct Comprehension child)`.
44
///
45
/// Omitting the prefilter list makes the rule universal. A universal rule is considered at every
46
/// focused expression for its priority. In Rust source, write slash filters with spaces around the
47
/// slash, e.g. `[* / Bubble]` or `[Atomic / Reference]`, to keep the syntax visually distinct.
48
enum ParsedPrefilter {
49
    /// Focused-expression variant prefilter, e.g. `Add` or `Sub`.
50
    Variant(Ident),
51
    /// Immediate-child variant prefilter, parsed from `* / Child`.
52
    Child { child: Ident },
53
    /// Paired focused-expression and immediate-child variant prefilter, parsed from `Root / Child`.
54
    VariantChild { variant: Ident, child: Ident },
55
    /// Atomic subvariant prefilter, parsed from `Atomic / Reference` or `Atomic / Literal`.
56
    Atom(Ident),
57
    /// Immediate-child atomic subvariant prefilter, parsed from `* / Atomic / Reference`.
58
    ChildAtom(Ident),
59
}
60

            
61
struct RegisterRuleArgs {
62
    /// Rule set names this rule belongs to.
63
    rule_sets: Vec<LitStr>,
64
    /// Priority used for every listed rule set.
65
    priority: LitInt,
66
    /// Complete prefilter alternatives. Empty means the rule is universal.
67
    prefilters: Vec<ParsedPrefilter>,
68
    /// Whether a failed application can only be invalidated by a symbol-table change.
69
    symbols_only: bool,
70
}
71

            
72
impl Parse for RegisterRuleArgs {
73
1659
    fn parse(input: ParseStream) -> Result<Self> {
74
1659
        if input.is_empty() {
75
2
            return Ok(RegisterRuleArgs {
76
2
                rule_sets: Vec::new(),
77
2
                priority: LitInt::new("0", Span::call_site()),
78
2
                prefilters: Vec::new(),
79
2
                symbols_only: false,
80
2
            });
81
1657
        }
82

            
83
1657
        let rule_sets = if input.peek(syn::token::Bracket) {
84
            let content;
85
            bracketed!(content in input);
86

            
87
            let mut rule_sets = Vec::new();
88
            while !content.is_empty() {
89
                let rule_set: LitStr = content.parse()?;
90
                rule_sets.push(rule_set);
91
                if content.is_empty() {
92
                    break;
93
                }
94
                let _: Comma = content.parse()?;
95
            }
96
            rule_sets
97
        } else {
98
1657
            vec![input.parse()?]
99
        };
100

            
101
1657
        let _: Comma = input.parse()?;
102
1657
        let priority: LitInt = input.parse()?;
103

            
104
        // Parse optional variant names in brackets: "Minion", 4200, [Add, Sub]
105
1657
        let mut prefilters = Vec::new();
106
1657
        if input.peek(Comma) {
107
1650
            let _: Comma = input.parse()?;
108
            let content;
109
1650
            bracketed!(content in input);
110
2805
            while !content.is_empty() {
111
2805
                if content.peek(Token![*]) {
112
35
                    let _: Token![*] = content.parse()?;
113
35
                    let _: Token![/] = content.parse()?;
114
35
                    let variant: Ident = content.parse()?;
115
35
                    if variant == "Atomic" && content.peek(Token![/]) {
116
5
                        let _: Token![/] = content.parse()?;
117
5
                        let subvariant: Ident = content.parse()?;
118
5
                        match subvariant.to_string().as_str() {
119
                            "Literal" | "Reference" => {
120
5
                                prefilters.push(ParsedPrefilter::ChildAtom(subvariant));
121
                            }
122
                            _ => {
123
                                return Err(syn::Error::new(
124
                                    subvariant.span(),
125
                                    "expected Literal or Reference after * / Atomic /",
126
                                ));
127
                            }
128
                        }
129
                    } else {
130
30
                        prefilters.push(ParsedPrefilter::Child { child: variant });
131
                    }
132
                } else {
133
2770
                    let variant: Ident = content.parse()?;
134
2770
                    if content.peek(Token![/]) {
135
125
                        let _: Token![/] = content.parse()?;
136
125
                        let subvariant: Ident = content.parse()?;
137
125
                        if variant != "Atomic" {
138
115
                            prefilters.push(ParsedPrefilter::VariantChild {
139
115
                                variant,
140
115
                                child: subvariant,
141
115
                            });
142
                        } else {
143
10
                            match subvariant.to_string().as_str() {
144
                                "Literal" | "Reference" => {
145
10
                                    prefilters.push(ParsedPrefilter::Atom(subvariant));
146
                                }
147
                                _ => {
148
                                    return Err(syn::Error::new(
149
                                        subvariant.span(),
150
                                        "expected Literal or Reference after Atomic /",
151
                                    ));
152
                                }
153
                            }
154
                        }
155
                    } else {
156
2645
                        prefilters.push(ParsedPrefilter::Variant(variant));
157
                    }
158
                }
159
2805
                if content.is_empty() {
160
1650
                    break;
161
1155
                }
162
1155
                let _: Comma = content.parse()?;
163
            }
164
7
        }
165

            
166
1657
        let symbols_only = if input.peek(Comma) {
167
10
            let _: Comma = input.parse()?;
168
10
            let dependency: Ident = input.parse()?;
169
10
            if dependency != "symbols_only" {
170
                return Err(syn::Error::new(
171
                    dependency.span(),
172
                    "expected `symbols_only`",
173
                ));
174
10
            }
175
10
            true
176
        } else {
177
1647
            false
178
        };
179

            
180
1657
        Ok(RegisterRuleArgs {
181
1657
            rule_sets,
182
1657
            priority,
183
1657
            prefilters,
184
1657
            symbols_only,
185
1657
        })
186
1659
    }
187
}
188

            
189
/// Register a rule with the given rule sets and priorities.
190
#[proc_macro_attribute]
191
1659
pub fn register_rule(arg_tokens: TokenStream, item: TokenStream) -> TokenStream {
192
1659
    let func = parse_macro_input!(item as ItemFn);
193
1659
    let rule_ident = &func.sig.ident;
194
1659
    let static_name = format!("CONJURE_GEN_RULE_{rule_ident}").to_uppercase();
195
1659
    let static_ident = Ident::new(&static_name, rule_ident.span());
196

            
197
1659
    let args = parse_macro_input!(arg_tokens as RegisterRuleArgs);
198

            
199
1659
    let rule_sets_token = if args.rule_sets.is_empty() {
200
2
        quote! { &[] }
201
    } else {
202
1657
        let rule_sets = &args.rule_sets;
203
1657
        let priority = &args.priority;
204
1657
        quote! { &[#((#rule_sets, #priority as u16)),*] }
205
    };
206

            
207
1659
    let prefilters = if args.prefilters.is_empty() {
208
9
        quote! { None }
209
    } else {
210
2805
        let prefilter_tokens = args.prefilters.iter().map(|prefilter| match prefilter {
211
            ParsedPrefilter::Variant(variant) => {
212
2645
                quote! {
213
                    ::conjure_cp::rule_engine::RulePrefilter::Variant(
214
                        ::conjure_cp::discriminant_from_name!(#variant)
215
                    )
216
                }
217
            }
218
            ParsedPrefilter::Child { child } => {
219
30
                quote! {
220
                    ::conjure_cp::rule_engine::RulePrefilter::Child {
221
                        child: ::conjure_cp::discriminant_from_name!(#child),
222
                    }
223
                }
224
            }
225
            ParsedPrefilter::VariantChild { variant, child } => {
226
115
                quote! {
227
                    ::conjure_cp::rule_engine::RulePrefilter::VariantChild {
228
                        variant: ::conjure_cp::discriminant_from_name!(#variant),
229
                        child: ::conjure_cp::discriminant_from_name!(#child),
230
                    }
231
                }
232
            }
233
            ParsedPrefilter::Atom(atom_variant) => {
234
10
                quote! {
235
                    ::conjure_cp::rule_engine::RulePrefilter::Atom(
236
                        ::conjure_cp::rule_engine::AtomKind::#atom_variant
237
                    )
238
                }
239
            }
240
            ParsedPrefilter::ChildAtom(atom_variant) => {
241
5
                quote! {
242
                    ::conjure_cp::rule_engine::RulePrefilter::ChildAtom(
243
                        ::conjure_cp::rule_engine::AtomKind::#atom_variant
244
                    )
245
                }
246
            }
247
2805
        });
248
1650
        quote! {
249
            Some(&[#(#prefilter_tokens),*])
250
        }
251
    };
252
1659
    let failure_invalidation = if args.symbols_only {
253
10
        quote! { ::conjure_cp::rule_engine::RuleFailureInvalidation::SymbolsOnly }
254
    } else {
255
1649
        quote! { ::conjure_cp::rule_engine::RuleFailureInvalidation::ExpressionOrSymbols }
256
    };
257

            
258
1659
    let expanded = quote! {
259
        #func
260

            
261
        pub static #static_ident: ::conjure_cp::rule_engine::Rule<'static> = ::conjure_cp::rule_engine::Rule {
262
            name: stringify!(#rule_ident),
263
            application: #rule_ident,
264
            rule_sets: #rule_sets_token,
265
            prefilters: #prefilters,
266
            failure_invalidation: #failure_invalidation,
267
        };
268

            
269
        ::conjure_cp::rule_engine::_dependencies::inventory::submit! {
270
            &#static_ident
271
        }
272
    };
273

            
274
1659
    TokenStream::from(expanded)
275
1659
}
276
// this is only constructed once at comptime so this doesn't matter
277
#[allow(clippy::large_enum_variant)]
278
enum ReprStateType {
279
    Struct(ItemStruct, Vec<ItemImpl>),
280
    Path(TypePath),
281
}
282

            
283
impl Parse for ReprStateType {
284
185
    fn parse(input: ParseStream) -> Result<Self> {
285
185
        if let Ok(strct) = input.parse::<ItemStruct>() {
286
185
            let mut imps = Vec::new();
287
385
            while let Ok(imp) = input.parse::<ItemImpl>() {
288
200
                imps.push(imp);
289
            }
290
185
            return Ok(ReprStateType::Struct(strct, imps));
291
        }
292
        if let Ok(path) = input.parse::<TypePath>() {
293
            return Ok(ReprStateType::Path(path));
294
        }
295
        Err(input.error(
296
            "Expected one of:\
297
        - A struct definition, e.g `struct State<T> {...}`\
298
        - A path to an existing type, e.g `Vec<T>`\
299
        ",
300
        ))
301
185
    }
302
}
303

            
304
struct ReprDefArgs {
305
    /// Name of this representation rule
306
    ident: Ident,
307
    /// Short name used in generated representation-variable names and traces.
308
    short_name: LitStr,
309
    /// Representation state type
310
    state_ty: ReprStateType,
311
    /// Initialisation at domain level: `init: (DomainPtr) -> Result<State<DomainPtr>, ReprInitError>`
312
    init_fn: ItemFn,
313
    /// Generating structural constraints: `structural: (&State<DeclarationPtr>) -> Vec<Expression>`
314
    structural_fn: ItemFn,
315
    /// Going down: `down: (&State<DeclarationPtr>, Literal) -> Result<State<Literal>, ReprDownError>`
316
    down_fn: ItemFn,
317
    /// Going up: `up: State<Literal> -> Literal`
318
    up_fn: ItemFn,
319
    /// Getting representation variables: `repr_vars: &State<DeclarationPtr> -> VecDeque<DeclarationPtr>`
320
    /// If not provided, we attempt to codegen one using uniplate
321
    repr_vars_fn: Option<ItemFn>,
322
    /// Measuring the representation-domain size. If omitted, this is generated using
323
    /// uniplate; representations with non-uniplate containers can provide it explicitly.
324
    compactness_fn: Option<ItemFn>,
325
    /// Restricting this representation to some solver families:
326
    /// `applies: (SolverFamily) -> bool`. If omitted, the representation is solver-independent.
327
    applies_fn: Option<ItemFn>,
328
    integer_encoding_fn: Option<ItemFn>,
329
    probe_score_fn: Option<ItemFn>,
330
}
331

            
332
impl Parse for ReprDefArgs {
333
185
    fn parse(input: ParseStream) -> Result<Self> {
334
185
        let ident = input.parse::<Ident>()?;
335
        let short_name_content;
336
185
        parenthesized!(short_name_content in input);
337
185
        let short_name = short_name_content.parse::<LitStr>()?;
338
185
        let state_ty = input.parse::<ReprStateType>()?;
339

            
340
        // TODO: Exact syntax subject to change
341

            
342
185
        let mut funcs = HashMap::<String, ItemFn>::new();
343
185
        let mut errors: Vec<syn::Error> = Vec::new();
344
185
        for _ in 0..9 {
345
1665
            match input.parse::<ItemFn>() {
346
                Ok(func) => {
347
1000
                    let ident = func.sig.ident.to_string();
348
1000
                    funcs.insert(ident, func);
349
                }
350
665
                Err(e) => errors.push(e),
351
            }
352
        }
353

            
354
185
        let fmt_errors = format!(
355
            "\nErrors:\n{}",
356
185
            errors.iter().map(syn::Error::to_string).join("\n")
357
        );
358

            
359
185
        let init_fn = funcs.remove("init").ok_or_else(|| {
360
            input.error(format!("Expected `fn init(DomainPtr) -> Result<State<DomainPtr>, ReprInitError>`{fmt_errors}"))
361
        })?;
362
185
        let structural_fn = funcs.remove("structural").ok_or_else(|| {
363
            input.error(format!(
364
                "Expected `fn structural(&State<DeclarationPtr>) -> Vec<Expression>`{fmt_errors}"
365
            ))
366
        })?;
367
185
        let down_fn = funcs.remove("down").ok_or_else(|| input.error(format!("Expected `fn down(&State<DomainPtr>, Literal) -> Result<State<Literal>, ReprDownError>`{fmt_errors}")))?;
368
185
        let up_fn = funcs.remove("up").ok_or_else(|| {
369
            input.error(format!(
370
                "Expected `fn up(State<Literal>) -> Literal`{fmt_errors}"
371
            ))
372
        })?;
373
185
        let repr_vars_fn = funcs.remove("repr_vars");
374
185
        let compactness_fn = funcs.remove("compactness");
375
185
        let applies_fn = funcs.remove("applies");
376
185
        let integer_encoding_fn = funcs.remove("integer_encoding");
377
185
        let probe_score_fn = funcs.remove("probe_score");
378

            
379
185
        if repr_vars_fn.is_none() && matches!(state_ty, ReprStateType::Path(..)) {
380
            return Err(input.error("A repr_vars implementation is required for external types"));
381
185
        }
382

            
383
185
        Ok(Self {
384
185
            ident,
385
185
            short_name,
386
185
            state_ty,
387
185
            init_fn,
388
185
            structural_fn,
389
185
            down_fn,
390
185
            up_fn,
391
185
            repr_vars_fn,
392
185
            compactness_fn,
393
185
            applies_fn,
394
185
            integer_encoding_fn,
395
185
            probe_score_fn,
396
185
        })
397
185
    }
398
}
399

            
400
#[proc_macro]
401
185
pub fn register_representation(input: TokenStream) -> TokenStream {
402
185
    let args = parse_macro_input!(input as ReprDefArgs);
403
185
    let repr_ident = &args.ident;
404
185
    let repr_short_name = &args.short_name;
405
185
    let repr_name_str = repr_ident.to_string();
406

            
407
    // prefix for generated names
408
185
    let prefix = format!("CONJURE_GEN_REPR_{}_", repr_name_str);
409

            
410
185
    let (user_state_ident, struct_def_tokens) = match &args.state_ty {
411
        ReprStateType::Struct(item_struct, _) => {
412
            // get ident and body of the struct
413
185
            let ident = item_struct.ident.clone();
414
185
            let prefixed_ident = Ident::new(
415
185
                &format!("{}{}", repr_name_str, ident),
416
185
                item_struct.ident.span(),
417
185
            );
418
185
            let tokens = generate_struct_def(item_struct, &prefixed_ident);
419
185
            (ident, tokens)
420
        }
421
        ReprStateType::Path(type_path) => {
422
            // for a path like `foo::MyState`, just use it directly; no struct to emit
423
            let ident = type_path
424
                .path
425
                .segments
426
                .last()
427
                .expect("state type path must have at least one segment")
428
                .ident
429
                .clone();
430
            (ident, quote! {})
431
        }
432
    };
433

            
434
    // Actual ident of the "State<T>" type
435
185
    let state_ident = match &args.state_ty {
436
        ReprStateType::Struct(..) => {
437
            // prefix user-defined struct's ident so it doesn't clash with anything
438
185
            Ident::new(
439
185
                &format!("{}{}", repr_name_str, user_state_ident),
440
185
                user_state_ident.span(),
441
185
            )
442
        }
443
        // otherwise use the provided name as is
444
        ReprStateType::Path(_) => user_state_ident.clone(),
445
    };
446

            
447
    // Rename the idents in user-defined functions to their prefixed versions
448
    // e.g:
449
    // MyState<T> -> CONJURE_GEN_REPR_<Rule>_MyState<T>
450
    // fn init(...) -> fn CONJURE_GEN_REPR_<Rule>_init(...)
451
185
    let prefixed_init = Ident::new(&format!("{}init", prefix), args.init_fn.sig.ident.span());
452
185
    let prefixed_structural = Ident::new(
453
185
        &format!("{}structural", prefix),
454
185
        args.structural_fn.sig.ident.span(),
455
185
    );
456
185
    let prefixed_down = Ident::new(&format!("{}down", prefix), args.down_fn.sig.ident.span());
457
185
    let prefixed_up = Ident::new(&format!("{}up", prefix), args.up_fn.sig.ident.span());
458
185
    let prefixed_repr_vars = args
459
185
        .repr_vars_fn
460
185
        .as_ref()
461
55
        .map(|f| Ident::new(&format!("{}repr_vars", prefix), f.sig.ident.span()));
462
185
    let prefixed_compactness = args
463
185
        .compactness_fn
464
185
        .as_ref()
465
105
        .map(|f| Ident::new(&format!("{}compactness", prefix), f.sig.ident.span()));
466
185
    let prefixed_applies = args
467
185
        .applies_fn
468
185
        .as_ref()
469
50
        .map(|f| Ident::new(&format!("{}applies", prefix), f.sig.ident.span()));
470
185
    let applies_fn = args.applies_fn.map(|f| {
471
50
        let f = rename_fn(f, prefixed_applies.as_ref().unwrap());
472
50
        quote! {
473
            #[allow(non_snake_case)]
474
            #f
475
        }
476
50
    });
477
185
    let applies_impl = match &prefixed_applies {
478
50
        Some(ident) => quote! {
479
            fn applies_to(family: ::conjure_cp::settings::SolverFamily) -> bool {
480
                #ident(family)
481
            }
482
        },
483
135
        None => quote! {},
484
    };
485

            
486
185
    let probe_score_impl = args.probe_score_fn.map(|f| quote! { #f });
487

            
488
185
    let integer_encoding_impl = args.integer_encoding_fn.map(|mut f| {
489
40
        f.sig.ident = Ident::new("is_integer_encoding", f.sig.ident.span());
490
40
        quote! { #f }
491
40
    });
492

            
493
185
    let mut init_fn = rename_fn(args.init_fn, &prefixed_init);
494
185
    let mut structural_fn = rename_fn(args.structural_fn, &prefixed_structural);
495
185
    let mut down_fn = rename_fn(args.down_fn, &prefixed_down);
496
185
    let mut up_fn = rename_fn(args.up_fn, &prefixed_up);
497
185
    let mut repr_vars_fn = args
498
185
        .repr_vars_fn
499
55
        .map(|f| rename_fn(f, prefixed_repr_vars.as_ref().unwrap()));
500
185
    let mut compactness_fn = args
501
185
        .compactness_fn
502
105
        .map(|f| rename_fn(f, prefixed_compactness.as_ref().unwrap()));
503

            
504
185
    if matches!(&args.state_ty, ReprStateType::Struct(..)) {
505
185
        init_fn = rename_ident_in_fn(init_fn, &user_state_ident, &state_ident);
506
185
        structural_fn = rename_ident_in_fn(structural_fn, &user_state_ident, &state_ident);
507
185
        down_fn = rename_ident_in_fn(down_fn, &user_state_ident, &state_ident);
508
185
        up_fn = rename_ident_in_fn(up_fn, &user_state_ident, &state_ident);
509
185
        repr_vars_fn = repr_vars_fn.map(|f| rename_ident_in_fn(f, &user_state_ident, &state_ident));
510
185
        compactness_fn =
511
185
            compactness_fn.map(|f| rename_ident_in_fn(f, &user_state_ident, &state_ident));
512
    }
513

            
514
    // Rename idents in the user-provided impl
515
185
    let renamed_impls = if let ReprStateType::Struct(_, impls) = args.state_ty {
516
185
        impls
517
185
            .into_iter()
518
200
            .map(|imp| rename_ident_in_impl(imp, &user_state_ident, &state_ident))
519
            .collect()
520
    } else {
521
        Vec::new()
522
    };
523

            
524
185
    let repr_vars_impl = if repr_vars_fn.is_some() {
525
55
        quote! {#prefixed_repr_vars(self)}
526
    } else {
527
130
        quote! {self.__collect_t_children()}
528
    };
529
185
    let repr_vars_fn_toks = repr_vars_fn.map(|f| {
530
55
        quote! {
531
            #[allow(non_snake_case)]
532
            #f
533
        }
534
55
    });
535
185
    let compactness_impl = if compactness_fn.is_some() {
536
105
        quote! {#prefixed_compactness(self)}
537
    } else {
538
80
        quote! {
539
            self.__collect_t_children()
540
                .iter()
541
                .map(default_impls::domain_size)
542
                .fold(1usize, usize::saturating_mul)
543
        }
544
    };
545
185
    let compactness_fn_toks = compactness_fn.map(|f| {
546
105
        quote! {
547
            #[allow(non_snake_case)]
548
            #f
549
        }
550
105
    });
551

            
552
    // Static name for the registry entry
553
185
    let dist_slice_name = format!("CONJURE_GEN_REPR_{}", repr_name_str).to_uppercase();
554
185
    let dist_slice_ident = Ident::new(&dist_slice_name, repr_ident.span());
555

            
556
185
    let init_cache_name = format!("CONJURE_GEN_REPR_{}_INIT_CACHE", repr_name_str).to_uppercase();
557
185
    let init_cache_ident = Ident::new(&init_cache_name, repr_ident.span());
558

            
559
185
    let expanded = quote! {
560
        // -- Dependencies
561
        use ::conjure_cp::representation::_dependencies::*;
562
        use ::conjure_cp::ast::{
563
            DeclarationPtr, DomainPtr, Expression, Literal, SymbolTable, Name
564
        };
565

            
566
        // -- User-provided struct definition
567
        #struct_def_tokens
568

            
569
        // -- User-provided struct impl
570
        #(#renamed_impls)*
571

            
572
        // -- User-provided functions
573
        #[allow(non_snake_case)]
574
        #init_fn
575
        #[allow(non_snake_case)]
576
        #structural_fn
577
        #[allow(non_snake_case)]
578
        #down_fn
579
        #[allow(non_snake_case)]
580
        #up_fn
581
        #repr_vars_fn_toks
582
        #compactness_fn_toks
583

            
584
        static #init_cache_ident: std::sync::LazyLock<FrozenMap<DomainPtr, Box<#state_ident<DomainPtr>>>> = std::sync::LazyLock::new(|| FrozenMap::new());
585

            
586
        // -- Trait implementations
587
        impl ReprDomainLevel for #state_ident<DomainPtr> {
588
            const RULE: &'static dyn ReprRuleStored = &#repr_ident;
589
            type Assignment = #state_ident<Literal>;
590
            type DeclLevel = #state_ident<DeclarationPtr>;
591

            
592
            fn init(dom: DomainPtr) -> ::core::result::Result<Self, ReprInitError>
593
            where
594
                Self: Sized,
595
            {
596
                if let Some(res) = #init_cache_ident.get(&dom) {
597
                    return Ok(res.clone());
598
                }
599

            
600
                let res = #prefixed_init(dom.clone())?;
601
                let _ = #init_cache_ident.insert(dom, Box::new(res.clone()));
602
                Ok(res)
603
            }
604

            
605
            fn compactness_score(&self) -> usize {
606
                #compactness_impl
607
            }
608

            
609
            fn down(
610
                &self,
611
                value: Literal,
612
            ) -> ::core::result::Result<Self::Assignment, ReprDownError> {
613
                #prefixed_down(self, value)
614
            }
615

            
616
            fn instantiate(self, decl: DeclarationPtr) -> ReprInstantiateResult<Self::DeclLevel> {
617
                default_impls::instantiate_default_impl(self, decl, #prefixed_structural)
618
            }
619
        }
620

            
621
        impl ReprDeclLevel for #state_ident<DeclarationPtr> {
622
            const RULE: &'static dyn ReprRuleStored = &#repr_ident;
623
            type Assignment = #state_ident<Literal>;
624
            type DomainLevel = #state_ident<DomainPtr>;
625

            
626
            fn to_domain_level(self) -> Self::DomainLevel {
627
                default_impls::to_domain_level_default_impl(self)
628
            }
629

            
630
            fn lookup_via(
631
                &self,
632
                lookup: &LookupFn<'_>,
633
            ) -> ::core::result::Result<Self::Assignment, ReprUpError> {
634
                default_impls::lookup_via_default_impl(self, lookup)
635
            }
636

            
637
            fn repr_vars(&self) -> ::std::collections::VecDeque<DeclarationPtr> {
638
                #repr_vars_impl
639
            }
640
        }
641

            
642
        impl ReprAssignment for #state_ident<Literal> {
643
            fn up(self) -> Literal {
644
                #prefixed_up(self)
645
            }
646
        }
647

            
648
        // -- ReprRule marker struct
649
        pub struct #repr_ident;
650

            
651
        #applies_fn
652

            
653
        impl ReprRule for #repr_ident {
654
            const STORED: &'static dyn ReprRuleStored = &#repr_ident;
655
            const NAME: &'static str = #repr_name_str;
656
            const SHORT_NAME: &'static str = #repr_short_name;
657
            type Assignment = #state_ident<Literal>;
658
            type DeclLevel = #state_ident<DeclarationPtr>;
659
            type DomainLevel = #state_ident<DomainPtr>;
660

            
661
            #applies_impl
662
            #integer_encoding_impl
663
            #probe_score_impl
664
        }
665

            
666
        // -- Registry entry
667
        pub static #dist_slice_ident: &'static dyn ReprRuleStored = &#repr_ident;
668

            
669
        ::conjure_cp::representation::_dependencies::inventory::submit! {
670
            #dist_slice_ident
671
        }
672
    };
673

            
674
185
    TokenStream::from(expanded)
675
185
}
676

            
677
/// Generates the struct definition with the necessary derive macros and serde attributes.
678
/// The struct is always emitted as `pub` with the given `prefixed_ident` as its name.
679
185
fn generate_struct_def(
680
185
    item_struct: &ItemStruct,
681
185
    prefixed_ident: &Ident,
682
185
) -> proc_macro2::TokenStream {
683
    // Find the generic type parameter name (e.g. `T`)
684
185
    let generic_param_ident = item_struct
685
185
        .generics
686
185
        .params
687
185
        .iter()
688
185
        .find_map(|p| {
689
185
            if let GenericParam::Type(tp) = p {
690
185
                Some(tp.ident.clone())
691
            } else {
692
                None
693
            }
694
185
        })
695
185
        .expect("state struct must have exactly one type parameter");
696

            
697
185
    let generics = &item_struct.generics;
698

            
699
185
    let serde_bound = format!(
700
        "ReprStateSerde: SerializeAs<{0}> + for<'d> DeserializeAs<'d, {0}>",
701
185
        generic_param_ident
702
    );
703

            
704
185
    let mut collect_children_exprs: Vec<proc_macro2::TokenStream> = Vec::new();
705
185
    let mut biplate_bounds: Vec<proc_macro2::TokenStream> = vec![quote! {
706
        #generic_param_ident: ::conjure_cp::representation::_dependencies::uniplate::Uniplate
707
    }];
708

            
709
185
    let fields = match &item_struct.fields {
710
        syn::Fields::Named(named) => {
711
185
            let field_tokens: Vec<_> = named
712
185
                .named
713
185
                .iter()
714
860
                .map(|f| {
715
860
                    let field_attrs = &f.attrs;
716
860
                    let field_vis = &f.vis;
717
860
                    let field_ident = f
718
860
                        .ident
719
860
                        .as_ref()
720
860
                        .expect("named field must have an identifier");
721
860
                    let field_ty = &f.ty;
722

            
723
860
                    if type_contains_ident(&f.ty, &generic_param_ident) {
724
250
                        if type_is_ident(&f.ty, &generic_param_ident) {
725
160
                            collect_children_exprs.push(quote! {
726
160
                                children.push_back(self.#field_ident.clone());
727
160
                            });
728
90
                        } else if type_is_option_of_ident(&f.ty, &generic_param_ident) {
729
30
                            collect_children_exprs.push(quote! {
730
30
                                children.extend(self.#field_ident.iter().cloned());
731
30
                            });
732
                        } else {
733
60
                            collect_children_exprs.push(quote! {
734
60
                                children.extend(
735
60
                                    ::conjure_cp::representation::_dependencies::uniplate::Biplate::<#generic_param_ident>::children_bi(&self.#field_ident)
736
60
                                );
737
60
                            });
738
60
                            biplate_bounds.push(quote! {
739
60
                                #field_ty: ::conjure_cp::representation::_dependencies::uniplate::Biplate<#generic_param_ident>
740
60
                            });
741
                        }
742

            
743
                        let serde_as_ty =
744
250
                            build_serde_as_type(field_ty, &generic_param_ident, "ReprStateSerde");
745
250
                        let serde_as_str = serde_as_ty.to_token_stream().to_string();
746
250
                        quote! {
747
                            #(#field_attrs)*
748
                            #[serde_as(as = #serde_as_str)]
749
                            #field_vis #field_ident: #field_ty
750
                        }
751
                    } else {
752
610
                        quote! {
753
                            #(#field_attrs)*
754
                            #field_vis #field_ident: #field_ty
755
                        }
756
                    }
757
860
                })
758
                .collect();
759

            
760
185
            quote! { { #(#field_tokens),* } }
761
        }
762
        syn::Fields::Unnamed(unnamed) => {
763
            let field_tokens: Vec<_> = unnamed
764
                .unnamed
765
                .iter()
766
                .enumerate()
767
                .map(|(idx, f)| {
768
                    let field_attrs = &f.attrs;
769
                    let field_vis = &f.vis;
770
                    let field_ty = &f.ty;
771

            
772
                    if type_contains_ident(&f.ty, &generic_param_ident) {
773
                        let index = syn::Index::from(idx);
774
                        if type_is_ident(&f.ty, &generic_param_ident) {
775
                            collect_children_exprs.push(quote! {
776
                                children.push_back(self.#index.clone());
777
                            });
778
                        } else if type_is_option_of_ident(&f.ty, &generic_param_ident) {
779
                            collect_children_exprs.push(quote! {
780
                                children.extend(self.#index.iter().cloned());
781
                            });
782
                        } else {
783
                            collect_children_exprs.push(quote! {
784
                                children.extend(
785
                                    ::conjure_cp::representation::_dependencies::uniplate::Biplate::<#generic_param_ident>::children_bi(&self.#index)
786
                                );
787
                            });
788
                            biplate_bounds.push(quote! {
789
                                #field_ty: ::conjure_cp::representation::_dependencies::uniplate::Biplate<#generic_param_ident>
790
                            });
791
                        }
792

            
793
                        let serde_as_ty =
794
                            build_serde_as_type(field_ty, &generic_param_ident, "ReprStateSerde");
795
                        let serde_as_str = serde_as_ty.to_token_stream().to_string();
796
                        quote! {
797
                            #(#field_attrs)*
798
                            #[serde_as(as = #serde_as_str)]
799
                            #field_vis #field_ty
800
                        }
801
                    } else {
802
                        quote! {
803
                            #(#field_attrs)*
804
                            #field_vis #field_ty
805
                        }
806
                    }
807
                })
808
                .collect();
809

            
810
            quote! { ( #(#field_tokens),* ); }
811
        }
812
        syn::Fields::Unit => quote! { ; },
813
    };
814

            
815
185
    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
816
185
    let method_where_clause = if biplate_bounds.is_empty() {
817
        quote! {}
818
    } else {
819
185
        quote! { where #(#biplate_bounds),* }
820
    };
821

            
822
185
    quote! {
823
        #[allow(non_camel_case_types)]
824
        #[::conjure_cp::representation::_dependencies::serde_with::serde_as(
825
            crate = "::conjure_cp::representation::_dependencies::serde_with"
826
        )]
827
        #[derive(
828
            Debug,
829
            Clone,
830
            PartialEq,
831
            Eq,
832
            ::conjure_cp::representation::_dependencies::funcmap::FuncMap,
833
            ::conjure_cp::representation::_dependencies::funcmap::TryFuncMap,
834
            ::conjure_cp::representation::_dependencies::serde::Serialize,
835
            ::conjure_cp::representation::_dependencies::serde::Deserialize
836
        )]
837
        #[serde(
838
            crate = "::conjure_cp::representation::_dependencies::serde",
839
            bound = #serde_bound
840
        )]
841
        #[funcmap(crate = "::conjure_cp::representation::_dependencies::funcmap")]
842
        pub struct #prefixed_ident #generics #fields
843

            
844
        impl #impl_generics #prefixed_ident #ty_generics #where_clause {
845
            fn __collect_t_children(&self) -> ::std::collections::VecDeque<#generic_param_ident>
846
            #method_where_clause
847
            {
848
                let mut children = ::std::collections::VecDeque::new();
849
                #(#collect_children_exprs)*
850
                children
851
            }
852
        }
853
    }
854
185
}
855

            
856
73
fn parse_parenthesized<T: Parse>(input: ParseStream) -> Result<Vec<T>> {
857
    let content;
858
73
    parenthesized!(content in input);
859

            
860
73
    let mut paths = Vec::new();
861
75
    while !content.is_empty() {
862
65
        let path = content.parse()?;
863
65
        paths.push(path);
864
65
        if content.is_empty() {
865
63
            break;
866
2
        }
867
2
        content.parse::<Comma>()?;
868
    }
869

            
870
73
    Ok(paths)
871
73
}
872

            
873
struct RuleSetArgs {
874
    name: LitStr,
875
    dependencies: Vec<LitStr>,
876
    applies_fn: Option<ExprClosure>,
877
}
878

            
879
impl Parse for RuleSetArgs {
880
75
    fn parse(input: ParseStream) -> Result<Self> {
881
75
        let name = input.parse()?;
882

            
883
75
        if input.is_empty() {
884
2
            return Ok(Self {
885
2
                name,
886
2
                dependencies: Vec::new(),
887
2
                applies_fn: None,
888
2
            });
889
73
        }
890

            
891
73
        input.parse::<Comma>()?;
892
73
        let dependencies = parse_parenthesized::<LitStr>(input)?;
893

            
894
73
        if input.is_empty() {
895
14
            return Ok(Self {
896
14
                name,
897
14
                dependencies,
898
14
                applies_fn: None,
899
14
            });
900
59
        }
901

            
902
59
        input.parse::<Comma>()?;
903
59
        let applies_fn = input.parse::<ExprClosure>()?;
904

            
905
59
        Ok(Self {
906
59
            name,
907
59
            dependencies,
908
59
            applies_fn: Some(applies_fn),
909
59
        })
910
75
    }
911
}
912

            
913
/**
914
* Register a rule set with the given name, dependencies, and metadata.
915
*
916
* # Example
917
* ```rust
918
 * use conjure_cp_rule_macros::register_rule_set;
919
 * register_rule_set!("MyRuleSet", ("DependencyRuleSet", "AnotherRuleSet"));
920
* ```
921
 */
922
#[proc_macro]
923
75
pub fn register_rule_set(args: TokenStream) -> TokenStream {
924
    let RuleSetArgs {
925
        name,
926
        dependencies,
927
        applies_fn,
928
75
    } = parse_macro_input!(args as RuleSetArgs);
929

            
930
75
    let static_name = format!("CONJURE_GEN_RULE_SET_{}", name.value()).to_uppercase();
931
75
    let static_ident = Ident::new(&static_name, Span::call_site());
932

            
933
75
    let dependencies = quote! {
934
        #(#dependencies),*
935
    };
936

            
937
75
    let applies_to_family = match applies_fn {
938
        // Does not apply by default, e.g. only used as a dependency
939
16
        None => quote! { |_: &::conjure_cp::settings::SolverFamily| false },
940
59
        Some(func) => quote! { #func },
941
    };
942

            
943
75
    let expanded = quote! {
944
        pub static #static_ident: ::conjure_cp::rule_engine::RuleSet<'static> =
945
            ::conjure_cp::rule_engine::RuleSet::new(#name, &[#dependencies], #applies_to_family);
946

            
947
        ::conjure_cp::rule_engine::_dependencies::inventory::submit! {
948
            &#static_ident
949
        }
950
    };
951

            
952
75
    TokenStream::from(expanded)
953
75
}