1#!/usr/bin/env python 2 3""" 4DataCollectingParser subclasses ctypesparser.CtypesParser and builds Description 5objects from the CtypesType objects and other information from CtypesParser. 6After parsing is complete, a DescriptionCollection object can be retrieved by 7calling DataCollectingParser.data(). 8""" 9 10from . import ctypesparser 11from ..descriptions import * 12from ..ctypedescs import * 13from ..expressions import * 14from ..messages import * 15from tempfile import mkstemp 16import os 17 18 19class DataCollectingParser(ctypesparser.CtypesParser, ctypesparser.CtypesTypeVisitor): 20 """Main class for the Parser component. Steps for use: 21 p=DataCollectingParser(names_of_header_files,options) 22 p.parse() 23 data=p.data() #A dictionary of constants, enums, structs, functions, etc. 24 """ 25 26 def __init__(self, headers, options): 27 super(DataCollectingParser, self).__init__(options) 28 self.headers = headers 29 self.options = options 30 31 self.constants = [] 32 self.typedefs = [] 33 self.structs = [] 34 self.enums = [] 35 self.functions = [] 36 self.variables = [] 37 self.macros = [] 38 39 self.all = [] 40 self.output_order = [] 41 42 # NULL is a useful macro to have defined 43 null = ConstantExpressionNode(None) 44 nullmacro = ConstantDescription("NULL", null, ("<built-in>", 1)) 45 self.constants.append(nullmacro) 46 self.all.append(nullmacro) 47 self.output_order.append(("constant", nullmacro)) 48 49 # A list of tuples describing macros; saved to be processed after 50 # everything else has been parsed 51 self.saved_macros = [] 52 # A set of structs that are already known 53 self.already_seen_structs = set() 54 # A dict of structs that have only been seen in opaque form 55 self.already_seen_opaque_structs = {} 56 # A set of enums that are already known 57 self.already_seen_enums = set() 58 # A dict of enums that have only been seen in opaque form 59 self.already_seen_opaque_enums = {} 60 61 def parse(self): 62 fd, fname = mkstemp(suffix=".h") 63 with os.fdopen(fd, "w") as f: 64 for header in self.options.other_headers: 65 f.write("#include <%s>\n" % header) 66 for header in self.headers: 67 f.write('#include "%s"\n' % os.path.abspath(header)) 68 f.flush() 69 try: 70 super(DataCollectingParser, self).parse(fname, self.options.debug_level) 71 finally: 72 os.unlink(fname) 73 74 for name, params, expr, (filename, lineno) in self.saved_macros: 75 self.handle_macro(name, params, expr, filename, lineno) 76 77 def handle_define_constant(self, name, expr, filename, lineno): 78 # Called by CParser 79 # Save to handle later 80 self.saved_macros.append((name, None, expr, (filename, lineno))) 81 82 def handle_define_unparseable(self, name, params, value, filename, lineno): 83 # Called by CParser 84 if params: 85 original_string = "#define %s(%s) %s" % (name, ",".join(params), " ".join(value)) 86 else: 87 original_string = "#define %s %s" % (name, " ".join(value)) 88 macro = MacroDescription(name, params, None, src=(filename, lineno)) 89 macro.error('Could not parse macro "%s"' % original_string, cls="macro") 90 macro.original_string = original_string 91 self.macros.append(macro) 92 self.all.append(macro) 93 self.output_order.append(("macro", macro)) 94 95 def handle_define_macro(self, name, params, expr, filename, lineno): 96 # Called by CParser 97 # Save to handle later 98 self.saved_macros.append((name, params, expr, (filename, lineno))) 99 100 def handle_undefine(self, macro, filename, lineno): 101 # save to handle later to get order correct 102 self.saved_macros.append(("#undef", None, macro, (filename, lineno))) 103 104 def handle_ctypes_typedef(self, name, ctype, filename, lineno): 105 # Called by CtypesParser 106 ctype.visit(self) 107 108 typedef = TypedefDescription(name, ctype, src=(filename, repr(lineno))) 109 110 self.typedefs.append(typedef) 111 self.all.append(typedef) 112 self.output_order.append(("typedef", typedef)) 113 114 def handle_ctypes_new_type(self, ctype, filename, lineno): 115 # Called by CtypesParser 116 if isinstance(ctype, ctypesparser.CtypesEnum): 117 self.handle_enum(ctype, filename, lineno) 118 else: 119 self.handle_struct(ctype, filename, lineno) 120 121 def handle_ctypes_function( 122 self, name, restype, argtypes, errcheck, variadic, attrib, filename, lineno 123 ): 124 # Called by CtypesParser 125 restype.visit(self) 126 for argtype in argtypes: 127 argtype.visit(self) 128 129 function = FunctionDescription( 130 name, restype, argtypes, errcheck, variadic, attrib, src=(filename, repr(lineno)) 131 ) 132 133 self.functions.append(function) 134 self.all.append(function) 135 self.output_order.append(("function", function)) 136 137 def handle_ctypes_variable(self, name, ctype, filename, lineno): 138 # Called by CtypesParser 139 ctype.visit(self) 140 141 variable = VariableDescription(name, ctype, src=(filename, repr(lineno))) 142 143 self.variables.append(variable) 144 self.all.append(variable) 145 self.output_order.append(("variable", variable)) 146 147 def handle_struct(self, ctypestruct, filename, lineno): 148 # Called from within DataCollectingParser 149 150 # When we find an opaque struct, we make a StructDescription for it 151 # and record it in self.already_seen_opaque_structs. If we later 152 # find a transparent struct with the same tag, we fill in the 153 # opaque struct with the information from the transparent struct and 154 # move the opaque struct to the end of the struct list. 155 156 name = "%s %s" % (ctypestruct.variety, ctypestruct.tag) 157 158 if name in self.already_seen_structs: 159 return 160 161 if ctypestruct.opaque: 162 if name not in self.already_seen_opaque_structs: 163 struct = StructDescription( 164 ctypestruct.tag, 165 ctypestruct.attrib, 166 ctypestruct.variety, 167 None, # No members 168 True, # Opaque 169 ctypestruct, 170 src=(filename, str(lineno)), 171 ) 172 173 self.already_seen_opaque_structs[name] = struct 174 self.structs.append(struct) 175 self.all.append(struct) 176 self.output_order.append(("struct", struct)) 177 178 else: 179 for (membername, ctype) in ctypestruct.members: 180 ctype.visit(self) 181 182 if name in self.already_seen_opaque_structs: 183 # Fill in older version 184 struct = self.already_seen_opaque_structs[name] 185 struct.opaque = False 186 struct.members = ctypestruct.members 187 struct.ctype = ctypestruct 188 struct.src = ctypestruct.src 189 190 self.output_order.append(("struct-body", struct)) 191 192 del self.already_seen_opaque_structs[name] 193 194 else: 195 struct = StructDescription( 196 ctypestruct.tag, 197 ctypestruct.attrib, 198 ctypestruct.variety, 199 ctypestruct.members, 200 False, # Not opaque 201 src=(filename, str(lineno)), 202 ctype=ctypestruct, 203 ) 204 self.structs.append(struct) 205 self.all.append(struct) 206 self.output_order.append(("struct", struct)) 207 self.output_order.append(("struct-body", struct)) 208 209 self.already_seen_structs.add(name) 210 211 def handle_enum(self, ctypeenum, filename, lineno): 212 # Called from within DataCollectingParser. 213 214 # Process for handling opaque enums is the same as process for opaque 215 # structs. See handle_struct() for more details. 216 217 tag = ctypeenum.tag 218 if tag in self.already_seen_enums: 219 return 220 221 if ctypeenum.opaque: 222 if tag not in self.already_seen_opaque_enums: 223 enum = EnumDescription(ctypeenum.tag, None, ctypeenum, src=(filename, str(lineno))) 224 enum.opaque = True 225 226 self.already_seen_opaque_enums[tag] = enum 227 self.enums.append(enum) 228 self.all.append(enum) 229 self.output_order.append(("enum", enum)) 230 231 else: 232 if tag in self.already_seen_opaque_enums: 233 # Fill in older opaque version 234 enum = self.already_seen_opaque_enums[tag] 235 enum.opaque = False 236 enum.ctype = ctypeenum 237 enum.src = ctypeenum.src 238 enum.members = ctypeenum.enumerators 239 240 del self.already_seen_opaque_enums[tag] 241 242 else: 243 enum = EnumDescription( 244 ctypeenum.tag, 245 ctypeenum.enumerators, 246 src=(filename, str(lineno)), 247 ctype=ctypeenum, 248 ) 249 enum.opaque = False 250 251 self.enums.append(enum) 252 self.all.append(enum) 253 self.output_order.append(("enum", enum)) 254 255 self.already_seen_enums.add(tag) 256 257 for (enumname, expr) in ctypeenum.enumerators: 258 constant = ConstantDescription(enumname, expr, src=(filename, lineno)) 259 260 self.constants.append(constant) 261 self.all.append(constant) 262 self.output_order.append(("constant", constant)) 263 264 def handle_macro(self, name, params, expr, filename, lineno): 265 # Called from within DataCollectingParser 266 src = (filename, lineno) 267 268 if expr == None: 269 expr = ConstantExpressionNode(True) 270 constant = ConstantDescription(name, expr, src) 271 self.constants.append(constant) 272 self.all.append(constant) 273 return 274 275 expr.visit(self) 276 277 if isinstance(expr, CtypesType): 278 if params: 279 macro = MacroDescription(name, "", src) 280 macro.error( 281 "%s has parameters but evaluates to a type. " 282 "Ctypesgen does not support it." % macro.casual_name(), 283 cls="macro", 284 ) 285 self.macros.append(macro) 286 self.all.append(macro) 287 self.output_order.append(("macro", macro)) 288 289 else: 290 typedef = TypedefDescription(name, expr, src) 291 self.typedefs.append(typedef) 292 self.all.append(typedef) 293 self.output_order.append(("typedef", typedef)) 294 295 elif name == "#undef": 296 undef = UndefDescription(expr, src) 297 self.all.append(undef) 298 self.output_order.append(("undef", undef)) 299 else: 300 macro = MacroDescription(name, params, expr, src) 301 self.macros.append(macro) 302 self.all.append(macro) 303 self.output_order.append(("macro", macro)) 304 305 # Macros could possibly contain things like __FILE__, __LINE__, etc... 306 # This could be supported, but it would be a lot of work. It would 307 # probably also bloat the Preamble considerably. 308 309 def handle_error(self, message, filename, lineno): 310 # Called by CParser 311 error_message("%s:%d: %s" % (filename, lineno, message), cls="cparser") 312 313 def handle_pp_error(self, message): 314 # Called by PreprocessorParser 315 error_message("%s: %s" % (self.options.cpp, message), cls="cparser") 316 317 def handle_status(self, message): 318 # Called by CParser 319 status_message(message) 320 321 def visit_struct(self, struct): 322 self.handle_struct(struct, struct.src[0], struct.src[1]) 323 324 def visit_enum(self, enum): 325 self.handle_enum(enum, enum.src[0], enum.src[1]) 326 327 def data(self): 328 return DescriptionCollection( 329 self.constants, 330 self.typedefs, 331 self.structs, 332 self.enums, 333 self.functions, 334 self.variables, 335 self.macros, 336 self.all, 337 self.output_order, 338 ) 339