From f875ae89d744059a36383f47c1e60faa115a9bdc Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 15 Apr 2018 16:34:52 -0700 Subject: [PATCH] make sure apply() only hits each pattern one --- masque/pattern.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/masque/pattern.py b/masque/pattern.py index 191ba5f..927c6b5 100644 --- a/masque/pattern.py +++ b/masque/pattern.py @@ -98,16 +98,30 @@ class Pattern: func: Callable[['Pattern'], 'Pattern'] ) -> 'Pattern': """ - Recursively apply func() to this pattern and its subpatterns. + Recursively apply func() to this pattern and any pattern it references. func() is expected to take and return a Pattern. - func() is first applied to the pattern as a whole, then the subpatterns. + func() is first applied to the pattern as a whole, then the referenced patterns. + It is only applied to any given pattern once, regardless of how many times it is + referenced. :param func: Function which accepts a Pattern, and returns a pattern. :return: The result of applying func() to this pattern and all subpatterns. + :raises: PatternError if called on a pattern containing a circular reference. """ + pat_map = {id(self): None} pat = func(self) + pat_map[id(self)] = pat + for subpat in pat.subpatterns: - subpat.pattern = subpat.pattern.apply(func) + ref_pat_id = id(subpat.pattern) + if ref_pat_id not in pat_map: + pat_map[ref_pat_id] = None + subpat.pattern = subpat.pattern.apply(func) + pat_map[ref_pat_id] = subpat.pattern + elif pat_map[ref_pat_id] is None: + raise PatternError('.apply() called on pattern with circular reference') + else: + subpat.pattern = pat_map[ref_pat_id] return pat def polygonize(self,