1 --[[--------------------------------------------------------------------------
2 -- Copyright (C) 2012 by Simon Dales --
5 -- This program is free software; you can redistribute it and/or modify --
6 -- it under the terms of the GNU General Public License as published by --
7 -- the Free Software Foundation; either version 2 of the License, or --
8 -- (at your option) any later version. --
10 -- This program is distributed in the hope that it will be useful, --
11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of --
12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --
13 -- GNU General Public License
for more details. --
15 -- You should have received a copy of the GNU General Public License --
16 -- along with
this program;
if not,
write to the --
17 -- Free Software Foundation, Inc., --
18 -- 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. --
19 ----------------------------------------------------------------------------]]
22 \brief a hack lua2dox converter
31 A hack lua2dox converter
34 This lets us make Doxygen output some documentation to let
37 It is partially cribbed from the functionality of lua2dox
39 Found on CPAN when looking
for something
else; kinda handy.
41 Improved from lua2dox to make the doxygen output more friendly.
42 Also it runs faster in lua rather than Perl.
44 Because
this Perl based system is called
"lua2dox"., I have decided to add
".lua" to the name
45 to keep the two separate.
51 <li> Ensure doxygen is installed on your system and that you are familiar with its use.
52 Best is to
try to make and document some simple C/C++/PHP to see what it produces.
53 You can experiment with the enclosed example code.
55 <li> Run
"doxygen -g" to create a
default Doxyfile.
57 Then alter it to let it recognise lua. Add the two following lines:
62 FILTER_PATTERNS = *.lua=lua2dox_filter
66 Either add them to the end or find the appropriate entry in Doxyfile.
68 There are other lines that you might like to alter, but see futher documentation
for details.
70 <li> When Doxyfile is edited run
"doxygen"
72 The core
function reads the input file (filename or stdin) and outputs some pseudo C-ish language.
73 It only has to be good enough
for doxygen to see it as legal.
74 Therefore our lua interpreter is fairly limited, but
"good enough".
76 One limitation is that each line is treated separately (except
for long comments).
77 The implication is that
class and function declarations must be on the same line.
78 Some functions can have their parameter lists extended over multiple lines to make it look neat.
79 Managing this where there are also some comments is a bit more coding than I want to do at this stage,
80 so it will probably not document accurately if we do do this.
82 However I have put in a hack that will insert the "missing" close paren.
83 The effect is that you will get the function documented, but not with the parameter list you might expect.
89 Here for linux or unix-like, for any other OS you need to refer to other documentation.
91 This file is "lua2dox.lua". It gets called by "lua2dox_filter"(bash).
92 Somewhere in your path (e.g. "~/bin" or "/usr/local/bin") put a link to "lua2dox_filter".
97 Read the external documentation that should be part of this package.
98 For example look for the "README" and some .PDFs.
102 -- we won't use our library code, so this becomes more portable
104 -- require 'elijah_fix_require'
105 -- require 'elijah_class'
107 --! \brief ``declare'' as
class
111 --! TWibble =
class()
112 --!
function TWibble.init(
this,Str)
114 --! -- more stuff here
118 function class(BaseClass, ClassInitialiser)
119 local newClass = {} -- a
new class newClass
120 if not ClassInitialiser and type(BaseClass) ==
'function' then
121 ClassInitialiser = BaseClass
123 elseif type(BaseClass) ==
'table' then
124 -- our
new class is a shallow copy of the base
class!
125 for i,v in pairs(BaseClass) do
128 newClass._base = BaseClass
130 -- the
class will be the metatable for all its newInstanceects,
131 -- and they will look up their methods in it.
132 newClass.__index = newClass
134 -- expose a constructor which can be called by <classname>(<args>)
135 local classMetatable = {}
136 classMetatable.__call =
137 function(class_tbl, ...)
138 local newInstance = {}
139 setmetatable(newInstance,newClass)
141 --
init(newInstance,...)
142 if class_tbl.
init then
143 class_tbl.
init(newInstance,...)
145 -- make sure that any stuff from the base
class is initialized!
146 if BaseClass and BaseClass.
init then
147 BaseClass.
init(newInstance, ...)
152 newClass.
init = ClassInitialiser
154 function(this, klass)
155 local thisMetatable = getmetatable(this)
156 while thisMetatable do
157 if thisMetatable == klass then
160 thisMetatable = thisMetatable._base
164 setmetatable(newClass, classMetatable)
168 -- require 'elijah_clock'
174 --! \brief get the current time
176 if os.gettimeofday then
177 return os.gettimeofday()
183 --! \brief constructor
192 --! \brief get time
string
200 return os.date('%c %Z',t0)
204 --require 'elijah_io'
207 --! \brief io to console
209 --! pseudo
class (no methods, just to keep documentation tidy)
212 --! \brief
write to stdout
219 --! \brief
write to stdout
228 --require 'elijah_string'
230 --! \brief trims a
string
232 return Str:match("^%s*(.-)%s*$")
235 --! \brief split a
string
239 --! \returns table of
string fragments
242 local fpat =
"(.-)" .. Pattern
244 local str, e, cap =
string.find(Str,fpat, 1)
246 if str ~= 1 or cap ~=
"" then
247 table.insert(splitStr,cap)
250 str, e, cap =
string.find(Str,fpat, last_end)
252 if last_end <= #Str then
253 cap =
string.sub(Str,last_end)
254 table.insert(splitStr, cap)
260 --require
'elijah_commandline'
263 --! \brief reads/parses commandline
266 --! \brief constructor
275 local val = this.argv[Key]
283 --require
'elijah_debug'
285 -------------------------------
286 --! \brief file buffer
288 --! an input file buffer
291 --! \brief
get contents of file
293 --! \param Filename name of file to read (or nil == stdin)
295 --
get lines from file
298 -- syphon lines to our table
299 --TCore_Debug_show_var(
'Filename',Filename)
301 for line in io.lines(Filename)
do
302 table.insert(filecontents,line)
305 --
get stuff from stdin as a
long string (with crlfs etc)
306 filecontents=io.read(
'*a')
307 -- make it a table of lines
308 filecontents = TString_split(filecontents,
'[\n]') -- note
this only works
for unix files.
313 this.filecontents = filecontents
314 this.contentsLen = #filecontents
315 this.currentLineNo = 1
321 --! \brief
get lineno
323 return this.currentLineNo
326 --! \brief
get a line
329 if this.currentLine then
330 line = this.currentLine
331 this.currentLine = nil
334 if this.currentLineNo<=this.contentsLen then
335 line = this.filecontents[this.currentLineNo]
336 this.currentLineNo = this.currentLineNo + 1
344 --! \brief save line fragment
346 this.currentLine = LineFrag
349 --! \brief is it
eof?
351 if this.currentLine or this.currentLineNo<=this.contentsLen then
357 --! \brief output stream
360 --! \brief constructor
365 --! \brief
write immediately
370 --! \brief
write immediately
375 --! \brief
write immediately
381 --! \brief
write to tail
386 table.insert(this.tailLine,Line)
389 --! \brief outout tail lines
391 for k,line in ipairs(this.tailLine) do
397 --! \brief input filter
400 --! \brief allow us to do errormessages
407 --! \brief trim comment off end of
string
409 --! If the
string has a comment on the end, this trims it off.
412 local pos_comment =
string.find(Line,'%-%-')
415 Line =
string.sub(Line,1,pos_comment-1)
416 tailComment =
string.sub(Line,pos_comment)
418 return Line,tailComment
421 --! \brief get directive from magic
424 local macroStr = '[\\\@]'
425 local pos_macro =
string.find(Line,macroStr)
427 --! ....\\ macro...stuff
428 --! ....\@ macro...stuff
429 local line =
string.sub(Line,pos_macro+1)
430 local space =
string.find(line,'%s+')
432 macro =
string.sub(line,1,space-1)
442 --! \brief check comment for fn
444 local fn_magic = Fn_magic
451 for k,line in ipairs(magicLines) do
453 if macro == 'fn' then
463 --! \brief run the filter
469 this.outStream = outStream -- save to this obj
474 local fn_magic -- function name/def from magic comment
481 while not (err or inStream:
eof()) do
483 -- TCore_Debug_show_var('inStream',inStream)
484 -- TCore_Debug_show_var('line',line )
485 if
string.sub(line,1,2)=='--' then -- its a comment
486 if
string.sub(line,3,3)=='!' then -- it's a magic comment
487 local magic =
string.sub(line,4)
490 elseif
string.sub(line,3,4)=='[[' then -- it's a
long comment
491 line =
string.sub(line,5) -- nibble head
493 local closeSquare,hitend,thisComment
494 while (not err) and (not hitend) and (not inStream:
eof()) do
495 closeSquare =
string.find(line,']]')
496 if not closeSquare then -- need to look on another line
497 thisComment = line .. '\n'
500 thisComment =
string.sub(line,1,closeSquare-1)
503 -- unget the tail of the line
504 -- in most cases it's empty. This may make us less efficient but
508 comment = comment .. thisComment
510 if
string.sub(comment,1,1)=='!' then -- it's a
long magic comment
521 elseif
string.find(line,'^function') or
string.find(line,'^local%s+function') then
523 local pos_fn =
string.find(line,'function')
527 -- we've got a function
529 if
string.find(line,'^local%s+') then
540 if
string.sub(fn,1,1)=='(' then
541 -- it's an anonymous function
544 -- fn has a name, so is interesting
546 -- want to fix for iffy declarations
547 local open_paren =
string.find(fn,'[%({]
')
550 fn0 = string.sub(fn,1,open_paren-1)
551 -- we might have a missing close paren
552 if not string.find(fn,'%)
') then
553 fn = fn .. ' ___MissingCloseParenHere___)
'
557 local dot = string.find(fn0,'[%.:]
')
558 if dot then -- it's a method
559 local klass =
string.sub(fn,1,dot-1)
560 local method =
string.sub(fn,dot+1)
564 '/*! \\memberof ' .. klass ..
' */ '
568 -- add vanilla
function
570 outStream:
writeln(fn_type ..
'function ' .. fn ..
'{}')
576 fn_magic = nil -- mustn
't indavertently use it again
577 elseif string.find(line,'=%s*
class%(
') then
578 -- it's a
class declaration
581 local equals =
string.find(line,
'=')
582 local klass =
string_trim(
string.sub(line,1,equals-1))
583 local tail =
string_trim(
string.sub(line,equals+1))
584 --
class(wibble wibble)
586 local parent =
string.sub(tail,7,-2)
588 parent =
' :public ' .. parent
590 outStream:
writeln(
'class ' .. klass .. parent ..
'{};')
592 -- we don
't know what this line means, so we can probably just comment it out
596 outStream:
writeln() -- keep
this line blank
604 outStream:
writeln(
'!empty file')
608 --! \brief
this application
611 --! \brief constructor
615 this.name = 'Lua2DoX'
616 this.version = '0.2 20130128'
617 this.copyright = 'Copyright (c) Simon Dales 2012-13'
621 return this.name .. ' (' .. this.version .. ') '
626 return this.name .. ' (' .. this.version .. ') '
630 return this.copyright
633 local This_app =
TApp()
638 local argv1 = cl:
getRaw(2)
639 if argv1 == '--help' then
644 lua2dox_filter <param>
647 <filename> : interprets filename
648 --version : show version/copyright info
649 --help : this help text]])
650 elseif argv1 == '--version' then
656 local filename = argv1