jbe/bsw@0: #!/usr/bin/env lua jbe/bsw@0: jbe/bsw@0: local args = {...} jbe/bsw@0: jbe/bsw@0: if not args[1] or string.match(args[1], "^%-") then jbe/bsw@0: print("Usage: autodoc.lua srcdir/ > documentation.html") jbe/bsw@0: os.exit(1) jbe/bsw@0: end jbe/bsw@0: jbe/bsw@0: local entries = {} jbe/bsw@0: jbe/bsw@0: for idx, srcdir in ipairs(args) do jbe/bsw@0: local find_proc = io.popen('find "' .. srcdir .. '" -name \\*.lua', "r") jbe/bsw@0: for filename in find_proc:lines() do jbe/bsw@0: local synopsis, comment, source jbe/bsw@0: local mode jbe/bsw@0: local function reset() jbe/bsw@0: synopsis, comment, source = {}, {}, {} jbe/bsw@0: mode = "idle" jbe/bsw@0: end jbe/bsw@0: reset() jbe/bsw@0: local function strip(tbl) jbe/bsw@0: while true do jbe/bsw@0: local line = tbl[#tbl] jbe/bsw@0: if line and string.find(line, "^%s*$") then jbe/bsw@0: tbl[#tbl] = nil jbe/bsw@0: else jbe/bsw@0: break jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: if #tbl > 0 then jbe/bsw@0: local min_indent = math.huge jbe/bsw@0: for idx, line in ipairs(tbl) do jbe/bsw@0: local spaces = string.match(line, "^(%s*)") jbe/bsw@0: if min_indent > #spaces then jbe/bsw@0: min_indent = #spaces jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: local pattern_parts = { "^" } jbe/bsw@0: for i = 1, min_indent do jbe/bsw@0: pattern_parts[#pattern_parts+1] = "%s" jbe/bsw@0: end jbe/bsw@0: pattern_parts[#pattern_parts+1] = "(.-)%s*$" jbe/bsw@0: local pattern = table.concat(pattern_parts) jbe/bsw@0: for idx, line in ipairs(tbl) do jbe/bsw@0: tbl[idx] = string.match(line, pattern) jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: local function entry_done() jbe/bsw@0: if #synopsis > 0 then jbe/bsw@0: strip(synopsis) jbe/bsw@0: strip(comment) jbe/bsw@0: strip(source) jbe/bsw@0: local stripped_synopsis = {} jbe/bsw@0: for idx, line in ipairs(synopsis) do jbe/bsw@0: local stripped_line = string.match(line, "^(.-)%-%-") or line jbe/bsw@0: stripped_line = string.match(stripped_line, "^(.-)%s*$") jbe/bsw@0: stripped_synopsis[#stripped_synopsis+1] = stripped_line jbe/bsw@0: end jbe/bsw@0: local concatted_synopsis = string.gsub( jbe/bsw@0: table.concat(stripped_synopsis, " "), "[%s]+", " " jbe/bsw@0: ) jbe/bsw@0: local func_call = string.match( jbe@197: concatted_synopsis, "^[A-Za-z0-9_,. ]+= ?(.-) ?$" jbe/bsw@0: ) jbe/bsw@0: if not func_call then jbe/bsw@0: func_call = string.match( jbe/bsw@0: concatted_synopsis, jbe/bsw@0: "^ ?for[A-Za-z0-9_, ]+in (.-) ? do[ %.]+end ?$" jbe/bsw@0: ) jbe/bsw@0: end jbe/bsw@0: if not func_call then jbe/bsw@0: func_call = string.match(concatted_synopsis, "^ ?(.-) ?$") jbe/bsw@0: end jbe/bsw@0: func_call = string.gsub( jbe/bsw@0: func_call, jbe@194: "^([^({]*)([({]).*[,;].*[,;].*[,;].*([)}])$", jbe@194: function(base, open, close) jbe@194: return base .. open .. " ... " .. close jbe/bsw@0: end jbe/bsw@0: ) jbe/bsw@0: if entries[func_call] then jbe/bsw@0: error("Multiple occurrences of: " .. func_call) jbe/bsw@0: end jbe/bsw@0: entries[func_call] = { jbe/bsw@0: func_call = func_call, jbe/bsw@0: synopsis = synopsis, jbe/bsw@0: comment = comment, jbe/bsw@0: source = source jbe/bsw@0: } jbe/bsw@0: end jbe/bsw@0: reset() jbe/bsw@0: end jbe@104: for line in io.lines(filename) do jbe/bsw@0: local function add_to(tbl) jbe/bsw@0: if #tbl > 0 or not string.match(line, "^%s*$") then jbe/bsw@0: tbl[#tbl+1] = line jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: if mode == "idle" then jbe/bsw@0: if string.find(line, "^%s*%-%-%[%[%-%-%s*$") then jbe/bsw@0: mode = "synopsis" jbe/bsw@0: end jbe/bsw@0: elseif mode == "synopsis" then jbe/bsw@0: if string.find(line, "^%s*$") and #synopsis > 0 then jbe/bsw@0: mode = "comment" jbe/bsw@0: elseif string.find(line, "^%s*%-%-]]%-%-%s*$") then jbe/bsw@0: mode = "source" jbe/bsw@0: else jbe/bsw@0: add_to(synopsis) jbe/bsw@0: end jbe/bsw@0: elseif mode == "comment" then jbe/bsw@0: if string.find(line, "^%s*%-%-]]%-%-%s*$") then jbe/bsw@0: mode = "source" jbe/bsw@0: else jbe/bsw@0: add_to(comment) jbe/bsw@0: end jbe/bsw@0: elseif mode == "source" then jbe/bsw@0: if string.find(line, "^%s*%-%-//%-%-%s*$") then jbe/bsw@0: entry_done() jbe/bsw@0: else jbe/bsw@0: add_to(source) jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: entry_done() jbe/bsw@0: end jbe/bsw@0: find_proc:close() jbe/bsw@0: end jbe/bsw@0: jbe/bsw@0: jbe/bsw@0: function output(...) jbe/bsw@0: return io.stdout:write(...) jbe/bsw@0: end jbe/bsw@0: jbe/bsw@0: function encode(text) jbe/bsw@0: return ( jbe/bsw@0: string.gsub( jbe/bsw@0: text, '[<>&"]', jbe/bsw@0: function(char) jbe/bsw@0: if char == '<' then jbe/bsw@0: return "<" jbe/bsw@0: elseif char == '>' then jbe/bsw@0: return ">" jbe/bsw@0: elseif char == '&' then jbe/bsw@0: return "&" jbe/bsw@0: elseif char == '"' then jbe/bsw@0: return """ jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: ) jbe/bsw@0: ) jbe/bsw@0: end jbe/bsw@0: jbe/bsw@0: function output_lines(tbl) jbe/bsw@0: for idx, line in ipairs(tbl) do jbe/bsw@0: if idx == 1 then jbe/bsw@0: output('
')
jbe/bsw@0:     end
jbe/bsw@0:     local command, comment = string.match(line, "^(.-)(%-%-.*)$")
jbe/bsw@0:     if command then
jbe/bsw@0:       output(
jbe/bsw@0:         encode(command),
jbe/bsw@0:         '', encode(comment), ''
jbe/bsw@0:       )
jbe/bsw@0:     else
jbe/bsw@0:       output(encode(line))
jbe/bsw@0:     end
jbe/bsw@0:     if idx == #tbl then
jbe/bsw@0:       output('
') jbe/bsw@0: end jbe/bsw@0: output('\n') jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: jbe/bsw@0: keys = {} jbe/bsw@0: for key in pairs(entries) do jbe/bsw@0: keys[#keys+1] = key jbe/bsw@0: end jbe/bsw@0: table.sort(keys) jbe/bsw@0: for idx, key in ipairs(keys) do jbe/bsw@0: local entry = entries[key] jbe@381: local m1, m2 = string.match(key, "?([A-Za-z0-9_.:]+)") jbe@381: local anchor = m1 .. m2 jbe@390: output('
  • \n') jbe@390: output('
    \n') jbe@390: output(' ', encode(entry.func_call), '\n') jbe@390: output('
    \n') jbe@390: output(' \n') jbe@390: output('
  • \n') jbe@64: end