summaryrefslogtreecommitdiffstats
path: root/src/Bindings
diff options
context:
space:
mode:
Diffstat (limited to 'src/Bindings')
-rw-r--r--src/Bindings/.gitignore1
-rw-r--r--src/Bindings/BindingsProcessor.lua843
-rw-r--r--src/Bindings/CMakeLists.txt28
-rw-r--r--src/Bindings/DiffAPIDesc.lua550
-rw-r--r--src/Bindings/ManualBindings.cpp32
5 files changed, 1429 insertions, 25 deletions
diff --git a/src/Bindings/.gitignore b/src/Bindings/.gitignore
index 711ae9c3a..16f9db55d 100644
--- a/src/Bindings/.gitignore
+++ b/src/Bindings/.gitignore
@@ -1,3 +1,4 @@
+docs/
lua51.dll
LuaState_Declaration.inc
LuaState_Implementation.cpp
diff --git a/src/Bindings/BindingsProcessor.lua b/src/Bindings/BindingsProcessor.lua
index e7b909ded..28a6a053c 100644
--- a/src/Bindings/BindingsProcessor.lua
+++ b/src/Bindings/BindingsProcessor.lua
@@ -3,11 +3,68 @@
-- 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
+
+To parse DoxyComments, the preprocessor first replaces them with markers and then the parser uses
+those markers to apply the DoxyComment to the next or previous item in the container, based on
+the DoxyComment type.
+
+Placeholders in use (i = internal ToLua++):
+ - \1 and \2: (i) Embedded Lua code
+ - \3 and \4: (i) Embedded C code ("<>")
+ - \5 and \6: (i) Embedded C code ("{}")
+ - \17 and \18: DoxyComment for next item ("/** ... */") via an ID lookup
+ - \19 and \20: DocyComment for previous item ("///< ... \n") via an ID lookup
+--]]
+
+
+
+
+
+--- 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
+
-local access = {public = 0, protected = 1, private = 2}
+local access =
+{
+ public = 0,
+ protected = 1,
+ private = 2
+}
@@ -24,20 +81,73 @@ local g_HasCustomPushImplementation =
-function parser_hook(s)
- local container = classContainer.curr -- get the current container
+--- Array-table of forward DoxyComments that are replaced in preprocess_hook() and substituted back in parser_hook()
+-- We need to use a lookup table because the comments themselves may contain "//" which the preprocessor
+-- would eliminate, thus breaking the code
+-- The "n" member is a counter for faster insertion
+local g_ForwardDoxyComments =
+{
+ n = 0
+}
+
+
+--- Array-table of backward DoxyComments that are replaced in preprocess_hook() and substituted back in parser_hook()
+-- We need to use a lookup table because the comments themselves may contain "//" which the preprocessor
+-- would eliminate, thus breaking the code
+-- The "n" member is a counter for faster insertion
+local g_BackwardDoxyComments =
+{
+ n = 0,
+}
+
+
+
+
- -- process access-specifying labels (public, private, etc)
+--- Provides extra parsing in addition to ToLua++'s own
+-- Called by ToLua++ each time it extracts the next piece of code to parse
+-- a_Code is the string representing the code to be parsed
+-- The return value is the remaining code to be parsed in the next iteration
+-- Processes the class access specifiers (public, protected, private), and doxycomments
+function parser_hook(a_Code)
+ -- Process access-specifying labels (public, private, etc)
do
- local b, e, label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type'
+ local b, e, label = string.find(a_Code, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type'
if b then
-
- -- found a label, get the new access value from the global 'access' table
+ -- Found a label, get the new access value for it:
if access[label] then
- container.curr_member_access = access[label]
+ classContainer.curr.curr_member_access = access[label]
end -- else ?
-
- return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:]
+ return strsub(a_Code, e) -- normally we would use 'e + 1', but we need to preserve the [^:]
+ end
+ end
+
+ -- Process forward DoxyComments:
+ do
+ local b, e, comment = a_Code:find("^%s*(%b\17\18)")
+ if (b) then
+ local curr = classContainer.curr
+ if (curr.n and (curr.n > 0)) then
+ curr[curr.n].next_DoxyComment = g_ForwardDoxyComments[tonumber(comment:sub(2, -2))]
+ else
+ curr.first_child_DoxyComment = g_ForwardDoxyComments[tonumber(comment:sub(2, -2))]
+ end
+ return strsub(a_Code, e + 1)
+ end
+ end
+
+ -- Process backward DoxyComments:
+ do
+ local b, e, comment = a_Code:find("^%s*(%b\19\20)")
+ if (b) then
+ comment = g_BackwardDoxyComments[tonumber(comment:sub(2, -2))]
+ local currContainer = classContainer.curr
+ if (currContainer.n > 0) then
+ currContainer[currContainer.n].DoxyComment = comment
+ else
+ print("Backward DoxyComment lost in " .. (currContainer.name or currContainer.lname or currContainer.cname or "<no name>"))
+ end
+ return strsub(a_Code, e + 1)
end
end
end
@@ -163,16 +273,725 @@ 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
+ local name = fn.Name
+ if (name:sub(1, 1) == ".") then
+ name = "[\"" .. name .. "\"]"
+ end
+ a_File:write("\t\t\t", 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")
+ if (desc.IsStatic) then
+ a_File:write("\t\t\t\t\tIsStatic = true,\n")
+ end
+ if (desc.DoxyComment) then
+ a_File:write("\t\t\t\t\tDesc = ", string.format("%q", desc.DoxyComment), ",\n")
+ end
+ 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")
+ if (v.Desc.DoxyComment) then
+ a_File:write("\t\t\t\tDesc = ", string.format("%q", v.Desc.DoxyComment), ",\n")
+ end
+ 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")
+ if (con.Desc.DoxyComment) then
+ a_File:write("\t\t\t\tDesc = ", string.format("%q", con.Desc.DoxyComment), ",\n")
+ end
+ 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 = {}
+ -- Make a copy of enum.lnames so that we can sort it:
+ local idx = 1
+ for i, valname in ipairs(enum.lnames) do
+ valnames[idx] = { Name = valname, DoxyComment = enum.DoxyComments[i] }
+ idx = idx + 1
+ end
+ table.sort(valnames,
+ function (a_Val1, a_Val2)
+ return (a_Val1.Name < a_Val2.Name)
+ end
+ )
+ for _, valname in ipairs(valnames) do
+ assert(not(valname.Name:find("\17")))
+ assert(not(valname.Name:find("\18")))
+ assert(not(valname.Name:find("\19")))
+ assert(not(valname.Name:find("\20")))
+ a_File:write("\t\t\t\t{\n")
+ a_File:write("\t\t\t\t\tName = \"", valname.Name, "\",\n")
+ if (valname.DoxyComment) then
+ a_File:write("\t\t\t\t\tDesc = ", string.format("%q", valname.DoxyComment), ",\n")
+ end
+ 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 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")
+ if (a_Class.DoxyComment) then
+ f:write("\t\tDesc = ", string.format("%q", a_Class.DoxyComment), ",\n")
+ end
+
+ -- 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
+
+
+
+
+
+--- Recursively applies the next_DoxyComment member to the next item, and first_child_DoxyComment to first child item in the container.
+-- a_Container is the ToLua++'s table potentially containing children as its array members
+local function applyNextDoxyComments(a_Container)
+ -- Apply the DoxyComment to the first child, if appropriate:
+ if (a_Container[1] and a_Container.first_child_DoxyComment) then
+ a_Container[1].DoxyComment = a_Container.first_child_DoxyComment
+ end
+
+ -- Apply each child's next_DoxyComment to the actual next child:
+ local i = 1
+ while (a_Container[i]) do
+ if (a_Container[i].next_DoxyComment) then
+ if (a_Container[i + 1]) then
+ a_Container[i + 1].DoxyComment = a_Container[i].next_DoxyComment
+ end
+ end
+ applyNextDoxyComments(a_Container[i])
+ i = i + 1
+ end
+end
+
+
+
+
+
+--- Generates documentation for a package.
+local function genPackageDocs(a_Self)
+ -- DEBUG: Output the raw package object:
+ do
+ local f = io.open("docs/_raw.lua", "w")
+ if (f) then
+ OutputTable(f, a_Self, "", "", {}, {})
+ f:close()
+ end
+ end
+
+ applyNextDoxyComments(a_Self)
+
+ -- Generate docs for each member:
+ local i = 1
+ local filenames = {}
+ while (a_Self[i]) do
+ if (
+ a_Self[i]:check_public_access() and -- Do not export private and protected members
+ 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]:check_public_access() and -- Don't export private and protected members
+ 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),
+ }
+ local _, _, hasStatic = string.find(a_Self.mod, "^%s*(static)")
+ if (hasStatic) then
+ desc.IsStatic = true
+ end
+ 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 (string.find(a_Self.type,'const%s+') or string.find(a_Self.mod, 'tolua_readonly') or string.find(a_Self.mod, 'tolua_inherits')) then
+ a_Constants[a_Self.lname] = desc
+ else
+ a_Variables[a_Self.lname] = desc
+ end
+end
+
+
+
+
+
+--- Generates the entire documentation for the API
+-- a_Package is ToLua++'s classPackage object
+-- Checks if the documentation folder is writable, if not, skips the docs generation
+-- Returns true if documentation was generated, false and message if not
+local function generateDocs(a_Package)
+ -- Check if the docs folder is writable:
+ local f, msg = io.open("docs/_files.lua", "w")
+ if not(f) then
+ return false, "Cannot access the docs folder: " .. msg
+ end
+ f:close()
+
+ -- Generate the docs:
+ classPackage.genDocs = genPackageDocs
+ classClass.genDocs = genClassDocs
+ classFunction.genMemberDocs = genFunctionMemberDocs
+ classVariable.genMemberDocs = genVariableMemberDocs
+ a_Package:genDocs()
+ return true
+end
+
+
+
+
+
+--- Outputs the cLuaState helper files.
+-- Called by ToLua++ before it starts outputting its generated files.
+-- a_Package is ToLua++'s classPackage object
function pre_output_hook(a_Package)
OutputLuaStateHelpers(a_Package)
+
+ -- Generate the documentation:
+ -- (must generate documentation before ToLua++ writes the bindings, because "static" information is lost at that point)
+ local isSuccess, msg = generateDocs(a_Package)
+ if not(isSuccess) then
+ print("API docs haven't been generated due to an error: " .. (msg or "<no message>"))
+ else
+ print("API docs have been generated.");
+ end
+end
+
+
+
+
+
+--- Outputs the documentation files.
+-- Called by ToLua++ after writing its generated files.
+-- a_Package is ToLua++'s classPackage object
+function post_output_hook(a_Package)
+ print("Lua bindings have been generated.")
+end
+
+
+
+
+
+--- Provides DoxyComment processing while parsing the C++ code.
+-- Called by ToLua++ parser before it starts parsing the code.
+-- a_Package is the ToLua++'s classPackage object, currently unparsed
+-- The C++ code to be parsed is in a_Packade.code
+function preprocess_hook(a_Package)
+ assert(a_Package)
+ assert(type(a_Package.code) == "string")
+
+ -- Replace all DoxyComments with placeholders so that they aren't erased later on:
+ a_Package.code = a_Package.code
+ :gsub("/%*%*%s*(.-)%s*%*/",
+ function (a_Comment)
+ local n = g_ForwardDoxyComments.n + 1
+ g_ForwardDoxyComments[n] = a_Comment
+ g_ForwardDoxyComments.n = n
+ return "\17" .. n .."\18"
+ end
+ ) -- Replace /** ... */ with an ID into a lookup table wrapped in DC1 and DC2
+ :gsub("///<%s*(.-)%s*\n%s*",
+ function (a_Comment)
+ local n = g_BackwardDoxyComments.n + 1
+ g_BackwardDoxyComments[n] = a_Comment
+ g_BackwardDoxyComments.n = n
+ return "\19" .. n .."\20\n"
+ end
+ ) -- Replace ///< comments with an ID into a lookup table wrapped in DC3 and DC4
+ local f = io.open("code_out.cpp", "wb")
+ f:write(a_Package.code)
+ f:close()
+end
+
+
+
+
+
+--- Chooses the smaller of the indices, and the number indicating whether it chose the first or the second
+-- If one of the indices is nil, returns the other one
+-- If both indices are nil, returns nil
+local function chooseNextIndex(a_Index1, a_Index2)
+ if not(a_Index1) then
+ return a_Index2, 2
+ end
+ if not(a_Index2) then
+ return a_Index1, 1
+ end
+ if (a_Index1 > a_Index2) then
+ return a_Index2, 2
+ else
+ return a_Index1, 1
+ end
+end
+
+
+
+
+
+--- Override for ToLua++'s own code extraction
+-- Called for each "$cfile" and "$hfile" directive in the package file
+-- a_FileName is the C++ header filename
+-- a_Contents is the code contents of the header file
+-- The function returns the code to be parsed by ToLua++
+-- In addition to the original function, this override extracts all DoxyComments as well
+-- This is needed when a function is marked with "// tolua_export" but its DoxyComment is not included
+function extract_code(a_FileName, a_Contents)
+ local code = '\n$#include "' .. a_FileName .. '"\n'
+ a_Contents= "\n" .. a_Contents .. "\n" -- add blank lines as sentinels
+ local _, e, c, t = strfind(a_Contents, "\n([^\n]-)[Tt][Oo][Ll][Uu][Aa]_([^%s]*)[^\n]*\n")
+ local dcb, dce, dc = strfind(a_Contents, "/%*%*.-%*/")
+ local nextEnd, whichOne = chooseNextIndex(e, dce)
+ while (nextEnd) do
+ if (whichOne == 2) then
+ code = code .. a_Contents:sub(dcb, dce) .. "\n"
+ else
+ t = strlower(t)
+ if (t == "begin") then
+ _, nextEnd, c = strfind(a_Contents,"(.-)\n[^\n]*[Tt][Oo][Ll][Uu][Aa]_[Ee][Nn][Dd][^\n]*\n", e)
+ if not(nextEnd) then
+ tolua_error("Unbalanced 'tolua_begin' directive in header file " .. a_FileName)
+ end
+ end
+ code = code .. c .. "\n"
+ end
+ _, e, c, t = strfind(a_Contents, "\n([^\n]-)[Tt][Oo][Ll][Uu][Aa]_([^%s]*)[^\n]*\n", nextEnd)
+ dcb, dce, dc = strfind(a_Contents, "/%*%*.-%*/", nextEnd)
+ nextEnd, whichOne = chooseNextIndex(e, dce)
+ end
+ return code
+end
+
+
+
+
+
+--- Installs a hook that is called by ToLua++ for each instantiation of classEnumerate
+-- The hook is used to fix DoxyComments in enums
+local function installEnumHook()
+ local oldEnumerate = Enumerate
+ Enumerate = function (a_Name, a_Body, a_VarName)
+ -- We need to remove the DoxyComment items from the enum
+ -- otherwise ToLua++ parser would make an enum value out of them
+ a_Body = string.gsub(a_Body, ",[%s\n]*}", "\n}") -- eliminate last ','
+ local t = split(strsub(a_Body, 2, -2), ',') -- eliminate braces
+ local doxyComments = {}
+ local enumValues = {}
+ local numEnumValues = 0
+ for _, txt in ipairs(t) do
+ txt = txt:gsub("(%b\17\18)",
+ function (a_CommentID)
+ doxyComments[numEnumValues + 1] = g_ForwardDoxyComments[tonumber(a_CommentID:sub(2, -2))]
+ return ""
+ end
+ ):gsub("(%b\19\20)",
+ function (a_CommentID)
+ doxyComments[numEnumValues] = g_BackwardDoxyComments[tonumber(a_CommentID:sub(2, -2))]
+ return ""
+ end
+ )
+ if (txt ~= "") then
+ numEnumValues = numEnumValues + 1
+ enumValues[numEnumValues] = txt
+ end
+ end
+ local res = oldEnumerate(a_Name, "{" .. table.concat(enumValues, ",") .. "}", a_VarName)
+ res.DoxyComments = doxyComments
+ return res
+ end
end
-function post_output_hook()
- print("Bindings have been generated.")
+if not(TOLUA_VERSION) then
+ -- BindingsProcessor has been called standalone, invoke the entire ToLua++ machinery:
+ print("Generating Lua bindings and docs...")
+ invokeToLua()
+ return
+else
+ -- We're being executed from inside the ToLua++ parser. Install the needed hooks:
+ installEnumHook()
end
diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt
index 5c79225cc..23ba4c7e6 100644
--- a/src/Bindings/CMakeLists.txt
+++ b/src/Bindings/CMakeLists.txt
@@ -1,4 +1,3 @@
-cmake_minimum_required (VERSION 2.6)
project (Cuberite)
include_directories ("${PROJECT_SOURCE_DIR}/../")
@@ -56,7 +55,6 @@ set (BINDING_OUTPUTS
)
set(BINDING_DEPENDENCIES
- tolua
../Bindings/AllToLua.pkg
../Bindings/BindingsProcessor.lua
../Bindings/LuaFunctions.h
@@ -139,17 +137,21 @@ set(BINDING_DEPENDENCIES
)
if (NOT MSVC)
- ADD_CUSTOM_COMMAND(
- # add any new generated bindings here
- OUTPUT ${BINDING_OUTPUTS}
-
- # Regenerate bindings:
- COMMAND tolua -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
-
- # add any new generation dependencies here
- DEPENDS ${BINDING_DEPENDENCIES}
- )
+ if (HAS_LUA_INTERPRETER)
+ ADD_CUSTOM_COMMAND(
+ OUTPUT ${BINDING_OUTPUTS}
+ COMMAND lua BindingsProcessor.lua
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ DEPENDS ${BINDING_DEPENDENCIES}
+ )
+ else()
+ ADD_CUSTOM_COMMAND(
+ OUTPUT ${BINDING_OUTPUTS}
+ COMMAND tolua -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ DEPENDS ${BINDING_DEPENDENCIES} tolua
+ )
+ endif()
endif ()
set_source_files_properties(${BINDING_OUTPUTS} PROPERTIES GENERATED TRUE)
diff --git a/src/Bindings/DiffAPIDesc.lua b/src/Bindings/DiffAPIDesc.lua
new file mode 100644
index 000000000..8b8c340e2
--- /dev/null
+++ b/src/Bindings/DiffAPIDesc.lua
@@ -0,0 +1,550 @@
+-- DiffAPIDesc.lua
+
+-- Creates a diff file containing documentation that is available from ToLua++'s doxycomment parsing, but not yet included in APIDesc.lua
+
+require("lfs")
+
+
+
+
+
+--- Translation for function names whose representation in APIDesc is different from the one in Docs
+-- Dictionary of "DocsName" -> "DescName"
+local g_FunctionNameDocsToDesc =
+{
+ ["new"] = "constructor",
+ ["delete"] = "destructor",
+ [".add"] = "operator_plus",
+ [".div"] = "operator_div",
+ [".eq"] = "operator_eq",
+ [".mul"] = "operator_mul",
+ [".sub"] = "operator_sub",
+}
+
+
+
+
+
+--- Translation from C types to Lua types
+-- Dictionary of "CType" -> "LuaType"
+local g_CTypeToLuaType =
+{
+ AString = "string",
+ bool = "boolean",
+ Byte = "number",
+ char = "number",
+ double = "number",
+ float = "number",
+ ForEachChunkProvider = "cWorld",
+ int = "number",
+ size_t = "number",
+ unsigned = "number",
+ ["const AString"] = "string",
+ ["const char*"] = "string",
+ ["std::string"] = "string",
+ ["Vector3<int>"] = "Vector3i",
+ ["Vector3<float>"] = "Vector3f",
+ ["Vector3<double>"] = "Vector3d",
+}
+
+
+
+
+
+--- Functions that should be ignored
+-- Dictionary of "FunctionName" -> true for each ignored function
+local g_IgnoreFunction =
+{
+ destructor = true,
+}
+
+
+
+
+
+local function caseInsensitiveCompare(a_Text1, a_Text2)
+ return (a_Text1:lower() < a_Text2:lower())
+end
+
+
+
+
+
+--- Loads the APIDesc.lua and its child files, returns the complete description
+-- Returns a table with Classes and Hooks members, Classes being a dictionary of "ClassName" -> { desc }
+local function loadAPIDesc()
+ -- Load the main APIDesc.lua file:
+ local apiDescPath = "../../Server/Plugins/APIDump/"
+ local desc = dofile(apiDescPath .. "APIDesc.lua")
+ if not(desc) then
+ error("Failed to load APIDesc")
+ end
+
+ -- Merge in the information from all files in the Classes subfolder:
+ local classesPath = apiDescPath .. "Classes/"
+ for fnam in lfs.dir(apiDescPath .. "Classes") do
+ if (string.find(fnam, ".*%.lua$")) then
+ local tbls = dofile(classesPath .. fnam)
+ for k, cls in pairs(tbls) do
+ desc.Classes[k] = cls;
+ end
+ end
+ end
+ return desc
+end
+
+
+
+
+
+--- Loads the API documentation generated by ToLua++'s parser
+-- Returns a dictionary of "ClassName" -> { docs }
+local function loadAPIDocs()
+ -- Get the filelist:
+ local files = dofile("docs/_files.lua")
+ if not(files) then
+ error("Failed to load _files.lua from docs")
+ end
+
+ -- Load the docs from all files, merge into a single dictionary:
+ local res = {}
+ for _, fnam in ipairs(files) do
+ local docs = dofile("docs/" .. fnam)
+ if (docs) then
+ for k, v in pairs(docs) do
+ assert(not(res[k])) -- Do we have a duplicate documentation entry?
+ res[k] = v
+ end
+ end
+ end
+ return res
+end
+
+
+
+
+
+--- Returns whether the function signature in the description matches the function documentation
+-- a_FunctionDesc is a single description for a function, as loaded from APIDesc.lua (one <FnDesc> item)
+-- a_FunctionDoc is a single documentation item for a function, as loaded from ToLua++'s parser
+local function functionDescMatchesDocs(a_FunctionDesc, a_FunctionDoc)
+ -- Check the number of parameters:
+ local numParams
+ local numOptionalParams = 0
+ if (not(a_FunctionDesc.Params) or (a_FunctionDesc.Params == "")) then
+ numParams = 0
+ else
+ _, numParams = string.gsub(a_FunctionDesc.Params, ",", "")
+ numParams = numParams + 1
+ _, numOptionalParams = string.gsub(a_FunctionDesc.Params, "%b[]", "")
+ end
+ local numDocParams = #(a_FunctionDoc.Params)
+ if ((numDocParams > numParams) or (numDocParams < numParams - numOptionalParams)) then
+ return false
+ end
+
+ return true
+end
+
+
+
+
+
+--- Returns an array of function descriptions that are in a_FunctionDocs but are missing from a_FunctionDescs
+-- a_FunctionDescs is an array of function descriptions, as loaded from APIDesc.lua (normalized into array)
+-- a_FunctionDocs is an array of function documentation items, as loaded from ToLua++'s parser
+-- If all descriptions match, nil is returned instead
+local function listMissingClassSingleFunctionDescs(a_FunctionDescs, a_FunctionDocs)
+ -- For each documentation item, try to find a match in a_FunctionDescs:
+ local res = {}
+ for _, docs in ipairs(a_FunctionDocs) do
+ local hasFound = false
+ for _, desc in ipairs(a_FunctionDescs) do
+ if (functionDescMatchesDocs(desc, docs)) then
+ hasFound = true
+ break
+ end
+ end -- for idx - freeDescs[]
+ if not(hasFound) then
+ table.insert(res, docs)
+ end
+ end -- for docs - a_FunctionDocs[]
+
+ -- If no result, return nil instead of an empty table:
+ if not(res[1]) then
+ return nil
+ end
+ return res
+end
+
+
+
+
+
+--- Returns a dict of "FnName" -> { { <FnDesc> }, ... } that are documented in a_FunctionDocs but missing from a_FunctionDescs
+-- If there are no such descriptions, returns nil instead
+-- a_FunctionDescs is a dict of "FnName" -> { <FnDescs> } loaded from APIDesc.lua et al
+-- <FnDescs> may be a single desc or an array of those
+-- a_FunctionDocs is a dict og "FnName" -> { { <FnDesc> }, ... } loaded from ToLua++'s parser
+local function listMissingClassFunctionDescs(a_FunctionDescs, a_FunctionDocs)
+ -- Match the docs and descriptions for each separate function:
+ local res = {}
+ local hasSome = false
+ a_FunctionDescs = a_FunctionDescs or {}
+ a_FunctionDocs = a_FunctionDocs or {}
+ for fnName, fnDocs in pairs(a_FunctionDocs) do
+ local fnDescName = g_FunctionNameDocsToDesc[fnName] or fnName
+ if not(g_IgnoreFunction[fnDescName]) then
+ local fnDescs = a_FunctionDescs[fnDescName]
+ if not(fnDescs) then
+ -- Function not described at all, insert a dummy empty description for the matching:
+ fnDescs = {}
+ elseif not(fnDescs[1]) then
+ -- Function has a single description, convert it to the same format as multi-overload functions use:
+ fnDescs = { fnDescs }
+ end
+ local missingDocs = listMissingClassSingleFunctionDescs(fnDescs, fnDocs)
+ if (missingDocs) then
+ res[fnName] = missingDocs
+ hasSome = true
+ end
+ end -- not ignored
+ end -- for fnName, fnDocs - a_FunctionDocs[]
+ if not(hasSome) then
+ return nil
+ end
+ return res
+end
+
+
+
+
+
+--- Returns a dictionary of "SymbolName" -> { <desc> } for any variable or constant that is documented but not described
+-- a_VarConstDescs is an array of variable or constant descriptions, as loaded from APIDesc.lua
+-- a_VarConstDocs is an array of variable or constant documentation items, as loaded from ToLua++'s parser
+-- If no symbol is to be returned, returns nil instead
+local function listMissingClassVarConstDescs(a_VarConstDescs, a_VarConstDocs)
+ -- Match the docs and descriptions for each separate function:
+ local res = {}
+ local hasSome = false
+ a_VarConstDescs = a_VarConstDescs or {}
+ a_VarConstDocs = a_VarConstDocs or {}
+ for symName, symDocs in pairs(a_VarConstDocs) do
+ local symDesc = a_VarConstDescs[symName]
+ if (
+ not(symDesc) or -- Symbol not described at all
+ not(symDesc.Notes) or -- Non-existent description
+ (
+ (symDesc.Notes == "") and -- Empty description
+ (type(symDocs.Notes) == "string") and -- Docs has a string ...
+ (symDocs.Notes ~= "") -- ... that is not empty
+ )
+ ) then
+ res[symName] = symDocs
+ hasSome = true
+ end
+ end
+ if not(hasSome) then
+ return nil
+ end
+ return res
+end
+
+
+
+
+
+--- Fills a_Missing with descriptions that are documented in a_ClassDocs but missing from a_ClassDesc
+-- a_ClassDesc is the class' description loaded from APIDesc et al
+-- a_ClassDocs is the class' documentation loaded from ToLua++'s parser
+local function listMissingClassDescs(a_ClassName, a_ClassDesc, a_ClassDocs, a_Missing)
+ local missing =
+ {
+ Functions = listMissingClassFunctionDescs(a_ClassDesc.Functions, a_ClassDocs.Functions),
+ Constants = listMissingClassVarConstDescs(a_ClassDesc.Constants, a_ClassDocs.Constants),
+ Variables = listMissingClassVarConstDescs(a_ClassDesc.Variables, a_ClassDocs.Variables),
+ }
+ if (
+ not(missing.Functions) and
+ not(missing.Constants) and
+ not(missing.Variables)
+ ) then
+ -- Nothing missing, don't add anything
+ return
+ end
+ a_Missing[a_ClassName] = missing
+end
+
+
+
+
+
+--- Returns a dictionary of "ClassName" -> { { <desc> }, ... } of descriptions that are documented in a_Docs but missing from a_Descs
+-- a_Descs is the descriptions loaded from APIDesc et al
+-- a_Docs is the documentation loaded from ToLua++'s parser
+local function findMissingDescs(a_Descs, a_Docs)
+ local descClasses = a_Descs.Classes
+ local res = {}
+ for clsName, clsDocs in pairs(a_Docs) do
+ local clsDesc = descClasses[clsName] or {}
+ listMissingClassDescs(clsName, clsDesc, clsDocs, res)
+ end
+ return res
+end
+
+
+
+
+
+local function outputTable(a_File, a_Table, a_Indent)
+ -- Extract all indices first:
+ local allIndices = {}
+ for k, _ in pairs(a_Table) do
+ table.insert(allIndices, k)
+ end
+
+ -- Sort the indices:
+ table.sort(allIndices,
+ function (a_Index1, a_Index2)
+ if (type(a_Index1) == "number") then
+ if (type(a_Index2) == "number") then
+ -- Both indices are numeric, sort by value
+ return (a_Index1 < a_Index2)
+ end
+ -- a_Index2 is non-numeric, always goes after a_Index1
+ return true
+ end
+ if (type(a_Index2) == "number") then
+ -- a_Index2 is numeric, a_Index1 is not
+ return false
+ end
+ -- Neither index is numeric, use regular string comparison:
+ return caseInsensitiveCompare(tostring(a_Index1), tostring(a_Index2))
+ end
+ )
+
+ -- Output by using the index order:
+ a_File:write(a_Indent, "{\n")
+ local indent = a_Indent .. "\t"
+ for _, index in ipairs(allIndices) do
+ -- Write the index:
+ a_File:write(indent, "[")
+ if (type(index) == "string") then
+ a_File:write(string.format("%q", index))
+ else
+ a_File:write(index)
+ end
+ a_File:write("] =")
+
+ -- Write the value:
+ local v = a_Table[index]
+ if (type(v) == "table") then
+ a_File:write("\n")
+ outputTable(a_File, v, indent)
+ elseif (type(v) == "string") then
+ a_File:write(string.format(" %q", v))
+ else
+ a_File:write(" ", tostring(v))
+ end
+ a_File:write(",\n")
+ end
+ a_File:write(a_Indent, "}")
+end
+
+
+
+
+
+--- Returns a description of function params, as used for output
+-- a_Params is nil or an array of parameters from ToLua++'s parser
+-- a_ClassMap is a dictionary of "ClassName" -> true for all known classes
+local function extractParamsForOutput(a_Params, a_ClassMap)
+ if not(a_Params) then
+ return ""
+ end
+ assert(a_ClassMap)
+
+ local params = {}
+ for _, param in ipairs(a_Params) do
+ local paramType = param.Type or ""
+ paramType = g_CTypeToLuaType[paramType] or paramType -- Translate from C type to Lua type
+ local paramName = param.Name or paramType or "[unknown]"
+ paramName = paramName:gsub("^a_", "") -- Remove the "a_" prefix, if present
+ local idxColon = paramType:find("::") -- Extract children classes and enums within classes
+ local paramTypeAnchor = ""
+ if (idxColon) then
+ paramTypeAnchor = "#" .. paramType:sub(idxColon + 2)
+ paramType = paramType:sub(1, idxColon - 1)
+ end
+ if (a_ClassMap[paramType]) then
+ -- Param type is a class name, make it a link
+ if not(param.Name) then
+ paramName = "{{" .. paramType .. paramTypeAnchor .. "}}"
+ else
+ paramName = "{{" .. paramType .. paramTypeAnchor .. "|" .. paramName .. "}}"
+ end
+ end
+ table.insert(params, paramName)
+ end
+ return table.concat(params, ", ")
+end
+
+
+
+
+
+--- Returns a single line of function description for output
+-- a_Desc is the function description
+-- a_ClassMap is a dictionary of "ClassName" -> true for all known classes
+local function formatFunctionDesc(a_Docs, a_ClassMap)
+ local staticClause = ""
+ if (a_Docs.IsStatic) then
+ staticClause = "IsStatic = true, "
+ end
+ return string.format("{ Params = %q, Return = %q, %sNotes = %q },\n",
+ extractParamsForOutput(a_Docs.Params, a_ClassMap),
+ extractParamsForOutput(a_Docs.Returns, a_ClassMap),
+ staticClause,
+ (a_Docs.Desc or ""):gsub("%.\n", ". "):gsub("\n", ". "):gsub("%s+", " ")
+ )
+end
+
+
+
+
+
+--- Outputs differences in function descriptions into a file
+-- a_File is the output file
+-- a_Functions is nil or a dictionary of "FunctionName" -> { { <desc> }, ... }
+-- a_ClassMap is a dictionary of "ClassName" -> true for all known classes
+local function outputFunctions(a_File, a_Functions, a_ClassMap)
+ assert(a_File)
+ if not(a_Functions) then
+ return
+ end
+
+ -- Get a sorted array of all function names:
+ local fnNames = {}
+ for fnName, _ in pairs(a_Functions) do
+ table.insert(fnNames, fnName)
+ end
+ table.sort(fnNames, caseInsensitiveCompare)
+
+ -- Output the function descs:
+ a_File:write("\t\tFunctions =\n\t\t{\n")
+ for _, fnName in ipairs(fnNames) do
+ a_File:write("\t\t\t", g_FunctionNameDocsToDesc[fnName] or fnName, " =")
+ local docs = a_Functions[fnName]
+ if (docs[2]) then
+ -- There are at least two descriptions, use the array format:
+ a_File:write("\n\t\t\t{\n")
+ for _, doc in ipairs(docs) do
+ a_File:write("\t\t\t\t", formatFunctionDesc(doc, a_ClassMap))
+ end
+ a_File:write("\t\t\t},\n")
+ else
+ -- There's only one description, use the simpler one-line format:
+ a_File:write(" ", formatFunctionDesc(docs[1], a_ClassMap))
+ end
+ end
+ a_File:write("\t\t},\n")
+end
+
+
+
+
+
+--- Returns the description of a single variable or constant
+-- a_Docs is the ToLua++'s documentation of the symbol
+-- a_ClassMap is a dictionary of "ClassName" -> true for all known classes
+local function formatVarConstDesc(a_Docs, a_ClassMap)
+ local descType = ""
+ if (a_Docs.Type) then
+ local luaType = g_CTypeToLuaType[a_Docs.Type] or a_Docs.Type
+ if (a_ClassMap[a_Docs.Type]) then
+ descType = string.format("Type = {{%q}}, ", luaType);
+ else
+ descType = string.format("Type = %q, ", luaType);
+ end
+ end
+ return string.format("{ %sNotes = %q },\n", descType, a_Docs.Desc or "")
+end
+
+
+
+
+
+--- Outputs differences in variables' or constants' descriptions into a file
+-- a_File is the output file
+-- a_VarConst is nil or a dictionary of "VariableOrConstantName" -> { <desc> }
+-- a_Header is a string, either "Variables" or "Constants"
+-- a_ClassMap is a dictionary of "ClassName" -> true for all known classes
+local function outputVarConst(a_File, a_VarConst, a_Header, a_ClassMap)
+ assert(a_File)
+ assert(type(a_Header) == "string")
+ if not(a_VarConst) then
+ return
+ end
+
+ -- Get a sorted array of all symbol names:
+ local symNames = {}
+ for symName, _ in pairs(a_VarConst) do
+ table.insert(symNames, symName)
+ end
+ table.sort(symNames, caseInsensitiveCompare)
+
+ -- Output the symbol descs:
+ a_File:write("\t\t", a_Header, " =\n\t\t{\n")
+ for _, symName in ipairs(symNames) do
+ local docs = a_VarConst[symName]
+ a_File:write("\t\t\t", symName, " = ", formatVarConstDesc(docs, a_ClassMap))
+ end
+ a_File:write("\t\t},\n")
+end
+
+
+
+
+
+--- Outputs the diff into a file
+-- a_Diff is the diff calculated by findMissingDescs()
+-- The output file is written as a Lua source file formatted to match APIDesc.lua
+local function outputDiff(a_Diff)
+ -- Sort the classnames:
+ local classNames = {}
+ local classMap = {}
+ for clsName, _ in pairs(a_Diff) do
+ table.insert(classNames, clsName)
+ classMap[clsName] = true
+ end
+ table.sort(classNames, caseInsensitiveCompare)
+
+ -- Output each class:
+ local f = assert(io.open("APIDiff.lua", "w"))
+ -- outputTable(f, diff, "")
+ f:write("return\n{\n")
+ for _, clsName in ipairs(classNames) do
+ f:write("\t", clsName, " =\n\t{\n")
+ local desc = a_Diff[clsName]
+ outputFunctions(f, desc.Functions, classMap)
+ outputVarConst(f, desc.Variables, "Variables", classMap)
+ outputVarConst(f, desc.Constants, "Constants", classMap)
+ f:write("\t},\n")
+ end
+ f:write("}\n")
+ f:close()
+end
+
+
+
+
+
+local apiDesc = loadAPIDesc()
+local apiDocs = loadAPIDocs()
+local diff = findMissingDescs(apiDesc, apiDocs)
+outputDiff(diff)
+print("Diff has been output to file APIDiff.lua.")
+
+
+
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 1d735ac83..8bcd5a4e6 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -30,6 +30,7 @@
#include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/MobHeadEntity.h"
#include "../BlockEntities/FlowerPotEntity.h"
+#include "../Generating/ChunkDesc.h"
#include "../LineBlockTracer.h"
#include "../WorldStorage/SchematicFileSerializer.h"
#include "../CompositeChat.h"
@@ -3443,6 +3444,33 @@ static int tolua_cBoundingBox_Intersect(lua_State * a_LuaState)
+static int tolua_cChunkDesc_GetBlockTypeMeta(lua_State * a_LuaState)
+{
+ /* Function signature:
+ ChunkDesc:GetBlockTypeMeta(RelX, RelY, RelZ) -> BlockType, BlockMeta
+ */
+
+ cLuaState L(a_LuaState);
+ const cChunkDesc * self;
+ int relX, relY, relZ;
+ if (!L.GetStackValues(1, self, relX, relY, relZ))
+ {
+ L.LogStackValues();
+ tolua_error(a_LuaState, "Invalid function params. Expected chunkDesc:GetBlockTypeMeta(relX, relY, relZ)", nullptr);
+ return 0;
+ }
+ BLOCKTYPE blockType;
+ NIBBLETYPE blockMeta;
+ self->GetBlockTypeMeta(relX, relY, relZ, blockType, blockMeta);
+ L.Push(blockType);
+ L.Push(blockMeta);
+ return 2;
+}
+
+
+
+
+
static int tolua_cCompositeChat_AddRunCommandPart(lua_State * tolua_S)
{
// function cCompositeChat:AddRunCommandPart(Message, Command, [Style])
@@ -3736,6 +3764,10 @@ void cManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "Intersect", tolua_cBoundingBox_Intersect);
tolua_endmodule(tolua_S);
+ tolua_beginmodule(tolua_S, "cChunkDesc");
+ tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cChunkDesc_GetBlockTypeMeta);
+ tolua_endmodule(tolua_S);
+
tolua_beginmodule(tolua_S, "cClientHandle");
tolua_constant(tolua_S, "MAX_VIEW_DISTANCE", cClientHandle::MAX_VIEW_DISTANCE);
tolua_constant(tolua_S, "MIN_VIEW_DISTANCE", cClientHandle::MIN_VIEW_DISTANCE);