diff options
author | Karl Berry <karl@freefriends.org> | 2012-07-27 23:39:36 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2012-07-27 23:39:36 +0000 |
commit | 667c94d4c814daec165a23fb251a7c38f37e202d (patch) | |
tree | afb5f123bcb70a169633701290e90e4049c48bf7 /Master/texmf-dist/scripts/lua2dox/lua2dox.lua | |
parent | e5a3f502a0f8618b02616ad91af5595471381723 (diff) |
new support package lua2dox (20jul12)
git-svn-id: svn://tug.org/texlive/trunk@27205 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts/lua2dox/lua2dox.lua')
-rwxr-xr-x | Master/texmf-dist/scripts/lua2dox/lua2dox.lua | 618 |
1 files changed, 618 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/lua2dox/lua2dox.lua b/Master/texmf-dist/scripts/lua2dox/lua2dox.lua new file mode 100755 index 00000000000..9249baac9cd --- /dev/null +++ b/Master/texmf-dist/scripts/lua2dox/lua2dox.lua @@ -0,0 +1,618 @@ +--[[-------------------------------------------------------------------------- + -- Copyright (C) 2012 by Simon Dales -- + -- simon@purrsoft.co.uk -- + -- -- + -- This program is free software; you can redistribute it and/or modify -- + -- it under the terms of the GNU General Public License as published by -- + -- the Free Software Foundation; either version 2 of the License, or -- + -- (at your option) any later version. -- + -- -- + -- This program is distributed in the hope that it will be useful, -- + -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- + -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- + -- GNU General Public License for more details. -- + -- -- + -- You should have received a copy of the GNU General Public License -- + -- along with this program; if not, write to the -- + -- Free Software Foundation, Inc., -- + -- 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -- +----------------------------------------------------------------------------]] + +--[[! + \file + \brief a hack lua2dox converter + ]] + +--[[! + \mainpage + + + A hack lua2dox converter + Version 0.1 + + This lets us make Doxygen output some documentation to let + us develop this code. + + It is partially cribbed from the functionality of lua2dox + (http://search.cpan.org/~alec/Doxygen-Lua-0.02/lib/Doxygen/Lua.pm). + Found on CPAN when looking for something else; kinda handy. + + Improved from lua2dox to make the doxygen output more friendly. + Also it runs faster in lua rather than Perl. + + Because this Perl based system is called "lua2dox"., I have decided to add ".lua" to the name + to keep the two separate. + + 0. Ensure doxygen is installed on your system and that you are familiar with its use. + Best is to try to make and document some simple C/C++/PHP to see what it produces. + + 1. Run "lua2dox_lua -g" to create a default Doxyfile. + + Then alter it to let it recognise lua. Add the two following lines: + + FILE_PATTERNS = *.lua + + FILTER_PATTERNS = *.lua=lua2dox_filter + + Either add them to the end or find the appropriate entry in Doxyfile. + + 2. When Doxyfile is edited run as "lua2dox_lua" + + When reading source with classes multiple passes are needed. + Each pass generates a list of member functions (as a file) that were found on this pass. + This list is read in on the next pass. + If the class+methods haven't changed this time then you only need to run it once, else run twice. + + The core function reads the input file (filename or stdin) and outputs some pseudo C-ish language. + It only has to be good enough for doxygen to see it as legal. + Therefore our lua interpreter is fairly limited, but "good enough". + + One limitation is that each line is treated separately (except for long comments). + The implication is that class and function declarations must be on the same line. + Some functions can have their parameter lists extended over multiple lines to make it look neat. + Managing this where there are also some comments is a bit more coding than I want to do at this stage, + so it will probably not document accurately if we do do this. + + However I have put in a hack that will insert the "missing" close paren. + The effect is that you will get the function documented, but not with the parameter list you might expect. + + Installation: + + Here for linux or unix-like, for any other OS you need to refer to other documentation. + + This file is "lua2dox.lua". It gets called by "lua2dox_lua". + Somewhere in your path (e.g. "~/bin" or "/usr/local/bin") put two links to "lua2dox_lua". + Names to use are "lua2dox_lua" and "lua2dox_filter". + + Call it as "lua2dox_lua" and the filter that gets called by doxygen is "lua2dox_filter". + + ]] + +-- we won't use our library code, so this becomes more portable + +local TConfig_config={ + ['LUA2DOX_COMMENTARY_FILE_IN']='./killme_lua2dox.methods' + ,['LUA2DOX_COMMENTARY_FILE_OUT']='./killme_lua2dox.methods.out' + } + +--! \brief Gets a config val +local function TConfig_get(Key,Default) + local val = TConfig_config[Key] + if not val then + val = Default + end + return val +end + +--! \brief sets a config val +local function TConfig_set(Key,Val) + TConfig_config[Key] = Val +end + +--! \brief write to stdout +--! +--! writes Str (if not nil) +local function TIO_write(Str) + if (Str) then + io.write(Str) + end +end + +--! \brief write to stdout +--! +--! writelns Str (if not nil) and then an eoln. +local function TIO_writeln(Str) + if (Str) then + io.write(Str) + end + io.write("\n") +end + +--! \brief show error to stdout +--! +--! writelns Str (if not nil) and then an eoln. +local function TIO_showError(Err,Str) + local err = Err + if not err then + err = 1 + end + + TIO_write('Error (' .. err .. '):') + TIO_writeln(Str) + return err +end + +--! \brief run system command +local function TOS_system(Cmd) + local errno,str + local rtn = os.execute(Cmd) + if not (rtn==0) then + errno = rtn + str = 'an error occured' + end + return errno,str +end + +--! \brief does file exist? +local function TOS_fileExists(Filename) + local fh = io.open(Filename,'r') + if fh~=nil then + fh:close() + return true + end + return false +end + +--! \brief get the current time +local function TClock_GetTimeNow() + if os.gettimeofday then + return os.gettimeofday() + else + return os.time() + end +end + +--! \brief get a timestamp +--! +--! not strictly necessary here but lets us put a timestamp on the end of the output stream. +--! Note that doxygen won't read this, and being off the end of the true file length (num lines), +--! it will have no effect. +--! However it lets us check the output file tail when debugging. +--! +local function TClock_getTimeStamp() + local now = TClock_GetTimeNow() + local fraction_secs = now - math.floor(now) + return os.date('%c %Z',now) .. ':' .. fraction_secs +end + +--! \brief trims a string from both ends +local function TString_trim(Str) + return Str:match("^%s*(.-)%s*$") +end + +--! \brief split a string +--! +--! \param Str +--! \param Pattern +--! \returns table of string fragments +local function TString_split(Str, Pattern) + local splitStr = {} + local fpat = "(.-)" .. Pattern + local last_end = 1 + local str, e, cap = string.find(Str,fpat, 1) + while str do + if str ~= 1 or cap ~= "" then + table.insert(splitStr,cap) + end + last_end = e+1 + str, e, cap = string.find(Str,fpat, last_end) + end + if last_end <= #Str then + cap = string.sub(Str,last_end) + table.insert(splitStr, cap) + end + return splitStr +end + +--! \brief trim comment off end of string +--! +--! If the string has a comment on the end, this trims it off. +--! +local function TString_removeCommentFromLine(Line) + local pos_comment = string.find(Line,'%-%-') + if pos_comment then + Line = string.sub(Line,1,pos_comment-1) + end + return Line +end + +local TClassList_methods = {} +--! \brief get methods +local function TClassList_method_get(Klass) + return TClassList_methods[Klass] +end + +--! \brief add a method to list of known methods +local function TClassList_method_add(Klass,Method) + local classRec = TClassList_methods[Klass] + if not classRec then + TClassList_methods[Klass] = {} + classRec = TClassList_methods[Klass] + end + table.insert(classRec,Method) +end + + +local TCommentary_fh + +--! \brief write to output file +local function TCommentary_writeln(Str) + if TCommentary_fh then + TCommentary_fh:write(Str .. '\n') + end +end + +local TCommentary_fileID +--! \brief open the commentary save file +local function TCommentary_open(Filename,InputLuaFilename) + TCommentary_fileID = '"' .. InputLuaFilename .. '" : ' .. TClock_getTimeStamp() + TCommentary_fh = io.open(Filename,'a+') + if TCommentary_fh then + TCommentary_writeln('// opened:' .. TCommentary_fileID) + end +end + +--! \brief close the methods save file +local function TCommentary_close() + if TCommentary_fh then + TCommentary_writeln('// closed: ' .. TCommentary_fileID) + TCommentary_fh:close() + TCommentary_fh = nil + end +end + +--! \brief read stuff from save file +local function TCommentary_readFileContents(Filename) + if TOS_fileExists(Filename) then + local klass,method,dot + local cmd,colon + local k,v,equals + for line in io.lines(Filename) do + if string.sub(line,1,2)=='//' then + -- it's a comment + else + colon = string.find(line,':') + if colon then + cmd = string.sub(line,1,colon) + line = string.sub(line,colon+1) + else + cmd = nil + end + if (cmd == 'method:') then + dot = string.find(line,'%.') + klass = string.sub(line,1,dot-1) + method = string.sub(line,dot+1) + TClassList_method_add(klass,method) + elseif(cmd == 'set:') then + equals = string.find(line,'=') + if equals then + k = string.sub(line,1,equals-1) + v = string.sub(line,equals+1) + TConfig_set(k,v) + else + TConfig_set(line,true) + end + else -- ignore + TIO_write('/* bad command:"' .. line .. '" */') + end + end + end + else + TIO_write('/* file "' .. Filename .. '" don\'t exist */') + end +end + +--! \brief method to save file +local function TCommentary_addMethod(Klass,Method) + if TCommentary_fh then + if Klass and Method then + if string.find(Klass,'%.') or string.find(Method,'%.') then + -- iffy, so we discard it + else + TCommentary_writeln('method:' .. Klass .. '.' .. Method) + end + end + end +end + +--! \brief output line to stream +--! +--! Wraps IO_writeln() +local function TIO_out2stream(Line) + TIO_writeln(Line) +end + +--! \brief suppress line +local function TIO_out2stream_commented(Line,Prefix,Suffix) + local line = Line + if (not Prefix) then + Prefix='' + end + line = Prefix .. ':' .. line + + if (Suffix) then + line = line .. Suffix + end + TIO_out2stream('// D' .. line) +end + + +TCommandline_argv = {} +--! \brief setup/parse the commandline +local function TCommandline_setup() + local argv1 =arg[1] + if not argv1 then + argv1 = 'base' + end + TCommandline_argv_appname = argv1 + + local i=2 + local argvi=1 + while (argvi) do + argvi = arg[i] + if argvi then + TCommandline_argv[i-1] = argvi + i = i + 1 + end + end +end +--! setup the commandline now +TCommandline_setup() + +--! \brief get commandline args +local function TCommandline_getargv() + return TCommandline_argv +end + +--! \brief get appname +local function TApp_get_appname() + return TCommandline_argv_appname +end + +--! \brief hack converter from lua to a pseudoC-ish language for doxygen +--! +--! This is a hack to make lua readable to doxygen. +--! +--! It works well enough to document functions/methods and classes, but not assignments. +--! Our pseudo-C gets confused if we allow assginments to be shown. +--! Because these are less interesting than class/functions/methods I have decided to +--! live with this limitation. +--! +local function TApp_lua2dox(FileContents) + local err + local lines = FileContents + local maxi=#lines + + local i = 1 + local line,head + while not err and (i<=maxi) do + line = TString_trim(lines[i]) + if #line==0 then + TIO_out2stream() + elseif string.sub(line,1,2)=='--' then + -- it's a comment of some kind + if string.sub(line,1,3)=='--!' then + -- it's a magic comment + TIO_out2stream('//!' .. string.sub(line,4)) + elseif string.sub(line,1,4)=='--[[' then + -- it's a multiline comment + -- read lines to end of comment + local hitend + local comment = '' + line = string.sub(line,5) --.. sep + while not err and (i<maxi) and not hitend do + comment = comment .. line .. '\n' + line = lines[i+1] + --if (string.sub(TString_trim(line),1,2)==']]') then + if (string.find(line,'\]\]')) then + local pos_close = string.find(line,'\]\]') + comment = comment .. string.sub(line,1,pos_close-1) + hitend=true + end + i = i + 1 + end + -- got long comment + if string.sub(comment,1,1)=='!' then + TIO_out2stream('/*' .. comment .. '*/') + else + TIO_out2stream('/* (longcomment):' .. comment .. '*/') + end + else + -- it's a boring comment + TIO_out2stream_commented(line,'--') + end + elseif string.find(line,'^function%s') or string.find(line,'^local%s+function%s')then + -- "function wibble..." + -- it's a function declaration + -- ....v... + local pos_fn = string.find(line,'function') + if (pos_fn) then + local pos_local = string.find(line,'^local%s+function%s') + local fn = TString_removeCommentFromLine(TString_trim(string.sub(line,pos_fn+8))) + + if (string.sub(fn,1,1)=='(') then + -- anonymous function + TIO_out2stream_commented(line,'anon fn') + else + --[[ + we might have extracted a fn def with parameters on multilines + The hack is to insert a new close paren with a note to that effect. + ]] + + if not string.find(fn,'%)') then + fn = fn .. ' ___MissingCloseParenHere___)' + end + + local plain_fn_str = 'function ' .. fn .. '{}' + if pos_local then + plain_fn_str = 'local ' .. plain_fn_str + end + + local dot = string.find(fn,'%.') + if dot then -- it's a method + local klass = string.sub(fn,1,dot-1) + local method = string.sub(fn,dot+1) + local method_str = klass .. '::' .. method .. '{}' + TCommentary_addMethod(klass,method) + TIO_out2stream(method_str) + else + TIO_out2stream(plain_fn_str) + end + end + end + elseif string.find(line,'=%s+class%(') then + -- it's a class definition + line = TString_removeCommentFromLine(line) + local klass,parent,pos_class + -- ....v... + pos_class = string.find(line,'=%s+class%(') + klass = TString_trim(string.sub(line,1,pos_class-1)) + parent = TString_trim(string.sub(line,pos_class+8)) + parent = string.sub(parent,1,-2) + + line = 'class ' .. klass + if (#parent>0) then + line = line .. ' :public ' .. parent + end + + -- need methods list + local methods = TClassList_method_get(klass) + local methods_str + if methods then + methods_str = 'public: ' + for k,v in pairs(methods) do + methods_str = methods_str .. v .. ';' + end + else + methods_str = '/* no methods reported */' + end + line = line .. '{' .. methods_str .. '}' + + TIO_out2stream(line .. ';',true) + else + -- we don't know what this line means, so we can probably just comment it out + TIO_out2stream_commented(line) + end + + i = i + 1 + end + return err +end + +--! \brief run the filter +--! +--! \param AppTimestamp application + timestamp for this run +--! \param Filename the filename or if nil stdin +--! \param CommentaryFiles names of commentary files +--! \return err or nil +--! +local function TApp_run_filter(AppTimestamp,Filename,CommentaryFiles) + local err + local filecontents + if Filename then + -- syphon lines to our table + filecontents={} + for line in io.lines(Filename) do + table.insert(filecontents,line) + end + else + -- get stuff from stdin as a long string (with crlfs etc) + filecontents=io.read('*a') + -- make it a table of lines + filecontents = TString_split(filecontents,'[\n]') -- note this only works for unix files. + Filename = 'stdin' + end + + if filecontents then + TCommentary_readFileContents(CommentaryFiles.infile) + TCommentary_open(CommentaryFiles.outfile,Filename) + + err = TApp_lua2dox(filecontents) + + TCommentary_close() + + TIO_writeln('// done (' .. AppTimestamp .. ')') + else + err = TIO_showError(1,'couldn\'t find any file contents') + end + return err +end + +--! \brief run doxygen for one +--! +--! \param AppTimestamp application + timestamp for this run +--! \param Argv commandline for this run +--! \param CommentaryFiles names of commentary files +--! \return err or nil +--! +local function TApp_run_doxygen(AppTimestamp,Argv,CommentaryFiles) + local err + TIO_writeln('running: ' .. AppTimestamp) + + local argv1 = Argv[1] + if argv1=='--help' then + local appname = TApp_get_appname() + TIO_writeln('Syntax:') + TIO_writeln(' ' .. appname .. ' [[-g] [-s]] [<Doxyfile name>]|--help') + TIO_writeln(' --help show this text') + TIO_writeln(' -g: generate new Doxyfile') + TIO_writeln(' -s: generate new Doxyfile without comments') + TIO_writeln(' <Doxyfile name>: name of Doxyfile') + + TIO_writeln() + TIO_writeln(' For help on doxygen run its help system directly') + --! \todo more help here + else + local cl = 'doxygen' + for i,argv_i in ipairs(Argv) do + if i>=1 then -- don't want to use this app's name + cl = cl .. ' ' .. argv_i + end + end + + TIO_writeln('about to run "' .. cl .. '"') + err = TOS_system(cl) + + if not err then + -- cycle commentary files + local newComments=CommentaryFiles.outfile + local nextRunsComments=CommentaryFiles.infile + if TOS_fileExists(newComments) then + TIO_writeln('found outfile "' .. newComments .. '"') + os.remove(nextRunsComments) + TIO_writeln('mv "' .. newComments .. '"-> ' .. nextRunsComments .. '"') + os.rename(newComments,nextRunsComments) + end + end + end + return err +end + +-- main +local timestamp = TClock_getTimeStamp() +local appname = TApp_get_appname() +local version = '0.1 20120704' +local appTimestamp = appname .. '(v' .. version .. ') :' .. timestamp +local commentary_files = { + infile=TConfig_get('LUA2DOX_COMMENTARY_FILE_IN') + ,outfile=TConfig_get('LUA2DOX_COMMENTARY_FILE_OUT') + } + +if appname == 'lua2dox_filter' then + err = TApp_run_filter(appTimestamp,TCommandline_getargv()[1],commentary_files) + TIO_writeln('// do filter') +else + err = TApp_run_doxygen(appTimestamp,TCommandline_getargv(),commentary_files) +end + +--eof
\ No newline at end of file |