Răsfoiți Sursa

Reworked LUA binding. Forgit to commit the generator

Panagiotis Christopoulos Charitos 11 ani în urmă
părinte
comite
9f1d41d0bd
1 a modificat fișierele cu 629 adăugiri și 0 ștergeri
  1. 629 0
      src/script/lua_glue_gen.py

+ 629 - 0
src/script/lua_glue_gen.py

@@ -0,0 +1,629 @@
+#!/usr/bin/python
+
+import os
+import optparse
+import xml.etree.ElementTree as et
+
+# Globals
+identation_level = 0
+out_file = None
+
+separator = "//=============================================================" \
+	"================="
+
+def parse_commandline():
+	""" Parse the command line arguments """
+
+	parser = optparse.OptionParser(usage = "usage: %prog [options]", \
+		description = "Create LUA bindings using XML")	
+
+	parser.add_option("-i", "--input", dest = "inp",
+		type = "string", help = "specify the XML files to parse. " \
+		"Seperate with :")
+
+	(options, args) = parser.parse_args()
+
+	if not options.inp:
+		parser.error("argument is missing")
+
+	return options.inp.split(":")
+
+def get_base_fname(path):
+	""" From path/to/a/file.ext return the "file" """
+	return os.path.splitext(os.path.basename(path))[0]
+
+def wglue(txt):
+	""" Write glue code to the output """
+	global out_file
+	global identation_level
+	out_file.write("%s%s\n" % ("\t" * identation_level, txt))
+
+def ident(number):
+	""" Increase or recrease identation for the wglue """
+	global identation_level
+	identation_level += number
+
+def type_is_bool(type):
+	""" Check if a type is boolean """
+
+	return type == "Bool" or type == "Bool8" or type == "bool"
+
+def type_is_number(type):
+	""" Check if a type is number """
+
+	numbers = ["U8", "U16", "U32", "U64", "I8", "I16", "I32", "I64", \
+		"U", "I", "PtrSize", "F32", "F64", \
+		"int", "unsigned", "unsigned int", "short", "unsigned short", "uint", \
+		"float", "double"]
+		
+	it_is = False
+	for num in numbers:
+		if num == type:
+			it_is = True
+			break
+
+	return it_is
+
+def parse_type_decl(arg_txt):
+	""" Parse an arg text """
+
+	tokens = arg_txt.split(" ")
+	tokens_size = len(tokens)
+
+	type = tokens[tokens_size - 1]
+	type_len = len(type)
+	is_ptr = False
+	is_ref = False
+	if type[type_len - 1] == "&":
+		is_ref = True
+	elif type[type_len - 1] == "*":
+		is_ptr = True
+
+	if is_ref or is_ptr:
+		type = type[:-1]
+
+	is_const = False
+	if tokens[0] == "const":
+		is_const = True
+
+	return (type, is_ref, is_ptr, is_const)
+
+def ret(ret_el):
+	""" Push return value """
+
+	if ret_el is None:
+		wglue("return 0;")
+		return
+
+	wglue("// Push return value")
+
+	zero_as_error = ret_el.get("zeroAsError", None)
+	if zero_as_error is not None and zero_as_error == "1":
+		wglue("if(ANKI_UNLIKELY(ret == 0))")
+		wglue("{")
+		ident(1)
+		wglue("lua_pushstring(l, \"Glue code returned zero\");")
+		wglue("return -1;")
+		ident(-1)
+		wglue("}")
+
+	type_txt = ret_el.text
+	(type, is_ref, is_ptr, is_const) = parse_type_decl(type_txt)
+	
+	if type_is_bool(type):
+		wglue("lua_pushboolean(l, ret);")
+	elif type_is_number(type):
+		wglue("lua_pushnumber(l, ret);")
+	elif type == "char" or type == "CString":
+		wglue("lua_pushstring(l, &ret[0]);")
+	elif type == "Error":
+		wglue("if(ANKI_UNLIKELY(ret))")
+		wglue("{")
+		ident(1)
+		wglue("lua_pushstring(l, \"Glue code returned an error\");")
+		wglue("return -1;")
+		ident(-1)
+		wglue("}")
+		wglue("")
+		wglue("lua_pushnumber(l, ret);")
+	else:
+	 	wglue("voidp = lua_newuserdata(l, sizeof(UserData));")
+		wglue("ud = reinterpret_cast<UserData*>(voidp);")
+		wglue("luaL_setmetatable(l, \"%s\");" % type)
+
+		if is_ptr:
+			if is_const:
+				wglue("ud->m_data = const_cast<void*>(" \
+					"reinterpret_cast<const void*>(ret));")
+			else:
+				wglue("ud->m_data = reinterpret_cast<void*>(ret);")
+			wglue("ud->m_gc = false;")
+		elif is_ref:
+			if is_const:
+				wglue("ud->m_data = const_cast<void*>(" \
+					"reinterpret_cast<const void*>(&ret));")
+			else:
+				wglue("ud->m_data = reinterpret_cast<void*>(&ret);")
+			wglue("ud->m_gc = false;")
+		else:
+			wglue("ud->m_data = LuaBinder::luaAlloc(l, sizeof(%s));" % type)
+	
+			wglue("if(ANKI_UNLIKELY(ud->m_data == nullptr))")
+			wglue("{")
+			ident(1)
+			wglue("ud->m_gc = false;")
+			wglue("lua_pushstring(l, \"Out of memory\");")
+			wglue("return -1;")
+			ident(-1)
+			wglue("}")
+			wglue("")
+
+			wglue("::new(ud->m_data) %s(std::move(ret));" % type)
+			wglue("ud->m_gc = true;")
+
+	wglue("")
+	wglue("return 1;")
+
+def arg(arg_txt, stack_index, index):
+	""" Write the pop code for a single argument """
+
+	(type, is_ref, is_ptr, is_const) = parse_type_decl(arg_txt)
+
+	if type_is_bool(type):
+		wglue("%s arg%d(luaL_checknumber(l, %d));" \
+			% (type, index, stack_index))
+	elif type_is_number(type):
+		wglue("%s arg%d(luaL_checknumber(l, %d));" \
+			% (type, index, stack_index))
+	elif type == "char" or type == "CString":
+		wglue("const char* arg%d(luaL_checkstring(l, %d));" \
+			% (index, stack_index))
+	else:
+		wglue("voidp = luaL_checkudata(l, %d, \"%s\");" \
+			% (stack_index, type))
+		wglue("ud = reinterpret_cast<UserData*>(voidp);")
+		wglue("%s* iarg%d = reinterpret_cast<%s*>(ud->m_data);" \
+			% (type, index, type))
+		wglue("ANKI_ASSERT(iarg%d != nullptr);" % index)
+
+		if is_ptr:
+			wglue("%s arg%d(iarg%d);" % (arg_txt, index, index))
+		else:
+			wglue("%s arg%d(*iarg%d);" % (arg_txt, index, index))
+
+
+def args(args_el, stack_index, class_name):
+	""" Write the pop code for argument parsing and return the arg list """
+
+	if args_el is None:
+		return ""
+
+	wglue("// Pop arguments")
+	arg_index = 0
+
+	# Do the work
+	args_str = ""
+	arg_index = 0
+	for arg_el in args_el.iter("arg"):
+		arg(arg_el.text, stack_index, arg_index)
+		args_str += "arg%d, " % arg_index
+		wglue("")
+		stack_index += 1
+		arg_index += 1
+
+	if len(args_str) > 0:
+		args_str = args_str[:-2]
+
+	return args_str
+
+def check_args(args_el, bias):
+	if args_el is None:
+		wglue("LuaBinder::checkArgsCount(l, %d);" % bias)
+	else:
+		count = 0
+		for arg_el in args_el.iter("arg"):
+			count += 1
+
+		wglue("LuaBinder::checkArgsCount(l, %d);" % (bias + count))
+
+	wglue("")
+
+def get_meth_alias(meth_el):
+	""" Return the method alias """
+
+	meth_name = meth_el.get("name")
+
+	if meth_name == "operator+":
+		meth_alias = "__add"
+	elif meth_name == "operator-":
+		meth_alias = "__sub"
+	elif meth_name == "operator*":
+		meth_alias = "__mul"
+	elif meth_name == "operator/":
+		meth_alias = "__div"
+	elif meth_name == "operator==":
+		meth_alias = "__eq"
+	elif meth_name == "operator<":
+		meth_alias = "__lt"
+	elif meth_name == "operator<=":
+		meth_alias = "__le"
+	elif meth_name == "operator>":
+		meth_alias = "__gt"
+	elif meth_name == "operator>=":
+		meth_alias = "__ge"
+	elif meth_name == "operator=":
+		meth_alias = "copy"
+	else:
+		meth_alias = meth_name
+
+	meth_alias_txt = meth_el.get("alias")
+	if meth_alias_txt is not None:
+		meth_alias = meth_alias_txt
+
+	return meth_alias
+
+def method(class_name, meth_el):
+	""" Handle a method """
+
+	meth_name = meth_el.get("name")
+	meth_alias = get_meth_alias(meth_el)
+
+	global separator
+
+	wglue(separator)
+	wglue("/// Pre-wrap method %s::%s." % (class_name, meth_name))
+	wglue("static inline int pwrap%s%s(lua_State* l)" \
+		% (class_name, meth_alias))
+	wglue("{")
+	ident(1)
+	wglue("UserData* ud;")
+	wglue("(void)ud;")
+	wglue("void* voidp;")
+	wglue("(void)voidp;")
+	wglue("")
+
+	check_args(meth_el.find("args"), 1)
+
+	# Get this pointer
+	wglue("// Get \"this\" as \"self\"")
+	wglue("voidp = luaL_checkudata(l, %d, classname%s);" % (1, class_name))
+	wglue("ud = reinterpret_cast<UserData*>(voidp);")
+	wglue("%s* self = reinterpret_cast<%s*>(ud->m_data);" \
+		% (class_name, class_name))
+	wglue("ANKI_ASSERT(self != nullptr);")
+	wglue("")
+
+	args_str = args(meth_el.find("args"), 2, class_name)
+
+	# Return value
+	ret_txt = None
+	ret_el = meth_el.find("return")
+	if ret_el is not None:
+		ret_txt = ret_el.text
+
+	# Method call
+	wglue("// Call the method")
+	call = meth_el.find("overrideCall")
+	if call is not None:
+		call = call.text
+
+	if call is not None:
+		wglue("%s" % call)
+	else:
+		if ret_txt is None:
+			wglue("self->%s(%s);" % (meth_name, args_str))
+		else:
+			wglue("%s ret = self->%s(%s);" % (ret_txt, meth_name, args_str))
+
+	wglue("")
+	ret(ret_el)
+
+	ident(-1)
+	wglue("}")
+	wglue("")
+
+	# Write the actual function
+	wglue(separator)
+	wglue("/// Wrap method %s::%s." % (class_name, meth_name))
+	wglue("static int wrap%s%s(lua_State* l)" % (class_name, meth_alias))
+	wglue("{")
+	ident(1)
+	wglue("int res = pwrap%s%s(l);" % (class_name, meth_alias))
+	wglue("if(res >= 0) return res;")
+	wglue("lua_error(l);")
+	wglue("return 0;")
+	ident(-1)
+	wglue("}")
+	wglue("")
+
+def static_method(class_name, meth_el):
+	""" Handle a static method """
+
+	meth_name = meth_el.get("name")
+	meth_alias = get_meth_alias(meth_el)
+
+	global separator
+
+	wglue(separator)
+	wglue("/// Pre-wrap static method %s::%s." % (class_name, meth_name))
+	wglue("static inline int pwrap%s%s(lua_State* l)" \
+		% (class_name, meth_alias))
+	wglue("{")
+	ident(1)
+	wglue("UserData* ud;")
+	wglue("(void)ud;")
+	wglue("void* voidp;")
+	wglue("(void)voidp;")
+	wglue("")
+
+	check_args(meth_el.find("args"), 0)
+
+	# Args
+	args_str = args(meth_el.find("args"), 1, class_name)
+	
+	# Return value
+	ret_txt = None
+	ret_el = meth_el.find("return")
+	if ret_el is not None:
+		ret_txt = ret_el.text
+
+	# Method call
+	wglue("// Call the method")
+	if ret_txt is None:
+		wglue("%s::%s(%s);" % (class_name, meth_name, args_str))
+	else:
+		wglue("%s ret = %s::%s(%s);" \
+			% (ret_txt, class_name, meth_name, args_str))
+
+	wglue("")
+	ret(ret_el)
+
+	ident(-1)
+	wglue("}")
+	wglue("")
+
+	# Write the actual function
+	wglue(separator)
+	wglue("/// Wrap static method %s::%s." % (class_name, meth_name))
+	wglue("static int wrap%s%s(lua_State* l)" % (class_name, meth_alias))
+	wglue("{")
+	ident(1)
+	wglue("int res = pwrap%s%s(l);" % (class_name, meth_alias))
+	wglue("if(res >= 0) return res;")
+	wglue("lua_error(l);")
+	wglue("return 0;")
+	ident(-1)
+	wglue("}")
+	wglue("")
+
+def constructor(constr_el, class_name):
+	""" Handle constructor """
+	
+	global separator
+
+	wglue(separator)
+	wglue("/// Pre-wrap constructor for %s." % (class_name))
+	wglue("static inline int pwrap%sCtor(lua_State* l)" % class_name)
+	wglue("{")
+	ident(1)
+	wglue("UserData* ud;")
+	wglue("(void)ud;")
+	wglue("void* voidp;")
+	wglue("(void)voidp;")
+	wglue("")
+
+	check_args(constr_el.find("args"), 0)
+
+	# Args
+	args_str = args(constr_el.find("args"), 1, class_name)
+
+	# Create new userdata
+	wglue("// Create user data")
+	wglue("voidp = lua_newuserdata(l, sizeof(UserData));")
+	wglue("ud = reinterpret_cast<UserData*>(voidp);")
+	wglue("ud->m_data = nullptr;")
+	wglue("ud->m_gc = false;")
+	wglue("luaL_setmetatable(l, classname%s);" % class_name)
+	wglue("")
+
+	wglue("void* inst = LuaBinder::luaAlloc(l, sizeof(%s));" % class_name)
+	wglue("if(ANKI_UNLIKELY(inst == nullptr))")
+	wglue("{")
+	ident(1)
+	wglue("lua_pushstring(l, \"Out of memory\");")
+	wglue("return -1;")
+	ident(-1)
+	wglue("}")
+	wglue("")
+	wglue("::new(inst) %s(%s);" % (class_name, args_str))
+	wglue("")
+
+	wglue("ud->m_data = inst;")
+	wglue("ud->m_gc = true;")
+	wglue("")
+
+	wglue("return 1;")
+	
+	ident(-1)
+	wglue("}")
+	wglue("")
+
+	# Write the actual function
+	wglue(separator)
+	wglue("/// Wrap constructor for %s." % class_name)
+	wglue("static int wrap%sCtor(lua_State* l)" % class_name)
+	wglue("{")
+	ident(1)
+	wglue("int res = pwrap%sCtor(l);" % class_name)
+	wglue("if(res >= 0) return res;")
+	wglue("lua_error(l);")
+	wglue("return 0;")
+	ident(-1)
+	wglue("}")
+	wglue("")
+
+def destructor(class_name):
+	""" Create a destroctor """
+	
+	global separator
+
+	wglue(separator)
+	wglue("/// Wrap destructor for %s." % (class_name))
+	wglue("static int wrap%sDtor(lua_State* l)" % class_name)
+	wglue("{")
+	ident(1)
+
+	wglue("LuaBinder::checkArgsCount(l, 1);")
+	wglue("void* voidp = luaL_checkudata(l, 1, classname%s);" % class_name)
+	wglue("UserData* ud = reinterpret_cast<UserData*>(voidp);")
+
+	wglue("if(ud->m_gc)")
+	wglue("{")
+	ident(1)
+	wglue("%s* inst = reinterpret_cast<%s*>(ud->m_data);" \
+		% (class_name, class_name))
+	wglue("inst->~%s();" % class_name)
+	wglue("LuaBinder::luaFree(l, inst);")
+	ident(-1)
+	wglue("}")
+	wglue("")
+
+	wglue("return 0;")
+
+	ident(-1)
+	wglue("}")
+	wglue("")
+
+def class_(class_el):
+	""" Create a class """
+	
+	global separator
+	class_name = class_el.get("name")
+
+	# Write class decoration and stuff
+	wglue(separator)
+	cmnt = "// %s" % class_name
+	cmnt += (79 - len(cmnt)) * " "
+	cmnt += "="
+	wglue(cmnt)
+	wglue(separator)
+	wglue("")
+
+	wglue(separator)
+	wglue("static const char* classname%s = \"%s\";" \
+		% (class_name, class_name))
+	wglue("")
+
+	# Constructor declarations
+	has_constructor = False
+	constructor_el = class_el.find("constructor")
+	if constructor_el is not None:
+		constructor(constructor_el, class_name)
+		has_constructor = True
+
+	# Destructor declarations
+	if has_constructor:
+		destructor(class_name)
+
+	# Methods LUA C functions declarations
+	meth_names_aliases = []
+	meths_el = class_el.find("methods")
+	if meths_el is not None:
+		for meth_el in meths_el.iter("method"):
+			is_static = meth_el.get("static")
+			is_static = is_static is not None and is_static == "1"
+
+			if is_static:
+				static_method(class_name, meth_el)
+			else:
+				method(class_name, meth_el)
+
+			meth_name = meth_el.get("name")
+			meth_alias = get_meth_alias(meth_el)
+			meth_names_aliases.append([meth_name, meth_alias, is_static])
+
+	# Start class declaration
+	wglue(separator)
+	wglue("/// Wrap class %s." % class_name)
+	wglue("static inline void wrap%s(lua_State* l)" % class_name)
+	wglue("{")
+	ident(1)
+	wglue("LuaBinder::createClass(l, classname%s);" % class_name)
+
+	# Register constructor
+	if has_constructor:
+		wglue("LuaBinder::pushLuaCFuncStaticMethod(l, classname%s, \"new\", " \
+			"wrap%sCtor);" % (class_name, class_name))
+
+	# Register destructor
+	if has_constructor:
+		wglue("LuaBinder::pushLuaCFuncMethod(l, \"__gc\", wrap%sDtor);" \
+			% class_name)
+
+	# Register methods
+	if len(meth_names_aliases) > 0:
+		for meth_name_alias in meth_names_aliases:
+			meth_alias = meth_name_alias[1]
+			is_static = meth_name_alias[2]
+			if is_static:
+				wglue("LuaBinder::pushLuaCFuncStaticMethod(l, classname%s, " \
+					"\"%s\", wrap%s%s);" \
+					% (class_name, meth_alias, class_name, meth_alias))
+			else:
+				wglue("LuaBinder::pushLuaCFuncMethod(l, \"%s\", wrap%s%s);" \
+					% (meth_alias, class_name, meth_alias))
+
+	wglue("lua_settop(l, 0);")
+
+	ident(-1)
+	wglue("}")
+	wglue("")
+
+def main():
+	""" Main function """
+
+	global out_file
+	global separator
+	filenames = parse_commandline()
+
+	for filename in filenames:
+		out_filename = get_base_fname(filename) + ".cpp"
+		out_file = open(out_filename, "w")
+
+		tree = et.parse(filename)
+		root = tree.getroot()
+
+		# Head
+		head = root.find("head")
+		if head is not None:
+			wglue("%s" % head.text)
+			wglue("")
+
+		class_names = []
+		for cls in root.iter("classes"):
+			for cl in cls.iter("class"):
+				class_(cl)
+				class_names.append(cl.get("name"))
+
+		wglue(separator)
+		wglue("/// Wrap the module.")
+		wglue("void wrapModule%s(lua_State* l)" % get_base_fname(filename))
+		wglue("{")
+		ident(1)
+		for class_name in class_names:
+			wglue("wrap%s(l);" % class_name)
+		ident(-1)
+		wglue("}")
+		wglue("")
+
+		# Tail
+		tail = root.find("tail")
+		if tail is not None:
+			wglue("%s" % tail.text)
+			wglue("")
+
+		out_file.close()
+
+if __name__ == "__main__":
+	main()
+