commit:     862594b151befc3ac3e1766df370890c8af88470
Author:     André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Thu Jul 17 16:26:32 2014 +0000
Commit:     André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Thu Jul 17 16:26:32 2014 +0000
URL:        
http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=862594b1

add-policy rule-gen: implement token generatorion

---
 .../packagerules/generators/addition_control.py    | 883 +++++++--------------
 1 file changed, 279 insertions(+), 604 deletions(-)

diff --git a/roverlay/packagerules/generators/addition_control.py 
b/roverlay/packagerules/generators/addition_control.py
index 73040e0..7f2cc95 100644
--- a/roverlay/packagerules/generators/addition_control.py
+++ b/roverlay/packagerules/generators/addition_control.py
@@ -1,684 +1,342 @@
-# R overlay -- package rule generators, addition control
+# R overlay -- abstract package rule generators, addition control
 # -*- coding: utf-8 -*-
 # Copyright (C) 2014 André Erdmann <d...@mailerd.de>
 # Distributed under the terms of the GNU General Public License;
 # either version 2 of the License, or (at your option) any later version.
 
 from __future__ import absolute_import
-# temporary_demo_func():
-from __future__ import print_function
 
 
 import abc
-# __metaclass__/metaclass= workaround
-_AbstractObject = abc.ABCMeta ( str("AbstractObject"), ( object, ), {} )
-
 import collections
+import fnmatch
+import re
+
+
+import roverlay.packagerules.generators.abstract.addition_control
 
-#~ import roverlay.util.fileio
-import roverlay.util.namespace
-#~
 import roverlay.packagerules.abstract.acceptors
-import roverlay.packagerules.abstract.rules
-#~ import roverlay.packagerules.acceptors.stringmatch
-#~ import roverlay.packagerules.acceptors.util
-import roverlay.packagerules.acceptors.trivial
-import roverlay.packagerules.actions.addition_control
-#~
-#~ from roverlay.packagerules.abstract.rules import (
-   #~ NestedPackageRule, PackageRule
-#~ )
-#~
-#~ from roverlay.packagerules.acceptors.stringmatch (
-   #~ NocaseStringAcceptor, StringAcceptor,
-   #~ RegexAcceptor, ExactRegexAcceptor
-#~ )
-#~
-#~ from roverlay.packagerules.acceptors.util import (
-   #~ get_package, get_package_name, get_ebuild_name, get_category,
-   #~ DEFAULT_CATEGORY_REPLACEMENT
-#~ )
-
-import roverlay.overlay.abccontrol
-from roverlay.overlay.abccontrol import AdditionControlResult
-
-
-# converting addition-control lists (cmdline, from file...) to rule objects,
-# hacky solution:
-#
-# ** step 1 ** -- collect category/package tokens, determine bitmask
-#
-#  create a dict (
-#     category_token|True => package_token|True => bitmask<policy>
-#     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^    ^~~~~~~~~~~~~~^
-#               "acceptor chain"                    "add-policy"
-#  )
-#
-#   "True" means "accept all".
-#
-#   make sure that the tokens get de-duped
-#    (normalize values, use tuples (or namespace))
-#
-#   Only the first and the last step need to know *what* category/package
-#   tokens are. All intermediate steps should not need to care about this.
-#
-
-
-
-class AbstractAdditionControlPackageRuleGenerator ( _AbstractObject ):
-   """(Abstract) object that takes cmdline/files as input and creates
-   add-policy package rules."""
-
-   # Note that tokens are not totally abstract,
-   #  the "match-all" (True) token is hardcoded
-
-   #CategoryToken = collections.namedtuple ( 'CategoryToken', '<attr>*' )
-   #PackageToken  = collections.namedtuple ( 'PackageToken',  '<attr>*' )
-
-   @abc.abstractmethod
-   def category_token_to_acceptor ( self, category_token, priority ):
-      """Creates a package rule acceptor for the given category token.
+import roverlay.packagerules.acceptors.stringmatch
+import roverlay.packagerules.acceptors.util
 
-      Returns: not-None acceptor (or nested acceptor)
 
-      Must not return None.
-      If a token is meaningless, then don't create it in the first place.
+from roverlay.packagerules.abstract.acceptors import Acceptor_AND
 
-      arguments:
-      * category_token -- a category token
-      * priority       -- priority of the acceptor (int)
-      """
-      raise NotImplementedError()
-   # --- end of category_token_to_acceptor (...) ---
+from roverlay.packagerules.acceptors.stringmatch import (
+   StringAcceptor, ExactRegexAcceptor,
+)
 
-   @abc.abstractmethod
-   def package_token_to_acceptor ( self, package_token, priority ):
-      """Creates a package rule acceptor for the given package token.
+from roverlay.packagerules.acceptors.util import (
+   get_category, get_ebuild_name, get_ebuild_version_tuple,
+)
 
-      Returns: not-None acceptor (or nested acceptor)
+#import roverlay.util.fileio
+import roverlay.util.namespace
 
-      arguments:
-      * package_token -- a package token
-      * priority      -- priority of the acceptor (int)
-      """
-      raise NotImplementedError()
-   # --- end of package_token_to_acceptor (...) ---
+import roverlay.util.portage_regex.default
+import roverlay.util.portage_regex.wildcard
+from roverlay.util.portage_regex.default  import RE_PACKAGE_EBUILD_FILE
+from roverlay.util.portage_regex.wildcard import (
+   RE_WILDCARD_PACKAGE, RE_WILDCARD_CATEGORY
+)
 
-   def create_package_rules ( self, reduced_bitmask_acceptor_chain_map ):
-      """Creates a nested add-policy package rule object.
-      The rule object's priority has to be set manually afterwards.
-
-      Returns: (nested) package rule or None
-      """
-      # create_package_rules() is defined/implemented below (step 5)
-      return create_package_rules (
-         reduced_bitmask_acceptor_chain_map,
-         convert_category_token_to_acceptor = self.category_token_to_acceptor,
-         convert_package_token_to_acceptor  = self.package_token_to_acceptor
-      )
-   # --- end of create_package_rules (...) ---
 
-   def prepare_bitmask_map ( self, acceptor_chain_bitmask_map ):
-      """Transforms the given "acceptor chain" -> "bitmask" map into the
-      reduced "effective bitmask" -> "acceptor chain" map.
 
-      Note: Involves in-place operations that modify
-            acceptor_chain_bitmask_map.
-            Pass a copy if the original map should remain unchanged.
 
-      Returns: reduced/optimized "effective bitmask" -> "acceptor chain"
 
-      arguments:
-      * acceptor_chain_bitmask_map -- "acceptor chain" -> "bitmask" map
-      """
-      expand_acceptor_chain_bitmasks ( acceptor_chain_bitmask_map )
 
-      bitmask_acceptor_chain_map = (
-         create_bitmask_acceptor_chain_map ( acceptor_chain_bitmask_map )
-      )
 
-      reduce_bitmask_acceptor_chain_map ( bitmask_acceptor_chain_map )
 
-      return bitmask_acceptor_chain_map
-   # --- end of prepare_bitmask_map (...) ---
 
-   def compile_bitmask_map ( self, acceptor_chain_bitmask_map ):
-      """Transforms the given "acceptor chain" -> "bitmask" map into a
-      (nested) package rule.
 
-      This is equal to calling
-         obj.create_package_rules (
-            obj.prepare_bitmask_map ( acceptor_chain_bitmask_map )
-         )
+class TokenValueError ( ValueError ):
+   pass
 
-      Returns: (nested) rule object or None
+class InvalidTokenInputString ( TokenValueError ):
+   pass
 
-      arguments:
-      * acceptor_chain_bitmask_map -- "acceptor chain" -> "bitmask" map
-      """
-      return self.create_package_rules (
-         prepare_bitmask_map ( acceptor_chain_bitmask_map )
-      )
-   # --- end of compile_bitmask_map (...) ---
-
-
-# --- end of AbstractAdditionControlPackageRuleGenerator ---
-
-
-#
-# ** step 2 ** -- expand global and category-wide bitmasks,
-#                 set effective package bitmask
-#                 (FIXME: does it make sense to apply the global bitmask?)
-#
-#  for each category with at least one non-True package_token loop
-#    for each package in category loop
-#       category->package |= category->True  [bitwise-OR]
-#    end loop
-#    (do not modify category->True, as it applies to packages not matched
-#     by any package_token, too)
-#  end loop
-#
-#  for each category loop
-#     for each entry in category loop
-#        reduce policy bitmask (keep effective bits only)
-#     end loop
-#  end loop
-#
-#  (merge the two for-loops in code)
-#
-
-def expand_acceptor_chain_bitmasks ( acceptor_chain_bitmask_map ):
-   """Expands a "acceptor chain" -> "bitmask" map.
-   (Sets the effective bitmask, propagates global bitmasks, ...)
-
-   In-place operation that modifies the acceptor_chain_bitmask_map arg.
-
-   Returns: None (implicit)
-
-   arguments:
-   * acceptor_chain_bitmask_map -- "acceptor chain" -> "bitmask" map
-   """
-   # naming convention: >e<ffective bit>mask< => emask
-
-   get_emask = AdditionControlResult.get_effective_package_policy
-
-   def normalize_entry ( mapping, key, additional_emask=0 ):
-      """Determines and sets the effective bitmask of mapping->key.
-
-      Returns: None (implicit)
-
-      arguments:
-      * mapping          -- dict-like object
-      * key              -- dict key (has to exist)
-      * additional_emask -- effective bitmask that should be propagated to
-                            mapping->key (global/category-wide bitmask)
-      """
-      new_value     = get_emask ( mapping [key] | additional_emask )
-      mapping [key] = new_value
-      return new_value
-   # --- end of normalize_entry (...) ---
-
-   def normalize_entry_maybe_missing ( mapping, key, additional_emask=0 ):
-      """Like normalize_entry(), but does not require the existence of
-      mapping->key (the entry will be created if necessary).
-
-      arguments:
-      * mapping          --
-      * key              --
-      * additional_emask --
-      """
-      if key in mapping:
-         return normalize_entry ( mapping, key, additional_emask )
-      else:
-         mapping [key] = additional_emask
-         return additional_emask
-   # --- end of normalize_entry_maybe_missing (...) ---
+class TokenItemNotSupported ( TokenValueError ):
+   pass
 
-   # propagate global/category-wide emask to package_token entries
 
-   # acceptor_chain_bitmask_map->True->True is the global emask
-   if True in acceptor_chain_bitmask_map:
-      global_emask = normalize_entry_maybe_missing (
-         acceptor_chain_bitmask_map [True], True
-      )
-   else:
-      global_emask = 0
 
 
-   for category_token, package_token_map in acceptor_chain_bitmask_map.items():
-      # --- cannot modify acceptor_chain_bitmask_map in this block
 
-      category_emask = normalize_entry_maybe_missing (
-         package_token_map, True, global_emask
-      )
+class AdditionControlPackageRuleGenerator (
+   roverlay.packagerules.generators.abstract.addition_control.\
+      AbstractAdditionControlPackageRuleGenerator
+):
 
-      for package_token in package_token_map:
-         # --- cannot modify acceptor_chain_bitmask_map, package_token_map
-         if package_token is not True:
-            # else already processed (category_emask)
-            normalize_entry (
-               package_token_map, package_token, category_emask
-            )
-         # -- end if <package_token>
-      # -- end for <package_token>
-
-   # -- end for <category_token,package_token_map>
-
-# --- end of expand_acceptor_chain_bitmasks (...) ---
-
-
-#
-# ** step 3 -- create reversed map **
-#
-#  BITMASK_MAP: create a dict (
-#     effective_bitmask => category_token|True => set(package_token|True>)
-#     ^~~~~~~~~~~~~~~~^    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
-#     "add-policy atom"                 "acceptor chain"
-#  )
-#
-#
-#  **hacky**:
-#   It would be better to split effective_bitmask into its components
-#   (2**k for k in ...), but this requires path compaction.
-#   Not implemented. (graph/spanning-tree and/or logic minimization)
-#
-
-def create_bitmask_acceptor_chain_map ( acceptor_chain_bitmask_map ):
-   """Transforms a "acceptor chain" -> "[effective] bitmask" map
-   into a "[effective] bitmask" -> "acceptor chain" mask without applying
-   any optimization/reduction steps.
-
-   Returns: "[effective] bitmask" -> "acceptor chain" map
-
-   arguments:
-   * acceptor_chain_bitmask_map -- "acceptor chain" -> "bitmask" map
-                                   Should be in expanded form
-                                   (expand_acceptor_chain_bitmasks())
-   """
-   bitmask_acceptor_chain_map = {}
+   #TOKEN_ITEM_IS_SPECIAL = 0 ## implicit
+   TOKEN_ITEM_IS_STR      = 1
+   TOKEN_ITEM_IS_REGEX    = 2
 
-   for category_token, package_token_map in acceptor_chain_bitmask_map.items():
+   TokenItemTuple = collections.namedtuple (
+      "TokenItemTuple", "item_type value"
+   )
 
-      for package_token, emask in package_token_map.items():
-         try:
-            emask_entry = bitmask_acceptor_chain_map [emask]
-         except KeyError:
-            bitmask_acceptor_chain_map [emask] = {
-               category_token: set ({ package_token, })
-            }
-         else:
-            try:
-               category_entry = emask_entry [category_token]
-            except KeyError:
-               emask_entry [category_token] = set ({ package_token, })
-            else:
-               category_entry.add ( package_token )
-      # -- end for <package token map>
-
-   # -- end for <category token map>
-
-   return bitmask_acceptor_chain_map
-# --- end of create_bitmask_acceptor_chain_map (...) ---
-
-
-#
-# ** step 4 -- naive path compaction **
-#   (Keep in mind that acceptor tokens cannot be merged with match-all)
-#
-#   reduce acceptor chains (drop-superseeded):
-#   for each effective_bitmask b in BITMASK_MAP loop
-#      if BITMASK_MAP->b->True->True exists then
-#         drop all other entries from BITMASK_MAP->b
-#      else
-#         for each category in BITMASK_MAP->b loop
-#            if category->True exists then
-#               drop all other entries from category
-#            end if
-#         end loop
-#      end if
-#   end loop
-#
-#   (optional: drop overlapping acceptor chains, e.g. regex ".*" is equal
-#    to True and therefore all other entries in the regex' branch can be
-#    removed)
-#
-#
-#   merge-bitmask: (OPTIONAL / NOT IMPLEMENTED)
-#    (bitwise-OR effective_bitmasks with identical acceptor chains)
-#
-#    *could* create a large table
-#      <add-policy atom X category/package> => bool
-#       (where category, package can also be "accept-all")
-#
-#
-#    +-----------------+-------+-------+-----+-------+-------+-----+-------+
-#    | add-policy atom | c0/p0 | c0/p1 | ... | c0/pM | c1/p0 | ... | cN/pJ |
-#    +=================+=======+=======+=====+=======+=======+=====+=======+
-#    | 2**0            | 0|1   | 0|1   | ... | 0|1   | 0|1   | ... | 0|1   |
-#    +-----------------+-------+-------+-----+-------+-------+-----+-------+
-#    | ...             | ...   | ...   | ... | ...   | ...   | ... | ...   |
-#    +-----------------+-------+-------+-----+-------+-------+-----+-------+
-#    | 2**k            | 0|1   | 0|1   | ... | 0|1   | 0|1   | ... | 0|1   |
-#    +-----------------+-------+-------+-----+-------+-------+-----+-------+
-#
-#   ++ reduce table
-#
-
-def reduce_bitmask_acceptor_chain_map ( bitmask_acceptor_chain_map ):
-   """Reduces/Optimizes a "effective bitmask" -> "acceptor chain" map.
-
-   In-place operation, the bitmask_acceptor_chain_map will be modified.
-
-   Returns: None (implicit)
-
-   arguments:
-   * bitmask_acceptor_chain_map -- "effective bitmask" -> "acceptor chain" map
-
-   Implementation detail:
-   The reduced map uses empty sets/dicts for representing "match-all"
-   acceptors.
-   """
+   # PackageToken: tuple ( None|$PN, None|$PV, None|$PR )
+   #  (revision not implemented because it cannot be matched in package rules
+   #   -- always 0)
+   #  condition: not all ( item is None for item in <PackageToken> )
+   #              and all not-None items must evaluate to True
+   #             <=> any ( <PackageToken> )
+   #
+   PackageToken  = collections.namedtuple ( 'PackageToken', 'name version' )
 
-   # could be integrated in create_bitmask_acceptor_chain_map(),
-   #  but kept separate for better readability
+   # CategoryToken: tuple ( $CATEGORY )
+   #  same conditions as for PackageToken apply,
+   #   which effectively means that $CATEGORY must a non-empty str
    #
+   CategoryToken = collections.namedtuple ( 'CategoryToken', 'name' )
 
-   # emask==0 can be ignored
-   bitmask_acceptor_chain_map.pop ( 0, True )
-
-   for emask in bitmask_acceptor_chain_map:
-      emask_matches_all = False
-
-      for category_token, package_token_set in (
-         bitmask_acceptor_chain_map [emask].items()
-      ):
-         if True not in package_token_set:
-            # category~DONT_CARE, package!=True <=> keep entries
-            pass
-         elif category_token is True:
-            # category==True, package==True <=> match all, *** BREAK LOOP ***
-            emask_matches_all = True
-            break
-         else:
-            # category!=True, package==True <=> match entire category
-            package_token_set.clear()
-      # -- end for <category->package token map>
-
-      if emask_matches_all:
-         bitmask_acceptor_chain_map [emask].clear()
-   # -- end for <emask>
-# --- end of reduce_bitmask_acceptor_chain_map (...) ---
-
-
-#
-# ** step 5 -- convert the acceptor chains to objects **
-#
-#   the final BITMASK_MAP can be easily translated into rule objects:
-#
-#   * convert the the effective bitmask into one or more
-#      PackageAdditionControl*Actions
-#
-#   * an OR<category OR<packages>> match statement
-#      represents the acceptor chain
-#      (reduce properly, e.g. "accept-all" entries)
-#
-#
-# ** done **
-#
-
-def create_packagerule_action_map():
-   """Helper function that creates all add-policy package rule actions.
-
-   Returns: dict ( "bitmask atom" (2**k) -> "package rule action" )
-
-   arguments: None
-   """
-   return {
-      action_cls.CONTROL_RESULT: action_cls (
-         priority=action_cls.CONTROL_RESULT
-      )
-         for action_cls in (
-            getattr (
-               roverlay.packagerules.actions.addition_control,
-               action_cls_name
-            ) for action_cls_name in (
-               roverlay.packagerules.actions.addition_control.ACTIONS
-            )
-         ) if action_cls.CONTROL_RESULT
-   }
-# --- end of create_packagerule_action_map (...) ---
 
+   def create_token_item_from_str ( self, s ):
+      get_obj = self.namespace.get_object_v
 
-def create_package_rules (
-   reduced_bitmask_acceptor_chain_map,
-   convert_category_token_to_acceptor,
-   convert_package_token_to_acceptor
-):
-   """Converts the given "effective bitmask" -> "acceptor chain" map
-   into a nested package rule.
-
-   Returns: (nested) package rule or None
-
-   arguments:
-   * reduced_bitmask_acceptor_chain_map -- reduced/optimized
-                                           "bitmask" -> "acceptor chain" map
-   * convert_category_token_to_acceptor -- function(token,priority)
-                                             -> category acceptor
-   * convert_package_token_to_acceptor  -- function(token,priority)
-                                             -> package acceptor
-   """
-   packagerule_actions    = create_packagerule_action_map()
-   # true acceptor with priority -1
-   always_true_acceptor   = (
-      roverlay.packagerules.acceptors.trivial.TrueAcceptor ( priority=-1 )
-   )
+      if not s:
+         return None
 
+      elif any ( char in "?*" for char in s ):
+         s_stripped = s.strip("*")
+         if not s_stripped or (
+            s_stripped == "?" and s_stripped != s
+         ):
+            # "****", "?*", "*?", ... => match all
+            return True
 
-   def get_acceptor_recursive ( category_token_map, priority ):
-      """
-      Creates a (possibly nested) acceptor for the given category_token_map.
-
-      Returns: not-None acceptor
-
-      arguments:
-      * category_token_map -- "category token" -> "package token" map
-                               ("acceptor chain")
-      * priority           -- "recommended" priority of the acceptor
-                               Ignored when returning an always-true acceptor.
-      """
-
-      # Note: it's illegal to set an acceptor's priority after its creation
-      #       this would violate namespace->get_object()
-      #        ==> use 0 as priority for objects where it doesn't matter much
-      #             (at the cost of unstable-sorted output)
-      #
-      #        The acceptor gets wrapped with an Acceptor_AND object anyway(*),
-      #        no need to set any priority.
-      #        (*) "the top-level logic of a MATCH-block is AND by convention"
-      #            the acceptor returned by get_*acceptor*() can be of *any*
-      #            type, but its str representation (--print-package-rules)
-      #            would always be the same.
-      #
-      #        ["proper" prio-setting is already implemented at
-      #         package/category acceptor level, so the outmost AND acceptor
-      #         has exactly one member and the member's priority matches
-      #         the acceptor's]
-      #
-
-      def get_package_acceptor ( package_tokens, prio ):
-         """Creates a (nested) acceptor for the given non-empty iterable
-         of package tokens.
-
-         Returns: not-None acceptor
-
-         arguments:
-         * package_tokens -- iterable of package tokens (not empty)
-         * prio           -- advised priority of the top-most acceptor
-                              (the object being returned)
-         """
-         if not package_tokens:
-            raise ValueError ( "package token set must not be empty!" )
-
-         elif len(package_tokens) == 1:
-            # special case: bind prio to package_acceptor
-            return convert_package_token_to_acceptor (
-               next(iter(package_tokens)), prio
+         else:
+            return get_obj (
+               self.__class__.TokenItemTuple,
+               (
+                  self.__class__.TOKEN_ITEM_IS_REGEX,
+                  get_obj ( fnmatch.translate, ( s, ) )
+               )
             )
 
-         else:
-            package_acceptors = [
-               convert_package_token_to_acceptor ( package_token, 0 )
-                  for package_token in package_tokens
-            ]
+      else:
+         return get_obj (
+            self.__class__.TokenItemTuple,
+            ( self.__class__.TOKEN_ITEM_IS_STR, s )
+         )
+   # --- end of create_token_item_from_str (...) ---
+
+   def validate_token ( self, package_token ):
+      all_none       = True
+      any_nonspecial = False
+
+      for item in package_token:
+         if item:
+            all_none = False
+            if item is not True:
+               any_nonspecial = True
+      # -- end for
+
+      if all_none:
+         assert not any_nonspecial
+         return None
+      elif any_nonspecial:
+         return package_token
+      else:
+         # FIXME: remove assert
+         assert all ( item in ( True, None ) for item in package_token ), 
package_token
+         return True
+   # --- end of validate_token (...) ---
+
+   def _create_package_token ( self, name_str, version_str, revision_str ):
+      if revision_str:
+         raise TokenItemNotSupported (
+            "revision {!s}".format ( revision_str )
+         )
 
-            # package acceptors get ORed, no need to set a specific priority
-            combined_acceptor = (
-               roverlay.packagerules.abstract.acceptors.Acceptor_OR ( prio )
-            )
+      package_token = self.namespace.get_object_v (
+         self.__class__.PackageToken,
+         (
+            self.create_token_item_from_str ( name_str ),
+            self.create_token_item_from_str ( version_str ),
+##          self.create_token_item_from_str ( revision_str ),
+         )
+      )
 
-            for package_acceptor in package_acceptors:
-               combined_acceptor.add_acceptor ( package_acceptor )
+      return self.validate_token ( package_token )
+   # --- end of _create_package_token (...) ---
 
-            return combined_acceptor
+   def _create_category_token ( self, category_str ):
+      category_token = self.namespace.get_object_v (
+         self.__class__.CategoryToken,
+         (
+            self.create_token_item_from_str ( category_str ),
+         )
+      )
 
-      # --- end of get_package_acceptor (...) ---
+      return self.validate_token ( category_token )
+   # --- end of _create_category_token (...) ---
+
+   def create_category_token ( self, input_str ):
+      # input_str doesn't need much parsing,
+      #  not using RE_WILDCARD_CATEGORY for now
+##      re_match = RE_WILDCARD_CATEGORY.match ( input_str )
+##      if not re_match:
+##         raise InvalidTokenInputString ( input_str )
+##
+##      match_vars = re_match.groupdict()
+##
+##      if not match_vars ['CATEGORY']:
+##         raise InvalidTokenInputString ( input_str )
+
+      if not input_str:
+         raise InvalidTokenInputString ( input_str )
+      else:
+         return self._create_category_token ( input_str )
+   # --- end of create_category_token (...) ---
 
-      def get_category_acceptor ( category_token, package_tokens, prio ):
-         """Creates a nested acceptor for the given category token and its
-         package tokens.
 
-         Returns: not-None acceptor
+   def create_token (
+      self, input_str, with_category=True, package_regex=RE_WILDCARD_PACKAGE
+   ):
+      re_match = package_regex.match ( input_str )
 
-         arguments:
-         * category_token -- category token (True or non-empty)
-         * package_tokens -- iterable of package tokens (can be empty)
-         """
+      if not re_match:
+         raise InvalidTokenInputString (
+            "{!s}: not matched by regex".format ( input_str )
+         )
 
-         if category_token is True:
-            return get_package_acceptor ( package_tokens, prio )
+      match_vars = re_match.groupdict()
 
-         elif package_tokens:
-            acceptor = (
-               roverlay.packagerules.abstract.acceptors.Acceptor_AND ( prio )
-            )
 
-            # match category first and then packages
-            acceptor.add_acceptor (
-               convert_category_token_to_acceptor ( category_token, 0 )
-            )
-            acceptor.add_acceptor (
-               get_package_acceptor ( package_tokens, 1 )
-            )
+      package_token = self._create_package_token (
+         match_vars ['PN'], match_vars ['PV'], match_vars ['revision']
+      )
 
-            return acceptor
+      if not package_token:
+         raise InvalidTokenInputString ( input_str )
 
-         else:
-            return convert_category_token_to_acceptor ( category_token, prio )
-      # --- end of get_category_acceptor (...) ---
-
-      if not category_token_map:
-         # match-all
-         return always_true_acceptor
-
-      elif len(category_token_map) == 1:
-         # special case: bind priority to entry
-         category_acceptor = None
-         for category_token, package_tokens in category_token_map.items():
-            if category_acceptor is None:
-               category_acceptor = get_category_acceptor (
-                  category_token, package_tokens, priority
-               )
-            else:
-               raise AssertionError ( "must not loop more than once." )
-         # -- end for
 
-         return category_acceptor
+      if with_category:
+         category_token = self._create_category_token (
+            match_vars ['CATEGORY']
+         )
 
-      else:
-         acceptors = [
-            get_category_acceptor ( category_token, package_tokens, k )
-               for ( k, ( category_token, package_tokens ) ) in enumerate (
-                  category_token_map.items()
+         if not category_token:
+            if match_vars ['CATEGORY']:
+               raise InvalidTokenInputString (
+                  "{!s}: invalid category str {!s}".format (
+                     input_str, match_vars ['CATEGORY']
+                  )
                )
-         ]
+            # --
 
-         combined_acceptor = (
-            roverlay.packagerules.abstract.acceptors.Acceptor_OR ( priority )
+            # => match-all
+            category_token = True
+         # -- end if
+
+         return ( category_token, package_token )
+
+      elif match_vars ['CATEGORY']:
+         raise InvalidTokenInputString (
+            "{!s}: must not contain CATEGORY".format ( input_str )
          )
+      else:
+         return package_token
+   # --- end of create_token (...) ---
 
-         for acceptor in acceptors:
-            combined_acceptor.add_acceptor ( acceptor )
+   def create_token_for_ebuild_filepath ( self, efile ):
+      return self.create_token (
+         efile, with_category=True, package_regex=RE_PACKAGE_EBUILD_FILE
+      )
+   # --- end of create_token_for_ebuild_filepath (...) ---
 
-         return combined_acceptor
+   def create_package_token ( self, input_str ):
+      return self.create_token ( input_str, with_category=False )
+   # --- end of create_package_token (...) ---
 
-   # --- end of get_acceptor_recursive (...) ---
 
-   def create_rule ( category_token_map, emask ):
-      """Creates a package rule for the given acceptor chain
-      ("category token" -> "package tokens" map)
+   def __init__ ( self, default_category ):
+      super ( AdditionControlPackageRuleGenerator, self ).__init__()
+      self.namespace = roverlay.util.namespace.SimpleNamespace()
 
-      Returns: not-None not-nested package rule (with a nested match block)
+   def clear_object_cache ( self ):
+      if self.namespace:
+         self.namespace.zap(True)
+   # --- end of clear_object_cache (...) ---
 
-      arguments:
-      * category_token_map -- acceptor chain
-      * emask              -- effective bitmask that gets translated into
-                              its corresponding package rule actions
-      """
+   def __del__ ( self ):
+      try:
+         self.clear_object_cache()
+      except NotImplementedError:
+         pass
 
-      # wrap actual acceptor with Acceptor_AND, see above
-      actual_acceptor = get_acceptor_recursive ( category_token_map, 0 )
-      and_acceptor    = roverlay.packagerules.abstract.acceptors.Acceptor_AND 
(0)
-      and_acceptor.add_acceptor ( actual_acceptor )
+   def token_item_to_acceptor ( self, token_item, value_getter, priority ):
+      if token_item in ( True, None ):
+         # should be catched elsewhere
+         return None
 
-      rule = roverlay.packagerules.abstract.rules.PackageRule ( priority=emask 
)
+      elif token_item.item_type == self.__class__.TOKEN_ITEM_IS_STR:
+         acceptor_cls = StringAcceptor
 
-      rule.set_acceptor ( and_acceptor )
 
-      have_any_action = False
-      for k, action in packagerule_actions.items():
-         have_any_action = True
-         if ( k & emask ):
-            rule.add_action ( action )
+      elif token_item.item_type == self.__class__.TOKEN_ITEM_IS_REGEX:
+         acceptor_cls = ExactRegexAcceptor
 
-      if not have_any_action:
+      else:
          raise AssertionError (
-            "rule object has no actions - bad emask? {:#x}".format (
-               emask=emask
-            )
+            "invalid token item type: {!s}".format ( token_item.item_type )
          )
+      # -- end if
 
-      return rule
-   # --- end of create_rule (...) ---
-
-   rules = [
-      create_rule ( category_token_map, emask )
-         for emask, category_token_map in \
-            reduced_bitmask_acceptor_chain_map.items()
-   ]
+      return self.namespace.get_object_v (
+         acceptor_cls, ( priority, value_getter, token_item.value )
+      )
+   # --- end of token_item_to_acceptor (...) ---
 
-   if not rules:
-      return None
+   def category_token_to_acceptor ( self, category_token, priority ):
+      if category_token is True:
+         raise Exception ( "C-TRUE" )
 
-   elif len(rules) == 1:
-      rules [0].priority = -1
-      return rules [0]
 
-   else:
-      combined_rule = roverlay.packagerules.abstract.rules.NestedPackageRule (
-         priority = -1
+      return self.token_item_to_acceptor (
+         category_token.name, get_category, priority
       )
-      combined_rule.set_acceptor ( always_true_acceptor )
+   # --- end of category_token_to_acceptor (...) ---
 
-      for rule in rules:
-         combined_rule.add_rule ( rule )
+   def package_token_to_acceptor ( self, package_token, priority ):
+      if package_token is True:
+         raise Exception ( "P-TRUE" )
+      else:
+         relevant_items = [
+            item_and_getter for item_and_getter in zip (
+               package_token,
+               (
+                  get_ebuild_name,
+                  get_ebuild_version_tuple
+               )
+            ) if item_and_getter[0] and item_and_getter[0] is not True
+         ]
 
-      return combined_rule
-   # -- end if
+         if not relevant_items:
+            raise TokenValueError ( package_token )
+
+         elif len(relevant_items) == 1:
+            return self.token_item_to_acceptor (
+               relevant_items[0][0], relevant_items[0][1], priority
+            )
+         else:
+            sub_acceptors = [
+               self.token_item_to_acceptor ( item, getter, 0 )
+               for item, getter in relevant_items
+            ]
+
+            combined_acceptor = Acceptor_AND ( priority=priority )
+            for sub_acceptor in sub_acceptors:
+               combined_acceptor.add_acceptor ( sub_acceptor )
+
+            return combined_acceptor
+   # --- end of package_token_to_acceptor (...) ---
 
-# --- end of create_package_rules (...) ---
 
 class SillyAdditionControlPackageRuleGenerator (
-   AbstractAdditionControlPackageRuleGenerator
+   roverlay.packagerules.generators.abstract.addition_control.\
+      AbstractAdditionControlPackageRuleGenerator
 ):
    """
    An add-policy package rule generator that doesn't care about its tokens.
@@ -705,41 +363,53 @@ class SillyAdditionControlPackageRuleGenerator (
 
 
 def temporary_demo_func():
-   rule_generator             = SillyAdditionControlPackageRuleGenerator()
+   #rule_generator             = SillyAdditionControlPackageRuleGenerator()
+   ARES                       = AdditionControlResult
+   rule_generator             = AdditionControlPackageRuleGenerator("sci-R")
+   CTOKEN                     = rule_generator.create_category_token
+   PTOKEN                     = rule_generator.create_package_token
    add_control_rule           = None
    bitmask_acceptor_chain_map = None
    acceptor_chain_bitmask_map = {
-      True: {
-         True: AdditionControlResult.PKG_REVBUMP_ON_COLLISION,
-         'p0': 8|2,
+      CTOKEN ( "sys-*" ): {
+         PTOKEN ( "a*-2" ): ARES.PKG_FORCE_DENY,
+      },
+      CTOKEN ( "*" ): {
+         PTOKEN ( "?*" ): ARES.PKG_REVBUMP_ON_COLLISION,
+         PTOKEN ( 'p0' ): ARES.PKG_FORCE_DENY|ARES.PKG_FORCE_REPLACE,
       },
-      'c': {
-         True: 2,
+      CTOKEN ( 'c' ): {
+         PTOKEN ( "***?****" ): ARES.PKG_FORCE_DENY,
       },
-      'd': {
-         True: AdditionControlResult.PKG_REPLACE_ONLY,
+      CTOKEN ( 'd' ): {
+         PTOKEN ( "*" ): ARES.PKG_REPLACE_ONLY,
       },
-      'f': {
-         'p1': AdditionControlResult.PKG_REPLACE_ONLY,
+      CTOKEN ( 'f' ): {
+         PTOKEN ( 'p1' ): ARES.PKG_REPLACE_ONLY,
+         PTOKEN ( 'p1-5.0' ): ARES.PKG_REPLACE_ONLY,
       },
    }
 
+
    print ( "** initial acceptor_chain -> raw_bitmask map" )
    print ( acceptor_chain_bitmask_map )
    print()
 
+   roverlay.packagerules.generators.abstract.addition_control.\
    expand_acceptor_chain_bitmasks ( acceptor_chain_bitmask_map )
    print ( "** expanded acceptor_chain -> effective_bitmask map" )
    print ( acceptor_chain_bitmask_map )
    print()
 
    bitmask_acceptor_chain_map = (
+      roverlay.packagerules.generators.abstract.addition_control.\
       create_bitmask_acceptor_chain_map ( acceptor_chain_bitmask_map )
    )
    print ( "** initial effective_bitmask -> acceptor_chain map" )
    print(bitmask_acceptor_chain_map)
    print()
 
+   roverlay.packagerules.generators.abstract.addition_control.\
    reduce_bitmask_acceptor_chain_map ( bitmask_acceptor_chain_map )
    print ( "** reduced effective_bitmask -> acceptor_chain map" )
    print(bitmask_acceptor_chain_map)
@@ -758,12 +428,17 @@ def temporary_demo_func():
    print ( "** content of the rule generator\'s namespace" )
    print ( rule_generator.namespace._objects )
 
+   print ( "** content of the rule generator\'s namespace after clear-cache" )
+   rule_generator.clear_object_cache()
+   print ( rule_generator.namespace._objects )
+
 # --- end of temporary_demo_func (...) ---
 
 
 if __name__ == '__main__':
    import sys
    import os
+   from roverlay.overlay.abccontrol import AdditionControlResult
 
    try:
       temporary_demo_func()

Reply via email to