text: """
  GTSAM Copyright 2010-2020, Georgia Tech Research Corporation,
  Atlanta, Georgia 30332-0415
  All Rights Reserved
  
  See LICENSE for the license information
  
  Parser classes and rules for parsing C++ functions.
  
  Author: Duy Nguyen Ta, Fan Jiang, Matthew Sklar, Varun Agrawal, and Frank Dellaert
  """
  
  from typing import Any, Iterable, List, Union
  
  from pyparsing import Optional, ParseResults, delimitedList  # type: ignore
  
  from .template import Template
  from .tokens import (COMMA, DEFAULT_ARG, EQUAL, IDENT, LOPBRACK, LPAREN, PAIR,
                       ROPBRACK, RPAREN, SEMI_COLON)
  from .type import TemplatedType, Type
  
  
  class Argument:
      """
      The type and name of a function/method argument.
  
      E.g.
      ```
      void sayHello(/*`s` is the method argument with type `const string&`*/ const string& s);
      ```
      """
      rule = ((Type.rule ^ TemplatedType.rule)("ctype")  #
              + IDENT("name")  #
              + Optional(EQUAL + DEFAULT_ARG)("default")
              ).setParseAction(lambda t: Argument(
                  t.ctype,  #
                  t.name,  #
                  t.default[0] if isinstance(t.default, ParseResults) else None))
  
      def __init__(self,
                   ctype: Union[Type, TemplatedType],
                   name: str,
                   default: ParseResults = None):
          if isinstance(ctype, Iterable):
              self.ctype = ctype[0]  # type: ignore
          else:
              self.ctype = ctype
          self.name = name
          self.default = default
          self.parent: Union[ArgumentList, None] = None
  
      def __repr__(self) -> str:
          return self.to_cpp()
  
      def to_cpp(self) -> str:
          """Return full C++ representation of argument."""
          return '{} {}'.format(repr(self.ctype), self.name)
  
  
  class ArgumentList:
      """
      List of Argument objects for all arguments in a function.
      """
      rule = Optional(delimitedList(Argument.rule)("args_list")).setParseAction(
          lambda t: ArgumentList.from_parse_result(t.args_list))
  
      def __init__(self, args_list: List[Argument]):
          self.args_list = args_list
          for arg in args_list:
              arg.parent = self
          # The parent object which contains the argument list
          # E.g. Method, StaticMethod, Template, Constructor, GlobalFunction
          self.parent: Any = None
  
      @staticmethod
      def from_parse_result(parse_result: ParseResults):
          """Return the result of parsing."""
          if parse_result:
              return ArgumentList(parse_result.asList())
          else:
              return ArgumentList([])
  
      def __repr__(self) -> str:
          return self.args_list.__repr__()
  
      def __len__(self) -> int:
          return len(self.args_list)
  
      def names(self) -> List[str]:
          """Return a list of the names of all the arguments."""
          return [arg.name for arg in self.args_list]
  
      def list(self) -> List[Argument]:
          """Return a list of the names of all the arguments."""
          return self.args_list
  
      def to_cpp(self, use_boost: bool) -> List[str]:
          """Generate the C++ code for wrapping."""
          return [arg.ctype.to_cpp(use_boost) for arg in self.args_list]
  
  
  class ReturnType:
      """
      Rule to parse the return type.
  
      The return type can either be a single type or a pair such as <type1, type2>.
      """
      _pair = (
          PAIR.suppress()  #
          + LOPBRACK  #
          + Type.rule("type1")  #
          + COMMA  #
          + Type.rule("type2")  #
          + ROPBRACK  #
      )
      rule = (_pair ^
              (Type.rule ^ TemplatedType.rule)("type1")).setParseAction(  # BR
                  lambda t: ReturnType(t.type1, t.type2))
  
      def __init__(self, type1: Union[Type, TemplatedType], type2: Type):
          # If a TemplatedType, the return is a ParseResults, so we extract out the type.
          self.type1 = type1[0] if isinstance(type1, ParseResults) else type1
          self.type2 = type2
          # The parent object which contains the return type
          # E.g. Method, StaticMethod, Template, Constructor, GlobalFunction
          self.parent: Any = None
  
      def is_void(self) -> bool:
          """
          Check if the return type is void.
          """
          return self.type1.typename.name == "void" and not self.type2
  
      def __repr__(self) -> str:
          return "{}{}".format(
              self.type1, (', ' + self.type2.__repr__()) if self.type2 else '')
  
      def to_cpp(self, use_boost: bool) -> str:
          """
          Generate the C++ code for wrapping.
  
          If there are two return types, we return a pair<>,
          otherwise we return the regular return type.
          """
          if self.type2:
              return "std::pair<{type1},{type2}>".format(
                  type1=self.type1.to_cpp(use_boost),
                  type2=self.type2.to_cpp(use_boost))
          else:
              return self.type1.to_cpp(use_boost)
  
  
  class GlobalFunction:
      """
      Rule to parse functions defined in the global scope.
      """
      rule = (
          Optional(Template.rule("template")) + ReturnType.rule("return_type")  #
          + IDENT("name")  #
          + LPAREN  #
          + ArgumentList.rule("args_list")  #
          + RPAREN  #
          + SEMI_COLON  #
      ).setParseAction(lambda t: GlobalFunction(t.name, t.return_type, t.
                                                args_list, t.template))
  
      def __init__(self,
                   name: str,
                   return_type: ReturnType,
                   args_list: ArgumentList,
                   template: Template,
                   parent: str = ''):
          self.name = name
          self.return_type = return_type
          self.args = args_list
          self.template = template
  
          self.parent = parent
          self.return_type.parent = self
          self.args.parent = self
  
      def __repr__(self) -> str:
          return "GlobalFunction:  {}{}({})".format(self.return_type, self.name,
                                                    self.args)
  
      def to_cpp(self) -> str:
          """Generate the C++ code for wrapping."""
          return self.name
  