Move PSAMacroEnumerator to macro_collector

It's useful for more than test_psa_constant_names.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
diff --git a/tests/scripts/test_psa_constant_names.py b/tests/scripts/test_psa_constant_names.py
index 21a2e8d..b3fdb8d 100755
--- a/tests/scripts/test_psa_constant_names.py
+++ b/tests/scripts/test_psa_constant_names.py
@@ -24,119 +24,14 @@
 
 import argparse
 from collections import namedtuple
-import itertools
 import os
 import re
 import subprocess
 import sys
-from typing import Dict, Iterable, Iterator, List, Set
 
 import scripts_path # pylint: disable=unused-import
 from mbedtls_dev import c_build_helper
-
-class PSAMacroEnumerator:
-    """Information about constructors of various PSA Crypto types.
-
-    This includes macro names as well as information about their arguments
-    when applicable.
-
-    This class only provides ways to enumerate expressions that evaluate to
-    values of the covered types. Derived classes are expected to populate
-    the set of known constructors of each kind, as well as populate
-    `self.arguments_for` for arguments that are not of a kind that is
-    enumerated here.
-    """
-
-    def __init__(self) -> None:
-        """Set up an empty set of known constructor macros.
-        """
-        self.statuses = set() #type: Set[str]
-        self.algorithms = set() #type: Set[str]
-        self.ecc_curves = set() #type: Set[str]
-        self.dh_groups = set() #type: Set[str]
-        self.key_types = set() #type: Set[str]
-        self.key_usage_flags = set() #type: Set[str]
-        self.hash_algorithms = set() #type: Set[str]
-        self.mac_algorithms = set() #type: Set[str]
-        self.ka_algorithms = set() #type: Set[str]
-        self.kdf_algorithms = set() #type: Set[str]
-        self.aead_algorithms = set() #type: Set[str]
-        # macro name -> list of argument names
-        self.argspecs = {} #type: Dict[str, List[str]]
-        # argument name -> list of values
-        self.arguments_for = {
-            'mac_length': [],
-            'min_mac_length': [],
-            'tag_length': [],
-            'min_tag_length': [],
-        } #type: Dict[str, List[str]]
-
-    def gather_arguments(self) -> None:
-        """Populate the list of values for macro arguments.
-
-        Call this after parsing all the inputs.
-        """
-        self.arguments_for['hash_alg'] = sorted(self.hash_algorithms)
-        self.arguments_for['mac_alg'] = sorted(self.mac_algorithms)
-        self.arguments_for['ka_alg'] = sorted(self.ka_algorithms)
-        self.arguments_for['kdf_alg'] = sorted(self.kdf_algorithms)
-        self.arguments_for['aead_alg'] = sorted(self.aead_algorithms)
-        self.arguments_for['curve'] = sorted(self.ecc_curves)
-        self.arguments_for['group'] = sorted(self.dh_groups)
-
-    @staticmethod
-    def _format_arguments(name: str, arguments: Iterable[str]) -> str:
-        """Format a macro call with arguments.."""
-        return name + '(' + ', '.join(arguments) + ')'
-
-    _argument_split_re = re.compile(r' *, *')
-    @classmethod
-    def _argument_split(cls, arguments: str) -> List[str]:
-        return re.split(cls._argument_split_re, arguments)
-
-    def distribute_arguments(self, name: str) -> Iterator[str]:
-        """Generate macro calls with each tested argument set.
-
-        If name is a macro without arguments, just yield "name".
-        If name is a macro with arguments, yield a series of
-        "name(arg1,...,argN)" where each argument takes each possible
-        value at least once.
-        """
-        try:
-            if name not in self.argspecs:
-                yield name
-                return
-            argspec = self.argspecs[name]
-            if argspec == []:
-                yield name + '()'
-                return
-            argument_lists = [self.arguments_for[arg] for arg in argspec]
-            arguments = [values[0] for values in argument_lists]
-            yield self._format_arguments(name, arguments)
-            # Dear Pylint, enumerate won't work here since we're modifying
-            # the array.
-            # pylint: disable=consider-using-enumerate
-            for i in range(len(arguments)):
-                for value in argument_lists[i][1:]:
-                    arguments[i] = value
-                    yield self._format_arguments(name, arguments)
-                arguments[i] = argument_lists[0][0]
-        except BaseException as e:
-            raise Exception('distribute_arguments({})'.format(name)) from e
-
-    def generate_expressions(self, names: Iterable[str]) -> Iterator[str]:
-        """Generate expressions covering values constructed from the given names.
-
-        `names` can be any iterable collection of macro names.
-
-        For example:
-        * ``generate_expressions(['PSA_ALG_CMAC', 'PSA_ALG_HMAC'])``
-          generates ``'PSA_ALG_CMAC'`` as well as ``'PSA_ALG_HMAC(h)'`` for
-          every known hash algorithm ``h``.
-        * ``macros.generate_expressions(macros.key_types)`` generates all
-          key types.
-        """
-        return itertools.chain(*map(self.distribute_arguments, names))
+from mbedtls_dev import macro_collector
 
 class ReadFileLineException(Exception):
     def __init__(self, filename, line_number):
@@ -183,7 +78,7 @@
             raise ReadFileLineException(self.filename, self.line_number) \
                 from exc_value
 
-class InputsForTest(PSAMacroEnumerator):
+class InputsForTest(macro_collector.PSAMacroEnumerator):
     # pylint: disable=too-many-instance-attributes
     """Accumulate information about macros to test.