lua_glue_gen.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. #!/usr/bin/python
  2. import os
  3. import optparse
  4. import xml.etree.ElementTree as et
  5. # Globals
  6. identation_level = 0
  7. out_file = None
  8. separator = "//=============================================================" \
  9. "================="
  10. def parse_commandline():
  11. """ Parse the command line arguments """
  12. parser = optparse.OptionParser(usage = "usage: %prog [options]", \
  13. description = "Create LUA bindings using XML")
  14. parser.add_option("-i", "--input", dest = "inp",
  15. type = "string", help = "specify the XML files to parse. " \
  16. "Seperate with :")
  17. (options, args) = parser.parse_args()
  18. if not options.inp:
  19. parser.error("argument is missing")
  20. return options.inp.split(":")
  21. def get_base_fname(path):
  22. """ From path/to/a/file.ext return the "file" """
  23. return os.path.splitext(os.path.basename(path))[0]
  24. def wglue(txt):
  25. """ Write glue code to the output """
  26. global out_file
  27. global identation_level
  28. out_file.write("%s%s\n" % ("\t" * identation_level, txt))
  29. def ident(number):
  30. """ Increase or recrease identation for the wglue """
  31. global identation_level
  32. identation_level += number
  33. def type_is_bool(type):
  34. """ Check if a type is boolean """
  35. return type == "Bool" or type == "Bool8" or type == "bool"
  36. def type_is_number(type):
  37. """ Check if a type is number """
  38. numbers = ["U8", "U16", "U32", "U64", "I8", "I16", "I32", "I64", \
  39. "U", "I", "PtrSize", "F32", "F64", \
  40. "int", "unsigned", "unsigned int", "short", "unsigned short", "uint", \
  41. "float", "double"]
  42. it_is = False
  43. for num in numbers:
  44. if num == type:
  45. it_is = True
  46. break
  47. return it_is
  48. def parse_type_decl(arg_txt):
  49. """ Parse an arg text """
  50. tokens = arg_txt.split(" ")
  51. tokens_size = len(tokens)
  52. type = tokens[tokens_size - 1]
  53. type_len = len(type)
  54. is_ptr = False
  55. is_ref = False
  56. if type[type_len - 1] == "&":
  57. is_ref = True
  58. elif type[type_len - 1] == "*":
  59. is_ptr = True
  60. if is_ref or is_ptr:
  61. type = type[:-1]
  62. is_const = False
  63. if tokens[0] == "const":
  64. is_const = True
  65. return (type, is_ref, is_ptr, is_const)
  66. def ret(ret_el):
  67. """ Push return value """
  68. if ret_el is None:
  69. wglue("return 0;")
  70. return
  71. wglue("// Push return value")
  72. type_txt = ret_el.text
  73. (type, is_ref, is_ptr, is_const) = parse_type_decl(type_txt)
  74. if is_ptr:
  75. wglue("if(ANKI_UNLIKELY(ret == nullptr))")
  76. wglue("{")
  77. ident(1)
  78. wglue("lua_pushstring(l, \"Glue code returned nullptr\");")
  79. wglue("return -1;")
  80. ident(-1)
  81. wglue("}")
  82. wglue("")
  83. if type_is_bool(type):
  84. wglue("lua_pushboolean(l, ret);")
  85. elif type_is_number(type):
  86. wglue("lua_pushnumber(l, ret);")
  87. elif type == "char" or type == "CString":
  88. wglue("lua_pushstring(l, &ret[0]);")
  89. elif type == "Error":
  90. wglue("if(ANKI_UNLIKELY(ret))")
  91. wglue("{")
  92. ident(1)
  93. wglue("lua_pushstring(l, \"Glue code returned an error\");")
  94. wglue("return -1;")
  95. ident(-1)
  96. wglue("}")
  97. wglue("")
  98. wglue("lua_pushnumber(l, ret);")
  99. else:
  100. wglue("voidp = lua_newuserdata(l, sizeof(UserData));")
  101. wglue("ud = reinterpret_cast<UserData*>(voidp);")
  102. wglue("luaL_setmetatable(l, \"%s\");" % type)
  103. if is_ptr:
  104. if is_const:
  105. wglue("ud->m_data = const_cast<void*>(" \
  106. "reinterpret_cast<const void*>(ret));")
  107. else:
  108. wglue("ud->m_data = reinterpret_cast<void*>(ret);")
  109. wglue("ud->m_gc = false;")
  110. elif is_ref:
  111. if is_const:
  112. wglue("ud->m_data = const_cast<void*>(" \
  113. "reinterpret_cast<const void*>(&ret));")
  114. else:
  115. wglue("ud->m_data = reinterpret_cast<void*>(&ret);")
  116. wglue("ud->m_gc = false;")
  117. else:
  118. wglue("ud->m_data = LuaBinder::luaAlloc(l, sizeof(%s));" % type)
  119. wglue("if(ANKI_UNLIKELY(ud->m_data == nullptr))")
  120. wglue("{")
  121. ident(1)
  122. wglue("ud->m_gc = false;")
  123. wglue("lua_pushstring(l, \"Out of memory\");")
  124. wglue("return -1;")
  125. ident(-1)
  126. wglue("}")
  127. wglue("")
  128. wglue("::new(ud->m_data) %s(std::move(ret));" % type)
  129. wglue("ud->m_gc = true;")
  130. wglue("")
  131. wglue("return 1;")
  132. def arg(arg_txt, stack_index, index):
  133. """ Write the pop code for a single argument """
  134. (type, is_ref, is_ptr, is_const) = parse_type_decl(arg_txt)
  135. if type_is_bool(type) or type_is_number(type):
  136. #wglue("%s arg%d(luaL_checknumber(l, %d));" \
  137. #% (type, index, stack_index))
  138. wglue("%s arg%d;" % (type, index))
  139. wglue("if(LuaBinder::checkNumber(l, %d, arg%d)) return -1;" \
  140. % (stack_index, index))
  141. elif type == "char" or type == "CString":
  142. wglue("const char* arg%d(luaL_checkstring(l, %d));" \
  143. % (index, stack_index))
  144. else:
  145. wglue("voidp = luaL_checkudata(l, %d, \"%s\");" \
  146. % (stack_index, type))
  147. wglue("ud = reinterpret_cast<UserData*>(voidp);")
  148. wglue("%s* iarg%d = reinterpret_cast<%s*>(ud->m_data);" \
  149. % (type, index, type))
  150. wglue("ANKI_ASSERT(iarg%d != nullptr);" % index)
  151. if is_ptr:
  152. wglue("%s arg%d(iarg%d);" % (arg_txt, index, index))
  153. else:
  154. wglue("%s arg%d(*iarg%d);" % (arg_txt, index, index))
  155. def args(args_el, stack_index, class_name):
  156. """ Write the pop code for argument parsing and return the arg list """
  157. if args_el is None:
  158. return ""
  159. wglue("// Pop arguments")
  160. arg_index = 0
  161. # Do the work
  162. args_str = ""
  163. arg_index = 0
  164. for arg_el in args_el.iter("arg"):
  165. arg(arg_el.text, stack_index, arg_index)
  166. args_str += "arg%d, " % arg_index
  167. wglue("")
  168. stack_index += 1
  169. arg_index += 1
  170. if len(args_str) > 0:
  171. args_str = args_str[:-2]
  172. return args_str
  173. def check_args(args_el, bias):
  174. if args_el is None:
  175. wglue("LuaBinder::checkArgsCount(l, %d);" % bias)
  176. else:
  177. count = 0
  178. for arg_el in args_el.iter("arg"):
  179. count += 1
  180. wglue("LuaBinder::checkArgsCount(l, %d);" % (bias + count))
  181. wglue("")
  182. def get_meth_alias(meth_el):
  183. """ Return the method alias """
  184. meth_name = meth_el.get("name")
  185. if meth_name == "operator+":
  186. meth_alias = "__add"
  187. elif meth_name == "operator-":
  188. meth_alias = "__sub"
  189. elif meth_name == "operator*":
  190. meth_alias = "__mul"
  191. elif meth_name == "operator/":
  192. meth_alias = "__div"
  193. elif meth_name == "operator==":
  194. meth_alias = "__eq"
  195. elif meth_name == "operator<":
  196. meth_alias = "__lt"
  197. elif meth_name == "operator<=":
  198. meth_alias = "__le"
  199. elif meth_name == "operator>":
  200. meth_alias = "__gt"
  201. elif meth_name == "operator>=":
  202. meth_alias = "__ge"
  203. elif meth_name == "operator=":
  204. meth_alias = "copy"
  205. else:
  206. meth_alias = meth_name
  207. meth_alias_txt = meth_el.get("alias")
  208. if meth_alias_txt is not None:
  209. meth_alias = meth_alias_txt
  210. return meth_alias
  211. def write_local_vars():
  212. wglue("UserData* ud;")
  213. wglue("(void)ud;")
  214. wglue("void* voidp;")
  215. wglue("(void)voidp;")
  216. wglue("Error err = ErrorCode::NONE;")
  217. wglue("(void)err;")
  218. wglue("")
  219. def method(class_name, meth_el):
  220. """ Handle a method """
  221. meth_name = meth_el.get("name")
  222. meth_alias = get_meth_alias(meth_el)
  223. global separator
  224. wglue(separator)
  225. wglue("/// Pre-wrap method %s::%s." % (class_name, meth_name))
  226. wglue("static inline int pwrap%s%s(lua_State* l)" \
  227. % (class_name, meth_alias))
  228. wglue("{")
  229. ident(1)
  230. write_local_vars()
  231. check_args(meth_el.find("args"), 1)
  232. # Get this pointer
  233. wglue("// Get \"this\" as \"self\"")
  234. wglue("voidp = luaL_checkudata(l, %d, classname%s);" % (1, class_name))
  235. wglue("ud = reinterpret_cast<UserData*>(voidp);")
  236. wglue("%s* self = reinterpret_cast<%s*>(ud->m_data);" \
  237. % (class_name, class_name))
  238. wglue("ANKI_ASSERT(self != nullptr);")
  239. wglue("")
  240. args_str = args(meth_el.find("args"), 2, class_name)
  241. # Return value
  242. ret_txt = None
  243. ret_el = meth_el.find("return")
  244. if ret_el is not None:
  245. ret_txt = ret_el.text
  246. # Method call
  247. wglue("// Call the method")
  248. call = meth_el.find("overrideCall")
  249. if call is not None:
  250. call = call.text
  251. if call is not None:
  252. wglue("%s" % call)
  253. else:
  254. if ret_txt is None:
  255. wglue("self->%s(%s);" % (meth_name, args_str))
  256. else:
  257. wglue("%s ret = self->%s(%s);" % (ret_txt, meth_name, args_str))
  258. wglue("")
  259. ret(ret_el)
  260. ident(-1)
  261. wglue("}")
  262. wglue("")
  263. # Write the actual function
  264. wglue(separator)
  265. wglue("/// Wrap method %s::%s." % (class_name, meth_name))
  266. wglue("static int wrap%s%s(lua_State* l)" % (class_name, meth_alias))
  267. wglue("{")
  268. ident(1)
  269. wglue("int res = pwrap%s%s(l);" % (class_name, meth_alias))
  270. wglue("if(res >= 0) return res;")
  271. wglue("lua_error(l);")
  272. wglue("return 0;")
  273. ident(-1)
  274. wglue("}")
  275. wglue("")
  276. def static_method(class_name, meth_el):
  277. """ Handle a static method """
  278. meth_name = meth_el.get("name")
  279. meth_alias = get_meth_alias(meth_el)
  280. global separator
  281. wglue(separator)
  282. wglue("/// Pre-wrap static method %s::%s." % (class_name, meth_name))
  283. wglue("static inline int pwrap%s%s(lua_State* l)" \
  284. % (class_name, meth_alias))
  285. wglue("{")
  286. ident(1)
  287. write_local_vars()
  288. check_args(meth_el.find("args"), 0)
  289. # Args
  290. args_str = args(meth_el.find("args"), 1, class_name)
  291. # Return value
  292. ret_txt = None
  293. ret_el = meth_el.find("return")
  294. if ret_el is not None:
  295. ret_txt = ret_el.text
  296. # Method call
  297. wglue("// Call the method")
  298. if ret_txt is None:
  299. wglue("%s::%s(%s);" % (class_name, meth_name, args_str))
  300. else:
  301. wglue("%s ret = %s::%s(%s);" \
  302. % (ret_txt, class_name, meth_name, args_str))
  303. wglue("")
  304. ret(ret_el)
  305. ident(-1)
  306. wglue("}")
  307. wglue("")
  308. # Write the actual function
  309. wglue(separator)
  310. wglue("/// Wrap static method %s::%s." % (class_name, meth_name))
  311. wglue("static int wrap%s%s(lua_State* l)" % (class_name, meth_alias))
  312. wglue("{")
  313. ident(1)
  314. wglue("int res = pwrap%s%s(l);" % (class_name, meth_alias))
  315. wglue("if(res >= 0) return res;")
  316. wglue("lua_error(l);")
  317. wglue("return 0;")
  318. ident(-1)
  319. wglue("}")
  320. wglue("")
  321. def constructor(constr_el, class_name):
  322. """ Handle constructor """
  323. global separator
  324. wglue(separator)
  325. wglue("/// Pre-wrap constructor for %s." % (class_name))
  326. wglue("static inline int pwrap%sCtor(lua_State* l)" % class_name)
  327. wglue("{")
  328. ident(1)
  329. write_local_vars()
  330. check_args(constr_el.find("args"), 0)
  331. # Args
  332. args_str = args(constr_el.find("args"), 1, class_name)
  333. # Create new userdata
  334. wglue("// Create user data")
  335. wglue("voidp = lua_newuserdata(l, sizeof(UserData));")
  336. wglue("ud = reinterpret_cast<UserData*>(voidp);")
  337. wglue("ud->m_data = nullptr;")
  338. wglue("ud->m_gc = false;")
  339. wglue("luaL_setmetatable(l, classname%s);" % class_name)
  340. wglue("")
  341. wglue("void* inst = LuaBinder::luaAlloc(l, sizeof(%s));" % class_name)
  342. wglue("if(ANKI_UNLIKELY(inst == nullptr))")
  343. wglue("{")
  344. ident(1)
  345. wglue("lua_pushstring(l, \"Out of memory\");")
  346. wglue("return -1;")
  347. ident(-1)
  348. wglue("}")
  349. wglue("")
  350. wglue("::new(inst) %s(%s);" % (class_name, args_str))
  351. wglue("")
  352. wglue("ud->m_data = inst;")
  353. wglue("ud->m_gc = true;")
  354. wglue("")
  355. wglue("return 1;")
  356. ident(-1)
  357. wglue("}")
  358. wglue("")
  359. # Write the actual function
  360. wglue(separator)
  361. wglue("/// Wrap constructor for %s." % class_name)
  362. wglue("static int wrap%sCtor(lua_State* l)" % class_name)
  363. wglue("{")
  364. ident(1)
  365. wglue("int res = pwrap%sCtor(l);" % class_name)
  366. wglue("if(res >= 0) return res;")
  367. wglue("lua_error(l);")
  368. wglue("return 0;")
  369. ident(-1)
  370. wglue("}")
  371. wglue("")
  372. def destructor(class_name):
  373. """ Create a destroctor """
  374. global separator
  375. wglue(separator)
  376. wglue("/// Wrap destructor for %s." % (class_name))
  377. wglue("static int wrap%sDtor(lua_State* l)" % class_name)
  378. wglue("{")
  379. ident(1)
  380. wglue("LuaBinder::checkArgsCount(l, 1);")
  381. wglue("void* voidp = luaL_checkudata(l, 1, classname%s);" % class_name)
  382. wglue("UserData* ud = reinterpret_cast<UserData*>(voidp);")
  383. wglue("if(ud->m_gc)")
  384. wglue("{")
  385. ident(1)
  386. wglue("%s* inst = reinterpret_cast<%s*>(ud->m_data);" \
  387. % (class_name, class_name))
  388. wglue("inst->~%s();" % class_name)
  389. wglue("LuaBinder::luaFree(l, inst);")
  390. ident(-1)
  391. wglue("}")
  392. wglue("")
  393. wglue("return 0;")
  394. ident(-1)
  395. wglue("}")
  396. wglue("")
  397. def class_(class_el):
  398. """ Create a class """
  399. global separator
  400. class_name = class_el.get("name")
  401. # Write class decoration and stuff
  402. wglue(separator)
  403. cmnt = "// %s" % class_name
  404. cmnt += (79 - len(cmnt)) * " "
  405. cmnt += "="
  406. wglue(cmnt)
  407. wglue(separator)
  408. wglue("")
  409. wglue(separator)
  410. wglue("static const char* classname%s = \"%s\";" \
  411. % (class_name, class_name))
  412. wglue("")
  413. # Constructor declarations
  414. has_constructor = False
  415. constructor_el = class_el.find("constructor")
  416. if constructor_el is not None:
  417. constructor(constructor_el, class_name)
  418. has_constructor = True
  419. # Destructor declarations
  420. if has_constructor:
  421. destructor(class_name)
  422. # Methods LUA C functions declarations
  423. meth_names_aliases = []
  424. meths_el = class_el.find("methods")
  425. if meths_el is not None:
  426. for meth_el in meths_el.iter("method"):
  427. is_static = meth_el.get("static")
  428. is_static = is_static is not None and is_static == "1"
  429. if is_static:
  430. static_method(class_name, meth_el)
  431. else:
  432. method(class_name, meth_el)
  433. meth_name = meth_el.get("name")
  434. meth_alias = get_meth_alias(meth_el)
  435. meth_names_aliases.append([meth_name, meth_alias, is_static])
  436. # Start class declaration
  437. wglue(separator)
  438. wglue("/// Wrap class %s." % class_name)
  439. wglue("static inline void wrap%s(lua_State* l)" % class_name)
  440. wglue("{")
  441. ident(1)
  442. wglue("LuaBinder::createClass(l, classname%s);" % class_name)
  443. # Register constructor
  444. if has_constructor:
  445. wglue("LuaBinder::pushLuaCFuncStaticMethod(l, classname%s, \"new\", " \
  446. "wrap%sCtor);" % (class_name, class_name))
  447. # Register destructor
  448. if has_constructor:
  449. wglue("LuaBinder::pushLuaCFuncMethod(l, \"__gc\", wrap%sDtor);" \
  450. % class_name)
  451. # Register methods
  452. if len(meth_names_aliases) > 0:
  453. for meth_name_alias in meth_names_aliases:
  454. meth_alias = meth_name_alias[1]
  455. is_static = meth_name_alias[2]
  456. if is_static:
  457. wglue("LuaBinder::pushLuaCFuncStaticMethod(l, classname%s, " \
  458. "\"%s\", wrap%s%s);" \
  459. % (class_name, meth_alias, class_name, meth_alias))
  460. else:
  461. wglue("LuaBinder::pushLuaCFuncMethod(l, \"%s\", wrap%s%s);" \
  462. % (meth_alias, class_name, meth_alias))
  463. wglue("lua_settop(l, 0);")
  464. ident(-1)
  465. wglue("}")
  466. wglue("")
  467. def main():
  468. """ Main function """
  469. global out_file
  470. global separator
  471. filenames = parse_commandline()
  472. for filename in filenames:
  473. out_filename = get_base_fname(filename) + ".cpp"
  474. out_file = open(out_filename, "w")
  475. tree = et.parse(filename)
  476. root = tree.getroot()
  477. # Head
  478. head = root.find("head")
  479. if head is not None:
  480. wglue("%s" % head.text)
  481. wglue("")
  482. class_names = []
  483. for cls in root.iter("classes"):
  484. for cl in cls.iter("class"):
  485. class_(cl)
  486. class_names.append(cl.get("name"))
  487. wglue(separator)
  488. wglue("/// Wrap the module.")
  489. wglue("void wrapModule%s(lua_State* l)" % get_base_fname(filename))
  490. wglue("{")
  491. ident(1)
  492. for class_name in class_names:
  493. wglue("wrap%s(l);" % class_name)
  494. ident(-1)
  495. wglue("}")
  496. wglue("")
  497. # Tail
  498. tail = root.find("tail")
  499. if tail is not None:
  500. wglue("%s" % tail.text)
  501. wglue("")
  502. out_file.close()
  503. if __name__ == "__main__":
  504. main()