Dan Talayco | f75360a | 2010-02-05 22:22:54 -0800 | [diff] [blame^] | 1 | """This module parse and store a C/C++ header file. |
| 2 | |
| 3 | Date June 2009 |
| 4 | Created by ykk |
| 5 | """ |
| 6 | import re |
| 7 | from config import * |
| 8 | |
| 9 | class textfile: |
| 10 | """Class to handle text file. |
| 11 | |
| 12 | Date June 2009 |
| 13 | Created by ykk |
| 14 | """ |
| 15 | def __init__(self, filename): |
| 16 | """Initialize filename with no content. |
| 17 | """ |
| 18 | ##Filename |
| 19 | if (isinstance(filename, str)): |
| 20 | self.filename = [] |
| 21 | self.filename.append(filename) |
| 22 | else: |
| 23 | self.filename = filename |
| 24 | ##Content |
| 25 | self.content = [] |
| 26 | |
| 27 | def read(self): |
| 28 | """Read file |
| 29 | """ |
| 30 | for filename in self.filename: |
| 31 | fileRef = open(filename, "r") |
| 32 | for line in fileRef: |
| 33 | self.content.append(line) |
| 34 | fileRef.close() |
| 35 | |
| 36 | class ctype: |
| 37 | """Class to represent types in C |
| 38 | """ |
| 39 | def __init__(self,typename, name=None, expanded=False): |
| 40 | """Initialize |
| 41 | """ |
| 42 | ##Name |
| 43 | self.name = name |
| 44 | ##Type of primitive |
| 45 | self.typename = typename |
| 46 | ##Expanded |
| 47 | self.expanded = expanded |
| 48 | |
| 49 | def expand(self, cheader): |
| 50 | """Expand type if applicable |
| 51 | """ |
| 52 | raise NotImplementedError() |
| 53 | |
| 54 | def get_names(self): |
| 55 | """Return name of variables |
| 56 | """ |
| 57 | raise NotImplementedError() |
| 58 | |
| 59 | class cprimitive(ctype): |
| 60 | """Class to represent C primitive |
| 61 | |
| 62 | Date October 2009 |
| 63 | Created by ykk |
| 64 | """ |
| 65 | def __init__(self,typename, name=None): |
| 66 | """Initialize and store primitive |
| 67 | """ |
| 68 | ctype.__init__(self, typename, name, True) |
| 69 | |
| 70 | def __str__(self): |
| 71 | """Return string representation |
| 72 | """ |
| 73 | if (self.name == None): |
| 74 | return self.typename |
| 75 | else: |
| 76 | return self.typename+" "+str(self.name) |
| 77 | |
| 78 | def expand(self, cheader): |
| 79 | """Expand type if applicable |
| 80 | """ |
| 81 | pass |
| 82 | |
| 83 | def get_names(self): |
| 84 | """Return name of variables |
| 85 | """ |
| 86 | namelist = [] |
| 87 | namelist.append(self.name) |
| 88 | return namelist |
| 89 | |
| 90 | class cstruct(ctype): |
| 91 | """Class to represent C struct |
| 92 | |
| 93 | Date October 2009 |
| 94 | Created by ykk |
| 95 | """ |
| 96 | def __init__(self, typename, name=None): |
| 97 | """Initialize struct |
| 98 | """ |
| 99 | ctype.__init__(self, typename, name) |
| 100 | ##List of members in struct |
| 101 | self.members = [] |
| 102 | |
| 103 | def __str__(self): |
| 104 | """Return string representation |
| 105 | """ |
| 106 | string = "struct "+self.typename |
| 107 | if (self.name != None): |
| 108 | string += " "+self.name |
| 109 | if (len(self.members) == 0): |
| 110 | return string |
| 111 | #Add members |
| 112 | string +=" {\n" |
| 113 | for member in self.members: |
| 114 | string += "\t"+str(member) |
| 115 | if (not isinstance(member, cstruct)): |
| 116 | string += ";" |
| 117 | string += "\n" |
| 118 | string +="};" |
| 119 | return string |
| 120 | |
| 121 | def expand(self, cheader): |
| 122 | """Expand struct |
| 123 | """ |
| 124 | self.expanded = True |
| 125 | #Expanded each member |
| 126 | for member in self.members: |
| 127 | if (isinstance(member, cstruct) and |
| 128 | (not member.expanded)): |
| 129 | try: |
| 130 | if (not cheader.structs[member.typename].expanded): |
| 131 | cheader.structs[member.typename].expand(cheader) |
| 132 | member.members=cheader.structs[member.typename].members[:] |
| 133 | member.expanded = True |
| 134 | except KeyError: |
| 135 | self.expanded=False |
| 136 | else: |
| 137 | member.expand(cheader) |
| 138 | |
| 139 | def get_names(self): |
| 140 | """Return name of variables |
| 141 | """ |
| 142 | namelist = [] |
| 143 | for member in self.members: |
| 144 | if (isinstance(member, cstruct)): |
| 145 | tmplist = member.get_names() |
| 146 | for item in tmplist: |
| 147 | namelist.append(member.name+"."+item) |
| 148 | else: |
| 149 | namelist.extend(member.get_names()) |
| 150 | return namelist |
| 151 | |
| 152 | |
| 153 | class carray(ctype): |
| 154 | """Class to represent C array |
| 155 | |
| 156 | Date October 2009 |
| 157 | Created by ykk |
| 158 | """ |
| 159 | def __init__(self, typename, name, isPrimitive, size): |
| 160 | """Initialize array of object. |
| 161 | """ |
| 162 | ctype.__init__(self, typename, name, |
| 163 | (isinstance(size, int) and isPrimitive)) |
| 164 | ##Object reference |
| 165 | if (isPrimitive): |
| 166 | self.object = cprimitive(typename, name) |
| 167 | else: |
| 168 | self.object = cstruct(typename, name) |
| 169 | ##Size of array |
| 170 | self.size = size |
| 171 | |
| 172 | def __str__(self): |
| 173 | """Return string representation |
| 174 | """ |
| 175 | return str(self.object)+"["+str(self.size)+"]" |
| 176 | |
| 177 | def expand(self, cheader): |
| 178 | """Expand array |
| 179 | """ |
| 180 | self.expanded = True |
| 181 | if (not self.object.expanded): |
| 182 | if (isinstance(self.object, cstruct)): |
| 183 | cheader.structs[self.object.typename].expand(cheader) |
| 184 | self.object.members=cheader.structs[self.object.typename].members[:] |
| 185 | else: |
| 186 | self.object.expand(cheader) |
| 187 | |
| 188 | if (not isinstance(self.size, int)): |
| 189 | val = cheader.get_value(self.size) |
| 190 | if (val == None): |
| 191 | self.expanded = False |
| 192 | else: |
| 193 | try: |
| 194 | self.size = int(val) |
| 195 | except ValueError: |
| 196 | self.size = val |
| 197 | self.expanded = False |
| 198 | |
| 199 | def get_names(self): |
| 200 | """Return name of variables |
| 201 | """ |
| 202 | namelist = [] |
| 203 | for i in range(0,self.size): |
| 204 | namelist.append(self.object.name) |
| 205 | return namelist |
| 206 | |
| 207 | class ctype_parser: |
| 208 | """Class to check c types |
| 209 | |
| 210 | Date October 2009 |
| 211 | Created by ykk |
| 212 | """ |
| 213 | def __init__(self): |
| 214 | """Initialize |
| 215 | """ |
| 216 | self.CPrimitives = ["char","signed char","unsigned char", |
| 217 | "short","unsigned short", |
| 218 | "int","unsigned int", |
| 219 | "long","unsigned long", |
| 220 | "long long","unsigned long long", |
| 221 | "float","double", |
| 222 | "uint8_t","uint16_t","uint32_t","uint64_t"] |
| 223 | |
| 224 | def is_primitive(self,type): |
| 225 | """Check type given is primitive. |
| 226 | |
| 227 | Return true if valid, and false otherwise |
| 228 | """ |
| 229 | if (type in self.CPrimitives): |
| 230 | return True |
| 231 | else: |
| 232 | return False |
| 233 | |
| 234 | def is_array(self, string): |
| 235 | """Check if string declares an array |
| 236 | """ |
| 237 | parts=string.strip().split() |
| 238 | if (len(parts) <= 1): |
| 239 | return False |
| 240 | else: |
| 241 | pattern = re.compile("\[.*?\]", re.MULTILINE) |
| 242 | values = pattern.findall(string) |
| 243 | if (len(values) == 1): |
| 244 | return True |
| 245 | else: |
| 246 | return False |
| 247 | |
| 248 | def parse_array(self, string): |
| 249 | """Parse array from string. |
| 250 | Return occurrence and name. |
| 251 | """ |
| 252 | pattern = re.compile("\[.*?\]", re.MULTILINE) |
| 253 | namepattern = re.compile(".*?\[", re.MULTILINE) |
| 254 | values = pattern.findall(string) |
| 255 | if (len(values) != 1): |
| 256 | return (1,string) |
| 257 | else: |
| 258 | val = values[0][1:-1] |
| 259 | try: |
| 260 | sizeval = int(val) |
| 261 | except ValueError: |
| 262 | if (val==""): |
| 263 | sizeval = 0 |
| 264 | else: |
| 265 | sizeval = val |
| 266 | return (sizeval, |
| 267 | namepattern.findall(string)[0].strip()[0:-1]) |
| 268 | |
| 269 | def parse_type(self, string): |
| 270 | """Parse string and return cstruct or cprimitive. |
| 271 | Else return None |
| 272 | """ |
| 273 | parts=string.strip().split() |
| 274 | if (len(parts) >= 2): |
| 275 | if (parts[0].strip() == "struct"): |
| 276 | typename = " ".join(parts[1:-1]) |
| 277 | else: |
| 278 | typename = " ".join(parts[:-1]) |
| 279 | (size, name) = self.parse_array(parts[-1]) |
| 280 | if IGNORE_ZERO_ARRAYS and size == 0: |
| 281 | return None |
| 282 | #Create appropriate type |
| 283 | if (size != 1): |
| 284 | #Array |
| 285 | return carray(typename, name, |
| 286 | self.is_primitive(typename),size) |
| 287 | else: |
| 288 | #Not array |
| 289 | if IGNORE_OFP_HEADER and typename == "ofp_header": |
| 290 | return None |
| 291 | if (self.is_primitive(typename)): |
| 292 | return cprimitive(typename, name) |
| 293 | else: |
| 294 | return cstruct(typename, name) |
| 295 | else: |
| 296 | return None |
| 297 | |
| 298 | class cheaderfile(textfile): |
| 299 | """Class to handle C header file. |
| 300 | |
| 301 | Date June 2009 |
| 302 | Created by ykk |
| 303 | """ |
| 304 | def __init__(self, filename): |
| 305 | """Initialize filename and read from file |
| 306 | """ |
| 307 | textfile.__init__(self,filename) |
| 308 | self.read() |
| 309 | self.__remove_comments() |
| 310 | ##Dictionary of macros |
| 311 | self.macros = {} |
| 312 | self.__get_macros() |
| 313 | ##Dictionary of enumerations |
| 314 | self.enums = {} |
| 315 | self.enum_values = {} |
| 316 | self.__get_enum() |
| 317 | self.__get_enum_values() |
| 318 | ##Dictionary of structs |
| 319 | self.structs = {} |
| 320 | self.__get_struct() |
| 321 | |
| 322 | def get_enum_name(self, enum, value): |
| 323 | """Return name of variable in enum |
| 324 | """ |
| 325 | for e in self.enums[enum]: |
| 326 | if (self.enum_values[e] == value): |
| 327 | return e |
| 328 | |
| 329 | def eval_value(self, value): |
| 330 | """Evaluate value string |
| 331 | """ |
| 332 | try: |
| 333 | return eval(value, self.enum_values) |
| 334 | except: |
| 335 | return value.strip() |
| 336 | |
| 337 | def get_value(self, name): |
| 338 | """Get value for variable name, |
| 339 | searching through enum and macros. |
| 340 | Else return None |
| 341 | """ |
| 342 | try: |
| 343 | return self.enum_values[name] |
| 344 | except KeyError: |
| 345 | try: |
| 346 | return self.macros[name] |
| 347 | except KeyError: |
| 348 | return None |
| 349 | |
| 350 | def __remove_comments(self): |
| 351 | """Remove all comments |
| 352 | """ |
| 353 | fileStr = "".join(self.content) |
| 354 | pattern = re.compile("\\\.*?\n", re.MULTILINE) |
| 355 | fileStr = pattern.sub("",fileStr) |
| 356 | pattern = re.compile(r"/\*.*?\*/", re.MULTILINE|re.DOTALL) |
| 357 | fileStr = pattern.sub("",fileStr) |
| 358 | pattern = re.compile("//.*$", re.MULTILINE) |
| 359 | fileStr = pattern.sub("",fileStr) |
| 360 | self.content = fileStr.split('\n') |
| 361 | |
| 362 | def __get_struct(self): |
| 363 | """Get all structs |
| 364 | """ |
| 365 | typeparser = ctype_parser() |
| 366 | fileStr = "".join(self.content) |
| 367 | #Remove attribute |
| 368 | attrpattern = re.compile("} __attribute__ \(\((.+?)\)\);", re.MULTILINE) |
| 369 | attrmatches = attrpattern.findall(fileStr) |
| 370 | for amatch in attrmatches: |
| 371 | fileStr=fileStr.replace(" __attribute__ (("+amatch+"));",";") |
| 372 | #Find all structs |
| 373 | pattern = re.compile("struct[\w\s]*?{.*?};", re.MULTILINE) |
| 374 | matches = pattern.findall(fileStr) |
| 375 | #Process each struct |
| 376 | namepattern = re.compile("struct(.+?)[ {]", re.MULTILINE) |
| 377 | pattern = re.compile("{(.+?)};", re.MULTILINE) |
| 378 | for match in matches: |
| 379 | structname = namepattern.findall(match)[0].strip() |
| 380 | if (len(structname) != 0): |
| 381 | values = pattern.findall(match)[0].strip().split(";") |
| 382 | cstru = cstruct(structname) |
| 383 | for val in values: |
| 384 | presult = typeparser.parse_type(val) |
| 385 | if (presult != None): |
| 386 | cstru.members.append(presult) |
| 387 | self.structs[structname] = cstru |
| 388 | #Expand all structs |
| 389 | for (structname, struct) in self.structs.items(): |
| 390 | struct.expand(self) |
| 391 | |
| 392 | def __get_enum(self): |
| 393 | """Get all enumeration |
| 394 | """ |
| 395 | fileStr = "".join(self.content) |
| 396 | #Find all enumerations |
| 397 | pattern = re.compile("enum[\w\s]*?{.*?}", re.MULTILINE) |
| 398 | matches = pattern.findall(fileStr) |
| 399 | #Process each enumeration |
| 400 | namepattern = re.compile("enum(.+?){", re.MULTILINE) |
| 401 | pattern = re.compile("{(.+?)}", re.MULTILINE) |
| 402 | for match in matches: |
| 403 | values = pattern.findall(match)[0].strip().split(",") |
| 404 | #Process each value in enumeration |
| 405 | enumList = [] |
| 406 | value = 0 |
| 407 | for val in values: |
| 408 | if not (val.strip() == ""): |
| 409 | valList=val.strip().split("=") |
| 410 | enumList.append(valList[0].strip()) |
| 411 | if (len(valList) == 1): |
| 412 | self.enum_values[valList[0].strip()] = value |
| 413 | value += 1 |
| 414 | else: |
| 415 | self.enum_values[valList[0].strip()] = self.eval_value(valList[1].strip()) |
| 416 | self.enums[namepattern.findall(match)[0].strip()] = enumList |
| 417 | |
| 418 | def __get_enum_values(self): |
| 419 | """Patch unresolved enum values |
| 420 | """ |
| 421 | for name,enumval in self.enum_values.items(): |
| 422 | if isinstance(enumval,str): |
| 423 | self.enum_values[name] = self.eval_value(enumval) |
| 424 | |
| 425 | def __get_macros(self): |
| 426 | """Extract macros |
| 427 | """ |
| 428 | for line in self.content: |
| 429 | if (line[0:8] == "#define "): |
| 430 | lineList = line[8:].split() |
| 431 | if (len(lineList) >= 2): |
| 432 | self.macros[lineList[0]] = self.eval_value("".join(lineList[1:])) |
| 433 | else: |
| 434 | self.macros[lineList[0]] = "" |