OC-Tsuki/utils/gendocs.lua

260 lines
6.1 KiB
Lua

local outdir = arg[1]
function string.trim(self)
return self:gsub("^%s+", ""):gsub("%s+$", "")
end
local escapes = {
["\\"] = "\\",
["n"] = "\n",
["t"] = "\t",
["r"] = "\r"
}
local docs = {}
local cdoc
local cfunc
local function parse_string(str, close, noesc)
local s = ""
local esc = false
repeat
local c = str.read()
if not esc then
if (c ~= "\\" or noesc) then
s = s .. c
else
esc = true
end
else
if (escapes[c]) then
s = s .. escapes[c]
else
return nil, "invalid escape"
end
end
until str.peek(#close) == close or str.peek(#close) == ""
str.seek(#close)
return s
end
local function parse_dec(str)
local num = ""
repeat
num = num .. str.read()
until not str.peek():match("%d") or str.peek() == ""
return tonumber(num, 10)
end
local function parse_hex(str)
if not str.peek():match("%x") then
return nil, "malformed hex"
end
local num = ""
repeat
num = num .. str.read()
until not str.peek():match("%x") or str.peek() == ""
return tonumber(num, 16)
end
local ln = 0
local function parse_line(line)
local args = {}
local idx = 1
local str = {
peek = function(amt)
amt = amt or 1
return line:sub(idx, idx+amt-1)
end,
read = function(amt)
amt = amt or 1
local data = line:sub(idx, idx+amt-1)
idx = idx + amt
return data
end,
seek = function(amt)
amt = amt or 0
idx = idx + amt
return idx
end
}
local function lassert(a, b)
return assert(a, "line #"..ln..": "..(b or "unknown error"))
end
while str.peek() ~= "" do
if (str.peek() == "\"") then
str.seek(1)
args[#args+1] = lassert(parse_string(str, "\"", false))
elseif (str.peek(2) == "0x") then
str.seek(2)
args[#args+1] = lassert(parse_hex(str))
elseif (str.peek(1):match("%d")) then
args[#args+1] = lassert(parse_dec(str))
elseif (str.peek() == "'") then
str.seek(1)
args[#args+1] = lassert(parse_string(str, "'", false))
elseif (str.peek() == " ") then
--do nothing
else
args[#args+1] = lassert(parse_string(str, " ", true))
end
end
return args
end
local function is_comment(line)
if (line:trim():sub(1, 2) == "--") then
return true, line:sub(3)
end
end
for line in io.stdin:lines() do
ln = ln+1
local com, rline = is_comment(line)
if (com) then
if (rline:match("^%-@module")) then
local args = parse_line(rline:sub(10))
if (cdoc) then
if (cfunc) then
cdoc.methods[#cdoc.methods+1] = cfunc
cfunc = nil
end
docs[#docs+1] = cdoc
end
cdoc = {
type = "module",
methods = {},
name = args[2],
mod = args[1]
}
elseif (rline:match("^%-@section")) then
local args = parse_line(rline:sub(11))
if (cdoc) then
if (cfunc) then
cdoc.methods[#cdoc.methods+1] = cfunc
cfunc = nil
end
docs[#docs+1] = cdoc
end
cdoc = {
type = "generic",
methods = {},
name = args[2],
mod = args[1]
}
elseif (rline:match("^%-@page")) then
if (cfunc) then
cdoc.methods[#cdoc.methods+1] = cfunc
end
local args = parse_line(rline:sub(8))
cfunc = {
type = "page",
doc = {},
name = args[1],
print_name = args[2]
}
elseif (rline:match("^%-@arg") and cfunc.type == "func") then
local args = parse_line(rline:sub(7))
cfunc.args[#cfunc.args+1] = {
name = args[1],
type = args[2],
doc = args[3]
}
elseif (rline:match("^%-@return") and cfunc.type == "func") then
local args = parse_line(rline:sub(10))
cfunc.ret[#cfunc.ret+1] = {
type = args[1],
doc = args[2],
}
elseif (rline:match("^%-@doc")) then
local args = parse_line(rline:sub(7))
cfunc.doc[#cfunc.doc+1] = {
doc = args[1],
}
elseif (rline:match("^%-@see")) then
local args = parse_line(rline:sub(7))
cfunc.doc[#cfunc.doc+1] = {
doc = "See \27_S"..args[1].."\27_E"
}
elseif (rline:match("^%-@note")) then
local args = parse_line(rline:sub(8))
cfunc.doc[#cfunc.doc+1] = {
doc = "NOTE: "..args[1]
}
elseif (rline:match("^%-@desc")) then
local args = parse_line(rline:sub(8))
cfunc.doc[#cfunc.doc+1] = {
doc = args[1]
}
elseif (rline:match("^%-@vararg")) then
cfunc.vararg = true
elseif (rline:match("^%-@varret")) then
cfunc.varret = true
elseif (rline:match("^%-@func")) then
if (cfunc) then
cdoc.methods[#cdoc.methods+1] = cfunc
end
local args = parse_line(rline:sub(8))
cfunc = {
type = "func",
doc = {},
name = args[1],
ret = {},
args = {}
}
end
else
io.stderr:write(line.."\n")
end
end
if (cfunc) then
cdoc.methods[#cdoc.methods+1] = cfunc
end
if (cdoc) then
docs[#docs+1] = cdoc
end
for i=1, #docs do
os.execute("mkdir -p \""..outdir.."/"..docs[i].mod:trim().."\"")
for j=1, #docs[i].methods do
local docc = ""
local f = io.open(outdir.."/"..docs[i].mod:trim().."/"..docs[i].methods[j].name:trim()..".tdf", "wb")
if (docs[i].methods[j].type == "func") then
local argtext = ""
local fun = docs[i].mod.."."..docs[i].methods[j].name.."("
if (#docs[i].methods[j].args > 0) then
for k=1, #docs[i].methods[j].args do
fun = fun .. docs[i].methods[j].args[k].name..":"..docs[i].methods[j].args[k].type .. ", "
argtext = argtext .. docs[i].methods[j].args[k].name .. " - " .. docs[i].methods[j].args[k].doc .. "\n"
end
if (docs[i].methods[j].vararg) then
fun = fun .. "..."
else
fun = fun:sub(1, #fun-2)
end
else
argtext = "No arguments"
end
fun = fun .. ")"
local rettext = ""
if (#docs[i].methods[j].ret > 0) then
fun = fun .. ":"
for k=1, #docs[i].methods[j].ret do
fun = fun .. docs[i].methods[j].ret[k].type .. ", "
rettext = rettext .. docs[i].methods[j].ret[k].type .. " - " .. docs[i].methods[j].ret[k].doc .. "\n"
end
if (docs[i].methods[j].varret) then
fun = fun .. "..."
else
fun = fun:sub(1, #fun-2)
end
else
rettext = "No return."
end
docc = docc .. fun .. "\n\nArguments:\n"..argtext.."\n\nReturns:\n"..rettext.."\n\n"
else
docc = docc .. docs[i].methods[j].print_name .. "\n\n"
end
for k=1, #docs[i].methods[j].doc do
docc = docc..docs[i].methods[j].doc[k].doc .. "\n"
end
f:write(docc)
f:close()
end
end