From e87b06ee0fe2a588b72a356bbb8378899365d626 Mon Sep 17 00:00:00 2001 From: Alexander Pickering Date: Sun, 5 Jul 2020 17:18:56 -0400 Subject: Add rockspec Add a rockspec and move the files around so that luarocks can install it correctly --- init.lua | 572 --------------------------------------------------------------- 1 file changed, 572 deletions(-) delete mode 100644 init.lua (limited to 'init.lua') diff --git a/init.lua b/init.lua deleted file mode 100644 index a3515ed..0000000 --- a/init.lua +++ /dev/null @@ -1,572 +0,0 @@ ---[[ -Mdoc creats documentation from comments in source files. -Mdoc works in several steps: - 1. Create "chunks" from input source files - 2. Create a dependnecy graph, so we only update the things that - need updating - 3. Output html documentation -]] -local lpeg = require("lpeg") -local lfs = require("lfs") -local et = require("etlua") -require("ext") -local opt = require("opt_parser") - -local args = {...} -local options = opt.parse_options(args) -if options.help then - print(opt.help()) - return -end -print("options:",options) - -local state = { - title = "", - sections = {}, - files = {}, - documents = {}, - known_names = {}, - index = {}, -} - -local function log(...) - if options.verbose then - print(...) - end -end -local function logf(...) - if options.verbose then - printf(...) - end -end - -local toskip = {["."] = true,[".."] = true} -local function scan(path,callback) - assert(path,"Cannot scan without a path to scan (was nil)") - assert(callback,"Cannot scan without a callback to call (was nil)") - for item in lfs.dir(path) do - log("looking at:",item) - if toskip[item] then - goto nextitem - end - local fullpath = path .. "/" .. item - local attributes = lfs.attributes(fullpath) - if attributes.mode == "directory" or attributes.mode == "link" then - log("found directory:",directory) - scan(fullpath,callback) - elseif attributes.mode == "file" then - log("found item:",item) - callback(fullpath) - end - ::nextitem:: - end -end -for _,path in pairs(options.paths) do - scan(path,function(p) - local attributes = lfs.attributes(p) - table.insert(state.files,{ - path = p, - relpath = p:match(string.format("%s/(.*)",path)), - lastmod = attributes.modification, - }) - end) -end -logf("Found %d files.",#state.files) - -for _,path in pairs(options.document_paths) do - scan(path,function(p) - local attributes = lfs.attributes(p) - table.insert(state.documents,{ - path = p, - lastmod = attributes.modification, - }) - end) -end -logf("Found %d documents.",#state.documents) - - -local function parse(text,filename) - assert(options.parser,"Failed to find a parser option") - assert(text,"Failed to get text") - text = text:gsub("@{(.+)}",function(data) - return string.format("[%s](%s.html)",data,data) - end) - log("Using parser:",options.parser) - local tmp = options.output .. "/" .. os.tmpname() - log("using temp name:",tmp) - local pd = io.popen(options.parser .. " > " .. tmp,"w") - pd:write(text) - pd:close() - local id = assert(io.open(tmp,"r")) - local ret = id:read("*a") - id:close() - assert(os.remove(tmp)) - return ret -end - -local function table_to_string(tbl) - --Collect all of our tables first, - --so that we don't break when we have recursive tables. - local tables = {} - local table_order = {} - local function tables_helper(t) - tables[t] = #table_order + 1 - table_order[#table_order + 1] = t - for k,v in pairs(t) do - if type(k) == "table" and not tables[k] then - tables_helper(k) - end - if type(v) == "table" and not tables[v] then - tables_helper(v) - end - end - end - tables_helper(tbl) - - --Get the string representation of an element - local errfun = function(e) error("Cannot format a " .. type(e)) end - local rep_map = { - --things we can format - number = function(e) - if e % 1 == 0 then return string.format("%d",e) - else return string.format("%f",e) end - end, - string = function(e) return string.format("%q",e) end, - boolean = function(e) return e and "true" or "false" end, - table = function(e) - assertf(tables[e] ~= nil,"Could not find dependency table %s",tostring(e)) - return string.format("table_%d",tables[e]) - end, - --things we can't format - ["function"] = errfun, - coroutine = errfun, - file = errfun, - userdata = errfun, - --nil can never happen - } - local sb = {} - --Create all the variables first, so that recursive tables will work - for n,_ in pairs(table_order) do - sb[#sb + 1] = string.format("local table_%d = {}",n) - end - --Go backwards through tables, since that should be the - --"dependency" order - for i = #table_order, 1, -1 do -- -1 is needed in case #table_order == 0 - local tstr = {} - local this_table = table_order[i] - for k,v in pairs(this_table) do - tstr[#tstr + 1] = string.format("table_%d[%s] = %s",i,rep_map[type(k)](k), rep_map[type(v)](v)) - end - sb[#sb + 1] = table.concat(tstr,"\n") - end - sb[#sb + 1] = "return table_1" - return table.concat(sb,"\n\n"); -end - ---io.open automatically creates parent directories -local oldopen = io.open -function io.open(path,mode) - if mode == "w" or mode == "w+" then - local path_so_far = "" - for folder in path:gmatch("([^/]+)/") do - path_so_far = path_so_far .. folder .. "/" - if lfs.attributes(path_so_far) == nil then - lfs.mkdir(path_so_far) - end - end - end - return oldopen(path,mode) -end - ---Create a cache directory -if not lfs.attributes(options.output .. "/cache") then - assert(lfs.mkdir(options.output .. "/cache")) - assert(lfs.mkdir(options.output .. "/cache/documents")) - assert(lfs.mkdir(options.output .. "/cache/files")) - assert(lfs.mkdir(options.output .. "/cache/chunks")) -end - ---Get the data for the reference documents -for dn, data in pairs(state.documents) do - local file_name = data.path:match(".*/([^.]+)%.[^.]+$") - local cachefile = string.format("%s/cache/documents/%s",options.output,file_name) - local out_attrs = lfs.attributes(cachefile) - if not out_attrs or out_attrs.modification < data.lastmod then - --we're out of date, update - local fd = assert(io.open(data.path,"r")) - local file_text = fd:read("*a") - fd:close() - local parsed_text = parse(file_text, data.path) - local od = assert(io.open(cachefile,"w")) - od:write(parsed_text) - od:close() - state.sections[file_name] = { - type = "reference", - data_file = cachefile - } - end -end - ---Openers and closers -local parse_between = { - ["/***"] = "*/", - ["--[[**"] = "]]" -} -for _,file in pairs(state.files) do - local cachefilename = string.format("%s/cache/files/%s",options.output,file.relpath) - local cacheattrs = lfs.attributes(cachefilename) - if (not cacheattrs) or cacheattrs.modification < file.lastmod then - local chunks = {} - local in_chunk = false - local line_num = 0 - local closer - local fd = assert(io.open(file.path,"r")) - for line in fd:lines() do - line_num = line_num + 1 - if parse_between[line] then - closer = parse_between[line] - table.insert(chunks,{}) - in_chunk = true - end - if in_chunk then - table.insert(chunks[#chunks],line) - end - if in_chunk and line == closer then - log("found end, chunks:",chunks) - chunks[#chunks] = { - text = chunks[#chunks], - file = file.path, - line = line_num, - relpath = file.relpath, - } - in_chunk = false - end - end - file.chunks = chunks - local cachefile = assert(io.open(cachefilename,"w")) - cachefile:write(table_to_string(chunks)) - cachefile:close() - else - file.chunks = loadfile(cachefilename)() - end -end - -for _,file in pairs(state.files) do - local function process_chunk(chunk) - assert(chunk.file and chunk.line, "Chunk without file or line num:") - local section = nil - local partname = nil - local sectiontype = nil - local short_desc - local desc = {} - --Parameters are { - -- name = "string", - -- type = "string", - -- optional = bool - -- optchain = bool - -- default = nil | "string" - --} - local ret = {} - for num,line in pairs(chunk.text) do - if num == 2 then - if line:match("^@.*") then - short_desc = "no short description provided" - else - short_desc = line - end - end - if num > 2 and type(desc) == "table" and not line:match("^@.*") then - table.insert(desc,line) - end - if line:match("^@") then - local command, rest = line:match("^@([^ ]+) (.*)") - if command == "function" then - sectiontype = "function" - if rest:find(":") then - local classname, functionname = rest:match("^([^:]+):([^%(]+)") - ret = { - --state.sections[classname] = state.sections[classname] or {type = "class"} - --state.sections[classname][functionname] = state.sections[classname][functionname] or { - type="method", - name = functionname, - path = {classname,functionname}, - short_desc = parse(short_desc), - line = chunk.line, - desc = parse(table.concat(desc,"\n")), - params = {}, - returns = {}, - file = chunk.relpath - } - section = classname - partname = functionname - else - local namespace, functionname - local c = rest:match("^([^.%(]+)%(") - if c then - namespace = "_G" - functionname = c - else - namespace, functionname = rest:match("([^.]+)%.([^%(]+)") - end - log("namespace:",namespace,"functionname:",functionname) - ret = { - --state.sections[namespace] = state.sections[namespace] or {type = "namespace"} - --state.sections[namespace][functionname] = state.sections[namespace][functionname] or { - type="function", - name = functionname, - short_desc = parse(short_desc), - path = {namespace,functionname}, - line = chunk.line, - desc = parse(table.concat(desc,"\n")), - params = {}, - returns = {}, - references = {}, - file = chunk.relpath - } - section = namespace - partname = functionname - end - elseif command == "tparam" or command == "tparam?" then - local parts = {} - local vartype, name, description - for word in rest:gmatch("([^ ]+)") do table.insert(parts,word) end - assertf(#parts >= 2,"@tparam at %s:%d requires at least a type and a variable name",chunk.file,chunk.line_num) - if parts[1] then - vartype = parts[1] - table.remove(parts,1) - end - if parts[1] then - name = parts[1] - table.remove(parts,1) - end - description = table.concat(parts,"\n") - - assertf(section and partname and sectiontype == "function","Tried to specify a tparam for something that wasn't a function at %s:%d",chunk.file,chunk.line) - local param = { - type = vartype, - name = name, - description = parse(description), - optional = command == "tparam?" - } - table.insert(ret.params,param) - --table.insert(state.sections[section][partname].params,param) - elseif command == "treturn" then - local vartype, description = rest:match("([^ ]+) (.*)") - assertf(section and partname and sectiontype == "function","Tried to specify a treturn for something that wasn't a function at %s:%d",chunk.file,chunk.line) - local tret = { - type = vartype, - description = description and parse(description) - } - table.insert(ret.returns,tret) - --table.insert(state.sections[section][partname].returns,{ - --type = vartype, - --description = description and parse(description) - --}) - elseif command == "field" then - local namespace, field, value - local c,v = rest:match("^([^ ]+) ([^ ]+)$") - if c then - namespace = "_G" - field = c - value = v - else - namespace, field, value = rest:match("([^.]+).([^ ]+) ?(.*)") - end - assertf(namespace and field and value, "Improperly defined @field at %s:%d, should be '@field module.field Description here'",chunk.file,chunk.line) - section = namespace - partname = field - state.sections[namespace] = state.sections[namespace] or {type="namespace"} - assertf(not state.sections[namespace][field],"2 or more definitions for %s.%s",namespace,field) - ret = { - --state.sections[namespace][field] = { - type = "field", - name = field, - path = {namespace,field}, - short_desc = parse(short_desc), - desc = desc and parse(table.concat(desc,"\n")), - line = chunk.line, - file = chunk.relpath - } - elseif command == "class" then - local classname = rest - section = classname - ret = { - --state.sections[classname] = state.sections[classname] or { - type = "class", - name = classname, - short_desc = parse(short_desc), - desc = parse(table.concat(desc,"\n")), - path = {classname} - } - partname = nil - elseif command == "inherits" then - assertf(ret and ret.type == "class","Don't know what is using @inherits at %s:%d, add a @class definition above it",chunk.file,chunk.line) - ret.inherits = ret.inherits or {} - table.insert(ret.inherits,rest) - --state.sections[section].inherits = state.sections[section].inherits or {} - --table.insert(state.sections[section].inherits,rest) - else - assertf(ret and ret.path[1] ,"Don't know where to put @%s, add a @function, @field or @table marker before it.",command) - if partname then - assertf(ret.path[2] ,"Don't know where to put @%s, add a @function, @field, or @table marker before it") - ret[command] = rest - --state.sections[section][partname][command] = rest - else - ret[command] = rest - --state.sections[section][command] = rest - end - end - end - end - return ret - end - local cachefilename = string.format("%s/cache/chunks/%s",options.output,file.relpath) - local cachefileattrs = lfs.attributes(cachefilename) - if (not cachefileattrs) or cachefileattrs.modification < file.lastmod then - for k,v in pairs(file.chunks) do - file.chunks[k] = process_chunk(v) - end - local cachefile = assert(io.open(cachefilename,"w")) - cachefile:write(table_to_string(file.chunks)) - cachefile:close() - else - file.chunks = loadfile(cachefilename)() - end -end - -for _,file in pairs(state.files) do - for _, chunk in pairs(file.chunks) do - assertf(chunk.path, "Chunk at %s:%d didn't have a path.", file.name, chunk.line) - local cursor = state.sections - for _, path_part in pairs(chunk.path) do - if not cursor[path_part] then - cursor[path_part] = {} - end - cursor = cursor[path_part] - end - for k,v in pairs(chunk) do - cursor[k] = v - end - end -end - ---Get the data for the reference documents -if options.index ~= nil then - local file_mod = lfs.attributes(options.index).modification - local file_name = options.index:match(".*/([^.]+)%.[^.]+$") - local cachefile = string.format("%s/cache/documents/%s",options.output,file_name) - local out_attrs = lfs.attributes(cachefile) - if not out_attrs or out_attrs.modification < file_mod then - --we're out of date, update - local fd = assert(io.open(options.index,"r")) - local file_text = fd:read("*a") - fd:close() - local parsed_text = parse(file_text, options.index) - local od = assert(io.open(cachefile,"w")) - od:write(parsed_text) - od:close() - end - state.sections[file_name] = { - type = "reference", - data_file = cachefile - } - state.index = state.sections[file_name] -end - -log("After copying everything in, state.sections is:") -log(state.sections) - -local headers = {} -for name,section in pairs(state.sections) do - log("section:",name,"type:",section.type,"data:") - local ust = "namespace" - for _,v in pairs(section) do - if v.type == "method" then - ust = "class" - end - end - section.type = section.type or ust - headers[section.type] = headers[section.type] or {} - section.name = name - table.insert(headers[section.type],section) -end -for _,group in pairs(headers) do - table.sort(group,function(a,b) - --print("sorting:",a.type,"against",b.type) - return a.type < b.type - end) -end ---print("headers:",headers) - -local nvfd = assert(io.open("navbar.etlua","r")) -local navbar = assert(et.compile(nvfd:read("*a"))) -nvfd:close() -local navbarhtml = navbar{headers = headers} -local pagefd = assert(io.open("page.etlua","r")) -local page = assert(et.compile(pagefd:read("*a"))) -pagefd:close() -local funcsignaturefd = assert(io.open("funcsignature.etlua","r")) -local funcsignature = assert(et.compile(funcsignaturefd:read("*a"))) -funcsignaturefd:close() -local sorted_headers = {} -for name,_ in pairs(headers) do - table.insert(sorted_headers,name) -end -table.sort(sorted_headers) -for _,name in pairs(sorted_headers) do - local header = headers[name] - for _,section in pairs(header) do - log("About to render section:",section.name) - log(section) - local pagehtml = assert(page{ - header = section, - navbar = navbarhtml, - options = options, - et = et, - funcsig = funcsignature, - }) - log("Done rendering pagehtml") - log("section name:",section.name) - log("section:",section) - local ofd = assert(io.open(options.output .. "/" .. section.name .. ".html","w")) - ofd:write(pagehtml) - ofd:close() - end -end - -if options.index ~= nil then - local indexfd = assert(io.open("index.etlua","r")) - local index = assert(et.compile(indexfd:read("*a"))) - indexfd:close() - log("state.index:",state.index) - local indextextfd = assert(io.open(state.index.data_file,"r")) - local indextext = indextextfd:read("*a") - indextextfd:close() - local indexhtml = index{ - navbar = navbarhtml, - options = options, - et = et, - text = indextext, - } - local ofd = assert(io.open(options.output .. "/index.html", "w")) - ofd:write(indexhtml) - ofd:close() -end - ---Copy style css -local css = assert(io.open("style.css","r")) -local css_out = assert(io.open(options.output .. "/style.css","w")) -for line in css:lines() do - css_out:write(line) -end -css:close() -css_out:close() - ---Generate html ---for header, sections in pairs(headers) do - - ----local ofd = assert(io.open(options.output .. "/" .. name,"w")) - --print("want to output section:",name,section.type) ---end - ---print(state) -- cgit v1.2.3-70-g09d2