Compare commits
3 Commits
f33a2cf32b
...
6d36cc7669
Author | SHA1 | Date | |
---|---|---|---|
6d36cc7669 | |||
a4a419c73b | |||
c69e652716 |
@ -2,7 +2,9 @@ import json
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# ---- GENERATOR DEFINITIONS
|
||||
# ---- GENERATOR ALGORITHM
|
||||
|
||||
# generate Lua to define enum constants
|
||||
def gen_enum(item):
|
||||
name = item.get("name", "")
|
||||
if name not in kEnumWhitelist:
|
||||
@ -20,71 +22,86 @@ def gen_enum(item):
|
||||
print(f"}} // {name}")
|
||||
print()
|
||||
|
||||
def gen_func(name, ovlds):
|
||||
# generate Lua API calls to define an imgui function which switches overloads and calls
|
||||
def gen_func(name, ovlds, indent="", ctxptr=None):
|
||||
print(f"{indent}lua_pushcfunction(L, [](auto L) {{")
|
||||
print(f"{indent} (void) L;")
|
||||
gen_func_def(name, ovlds, indent=indent+" ", ctxptr=ctxptr)
|
||||
print(f"{indent}}});")
|
||||
print(f"{indent}lua_setfield(L, -2, \"{name}\");")
|
||||
print()
|
||||
|
||||
# generate Lua API calls to push a function which calls a appropriate overload
|
||||
def gen_func_def(name, ovlds, indent="", ctxptr=None):
|
||||
ovld_root = (0, {}, None)
|
||||
for ovld in ovlds:
|
||||
ovld_node = ovld_root
|
||||
idx = 0
|
||||
for arg in ovld.get("inner", []):
|
||||
if "ParmVarDecl" != arg.get("kind"): continue
|
||||
tname = get_type_from_argument(arg)
|
||||
if tname not in kLuaTypeChecker:
|
||||
break
|
||||
ovld_node[1][tname] = (idx, {}, None)
|
||||
if tname not in ovld_node[1]:
|
||||
ovld_node[1][tname] = (ovld_node[0]+1, {}, None)
|
||||
ovld_node = ovld_node[1][tname]
|
||||
idx += 1
|
||||
ovld_node[1]["$"] = (idx, {}, ovld)
|
||||
ovld_node[1]["$"] = (ovld_node[0], {}, ovld)
|
||||
|
||||
def _output(ovld_node, depth=1):
|
||||
indent = " "*depth
|
||||
def _output(ovld_node, depth=0):
|
||||
ind = indent + " "*depth
|
||||
if 0 == len(ovld_node[1]):
|
||||
gen_single_func(ovld_node[2], indent=indent)
|
||||
gen_func_call(name, ovld_node[2], indent=ind, ctxptr=ctxptr)
|
||||
elif 1 == len(ovld_node[1]):
|
||||
_output(*ovld_node[1].values(), depth=depth)
|
||||
else:
|
||||
for tname in ovld_node[1]:
|
||||
checker = kLuaTypeChecker[tname].replace("#", str(ovld_node[0]+1))
|
||||
print(f"{indent}if ({checker}) {{")
|
||||
print(f"{ind}if ({checker}) {{")
|
||||
_output(ovld_node[1][tname], depth=depth+1)
|
||||
print(f"{indent}}}")
|
||||
print(f"{indent}return luaL_error(L, \"unexpected type in param#{ovld_node[0]}\");")
|
||||
print("lua_pushcfunction(L, [](auto L) {")
|
||||
print(" (void) L;")
|
||||
_output(ovld_root)
|
||||
print("});")
|
||||
print(f"lua_setfield(L, -2, \"{name}\");")
|
||||
print()
|
||||
print(f"{ind}}}")
|
||||
print(f"{ind}return luaL_error(L, \"unexpected type in param#{ovld_node[0]}\");")
|
||||
|
||||
def gen_single_func(item, indent=""):
|
||||
name = item["name"]
|
||||
_output(ovld_root)
|
||||
|
||||
# generate Lua to call the function declared by the AST
|
||||
def gen_func_call(name, item, indent="", ctxptr=None):
|
||||
pops = []
|
||||
params = []
|
||||
pushes = []
|
||||
pcount = 0
|
||||
|
||||
if ctxptr is not None:
|
||||
pops.append(
|
||||
f"const {ctxptr}* p0 = reinterpret_cast<{ctxptr}*>(lua_topointer(L, 1))")
|
||||
pcount = 1
|
||||
|
||||
for arg in item.get("inner", []):
|
||||
if "ParmVarDecl" != arg.get("kind"): continue
|
||||
pop, param, push = gen_argument(pcount, arg)
|
||||
pop, param, push = gen_func_argument(pcount, arg)
|
||||
pcount += 1
|
||||
pops .extend(pop)
|
||||
params.extend(param)
|
||||
push .extend(push)
|
||||
pushes.extend(push)
|
||||
|
||||
pushes = gen_return(item)
|
||||
pushes = gen_func_return(item)
|
||||
use_ret = 0 < len(pushes)
|
||||
|
||||
# text output
|
||||
nl = "\n"+indent
|
||||
cm = ", "
|
||||
prefix = "ImGui::" if ctxptr is None else "p0->"
|
||||
if 0 < len(pops):
|
||||
print(f"{indent}{nl.join(pops)}")
|
||||
if use_ret:
|
||||
print(f"{indent}const auto r = ImGui::{name}({cm.join(params)});")
|
||||
print(f"{indent}const auto r = {prefix}{name}({cm.join(params)});")
|
||||
else:
|
||||
print(f"{indent}ImGui::{name}({cm.join(params)});")
|
||||
print(f"{indent}{prefix}{name}({cm.join(params)});")
|
||||
if 0 < len(pushes):
|
||||
print(f"{indent}{nl.join(pushes)}")
|
||||
print(f"{indent}return {len(pushes)};")
|
||||
|
||||
def gen_return(item):
|
||||
# generate Lua to push return values of the function
|
||||
# returns lines of the source code
|
||||
def gen_func_return(item):
|
||||
ftype = item["type"]["qualType"]
|
||||
type = ftype[0:ftype.find("(")-1]
|
||||
|
||||
@ -96,14 +113,19 @@ def gen_return(item):
|
||||
return ["lua_pushnumber(L, static_cast<lua_Number>(r));"]
|
||||
if "ImVec2" == type:
|
||||
return [
|
||||
"lua_pushnumber(L, static_cast<lua_Number>(r.x));",
|
||||
"lua_pushnumber(L, static_cast<lua_Number>(r.y));",
|
||||
"lua_pushnumber(L, static_cast<lua_Number>(r.x));",
|
||||
"lua_pushnumber(L, static_cast<lua_Number>(r.y));",
|
||||
]
|
||||
|
||||
print(f"unknown return type: {type}", file=sys.stderr)
|
||||
return []
|
||||
|
||||
def gen_argument(pc, item):
|
||||
# generate Lua to push one of parameters of the function
|
||||
# returns a tuple with the followings:
|
||||
# - string list of sentences to define variable for storing the parameter
|
||||
# - string list of expressions to pass to the actual C++ function
|
||||
# - string list of sentences to load the parameter
|
||||
def gen_func_argument(pc, item):
|
||||
type = item["type"].get("desugaredQualType", item["type"]["qualType"])
|
||||
n = pc+1
|
||||
pn = f"p{pc}"
|
||||
@ -114,6 +136,9 @@ def gen_argument(pc, item):
|
||||
if "bool" == type:
|
||||
return ([f"const bool {pn} = lua_toboolean(L, {n});"], [pn], [])
|
||||
|
||||
if "float" == type:
|
||||
return ([f"const float {pn} = static_cast<float>(lua_tonumber(L, {n}));"], [pn], [])
|
||||
|
||||
if "const char *" == type:
|
||||
return ([f"const char* {pn} = luaL_checkstring(L, {n});"], [pn], [])
|
||||
|
||||
@ -129,21 +154,38 @@ def gen_argument(pc, item):
|
||||
print(f"unknown argument type: {type}", file=sys.stderr)
|
||||
return ([], [], [])
|
||||
|
||||
def gen_struct(name, funcs):
|
||||
print(f"static const auto Push{name} = [](auto L, {name}* ctxptr) {{")
|
||||
print(f" *reinterpret_cast<{name}**>(lua_newuserdata(sizeof({name}*))) = ctxptr;")
|
||||
print(f" if (luaL_newmetatable(L, \"imgui4lua::{name}\")) {{")
|
||||
for fname in funcs:
|
||||
print(f" lua_createtable(L, 0, 0);")
|
||||
print()
|
||||
gen_func(fname, funcs[fname], indent=" ", ctxptr=name)
|
||||
print(f" lua_setfield(L, -2, \"__index\")")
|
||||
print(f" }}")
|
||||
print(f" lua_setmetatable(L, -2);")
|
||||
print(f"}};")
|
||||
|
||||
|
||||
# ---- GENERATOR UTILITIES
|
||||
|
||||
# get a variable type from the AST of an argument
|
||||
def get_type_from_argument(item):
|
||||
return item["type"].get("desugaredQualType", item["type"]["qualType"])
|
||||
|
||||
|
||||
# ---- WALKER DEFINITIONS
|
||||
class Walker:
|
||||
class RootWalker:
|
||||
def __init__(self):
|
||||
self._funcs = {}
|
||||
self._enums = {}
|
||||
self._structs = {}
|
||||
|
||||
def emit(self):
|
||||
for x in self._enums: gen_enum(self._enums[x])
|
||||
for x in self._funcs: gen_func(x, self._funcs[x])
|
||||
for x in self._structs: self._structs[x].emit()
|
||||
#for x in self._enums: gen_enum(self._enums[x])
|
||||
#for x in self._funcs: gen_func(x, self._funcs[x])
|
||||
|
||||
def walk(self, item):
|
||||
kind = item.get("kind")
|
||||
@ -151,6 +193,9 @@ class Walker:
|
||||
if "EnumDecl" == kind:
|
||||
if name in kEnumWhitelist:
|
||||
self._enums[name] = item
|
||||
elif "CXXRecordDecl" == kind:
|
||||
if name in kStructWhitelist and "inner" in item:
|
||||
self._structs[name] = StructWalker(kStructWhitelist[name], item)
|
||||
else:
|
||||
w = self.walk
|
||||
if "NamespaceDecl" == kind:
|
||||
@ -170,6 +215,29 @@ class Walker:
|
||||
else:
|
||||
self.walk(item)
|
||||
|
||||
class StructWalker:
|
||||
def __init__(self, whitelist, item):
|
||||
self._funcWhitelist = whitelist
|
||||
self._name = item.get("name")
|
||||
self._funcs = {}
|
||||
self._walk(item)
|
||||
|
||||
def _walk(self, item):
|
||||
kind = item.get("kind")
|
||||
name = item.get("name")
|
||||
if "CXXMethodDecl" == kind:
|
||||
if name in self._funcWhitelist:
|
||||
if name not in self._funcs:
|
||||
self._funcs[name] = [item]
|
||||
else:
|
||||
self._funcs[name].append(item)
|
||||
else:
|
||||
for child in item.get("inner", []):
|
||||
self._walk(child)
|
||||
|
||||
def emit(self):
|
||||
gen_struct(self._name, self._funcs)
|
||||
|
||||
|
||||
# ---- DATA DEFINITIONS
|
||||
kFuncWhitelist = [
|
||||
@ -190,7 +258,61 @@ kFuncWhitelist = [
|
||||
kEnumWhitelist = [
|
||||
"ImGuiWindowFlags_",
|
||||
]
|
||||
kStructWhitelist = {
|
||||
"ImDrawList": [
|
||||
"PushClipRect",
|
||||
"PushClipRectFullScreen",
|
||||
"PopClipRect",
|
||||
"GetClipRectMin",
|
||||
"GetClipRectMax",
|
||||
"PushClipRect",
|
||||
"PushClipRectFullScreen",
|
||||
"PopClipRect",
|
||||
#"PushTextureID",
|
||||
"PopTextureID",
|
||||
"GetClipRectMin",
|
||||
"GetClipRectMax",
|
||||
"AddLine",
|
||||
"AddRect",
|
||||
"AddRectFilled",
|
||||
"AddRectFilledMultiColor",
|
||||
"AddQuad",
|
||||
"AddQuadFilled",
|
||||
"AddTriangle",
|
||||
"AddTriangleFilled",
|
||||
"AddCircle",
|
||||
"AddCircleFilled",
|
||||
"AddNgon",
|
||||
"AddNgonFilled",
|
||||
"AddEllipse",
|
||||
"AddEllipseFilled",
|
||||
"AddText",
|
||||
#"AddPolyline",
|
||||
#"AddConvexPolyFilled",
|
||||
"AddBezierCubic",
|
||||
"AddBezierQuadratic",
|
||||
#"AddImage",
|
||||
#"AddImageQuad",
|
||||
#"AddImageRounded",
|
||||
"PathClear",
|
||||
"PathLineTo",
|
||||
"PathLineToMergeDuplicate",
|
||||
"PathFillConvex",
|
||||
"PathStroke",
|
||||
"PathArcTo",
|
||||
"PathArcToFast",
|
||||
"PathEllipticalArcTo",
|
||||
"PathBezierCubicCurveTo",
|
||||
"PathBezierQuadraticCurveTo",
|
||||
"PathRect",
|
||||
"AddDrawCmd",
|
||||
"ChannelsSplit",
|
||||
"ChannelsMerge",
|
||||
"ChannelsSetCurrent",
|
||||
],
|
||||
}
|
||||
kLuaTypeChecker = {
|
||||
"ImDrawList": "lua_isudata(L, #, \"imgui4lua::@\")",
|
||||
"int": "LUA_TNUMBER == lua_type(L, #)",
|
||||
"const char *": "LUA_TSTRING == lua_type(L, #)",
|
||||
"$": "lua_isnone(L, #)",
|
||||
@ -200,7 +322,7 @@ kLuaTypeChecker = {
|
||||
# ---- ENTRYPOINT
|
||||
proc = subprocess.run(["clang++", "-x", "c++", "-std=c++2b", "-Xclang", "-ast-dump=json", "-fsyntax-only", sys.argv[1]], capture_output=True)
|
||||
if 0 == proc.returncode:
|
||||
walker = Walker()
|
||||
walker = RootWalker()
|
||||
walker.walk(json.loads(proc.stdout))
|
||||
walker.emit()
|
||||
else:
|
||||
|
2
thirdparty/CMakeLists.txt
vendored
2
thirdparty/CMakeLists.txt
vendored
@ -15,7 +15,7 @@ FetchContent_MakeAvailable(googletest)
|
||||
FetchContent_Declare(
|
||||
imgui
|
||||
GIT_REPOSITORY https://github.com/ocornut/imgui.git
|
||||
GIT_TAG v1.89.9
|
||||
GIT_TAG v1.90
|
||||
)
|
||||
FetchContent_Populate(imgui)
|
||||
include(imgui.cmake)
|
||||
|
Loading…
Reference in New Issue
Block a user