diff --git a/lib/doc.lua b/lib/doc.lua new file mode 100644 index 0000000..d7dbef5 --- /dev/null +++ b/lib/doc.lua @@ -0,0 +1,113 @@ +local doc = {} +doc.searchers = {} +doc.tctab = { + ["string"] = 31, + ["table"] = 32, + ["number"] = 33, + ["boolean"] = 35, + ["function"] = 36 +} + +function doc.parsefile(path) -- string -- table -- parses file from *path* to return a documentation table + local fdoc = {} + local f = io.open(path) + local lines = {} + for l in f:read("*a"):gmatch("[^\n]+") do + if l:find("function") and not l:find("local") then + lines[#lines+1] = l + end + end + for k,v in pairs(lines) do + local name, args, desc = v:match("function%s+(.+)%s*%((.*)%)%s*%-%-%s*(.+)") + if name and args and desc then + local fd = {["description"]=desc or desc,["args"]={},["atypes"]={}} + for word in args:gmatch("[^%s,]+") do + fd.args[#fd.args+1] = {word} + fd.atypes[word] = "unknown" + end + local argtypes, outtypes, description = desc:match("(.-)%-%-(.-)%-%-%s*(.+)") + if argtypes and outtypes and description then + local wc = 1 + for word in argtypes:gmatch("%S+") do + fd.args[wc][2] = word + fd.atypes[fd.args[wc][1]] = word + wc = wc + 1 + end + local wc = 1 + for word in outtypes:gmatch("%S+") do + fd.outtypes = fd.outtypes or {} + fd.outtypes[#fd.outtypes+1] = word + end + fd.description = description + end + fdoc[name] = fd + end + end + return fdoc +end + +function doc.format(fdoc) -- table -- string -- returns VT100 formatted documentation from documentation table *fdoc* + local rs = "" -- string to return + for fname,finfo in pairs(fdoc) do + if rs:len() > 0 then rs = rs .. "\n\n" end + local as = "" -- string containing arguments for a given function, with colours for type + for k,v in ipairs(finfo.args) do + local c = doc.tctab[v[2]] or 0 + if k > 1 then + as = as .. ", " + end + if v[2] then + as = string.format("%s%s: \27[%im%s\27[0m",as,v[2],c,v[1]) + else + as = string.format("%s\27[%im%s\27[0m",as,c,v[1]) + end + end + local rv = "" + if finfo.outtypes then + rv = ": " + for k,v in ipairs(finfo.outtypes) do + if k > 1 then + rv = rv .. ", " + end + local c = doc.tctab[v] or 0 + rv = string.format("%s\27[%im%s\27[0m",rv,c,v) + end + end + local nd = finfo.description + for k,v in pairs(finfo.atypes) do + local c = doc.tctab[v] or 7 + nd=nd:gsub("%*"..k.."%*","\27["..tostring(c).."m"..k.."\27[0m") + end + rs = string.format("%s\27[36m%s\27[0m(%s)%s\n%s",rs,fname,as,rv,nd) + end + return rs +end + +function doc.searchers.lib(name) -- string -- string string -- Tries to find a documentation from a library with *name*. Returns either a string of documentation, or false and a reason. + local lib = os.getenv("LIB") or "/boot/lib" + local dt + for d in lib:gmatch("[^\n]+") do + if fs.exists(d.."/"..name) then + dt = doc.parsefile(d.."/"..name) + elseif fs.exists(d.."/"..name..".lua") then + dt = doc.parsefile(d.."/"..name..".lua") + end + end + if not dt then return false, "unable to find documentation for "..tostring(name) end + return doc.format(dt) +end + +function doc.docs(topic) -- string -- boolean -- Displays the documentation for *topic*, returning true, or errors. Also callable as just doc(). + local lib = os.getenv("LIB") or "/boot/lib" + local dt + for k,v in pairs(doc.searchers) do + dt=v(topic) + if dt then + print(dt) + return true + end + end + error("unable to find documentation for "..tostring(name)) +end + +return setmetatable(doc,{__call=function(_,topic) return doc.docs(topic) end})