blob: 2fcdc9f32c344b55b18312cd5e90530e567703ca [file] [log] [blame]
Dan Talaycof75360a2010-02-05 22:22:54 -08001"""This module generate Python code for C structs.
2
3Date January 2010
4Created by ykk
5"""
6import cheader
7import c2py
8import datetime
9import struct
10import re
11from config import *
12
13def _space_to(n, str):
14 """
15 Generate a string of spaces to achieve width n given string str
16 If length of str >= n, return one space
17 """
18 spaces = n - len(str)
19 if spaces > 0:
20 return " " * spaces
21 return " "
22
23class rules:
24 """Class that specify rules for pythonization
25
26 Date January 2010
27 Created by ykk
28 """
29 def __init__(self):
30 """Initialize rules
31 """
32 ##Default values for members
33 self.default_values = {}
34 #Default values for struct
35 self.struct_default = {}
36 ##What is a tab
37 self.tab = " "
38 ##Macros to exclude
39 self.excluded_macros = []
40 ##Enforce mapping
41 self.enforced_maps = {}
42
43 def get_enforced_map(self, structname):
44 """Get code to enforce mapping
45 """
46 code = []
47 try:
48 mapping = self.enforced_maps[structname]
49 except KeyError:
50 return None
51 for (x,xlist) in mapping:
52 code.append("if (not (self."+x+" in "+xlist+")):")
53 code.append(self.tab+"return (False, \""+x+" must have values from "+xlist+"\")")
54 return code
55
56
57 def get_struct_default(self, structname, fieldname):
58 """Get code to set defaults for member struct
59 """
60 try:
61 return "."+fieldname+self.struct_default[(structname, fieldname)]
62 except KeyError:
63 return None
64
65 def get_default_value(self, structname, fieldname):
66 """Get default value for struct's field
67 """
68 try:
69 return self.default_values[(structname, fieldname)]
70 except KeyError:
71 return 0
72
73 def include_macro(self, name):
74 """Check if macro should be included
75 """
76 return not (name in self.excluded_macros)
77
78class pythonizer:
79 """Class that pythonize C structures
80
81 Date January 2010
82 Created by ykk
83 """
84 def __init__(self, cheaderfile, pyrules = None, tab=" "):
85 """Initialize
86 """
87 ##Rules
88 if (pyrules == None):
89 self.rules = rules()
90 else:
91 self.rules = pyrules
92 ##What is a tab (same as rules)
93 self.tab = str(tab)
94 self.rules.tab = self.tab
95 ##Reference to C header file
96 self.cheader = cheaderfile
97 ##Reference to cstruct2py
98 self.__c2py = c2py.cstruct2py()
99 ##Code for assertion
100 self.__assertcode = []
101
102 def pycode(self,preamble=None):
103 """Return pythonized code
104 """
105 code = []
106 code.append("import struct")
107 code.append("")
108 if (preamble != None):
109 fileRef = open(preamble,"r")
110 for l in fileRef:
111 code.append(l[:-1])
112 fileRef.close()
113 code.append("# Structure definitions")
114 for name,struct in self.cheader.structs.items():
115 code.extend(self.pycode_struct(struct))
116 code.append("")
117 code.append("# Enumerated type definitions")
118 for name,enum in self.cheader.enums.items():
119 code.extend(self.pycode_enum(name,enum))
120 if GEN_ENUM_DICTIONARY:
121 code.extend(self.pycode_enum_map(name,enum))
122 code.append("")
123 code.append("# Values from macro definitions")
124 for name,macro in self.cheader.macros.items():
125 code.extend(self.pycode_macro(name))
126 code.append("")
127 code.append("# Basic structure size definitions.")
128 if IGNORE_OFP_HEADER:
129 code.append("# Does not include ofp_header members.")
130 if IGNORE_ZERO_ARRAYS:
131 code.append("# Does not include variable length arrays.")
132 struct_keys = self.cheader.structs.keys()
133 struct_keys.sort()
134 for name in struct_keys:
135 struct = self.cheader.structs[name]
136 code.append(self.pycode_struct_size(name, struct))
Dan Talaycoac1cb812010-02-06 20:34:18 -0800137 if GEN_AUX_INFO:
138 self.gen_struct_map()
Dan Talaycof75360a2010-02-05 22:22:54 -0800139
140 return code
141
142 def pycode_enum(self, name, enum):
143 """Return Python array for enum
144 """
145 code=[]
146 code.append(name+" = "+str(enum))
147 ev = []
148 for e in enum:
149 v = self.cheader.get_value(e)
150 ev.append(v)
151 code.append(e+"%s= "%_space_to(36,e)+str(v))
152 if GEN_ENUM_VALUES_LIST:
153 code.append(name+"_values = "+str(ev))
154 return code
155
156 def pycode_enum_map(self, name, enum):
157 """Return Python dictionary for enum
158 """
159 code = []
160 code.append(name+"_map = {")
161 first = 1
162 for e in enum:
163 v = self.cheader.get_value(e)
164 if first:
165 prev_e = e
166 prev_v = v
167 first = 0
168 else:
169 code.append(self.tab + "'%s'%s: %s," %
170 (prev_e, _space_to(30, prev_e), prev_v))
171 prev_e = e
172 prev_v = v
173 code.append(self.tab + "'%s'%s: %s" %
174 (prev_e, _space_to(30, prev_e), prev_v))
175 code.append("}")
176 return code
177
178 def pycode_macro(self,name):
179 """Return Python dict for macro
180 """
181 code = []
182 if (self.rules.include_macro(name)):
183 code.append(name+" = "+str(self.cheader.get_value(name)))
184 return code
185
186 def pycode_struct_size(self, name, struct):
187 """Return one liner giving the structure size in bytes
188 """
189 pattern = '!' + self.__c2py.get_pattern(struct)
190 bytes = self.__c2py.get_size(pattern)
191 code = name.upper() + "_BYTES = " + str(bytes)
192 return code
193
194 def pycode_struct(self, struct_in):
195 """Return Python class code given C struct.
196
197 Returns None if struct_in is not cheader.cstruct.
198 Else return list of strings that codes Python class.
199 """
200 if (not isinstance(struct_in, cheader.cstruct)):
201 return None
202
203 code=[]
204 self.__assertcode = []
205 code.extend(self.codeheader(struct_in))
206 code.extend(self.codeinit(struct_in))
207 code.append("")
208 code.extend(self.codeassert(struct_in))
209 code.append("")
210 code.extend(self.codepack(struct_in))
211 code.append("")
212 code.extend(self.codeunpack(struct_in))
213 code.append("")
214 code.extend(self.codelen(struct_in))
215 code.append("")
216 if GEN_OBJ_EQUALITY:
217 code.extend(self.codeeq(struct_in))
218 code.append("")
219 if GEN_OBJ_SHOW:
220 code.extend(self.codeshow(struct_in))
221 code.append("")
222 return code
223
224 def codeheader(self, struct_in):
225 """Return Python code for header
226 """
227 code=[]
228 code.append("class "+struct_in.typename+":")
229 code.append(self.tab+"\"\"\"Automatically generated Python class for "+struct_in.typename)
230 code.append("")
231 code.append(self.tab+"Date "+str(datetime.date.today()))
232 code.append(self.tab+"Created by "+self.__module__+"."+self.__class__.__name__)
233 if IGNORE_OFP_HEADER:
234 code.append(self.tab+"Core structure: Messages do not include ofp_header")
235 if IGNORE_ZERO_ARRAYS:
236 code.append(self.tab+"Does not include var-length arrays")
237 code.append(self.tab+"\"\"\"")
238 return code
239
240 def codeinit(self, struct_in):
241 """Return Python code for init function
242 """
243 code = []
244 code.append(self.tab+"def __init__(self):")
245 code.append(self.tab*2+"\"\"\"Initialize")
246 code.append(self.tab*2+"Declare members and default values")
247 code.append(self.tab*2+"\"\"\"")
248 code.extend(self.codemembers(struct_in,self.tab*2+"self"))
249 return code
250
251 def codemembers(self, struct_in, prepend=""):
252 """Return members of class
253 """
254 code = []
255 for member in struct_in.members:
256 if (isinstance(member, cheader.cstruct)):
257 code.append(prepend+"."+member.name+" = "+member.typename+"()")
258 struct_default = self.rules.get_struct_default(struct_in.typename, member.name)
259 if (struct_default != None):
260 code.append(prepend+struct_default)
261 self.__structassert(member, (prepend+"."+member.name).strip())
262 elif (isinstance(member, cheader.carray)):
263 if (member.typename == "char"):
264 initvalue = "\"\""
265 self.__stringassert(member, (prepend+"."+member.name).strip())
266 else:
267 if (isinstance(member.object, cheader.cprimitive)):
268 initvalue="0"
269 else:
270 initvalue="None"
271 initvalue=(initvalue+",")*member.size
272 initvalue="["+initvalue[:-1]+"]"
273 self.__arrayassert(member, (prepend+"."+member.name).strip())
274 code.append(prepend+"."+member.name+"= "+initvalue)
275 else:
276 code.append(prepend+"."+member.name+" = "+
277 str(self.rules.get_default_value(struct_in.typename, member.name)))
278 return code
279
Dan Talaycoac1cb812010-02-06 20:34:18 -0800280 def gen_struct_map(self):
281 print
282 print "# Class to array member map"
283 print "class_to_members_map = {"
284 for name, struct in self.cheader.structs.items():
285 if not len(struct.members):
286 continue
287 s = " '" + name + "'"
288 print s + _space_to(36, s) + ": ["
289 prev = None
290 for member in struct.members:
291 if re.search('pad', member.name):
292 continue
293 if prev:
294 print _space_to(39, "") + "'" + prev + "',"
295 prev = member.name
296 print _space_to(39, "") + "'" + prev + "'"
297 print _space_to(38, "") + "],"
298 print " '_ignore' : []"
299 print "}"
300
Dan Talaycof75360a2010-02-05 22:22:54 -0800301 def __structassert(self, cstruct, cstructname):
302 """Return code to check for C array
303 """
304 self.__assertcode.append(self.tab*2+"if(not isinstance("+cstructname+", "+cstruct.typename+")):")
305 self.__assertcode.append(self.tab*3+"return (False, \""+cstructname+" is not class "+cstruct.typename+" as expected.\")")
306
307 def __addassert(self, prefix):
308 code = []
309 code.append(prefix+"if(not self.__assert()[0]):")
310 code.append(prefix+self.tab+"return None")
311 return code
312
313 def __stringassert(self, carray, carrayname):
314 """Return code to check for C array
315 """
316 self.__assertcode.append(self.tab*2+"if(not isinstance("+carrayname+", str)):")
317 self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not string as expected.\")")
318 self.__assertcode.append(self.tab*2+"if(len("+carrayname+") > "+str(carray.size)+"):")
319 self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not of size "+str(carray.size)+" as expected.\")")
320
321 def __arrayassert(self, carray, carrayname):
322 """Return code to check for C array
323 """
324 if (carray.size == 0):
325 return
326 self.__assertcode.append(self.tab*2+"if(not isinstance("+carrayname+", list)):")
327 self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not list as expected.\")")
328 self.__assertcode.append(self.tab*2+"if(len("+carrayname+") != "+str(carray.size)+"):")
329 self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not of size "+str(carray.size)+" as expected.\")")
330
331 def codeassert(self, struct_in):
332 """Return code for sanity checking
333 """
334 code = []
335 code.append(self.tab+"def __assert(self):")
336 code.append(self.tab*2+"\"\"\"Sanity check")
337 code.append(self.tab*2+"\"\"\"")
338 enforce = self.rules.get_enforced_map(struct_in.typename)
339 if (enforce != None):
340 for line in enforce:
341 code.append(self.tab*2+line)
342 code.extend(self.__assertcode)
343 code.append(self.tab*2+"return (True, None)")
344 return code
345
346 def codepack(self, struct_in, prefix="!"):
347 """Return code that pack struct
348 """
349 code = []
350 code.append(self.tab+"def pack(self, assertstruct=True):")
351 code.append(self.tab*2+"\"\"\"Pack message")
352 code.append(self.tab*2+"Packs empty array used as placeholder")
353 code.append(self.tab*2+"\"\"\"")
354 code.append(self.tab*2+"if(assertstruct):")
355 code.extend(self.__addassert(self.tab*3))
356 code.append(self.tab*2+"packed = \"\"")
357 primPattern = ""
358 primMemberNames = []
359 for member in struct_in.members:
360 if (isinstance(member, cheader.cprimitive)):
361 #Primitives
362 primPattern += self.__c2py.structmap[member.typename]
363 primMemberNames.append("self."+member.name)
364 else:
365 (primPattern, primMemberNames) = \
366 self.__codepackprimitive(code, primPattern,
367 primMemberNames, prefix)
368 if (isinstance(member, cheader.cstruct)):
369 #Struct
370 code.append(self.tab*2+"packed += self."+member.name+".pack()")
371 elif (isinstance(member, cheader.carray) and member.typename == "char"):
372 #String
373 code.append(self.tab*2+"packed += self."+member.name+".ljust("+\
374 str(member.size)+",'\\0')")
375 elif (isinstance(member, cheader.carray) and \
376 isinstance(member.object, cheader.cprimitive)):
377 #Array of Primitives
378 expandedarr = ""
379 if (member.size != 0):
380 for x in range(0, member.size):
381 expandedarr += ", self."+member.name+"["+\
382 str(x).strip()+"]"
383 code.append(self.tab*2+"packed += struct.pack(\""+prefix+\
384 self.__c2py.structmap[member.object.typename]*member.size+\
385 "\""+expandedarr+")")
386 else:
387 code.append(self.tab*2+"for i in self."+member.name+":")
388 code.append(self.tab*3+"packed += struct.pack(\""+\
389 prefix+self.__c2py.get_pattern(member.object)+\
390 "\",i)")
391 elif (isinstance(member, cheader.carray) and \
392 isinstance(member.object, cheader.cstruct)):
393 #Array of struct
394 if (member.size != 0):
395 for x in range(0, member.size):
396 code.append(self.tab*2+"packed += self."+member.name+"["+\
397 str(x).strip()+"].pack()")
398 else:
399 code.append(self.tab*2+"for i in self."+member.name+":")
400 code.append(self.tab*3+"packed += i.pack(assertstruct)")
401 #Clear remaining fields
402 (primPattern, primMemberNames) = \
403 self.__codepackprimitive(code, primPattern,
404 primMemberNames, prefix)
405 code.append(self.tab*2+"return packed")
406 return code
407
408 def __codepackprimitive(self, code, primPattern, primMemberNames, prefix):
409 """Return code for packing primitives
410 """
411 if (primPattern != ""):
412 #Clear prior primitives
413 code.append(self.tab*2+"packed += struct.pack(\""+\
414 prefix+primPattern+"\", "+\
415 str(primMemberNames).replace("'","")[1:-1]+")")
416 return ("",[])
417
418 def codelen(self, struct_in):
419 """Return code to return length
420 """
421 pattern = "!" + self.__c2py.get_pattern(struct_in)
422 code = []
423 code.append(self.tab+"def __len__(self):")
424 code.append(self.tab*2+"\"\"\"Return length of message")
425 code.append(self.tab*2+"\"\"\"")
426 code.append(self.tab*2+"l = "+str(self.__c2py.get_size(pattern)))
427 for member in struct_in.members:
428 if (isinstance(member, cheader.carray) and member.size == 0):
429 if (isinstance(member.object, cheader.cstruct)):
430 code.append(self.tab*2+"for i in self."+member.name+":")
431 code.append(self.tab*3+"l += i.length()")
432 else:
433 pattern="!"+self.__c2py.get_pattern(member.object)
434 size=self.__c2py.get_size(pattern)
435 code.append(self.tab*2+"l += len(self."+member.name+")*"+str(size))
436 code.append(self.tab*2+"return l")
437 return code
438
439 def codeeq(self, struct_in):
440 """Return code to return equality comparisons
441 """
442 code = []
443 code.append(self.tab+"def __eq__(self, other):")
444 code.append(self.tab*2+"\"\"\"Return True if self and other have same values")
445 code.append(self.tab*2+"\"\"\"")
446 code.append(self.tab*2+"if type(self) != type(other): return False")
447 for member in struct_in.members:
448 code.append(self.tab*2 + "if self." + member.name + " != other." +
449 member.name + ": return False")
450 code.append(self.tab*2+"return True")
451 code.append("")
452 code.append(self.tab+"def __ne__(self, other): return not self.__eq__(other)")
453 return code
454
455 def codeshow(self, struct_in):
456 """Return code to print basic members of structure
457 """
458 code = []
459 code.append(self.tab+"def show(self, prefix=''):")
460 code.append(self.tab*2+"\"\"\"" + "Print basic members of structure")
461 code.append(self.tab*2+"\"\"\"")
462 for member in struct_in.members:
463 if re.search('pad', member.name):
464 continue
465 elif (isinstance(member, cheader.cstruct)):
466 code.append(self.tab*2 + "print prefix + '" +
467 member.name + ": ' ")
468 code.append(self.tab*2 + "self." + member.name +
469 ".show(prefix + ' ')")
470 elif (isinstance(member, cheader.carray) and
471 not isinstance(member.object, cheader.cprimitive)):
472 code.append(self.tab*2 + "print prefix + '" + member.name +
473 ": ' ")
474 code.append(self.tab*2 + "for obj in self." + member.name + ":")
475 code.append(self.tab*3 + "obj.show(prefix + ' ')")
476 else:
477 code.append(self.tab*2 + "print prefix + '" + member.name +
478 ": ' + str(self." + member.name + ")")
479 return code
480
481 def codeunpack(self, struct_in, prefix="!"):
482 """Return code that unpack struct
483 """
484 pattern = self.__c2py.get_pattern(struct_in)
485 structlen = self.__c2py.get_size(prefix + pattern)
486 code = []
487 code.append(self.tab+"def unpack(self, binaryString):")
488 code.append(self.tab*2+"\"\"\"Unpack message")
489 code.append(self.tab*2+"Do not unpack empty array used as placeholder")
490 code.append(self.tab*2+"since they can contain heterogeneous type")
491 code.append(self.tab*2+"\"\"\"")
492 code.append(self.tab*2+"if (len(binaryString) < "+str(structlen)+"):")
493 code.append(self.tab*3+"return binaryString")
494 offset = 0
495 primPattern = ""
496 primMemberNames = []
497 for member in struct_in.members:
498 if (isinstance(member, cheader.cprimitive)):
499 #Primitives
500 primPattern += self.__c2py.structmap[member.typename]
501 primMemberNames.append("self."+member.name)
502 else:
503 (primPattern, primMemberNames, offset) = \
504 self.__codeunpackprimitive(code, offset, primPattern,
505 primMemberNames, prefix)
506 if (isinstance(member, cheader.cstruct)):
507 #Struct
508 code.append(self.tab*2+"self."+member.name+\
509 ".unpack(binaryString["+str(offset)+":])")
510 pattern = self.__c2py.get_pattern(member)
511 offset += self.__c2py.get_size(prefix+pattern)
512 elif (isinstance(member, cheader.carray) and member.typename == "char"):
513 #String
514 code.append(self.tab*2+"self."+member.name+\
515 " = binaryString["+str(offset)+":"+\
516 str(offset+member.size)+"].replace(\"\\0\",\"\")")
517 offset += member.size
518 elif (isinstance(member, cheader.carray) and \
519 isinstance(member.object, cheader.cprimitive)):
520 #Array of Primitives
521 expandedarr = ""
522 if (member.size != 0):
523 arrpattern = self.__c2py.structmap[member.object.typename]*member.size
524 for x in range(0, member.size):
525 expandedarr += "self."+member.name+"["+\
526 str(x).strip()+"], "
527 code.append(self.tab*2+"("+expandedarr[:-2]+") = struct.unpack_from(\""+\
528 prefix+arrpattern+\
529 "\", binaryString, "+str(offset)+")")
530 offset += struct.calcsize(prefix + arrpattern)
531 elif (isinstance(member, cheader.carray) and \
532 isinstance(member.object, cheader.cstruct)):
533 #Array of struct
534 astructlen = self.__c2py.get_size("!"+self.__c2py.get_pattern(member.object))
535 for x in range(0, member.size):
536 code.append(self.tab*2+"self."+member.name+"["+str(x)+"]"+\
537 ".unpack(binaryString["+str(offset)+":])")
538 offset += astructlen
539 #Clear remaining fields
540 (primPattern, primMemberNames, offset) = \
541 self.__codeunpackprimitive(code, offset, primPattern,
542 primMemberNames, prefix)
543 code.append(self.tab*2+"return binaryString["+str(structlen)+":]");
544 return code
545
546 def __codeunpackprimitive(self, code, offset, primPattern,
547 primMemberNames, prefix):
548 """Return code for unpacking primitives
549 """
550 if (primPattern != ""):
551 #Clear prior primitives
552 code.append(self.tab*2+"("+str(primMemberNames).replace("'","")[1:-1]+\
553 ") = struct.unpack_from(\""+\
554 prefix+primPattern+"\", binaryString, "+str(offset)+")")
555 return ("",[], offset+struct.calcsize(prefix+primPattern))
556