Compare commits
3 Commits
f33a2cf32b
...
6d36cc7669
Author | SHA1 | Date | |
---|---|---|---|
6d36cc7669 | |||
a4a419c73b | |||
c69e652716 |
@ -2,7 +2,9 @@ import json
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# ---- GENERATOR DEFINITIONS
|
# ---- GENERATOR ALGORITHM
|
||||||
|
|
||||||
|
# generate Lua to define enum constants
|
||||||
def gen_enum(item):
|
def gen_enum(item):
|
||||||
name = item.get("name", "")
|
name = item.get("name", "")
|
||||||
if name not in kEnumWhitelist:
|
if name not in kEnumWhitelist:
|
||||||
@ -20,71 +22,86 @@ def gen_enum(item):
|
|||||||
print(f"}} // {name}")
|
print(f"}} // {name}")
|
||||||
print()
|
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)
|
ovld_root = (0, {}, None)
|
||||||
for ovld in ovlds:
|
for ovld in ovlds:
|
||||||
ovld_node = ovld_root
|
ovld_node = ovld_root
|
||||||
idx = 0
|
|
||||||
for arg in ovld.get("inner", []):
|
for arg in ovld.get("inner", []):
|
||||||
if "ParmVarDecl" != arg.get("kind"): continue
|
if "ParmVarDecl" != arg.get("kind"): continue
|
||||||
tname = get_type_from_argument(arg)
|
tname = get_type_from_argument(arg)
|
||||||
if tname not in kLuaTypeChecker:
|
if tname not in kLuaTypeChecker:
|
||||||
break
|
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]
|
ovld_node = ovld_node[1][tname]
|
||||||
idx += 1
|
ovld_node[1]["$"] = (ovld_node[0], {}, ovld)
|
||||||
ovld_node[1]["$"] = (idx, {}, ovld)
|
|
||||||
|
|
||||||
def _output(ovld_node, depth=1):
|
def _output(ovld_node, depth=0):
|
||||||
indent = " "*depth
|
ind = indent + " "*depth
|
||||||
if 0 == len(ovld_node[1]):
|
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]):
|
elif 1 == len(ovld_node[1]):
|
||||||
_output(*ovld_node[1].values(), depth=depth)
|
_output(*ovld_node[1].values(), depth=depth)
|
||||||
else:
|
else:
|
||||||
for tname in ovld_node[1]:
|
for tname in ovld_node[1]:
|
||||||
checker = kLuaTypeChecker[tname].replace("#", str(ovld_node[0]+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)
|
_output(ovld_node[1][tname], depth=depth+1)
|
||||||
print(f"{indent}}}")
|
print(f"{ind}}}")
|
||||||
print(f"{indent}return luaL_error(L, \"unexpected type in param#{ovld_node[0]}\");")
|
print(f"{ind}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()
|
|
||||||
|
|
||||||
def gen_single_func(item, indent=""):
|
_output(ovld_root)
|
||||||
name = item["name"]
|
|
||||||
|
# generate Lua to call the function declared by the AST
|
||||||
|
def gen_func_call(name, item, indent="", ctxptr=None):
|
||||||
pops = []
|
pops = []
|
||||||
params = []
|
params = []
|
||||||
|
pushes = []
|
||||||
pcount = 0
|
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", []):
|
for arg in item.get("inner", []):
|
||||||
if "ParmVarDecl" != arg.get("kind"): continue
|
if "ParmVarDecl" != arg.get("kind"): continue
|
||||||
pop, param, push = gen_argument(pcount, arg)
|
pop, param, push = gen_func_argument(pcount, arg)
|
||||||
pcount += 1
|
pcount += 1
|
||||||
pops .extend(pop)
|
pops .extend(pop)
|
||||||
params.extend(param)
|
params.extend(param)
|
||||||
push .extend(push)
|
pushes.extend(push)
|
||||||
|
|
||||||
pushes = gen_return(item)
|
pushes = gen_func_return(item)
|
||||||
use_ret = 0 < len(pushes)
|
use_ret = 0 < len(pushes)
|
||||||
|
|
||||||
# text output
|
# text output
|
||||||
nl = "\n"+indent
|
nl = "\n"+indent
|
||||||
cm = ", "
|
cm = ", "
|
||||||
|
prefix = "ImGui::" if ctxptr is None else "p0->"
|
||||||
if 0 < len(pops):
|
if 0 < len(pops):
|
||||||
print(f"{indent}{nl.join(pops)}")
|
print(f"{indent}{nl.join(pops)}")
|
||||||
if use_ret:
|
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:
|
else:
|
||||||
print(f"{indent}ImGui::{name}({cm.join(params)});")
|
print(f"{indent}{prefix}{name}({cm.join(params)});")
|
||||||
if 0 < len(pushes):
|
if 0 < len(pushes):
|
||||||
print(f"{indent}{nl.join(pushes)}")
|
print(f"{indent}{nl.join(pushes)}")
|
||||||
print(f"{indent}return {len(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"]
|
ftype = item["type"]["qualType"]
|
||||||
type = ftype[0:ftype.find("(")-1]
|
type = ftype[0:ftype.find("(")-1]
|
||||||
|
|
||||||
@ -96,14 +113,19 @@ def gen_return(item):
|
|||||||
return ["lua_pushnumber(L, static_cast<lua_Number>(r));"]
|
return ["lua_pushnumber(L, static_cast<lua_Number>(r));"]
|
||||||
if "ImVec2" == type:
|
if "ImVec2" == type:
|
||||||
return [
|
return [
|
||||||
"lua_pushnumber(L, static_cast<lua_Number>(r.x));",
|
"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.y));",
|
||||||
]
|
]
|
||||||
|
|
||||||
print(f"unknown return type: {type}", file=sys.stderr)
|
print(f"unknown return type: {type}", file=sys.stderr)
|
||||||
return []
|
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"])
|
type = item["type"].get("desugaredQualType", item["type"]["qualType"])
|
||||||
n = pc+1
|
n = pc+1
|
||||||
pn = f"p{pc}"
|
pn = f"p{pc}"
|
||||||
@ -114,6 +136,9 @@ def gen_argument(pc, item):
|
|||||||
if "bool" == type:
|
if "bool" == type:
|
||||||
return ([f"const bool {pn} = lua_toboolean(L, {n});"], [pn], [])
|
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:
|
if "const char *" == type:
|
||||||
return ([f"const char* {pn} = luaL_checkstring(L, {n});"], [pn], [])
|
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)
|
print(f"unknown argument type: {type}", file=sys.stderr)
|
||||||
return ([], [], [])
|
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
|
# ---- GENERATOR UTILITIES
|
||||||
|
|
||||||
|
# get a variable type from the AST of an argument
|
||||||
def get_type_from_argument(item):
|
def get_type_from_argument(item):
|
||||||
return item["type"].get("desugaredQualType", item["type"]["qualType"])
|
return item["type"].get("desugaredQualType", item["type"]["qualType"])
|
||||||
|
|
||||||
|
|
||||||
# ---- WALKER DEFINITIONS
|
# ---- WALKER DEFINITIONS
|
||||||
class Walker:
|
class RootWalker:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._funcs = {}
|
self._funcs = {}
|
||||||
self._enums = {}
|
self._enums = {}
|
||||||
|
self._structs = {}
|
||||||
|
|
||||||
def emit(self):
|
def emit(self):
|
||||||
for x in self._enums: gen_enum(self._enums[x])
|
for x in self._structs: self._structs[x].emit()
|
||||||
for x in self._funcs: gen_func(x, self._funcs[x])
|
#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):
|
def walk(self, item):
|
||||||
kind = item.get("kind")
|
kind = item.get("kind")
|
||||||
@ -151,6 +193,9 @@ class Walker:
|
|||||||
if "EnumDecl" == kind:
|
if "EnumDecl" == kind:
|
||||||
if name in kEnumWhitelist:
|
if name in kEnumWhitelist:
|
||||||
self._enums[name] = item
|
self._enums[name] = item
|
||||||
|
elif "CXXRecordDecl" == kind:
|
||||||
|
if name in kStructWhitelist and "inner" in item:
|
||||||
|
self._structs[name] = StructWalker(kStructWhitelist[name], item)
|
||||||
else:
|
else:
|
||||||
w = self.walk
|
w = self.walk
|
||||||
if "NamespaceDecl" == kind:
|
if "NamespaceDecl" == kind:
|
||||||
@ -170,6 +215,29 @@ class Walker:
|
|||||||
else:
|
else:
|
||||||
self.walk(item)
|
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
|
# ---- DATA DEFINITIONS
|
||||||
kFuncWhitelist = [
|
kFuncWhitelist = [
|
||||||
@ -190,7 +258,61 @@ kFuncWhitelist = [
|
|||||||
kEnumWhitelist = [
|
kEnumWhitelist = [
|
||||||
"ImGuiWindowFlags_",
|
"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 = {
|
kLuaTypeChecker = {
|
||||||
|
"ImDrawList": "lua_isudata(L, #, \"imgui4lua::@\")",
|
||||||
"int": "LUA_TNUMBER == lua_type(L, #)",
|
"int": "LUA_TNUMBER == lua_type(L, #)",
|
||||||
"const char *": "LUA_TSTRING == lua_type(L, #)",
|
"const char *": "LUA_TSTRING == lua_type(L, #)",
|
||||||
"$": "lua_isnone(L, #)",
|
"$": "lua_isnone(L, #)",
|
||||||
@ -200,7 +322,7 @@ kLuaTypeChecker = {
|
|||||||
# ---- ENTRYPOINT
|
# ---- ENTRYPOINT
|
||||||
proc = subprocess.run(["clang++", "-x", "c++", "-std=c++2b", "-Xclang", "-ast-dump=json", "-fsyntax-only", sys.argv[1]], capture_output=True)
|
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:
|
if 0 == proc.returncode:
|
||||||
walker = Walker()
|
walker = RootWalker()
|
||||||
walker.walk(json.loads(proc.stdout))
|
walker.walk(json.loads(proc.stdout))
|
||||||
walker.emit()
|
walker.emit()
|
||||||
else:
|
else:
|
||||||
|
2
thirdparty/CMakeLists.txt
vendored
2
thirdparty/CMakeLists.txt
vendored
@ -15,7 +15,7 @@ FetchContent_MakeAvailable(googletest)
|
|||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
imgui
|
imgui
|
||||||
GIT_REPOSITORY https://github.com/ocornut/imgui.git
|
GIT_REPOSITORY https://github.com/ocornut/imgui.git
|
||||||
GIT_TAG v1.89.9
|
GIT_TAG v1.90
|
||||||
)
|
)
|
||||||
FetchContent_Populate(imgui)
|
FetchContent_Populate(imgui)
|
||||||
include(imgui.cmake)
|
include(imgui.cmake)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user