Code clarification and commenting for interpolation

Signed-off-by: martin f. krafft <madduck@madduck.net>
diff --git a/reclass/datatypes/parameters.py b/reclass/datatypes/parameters.py
index 26b7e74..ccfb6fe 100644
--- a/reclass/datatypes/parameters.py
+++ b/reclass/datatypes/parameters.py
@@ -43,7 +43,7 @@
             delimiter = Parameters.DEFAULT_PATH_DELIMITER
         self._delimiter = delimiter
         self._base = {}
-        self._refs = {}
+        self._occurrences = {}
         if mapping is not None:
             # we initialise by merging, otherwise the list of references might
             # not be updated
@@ -68,9 +68,6 @@
     def as_dict(self):
         return self._base.copy()
 
-    def _register_ref_occurrence(self, path, ref):
-        self._refs[path] = ref
-
     def _update_scalar(self, cur, new, path):
         if self.delimiter is None or not isinstance(new, types.StringTypes):
             return new
@@ -82,7 +79,11 @@
                 # references
                 return new
 
-            self._register_ref_occurrence(path, ret)
+            # finally, keep a reference to the RefValue instance we just
+            # created, in a dict indexed by the dictionary path, instead of
+            # just a list. The keys are required to resolve dependencies
+            # during interpolation
+            self._occurrences[path] = ret
             return ret
 
     def _extend_list(self, cur, new, path):
@@ -138,37 +139,59 @@
         elif isinstance(other, self.__class__):
             self._base = self._merge_recurse(self._base, other._base,
                                              None)
-            self._refs.update(other._refs)
+            self._occurrences.update(other._occurrences)
 
         else:
             raise TypeError('Cannot merge %s objects into %s' % (type(other),
                             self.__class__.__name__))
 
+    def has_unresolved_refs(self):
+        return len(self._occurrences) > 0
+
     def interpolate(self):
-        while len(self._refs) > 0:
-            path, refvalue = self._refs.iteritems().next()
+        while self.has_unresolved_refs():
+            # we could use a view here, but this is simple enough:
+            # _interpolate_inner removes references from the refs hash after
+            # processing them, so we cannot just iterate the dict
+            path, refvalue = self._occurrences.iteritems().next()
             self._interpolate_inner(path, refvalue)
 
     def _interpolate_inner(self, path, refvalue):
-        self._refs[path] = None
+        self._occurrences[path] = True  # mark as seen
         for ref in refvalue.get_references():
             path_from_ref = DictPath(self.delimiter, ref)
             try:
-                refvalue_inner = self._refs[path_from_ref]
-                if refvalue_inner is None:
+                refvalue_inner = self._occurrences[path_from_ref]
+
+                # If there is no reference, then this will throw a KeyError,
+                # look further down where this is caught and execution passed
+                # to the next iteration of the loop
+                #
+                # If we get here, then the ref references another parameter,
+                # requiring us to recurse, dereferencing first those refs that
+                # are most used and are thus at the leaves of the dependency
+                # tree.
+
+                if refvalue_inner is True:
                     # every call to _interpolate_inner replaces the value of
-                    # the saved occurrences of a reference with None.
-                    # Therefore, if we encounter None instead of a refvalue,
+                    # the saved occurrences of a reference with True.
+                    # Therefore, if we encounter True instead of a refvalue,
                     # it means that we have already processed it and are now
                     # faced with a cyclical reference.
                     raise InfiniteRecursionError(path, ref)
                 self._interpolate_inner(path_from_ref, refvalue_inner)
+
             except KeyError as e:
-                pass
+                # not actually an error, but we are done resolving all
+                # dependencies of the current ref, so move on
+                continue
+
         try:
             new = refvalue.render(self._base)
             path.set_value(self._base, new)
-            del self._refs[path]
+
+            # finally, remove the reference from the occurrences cache
+            del self._occurrences[path]
         except UndefinedVariableError as e:
             raise UndefinedVariableError(e.var, path)