| rev | line source | 
| jbe/bsw@0 | 1 #!/usr/bin/env lua | 
| jbe/bsw@0 | 2 | 
| jbe/bsw@0 | 3 local args = {...} | 
| jbe/bsw@0 | 4 | 
| jbe/bsw@0 | 5 if not args[1] or string.match(args[1], "^%-") then | 
| jbe/bsw@0 | 6   print("Usage: autodoc.lua srcdir/ > documentation.html") | 
| jbe/bsw@0 | 7   os.exit(1) | 
| jbe/bsw@0 | 8 end | 
| jbe/bsw@0 | 9 | 
| jbe/bsw@0 | 10 local entries = {} | 
| jbe/bsw@0 | 11 | 
| jbe/bsw@0 | 12 for idx, srcdir in ipairs(args) do | 
| jbe/bsw@0 | 13   local find_proc = io.popen('find "' .. srcdir .. '" -name \\*.lua', "r") | 
| jbe/bsw@0 | 14   for filename in find_proc:lines() do | 
| jbe/bsw@0 | 15     local synopsis, comment, source | 
| jbe/bsw@0 | 16     local mode | 
| jbe/bsw@0 | 17     local function reset() | 
| jbe/bsw@0 | 18       synopsis, comment, source = {}, {}, {} | 
| jbe/bsw@0 | 19       mode = "idle" | 
| jbe/bsw@0 | 20     end | 
| jbe/bsw@0 | 21     reset() | 
| jbe/bsw@0 | 22     local function strip(tbl) | 
| jbe/bsw@0 | 23       while true do | 
| jbe/bsw@0 | 24         local line = tbl[#tbl] | 
| jbe/bsw@0 | 25         if line and string.find(line, "^%s*$") then | 
| jbe/bsw@0 | 26           tbl[#tbl] = nil | 
| jbe/bsw@0 | 27         else | 
| jbe/bsw@0 | 28           break | 
| jbe/bsw@0 | 29         end | 
| jbe/bsw@0 | 30       end | 
| jbe/bsw@0 | 31       if #tbl > 0 then | 
| jbe/bsw@0 | 32         local min_indent = math.huge | 
| jbe/bsw@0 | 33         for idx, line in ipairs(tbl) do | 
| jbe/bsw@0 | 34           local spaces = string.match(line, "^(%s*)") | 
| jbe/bsw@0 | 35           if min_indent > #spaces then | 
| jbe/bsw@0 | 36             min_indent = #spaces | 
| jbe/bsw@0 | 37           end | 
| jbe/bsw@0 | 38         end | 
| jbe/bsw@0 | 39         local pattern_parts = { "^" } | 
| jbe/bsw@0 | 40         for i = 1, min_indent do | 
| jbe/bsw@0 | 41           pattern_parts[#pattern_parts+1] = "%s" | 
| jbe/bsw@0 | 42         end | 
| jbe/bsw@0 | 43         pattern_parts[#pattern_parts+1] = "(.-)%s*$" | 
| jbe/bsw@0 | 44         local pattern = table.concat(pattern_parts) | 
| jbe/bsw@0 | 45         for idx, line in ipairs(tbl) do | 
| jbe/bsw@0 | 46           tbl[idx] = string.match(line, pattern) | 
| jbe/bsw@0 | 47         end | 
| jbe/bsw@0 | 48       end | 
| jbe/bsw@0 | 49     end | 
| jbe/bsw@0 | 50     local function entry_done() | 
| jbe/bsw@0 | 51       if #synopsis > 0 then | 
| jbe/bsw@0 | 52         strip(synopsis) | 
| jbe/bsw@0 | 53         strip(comment) | 
| jbe/bsw@0 | 54         strip(source) | 
| jbe/bsw@0 | 55         local stripped_synopsis = {} | 
| jbe/bsw@0 | 56         for idx, line in ipairs(synopsis) do | 
| jbe/bsw@0 | 57           local stripped_line = string.match(line, "^(.-)%-%-") or line | 
| jbe/bsw@0 | 58           stripped_line = string.match(stripped_line, "^(.-)%s*$") | 
| jbe/bsw@0 | 59           stripped_synopsis[#stripped_synopsis+1] = stripped_line | 
| jbe/bsw@0 | 60         end | 
| jbe/bsw@0 | 61         local concatted_synopsis = string.gsub( | 
| jbe/bsw@0 | 62           table.concat(stripped_synopsis, " "), "[%s]+", " " | 
| jbe/bsw@0 | 63         ) | 
| jbe/bsw@0 | 64         local func_call = string.match( | 
| jbe@197 | 65           concatted_synopsis, "^[A-Za-z0-9_,. ]+= ?(.-) ?$" | 
| jbe/bsw@0 | 66         ) | 
| jbe/bsw@0 | 67         if not func_call then | 
| jbe/bsw@0 | 68           func_call = string.match( | 
| jbe/bsw@0 | 69             concatted_synopsis, | 
| jbe/bsw@0 | 70             "^ ?for[A-Za-z0-9_, ]+in (.-) ? do[ %.]+end ?$" | 
| jbe/bsw@0 | 71           ) | 
| jbe/bsw@0 | 72         end | 
| jbe/bsw@0 | 73         if not func_call then | 
| jbe/bsw@0 | 74           func_call = string.match(concatted_synopsis, "^ ?(.-) ?$") | 
| jbe/bsw@0 | 75         end | 
| jbe/bsw@0 | 76         func_call = string.gsub( | 
| jbe/bsw@0 | 77           func_call, | 
| jbe@194 | 78           "^([^({]*)([({]).*[,;].*[,;].*[,;].*([)}])$", | 
| jbe@194 | 79           function(base, open, close) | 
| jbe@194 | 80             return base .. open .. " ... " .. close | 
| jbe/bsw@0 | 81           end | 
| jbe/bsw@0 | 82         ) | 
| jbe/bsw@0 | 83         if entries[func_call] then | 
| jbe/bsw@0 | 84           error("Multiple occurrences of: " .. func_call) | 
| jbe/bsw@0 | 85         end | 
| jbe/bsw@0 | 86         entries[func_call] = { | 
| jbe/bsw@0 | 87           func_call   = func_call, | 
| jbe/bsw@0 | 88           synopsis    = synopsis, | 
| jbe/bsw@0 | 89           comment     = comment, | 
| jbe/bsw@0 | 90           source      = source | 
| jbe/bsw@0 | 91         } | 
| jbe/bsw@0 | 92       end | 
| jbe/bsw@0 | 93       reset() | 
| jbe/bsw@0 | 94     end | 
| jbe@104 | 95     for line in io.lines(filename) do | 
| jbe/bsw@0 | 96       local function add_to(tbl) | 
| jbe/bsw@0 | 97         if #tbl > 0 or not string.match(line, "^%s*$") then | 
| jbe/bsw@0 | 98           tbl[#tbl+1] = line | 
| jbe/bsw@0 | 99         end | 
| jbe/bsw@0 | 100       end | 
| jbe/bsw@0 | 101       if mode == "idle" then | 
| jbe/bsw@0 | 102         if string.find(line, "^%s*%-%-%[%[%-%-%s*$") then | 
| jbe/bsw@0 | 103           mode = "synopsis" | 
| jbe/bsw@0 | 104         end | 
| jbe/bsw@0 | 105       elseif mode == "synopsis" then | 
| jbe/bsw@0 | 106         if string.find(line, "^%s*$") and #synopsis > 0 then | 
| jbe/bsw@0 | 107           mode = "comment" | 
| jbe/bsw@0 | 108         elseif string.find(line, "^%s*%-%-]]%-%-%s*$") then | 
| jbe/bsw@0 | 109           mode = "source" | 
| jbe/bsw@0 | 110         else | 
| jbe/bsw@0 | 111           add_to(synopsis) | 
| jbe/bsw@0 | 112         end | 
| jbe/bsw@0 | 113       elseif mode == "comment" then | 
| jbe/bsw@0 | 114         if string.find(line, "^%s*%-%-]]%-%-%s*$") then | 
| jbe/bsw@0 | 115           mode = "source" | 
| jbe/bsw@0 | 116         else | 
| jbe/bsw@0 | 117           add_to(comment) | 
| jbe/bsw@0 | 118         end | 
| jbe/bsw@0 | 119       elseif mode == "source" then | 
| jbe/bsw@0 | 120         if string.find(line, "^%s*%-%-//%-%-%s*$") then | 
| jbe/bsw@0 | 121           entry_done() | 
| jbe/bsw@0 | 122         else | 
| jbe/bsw@0 | 123           add_to(source) | 
| jbe/bsw@0 | 124         end | 
| jbe/bsw@0 | 125       end | 
| jbe/bsw@0 | 126     end | 
| jbe/bsw@0 | 127     entry_done() | 
| jbe/bsw@0 | 128   end | 
| jbe/bsw@0 | 129   find_proc:close() | 
| jbe/bsw@0 | 130 end | 
| jbe/bsw@0 | 131 | 
| jbe/bsw@0 | 132 | 
| jbe/bsw@0 | 133 function output(...) | 
| jbe/bsw@0 | 134   return io.stdout:write(...) | 
| jbe/bsw@0 | 135 end | 
| jbe/bsw@0 | 136 | 
| jbe/bsw@0 | 137 function encode(text) | 
| jbe/bsw@0 | 138   return ( | 
| jbe/bsw@0 | 139     string.gsub( | 
| jbe/bsw@0 | 140       text, '[<>&"]', | 
| jbe/bsw@0 | 141       function(char) | 
| jbe/bsw@0 | 142         if char == '<' then | 
| jbe/bsw@0 | 143           return "<" | 
| jbe/bsw@0 | 144         elseif char == '>' then | 
| jbe/bsw@0 | 145           return ">" | 
| jbe/bsw@0 | 146         elseif char == '&' then | 
| jbe/bsw@0 | 147           return "&" | 
| jbe/bsw@0 | 148         elseif char == '"' then | 
| jbe/bsw@0 | 149           return """ | 
| jbe/bsw@0 | 150         end | 
| jbe/bsw@0 | 151       end | 
| jbe/bsw@0 | 152     ) | 
| jbe/bsw@0 | 153   ) | 
| jbe/bsw@0 | 154 end | 
| jbe/bsw@0 | 155 | 
| jbe/bsw@0 | 156 function output_lines(tbl) | 
| jbe/bsw@0 | 157   for idx, line in ipairs(tbl) do | 
| jbe/bsw@0 | 158     if idx == 1 then | 
| jbe/bsw@0 | 159       output('<pre>') | 
| jbe/bsw@0 | 160     end | 
| jbe/bsw@0 | 161     local command, comment = string.match(line, "^(.-)(%-%-.*)$") | 
| jbe/bsw@0 | 162     if command then | 
| jbe/bsw@0 | 163       output( | 
| jbe/bsw@0 | 164         encode(command), | 
| jbe/bsw@0 | 165         '<span class="autodoc_comment_tail">', encode(comment), '</span>' | 
| jbe/bsw@0 | 166       ) | 
| jbe/bsw@0 | 167     else | 
| jbe/bsw@0 | 168       output(encode(line)) | 
| jbe/bsw@0 | 169     end | 
| jbe/bsw@0 | 170     if idx == #tbl then | 
| jbe/bsw@0 | 171       output('</pre>') | 
| jbe/bsw@0 | 172     end | 
| jbe/bsw@0 | 173     output('\n') | 
| jbe/bsw@0 | 174   end | 
| jbe/bsw@0 | 175 end | 
| jbe/bsw@0 | 176 | 
| jbe/bsw@0 | 177 keys = {} | 
| jbe/bsw@0 | 178 for key in pairs(entries) do | 
| jbe/bsw@0 | 179   keys[#keys+1] = key | 
| jbe/bsw@0 | 180 end | 
| jbe/bsw@0 | 181 table.sort(keys) | 
| jbe/bsw@0 | 182 for idx, key in ipairs(keys) do | 
| jbe/bsw@0 | 183   local entry = entries[key] | 
| jbe/bsw@0 | 184   output('<li class="autodoc_entry">\n') | 
| jbe/bsw@0 | 185   output( | 
| jbe/bsw@0 | 186     '  <div class="short_synopsis"', | 
| jbe/bsw@0 | 187     ' onclick="document.getElementById(\'autodoc_details_', | 
| jbe/bsw@0 | 188     idx, | 
| jbe/bsw@0 | 189     '\').style.display = document.getElementById(\'autodoc_details_', | 
| jbe/bsw@0 | 190     idx, | 
| jbe/bsw@0 | 191     '\').style.display ? \'\' : \'none\';">\n' | 
| jbe/bsw@0 | 192   ) | 
| jbe/bsw@0 | 193   output('    ', encode(entry.func_call), '\n') | 
| jbe/bsw@0 | 194   output('  </div>\n') | 
| jbe/bsw@0 | 195   output( | 
| jbe/bsw@0 | 196     '  <div id="autodoc_details_', | 
| jbe/bsw@0 | 197     idx, | 
| jbe/bsw@0 | 198     '" class="autodoc_details" style="display: none;">\n' | 
| jbe/bsw@0 | 199   ) | 
| jbe/bsw@0 | 200   output('    <div class="autodoc_synopsis">\n') | 
| jbe/bsw@0 | 201   output_lines(entry.synopsis) | 
| jbe/bsw@0 | 202   output('    </div>\n') | 
| jbe/bsw@0 | 203   output('    <div class="autodoc_comment">') | 
| jbe/bsw@0 | 204   for idx, line in ipairs(entry.comment) do | 
| jbe/bsw@0 | 205     output(encode(line)) | 
| jbe/bsw@0 | 206     if idx < #entry.comment then | 
| jbe/bsw@0 | 207       output('<br/>') | 
| jbe/bsw@0 | 208     end | 
| jbe/bsw@0 | 209   end | 
| jbe/bsw@0 | 210   output('    </div>\n') | 
| jbe/bsw@0 | 211   output('    <div class="autodoc_source">\n') | 
| jbe/bsw@0 | 212   output_lines(entry.source) | 
| jbe/bsw@0 | 213   output('    </div>\n') | 
| jbe/bsw@0 | 214   output('  </div>\n') | 
| jbe/bsw@0 | 215   output('</li>\n') | 
| jbe@64 | 216 end |