def __init__(self, enums_dict=None, consts_dict=None, typedefs_dict=None, hints_dict=None, structs_dict=None, funcs_dict=None, strings_dict=None, func_ptrs_dict=None, index_dict=None): """Constructs a new HeaderParser instance. The optional arguments listed below can be used to passing in dict-like objects specifying pre-defined declarations. By default empty UniqueOrderedDicts will be instantiated and then populated according to the contents of the headers. Args: enums_dict: nested mappings from {enum_name: {member_name: value}} consts_dict: mapping from {const_name: value} typedefs_dict: mapping from {type_name: ctypes_typename} hints_dict: mapping from {var_name: shape_tuple} structs_dict: mapping from {struct_name: Struct_instance} funcs_dict: mapping from {func_name: Function_instance} strings_dict: mapping from {var_name: StaticStringArray_instance} func_ptrs_dict: mapping from {var_name: FunctionPtr_instance} index_dict: mapping from {lowercase_struct_name: {var_name: shape_tuple}} """ self.enums_dict = (enums_dict if enums_dict is not None else codegen_util.UniqueOrderedDict()) self.consts_dict = (consts_dict if consts_dict is not None else codegen_util.UniqueOrderedDict()) self.typedefs_dict = (typedefs_dict if typedefs_dict is not None else codegen_util.UniqueOrderedDict()) self.hints_dict = (hints_dict if hints_dict is not None else codegen_util.UniqueOrderedDict()) self.structs_dict = (structs_dict if structs_dict is not None else codegen_util.UniqueOrderedDict()) self.funcs_dict = (funcs_dict if funcs_dict is not None else codegen_util.UniqueOrderedDict()) self.strings_dict = (strings_dict if strings_dict is not None else codegen_util.UniqueOrderedDict()) self.func_ptrs_dict = (func_ptrs_dict if func_ptrs_dict is not None else codegen_util.UniqueOrderedDict()) self.index_dict = (index_dict if index_dict is not None else codegen_util.UniqueOrderedDict())
def parse_functions(self, src): """Updates self.funcs_dict.""" parser = header_parsing.MJAPI_FUNCTION_DECL for tokens, _, _ in parser.scanString(src): for token in tokens: name = codegen_util.mangle_varname(token.name) comment = codegen_util.mangle_comment(token.comment) args = codegen_util.UniqueOrderedDict() for arg in token.arguments: a = self.get_type_from_token(arg) args[a.name] = a r = self.get_type_from_token(token.return_value) f = c_declarations.Function(name, args, r, comment) self.funcs_dict[f.name] = f
def parse_enums(self, src): """Parses mj*.h, update self.enums_dict.""" parser = header_parsing.ENUM_DECL for tokens, _, _ in parser.scanString(src): for enum in tokens: members = codegen_util.UniqueOrderedDict() value = 0 for member in enum.members: # Leftward bitshift if member.bit_lshift_a: value = int(member.bit_lshift_a) << int(member.bit_lshift_b) # Assignment elif member.value: value = int(member.value) # Implicit count else: value += 1 members.update({member.name: value}) self.enums_dict.update({enum.name: members})
def get_type_from_token(self, token, parent=None): """Accepts a token returned by a parser, returns a subclass of CDeclBase.""" comment = codegen_util.mangle_comment(token.comment) is_const = token.is_const == "const" # A new struct declaration if token.members: name = token.name # If the name is empty, see if there is a type declaration that matches # this struct's typename if not name: for k, v in six.iteritems(self.typedefs_dict): if v == token.typename: name = k # Anonymous structs need a dummy typename typename = token.typename if not typename: if parent: typename = token.name else: raise Error( "Anonymous structs that aren't members of a named struct are not " "supported (name = '{token.name}').".format( token=token)) # Mangle the name if it contains any protected keywords name = codegen_util.mangle_varname(name) members = codegen_util.UniqueOrderedDict() sub_structs = codegen_util.UniqueOrderedDict() out = c_declarations.Struct(name, typename, members, sub_structs, comment, parent, is_const) # Map the old typename to the mangled typename in typedefs_dict self.typedefs_dict[typename] = out.ctypes_typename # Add members for sub_token in token.members: # Recurse into nested structs member = self.get_type_from_token(sub_token, parent=out) out.members[member.name] = member # Nested sub-structures need special treatment if isinstance(member, c_declarations.Struct): out.sub_structs[member.name] = member # Add to dict of structs self.structs_dict[out.ctypes_typename] = out else: name = codegen_util.mangle_varname(token.name) typename = self.resolve_typename(token.typename) # 1D array with size defined at compile time if token.size: shape = self.get_shape_tuple(token.size) if typename in header_parsing.CTYPES_TO_NUMPY: out = c_declarations.StaticNDArray(name, typename, shape, comment, parent, is_const) else: out = c_declarations.StaticPtrArray( name, typename, shape, comment, parent, is_const) elif token.ptr: # Pointer to a numpy-compatible type, could be an array or a scalar if typename in header_parsing.CTYPES_TO_NUMPY: # Multidimensional array (one or more dimensions might be undefined) if name in self.hints_dict: # Dynamically-sized dimensions have string identifiers shape = self.hints_dict[name] if any(isinstance(d, str) for d in shape): out = c_declarations.DynamicNDArray( name, typename, shape, comment, parent, is_const) else: out = c_declarations.StaticNDArray( name, typename, shape, comment, parent, is_const) # This must be a pointer to a scalar primitive else: out = c_declarations.ScalarPrimitivePtr( name, typename, comment, parent, is_const) # Pointer to struct or other arbitrary type else: out = c_declarations.ScalarPrimitivePtr( name, typename, comment, parent, is_const) # A struct we've already encountered elif typename in self.structs_dict: s = self.structs_dict[typename] out = c_declarations.Struct(name, s.typename, s.members, s.sub_structs, comment, parent) # Presumably this is a scalar primitive else: out = c_declarations.ScalarPrimitive(name, typename, comment, parent, is_const) return out
def main(unused_argv): special_header_paths = {} # Get the path to the mjmodel and mjxmacro header files. # These header files need special handling. for header in (_MJMODEL_H, _MJXMACRO_H): for path in FLAGS.header_paths: if path.endswith(header): special_header_paths[header] = path break if header not in special_header_paths: logging.fatal("List of inputs must contain a path to %s", header) # Make sure mjmodel.h is parsed first, since it is included by other headers. srcs = codegen_util.UniqueOrderedDict() sorted_header_paths = sorted(FLAGS.header_paths) sorted_header_paths.remove(special_header_paths[_MJMODEL_H]) sorted_header_paths.insert(0, special_header_paths[_MJMODEL_H]) for p in sorted_header_paths: with io.open(p, "r", errors="ignore") as f: srcs[p] = f.read() # consts_dict should be a codegen_util.UniqueOrderedDict. # This is a temporary workaround due to the fact that the parser does not yet # handle nested `#if define(predicate)` blocks, which results in some # constants being parsed twice. We therefore can't enforce the uniqueness of # the keys in `consts_dict`. As of MuJoCo v1.30 there is only a single problem # block beginning on line 10 in mujoco.h, and a single constant is affected # (MJAPI). consts_dict = collections.OrderedDict() # These are commented in `mjdata.h` but have no macros in `mjxmacro.h`. hints_dict = codegen_util.UniqueOrderedDict({"buffer": ("nbuffer",), "stack": ("nstack",)}) parser = binding_generator.BindingGenerator( consts_dict=consts_dict, hints_dict=hints_dict) # Parse enums. for pth, src in six.iteritems(srcs): if pth is not special_header_paths[_MJXMACRO_H]: parser.parse_enums(src) # Parse constants and type declarations. for pth, src in six.iteritems(srcs): if pth is not special_header_paths[_MJXMACRO_H]: parser.parse_consts_typedefs(src) # Get shape hints from mjxmacro.h. parser.parse_hints(srcs[special_header_paths[_MJXMACRO_H]]) # Parse structs and function pointer type declarations. for pth, src in six.iteritems(srcs): if pth is not special_header_paths[_MJXMACRO_H]: parser.parse_structs_and_function_pointer_typedefs(src) # Parse functions. for pth, src in six.iteritems(srcs): if pth is not special_header_paths[_MJXMACRO_H]: parser.parse_functions(src) # Parse global strings and function pointers. for pth, src in six.iteritems(srcs): if pth is not special_header_paths[_MJXMACRO_H]: parser.parse_global_strings(src) parser.parse_function_pointers(src) # Create the output directory if it doesn't already exist. if not os.path.exists(FLAGS.output_dir): os.makedirs(FLAGS.output_dir) # Generate Python source files and write them to the output directory. parser.write_consts(os.path.join(FLAGS.output_dir, "constants.py")) parser.write_enums(os.path.join(FLAGS.output_dir, "enums.py")) parser.write_types(os.path.join(FLAGS.output_dir, "types.py")) parser.write_wrappers(os.path.join(FLAGS.output_dir, "wrappers.py")) parser.write_funcs_and_globals(os.path.join(FLAGS.output_dir, "functions.py")) parser.write_index_dict(os.path.join(FLAGS.output_dir, "sizes.py"))
def main(unused_argv): # Get the path to the xmacro header file. xmacro_hdr_path = None for path in FLAGS.header_paths: if path.endswith("mjxmacro.h"): xmacro_hdr_path = path break if xmacro_hdr_path is None: logging.fatal("List of inputs must contain a path to mjxmacro.h") srcs = codegen_util.UniqueOrderedDict() for p in sorted(FLAGS.header_paths): with open(p, "r") as f: srcs[p] = f.read() # consts_dict should be a codegen_util.UniqueOrderedDict. # This is a temporary workaround due to the fact that the parser does not yet # handle nested `#if define(predicate)` blocks, which results in some # constants being parsed twice. We therefore can't enforce the uniqueness of # the keys in `consts_dict`. As of MuJoCo v1.30 there is only a single problem # block beginning on line 10 in mujoco.h, and a single constant is affected # (MJAPI). consts_dict = collections.OrderedDict() # These are commented in `mjdata.h` but have no macros in `mjxmacro.h`. hints_dict = codegen_util.UniqueOrderedDict({ "buffer": ("nbuffer", ), "stack": ("nstack", ) }) parser = binding_generator.BindingGenerator(consts_dict=consts_dict, hints_dict=hints_dict) # Parse enums. for pth, src in six.iteritems(srcs): if pth is not xmacro_hdr_path: parser.parse_enums(src) # Parse constants and type declarations. for pth, src in six.iteritems(srcs): if pth is not xmacro_hdr_path: parser.parse_consts_typedefs(src) # Get shape hints from mjxmacro.h. parser.parse_hints(srcs[xmacro_hdr_path]) # Parse structs. for pth, src in six.iteritems(srcs): if pth is not xmacro_hdr_path: parser.parse_structs(src) # Parse functions. for pth, src in six.iteritems(srcs): if pth is not xmacro_hdr_path: parser.parse_functions(src) # Parse global strings and function pointers. for pth, src in six.iteritems(srcs): if pth is not xmacro_hdr_path: parser.parse_global_strings(src) parser.parse_function_pointers(src) # Create the output directory if it doesn't already exist. if not os.path.exists(FLAGS.output_dir): os.makedirs(FLAGS.output_dir) # Generate Python source files and write them to the output directory. parser.write_consts(os.path.join(FLAGS.output_dir, "constants.py")) parser.write_enums(os.path.join(FLAGS.output_dir, "enums.py")) parser.write_types(os.path.join(FLAGS.output_dir, "types.py")) parser.write_wrappers(os.path.join(FLAGS.output_dir, "wrappers.py")) parser.write_funcs_and_globals( os.path.join(FLAGS.output_dir, "functions.py")) parser.write_index_dict(os.path.join(FLAGS.output_dir, "sizes.py"))