1 # 2 # QAPI helper library 3 # 4 # Copyright IBM, Corp. 2011 5 # Copyright (c) 2013 Red Hat Inc. 6 # 7 # Authors: 8 # Anthony Liguori <aliguori (at] us.ibm.com> 9 # Markus Armbruster <armbru (at] redhat.com> 10 # 11 # This work is licensed under the terms of the GNU GPLv2. 12 # See the COPYING.LIB file in the top-level directory. 13 14 from ordereddict import OrderedDict 15 import sys 16 17 builtin_types = [ 18 'str', 'int', 'number', 'bool', 19 'int8', 'int16', 'int32', 'int64', 20 'uint8', 'uint16', 'uint32', 'uint64' 21 ] 22 23 builtin_type_qtypes = { 24 'str': 'QTYPE_QSTRING', 25 'int': 'QTYPE_QINT', 26 'number': 'QTYPE_QFLOAT', 27 'bool': 'QTYPE_QBOOL', 28 'int8': 'QTYPE_QINT', 29 'int16': 'QTYPE_QINT', 30 'int32': 'QTYPE_QINT', 31 'int64': 'QTYPE_QINT', 32 'uint8': 'QTYPE_QINT', 33 'uint16': 'QTYPE_QINT', 34 'uint32': 'QTYPE_QINT', 35 'uint64': 'QTYPE_QINT', 36 } 37 38 class QAPISchemaError(Exception): 39 def __init__(self, schema, msg): 40 self.fp = schema.fp 41 self.msg = msg 42 self.line = self.col = 1 43 for ch in schema.src[0:schema.pos]: 44 if ch == '\n': 45 self.line += 1 46 self.col = 1 47 elif ch == '\t': 48 self.col = (self.col + 7) % 8 + 1 49 else: 50 self.col += 1 51 52 def __str__(self): 53 return "%s:%s:%s: %s" % (self.fp.name, self.line, self.col, self.msg) 54 55 class QAPISchema: 56 57 def __init__(self, fp): 58 self.fp = fp 59 self.src = fp.read() 60 if self.src == '' or self.src[-1] != '\n': 61 self.src += '\n' 62 self.cursor = 0 63 self.exprs = [] 64 self.accept() 65 66 while self.tok != None: 67 self.exprs.append(self.get_expr(False)) 68 69 def accept(self): 70 while True: 71 self.tok = self.src[self.cursor] 72 self.pos = self.cursor 73 self.cursor += 1 74 self.val = None 75 76 if self.tok == '#': 77 self.cursor = self.src.find('\n', self.cursor) 78 elif self.tok in ['{', '}', ':', ',', '[', ']']: 79 return 80 elif self.tok == "'": 81 string = '' 82 esc = False 83 while True: 84 ch = self.src[self.cursor] 85 self.cursor += 1 86 if ch == '\n': 87 raise QAPISchemaError(self, 88 'Missing terminating "\'"') 89 if esc: 90 string += ch 91 esc = False 92 elif ch == "\\": 93 esc = True 94 elif ch == "'": 95 self.val = string 96 return 97 else: 98 string += ch 99 elif self.tok == '\n': 100 if self.cursor == len(self.src): 101 self.tok = None 102 return 103 elif not self.tok.isspace(): 104 raise QAPISchemaError(self, 'Stray "%s"' % self.tok) 105 106 def get_members(self): 107 expr = OrderedDict() 108 if self.tok == '}': 109 self.accept() 110 return expr 111 if self.tok != "'": 112 raise QAPISchemaError(self, 'Expected string or "}"') 113 while True: 114 key = self.val 115 self.accept() 116 if self.tok != ':': 117 raise QAPISchemaError(self, 'Expected ":"') 118 self.accept() 119 expr[key] = self.get_expr(True) 120 if self.tok == '}': 121 self.accept() 122 return expr 123 if self.tok != ',': 124 raise QAPISchemaError(self, 'Expected "," or "}"') 125 self.accept() 126 if self.tok != "'": 127 raise QAPISchemaError(self, 'Expected string') 128 129 def get_values(self): 130 expr = [] 131 if self.tok == ']': 132 self.accept() 133 return expr 134 if not self.tok in [ '{', '[', "'" ]: 135 raise QAPISchemaError(self, 'Expected "{", "[", "]" or string') 136 while True: 137 expr.append(self.get_expr(True)) 138 if self.tok == ']': 139 self.accept() 140 return expr 141 if self.tok != ',': 142 raise QAPISchemaError(self, 'Expected "," or "]"') 143 self.accept() 144 145 def get_expr(self, nested): 146 if self.tok != '{' and not nested: 147 raise QAPISchemaError(self, 'Expected "{"') 148 if self.tok == '{': 149 self.accept() 150 expr = self.get_members() 151 elif self.tok == '[': 152 self.accept() 153 expr = self.get_values() 154 elif self.tok == "'": 155 expr = self.val 156 self.accept() 157 else: 158 raise QAPISchemaError(self, 'Expected "{", "[" or string') 159 return expr 160 161 def parse_schema(fp): 162 try: 163 schema = QAPISchema(fp) 164 except QAPISchemaError, e: 165 print >>sys.stderr, e 166 exit(1) 167 168 exprs = [] 169 170 for expr in schema.exprs: 171 if expr.has_key('enum'): 172 add_enum(expr['enum']) 173 elif expr.has_key('union'): 174 add_union(expr) 175 add_enum('%sKind' % expr['union']) 176 elif expr.has_key('type'): 177 add_struct(expr) 178 exprs.append(expr) 179 180 return exprs 181 182 def parse_args(typeinfo): 183 if isinstance(typeinfo, basestring): 184 struct = find_struct(typeinfo) 185 assert struct != None 186 typeinfo = struct['data'] 187 188 for member in typeinfo: 189 argname = member 190 argentry = typeinfo[member] 191 optional = False 192 structured = False 193 if member.startswith('*'): 194 argname = member[1:] 195 optional = True 196 if isinstance(argentry, OrderedDict): 197 structured = True 198 yield (argname, argentry, optional, structured) 199 200 def de_camel_case(name): 201 new_name = '' 202 for ch in name: 203 if ch.isupper() and new_name: 204 new_name += '_' 205 if ch == '-': 206 new_name += '_' 207 else: 208 new_name += ch.lower() 209 return new_name 210 211 def camel_case(name): 212 new_name = '' 213 first = True 214 for ch in name: 215 if ch in ['_', '-']: 216 first = True 217 elif first: 218 new_name += ch.upper() 219 first = False 220 else: 221 new_name += ch.lower() 222 return new_name 223 224 def c_var(name, protect=True): 225 # ANSI X3J11/88-090, 3.1.1 226 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue', 227 'default', 'do', 'double', 'else', 'enum', 'extern', 'float', 228 'for', 'goto', 'if', 'int', 'long', 'register', 'return', 229 'short', 'signed', 'sizeof', 'static', 'struct', 'switch', 230 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while']) 231 # ISO/IEC 9899:1999, 6.4.1 232 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary']) 233 # ISO/IEC 9899:2011, 6.4.1 234 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn', 235 '_Static_assert', '_Thread_local']) 236 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html 237 # excluding _.* 238 gcc_words = set(['asm', 'typeof']) 239 # C++ ISO/IEC 14882:2003 2.11 240 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete', 241 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable', 242 'namespace', 'new', 'operator', 'private', 'protected', 243 'public', 'reinterpret_cast', 'static_cast', 'template', 244 'this', 'throw', 'true', 'try', 'typeid', 'typename', 245 'using', 'virtual', 'wchar_t', 246 # alternative representations 247 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 248 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) 249 # namespace pollution: 250 polluted_words = set(['unix']) 251 if protect and (name in c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words): 252 return "q_" + name 253 return name.replace('-', '_').lstrip("*") 254 255 def c_fun(name, protect=True): 256 return c_var(name, protect).replace('.', '_') 257 258 def c_list_type(name): 259 return '%sList' % name 260 261 def type_name(name): 262 if type(name) == list: 263 return c_list_type(name[0]) 264 return name 265 266 enum_types = [] 267 struct_types = [] 268 union_types = [] 269 270 def add_struct(definition): 271 global struct_types 272 struct_types.append(definition) 273 274 def find_struct(name): 275 global struct_types 276 for struct in struct_types: 277 if struct['type'] == name: 278 return struct 279 return None 280 281 def add_union(definition): 282 global union_types 283 union_types.append(definition) 284 285 def find_union(name): 286 global union_types 287 for union in union_types: 288 if union['union'] == name: 289 return union 290 return None 291 292 def add_enum(name): 293 global enum_types 294 enum_types.append(name) 295 296 def is_enum(name): 297 global enum_types 298 return (name in enum_types) 299 300 def c_type(name): 301 if name == 'str': 302 return 'char *' 303 elif name == 'int': 304 return 'int64_t' 305 elif (name == 'int8' or name == 'int16' or name == 'int32' or 306 name == 'int64' or name == 'uint8' or name == 'uint16' or 307 name == 'uint32' or name == 'uint64'): 308 return name + '_t' 309 elif name == 'size': 310 return 'uint64_t' 311 elif name == 'bool': 312 return 'bool' 313 elif name == 'number': 314 return 'double' 315 elif type(name) == list: 316 return '%s *' % c_list_type(name[0]) 317 elif is_enum(name): 318 return name 319 elif name == None or len(name) == 0: 320 return 'void' 321 elif name == name.upper(): 322 return '%sEvent *' % camel_case(name) 323 else: 324 return '%s *' % name 325 326 def genindent(count): 327 ret = "" 328 for i in range(count): 329 ret += " " 330 return ret 331 332 indent_level = 0 333 334 def push_indent(indent_amount=4): 335 global indent_level 336 indent_level += indent_amount 337 338 def pop_indent(indent_amount=4): 339 global indent_level 340 indent_level -= indent_amount 341 342 def cgen(code, **kwds): 343 indent = genindent(indent_level) 344 lines = code.split('\n') 345 lines = map(lambda x: indent + x, lines) 346 return '\n'.join(lines) % kwds + '\n' 347 348 def mcgen(code, **kwds): 349 return cgen('\n'.join(code.split('\n')[1:-1]), **kwds) 350 351 def basename(filename): 352 return filename.split("/")[-1] 353 354 def guardname(filename): 355 guard = basename(filename).rsplit(".", 1)[0] 356 for substr in [".", " ", "-"]: 357 guard = guard.replace(substr, "_") 358 return guard.upper() + '_H' 359 360 def guardstart(name): 361 return mcgen(''' 362 363 #ifndef %(name)s 364 #define %(name)s 365 366 ''', 367 name=guardname(name)) 368 369 def guardend(name): 370 return mcgen(''' 371 372 #endif /* %(name)s */ 373 374 ''', 375 name=guardname(name)) 376