diff options
author | KillTheMule <KillTheMule@users.noreply.github.com> | 2019-03-29 21:54:34 +0100 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2019-05-18 22:01:14 +0200 |
commit | b102c11e3860985e25b7baf48bba079f5042125a (patch) | |
tree | fe5ddf35330f3edd28b616b08ae7ca1ae17e2d22 /scripts/lua2dox.lua | |
parent | 974b43fd7940cd807e5a6b67c77cb2e0462b11a4 (diff) | |
download | rneovim-b102c11e3860985e25b7baf48bba079f5042125a.tar.gz rneovim-b102c11e3860985e25b7baf48bba079f5042125a.tar.bz2 rneovim-b102c11e3860985e25b7baf48bba079f5042125a.zip |
gen_vimdoc.py: get Lua docs via lua2dox.lua #9740
Diffstat (limited to 'scripts/lua2dox.lua')
-rw-r--r-- | scripts/lua2dox.lua | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/scripts/lua2dox.lua b/scripts/lua2dox.lua new file mode 100644 index 0000000000..ae76a36d50 --- /dev/null +++ b/scripts/lua2dox.lua @@ -0,0 +1,666 @@ +--[[-------------------------------------------------------------------------- +-- 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,3)=='-' then -- it's a nonmagic doc comment + local comment = string.sub(line,4) + outStream:writeln('/// '.. comment) + 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 ' -- static functions seem to be excluded + 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 |