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}