336 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			336 lines
		
	
	
		
			9.6 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 Iterable, List, Union
 | |
| 
 | |
| 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, 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: str,
 | |
|                  name: str,
 | |
|                  return_type: ReturnType,
 | |
|                  args: ArgumentList,
 | |
|                  is_const: str,
 | |
|                  parent: Union[str, "Class"] = ''):
 | |
|         self.template = template
 | |
|         self.name = name
 | |
|         self.return_type = return_type
 | |
|         self.args = args
 | |
|         self.is_const = is_const
 | |
| 
 | |
|         self.parent = parent
 | |
| 
 | |
|     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 = (
 | |
|         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))
 | |
| 
 | |
|     def __init__(self,
 | |
|                  name: str,
 | |
|                  return_type: ReturnType,
 | |
|                  args: ArgumentList,
 | |
|                  parent: Union[str, "Class"] = ''):
 | |
|         self.name = name
 | |
|         self.return_type = return_type
 | |
|         self.args = args
 | |
| 
 | |
|         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 = (
 | |
|         IDENT("name")  #
 | |
|         + LPAREN  #
 | |
|         + ArgumentList.rule("args_list")  #
 | |
|         + RPAREN  #
 | |
|         + SEMI_COLON  # BR
 | |
|     ).setParseAction(lambda t: Constructor(t.name, t.args_list))
 | |
| 
 | |
|     def __init__(self,
 | |
|                  name: str,
 | |
|                  args: ArgumentList,
 | |
|                  parent: Union["Class", str] = ''):
 | |
|         self.name = name
 | |
|         self.args = args
 | |
| 
 | |
|         self.parent = parent
 | |
| 
 | |
|     def __repr__(self) -> str:
 | |
|         return "Constructor: {}".format(self.name)
 | |
| 
 | |
| 
 | |
| 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[str, "Class"] = ''):
 | |
|         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.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  #
 | |
|                           ^ 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,
 | |
|                                          Variable, Operator]]):
 | |
|             self.ctors = []
 | |
|             self.methods = []
 | |
|             self.static_methods = []
 | |
|             self.properties = []
 | |
|             self.operators = []
 | |
|             self.enums = []
 | |
|             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: Template,
 | |
|         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: str = '',
 | |
|     ):
 | |
|         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]
 | |
| 
 | |
|             # If the base class is a TemplatedType,
 | |
|             # we want the instantiated Typename
 | |
|             if isinstance(parent_class, TemplatedType):
 | |
|                 parent_class = parent_class.typename
 | |
| 
 | |
|             self.parent_class = parent_class
 | |
|         else:
 | |
|             self.parent_class = ''
 | |
| 
 | |
|         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)
 |