Added a Pure-Lua implementation for bindings generation.
The BindingsProcessor.lua script can be opened in ZeroBraneStudio and debugged from there, it invokes the entire ToLua++ processing. Also added docs-generation to the ToLua++ processor.
This commit is contained in:
parent
db65e11d57
commit
1a30c2262b
@ -1 +1 @@
|
||||
Subproject commit 44cd937e7687dadec80068116a2e1d683b139d06
|
||||
Subproject commit 5d69f56b06fdfe1e67cd2572083abb863ec3b708
|
1
src/Bindings/.gitignore
vendored
1
src/Bindings/.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
docs/
|
||||
lua51.dll
|
||||
LuaState_Declaration.inc
|
||||
LuaState_Implementation.cpp
|
||||
|
@ -3,6 +3,47 @@
|
||||
|
||||
-- Implements additional processing that is done while generating the Lua bindings
|
||||
|
||||
--[[
|
||||
The primary purpose of this file is to provide transformations for ToLua - it is loaded by ToLua++
|
||||
before processing the C++ code.
|
||||
|
||||
This file can also be used as a standalone Lua program to actually generate the bindings, it invokes
|
||||
ToLua++ if executed by a regular Lua interpreter
|
||||
|
||||
The transformations implemented:
|
||||
- Modify ToLua++ behavior so that it doesn't generate bindings for private and protected members
|
||||
- Export additional files to be included in cLuaState:
|
||||
- Forward declarations and typedefs for custom classes' pointers
|
||||
- Pushing and popping of bindings' classes
|
||||
--]]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Invokes the ToLua++ parser
|
||||
-- Called when this script detects it has been run outside of ToLua++'s processing
|
||||
local function invokeToLua()
|
||||
-- The values used by ToLua scripts, normally filled from the cmdline params:
|
||||
flags =
|
||||
{
|
||||
L = "BindingsProcessor.lua",
|
||||
o = "Bindings.cpp",
|
||||
H = "Bindings.h",
|
||||
f = "AllToLua.pkg",
|
||||
-- P = true, -- Prints the structure to stdout, doesn't generate cpp file
|
||||
}
|
||||
_extra_parameters = {}
|
||||
TOLUA_VERSION = "tolua++-1.0.92"
|
||||
TOLUA_LUA_VERSION = "Lua 5.1"
|
||||
|
||||
-- Path to the ToLua scripts
|
||||
path = "../../lib/tolua++/src/bin/lua/"
|
||||
|
||||
-- Run the ToLua processing:
|
||||
dofile(path .. "all.lua")
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
@ -163,6 +204,452 @@ end
|
||||
|
||||
|
||||
|
||||
local function FormatString(a_Str)
|
||||
local fmt = string.format("%q", a_Str)
|
||||
return (string.gsub(string.gsub(fmt, "\\\n", "\\n"), "\\\r\n", "\\r\\n"))
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local function OutputTable(a_File, a_Table, a_Name, a_Indent, a_Visited, a_Metas)
|
||||
-- Check and update the "visited" status:
|
||||
if (a_Visited[a_Table]) then
|
||||
a_File:write(a_Indent .. "{ \"visited: " .. a_Visited[a_Table] .. "\", }")
|
||||
return
|
||||
end
|
||||
a_Visited[a_Table] = a_Name
|
||||
|
||||
-- Output the table contents:
|
||||
a_File:write(a_Indent .. "{\n")
|
||||
local indent = a_Indent .. "\t"
|
||||
for k, v in pairs(a_Table) do
|
||||
if (type(k) == "string") then
|
||||
a_File:write(indent .. "[" .. FormatString(k) .. "] =")
|
||||
else
|
||||
a_File:write(indent .. "[" .. tostring(k) .. "] =")
|
||||
end
|
||||
local t = type(v)
|
||||
if (
|
||||
(t == "number") or
|
||||
(t == "boolean")
|
||||
) then
|
||||
a_File:write(" ", tostring(v))
|
||||
elseif (t == "string") then
|
||||
a_File:write(" ", FormatString(v))
|
||||
elseif (t == "table") then
|
||||
local metatab = getmetatable(v)
|
||||
if (metatab) then
|
||||
a_File:write(" -- meta: " .. tostring(metatab))
|
||||
a_Metas[metatab] = metatab
|
||||
end
|
||||
a_File:write("\n")
|
||||
OutputTable(a_File, v, a_Name .. "." .. tostring(k), indent, a_Visited, a_Metas)
|
||||
else
|
||||
print("Unhandled type: " .. t .. ": " .. tostring(v))
|
||||
a_File:write(" ", tostring(v))
|
||||
end
|
||||
a_File:write(",\n")
|
||||
end -- for k, v - a_Table
|
||||
a_File:write(a_Indent .. "}")
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Outputs the docs for all the functions in the specified class
|
||||
-- a_File is the output file
|
||||
-- a_Class is the ToLua's classClass object
|
||||
-- a_Functions is a dictionary of function descriptions: "name" -> { {<description>}, ...}
|
||||
local function outputClassFunctionDocs(a_File, a_Class, a_Functions)
|
||||
-- Sort the functions by name:
|
||||
local functions = {}
|
||||
for name, descs in pairs(a_Functions) do
|
||||
table.insert(functions, { Name = name, Descs = descs })
|
||||
end
|
||||
table.sort(functions,
|
||||
function (a_Fn1, a_Fn2)
|
||||
return (a_Fn1.Name < a_Fn2.Name)
|
||||
end
|
||||
)
|
||||
|
||||
-- If there are no functions, bail out:
|
||||
if not(functions[1]) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Output the descriptions:
|
||||
a_File:write("\t\tFunctions =\n\t\t{\n")
|
||||
for _, fn in ipairs(functions) do
|
||||
a_File:write("\t\t\t", fn.Name, " =\n\t\t\t{\n")
|
||||
for _, desc in ipairs(fn.Descs) do
|
||||
a_File:write("\t\t\t\t{\n\t\t\t\t\tParams =\n\t\t\t\t\t{\n")
|
||||
for _, param in ipairs(desc.Parameters) do
|
||||
a_File:write("\t\t\t\t\t\t{\n")
|
||||
a_File:write("\t\t\t\t\t\t\tType = \"", param.Type, "\",\n")
|
||||
a_File:write("\t\t\t\t\t\t\tName = \"", param.Name, "\",\n")
|
||||
a_File:write("\t\t\t\t\t\t},\n")
|
||||
end
|
||||
a_File:write("\t\t\t\t\t},\n\t\t\t\t\tReturns =\n\t\t\t\t\t{\n")
|
||||
for _, ret in ipairs(desc.Returns) do
|
||||
a_File:write("\t\t\t\t\t\t{\n\t\t\t\t\t\t\tType = \"", ret.Type, "\",\n\t\t\t\t\t\t},\n")
|
||||
end
|
||||
a_File:write("\t\t\t\t\t}\n\t\t\t\t\tDesc = ", string.format("%q", desc.DoxyComment or ""), ",\n")
|
||||
a_File:write("\t\t\t\t},\n")
|
||||
end
|
||||
a_File:write("\t\t\t},\n")
|
||||
end
|
||||
a_File:write("\t\t},\n")
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Outputs the docs for all the member variables in the specified class
|
||||
-- a_File is the output file
|
||||
-- a_Class is the ToLua's classClass object
|
||||
-- a_Variables is a dictionary of variable descriptions: "name" -> {<description>}
|
||||
local function outputClassVariableDocs(a_File, a_Class, a_Variables)
|
||||
-- Sort the variables by name:
|
||||
local variables = {}
|
||||
for name, desc in pairs(a_Variables) do
|
||||
table.insert(variables, { Name = name, Desc = desc })
|
||||
end
|
||||
table.sort(variables,
|
||||
function (a_Var1, a_Var2)
|
||||
return (a_Var1.Name < a_Var2.Name)
|
||||
end
|
||||
)
|
||||
|
||||
-- If there are no variables, bail out:
|
||||
if not(variables[1]) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Output the descriptions:
|
||||
a_File:write("\t\tVariables =\n\t\t{\n")
|
||||
for _, v in ipairs(variables) do
|
||||
a_File:write("\t\t\t", v.Name, " =\n\t\t\t{\n")
|
||||
a_File:write("\t\t\t\tType = \"", v.Desc.Type, "\",\n")
|
||||
a_File:write("\t\t\t\tDesc = [[", v.Desc.DoxyComment or "", "]],\n")
|
||||
a_File:write("\t\t\t},\n")
|
||||
end
|
||||
a_File:write("\t\t},\n")
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Outputs the docs for all the member constants in the specified class
|
||||
-- a_File is the output file
|
||||
-- a_Class is the ToLua's classClass object
|
||||
-- a_Constants is a dictionary of constant descriptions: "name" -> {<description>}
|
||||
-- a_IgnoredConstants is a dictionary of constants not to be exported: "name" -> true (used for ToLua++'s multi-inheritance)
|
||||
local function outputClassConstantDocs(a_File, a_Class, a_Constants, a_IgnoredConstants)
|
||||
-- Sort the constants by name:
|
||||
local constants = {}
|
||||
for name, desc in pairs(a_Constants) do
|
||||
if not(a_IgnoredConstants[name]) then
|
||||
table.insert(constants, { Name = name, Desc = desc })
|
||||
end
|
||||
end
|
||||
table.sort(constants,
|
||||
function (a_Var1, a_Var2)
|
||||
return (a_Var1.Name < a_Var2.Name)
|
||||
end
|
||||
)
|
||||
|
||||
-- If there are no constants, bail out:
|
||||
if not(constants[1]) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Output the descriptions:
|
||||
a_File:write("\t\tConstants =\n\t\t{\n")
|
||||
for _, con in ipairs(constants) do
|
||||
a_File:write("\t\t\t", con.Name, " =\n\t\t\t{\n")
|
||||
a_File:write("\t\t\t\tType = \"", con.Desc.Type, "\",\n")
|
||||
a_File:write("\t\t\t\tDesc = [[", con.Desc.DoxyComment or "", "]],\n")
|
||||
a_File:write("\t\t\t},\n")
|
||||
end
|
||||
a_File:write("\t\t},\n")
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Outputs the docs for all the member enums in the specified class
|
||||
-- a_File is the output file
|
||||
-- a_Class is the ToLua's classClass object
|
||||
-- a_Enums is an array of ToLua's classEnum objects
|
||||
local function outputClassEnumDocs(a_File, a_Class, a_Enums)
|
||||
-- If there are no enums, bail out:
|
||||
if (not(a_Enums) or not(a_Enums[1])) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Sort the enums by name:
|
||||
table.sort(a_Enums,
|
||||
function (a_Enum1, a_Enum2)
|
||||
return (a_Enum1.name < a_Enum2.name)
|
||||
end
|
||||
)
|
||||
|
||||
-- Output the enums:
|
||||
a_File:write("\t\tEnums =\n\t\t{\n")
|
||||
for i, enum in ipairs(a_Enums) do
|
||||
local name = enum.name
|
||||
if (not(name) or (name == "")) then
|
||||
name = string.format("unnamedEnum_%d", i)
|
||||
end
|
||||
a_File:write("\t\t\t", name, " =\n\t\t\t{\n")
|
||||
local valnames = {}
|
||||
local idx = 1
|
||||
for _, valname in ipairs(enum.lnames) do
|
||||
valnames[idx] = valname
|
||||
idx = idx + 1
|
||||
end
|
||||
table.sort(valnames)
|
||||
for _, valname in ipairs(valnames) do
|
||||
a_File:write("\t\t\t\t\"", valname, "\",\n")
|
||||
end
|
||||
a_File:write("\t\t\t},\n")
|
||||
end
|
||||
a_File:write("\t\t},\n")
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Outputs the docs for the specified class, which has been parsed for its functions, variables and constants
|
||||
-- a_Class is the ToLua's classClass object
|
||||
-- a_Functions is a dictionary of function descriptions: "name" -> { {<description>}, ...}
|
||||
-- a_Variables is a dictionary of variable descriptions: "name" -> {<description>}
|
||||
-- a_Constants is a dictionary of constant descriptions: "name" -> {<description>}
|
||||
-- a_Filenames is an array into which the name of the docs file is to be appended
|
||||
local function outputClassDocs(a_Class, a_Functions, a_Variables, a_Constants, a_Filenames)
|
||||
-- Add the output file to list of filenames:
|
||||
local fnam = a_Class.lname .. ".lua"
|
||||
table.insert(a_Filenames, fnam)
|
||||
|
||||
-- Output the header:
|
||||
local f = assert(io.open("docs/" .. fnam, "w"))
|
||||
f:write("return\n{\n\t", a_Class.lname, " =\n\t{\n")
|
||||
f:write("\t\tDesc = [[", a_Class.doxycomment or "", "]],\n")
|
||||
|
||||
-- If the class inherits from anything, output it here:
|
||||
local ignoredConstants = {}
|
||||
if (a_Class.base and (a_Class.base ~= "")) then
|
||||
local bases = {a_Class.base}
|
||||
local idx = 2
|
||||
for _, b in ipairs(a_Class.extra_bases or {}) do
|
||||
bases[idx] = b
|
||||
idx = idx + 1
|
||||
-- ToLua++ handles multiple inheritance by adding "constants" for the base types; ignore those:
|
||||
ignoredConstants["__" .. b .. "__"] = true
|
||||
end
|
||||
table.sort(bases)
|
||||
f:write("\t\tInherits =\n\t\t{\n")
|
||||
for _, b in ipairs(bases) do
|
||||
f:write("\t\t\t", string.format("%q", b), ",\n")
|
||||
end
|
||||
f:write("\t\t},\n")
|
||||
end
|
||||
|
||||
-- Output the functions:
|
||||
outputClassFunctionDocs(f, a_Class, a_Functions)
|
||||
|
||||
-- Output the variables:
|
||||
outputClassVariableDocs(f, a_Class, a_Variables)
|
||||
|
||||
-- Output the constants:
|
||||
outputClassConstantDocs(f, a_Class, a_Constants, ignoredConstants)
|
||||
|
||||
-- Output the enums:
|
||||
outputClassEnumDocs(f, a_Class, a_Class.enums)
|
||||
|
||||
-- Output the footer:
|
||||
f:write("\t}\n}\n")
|
||||
f:close()
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local function genPackageDocs(a_Self)
|
||||
-- Generate docs for each member:
|
||||
local i = 1
|
||||
local filenames = {}
|
||||
while (a_Self[i]) do
|
||||
if (a_Self[i].genDocs) then
|
||||
a_Self[i]:genDocs(filenames)
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
-- Output the globals' docs:
|
||||
local functions = {}
|
||||
local variables = {}
|
||||
local constants = {}
|
||||
while (a_Self[i]) do
|
||||
if (a_Self[i].genMemberDocs) then
|
||||
a_Self[i]:genMemberDocs(functions, variables, constants)
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
local oldName = a_Self.lname
|
||||
a_Self.lname = "Globals"
|
||||
outputClassDocs(a_Self, functions, variables, constants, filenames)
|
||||
a_Self.lname = oldName
|
||||
|
||||
-- Output the list of docs files:
|
||||
table.sort(filenames)
|
||||
local f = assert(io.open("docs/_files.lua", "w"))
|
||||
f:write("return\n{\n")
|
||||
for _, fnam in ipairs(filenames) do
|
||||
f:write("\t\"", fnam, "\",\n")
|
||||
end
|
||||
f:write("}\n")
|
||||
f:close()
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local function genClassDocs(a_Self, a_Filenames)
|
||||
assert(a_Self.lname)
|
||||
assert(type(a_Filenames) == "table")
|
||||
--[[
|
||||
print("\n\nGenerating docs for class " .. a_Self.lname)
|
||||
local visited = {[a_Self.parent] = "<package>"}
|
||||
local metas = {}
|
||||
OutputTable(io.stdout, a_Self, a_Self.lname, "", visited, metas)
|
||||
--]]
|
||||
|
||||
-- Collect the function, variable and constant docs:
|
||||
local i = 1
|
||||
local functions = {}
|
||||
local variables = {}
|
||||
local constants = {}
|
||||
while (a_Self[i]) do
|
||||
if (a_Self[i].genMemberDocs) then
|
||||
a_Self[i]:genMemberDocs(functions, variables, constants)
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
-- Output the individual docs
|
||||
outputClassDocs(a_Self, functions, variables, constants, a_Filenames)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Parses the specified function's parameters and returns their description as a table
|
||||
-- a_Function is the ToLua's classFunction object
|
||||
local function parseFunctionParameters(a_Function)
|
||||
-- If the only param is a "void", then report no params:
|
||||
if (
|
||||
a_Function.args and -- The params are present
|
||||
(#(a_Function.args) == 1) and -- There is exactly one param
|
||||
(a_Function.args[1].type == "void") -- The param is a void
|
||||
) then
|
||||
return {}
|
||||
end
|
||||
|
||||
local res = {}
|
||||
local idx = 1
|
||||
for _, param in ipairs(a_Function.args or {}) do
|
||||
local t = param.type
|
||||
t = t:gsub("^const ", "") -- Remove the "const" keyword, if present
|
||||
res[idx] =
|
||||
{
|
||||
Name = param.name,
|
||||
Type = t,
|
||||
IsConst = (param.type:match("^const ") ~= nil),
|
||||
}
|
||||
idx = idx + 1
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Parses the specified function's return values and returns their description as a table
|
||||
-- a_Function is the ToLua's classFunction object
|
||||
local function parseFunctionReturns(a_Function)
|
||||
local res = {}
|
||||
local idx = 1
|
||||
if (a_Function.type and (a_Function.type ~= "void")) then
|
||||
res[idx] = { Type = a_Function.type }
|
||||
idx = idx + 1
|
||||
end
|
||||
for _, param in ipairs(a_Function.args or {}) do
|
||||
if ((param.mod == "&") or (param.ret == "&")) then
|
||||
res[idx] = { Type = param.type:gsub("^const ", "") }
|
||||
idx = idx + 1
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local function genFunctionMemberDocs(a_Self, a_Functions, a_Variables, a_Constants)
|
||||
assert(a_Self.lname)
|
||||
|
||||
local fn = a_Functions[a_Self.lname] or {}
|
||||
a_Functions[a_Self.lname] = fn
|
||||
|
||||
local desc =
|
||||
{
|
||||
LuaName = a_Self.lname,
|
||||
CType = a_Self.type,
|
||||
DoxyComment = a_Self.DoxyComment,
|
||||
Parameters = parseFunctionParameters(a_Self),
|
||||
Returns = parseFunctionReturns(a_Self),
|
||||
}
|
||||
table.insert(fn, desc)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local function genVariableMemberDocs(a_Self, a_Functions, a_Variables, a_Constants)
|
||||
assert(a_Self.lname)
|
||||
|
||||
local desc =
|
||||
{
|
||||
Name = a_Self.lname,
|
||||
Type = a_Self.type,
|
||||
DoxyComment = a_Self.DoxyComment,
|
||||
}
|
||||
|
||||
if (a_Self.csetname) then
|
||||
a_Variables[a_Self.lname] = desc
|
||||
else
|
||||
a_Constants[a_Self.lname] = desc
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function pre_output_hook(a_Package)
|
||||
OutputLuaStateHelpers(a_Package)
|
||||
end
|
||||
@ -171,8 +658,26 @@ end
|
||||
|
||||
|
||||
|
||||
function post_output_hook()
|
||||
print("Bindings have been generated.")
|
||||
function post_output_hook(a_Package)
|
||||
-- Generate the documentation:
|
||||
classPackage.genDocs = genPackageDocs
|
||||
classClass.genDocs = genClassDocs
|
||||
classFunction.genMemberDocs = genFunctionMemberDocs
|
||||
classVariable.genMemberDocs = genVariableMemberDocs
|
||||
a_Package:genDocs()
|
||||
|
||||
print("Lua bindings and docs have been generated.")
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if not(TOLUA_VERSION) then
|
||||
-- BindingsProcessor has been called standalone, invoke the entire ToLua++ machinery:
|
||||
print("Generating Lua bindings and docs...")
|
||||
invokeToLua()
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
|
@ -378,3 +378,6 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||
endif()
|
||||
|
||||
target_link_libraries(${EXECUTABLE} luaexpat jsoncpp_lib_static mbedtls zlib sqlite lua SQLiteCpp event_core event_extra)
|
||||
|
||||
# Create a folder for Bindings' documentation:
|
||||
FILE(MAKE_DIRECTORY "Bindings/docs")
|
||||
|
Loading…
Reference in New Issue
Block a user