diff --git a/masque/library.py b/masque/library.py index 1240b48..b730fba 100644 --- a/masque/library.py +++ b/masque/library.py @@ -1063,6 +1063,25 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta): return self + def resolve_repeated_refs(self, name: str | None = None) -> Self: + """ + Expand all repeated references into multiple individual references. + Alters the library in-place. + + Args: + name: If specified, only resolve repeated refs in this pattern. + Otherwise, resolve in all patterns. + + Returns: + self + """ + if name is not None: + self[name].resolve_repeated_refs() + else: + for pat in self.values(): + pat.resolve_repeated_refs() + return self + def subtree( self, tops: str | Sequence[str], diff --git a/masque/pattern.py b/masque/pattern.py index 9e15910..ea8fdd5 100644 --- a/masque/pattern.py +++ b/masque/pattern.py @@ -976,6 +976,28 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): del self.labels[layer] return self + def resolve_repeated_refs(self) -> Self: + """ + Expand all repeated references into multiple individual references. + Alters the current pattern in-place. + + Returns: + self + """ + new_refs: defaultdict[str | None, list[Ref]] = defaultdict(list) + for target, rseq in self.refs.items(): + for ref in rseq: + if ref.repetition is None: + new_refs[target].append(ref) + else: + for dd in ref.repetition.displacements: + new_ref = ref.deepcopy() + new_ref.offset = ref.offset + dd + new_ref.repetition = None + new_refs[target].append(new_ref) + self.refs = new_refs + return self + def prune_refs(self) -> Self: """ Remove empty ref lists in `self.refs`.