summaryrefslogtreecommitdiff
path: root/web/lua2dox/lua2dox.lua
diff options
context:
space:
mode:
Diffstat (limited to 'web/lua2dox/lua2dox.lua')
-rw-r--r--web/lua2dox/lua2dox.lua663
1 files changed, 663 insertions, 0 deletions
diff --git a/web/lua2dox/lua2dox.lua b/web/lua2dox/lua2dox.lua
new file mode 100644
index 0000000000..7aaf75f795
--- /dev/null
+++ b/web/lua2dox/lua2dox.lua
@@ -0,0 +1,663 @@
+--[[--------------------------------------------------------------------------
+ -- 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
+
+ Introduction
+ ------------
+
+ A hack lua2dox converter
+ Version 0.2
+
+ 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.
+
+ Running
+ -------
+
+ <ol>
+ <li> 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.
+ You can experiment with the enclosed example code.
+
+ <li> Run "doxygen -g" to create a default Doxyfile.
+
+ Then alter it to let it recognise lua. Add the two following lines:
+
+ \code{.bash}
+ FILE_PATTERNS = *.lua
+
+ FILTER_PATTERNS = *.lua=lua2dox_filter
+ \endcode
+
+
+ Either add them to the end or find the appropriate entry in Doxyfile.
+
+ There are other lines that you might like to alter, but see futher documentation for details.
+
+ <li> When Doxyfile is edited run "doxygen"
+
+ 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.
+ </ol>
+
+ 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_filter"(bash).
+ Somewhere in your path (e.g. "~/bin" or "/usr/local/bin") put a link to "lua2dox_filter".
+
+ Documentation
+ -------------
+
+ Read the external documentation that should be part of this package.
+ For example look for the "README" and some .PDFs.
+
+ ]]
+
+-- we won't use our library code, so this becomes more portable
+
+-- require 'elijah_fix_require'
+-- require 'elijah_class'
+--
+--! \brief ``declare'' as class
+--!
+--! use as:
+--! \code{.lua}
+--! TWibble = class()
+--! function TWibble.init(this,Str)
+--! this.str = Str
+--! -- more stuff here
+--! end
+--! \endcode
+--!
+function class(BaseClass, ClassInitialiser)
+ local newClass = {} -- a new class newClass
+ if not ClassInitialiser and type(BaseClass) == 'function' then
+ ClassInitialiser = BaseClass
+ BaseClass = nil
+ elseif type(BaseClass) == 'table' then
+ -- our new class is a shallow copy of the base class!
+ for i,v in pairs(BaseClass) do
+ newClass[i] = v
+ end
+ newClass._base = BaseClass
+ end
+ -- the class will be the metatable for all its newInstanceects,
+ -- and they will look up their methods in it.
+ newClass.__index = newClass
+
+ -- expose a constructor which can be called by <classname>(<args>)
+ local classMetatable = {}
+ classMetatable.__call =
+ function(class_tbl, ...)
+ local newInstance = {}
+ setmetatable(newInstance,newClass)
+ --if init then
+ -- init(newInstance,...)
+ if class_tbl.init then
+ class_tbl.init(newInstance,...)
+ else
+ -- make sure that any stuff from the base class is initialized!
+ if BaseClass and BaseClass.init then
+ BaseClass.init(newInstance, ...)
+ end
+ end
+ return newInstance
+ end
+ newClass.init = ClassInitialiser
+ newClass.is_a =
+ function(this, klass)
+ local thisMetatable = getmetatable(this)
+ while thisMetatable do
+ if thisMetatable == klass then
+ return true
+ end
+ thisMetatable = thisMetatable._base
+ end
+ return false
+ end
+ setmetatable(newClass, classMetatable)
+ return newClass
+end
+
+-- require 'elijah_clock'
+
+--! \class TCore_Clock
+--! \brief a clock
+TCore_Clock = class()
+
+--! \brief get the current time
+function TCore_Clock.GetTimeNow()
+ if os.gettimeofday then
+ return os.gettimeofday()
+ else
+ return os.time()
+ end
+end
+
+--! \brief constructor
+function TCore_Clock.init(this,T0)
+ if T0 then
+ this.t0 = T0
+ else
+ this.t0 = TCore_Clock.GetTimeNow()
+ end
+end
+
+--! \brief get time string
+function TCore_Clock.getTimeStamp(this,T0)
+ local t0
+ if T0 then
+ t0 = T0
+ else
+ t0 = this.t0
+ end
+ return os.date('%c %Z',t0)
+end
+
+
+--require 'elijah_io'
+
+--! \class TCore_IO
+--! \brief io to console
+--!
+--! pseudo class (no methods, just to keep documentation tidy)
+TCore_IO = class()
+--
+--! \brief write to stdout
+function TCore_IO_write(Str)
+ if (Str) then
+ io.write(Str)
+ end
+end
+
+--! \brief write to stdout
+function TCore_IO_writeln(Str)
+ if (Str) then
+ io.write(Str)
+ end
+ io.write("\n")
+end
+
+
+--require 'elijah_string'
+
+--! \brief trims a string
+function string_trim(Str)
+ return Str:match("^%s*(.-)%s*$")
+end
+
+--! \brief split a string
+--!
+--! \param Str
+--! \param Pattern
+--! \returns table of string fragments
+function string_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
+
+
+--require 'elijah_commandline'
+
+--! \class TCore_Commandline
+--! \brief reads/parses commandline
+TCore_Commandline = class()
+
+--! \brief constructor
+function TCore_Commandline.init(this)
+ this.argv = arg
+ this.parsed = {}
+ this.params = {}
+end
+
+--! \brief get value
+function TCore_Commandline.getRaw(this,Key,Default)
+ local val = this.argv[Key]
+ if not val then
+ val = Default
+ end
+ return val
+end
+
+
+--require 'elijah_debug'
+
+-------------------------------
+--! \brief file buffer
+--!
+--! an input file buffer
+TStream_Read = class()
+
+--! \brief get contents of file
+--!
+--! \param Filename name of file to read (or nil == stdin)
+function TStream_Read.getContents(this,Filename)
+ -- get lines from file
+ local filecontents
+ if Filename then
+ -- syphon lines to our table
+ --TCore_Debug_show_var('Filename',Filename)
+ 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
+ this.filecontents = filecontents
+ this.contentsLen = #filecontents
+ this.currentLineNo = 1
+ end
+
+ return filecontents
+end
+
+--! \brief get lineno
+function TStream_Read.getLineNo(this)
+ return this.currentLineNo
+end
+
+--! \brief get a line
+function TStream_Read.getLine(this)
+ local line
+ if this.currentLine then
+ line = this.currentLine
+ this.currentLine = nil
+ else
+ -- get line
+ if this.currentLineNo<=this.contentsLen then
+ line = this.filecontents[this.currentLineNo]
+ this.currentLineNo = this.currentLineNo + 1
+ else
+ line = ''
+ end
+ end
+ return line
+end
+
+--! \brief save line fragment
+function TStream_Read.ungetLine(this,LineFrag)
+ this.currentLine = LineFrag
+end
+
+--! \brief is it eof?
+function TStream_Read.eof(this)
+ if this.currentLine or this.currentLineNo<=this.contentsLen then
+ return false
+ end
+ return true
+end
+
+--! \brief output stream
+TStream_Write = class()
+
+--! \brief constructor
+function TStream_Write.init(this)
+ this.tailLine = {}
+end
+
+--! \brief write immediately
+function TStream_Write.write(this,Str)
+ TCore_IO_write(Str)
+end
+
+--! \brief write immediately
+function TStream_Write.writeln(this,Str)
+ TCore_IO_writeln(Str)
+end
+
+--! \brief write immediately
+function TStream_Write.writelnComment(this,Str)
+ TCore_IO_write('// ZZ: ')
+ TCore_IO_writeln(Str)
+end
+
+--! \brief write to tail
+function TStream_Write.writelnTail(this,Line)
+ if not Line then
+ Line = ''
+ end
+ table.insert(this.tailLine,Line)
+end
+
+--! \brief outout tail lines
+function TStream_Write.write_tailLines(this)
+ for k,line in ipairs(this.tailLine) do
+ TCore_IO_writeln(line)
+ end
+ TCore_IO_write('// Lua2DoX new eof')
+end
+
+--! \brief input filter
+TLua2DoX_filter = class()
+
+--! \brief allow us to do errormessages
+function TLua2DoX_filter.warning(this,Line,LineNo,Legend)
+ this.outStream:writelnTail(
+ '//! \todo warning! ' .. Legend .. ' (@' .. LineNo .. ')"' .. Line .. '"'
+ )
+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,'%-%-')
+ local tailComment
+ if pos_comment then
+ Line = string.sub(Line,1,pos_comment-1)
+ tailComment = string.sub(Line,pos_comment)
+ end
+ return Line,tailComment
+end
+
+--! \brief get directive from magic
+local function getMagicDirective(Line)
+ local macro,tail
+ local macroStr = '[\\\@]'
+ local pos_macro = string.find(Line,macroStr)
+ if pos_macro then
+ --! ....\\ macro...stuff
+ --! ....\@ macro...stuff
+ local line = string.sub(Line,pos_macro+1)
+ local space = string.find(line,'%s+')
+ if space then
+ macro = string.sub(line,1,space-1)
+ tail = string_trim(string.sub(line,space+1))
+ else
+ macro = line
+ tail = ''
+ end
+ end
+ return macro,tail
+end
+
+--! \brief check comment for fn
+local function checkComment4fn(Fn_magic,MagicLines)
+ local fn_magic = Fn_magic
+-- TCore_IO_writeln('// checkComment4fn "' .. MagicLines .. '"')
+
+ local magicLines = string_split(MagicLines,'\n')
+
+ local macro,tail
+
+ for k,line in ipairs(magicLines) do
+ macro,tail = getMagicDirective(line)
+ if macro == 'fn' then
+ fn_magic = tail
+ -- TCore_IO_writeln('// found fn "' .. fn_magic .. '"')
+ else
+ --TCore_IO_writeln('// not found fn "' .. line .. '"')
+ end
+ end
+
+ return fn_magic
+end
+--! \brief run the filter
+function TLua2DoX_filter.readfile(this,AppStamp,Filename)
+ local err
+
+ local inStream = TStream_Read()
+ local outStream = TStream_Write()
+ this.outStream = outStream -- save to this obj
+
+ if (inStream:getContents(Filename)) then
+ -- output the file
+ local line
+ local fn_magic -- function name/def from magic comment
+
+ outStream:writelnTail('// #######################')
+ outStream:writelnTail('// app run:' .. AppStamp)
+ outStream:writelnTail('// #######################')
+ outStream:writelnTail()
+
+ while not (err or inStream:eof()) do
+ line = string_trim(inStream:getLine())
+-- TCore_Debug_show_var('inStream',inStream)
+-- TCore_Debug_show_var('line',line )
+ if string.sub(line,1,2)=='--' then -- its a comment
+ if string.sub(line,3,3)=='!' then -- it's a magic comment
+ local magic = string.sub(line,4)
+ outStream:writeln('//!' .. magic)
+ fn_magic = checkComment4fn(fn_magic,magic)
+ elseif string.sub(line,3,4)=='[[' then -- it's a long comment
+ line = string.sub(line,5) -- nibble head
+ local comment = ''
+ local closeSquare,hitend,thisComment
+ while (not err) and (not hitend) and (not inStream:eof()) do
+ closeSquare = string.find(line,']]')
+ if not closeSquare then -- need to look on another line
+ thisComment = line .. '\n'
+ line = inStream:getLine()
+ else
+ thisComment = string.sub(line,1,closeSquare-1)
+ hitend = true
+
+ -- unget the tail of the line
+ -- in most cases it's empty. This may make us less efficient but
+ -- easier to program
+ inStream:ungetLine(string_trim(string.sub(line,closeSquare+2)))
+ end
+ comment = comment .. thisComment
+ end
+ if string.sub(comment,1,1)=='!' then -- it's a long magic comment
+ outStream:write('/*' .. comment .. '*/ ')
+ fn_magic = checkComment4fn(fn_magic,comment)
+ else -- discard
+ outStream:write('/* zz:' .. comment .. '*/ ')
+ fn_magic = nil
+ end
+ else
+ outStream:writeln('// zz:"' .. line .. '"')
+ fn_magic = nil
+ end
+ elseif string.find(line,'^function') or string.find(line,'^local%s+function') then
+ -- it's a function
+ local pos_fn = string.find(line,'function')
+ -- function
+ -- ....v...
+ if pos_fn then
+ -- we've got a function
+ local fn_type
+ if string.find(line,'^local%s+') then
+ fn_type = 'static '
+ else
+ fn_type = ''
+ end
+ local fn = TString_removeCommentFromLine(string_trim(string.sub(line,pos_fn+8)))
+ if fn_magic then
+ fn = fn_magic
+ fn_magic = nil
+ end
+
+ if string.sub(fn,1,1)=='(' then
+ -- it's an anonymous function
+ outStream:writelnComment(line)
+ else
+ -- fn has a name, so is interesting
+
+ -- want to fix for iffy declarations
+ local open_paren = string.find(fn,'[%({]')
+ local fn0 = fn
+ if open_paren then
+ fn0 = string.sub(fn,1,open_paren-1)
+ -- we might have a missing close paren
+ if not string.find(fn,'%)') then
+ fn = fn .. ' ___MissingCloseParenHere___)'
+ end
+ end
+
+ local dot = string.find(fn0,'[%.:]')
+ if dot then -- it's a method
+ local klass = string.sub(fn,1,dot-1)
+ local method = string.sub(fn,dot+1)
+ --TCore_IO_writeln('function ' .. klass .. '::' .. method .. ftail .. '{}')
+ --TCore_IO_writeln(klass .. '::' .. method .. ftail .. '{}')
+ outStream:writeln(
+ '/*! \\memberof ' .. klass .. ' */ '
+ .. method .. '{}'
+ )
+ else
+ -- add vanilla function
+
+ outStream:writeln(fn_type .. 'function ' .. fn .. '{}')
+ end
+ end
+ else
+ this:warning(inStream:getLineNo(),'something weird here')
+ end
+ fn_magic = nil -- mustn't indavertently use it again
+ elseif string.find(line,'=%s*class%(') then
+ -- it's a class declaration
+ local tailComment
+ line,tailComment = TString_removeCommentFromLine(line)
+ local equals = string.find(line,'=')
+ local klass = string_trim(string.sub(line,1,equals-1))
+ local tail = string_trim(string.sub(line,equals+1))
+ -- class(wibble wibble)
+ -- ....v.
+ local parent = string.sub(tail,7,-2)
+ if #parent>0 then
+ parent = ' :public ' .. parent
+ end
+ outStream:writeln('class ' .. klass .. parent .. '{};')
+ else
+ -- we don't know what this line means, so we can probably just comment it out
+ if #line>0 then
+ outStream:writeln('// zz: ' .. line)
+ else
+ outStream:writeln() -- keep this line blank
+ end
+ end
+ end
+
+ -- output the tail
+ outStream:write_tailLines()
+ else
+ outStream:writeln('!empty file')
+ end
+end
+
+--! \brief this application
+TApp = class()
+
+--! \brief constructor
+function TApp.init(this)
+ local t0 = TCore_Clock()
+ this.timestamp = t0:getTimeStamp()
+ this.name = 'Lua2DoX'
+ this.version = '0.2 20130128'
+ this.copyright = 'Copyright (c) Simon Dales 2012-13'
+end
+
+function TApp.getRunStamp(this)
+ return this.name .. ' (' .. this.version .. ') '
+ .. this.timestamp
+end
+
+function TApp.getVersion(this)
+ return this.name .. ' (' .. this.version .. ') '
+end
+
+function TApp.getCopyright(this)
+ return this.copyright
+end
+
+local This_app = TApp()
+
+--main
+local cl = TCore_Commandline()
+
+local argv1 = cl:getRaw(2)
+if argv1 == '--help' then
+ TCore_IO_writeln(This_app:getVersion())
+ TCore_IO_writeln(This_app:getCopyright())
+ TCore_IO_writeln([[
+run as:
+lua2dox_filter <param>
+--------------
+Param:
+ <filename> : interprets filename
+ --version : show version/copyright info
+ --help : this help text]])
+elseif argv1 == '--version' then
+ TCore_IO_writeln(This_app:getVersion())
+ TCore_IO_writeln(This_app:getCopyright())
+else
+ -- it's a filter
+ local appStamp = This_app:getRunStamp()
+ local filename = argv1
+
+ local filter = TLua2DoX_filter()
+ filter:readfile(appStamp,filename)
+end
+
+
+--eof \ No newline at end of file