------------------------------------------------------------------------------- -- Name: genidocs.lua -- Purpose: This script generates docs from the wxLua interface files -- Author: John Labenski -- Created: 19/05/2006 -- Copyright: John Labenski -- Licence: wxWidgets licence ------------------------------------------------------------------------------- completeClassRefTable = nil -- a table of names that is a complete list of classes -- from a library the wrapper are for -- For wxWidgets this is taken from the alphabetical -- list of classes in the wxWidgets reference manual -- This is used to print if a class is wrapped or not. typedefTable = {} -- filled from the data cache files dataTypeTable = {} preprocConditionTable = {} colours = {} colours.class = "DD0000" -- red colours.member = "CC6600" -- orange colours.rename = "990099" -- dark pink colours.override = "BB0055" -- reddish pink colours.operator = "663300" -- brown colours.enum = "0066CC" -- blue colours.define = "006666" -- turquoise colours.event = "660033" -- purple colours.func = "AA0000" -- dark red colours.comment = "009900" -- green colours.blkcomment = "888888" -- grey colours.in_manual = "AAFFAA" -- for table showing classes colours.in_wxlua = "AAFFAA" colours.not_in_manual = "FFAAAA" colours.not_in_wxlua = "FFAAAA" -- ---------------------------------------------------------------------------- -- Dummy function that genwxbind.lua has and the XXX_rules.lua might use -- ---------------------------------------------------------------------------- function AllocDataType() end -- ---------------------------------------------------------------------------- -- For testing and choosing pleasing colors -- ---------------------------------------------------------------------------- function GenerateTestColours(fileTable) table.insert(fileTable, "

Colours used to denote types

") table.insert(fileTable, MakeColour("Comments - //", colours.comment).."
") table.insert(fileTable, MakeColour("Block Comments - /* ... */", colours.blkcomment).."
") table.insert(fileTable, MakeColour("Enums - %enum", colours.enum).."
") table.insert(fileTable, MakeColour("Defines - %define [_string] [_object] [_pointer]", colours.define).."
") table.insert(fileTable, MakeColour("Events - %define_event", colours.event).."
") table.insert(fileTable, MakeColour("Functions - %function", colours.func).."
") table.insert(fileTable, MakeColour("Classes - %class", colours.class).."
") table.insert(fileTable, MakeColour("Class Members - %member", colours.member).."
") table.insert(fileTable, MakeColour("Renamed Functions - %rename", colours.rename).."
") table.insert(fileTable, MakeColour("Overridden Functions - %override", colours.override).."
") table.insert(fileTable, MakeColour("Operator Functions - %operator", colours.operator).."

") end -- ---------------------------------------------------------------------------- -- Make simple HTML tag items -- ---------------------------------------------------------------------------- -- color is "RRGGBB" in hex function MakeColour(str, color, size) if size then return ""..str.."" end return ""..str.."" end function MakeBold(str) return ""..str.."" end function MakeItalic(str) return ""..str.."" end function MakeLink(link_name, str) --papers return ""..(str or link_name).."" end function MakeTag(link_name, str) --Papers return ""..(str or link_name).."" end -- convert invalid chars to something valid for use in ", ">") s = string.gsub(s, "<", "<") return s end -- ---------------------------------------------------------------------------- -- Make the HTML footer -- ---------------------------------------------------------------------------- function GenerateFooter(fileTable) table.insert(fileTable, "") table.insert(fileTable, "") return fileTable end -- ---------------------------------------------------------------------------- -- Make the Class reference HTML code -- ---------------------------------------------------------------------------- function GenerateClassReference(fileTable) local names = {} table.insert(fileTable, "

Classes

") local allClasses = {} if completeClassRefTable then for k, v in pairs(completeClassRefTable) do allClasses[k] = false -- for example ALL wxWidgets classes end end for k, v in pairs(dataTypeTable) do -- hack for special classes if (v.ValueType == "class") or (v.ValueType == "struct") or (v.ValueType == "wx2lua") then allClasses[k] = true -- the ones we wrap end end for k, v in pairs(allClasses) do table.insert(names, k) end table.sort(names) --[[
row 1, cell 1 row 1, cell 2
row 2, cell 1 row 2, cell 2
]] if completeClassRefTable then table.insert(fileTable, "") table.insert(fileTable, " ") for n = 1, #names do local cname = names[n] table.insert(fileTable, "") -- link to class in html file if allClasses[cname] then table.insert(fileTable, " else table.insert(fileTable, "") -- optional end table.insert(fileTable, "
Class Name "..completeClassRefColLabel.." Wrapped by wxLua Notes
"..MakeLink(cname)) -- optional "..cname) end -- in "manual" or complete list of classes if completeClassRefTable and completeClassRefTable[cname] then table.insert(fileTable, "X") else table.insert(fileTable, " ") end -- wrapped by wxLua if allClasses[cname] then table.insert(fileTable, "X") else table.insert(fileTable, " ") end -- note about the class if msgForClassInIndex and msgForClassInIndex[cname] then table.insert(fileTable, ""..msgForClassInIndex[cname]) else table.insert(fileTable, " ") end -- table.insert(fileTable, "

") else for n = 1, #names do table.insert(fileTable, MakeLink(names[n]).."
") end end table.insert(fileTable, "
") return fileTable end -- ---------------------------------------------------------------------------- -- Make the Enum reference HTML code -- ---------------------------------------------------------------------------- function GenerateEnumReference(fileTable) local names = {} table.insert(fileTable, "

Enums

") for k, v in pairs(dataTypeTable) do if v.ValueType == "enum" then table.insert(names, k) end end table.sort(names) for n = 1, #names do table.insert(fileTable, MakeLink(names[n]).."
") end table.insert(fileTable, "
") return fileTable end -- ---------------------------------------------------------------------------- -- Helper functions -- ---------------------------------------------------------------------------- local nameChars = {} -- valid chars for C variables for function names for n = string.byte("a"), string.byte("z") do nameChars[n] = true end for n = string.byte("A"), string.byte("Z") do nameChars[n] = true end for n = string.byte("0"), string.byte("9") do nameChars[n] = true end nameChars[string.byte("_")] = true nameChars[string.byte(":")] = true function GetPreviousWord(str, pos) local start_pos = 0 local end_pos = 0 for n = pos, 0, -1 do if not nameChars[string.byte(str, n)] then if end_pos ~= 0 then start_pos = n+1 break end elseif end_pos == 0 then end_pos = n end end return string.sub(str, start_pos, end_pos), start_pos end -- if the tag in the txt is before the ifbefore_pos then return true function TagIsBefore(txt, tag, ifbefore_pos) local pos = string.find(txt, tag, 1, 1) if pos and ((ifbefore_pos == nil) or (pos < ifbefore_pos)) then return true end return false end function GetAllComments(str) local function FindAllStrings(str, find_txt, tbl) local s, e = string.find(str, find_txt, 1, 1) while s do table.insert(tbl, { ["s"] = s, ["e"] = e, ["txt"] = find_txt }) s, e = string.find(str, find_txt, e+1, 1) end end local t = {} FindAllStrings(str, "//", t) FindAllStrings(str, "/*", t) FindAllStrings(str, "*/", t) table.sort(t, function(t1, t2) return t1.s < t2.s end) return t end -- ---------------------------------------------------------------------------- -- Read the .i files and convert them to HTML -- ---------------------------------------------------------------------------- function ReadInterfaceFiles(fileTable) table.insert(fileTable, "

Interface files

") for i = 1, #interface_fileTable do for j = 1, #interface_fileTable[i].files do local s = interface_fileTable[i].file_path..interface_fileTable[i].files[j] table.insert(fileTable, MakeLink(MakeTagName(s), s).."
") end end local strSp = string.byte(" ") for i = 1, #interface_fileTable do for j = 1, #interface_fileTable[i].files do table.insert(fileTable, "

\n") local filename = interface_fileTable[i].file_path..interface_fileTable[i].files[j] table.insert(fileTable, "

"..MakeTag(MakeTagName(filename), filename).." - Lua table = '"..interface_fileTable[i].namespace.."'

") table.insert(fileTable, "
\n") local in_blk_comment = false local line_n = 0 for line in io.lines(filename) do line_n = line_n + 1 local cname = "" local out_line = MakeHTML(line) local comment_pos = string.find(line, "//", 1, 1) or 1E6 -- handle all comments in the order they appear local t = GetAllComments(out_line) for n = 1, #t do if t[n].txt == "//" then out_line = string.sub(out_line, 1, t[n].s-1)..MakeColour(string.sub(out_line, t[n].s), colours.comment) break elseif t[n].txt == "/*" then if in_blk_comment then print("ERROR mismatched /* */ in :", filename, line_n, line) end in_blk_comment = true out_line = string.sub(out_line, 1, t[n].s-1)..""..string.sub(out_line, t[n].s) t = GetAllComments(out_line) elseif t[n].txt == "*/" then if not in_blk_comment then print("ERROR mismatched /* */ in :", filename, line_n, line) end in_blk_comment = false out_line = string.sub(out_line, 1, t[n].s+1)..""..string.sub(out_line, t[n].s+2) t = GetAllComments(out_line) end end local class_pos, class_pos2 = string.find(line, "%class", 1, 1) local enum_pos, enum_pos2 = string.find(line, "%enum", 1, 1) if not class_pos then class_pos, class_pos2 = string.find(line, "%struct", 1, 1) end local start_block = nil local end_block = nil if (class_pos and (class_pos < comment_pos)) or (enum_pos and (enum_pos < comment_pos)) then start_block = true -- find this class not the base class local comma = string.find(line, ",", 1, 1) local start_pos = 0 if class_pos and comma then cname, start_pos = GetPreviousWord(line, comma-1) elseif comment_pos < 1E6 then cname, start_pos = GetPreviousWord(line, comment_pos-1) else cname, start_pos = GetPreviousWord(line, string.len(line)) end if cname == "enum" then out_line = string.sub(out_line, 1, start_pos-1)..cname..string.sub(out_line, start_pos+string.len(cname)) else out_line = string.sub(out_line, 1, start_pos-1)..MakeTag(cname)..string.sub(out_line, start_pos+string.len(cname)) end if class_pos then out_line = MakeColour(out_line, colours.class, 1) end if enum_pos then out_line = MakeColour(out_line, colours.enum, 1) end out_line = MakeBold(out_line) else -- priortize the colouring so we don't have to check for every single case if TagIsBefore(line, "%endclass", comment_pos) then out_line = MakeColour(out_line, colours.class) end_block = true class_pos = string.find(line, "%endclass", 1, 1) elseif TagIsBefore(line, "%endstruct", comment_pos) then out_line = MakeColour(out_line, colours.class) end_block = true class_pos = string.find(line, "%endstruct", 1, 1) elseif TagIsBefore(line, "%endenum", comment_pos) then end_block = true enum_pos = string.find(line, "%endenum", 1, 1) elseif TagIsBefore(line, "%member", comment_pos) then out_line = MakeColour(out_line, colours.member) elseif TagIsBefore(line, "%rename", comment_pos) then out_line = MakeColour(out_line, colours.rename) elseif TagIsBefore(line, "%override", 1E6) then out_line = MakeColour(out_line, colours.override) elseif TagIsBefore(line, "%event", comment_pos) then out_line = MakeColour(out_line, colours.event) elseif TagIsBefore(line, "%define", comment_pos) then out_line = MakeColour(out_line, colours.define) elseif TagIsBefore(line, "%function", comment_pos) then out_line = MakeColour(out_line, colours.func) end end local used = {} used[cname] = true for w in string.gmatch(line, "([%w_]+)") do if ((string.len(cname) == 0) or (not string.find(w, cname, 1, 1))) and (not used[w]) and dataTypeTable[w] and (dataTypeTable[w].ValueType ~= "number") and (dataTypeTable[w].ValueType ~= "wxtypedef") and (dataTypeTable[w].ValueType ~= "special") then used[w] = true -- replace the classname with a link, but not if it's part of a name --out_line = string.gsub(out_line, w, MakeLink(w)) local pat = "[ %&%*%(%)%{%}%[%]%+%-%=%<%>%.%-%+%|%/%,]" -- need extra ending space to find words at end of line local s, e = string.find(out_line.." ", w..pat, 1) while s do local link = MakeLink(w) out_line = string.sub(out_line, 1, s-1)..link..string.sub(out_line, e) s, e = string.find(out_line.." ", w..pat, s+string.len(link)) end end end -- italicize the %keywords out_line = string.gsub(out_line, "(%%[%w_]+)", function(s) return ""..s.."" end) --[[ -- alternate to blockquote, just force the spaces local start_spaces = 0 for n = 1, string.len(out_line) do if string.byte(out_line, n) == strSp then start_spaces = start_spaces + 1 else break end end if start_spaces > 0 then out_line = string.rep(" ", start_spaces)..string.sub(out_line, start_spaces) end ]] local tail = "
" if start_block then tail = "" -- don't add extra space since blockquote already gives a linebreak if in_blk_comment then out_line = out_line.."" end out_line = out_line.."\n
" -- need to restart font color after blockquote for "tidy" if enum_pos then out_line = out_line.."" end -- restart the block comment after blockquote, overrides enum colour if in_blk_comment then out_line = out_line.."" end elseif end_block then -- need to restart font color after blockquote for "tidy" if class_pos then out_line = "
"..MakeColour(out_line, colours.class) end if enum_pos then out_line = "\n"..MakeColour(out_line, colours.enum) end -- restart the block comment after blockquote if in_blk_comment then out_line = ""..out_line.."" end end table.insert(fileTable, out_line..tail) end end end end -- ---------------------------------------------------------------------------- -- Load a file of the classes listed in the wxWidgets manual -- ---------------------------------------------------------------------------- function LoadCompleteClassRef(filePath) for line in io.lines(filePath) do -- only create this if necessary if not completeClassRefTable then completeClassRefTable = {} end for w in string.gmatch(line, "([%w_]+)") do -- strip spaces if any completeClassRefTable[w] = true end end end -- --------------------------------------------------------------------------- -- Do the contents of the file match the strings in the fileData table? -- the table may contain any number of \n per index -- returns true for a match or false if not -- --------------------------------------------------------------------------- function FileDataIsTableData(filename, fileData) local file_handle = io.open(filename) if not file_handle then return false end -- ok if it doesn't exist local f = file_handle:read("*a") local is_same = (f == table.concat(fileData, "\n")) io.close(file_handle) return is_same end -- --------------------------------------------------------------------------- -- Write the contents of the table fileData (indexes 1.. are line numbers) -- to the filename, but only write to the file if FileDataIsTableData returns -- false. If overwrite_always is true then always overwrite the file. -- returns true if the file was overwritten -- --------------------------------------------------------------------------- function WriteTableToFile(filename, fileData, overwrite_always) assert(filename and fileData, "Invalid filename or fileData in WriteTableToFile") if (not overwrite_always) and FileDataIsTableData(filename, fileData) then print("No changes to file : '"..filename.."'") return false end print("Updating file : '"..filename.."'") local outfile = io.open(filename, "w+") if not outfile then print("Unable to open file for writing '"..filename.."'.") return end outfile:write(table.concat(fileData, "\n")) outfile:flush() outfile:close() return true end -- ---------------------------------------------------------------------------- -- main() -- ---------------------------------------------------------------------------- function main() -- load rules file if not rulesFilename then print("Warning: No rules filename set!") rulesFilename = "" end local rules = loadfile("./"..rulesFilename) if rules then rules() print("Loaded rules file: "..rulesFilename) else print("ERROR : unable to load rules file: "..rulesFilename) print("This could mean that either the file cannot be found or there is an error in it.") print("The rules file should be valid lua code, try running it with lua directly.") end for n = 1, #interface_fileTable do local datatypes_filename = interface_fileTable[n].file_path..interface_fileTable[n].datatypes_filename local datatypes_file = loadfile(datatypes_filename) if datatypes_file then datatypes_file() print("Loaded data types file: "..datatypes_filename) else print("WARNING: unable to load data types file: "..datatypes_filename) end end dataTypeTable["wxString"].ValueType = "class" -- FIXME hack for wxString DefType as "special" if completeClassRefFileTable then for n = 1, #completeClassRefFileTable do LoadCompleteClassRef(completeClassRefFileTable[n]) print("Loaded complete class reference : "..completeClassRefFileTable[n]) end end fileTable = { htmlHeader } GenerateClassReference(fileTable) table.insert(fileTable, "
") GenerateEnumReference(fileTable) table.insert(fileTable, "
") GenerateTestColours(fileTable) table.insert(fileTable, "
") ReadInterfaceFiles(fileTable) GenerateFooter(fileTable) WriteTableToFile(output_filename , fileTable) --for n = 1, #fileTable do print(fileTable[n]) end end main()