qapi.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. #
  2. # QAPI helper library
  3. #
  4. # Copyright IBM, Corp. 2011
  5. #
  6. # Authors:
  7. # Anthony Liguori <aliguori@us.ibm.com>
  8. #
  9. # This work is licensed under the terms of the GNU GPLv2.
  10. # See the COPYING.LIB file in the top-level directory.
  11. from ordereddict import OrderedDict
  12. builtin_types = [
  13. 'str', 'int', 'number', 'bool',
  14. 'int8', 'int16', 'int32', 'int64',
  15. 'uint8', 'uint16', 'uint32', 'uint64'
  16. ]
  17. def tokenize(data):
  18. while len(data):
  19. ch = data[0]
  20. data = data[1:]
  21. if ch in ['{', '}', ':', ',', '[', ']']:
  22. yield ch
  23. elif ch in ' \n':
  24. None
  25. elif ch == "'":
  26. string = ''
  27. esc = False
  28. while True:
  29. if (data == ''):
  30. raise Exception("Mismatched quotes")
  31. ch = data[0]
  32. data = data[1:]
  33. if esc:
  34. string += ch
  35. esc = False
  36. elif ch == "\\":
  37. esc = True
  38. elif ch == "'":
  39. break
  40. else:
  41. string += ch
  42. yield string
  43. def parse(tokens):
  44. if tokens[0] == '{':
  45. ret = OrderedDict()
  46. tokens = tokens[1:]
  47. while tokens[0] != '}':
  48. key = tokens[0]
  49. tokens = tokens[1:]
  50. tokens = tokens[1:] # :
  51. value, tokens = parse(tokens)
  52. if tokens[0] == ',':
  53. tokens = tokens[1:]
  54. ret[key] = value
  55. tokens = tokens[1:]
  56. return ret, tokens
  57. elif tokens[0] == '[':
  58. ret = []
  59. tokens = tokens[1:]
  60. while tokens[0] != ']':
  61. value, tokens = parse(tokens)
  62. if tokens[0] == ',':
  63. tokens = tokens[1:]
  64. ret.append(value)
  65. tokens = tokens[1:]
  66. return ret, tokens
  67. else:
  68. return tokens[0], tokens[1:]
  69. def evaluate(string):
  70. return parse(map(lambda x: x, tokenize(string)))[0]
  71. def get_expr(fp):
  72. expr = ''
  73. for line in fp:
  74. if line.startswith('#') or line == '\n':
  75. continue
  76. if line.startswith(' '):
  77. expr += line
  78. elif expr:
  79. yield expr
  80. expr = line
  81. else:
  82. expr += line
  83. if expr:
  84. yield expr
  85. def parse_schema(fp):
  86. exprs = []
  87. for expr in get_expr(fp):
  88. expr_eval = evaluate(expr)
  89. if expr_eval.has_key('enum'):
  90. add_enum(expr_eval['enum'])
  91. elif expr_eval.has_key('union'):
  92. add_enum('%sKind' % expr_eval['union'])
  93. elif expr_eval.has_key('type'):
  94. add_struct(expr_eval)
  95. exprs.append(expr_eval)
  96. return exprs
  97. def parse_args(typeinfo):
  98. if isinstance(typeinfo, basestring):
  99. struct = find_struct(typeinfo)
  100. assert struct != None
  101. typeinfo = struct['data']
  102. for member in typeinfo:
  103. argname = member
  104. argentry = typeinfo[member]
  105. optional = False
  106. structured = False
  107. if member.startswith('*'):
  108. argname = member[1:]
  109. optional = True
  110. if isinstance(argentry, OrderedDict):
  111. structured = True
  112. yield (argname, argentry, optional, structured)
  113. def de_camel_case(name):
  114. new_name = ''
  115. for ch in name:
  116. if ch.isupper() and new_name:
  117. new_name += '_'
  118. if ch == '-':
  119. new_name += '_'
  120. else:
  121. new_name += ch.lower()
  122. return new_name
  123. def camel_case(name):
  124. new_name = ''
  125. first = True
  126. for ch in name:
  127. if ch in ['_', '-']:
  128. first = True
  129. elif first:
  130. new_name += ch.upper()
  131. first = False
  132. else:
  133. new_name += ch.lower()
  134. return new_name
  135. def c_var(name, protect=True):
  136. # ANSI X3J11/88-090, 3.1.1
  137. c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
  138. 'default', 'do', 'double', 'else', 'enum', 'extern', 'float',
  139. 'for', 'goto', 'if', 'int', 'long', 'register', 'return',
  140. 'short', 'signed', 'sizeof', 'static', 'struct', 'switch',
  141. 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while'])
  142. # ISO/IEC 9899:1999, 6.4.1
  143. c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
  144. # ISO/IEC 9899:2011, 6.4.1
  145. c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn',
  146. '_Static_assert', '_Thread_local'])
  147. # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
  148. # excluding _.*
  149. gcc_words = set(['asm', 'typeof'])
  150. # namespace pollution:
  151. polluted_words = set(['unix'])
  152. if protect and (name in c89_words | c99_words | c11_words | gcc_words | polluted_words):
  153. return "q_" + name
  154. return name.replace('-', '_').lstrip("*")
  155. def c_fun(name, protect=True):
  156. return c_var(name, protect).replace('.', '_')
  157. def c_list_type(name):
  158. return '%sList' % name
  159. def type_name(name):
  160. if type(name) == list:
  161. return c_list_type(name[0])
  162. return name
  163. enum_types = []
  164. struct_types = []
  165. def add_struct(definition):
  166. global struct_types
  167. struct_types.append(definition)
  168. def find_struct(name):
  169. global struct_types
  170. for struct in struct_types:
  171. if struct['type'] == name:
  172. return struct
  173. return None
  174. def add_enum(name):
  175. global enum_types
  176. enum_types.append(name)
  177. def is_enum(name):
  178. global enum_types
  179. return (name in enum_types)
  180. def c_type(name):
  181. if name == 'str':
  182. return 'char *'
  183. elif name == 'int':
  184. return 'int64_t'
  185. elif (name == 'int8' or name == 'int16' or name == 'int32' or
  186. name == 'int64' or name == 'uint8' or name == 'uint16' or
  187. name == 'uint32' or name == 'uint64'):
  188. return name + '_t'
  189. elif name == 'size':
  190. return 'uint64_t'
  191. elif name == 'bool':
  192. return 'bool'
  193. elif name == 'number':
  194. return 'double'
  195. elif type(name) == list:
  196. return '%s *' % c_list_type(name[0])
  197. elif is_enum(name):
  198. return name
  199. elif name == None or len(name) == 0:
  200. return 'void'
  201. elif name == name.upper():
  202. return '%sEvent *' % camel_case(name)
  203. else:
  204. return '%s *' % name
  205. def genindent(count):
  206. ret = ""
  207. for i in range(count):
  208. ret += " "
  209. return ret
  210. indent_level = 0
  211. def push_indent(indent_amount=4):
  212. global indent_level
  213. indent_level += indent_amount
  214. def pop_indent(indent_amount=4):
  215. global indent_level
  216. indent_level -= indent_amount
  217. def cgen(code, **kwds):
  218. indent = genindent(indent_level)
  219. lines = code.split('\n')
  220. lines = map(lambda x: indent + x, lines)
  221. return '\n'.join(lines) % kwds + '\n'
  222. def mcgen(code, **kwds):
  223. return cgen('\n'.join(code.split('\n')[1:-1]), **kwds)
  224. def basename(filename):
  225. return filename.split("/")[-1]
  226. def guardname(filename):
  227. guard = basename(filename).rsplit(".", 1)[0]
  228. for substr in [".", " ", "-"]:
  229. guard = guard.replace(substr, "_")
  230. return guard.upper() + '_H'
  231. def guardstart(name):
  232. return mcgen('''
  233. #ifndef %(name)s
  234. #define %(name)s
  235. ''',
  236. name=guardname(name))
  237. def guardend(name):
  238. return mcgen('''
  239. #endif /* %(name)s */
  240. ''',
  241. name=guardname(name))