Source code for flexicon.code.Grammar.compound_rule_collection
#
# compound_rule_collection.py
#
# Class: CompoundRuleCollection
# Smart collection for compound rules with type-aware filtering
# and unified access across multiple concrete types.
#
# Platform: Python.NET
# FieldWorks Version 9+
#
# Copyright 2025
#
"""
Smart collection class for compound rules.
This module provides CompoundRuleCollection, a smart collection that manages
compound rules while showing type diversity and supporting unified operations
across the two concrete types:
- MoEndoCompound
- MoExoCompound
Problem:
GetAll() returns objects with multiple concrete implementations. Users
need to:
- See which types are in the collection
- Filter by type if they want to
- Work with all types together without manual casting
- Filter by common properties that work across all types
Solution:
CompoundRuleCollection provides:
- __str__() showing type breakdown
- by_type() filtering to specific concrete types
- filter() for common criteria (name_contains, head_dependency)
- Convenience methods (endo_compounds(), exo_compounds())
- Chainable filtering: rules.endo_compounds().filter(name_contains='V')
- where() for custom predicates
Example::
from flexlibs2.code.Grammar.compound_rule_collection import CompoundRuleCollection
# GetAll() now returns CompoundRuleCollection
rules = morphRuleOps.GetAll()
print(rules) # Shows type breakdown
# CompoundRuleCollection (8 total)
# MoEndoCompound: 5 (62%)
# MoExoCompound: 3 (38%)
# Filter by type
endo_only = rules.endo_compounds()
print(len(endo_only)) # 5
# Filter by name
verb_rules = rules.filter(name_contains='Verb')
# Chain filters
verb_endo = rules.endo_compounds().filter(name_contains='Verb')
# Iterate
for rule in rules:
print(rule.name)
"""
from ..Shared.smart_collection import SmartCollection
from .compound_rule import CompoundRule
[docs]
class CompoundRuleCollection(SmartCollection):
"""
Smart collection for compound rules with type-aware filtering.
Manages collections of compound rules (CompoundRule wrapper objects)
with type-aware display and filtering capabilities. Supports filtering by
common properties (name, head_dependency) and by concrete type.
Attributes:
_items: List of CompoundRule wrapper objects
Example::
rules = morphRuleOps.GetAll() # Returns CompoundRuleCollection
print(rules) # Shows type breakdown
endo = rules.endo_compounds() # Filter to MoEndoCompound
verb = rules.filter(name_contains='Verb') # Name filter
both = rules.endo_compounds().filter(name_contains='Verb') # Chain
"""
def __init__(self, items=None):
"""
Initialize a CompoundRuleCollection.
Args:
items: Iterable of CompoundRule objects, or None for empty.
Example::
collection = CompoundRuleCollection()
collection = CompoundRuleCollection([rule1, rule2, rule3])
"""
super().__init__(items)
[docs]
def filter(self, name_contains=None, head_dependency=None, where=None):
"""
Filter the collection by common rule properties.
Supports filtering by properties that work across all rule types
(name, head_dependency). For complex filtering, use where().
Args:
name_contains (str, optional): Filter to rules whose name contains
this string (case-sensitive).
head_dependency (int, optional): Filter by head dependency value.
where (callable, optional): Custom predicate function. If provided,
other criteria are ignored.
Returns:
CompoundRuleCollection: New collection with filtered items.
Example::
# Filter by name
verb_rules = rules.filter(name_contains='Verb')
# Filter by head dependency
lhs_head = rules.filter(head_dependency=0)
# Custom filtering
with_contexts = rules.where(lambda r: r.left_context or r.right_context)
# Chain filters
verb_lhs = rules.filter(name_contains='Verb').filter(head_dependency=0)
Notes:
- name_contains is case-sensitive
- All criteria are AND-ed together unless only one is provided
- where() takes precedence over other criteria
- Returns new collection (doesn't modify original)
- Use where() for complex custom filtering
"""
# If custom predicate provided, use it
if where is not None:
filtered = [item for item in self._items if where(item)]
return CompoundRuleCollection(filtered)
# Apply criteria filters
filtered = self._items
if name_contains is not None:
filtered = [rule for rule in filtered if name_contains in (rule.name or "")]
if head_dependency is not None:
filtered = [rule for rule in filtered if rule.head_dependency == head_dependency]
return CompoundRuleCollection(filtered)
[docs]
def where(self, predicate):
"""
Filter using a custom predicate function.
For filtering by complex criteria or properties not supported by filter().
Args:
predicate (callable): Function that takes a CompoundRule and
returns True to include it in the result.
Returns:
CompoundRuleCollection: New collection with items matching the predicate.
Example::
# Complex predicate: rules with both left and right contexts
full_context = rules.where(
lambda r: r.left_context is not None and r.right_context is not None
)
# Combining conditions
endo_with_context = rules.where(
lambda r: r.is_endo_compound and (r.left_context or r.right_context)
)
# Type checking combined with other criteria
exo_rules = rules.where(lambda r: r.is_exo_compound)
Notes:
- Predicate receives each CompoundRule object
- Return True to include in result, False to exclude
- For simple filters (name, head_dependency), use filter() instead
"""
filtered = [rule for rule in self._items if predicate(rule)]
return CompoundRuleCollection(filtered)
[docs]
def endo_compounds(self):
"""
Get only the endocentric (MoEndoCompound) rules from the collection.
Convenience method for filtering to MoEndoCompound objects only.
Returns:
CompoundRuleCollection: New collection with only MoEndoCompound objects.
Example::
endo = rules.endo_compounds()
print(f"Found {len(endo)} endocentric rules")
# Chain with other filters
verb_endo = rules.endo_compounds().filter(name_contains='Verb')
Notes:
- Equivalent to by_type('MoEndoCompound')
- Endo compounds have head internal to the compound
- Use is_endo_compound on individual rules to check type
"""
return self.by_type("MoEndoCompound")
[docs]
def exo_compounds(self):
"""
Get only the exocentric (MoExoCompound) rules from the collection.
Convenience method for filtering to MoExoCompound objects only.
Returns:
CompoundRuleCollection: New collection with only MoExoCompound objects.
Example::
exo = rules.exo_compounds()
print(f"Found {len(exo)} exocentric rules")
# Chain with other filters
noun_exo = rules.exo_compounds().filter(name_contains='Noun')
Notes:
- Equivalent to by_type('MoExoCompound')
- Exo compounds have head external to the compound
- Use is_exo_compound on individual rules to check type
"""
return self.by_type("MoExoCompound")
def __repr__(self):
"""Technical representation."""
return f"CompoundRuleCollection({len(self._items)} rules)"