1------------------------------------------------------------------------------- 2-- Name: genidocs.lua 3-- Purpose: This script generates docs from the wxLua interface files 4-- Author: John Labenski 5-- Created: 19/05/2006 6-- Copyright: John Labenski 7-- Licence: wxWidgets licence 8------------------------------------------------------------------------------- 9 10completeClassRefTable = nil -- a table of names that is a complete list of classes 11 -- from a library the wrapper are for 12 -- For wxWidgets this is taken from the alphabetical 13 -- list of classes in the wxWidgets reference manual 14 -- This is used to print if a class is wrapped or not. 15 16typedefTable = {} -- filled from the data cache files 17dataTypeTable = {} 18preprocConditionTable = {} 19 20colours = {} 21 22colours.class = "DD0000" -- red 23colours.member = "CC6600" -- orange 24colours.rename = "990099" -- dark pink 25colours.override = "BB0055" -- reddish pink 26colours.operator = "663300" -- brown 27 28colours.enum = "0066CC" -- blue 29colours.define = "006666" -- turquoise 30colours.event = "660033" -- purple 31colours.func = "AA0000" -- dark red 32 33colours.comment = "009900" -- green 34colours.blkcomment = "888888" -- grey 35 36colours.in_manual = "AAFFAA" -- for table showing classes 37colours.in_wxlua = "AAFFAA" 38colours.not_in_manual = "FFAAAA" 39colours.not_in_wxlua = "FFAAAA" 40 41-- ---------------------------------------------------------------------------- 42-- Dummy function that genwxbind.lua has and the XXX_rules.lua might use 43-- ---------------------------------------------------------------------------- 44 45function AllocDataType() end 46 47-- ---------------------------------------------------------------------------- 48-- For testing and choosing pleasing colors 49-- ---------------------------------------------------------------------------- 50 51function GenerateTestColours(fileTable) 52 table.insert(fileTable, "<h2>Colours used to denote types</h2>") 53 54 table.insert(fileTable, MakeColour("Comments - //", colours.comment).."<br>") 55 table.insert(fileTable, MakeColour("Block Comments - /* ... */", colours.blkcomment).."<br>") 56 57 table.insert(fileTable, MakeColour("Enums - enum", colours.enum).."<br>") 58 table.insert(fileTable, MakeColour("Defines - #define [_string] [_object] [_pointer]", colours.define).."<br>") 59 table.insert(fileTable, MakeColour("Events - %define_event", colours.event).."<br>") 60 table.insert(fileTable, MakeColour("Functions - %function", colours.func).."<br>") 61 62 table.insert(fileTable, MakeColour("Classes - class", colours.class).."<br>") 63 table.insert(fileTable, MakeColour("Class Members - %member", colours.member).."<br>") 64 table.insert(fileTable, MakeColour("Renamed Functions - %rename", colours.rename).."<br>") 65 table.insert(fileTable, MakeColour("Overridden Functions - %override", colours.override).."<br>") 66 table.insert(fileTable, MakeColour("Operator Functions - operator", colours.operator).."<br><br>") 67end 68 69 70-- ---------------------------------------------------------------------------- 71-- Make simple HTML tag items 72-- ---------------------------------------------------------------------------- 73 74-- color is "RRGGBB" in hex 75function MakeColour(str, color, size) 76 if size then 77 return "<font size=+"..size.." color=#"..color..">"..str.."</font>" 78 end 79 80 return "<font color=#"..color..">"..str.."</font>" 81end 82function MakeBold(str) 83 return "<b>"..str.."</b>" 84end 85function MakeItalic(str) 86 return "<i>"..str.."</i>" 87end 88function MakeLink(link_name, str) 89 --<a href="#papers">papers</a> 90 return "<a href=\"#"..link_name.."\">"..(str or link_name).."</a>" 91end 92function MakeTag(link_name, str) 93 --<a name="papers">Papers</a> 94 return "<a name=\""..link_name.."\">"..(str or link_name).."</a>" 95end 96 97-- convert invalid chars to something valid for use in <a name=... 98function MakeTagName(name) 99 local s = string.lower(name) 100 s = string.gsub(s, "%/", "_") 101 s = string.gsub(s, "% ", "_") 102 s = string.gsub(s, "%(", "_") 103 s = string.gsub(s, "%)", "_") 104 return s 105end 106 107-- replace any chars as necessary before adding our own code 108function MakeHTML(str) 109 local s = string.gsub(str, "&", "&") 110 s = string.gsub(s, ">", ">") 111 s = string.gsub(s, "<", "<") 112 return s 113end 114 115-- ---------------------------------------------------------------------------- 116-- Make the HTML footer 117-- ---------------------------------------------------------------------------- 118 119function GenerateFooter(fileTable) 120 table.insert(fileTable, "</body>") 121 table.insert(fileTable, "</html>") 122 123 return fileTable 124end 125 126-- ---------------------------------------------------------------------------- 127-- Make the Class reference HTML code 128-- ---------------------------------------------------------------------------- 129 130function GenerateClassReference(fileTable) 131 local names = {} 132 133 table.insert(fileTable, "<h2>Classes</h2>") 134 135 local allClasses = {} 136 137 if completeClassRefTable then 138 for k, v in pairs(completeClassRefTable) do 139 allClasses[k] = false -- for example ALL wxWidgets classes 140 end 141 end 142 for k, v in pairs(dataTypeTable) do 143 -- hack for special classes 144 if (v.ValueType == "class") or (v.ValueType == "struct") or (v.ValueType == "wx2lua") then 145 allClasses[k] = true -- the ones we wrap 146 end 147 end 148 149 for k, v in pairs(allClasses) do 150 table.insert(names, k) 151 end 152 table.sort(names) 153 154 --[[ 155 <table border="1"> 156 <tr> <td>row 1, cell 1</td> <td>row 1, cell 2</td> </tr> 157 <tr> <td>row 2, cell 1</td> <td>row 2, cell 2</td> </tr> 158 </table> 159 ]] 160 161 if completeClassRefTable then 162 163 table.insert(fileTable, "<table border=\"1\" summary=\"Table showing what wxWidgets C++ classes are wrapped by wxLua\">") 164 table.insert(fileTable, " <tr><th>Class Name</th> <th>"..completeClassRefColLabel.."</th> <th>Wrapped by wxLua</th> <th>Notes</th></tr>") 165 166 for n = 1, #names do 167 local cname = names[n] 168 169 table.insert(fileTable, "<tr>") 170 171 -- link to class in html file 172 if allClasses[cname] then 173 table.insert(fileTable, "<td>"..MakeLink(cname)) -- optional </td> 174 else 175 table.insert(fileTable, "<td>"..cname) 176 end 177 178 -- in "manual" or complete list of classes 179 if completeClassRefTable and completeClassRefTable[cname] then 180 table.insert(fileTable, "<td align=\"center\" bgcolor=#"..colours.in_manual..">X") 181 else 182 table.insert(fileTable, "<td bgcolor=#"..colours.not_in_manual.."> ") 183 end 184 185 -- wrapped by wxLua 186 if allClasses[cname] then 187 table.insert(fileTable, "<td align=\"center\" bgcolor=#"..colours.in_wxlua..">X") 188 else 189 table.insert(fileTable, "<td bgcolor=#"..colours.not_in_wxlua.."> ") 190 end 191 192 -- note about the class 193 if msgForClassInIndex and msgForClassInIndex[cname] then 194 table.insert(fileTable, "<td>"..msgForClassInIndex[cname]) 195 else 196 table.insert(fileTable, "<td> ") 197 end 198 199 -- table.insert(fileTable, "</tr>") -- optional </tr> 200 end 201 202 table.insert(fileTable, "</table><br>") 203 else 204 for n = 1, #names do 205 table.insert(fileTable, MakeLink(names[n]).."<br>") 206 end 207 end 208 209 table.insert(fileTable, "<br>") 210 211 return fileTable 212end 213 214-- ---------------------------------------------------------------------------- 215-- Make the Enum reference HTML code 216-- ---------------------------------------------------------------------------- 217 218function GenerateEnumReference(fileTable) 219 local names = {} 220 221 table.insert(fileTable, "<h2>Enums</h2>") 222 223 for k, v in pairs(dataTypeTable) do 224 if v.ValueType == "enum" then 225 table.insert(names, k) 226 end 227 end 228 table.sort(names) 229 for n = 1, #names do 230 table.insert(fileTable, MakeLink(names[n]).."<br>") 231 end 232 233 table.insert(fileTable, "<br>") 234 235 return fileTable 236end 237 238-- ---------------------------------------------------------------------------- 239-- Helper functions 240-- ---------------------------------------------------------------------------- 241 242local nameChars = {} -- valid chars for C variables for function names 243for n = string.byte("a"), string.byte("z") do nameChars[n] = true end 244for n = string.byte("A"), string.byte("Z") do nameChars[n] = true end 245for n = string.byte("0"), string.byte("9") do nameChars[n] = true end 246nameChars[string.byte("_")] = true 247nameChars[string.byte(":")] = true 248 249function GetPreviousWord(str, pos) 250 local start_pos = 0 251 local end_pos = 0 252 for n = pos, 0, -1 do 253 if not nameChars[string.byte(str, n)] then 254 if end_pos ~= 0 then 255 start_pos = n+1 256 break 257 end 258 elseif end_pos == 0 then 259 end_pos = n 260 end 261 end 262 return string.sub(str, start_pos, end_pos), start_pos 263end 264 265-- if the tag in the txt is before the ifbefore_pos then return true 266function TagIsBefore(txt, tag, ifbefore_pos) 267 local pos = string.find(txt, tag, 1, 1) 268 if pos and ((ifbefore_pos == nil) or (pos < ifbefore_pos)) then 269 return true 270 end 271 return false 272end 273 274 275function GetAllComments(str) 276 local function FindAllStrings(str, find_txt, tbl) 277 local s, e = string.find(str, find_txt, 1, 1) 278 while s do 279 table.insert(tbl, { ["s"] = s, ["e"] = e, ["txt"] = find_txt }) 280 s, e = string.find(str, find_txt, e+1, 1) 281 end 282 end 283 284 local t = {} 285 FindAllStrings(str, "//", t) 286 FindAllStrings(str, "/*", t) 287 FindAllStrings(str, "*/", t) 288 289 table.sort(t, function(t1, t2) return t1.s < t2.s end) 290 291 return t 292end 293 294-- ---------------------------------------------------------------------------- 295-- Read the .i files and convert them to HTML 296-- ---------------------------------------------------------------------------- 297 298function ReadInterfaceFiles(fileTable) 299 300 table.insert(fileTable, "<h2>Interface files</h2>") 301 302 for i = 1, #interface_fileTable do 303 for j = 1, #interface_fileTable[i].files do 304 local s = interface_fileTable[i].file_path..interface_fileTable[i].files[j] 305 table.insert(fileTable, MakeLink(MakeTagName(s), s).."<br>") 306 end 307 end 308 309 local strSp = string.byte(" ") 310 311 for i = 1, #interface_fileTable do 312 for j = 1, #interface_fileTable[i].files do 313 314 table.insert(fileTable, "<br><HR>\n") 315 local filename = interface_fileTable[i].file_path..interface_fileTable[i].files[j] 316 table.insert(fileTable, "<h2>"..MakeTag(MakeTagName(filename), filename).." - Lua table = '"..interface_fileTable[i].namespace.."'</h2>") 317 table.insert(fileTable, "<HR>\n") 318 319 local in_comment = false 320 local in_class = false 321 local in_enum = false 322 local brace_count = 0 323 local in_block = false 324 325 local line_n = 0 326 327 for line in io.lines(filename) do 328 line_n = line_n + 1 329 local cname = "" 330 local out_line = MakeHTML(line) 331 332 local comment_pos = string.find(line, "//", 1, 1) or 1E6 333 334 -- handle all comments in the order they appear 335 local t = GetAllComments(out_line) 336 for n = 1, #t do 337 if t[n].txt == "//" then 338 out_line = string.sub(out_line, 1, t[n].s-1)..MakeColour(string.sub(out_line, t[n].s), colours.comment) 339 break 340 elseif t[n].txt == "/*" then 341 if in_comment then print("ERROR mismatched /* */ in :", filename, line_n, line) end 342 343 in_comment = true 344 out_line = string.sub(out_line, 1, t[n].s-1).."<font color=#"..colours.blkcomment..">"..string.sub(out_line, t[n].s) 345 t = GetAllComments(out_line) 346 elseif t[n].txt == "*/" then 347 if not in_comment then print("ERROR mismatched /* */ in :", filename, line_n, line) end 348 in_comment = false 349 out_line = string.sub(out_line, 1, t[n].s+1).."</font>"..string.sub(out_line, t[n].s+2) 350 t = GetAllComments(out_line) 351 end 352 end 353 354 local class_pos, class_pos2 = string.find(line, "class ", 1, 1) 355 local enum_pos, enum_pos2 = string.find(line, "enum ", 1, 1) 356 357 if not class_pos then 358 class_pos, class_pos2 = string.find(line, "struct ", 1, 1) 359 end 360 361 local brace_open_pos = string.find(line, "{", 1, 1) 362 local brace_close_pos = string.find(line, "}", 1, 1) 363 364 if (brace_open_pos and (brace_open_pos < comment_pos)) then 365 brace_count = brace_count + 1 366 end 367 if (brace_close_pos and (brace_close_pos < comment_pos)) then 368 brace_count = brace_count - 1 369 end 370 371 if (brace_count < 0) then 372 print("ERROR - brace mismatch ", filename, line_n, "'"..line.."'") 373 end 374 375 if (class_pos and (class_pos < comment_pos)) or 376 (enum_pos and (enum_pos < comment_pos)) then 377 378 in_class = (class_pos ~= nil) 379 in_enum = (enum_pos ~= nil) 380 381 -- find this class not the base class 382 local colon = string.find(line, ":", 1, 1) 383 local start_pos = 0 384 if class_pos and colon then 385 cname, start_pos = GetPreviousWord(line, colon-1) 386 elseif comment_pos < 1E6 then 387 cname, start_pos = GetPreviousWord(line, comment_pos-1) 388 else 389 cname, start_pos = GetPreviousWord(line, string.len(line)) 390 end 391 392 if cname == "enum" then 393 out_line = string.sub(out_line, 1, start_pos-1)..cname..string.sub(out_line, start_pos+string.len(cname)) 394 else 395 out_line = string.sub(out_line, 1, start_pos-1)..MakeTag(cname)..string.sub(out_line, start_pos+string.len(cname)) 396 end 397 398 if class_pos then 399 out_line = MakeColour(out_line, colours.class, 1) 400 end 401 if enum_pos then 402 out_line = MakeColour(out_line, colours.enum, 1) 403 end 404 405 out_line = MakeBold(out_line) 406 else 407 -- priortize the colouring so we don't have to check for every single case 408 409 if TagIsBefore(line, "}", comment_pos) and (brace_count == 0) then 410 --out_line = MakeColour(out_line, colours.class) 411 --end_block = true 412 --class_pos = string.find(line, "}", 1, 1) 413 elseif TagIsBefore(line, "%member", comment_pos) then 414 out_line = MakeColour(out_line, colours.member) 415 elseif TagIsBefore(line, "%rename", comment_pos) then 416 out_line = MakeColour(out_line, colours.rename) 417 elseif TagIsBefore(line, "%override", 1E6) then 418 out_line = MakeColour(out_line, colours.override) 419 elseif TagIsBefore(line, "%event", comment_pos) then 420 out_line = MakeColour(out_line, colours.event) 421 elseif TagIsBefore(line, "#define", comment_pos) then 422 out_line = MakeColour(out_line, colours.define) 423 elseif TagIsBefore(line, "%function", comment_pos) then 424 out_line = MakeColour(out_line, colours.func) 425 end 426 end 427 428 local used = {} 429 used[cname] = true 430 431 for w in string.gmatch(line, "([%w_]+)") do 432 if ((string.len(cname) == 0) or (not string.find(w, cname, 1, 1))) and 433 (not used[w]) and 434 dataTypeTable[w] and (dataTypeTable[w].ValueType ~= "number") and 435 (dataTypeTable[w].ValueType ~= "wxtypedef") and (dataTypeTable[w].ValueType ~= "special") then 436 437 used[w] = true 438 439 -- replace the classname with a link, but not if it's part of a name 440 --out_line = string.gsub(out_line, w, MakeLink(w)) 441 local pat = "[ %&%*%(%)%{%}%[%]%+%-%=%<%>%.%-%+%|%/%,]" 442 -- need extra ending space to find words at end of line 443 local s, e = string.find(out_line.." ", w..pat, 1) 444 while s do 445 local link = MakeLink(w) 446 out_line = string.sub(out_line, 1, s-1)..link..string.sub(out_line, e) 447 s, e = string.find(out_line.." ", w..pat, s+string.len(link)) 448 end 449 end 450 end 451 452 -- italicize the %keywords 453 out_line = string.gsub(out_line, "(%%[%w_]+)", function(s) return "<i>"..s.."</i>" end) 454 455--[[ 456 -- alternate to blockquote, just force the spaces 457 local start_spaces = 0 458 459 for n = 1, string.len(out_line) do 460 if string.byte(out_line, n) == strSp then 461 start_spaces = start_spaces + 1 462 else 463 break 464 end 465 end 466 if start_spaces > 0 then 467 out_line = string.rep(" ", start_spaces)..string.sub(out_line, start_spaces) 468 end 469]] 470 471 local tail = "<br>" 472 473 local start_block = false 474 local end_block = false 475 476 if (in_class or in_enum) and (not in_block) and (brace_count > 0) then 477 start_block = true 478 elseif (in_class or in_enum) and in_block and (brace_count == 0) then 479 end_block = true 480 end 481 482 if start_block then 483 tail = "" -- don't add extra space since blockquote already gives a linebreak 484 485 in_block = true 486 487 if in_comment then 488 out_line = out_line.."</font>" 489 end 490 491 out_line = out_line.."\n<blockquote>" 492 493 -- need to restart font color after blockquote for "tidy" 494 if enum_pos then 495 out_line = out_line.."<font color=#"..colours.enum..">" 496 end 497 -- restart the block comment after blockquote, overrides enum colour 498 if in_comment then 499 out_line = out_line.."<font color=#"..colours.blkcomment..">" 500 end 501 elseif end_block then 502 -- need to restart font color after blockquote for "tidy" 503 504 in_block = false 505 506 if in_class then 507 in_class = false 508 out_line = "</blockquote>"..out_line -- MakeColour(out_line, colours.class) 509 end 510 if in_enum then 511 in_enum = false 512 out_line = "</font>\n</blockquote>"..out_line --MakeColour(out_line, colours.enum) 513 end 514 -- restart the block comment after blockquote 515 if in_comment then 516 out_line = "</font>"..out_line.."<font color=#"..colours.blkcomment..">" 517 end 518 519 end 520 521 table.insert(fileTable, out_line..tail) 522 end 523 end 524 end 525end 526 527-- ---------------------------------------------------------------------------- 528-- Load a file of the classes listed in the wxWidgets manual 529-- ---------------------------------------------------------------------------- 530 531function LoadCompleteClassRef(filePath) 532 for line in io.lines(filePath) do 533 -- only create this if necessary 534 if not completeClassRefTable then completeClassRefTable = {} end 535 536 for w in string.gmatch(line, "([%w_]+)") do -- strip spaces if any 537 completeClassRefTable[w] = true 538 end 539 end 540end 541 542-- --------------------------------------------------------------------------- 543-- Do the contents of the file match the strings in the fileData table? 544-- the table may contain any number of \n per index 545-- returns true for a match or false if not 546-- --------------------------------------------------------------------------- 547function FileDataIsTableData(filename, fileData) 548 local file_handle = io.open(filename) 549 if not file_handle then return false end -- ok if it doesn't exist 550 551 local f = file_handle:read("*a") 552 local is_same = (f == table.concat(fileData, "\n")) 553 io.close(file_handle) 554 return is_same 555end 556 557-- --------------------------------------------------------------------------- 558-- Write the contents of the table fileData (indexes 1.. are line numbers) 559-- to the filename, but only write to the file if FileDataIsTableData returns 560-- false. If overwrite_always is true then always overwrite the file. 561-- returns true if the file was overwritten 562-- --------------------------------------------------------------------------- 563function WriteTableToFile(filename, fileData, overwrite_always) 564 assert(filename and fileData, "Invalid filename or fileData in WriteTableToFile") 565 566 if (not overwrite_always) and FileDataIsTableData(filename, fileData) then 567 print("No changes to file : '"..filename.."'") 568 return false 569 end 570 571 print("Updating file : '"..filename.."'") 572 573 local outfile = io.open(filename, "w+") 574 if not outfile then 575 print("Unable to open file for writing '"..filename.."'.") 576 return 577 end 578 579 outfile:write(table.concat(fileData, "\n")) 580 581 outfile:flush() 582 outfile:close() 583 return true 584end 585 586-- ---------------------------------------------------------------------------- 587-- main() 588-- ---------------------------------------------------------------------------- 589 590function main() 591 -- load rules file 592 if not rulesFilename then 593 print("Warning: No rules filename set!") 594 rulesFilename = "" 595 end 596 597 local rules = loadfile("./"..rulesFilename) 598 if rules then 599 rules() 600 print("Loaded rules file: "..rulesFilename) 601 else 602 print("ERROR : unable to load rules file: "..rulesFilename) 603 print("This could mean that either the file cannot be found or there is an error in it.") 604 print("The rules file should be valid lua code, try running it with lua directly.") 605 end 606 607 for n = 1, #interface_fileTable do 608 local datatypes_filename = interface_fileTable[n].file_path..interface_fileTable[n].datatypes_filename 609 local datatypes_file = loadfile(datatypes_filename) 610 if datatypes_file then 611 datatypes_file() 612 print("Loaded data types file: "..datatypes_filename) 613 else 614 print("WARNING: unable to load data types file: "..datatypes_filename) 615 end 616 end 617 618 dataTypeTable["wxString"].ValueType = "class" -- FIXME hack for wxString DefType as "special" 619 620 if completeClassRefFileTable then 621 for n = 1, #completeClassRefFileTable do 622 LoadCompleteClassRef(completeClassRefFileTable[n]) 623 print("Loaded complete class reference : "..completeClassRefFileTable[n]) 624 end 625 end 626 627 fileTable = { htmlHeader } 628 GenerateClassReference(fileTable) 629 table.insert(fileTable, "<HR>") 630 GenerateEnumReference(fileTable) 631 table.insert(fileTable, "<HR>") 632 GenerateTestColours(fileTable) 633 table.insert(fileTable, "<HR>") 634 ReadInterfaceFiles(fileTable) 635 GenerateFooter(fileTable) 636 637 638 WriteTableToFile(output_filename , fileTable) 639 --for n = 1, #fileTable do print(fileTable[n]) end 640 641end 642 643main() 644