338 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
		
		
			
		
	
	
			338 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
|  | """
 | ||
|  | 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++ classes. | ||
|  | 
 | ||
|  | Author: Duy Nguyen Ta, Fan Jiang, Matthew Sklar, Varun Agrawal, and Frank Dellaert | ||
|  | """
 | ||
|  | 
 | ||
|  | from typing import Any, Iterable, List, Union | ||
|  | 
 | ||
|  | from pyparsing import Literal, Optional, ZeroOrMore  # type: ignore | ||
|  | 
 | ||
|  | from .enum import Enum | ||
|  | from .function import ArgumentList, ReturnType | ||
|  | from .template import Template | ||
|  | 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 | ||
|  | 
 | ||
|  | 
 | ||
|  | class Method: | ||
|  |     """
 | ||
|  |     Rule to parse a method in a class. | ||
|  | 
 | ||
|  |     E.g. | ||
|  |     ``` | ||
|  |     class Hello { | ||
|  |         void sayHello() const; | ||
|  |     }; | ||
|  |     ``` | ||
|  |     """
 | ||
|  |     rule = ( | ||
|  |         Optional(Template.rule("template"))  # | ||
|  |         + ReturnType.rule("return_type")  # | ||
|  |         + IDENT("name")  # | ||
|  |         + LPAREN  # | ||
|  |         + ArgumentList.rule("args_list")  # | ||
|  |         + RPAREN  # | ||
|  |         + Optional(CONST("is_const"))  # | ||
|  |         + SEMI_COLON  # BR | ||
|  |     ).setParseAction(lambda t: Method(t.template, t.name, t.return_type, t. | ||
|  |                                       args_list, t.is_const)) | ||
|  | 
 | ||
|  |     def __init__(self, | ||
|  |                  template: Union[Template, Any], | ||
|  |                  name: str, | ||
|  |                  return_type: ReturnType, | ||
|  |                  args: ArgumentList, | ||
|  |                  is_const: str, | ||
|  |                  parent: Union["Class", Any] = ''): | ||
|  |         self.template = template | ||
|  |         self.name = name | ||
|  |         self.return_type = return_type | ||
|  |         self.args = args | ||
|  |         self.is_const = is_const | ||
|  | 
 | ||
|  |         self.parent = parent | ||
|  | 
 | ||
|  |     def to_cpp(self) -> str: | ||
|  |         """Generate the C++ code for wrapping.""" | ||
|  |         return self.name | ||
|  | 
 | ||
|  |     def __repr__(self) -> str: | ||
|  |         return "Method: {} {} {}({}){}".format( | ||
|  |             self.template, | ||
|  |             self.return_type, | ||
|  |             self.name, | ||
|  |             self.args, | ||
|  |             self.is_const, | ||
|  |         ) | ||
|  | 
 | ||
|  | 
 | ||
|  | class StaticMethod: | ||
|  |     """
 | ||
|  |     Rule to parse all the static methods in a class. | ||
|  | 
 | ||
|  |     E.g. | ||
|  |     ``` | ||
|  |     class Hello { | ||
|  |         static void changeGreeting(); | ||
|  |     }; | ||
|  |     ``` | ||
|  |     """
 | ||
|  |     rule = ( | ||
|  |         Optional(Template.rule("template"))  # | ||
|  |         + STATIC  # | ||
|  |         + ReturnType.rule("return_type")  # | ||
|  |         + IDENT("name")  # | ||
|  |         + LPAREN  # | ||
|  |         + ArgumentList.rule("args_list")  # | ||
|  |         + RPAREN  # | ||
|  |         + SEMI_COLON  # BR | ||
|  |     ).setParseAction( | ||
|  |         lambda t: StaticMethod(t.name, t.return_type, t.args_list, t.template)) | ||
|  | 
 | ||
|  |     def __init__(self, | ||
|  |                  name: str, | ||
|  |                  return_type: ReturnType, | ||
|  |                  args: ArgumentList, | ||
|  |                  template: Union[Template, Any] = None, | ||
|  |                  parent: Union["Class", Any] = ''): | ||
|  |         self.name = name | ||
|  |         self.return_type = return_type | ||
|  |         self.args = args | ||
|  |         self.template = template | ||
|  | 
 | ||
|  |         self.parent = parent | ||
|  | 
 | ||
|  |     def __repr__(self) -> str: | ||
|  |         return "static {} {}{}".format(self.return_type, self.name, self.args) | ||
|  | 
 | ||
|  |     def to_cpp(self) -> str: | ||
|  |         """Generate the C++ code for wrapping.""" | ||
|  |         return self.name | ||
|  | 
 | ||
|  | 
 | ||
|  | class Constructor: | ||
|  |     """
 | ||
|  |     Rule to parse the class constructor. | ||
|  |     Can have 0 or more arguments. | ||
|  |     """
 | ||
|  |     rule = ( | ||
|  |         Optional(Template.rule("template"))  # | ||
|  |         + IDENT("name")  # | ||
|  |         + LPAREN  # | ||
|  |         + ArgumentList.rule("args_list")  # | ||
|  |         + RPAREN  # | ||
|  |         + SEMI_COLON  # BR | ||
|  |     ).setParseAction(lambda t: Constructor(t.name, t.args_list, t.template)) | ||
|  | 
 | ||
|  |     def __init__(self, | ||
|  |                  name: str, | ||
|  |                  args: ArgumentList, | ||
|  |                  template: Union[Template, Any], | ||
|  |                  parent: Union["Class", Any] = ''): | ||
|  |         self.name = name | ||
|  |         self.args = args | ||
|  |         self.template = template | ||
|  | 
 | ||
|  |         self.parent = parent | ||
|  | 
 | ||
|  |     def __repr__(self) -> str: | ||
|  |         return "Constructor: {}{}".format(self.name, self.args) | ||
|  | 
 | ||
|  | 
 | ||
|  | class Operator: | ||
|  |     """
 | ||
|  |     Rule for parsing operator overloads. | ||
|  | 
 | ||
|  |     E.g. | ||
|  |     ``` | ||
|  |     class Overload { | ||
|  |         Vector2 operator+(const Vector2 &v) const; | ||
|  |     }; | ||
|  |     """
 | ||
|  |     rule = ( | ||
|  |         ReturnType.rule("return_type")  # | ||
|  |         + Literal("operator")("name")  # | ||
|  |         + OPERATOR("operator")  # | ||
|  |         + LPAREN  # | ||
|  |         + ArgumentList.rule("args_list")  # | ||
|  |         + RPAREN  # | ||
|  |         + CONST("is_const")  # | ||
|  |         + SEMI_COLON  # BR | ||
|  |     ).setParseAction(lambda t: Operator(t.name, t.operator, t.return_type, t. | ||
|  |                                         args_list, t.is_const)) | ||
|  | 
 | ||
|  |     def __init__(self, | ||
|  |                  name: str, | ||
|  |                  operator: str, | ||
|  |                  return_type: ReturnType, | ||
|  |                  args: ArgumentList, | ||
|  |                  is_const: str, | ||
|  |                  parent: Union["Class", Any] = ''): | ||
|  |         self.name = name | ||
|  |         self.operator = operator | ||
|  |         self.return_type = return_type | ||
|  |         self.args = args | ||
|  |         self.is_const = is_const | ||
|  |         self.is_unary = len(args) == 0 | ||
|  | 
 | ||
|  |         self.parent = parent | ||
|  | 
 | ||
|  |         # Check for valid unary operators | ||
|  |         if self.is_unary and self.operator not in ('+', '-'): | ||
|  |             raise ValueError("Invalid unary operator {} used for {}".format( | ||
|  |                 self.operator, self)) | ||
|  | 
 | ||
|  |         # Check that number of arguments are either 0 or 1 | ||
|  |         assert 0 <= len(args) < 2, \ | ||
|  |             "Operator overload should be at most 1 argument, " \ | ||
|  |                 "{} arguments provided".format(len(args)) | ||
|  | 
 | ||
|  |         # Check to ensure arg and return type are the same. | ||
|  |         if len(args) == 1 and self.operator not in ("()", "[]"): | ||
|  |             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: | ||
|  |         return "Operator: {}{}{}({}) {}".format( | ||
|  |             self.return_type, | ||
|  |             self.name, | ||
|  |             self.operator, | ||
|  |             self.args, | ||
|  |             self.is_const, | ||
|  |         ) | ||
|  | 
 | ||
|  | 
 | ||
|  | class Class: | ||
|  |     """
 | ||
|  |     Rule to parse a class defined in the interface file. | ||
|  | 
 | ||
|  |     E.g. | ||
|  |     ``` | ||
|  |     class Hello { | ||
|  |         ... | ||
|  |     }; | ||
|  |     ``` | ||
|  |     """
 | ||
|  |     class Members: | ||
|  |         """
 | ||
|  |         Rule for all the members within a class. | ||
|  |         """
 | ||
|  |         rule = ZeroOrMore(Constructor.rule  # | ||
|  |                           ^ Method.rule  # | ||
|  |                           ^ StaticMethod.rule  # | ||
|  |                           ^ Variable.rule  # | ||
|  |                           ^ Operator.rule  # | ||
|  |                           ^ Enum.rule  # | ||
|  |                           ).setParseAction(lambda t: Class.Members(t.asList())) | ||
|  | 
 | ||
|  |         def __init__(self, | ||
|  |                      members: List[Union[Constructor, Method, StaticMethod, | ||
|  |                                          Variable, Operator]]): | ||
|  |             self.ctors = [] | ||
|  |             self.methods = [] | ||
|  |             self.static_methods = [] | ||
|  |             self.properties = [] | ||
|  |             self.operators = [] | ||
|  |             self.enums: List[Enum] = [] | ||
|  |             for m in members: | ||
|  |                 if isinstance(m, Constructor): | ||
|  |                     self.ctors.append(m) | ||
|  |                 elif isinstance(m, Method): | ||
|  |                     self.methods.append(m) | ||
|  |                 elif isinstance(m, StaticMethod): | ||
|  |                     self.static_methods.append(m) | ||
|  |                 elif isinstance(m, Variable): | ||
|  |                     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 = ( | ||
|  |         Optional(Template.rule("template"))  # | ||
|  |         + Optional(VIRTUAL("is_virtual"))  # | ||
|  |         + CLASS  # | ||
|  |         + IDENT("name")  # | ||
|  |         + Optional(_parent)  # | ||
|  |         + LBRACE  # | ||
|  |         + Members.rule("members")  # | ||
|  |         + RBRACE  # | ||
|  |         + SEMI_COLON  # BR | ||
|  |     ).setParseAction(lambda t: Class( | ||
|  |         t.template, t.is_virtual, t.name, t.parent_class, t.members.ctors, t. | ||
|  |         members.methods, t.members.static_methods, t.members.properties, t. | ||
|  |         members.operators, t.members.enums)) | ||
|  | 
 | ||
|  |     def __init__( | ||
|  |         self, | ||
|  |         template: Union[Template, None], | ||
|  |         is_virtual: str, | ||
|  |         name: str, | ||
|  |         parent_class: list, | ||
|  |         ctors: List[Constructor], | ||
|  |         methods: List[Method], | ||
|  |         static_methods: List[StaticMethod], | ||
|  |         properties: List[Variable], | ||
|  |         operators: List[Operator], | ||
|  |         enums: List[Enum], | ||
|  |         parent: Any = '', | ||
|  |     ): | ||
|  |         self.template = template | ||
|  |         self.is_virtual = is_virtual | ||
|  |         self.name = name | ||
|  |         if parent_class: | ||
|  |             # If it is in an iterable, extract the parent class. | ||
|  |             if isinstance(parent_class, Iterable): | ||
|  |                 parent_class = parent_class[0]  # type: ignore | ||
|  | 
 | ||
|  |             # If the base class is a TemplatedType, | ||
|  |             # we want the instantiated Typename | ||
|  |             if isinstance(parent_class, TemplatedType): | ||
|  |                 parent_class = parent_class.typename  # type: ignore | ||
|  | 
 | ||
|  |             self.parent_class = parent_class | ||
|  |         else: | ||
|  |             self.parent_class = ''  # type: ignore | ||
|  | 
 | ||
|  |         self.ctors = ctors | ||
|  |         self.methods = methods | ||
|  |         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. | ||
|  |         for ctor in self.ctors: | ||
|  |             if ctor.name != self.name: | ||
|  |                 raise ValueError("Error in constructor name! {} != {}".format( | ||
|  |                     ctor.name, self.name)) | ||
|  | 
 | ||
|  |         for ctor in self.ctors: | ||
|  |             ctor.parent = self | ||
|  |         for method in self.methods: | ||
|  |             method.parent = self | ||
|  |         for static_method in self.static_methods: | ||
|  |             static_method.parent = self | ||
|  |         for _property in self.properties: | ||
|  |             _property.parent = self | ||
|  | 
 | ||
|  |     def namespaces(self) -> list: | ||
|  |         """Get the namespaces which this class is nested under as a list.""" | ||
|  |         return collect_namespaces(self) | ||
|  | 
 | ||
|  |     def __repr__(self): | ||
|  |         return "Class: {self.name}".format(self=self) |