133 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Python
		
	
	
		
		
			
		
	
	
			133 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Python
		
	
	
|  | """
 | ||
|  | 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  # type: ignore | ||
|  | 
 | ||
|  | 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, ForwardDeclaration]: | ||
|  |         """
 | ||
|  |         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 level 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 |