2020-05-12 16:59:17 +10:00
local _ , serial = pcall ( require , " serialization " )
2020-05-11 00:59:05 +10:00
local doc = { }
doc.searchers = { }
doc.tctab = {
[ " string " ] = 31 ,
[ " table " ] = 32 ,
2020-06-21 19:16:50 +10:00
[ " userdata " ] = 32 ,
2020-05-11 00:59:05 +10:00
[ " 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
2020-05-12 16:59:17 +10:00
function doc . searchers . cdoc ( topic ) -- string -- string string -- Searches for documentation labelled as *topic* in .dict files under /boot/doc/
if not serial then return end
for k , v in ipairs ( fs.list ( " /boot/doc " ) ) do
if v : sub ( - 5 ) == " .dict " then
local f = io.open ( " /boot/doc/ " .. v , " rb " )
for line in f : lines ( ) do
local mname , docs = line : match ( " ^(.-) \t (.+)$ " )
if mname == topic or mname == topic .. " .lua " then
return doc.format ( serial.unserialize ( docs ) )
end
end
end
end
end
2020-06-21 19:16:50 +10:00
function doc . searchers . component ( name )
local dt = { }
local addr = component.list ( name ) ( )
if addr then
for fname , _ in pairs ( component.methods ( addr ) ) do
fd = { args = { } , outtypes = { } , atypes = { } }
local ds = component.doc ( addr , fname )
local ins , outs , desc = ds : match ( " %((.-)%) " ) or " " , ds : match ( " %):(.*)%-%- " ) or " " , ds : match ( " %-%-(.+) " ) or " "
for arg in ins : gmatch ( " [^,%s%[%]]+ " ) do
local an , at = arg : match ( " (.-):(.+) " )
at = at : match ( " (.-)= " ) or at
fd.args [ # fd.args + 1 ] = { an , at }
fd.atypes [ an ] = at
end
for out in outs : gmatch ( " [^,]+ " ) do
fd.outtypes [ # fd.outtypes + 1 ] = out : match ( " ^%s*(.-)%s*$ " )
end
fd.description = desc or " "
dt [ name .. " . " .. fname ] = fd
end
else
return
end
return doc.format ( dt )
end
2020-05-11 00:59:05 +10:00
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 } )