text: """
  GTSAM Copyright 2010-2020, Georgia Tech Research Corporation,
  Atlanta, Georgia 30332-0415
  All Rights Reserved
  
  See LICENSE for the license information
  
  Classes and rules to parse a namespace.
  
  Author: Duy Nguyen Ta, Fan Jiang, Matthew Sklar, Varun Agrawal, and Frank Dellaert
  """
  
  # pylint: disable=unnecessary-lambda, expression-not-assigned
  
  from typing import List, Union
  
  from pyparsing import Forward, ParseResults, ZeroOrMore
  
  from .classes import Class, collect_namespaces
  from .declaration import ForwardDeclaration, Include
  from .enum import Enum
  from .function import GlobalFunction
  from .template import TypedefTemplateInstantiation
  from .tokens import IDENT, LBRACE, NAMESPACE, RBRACE
  from .type import Typename
  from .variable import Variable
  
  
  def find_sub_namespace(namespace: "Namespace",
                         str_namespaces: List["Namespace"]) -> list:
      """
      Get the namespaces nested under `namespace`, filtered by a list of namespace strings.
  
      Args:
          namespace: The top-level namespace under which to find sub-namespaces.
          str_namespaces: The list of namespace strings to filter against.
      """
      if not str_namespaces:
          return [namespace]
  
      sub_namespaces = (ns for ns in namespace.content
                        if isinstance(ns, Namespace))
  
      found_namespaces = [
          ns for ns in sub_namespaces if ns.name == str_namespaces[0]
      ]
      if not found_namespaces:
          return []
  
      res = []
      for found_namespace in found_namespaces:
          ns = find_sub_namespace(found_namespace, str_namespaces[1:])
          if ns:
              res += ns
      return res
  
  
  class Namespace:
      """Rule for parsing a namespace in the interface file."""
  
      rule = Forward()
      rule << (
          NAMESPACE  #
          + IDENT("name")  #
          + LBRACE  #
          + ZeroOrMore(  # BR
              ForwardDeclaration.rule  #
              ^ Include.rule  #
              ^ Class.rule  #
              ^ TypedefTemplateInstantiation.rule  #
              ^ GlobalFunction.rule  #
              ^ Enum.rule  #
              ^ Variable.rule  #
              ^ rule  #
          )("content")  # BR
          + RBRACE  #
      ).setParseAction(lambda t: Namespace.from_parse_result(t))
  
      def __init__(self, name: str, content: ZeroOrMore, parent=''):
          self.name = name
          self.content = content
          self.parent = parent
          for child in self.content:
              child.parent = self
  
      @staticmethod
      def from_parse_result(t: ParseResults):
          """Return the result of parsing."""
          if t.content:
              content = t.content.asList()
          else:
              content = []
          return Namespace(t.name, content)
  
      def find_class_or_function(
              self, typename: Typename) -> Union[Class, GlobalFunction]:
          """
          Find the Class or GlobalFunction object given its typename.
          We have to traverse the tree of namespaces.
          """
          found_namespaces = find_sub_namespace(self, typename.namespaces)
          res = []
          for namespace in found_namespaces:
              classes_and_funcs = (c for c in namespace.content
                                   if isinstance(c, (Class, GlobalFunction, ForwardDeclaration)))
              res += [c for c in classes_and_funcs if c.name == typename.name]
          if not res:
              raise ValueError("Cannot find class {} in module!".format(
                  typename.name))
          elif len(res) > 1:
              raise ValueError(
                  "Found more than one classes {} in module!".format(
                      typename.name))
          else:
              return res[0]
  
      def top_level(self) -> "Namespace":
          """Return the top leve namespace."""
          if self.name == '' or self.parent == '':
              return self
          else:
              return self.parent.top_level()
  
      def __repr__(self) -> str:
          return "Namespace: {}\n\t{}".format(self.name, self.content)
  
      def full_namespaces(self) -> List["Namespace"]:
          """Get the full namespace list."""
          ancestors = collect_namespaces(self)
          if self.name:
              ancestors.append(self.name)
          return ancestors
  