Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap Update #799

Merged
merged 2 commits into from
Jun 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion wrap/.github/workflows/linux-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
python-version: [3.5, 3.6, 3.7, 3.8, 3.9]

steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion wrap/.github/workflows/macos-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
python-version: [3.5, 3.6, 3.7, 3.8, 3.9]

steps:
- name: Checkout
Expand Down
3 changes: 2 additions & 1 deletion wrap/DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ The python wrapper supports keyword arguments for functions/methods. Hence, the
```

- Using classes defined in other modules
- If you are using a class `OtherClass` not wrapped in an interface file, add `class OtherClass;` as a forward declaration to avoid a dependency error. `OtherClass` should be in the same project.
- If you are using a class `OtherClass` not wrapped in an interface file, add `class OtherClass;` as a forward declaration to avoid a dependency error.
- `OtherClass` may not be in the same project. If this is the case, include the header for the appropriate project `#include <other_project/OtherClass.h>`.

- Virtual inheritance
- Specify fully-qualified base classes, i.e. `virtual class Derived : ns::Base {` where `ns` is the namespace.
Expand Down
2 changes: 1 addition & 1 deletion wrap/gtwrap/interface_parser/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ def __init__(self,

# Check to ensure arg and return type are the same.
if len(args) == 1 and self.operator not in ("()", "[]"):
assert args.args_list[0].ctype.typename.name == return_type.type1.typename.name, \
assert args.list()[0].ctype.typename.name == return_type.type1.typename.name, \
"Mixed type overloading not supported. Both arg and return type must be the same."

def __repr__(self) -> str:
Expand Down
13 changes: 9 additions & 4 deletions wrap/gtwrap/interface_parser/declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from .tokens import (CLASS, COLON, INCLUDE, LOPBRACK, ROPBRACK, SEMI_COLON,
VIRTUAL)
from .type import Typename
from .utils import collect_namespaces


class Include:
Expand Down Expand Up @@ -42,11 +43,12 @@ class ForwardDeclaration:
t.name, t.parent_type, t.is_virtual))

def __init__(self,
name: Typename,
typename: Typename,
parent_type: str,
is_virtual: str,
parent: str = ''):
self.name = name
self.name = typename.name
self.typename = typename
if parent_type:
self.parent_type = parent_type
else:
Expand All @@ -55,6 +57,9 @@ def __init__(self,
self.is_virtual = is_virtual
self.parent = parent

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

def __repr__(self) -> str:
return "ForwardDeclaration: {} {}({})".format(self.is_virtual,
self.name, self.parent)
return "ForwardDeclaration: {} {}".format(self.is_virtual, self.name)
30 changes: 13 additions & 17 deletions wrap/gtwrap/interface_parser/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ class Argument:
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 ^ Type.rule ^ TemplatedType.rule) + \
Optional(LPAREN + RPAREN) # Needed to parse the parens for default constructors
)("default")
).setParseAction(lambda t: Argument(t.ctype, t.name, t.default))
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],
Expand All @@ -44,18 +46,8 @@ def __init__(self,
else:
self.ctype = ctype
self.name = name
# If the length is 1, it's a regular type,
if len(default) == 1:
default = default[0]
# 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
self.parent = None # type: Union[ArgumentList, None]

def __repr__(self) -> str:
return self.to_cpp()
Expand Down Expand Up @@ -94,10 +86,14 @@ def __repr__(self) -> str:
def __len__(self) -> int:
return len(self.args_list)

def args_names(self) -> List[str]:
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]
Expand Down
2 changes: 1 addition & 1 deletion wrap/gtwrap/interface_parser/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def find_class_or_function(
res = []
for namespace in found_namespaces:
classes_and_funcs = (c for c in namespace.content
if isinstance(c, (Class, GlobalFunction)))
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(
Expand Down
28 changes: 16 additions & 12 deletions wrap/gtwrap/interface_parser/tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
Author: Duy Nguyen Ta, Fan Jiang, Matthew Sklar, Varun Agrawal, and Frank Dellaert
"""

from pyparsing import (Keyword, Literal, Or, QuotedString, Suppress, Word,
alphanums, alphas, delimitedList, nums,
pyparsing_common)
from pyparsing import (Keyword, Literal, OneOrMore, Or, QuotedString, Suppress,
Word, alphanums, alphas, nestedExpr, nums,
originalTextFor, printables)

# rule for identifiers (e.g. variable names)
IDENT = Word(alphas + '_', alphanums + '_') ^ Word(nums)
Expand All @@ -22,16 +22,20 @@
LPAREN, RPAREN, LBRACE, RBRACE, COLON, SEMI_COLON = map(Suppress, "(){}:;")
LOPBRACK, ROPBRACK, COMMA, EQUAL = map(Suppress, "<>,=")

# Encapsulating type for numbers, and single and double quoted strings.
# The pyparsing_common utilities ensure correct coversion to the corresponding type.
# E.g. pyparsing_common.number will convert 3.1415 to a float type.
NUMBER_OR_STRING = (pyparsing_common.number ^ QuotedString('"') ^ QuotedString("'"))

# A python tuple, e.g. (1, 9, "random", 3.1415)
TUPLE = (LPAREN + delimitedList(NUMBER_OR_STRING) + RPAREN)

# Default argument passed to functions/methods.
DEFAULT_ARG = (NUMBER_OR_STRING ^ pyparsing_common.identifier ^ TUPLE)
# Allow anything up to ',' or ';' except when they
# appear inside matched expressions such as
# (a, b) {c, b} "hello, world", templates, initializer lists, etc.
DEFAULT_ARG = originalTextFor(
OneOrMore(
QuotedString('"') ^ # parse double quoted strings
QuotedString("'") ^ # parse single quoted strings
Word(printables, excludeChars="(){}[]<>,;") ^ # parse arbitrary words
nestedExpr(opener='(', closer=')') ^ # parse expression in parentheses
nestedExpr(opener='[', closer=']') ^ # parse expression in brackets
nestedExpr(opener='{', closer='}') ^ # parse expression in braces
nestedExpr(opener='<', closer='>') # parse template expressions
))

CONST, VIRTUAL, CLASS, STATIC, PAIR, TEMPLATE, TYPEDEF, INCLUDE = map(
Keyword,
Expand Down
16 changes: 7 additions & 9 deletions wrap/gtwrap/interface_parser/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from pyparsing import Optional, ParseResults

from .tokens import DEFAULT_ARG, EQUAL, IDENT, SEMI_COLON, STATIC
from .tokens import DEFAULT_ARG, EQUAL, IDENT, SEMI_COLON
from .type import TemplatedType, Type


Expand All @@ -32,10 +32,12 @@ class Hello {
"""
rule = ((Type.rule ^ TemplatedType.rule)("ctype") #
+ IDENT("name") #
#TODO(Varun) Add support for non-basic types
+ Optional(EQUAL + (DEFAULT_ARG))("default") #
+ Optional(EQUAL + DEFAULT_ARG)("default") #
+ SEMI_COLON #
).setParseAction(lambda t: Variable(t.ctype, t.name, t.default))
).setParseAction(lambda t: Variable(
t.ctype, #
t.name, #
t.default[0] if isinstance(t.default, ParseResults) else None))

def __init__(self,
ctype: Type,
Expand All @@ -44,11 +46,7 @@ def __init__(self,
parent=''):
self.ctype = ctype[0] # ParseResult is a list
self.name = name
if default:
self.default = default[0]
else:
self.default = None

self.default = default
self.parent = parent

def __repr__(self) -> str:
Expand Down
Loading