Skip to content

Commit

Permalink
Squashed 'wrap/' changes from 903694b77..b2144a712
Browse files Browse the repository at this point in the history
b2144a712 Merge pull request #95 from borglab/feature/empty-str-default-arg
9f1e727d8 Merge pull request #96 from borglab/fix/cmake
97ee2ff0c fix CMake typo
64a599827 support empty strings as default args
7b14ed542 Merge pull request #94 from borglab/fix/cmake-messages
0978641fe clean up
5b9272557 Merge pull request #91 from borglab/feature/enums
56e6f48b3 Merge pull request #93 from borglab/feature/better-template
27cc7cebf better cmake messages
a6318b567 fix tests
b7f60463f remove export_values()
38304fe0a support for class nested enums
348160740 minor fixes
5b6d66a97 use cpp_class and correct module name
2f7ae0676 add newlines and formatting
6e7cecc50 remove support for enum value assignment
c1dc925a6 formatting
798732598 better pybind template
f6dad2959 pybind_wrapper fixes with formatting
7b4a06560 Merge branch 'master' into feature/enums
1982b7131 more comprehensive tests for enums
3a0eafd66 code for wrapping enums
398780982 tests for enum support

git-subtree-dir: wrap
git-subtree-split: b2144a712953dcc3e001c97c2ace791149c97278
  • Loading branch information
varunagrawal committed Apr 20, 2021
1 parent 56d060b commit 048666e
Show file tree
Hide file tree
Showing 21 changed files with 399 additions and 109 deletions.
38 changes: 21 additions & 17 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,19 @@ configure_package_config_file(
INSTALL_INCLUDE_DIR
INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})

message(STATUS "Package config : ${CMAKE_INSTALL_PREFIX}/${INSTALL_CMAKE_DIR}")
# Set all the install paths
set(GTWRAP_CMAKE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/${INSTALL_CMAKE_DIR})
set(GTWRAP_LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR})
set(GTWRAP_BIN_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/${INSTALL_BIN_DIR})
set(GTWRAP_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/${INSTALL_INCLUDE_DIR})

# ##############################################################################
# Install the package

message(STATUS "CMake : ${CMAKE_INSTALL_PREFIX}/${INSTALL_CMAKE_DIR}")
# Install CMake scripts to the standard CMake script directory.
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/gtwrapConfig.cmake
cmake/MatlabWrap.cmake cmake/PybindWrap.cmake cmake/GtwrapUtils.cmake
DESTINATION "${CMAKE_INSTALL_PREFIX}/${INSTALL_CMAKE_DIR}")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/gtwrapConfig.cmake
cmake/MatlabWrap.cmake cmake/PybindWrap.cmake
cmake/GtwrapUtils.cmake DESTINATION "${GTWRAP_CMAKE_INSTALL_DIR}")

# Configure the include directory for matlab.h This allows the #include to be
# either gtwrap/matlab.h, wrap/matlab.h or something custom.
Expand All @@ -60,24 +62,26 @@ configure_file(${PROJECT_SOURCE_DIR}/templates/matlab_wrapper.tpl.in

# Install the gtwrap python package as a directory so it can be found by CMake
# for wrapping.
message(STATUS "Lib path : ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}")
install(DIRECTORY gtwrap
DESTINATION "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}")
install(DIRECTORY gtwrap DESTINATION "${GTWRAP_LIB_INSTALL_DIR}")

# Install pybind11 directory to `CMAKE_INSTALL_PREFIX/lib/gtwrap/pybind11` This
# will allow the gtwrapConfig.cmake file to load it later.
install(DIRECTORY pybind11
DESTINATION "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}")
install(DIRECTORY pybind11 DESTINATION "${GTWRAP_LIB_INSTALL_DIR}")

# Install wrapping scripts as binaries to `CMAKE_INSTALL_PREFIX/bin` so they can
# be invoked for wrapping. We use DESTINATION (instead of TYPE) so we can
# support older CMake versions.
message(STATUS "Bin path : ${CMAKE_INSTALL_PREFIX}/${INSTALL_BIN_DIR}")
install(PROGRAMS scripts/pybind_wrap.py scripts/matlab_wrap.py
DESTINATION "${CMAKE_INSTALL_PREFIX}/${INSTALL_BIN_DIR}")
DESTINATION "${GTWRAP_BIN_INSTALL_DIR}")

# Install the matlab.h file to `CMAKE_INSTALL_PREFIX/lib/gtwrap/matlab.h`.
message(
STATUS "Header path : ${CMAKE_INSTALL_PREFIX}/${INSTALL_INCLUDE_DIR}")
install(FILES matlab.h
DESTINATION "${CMAKE_INSTALL_PREFIX}/${INSTALL_INCLUDE_DIR}")
install(FILES matlab.h DESTINATION "${GTWRAP_INCLUDE_INSTALL_DIR}")

string(ASCII 27 Esc)
set(gtwrap "${Esc}[1;36mgtwrap${Esc}[m")
message(STATUS "${gtwrap} Package config : ${GTWRAP_CMAKE_INSTALL_DIR}")
message(STATUS "${gtwrap} version : ${PROJECT_VERSION}")
message(STATUS "${gtwrap} CMake path : ${GTWRAP_CMAKE_INSTALL_DIR}")
message(STATUS "${gtwrap} library path : ${GTWRAP_LIB_INSTALL_DIR}")
message(STATUS "${gtwrap} binary path : ${GTWRAP_BIN_INSTALL_DIR}")
message(STATUS "${gtwrap} header path : ${GTWRAP_INCLUDE_INSTALL_DIR}")
2 changes: 2 additions & 0 deletions gtwrap/interface_parser/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
"""

import sys

import pyparsing

from .classes import *
from .declaration import *
from .enum import *
from .function import *
from .module import *
from .namespace import *
Expand Down
42 changes: 20 additions & 22 deletions gtwrap/interface_parser/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@

from typing import Iterable, List, Union

from pyparsing import Optional, ZeroOrMore, Literal
from pyparsing import Literal, Optional, ZeroOrMore

from .enum import Enum
from .function import ArgumentList, ReturnType
from .template import Template
from .tokens import (CLASS, COLON, CONST, IDENT, LBRACE, LPAREN, RBRACE,
RPAREN, SEMI_COLON, STATIC, VIRTUAL, OPERATOR)
from .type import TemplatedType, Type, Typename
from .tokens import (CLASS, COLON, CONST, IDENT, LBRACE, LPAREN, OPERATOR,
RBRACE, RPAREN, SEMI_COLON, STATIC, VIRTUAL)
from .type import TemplatedType, Typename
from .utils import collect_namespaces
from .variable import Variable


Expand Down Expand Up @@ -200,21 +202,6 @@ def __repr__(self) -> str:
)


def collect_namespaces(obj):
"""
Get the chain of namespaces from the lowest to highest for the given object.
Args:
obj: Object of type Namespace, Class or InstantiatedClass.
"""
namespaces = []
ancestor = obj.parent
while ancestor and ancestor.name:
namespaces = [ancestor.name] + namespaces
ancestor = ancestor.parent
return [''] + namespaces


class Class:
"""
Rule to parse a class defined in the interface file.
Expand All @@ -230,9 +217,13 @@ class Members:
"""
Rule for all the members within a class.
"""
rule = ZeroOrMore(Constructor.rule ^ StaticMethod.rule ^ Method.rule
^ Variable.rule ^ Operator.rule).setParseAction(
lambda t: Class.Members(t.asList()))
rule = ZeroOrMore(Constructor.rule #
^ StaticMethod.rule #
^ Method.rule #
^ Variable.rule #
^ Operator.rule #
^ Enum.rule #
).setParseAction(lambda t: Class.Members(t.asList()))

def __init__(self,
members: List[Union[Constructor, Method, StaticMethod,
Expand All @@ -242,6 +233,7 @@ def __init__(self,
self.static_methods = []
self.properties = []
self.operators = []
self.enums = []
for m in members:
if isinstance(m, Constructor):
self.ctors.append(m)
Expand All @@ -253,6 +245,8 @@ def __init__(self,
self.properties.append(m)
elif isinstance(m, Operator):
self.operators.append(m)
elif isinstance(m, Enum):
self.enums.append(m)

_parent = COLON + (TemplatedType.rule ^ Typename.rule)("parent_class")
rule = (
Expand All @@ -275,6 +269,7 @@ def __init__(self,
t.members.static_methods,
t.members.properties,
t.members.operators,
t.members.enums
))

def __init__(
Expand All @@ -288,6 +283,7 @@ def __init__(
static_methods: List[StaticMethod],
properties: List[Variable],
operators: List[Operator],
enums: List[Enum],
parent: str = '',
):
self.template = template
Expand All @@ -312,6 +308,8 @@ def __init__(
self.static_methods = static_methods
self.properties = properties
self.operators = operators
self.enums = enums

self.parent = parent

# Make sure ctors' names and class name are the same.
Expand Down
70 changes: 70 additions & 0 deletions gtwrap/interface_parser/enum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""
GTSAM Copyright 2010-2020, Georgia Tech Research Corporation,
Atlanta, Georgia 30332-0415
All Rights Reserved
See LICENSE for the license information
Parser class and rules for parsing C++ enums.
Author: Varun Agrawal
"""

from pyparsing import delimitedList

from .tokens import ENUM, IDENT, LBRACE, RBRACE, SEMI_COLON
from .type import Typename
from .utils import collect_namespaces


class Enumerator:
"""
Rule to parse an enumerator inside an enum.
"""
rule = (
IDENT("enumerator")).setParseAction(lambda t: Enumerator(t.enumerator))

def __init__(self, name):
self.name = name

def __repr__(self):
return "Enumerator: ({0})".format(self.name)


class Enum:
"""
Rule to parse enums defined in the interface file.
E.g.
```
enum Kind {
Dog,
Cat
};
```
"""

rule = (ENUM + IDENT("name") + LBRACE +
delimitedList(Enumerator.rule)("enumerators") + RBRACE +
SEMI_COLON).setParseAction(lambda t: Enum(t.name, t.enumerators))

def __init__(self, name, enumerators, parent=''):
self.name = name
self.enumerators = enumerators
self.parent = parent

def namespaces(self) -> list:
"""Get the namespaces which this class is nested under as a list."""
return collect_namespaces(self)

def cpp_typename(self):
"""
Return a Typename with the namespaces and cpp name of this
class.
"""
namespaces_name = self.namespaces()
namespaces_name.append(self.name)
return Typename(namespaces_name)

def __repr__(self):
return "Enum: {0}".format(self.name)
3 changes: 3 additions & 0 deletions gtwrap/interface_parser/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ def __init__(self,
# This means a tuple has been passed so we convert accordingly
elif len(default) > 1:
default = tuple(default.asList())
else:
# set to None explicitly so we can support empty strings
default = None
self.default = default

self.parent: Union[ArgumentList, None] = None
Expand Down
10 changes: 4 additions & 6 deletions gtwrap/interface_parser/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@

# pylint: disable=unnecessary-lambda, unused-import, expression-not-assigned, no-else-return, protected-access, too-few-public-methods, too-many-arguments

import sys

import pyparsing # type: ignore
from pyparsing import (ParserElement, ParseResults, ZeroOrMore,
cppStyleComment, stringEnd)
from pyparsing import ParseResults, ZeroOrMore, cppStyleComment, stringEnd

from .classes import Class
from .declaration import ForwardDeclaration, Include
from .enum import Enum
from .function import GlobalFunction
from .namespace import Namespace
from .template import TypedefTemplateInstantiation
Expand All @@ -44,7 +41,8 @@ class Module:
^ Class.rule #
^ TypedefTemplateInstantiation.rule #
^ GlobalFunction.rule #
^ Variable.rule #
^ Enum.rule #
^ Variable.rule #
^ Namespace.rule #
).setParseAction(lambda t: Namespace('', t.asList())) +
stringEnd)
Expand Down
4 changes: 3 additions & 1 deletion gtwrap/interface_parser/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

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
Expand Down Expand Up @@ -68,7 +69,8 @@ class Namespace:
^ Class.rule #
^ TypedefTemplateInstantiation.rule #
^ GlobalFunction.rule #
^ Variable.rule #
^ Enum.rule #
^ Variable.rule #
^ rule #
)("content") # BR
+ RBRACE #
Expand Down
1 change: 1 addition & 0 deletions gtwrap/interface_parser/tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"#include",
],
)
ENUM = Keyword("enum") ^ Keyword("enum class") ^ Keyword("enum struct")
NAMESPACE = Keyword("namespace")
BASIS_TYPES = map(
Keyword,
Expand Down
26 changes: 26 additions & 0 deletions gtwrap/interface_parser/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
GTSAM Copyright 2010-2020, Georgia Tech Research Corporation,
Atlanta, Georgia 30332-0415
All Rights Reserved
See LICENSE for the license information
Various common utilities.
Author: Varun Agrawal
"""


def collect_namespaces(obj):
"""
Get the chain of namespaces from the lowest to highest for the given object.
Args:
obj: Object of type Namespace, Class, InstantiatedClass, or Enum.
"""
namespaces = []
ancestor = obj.parent
while ancestor and ancestor.name:
namespaces = [ancestor.name] + namespaces
ancestor = ancestor.parent
return [''] + namespaces
2 changes: 2 additions & 0 deletions gtwrap/interface_parser/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ def __init__(self,
self.name = name
if default:
self.default = default[0]
else:
self.default = None

self.parent = parent

Expand Down
Loading

0 comments on commit 048666e

Please sign in to comment.