""" 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 Iterable, List, Union from pyparsing import Optional, ParseResults, delimitedList 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] else: self.ctype = ctype self.name = name self.default = default self.parent = None # type: Union[ArgumentList, 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 = 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 . """ _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 = 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