Skip to main content

graphrecords_query/registry/
pattern.rs

1use super::{
2    capability::{CapabilityIdentifier, CapabilityRegistry},
3    descriptor::{
4        ArgumentDescriptor, ArgumentMissingPolicy, ArgumentValueSource, ArityDescriptor,
5        DomainDescriptor, IndexDescriptor, LaneShapeDescriptor, OperandDescriptor, OrderDescriptor,
6        RetentionDescriptor, ValueArgumentDescriptor, ValueDescriptor, ValueRole,
7    },
8};
9use graphrecords_utils::aliases::GrHashMap;
10
11pub type VariableIdentifier = usize;
12
13#[derive(Clone, Debug)]
14pub enum IndexPattern {
15    Any,
16    Registered,
17    Capable(CapabilitySet),
18    Entity,
19    Concrete(DomainDescriptor),
20    Expanded { parent: Box<Self>, child: Box<Self> },
21    Variable(VariableIdentifier, Box<Self>),
22}
23
24#[derive(Clone, Debug)]
25pub enum ValuePattern {
26    Registered,
27    Concrete(ValueDescriptor),
28    Capable(CapabilitySet),
29    GroupKeyIs(Box<IndexPattern>),
30    IndexValue(IndexPattern),
31    EntityReference(IndexPattern),
32    Variable(VariableIdentifier, Box<Self>),
33}
34
35#[derive(Clone, Debug)]
36pub struct CapabilitySet(Vec<CapabilityIdentifier>);
37
38impl CapabilitySet {
39    #[must_use]
40    pub const fn new(capabilities: Vec<CapabilityIdentifier>) -> Self {
41        Self(capabilities)
42    }
43}
44
45#[derive(Clone, Debug)]
46pub enum ShapePattern {
47    Any,
48    Indexed {
49        index: IndexPattern,
50        value: ValuePattern,
51    },
52    Bare {
53        value: ValuePattern,
54    },
55    Variable(VariableIdentifier, Box<Self>),
56}
57
58#[derive(Clone, Debug)]
59pub enum OrderPattern {
60    Any,
61    Ordered,
62    Unordered,
63    Variable(VariableIdentifier, Box<Self>),
64}
65
66#[derive(Clone, Debug)]
67pub enum ArityPattern {
68    Any,
69    Multiple(OrderPattern),
70    Single,
71    Definite,
72    Variable(VariableIdentifier, Box<Self>),
73}
74
75#[derive(Clone, Debug)]
76pub enum StatePattern {
77    Lane {
78        shape: ShapePattern,
79        arity: ArityPattern,
80    },
81    Group {
82        member: IndexPattern,
83        key: IndexPattern,
84        payload: Box<Self>,
85    },
86    Variable(VariableIdentifier, Box<Self>),
87}
88
89#[derive(Clone, Debug)]
90pub enum AlignmentDescriptor {
91    Keyed(IndexPattern),
92    Unaligned,
93}
94
95#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
96pub enum RetentionPattern {
97    Any,
98    Fixed(RetentionDescriptor),
99}
100
101#[derive(Clone, Debug)]
102pub enum ArgumentPattern {
103    Value {
104        value: ValuePattern,
105        alignment: AlignmentDescriptor,
106        retention: RetentionPattern,
107    },
108    Set(ValuePattern),
109    Field(DomainDescriptor),
110    Selector(DomainDescriptor),
111    Operand(StatePattern),
112}
113
114#[derive(Clone, Debug, Default)]
115pub struct Bindings {
116    indices: GrHashMap<VariableIdentifier, IndexDescriptor>,
117    values: GrHashMap<VariableIdentifier, ValueDescriptor>,
118    shapes: GrHashMap<VariableIdentifier, LaneShapeDescriptor>,
119    orders: GrHashMap<VariableIdentifier, OrderDescriptor>,
120    arities: GrHashMap<VariableIdentifier, ArityDescriptor>,
121    operands: GrHashMap<VariableIdentifier, OperandDescriptor>,
122    argument_retention: RetentionDescriptor,
123}
124
125impl Bindings {
126    fn bind_index(&mut self, variable: VariableIdentifier, index: &IndexDescriptor) -> bool {
127        if let Some(bound) = self.indices.get(&variable) {
128            return bound == index;
129        }
130
131        self.indices.insert(variable, index.clone());
132        true
133    }
134
135    #[must_use]
136    pub fn index(&self, variable: VariableIdentifier) -> Option<&IndexDescriptor> {
137        self.indices.get(&variable)
138    }
139
140    fn bind_value(&mut self, variable: VariableIdentifier, value: &ValueDescriptor) -> bool {
141        if let Some(bound) = self.values.get(&variable) {
142            return bound == value;
143        }
144
145        self.values.insert(variable, value.clone());
146        true
147    }
148
149    #[must_use]
150    pub fn value(&self, variable: VariableIdentifier) -> Option<&ValueDescriptor> {
151        self.values.get(&variable)
152    }
153
154    fn bind_shape(&mut self, variable: VariableIdentifier, shape: &LaneShapeDescriptor) -> bool {
155        if let Some(bound) = self.shapes.get(&variable) {
156            return bound == shape;
157        }
158
159        self.shapes.insert(variable, shape.clone());
160        true
161    }
162
163    #[must_use]
164    pub fn shape(&self, variable: VariableIdentifier) -> Option<&LaneShapeDescriptor> {
165        self.shapes.get(&variable)
166    }
167
168    fn bind_order(&mut self, variable: VariableIdentifier, order: OrderDescriptor) -> bool {
169        if let Some(bound) = self.orders.get(&variable) {
170            return *bound == order;
171        }
172
173        self.orders.insert(variable, order);
174        true
175    }
176
177    #[must_use]
178    pub fn order(&self, variable: VariableIdentifier) -> Option<OrderDescriptor> {
179        self.orders.get(&variable).copied()
180    }
181
182    fn bind_arity(&mut self, variable: VariableIdentifier, arity: ArityDescriptor) -> bool {
183        if let Some(bound) = self.arities.get(&variable) {
184            return *bound == arity;
185        }
186
187        self.arities.insert(variable, arity);
188        true
189    }
190
191    #[must_use]
192    pub fn arity(&self, variable: VariableIdentifier) -> Option<ArityDescriptor> {
193        self.arities.get(&variable).copied()
194    }
195
196    fn bind_operand(&mut self, variable: VariableIdentifier, operand: &OperandDescriptor) -> bool {
197        if let Some(bound) = self.operands.get(&variable) {
198            return bound == operand;
199        }
200
201        self.operands.insert(variable, operand.clone());
202        true
203    }
204
205    #[must_use]
206    pub fn operand(&self, variable: VariableIdentifier) -> Option<&OperandDescriptor> {
207        self.operands.get(&variable)
208    }
209
210    fn compose_retention(&mut self, retention: RetentionDescriptor) {
211        if retention == RetentionDescriptor::Dropping {
212            self.argument_retention = RetentionDescriptor::Dropping;
213        }
214    }
215
216    #[must_use]
217    pub const fn argument_retention(&self) -> RetentionDescriptor {
218        self.argument_retention
219    }
220}
221
222impl IndexPattern {
223    fn matches(
224        &self,
225        index: &IndexDescriptor,
226        capabilities: &CapabilityRegistry,
227        bindings: &mut Bindings,
228    ) -> bool {
229        match self {
230            Self::Any => true,
231            Self::Registered => capabilities.contains_index(index),
232            Self::Capable(required_capabilities) => required_capabilities
233                .0
234                .iter()
235                .all(|capability| capabilities.index_has(*capability, index)),
236            Self::Entity => capabilities.index_has(CapabilityIdentifier::Entity, index),
237            Self::Concrete(domain) => {
238                matches!(index, IndexDescriptor::Domain(candidate) if candidate == domain)
239            }
240            Self::Expanded { parent, child } => match index {
241                IndexDescriptor::Expanded {
242                    parent: parent_descriptor,
243                    child: child_descriptor,
244                } => {
245                    parent.matches(parent_descriptor, capabilities, bindings)
246                        && child.matches(child_descriptor, capabilities, bindings)
247                }
248                IndexDescriptor::Domain(_) | IndexDescriptor::ExpandedSource { .. } => false,
249            },
250            Self::Variable(variable, bound) => {
251                if !bound.matches(index, capabilities, bindings) {
252                    return false;
253                }
254                bindings.bind_index(*variable, index)
255            }
256        }
257    }
258}
259
260impl ValuePattern {
261    fn matches(
262        &self,
263        value: &ValueDescriptor,
264        capabilities: &CapabilityRegistry,
265        bindings: &mut Bindings,
266    ) -> bool {
267        match self {
268            Self::Registered => capabilities.contains_value(value),
269            Self::Concrete(descriptor) => value == descriptor,
270            Self::Capable(required_capabilities) => required_capabilities
271                .0
272                .iter()
273                .all(|capability| capabilities.value_has(*capability, value)),
274            Self::GroupKeyIs(key_pattern) => capabilities
275                .group_key(value)
276                .is_some_and(|key| key_pattern.matches(&key, capabilities, bindings)),
277            Self::IndexValue(index_pattern) => match value.role() {
278                ValueRole::Index(index) => index_pattern.matches(index, capabilities, bindings),
279                _ => false,
280            },
281            Self::EntityReference(index_pattern) => match value.role() {
282                ValueRole::EntityReference(index) => {
283                    index_pattern.matches(index, capabilities, bindings)
284                }
285                _ => false,
286            },
287            Self::Variable(variable, bound) => {
288                bound.matches(value, capabilities, bindings)
289                    && bindings.bind_value(*variable, value)
290            }
291        }
292    }
293}
294
295impl ShapePattern {
296    fn matches(
297        &self,
298        shape: &LaneShapeDescriptor,
299        capabilities: &CapabilityRegistry,
300        bindings: &mut Bindings,
301    ) -> bool {
302        match self {
303            Self::Any => match shape {
304                LaneShapeDescriptor::Indexed { .. } => true,
305                LaneShapeDescriptor::Bare { value } => {
306                    capabilities.value_has(CapabilityIdentifier::BareValue, value)
307                }
308            },
309            Self::Indexed { index, value } => match shape {
310                LaneShapeDescriptor::Indexed {
311                    index: index_descriptor,
312                    value: value_descriptor,
313                } => {
314                    index.matches(index_descriptor, capabilities, bindings)
315                        && value.matches(value_descriptor, capabilities, bindings)
316                }
317                LaneShapeDescriptor::Bare { .. } => false,
318            },
319            Self::Bare { value } => match shape {
320                LaneShapeDescriptor::Bare {
321                    value: value_descriptor,
322                } => value.matches(value_descriptor, capabilities, bindings),
323                LaneShapeDescriptor::Indexed { .. } => false,
324            },
325            Self::Variable(variable, bound) => {
326                bound.matches(shape, capabilities, bindings)
327                    && bindings.bind_shape(*variable, shape)
328            }
329        }
330    }
331}
332
333impl OrderPattern {
334    fn matches(&self, order: OrderDescriptor, bindings: &mut Bindings) -> bool {
335        match self {
336            Self::Any => true,
337            Self::Ordered => order == OrderDescriptor::Ordered,
338            Self::Unordered => order == OrderDescriptor::Unordered,
339            Self::Variable(variable, bound) => {
340                bound.matches(order, bindings) && bindings.bind_order(*variable, order)
341            }
342        }
343    }
344}
345
346impl ArityPattern {
347    fn matches(&self, arity: ArityDescriptor, bindings: &mut Bindings) -> bool {
348        match (self, arity) {
349            (
350                Self::Multiple(order),
351                ArityDescriptor::Multiple {
352                    order: order_descriptor,
353                },
354            ) => order.matches(order_descriptor, bindings),
355            (Self::Any, _)
356            | (Self::Single, ArityDescriptor::Single)
357            | (Self::Definite, ArityDescriptor::Definite) => true,
358            (Self::Variable(variable, bound), _) => {
359                bound.matches(arity, bindings) && bindings.bind_arity(*variable, arity)
360            }
361            _ => false,
362        }
363    }
364}
365
366impl StatePattern {
367    #[must_use]
368    pub fn matches(
369        &self,
370        operand: &OperandDescriptor,
371        capabilities: &CapabilityRegistry,
372    ) -> Option<Bindings> {
373        let mut bindings = Bindings::default();
374        self.matches_into(operand, capabilities, &mut bindings)
375            .then_some(bindings)
376    }
377
378    fn matches_into(
379        &self,
380        operand: &OperandDescriptor,
381        capabilities: &CapabilityRegistry,
382        bindings: &mut Bindings,
383    ) -> bool {
384        if let Self::Variable(variable, bound) = self {
385            return bound.matches_into(operand, capabilities, bindings)
386                && bindings.bind_operand(*variable, operand);
387        }
388
389        match (self, operand) {
390            (
391                Self::Lane { shape, arity },
392                OperandDescriptor::Lane {
393                    shape: shape_descriptor,
394                    arity: arity_descriptor,
395                },
396            ) => {
397                shape.matches(shape_descriptor, capabilities, bindings)
398                    && arity.matches(*arity_descriptor, bindings)
399            }
400            (
401                Self::Group {
402                    member,
403                    key,
404                    payload,
405                },
406                OperandDescriptor::Group {
407                    member: member_descriptor,
408                    key: key_descriptor,
409                    payload: payload_descriptor,
410                },
411            ) => {
412                member.matches(member_descriptor, capabilities, bindings)
413                    && key.matches(key_descriptor, capabilities, bindings)
414                    && payload.matches_into(payload_descriptor, capabilities, bindings)
415            }
416            _ => false,
417        }
418    }
419}
420
421impl AlignmentDescriptor {
422    fn admits(
423        &self,
424        argument: &ValueArgumentDescriptor,
425        capabilities: &CapabilityRegistry,
426        bindings: &mut Bindings,
427    ) -> bool {
428        if matches!(argument.value().role(), ValueRole::Unit) {
429            return false;
430        }
431
432        match argument.missing() {
433            ArgumentMissingPolicy::None => {
434                self.admits_source(argument.source(), capabilities, bindings)
435            }
436            ArgumentMissingPolicy::Drop => {
437                self.admits_lookup(argument.source(), capabilities, bindings)
438            }
439            ArgumentMissingPolicy::Replace(replacement) => {
440                self.admits_lookup(argument.source(), capabilities, bindings)
441                    && self.admits_source(replacement, capabilities, bindings)
442            }
443        }
444    }
445
446    fn admits_source(
447        &self,
448        source: &ArgumentValueSource,
449        capabilities: &CapabilityRegistry,
450        bindings: &mut Bindings,
451    ) -> bool {
452        let ArgumentValueSource::Operand(operand) = source else {
453            return true;
454        };
455        let OperandDescriptor::Lane { shape, arity } = operand else {
456            return false;
457        };
458
459        match (self, shape, arity) {
460            (
461                Self::Keyed(pattern),
462                LaneShapeDescriptor::Indexed { index, .. },
463                ArityDescriptor::Multiple { .. },
464            ) => pattern.matches(index, capabilities, bindings),
465            (
466                _,
467                LaneShapeDescriptor::Bare { .. },
468                ArityDescriptor::Single | ArityDescriptor::Definite,
469            ) => true,
470            _ => false,
471        }
472    }
473
474    fn admits_lookup(
475        &self,
476        source: &ArgumentValueSource,
477        capabilities: &CapabilityRegistry,
478        bindings: &mut Bindings,
479    ) -> bool {
480        let ArgumentValueSource::Operand(OperandDescriptor::Lane { shape, arity }) = source else {
481            return false;
482        };
483
484        match (self, shape, arity) {
485            (
486                Self::Keyed(pattern),
487                LaneShapeDescriptor::Indexed { index, .. },
488                ArityDescriptor::Multiple { .. },
489            ) => pattern.matches(index, capabilities, bindings),
490            (Self::Unaligned, LaneShapeDescriptor::Bare { .. }, ArityDescriptor::Single) => true,
491            _ => false,
492        }
493    }
494}
495
496impl RetentionPattern {
497    fn matches(self, retention: RetentionDescriptor) -> bool {
498        match self {
499            Self::Any => true,
500            Self::Fixed(required) => required == retention,
501        }
502    }
503}
504
505impl ArgumentPattern {
506    #[must_use]
507    pub fn field<T: 'static>() -> Self {
508        Self::Field(DomainDescriptor::of::<T>())
509    }
510
511    #[must_use]
512    pub fn selector<T: 'static>() -> Self {
513        Self::Selector(DomainDescriptor::of::<T>())
514    }
515
516    pub(super) fn matches(
517        &self,
518        argument: &ArgumentDescriptor,
519        capabilities: &CapabilityRegistry,
520        bindings: &mut Bindings,
521    ) -> bool {
522        match (self, argument) {
523            (
524                Self::Value {
525                    value,
526                    alignment,
527                    retention,
528                },
529                ArgumentDescriptor::Value(descriptor),
530            ) => {
531                if !retention.matches(descriptor.retention())
532                    || !alignment.admits(descriptor, capabilities, bindings)
533                    || !Self::matches_value(value, descriptor, capabilities, bindings)
534                {
535                    return false;
536                }
537
538                bindings.compose_retention(descriptor.retention());
539
540                true
541            }
542            (Self::Set(value), ArgumentDescriptor::Value(descriptor)) => {
543                matches!(descriptor.missing(), ArgumentMissingPolicy::None)
544                    && Self::admits_set_source(descriptor.source())
545                    && value.matches(descriptor.value(), capabilities, bindings)
546            }
547            (Self::Field(pattern), ArgumentDescriptor::Field(field)) => pattern == field,
548            (Self::Selector(pattern), ArgumentDescriptor::Selector(selector)) => {
549                pattern == selector
550            }
551            (Self::Operand(pattern), ArgumentDescriptor::Operand(operand)) => {
552                pattern.matches_into(operand, capabilities, bindings)
553            }
554            _ => false,
555        }
556    }
557
558    const fn admits_set_source(source: &ArgumentValueSource) -> bool {
559        match source {
560            ArgumentValueSource::Literal(_) => true,
561            ArgumentValueSource::Operand(operand) => {
562                matches!(operand, OperandDescriptor::Lane { .. })
563            }
564        }
565    }
566
567    fn matches_value(
568        pattern: &ValuePattern,
569        argument: &ValueArgumentDescriptor,
570        capabilities: &CapabilityRegistry,
571        bindings: &mut Bindings,
572    ) -> bool {
573        if !pattern.matches(argument.value(), capabilities, bindings) {
574            return false;
575        }
576
577        let ArgumentMissingPolicy::Replace(replacement) = argument.missing() else {
578            return true;
579        };
580
581        pattern.matches(replacement.value(), capabilities, bindings)
582    }
583}