""" 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 .function import GlobalFunction from .template import TypedefTemplateInstantiation from .tokens import IDENT, LBRACE, NAMESPACE, RBRACE from .type import Typename 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 # ^ 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))) 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