convert to sensible line feeds
This commit is contained in:
parent
73e22a3c6c
commit
05bd15a266
658
doc.lua
658
doc.lua
|
@ -1,329 +1,329 @@
|
|||
------
|
||||
-- Defining the ldoc document model.
|
||||
|
||||
|
||||
require 'pl'
|
||||
|
||||
local doc = {}
|
||||
|
||||
local tools = require 'tools'
|
||||
local split_dotted_name = tools.split_dotted_name
|
||||
|
||||
-- these are the basic tags known to ldoc. They come in several varieties:
|
||||
-- - tags with multiple values like 'param' (TAG_MULTI)
|
||||
-- - tags which are identifiers, like 'name' (TAG_ID)
|
||||
-- - tags with a single value, like 'release' (TAG_SINGLE)
|
||||
-- - tags which represent a type, like 'function' (TAG_TYPE)
|
||||
local known_tags = {
|
||||
param = 'M', see = 'M', usage = 'M', ['return'] = 'M', field = 'M', author='M';
|
||||
class = 'id', name = 'id', pragma = 'id';
|
||||
copyright = 'S', description = 'S', release = 'S';
|
||||
module = 'T', script = 'T',['function'] = 'T', table = 'T'
|
||||
}
|
||||
known_tags._alias = {}
|
||||
known_tags._project_level = {
|
||||
module = true,
|
||||
script = true
|
||||
}
|
||||
|
||||
local TAG_MULTI,TAG_ID,TAG_SINGLE,TAG_TYPE = 'M','id','S','T'
|
||||
doc.TAG_MULTI,doc.TAG_ID,doc.TAG_SINGLE,doc.TAG_TYPE = TAG_MULTI,TAG_ID,TAG_SINGLE,TAG_TYPE
|
||||
|
||||
-- add a new tag.
|
||||
function doc.add_tag(tag,type,project_level)
|
||||
if not known_tags[tag] then
|
||||
known_tags[tag] = type
|
||||
known_tags._project_level[tag] = project_level
|
||||
end
|
||||
end
|
||||
|
||||
-- add an alias to an existing tag (exposed through ldoc API)
|
||||
function doc.add_alias (a,tag)
|
||||
known_tags._alias[a] = tag
|
||||
end
|
||||
|
||||
-- get the tag alias value, if it exists.
|
||||
function doc.get_alias(tag)
|
||||
return known_tags._alias[tag]
|
||||
end
|
||||
|
||||
-- is it a'project level' tag, such as 'module' or 'script'?
|
||||
function doc.project_level(tag)
|
||||
return known_tags._project_level[tag]
|
||||
end
|
||||
|
||||
-- we process each file, resulting in a File object, which has a list of Item objects.
|
||||
-- Items can be modules, scripts ('project level') or functions, tables, etc.
|
||||
-- (In the code 'module' refers to any project level tag.)
|
||||
-- When the File object is finalized, we specialize some items as modules which
|
||||
-- are 'container' types containing functions and tables, etc.
|
||||
|
||||
local File = class()
|
||||
local Item = class()
|
||||
local Module = class(Item) -- a specialized kind of Item
|
||||
|
||||
doc.File = File
|
||||
doc.Item = Item
|
||||
doc.Module = Module
|
||||
|
||||
function File:_init(filename)
|
||||
self.filename = filename
|
||||
self.items = List()
|
||||
self.modules = List()
|
||||
end
|
||||
|
||||
function File:new_item(tags,line)
|
||||
local item = Item(tags)
|
||||
self.items:append(item)
|
||||
item.file = self
|
||||
item.lineno = line
|
||||
return item
|
||||
end
|
||||
|
||||
function File:finish()
|
||||
local this_mod
|
||||
local items = self.items
|
||||
for item in items:iter() do
|
||||
item:finish()
|
||||
if doc.project_level(item.type) then
|
||||
this_mod = item
|
||||
-- if name is 'package.mod', then mod_name is 'mod'
|
||||
local package,mname = split_dotted_name(this_mod.name)
|
||||
if not package then
|
||||
mname = this_mod.name
|
||||
package = ''
|
||||
else
|
||||
package = package .. '.'
|
||||
end
|
||||
self.modules:append(this_mod)
|
||||
this_mod.package = package
|
||||
this_mod.mod_name = mname
|
||||
this_mod.kinds = ModuleMap() -- the iterator over the module contents
|
||||
else -- add the item to the module's item list
|
||||
if this_mod then
|
||||
-- new-style modules will have qualified names like 'mod.foo'
|
||||
local mod,fname = split_dotted_name(item.name)
|
||||
-- warning for inferred unqualified names in new style modules
|
||||
-- (retired until we handle methods like Set:unset() properly)
|
||||
if not mod and not this_mod.old_style and item.inferred then
|
||||
--item:warning(item.name .. ' is declared in global scope')
|
||||
end
|
||||
-- if that's the mod_name, then we want to only use 'foo'
|
||||
if mod == this_mod.mod_name and this_mod.tags.pragma ~= 'nostrip' then
|
||||
item.name = fname
|
||||
end
|
||||
item.module = this_mod
|
||||
local these_items = this_mod.items
|
||||
these_items.by_name[item.name] = item
|
||||
these_items:append(item)
|
||||
|
||||
-- register this item with the iterator
|
||||
this_mod.kinds:add(item,these_items)
|
||||
|
||||
else
|
||||
-- must be a free-standing function (sometimes a problem...)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Item:_init(tags)
|
||||
self.summary = tags.summary
|
||||
self.description = tags.description
|
||||
tags.summary = nil
|
||||
tags.description = nil
|
||||
self.tags = {}
|
||||
self.formal_args = tags.formal_args
|
||||
tags.formal_args = nil
|
||||
for tag,value in pairs(tags) do
|
||||
local ttype = known_tags[tag]
|
||||
if ttype == TAG_MULTI then
|
||||
if type(value) == 'string' then
|
||||
value = List{value}
|
||||
end
|
||||
self.tags[tag] = value
|
||||
elseif ttype == TAG_ID then
|
||||
if type(value) ~= 'string' then
|
||||
-- such tags are _not_ multiple, e.g. name
|
||||
self:error(tag..' cannot have multiple values')
|
||||
else
|
||||
self.tags[tag] = tools.extract_identifier(value)
|
||||
end
|
||||
elseif ttype == TAG_SINGLE then
|
||||
self.tags[tag] = value
|
||||
else
|
||||
self:warning ('unknown tag: '..tag)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- preliminary processing of tags. We check for any aliases, and for tags
|
||||
-- which represent types. This implements the shortcut notation.
|
||||
function Item.check_tag(tags,tag)
|
||||
tag = doc.get_alias(tag) or tag
|
||||
local ttype = known_tags[tag]
|
||||
if ttype == TAG_TYPE then
|
||||
tags.class = tag
|
||||
tag = 'name'
|
||||
end
|
||||
return tag
|
||||
end
|
||||
|
||||
|
||||
function Item:finish()
|
||||
local tags = self.tags
|
||||
self.name = tags.name
|
||||
self.type = tags.class
|
||||
self.usage = tags.usage
|
||||
tags.name = nil
|
||||
tags.class = nil
|
||||
tags.usage = nil
|
||||
-- see tags are multiple, but they may also be comma-separated
|
||||
if tags.see then
|
||||
tags.see = tools.expand_comma_list(tags.see)
|
||||
end
|
||||
if doc.project_level(self.type) then
|
||||
-- we are a module, so become one!
|
||||
self.items = List()
|
||||
self.items.by_name = {}
|
||||
setmetatable(self,Module)
|
||||
else
|
||||
-- params are either a function's arguments, or a table's fields, etc.
|
||||
local params
|
||||
if self.type == 'function' then
|
||||
params = tags.param or List()
|
||||
if tags['return'] then
|
||||
self.ret = tags['return']
|
||||
end
|
||||
else
|
||||
params = tags.field or List()
|
||||
end
|
||||
tags.param = nil
|
||||
local names,comments = List(),List()
|
||||
for p in params:iter() do
|
||||
local name,comment = p:match('%s*([%w_%.:]+)(.*)')
|
||||
names:append(name)
|
||||
comments:append(comment)
|
||||
end
|
||||
-- not all arguments may be commented --
|
||||
if self.formal_args then
|
||||
-- however, ldoc allows comments in the arg list to be used
|
||||
local fargs = self.formal_args
|
||||
for a in fargs:iter() do
|
||||
if not names:index(a) then
|
||||
names:append(a)
|
||||
comments:append (fargs.comments[a] or '')
|
||||
end
|
||||
end
|
||||
end
|
||||
self.params = names
|
||||
for i,name in ipairs(self.params) do
|
||||
self.params[name] = comments[i]
|
||||
end
|
||||
self.args = '('..self.params:join(', ')..')'
|
||||
end
|
||||
end
|
||||
|
||||
function Item:warning(msg)
|
||||
local name = self.file and self.file.filename
|
||||
if type(name) == 'table' then pretty.dump(name); name = '?' end
|
||||
name = name or '?'
|
||||
io.stderr:write(name,':',self.lineno or '?',' ',msg,'\n')
|
||||
end
|
||||
|
||||
-- resolving @see references. A word may be either a function in this module,
|
||||
-- or a module in this package. A MOD.NAME reference is within this package.
|
||||
-- Otherwise, the full qualified name must be used.
|
||||
-- First, check whether it is already a fully qualified module name.
|
||||
-- Then split it and see if the module part is a qualified module
|
||||
-- and try look up the name part in that module.
|
||||
-- If this isn't successful then try prepending the current package to the reference,
|
||||
-- and try to to resolve this.
|
||||
function Module:resolve_references(modules)
|
||||
local found = List()
|
||||
|
||||
local function process_see_reference (item,see,s)
|
||||
local mod_ref,fun_ref,name,packmod
|
||||
-- is this a fully qualified module name?
|
||||
local mod_ref = modules.by_name[s]
|
||||
if mod_ref then return mod_ref,nil end
|
||||
local packmod,name = split_dotted_name(s) -- e.g. 'pl.utils','split'
|
||||
if packmod then -- qualified name
|
||||
mod_ref = modules.by_name[packmod] -- fully qualified mod name?
|
||||
if not mod_ref then
|
||||
mod_ref = modules.by_name[self.package..packmod]
|
||||
end
|
||||
if not mod_ref then
|
||||
item:warning("module not found: "..packmod)
|
||||
return nil
|
||||
end
|
||||
fun_ref = mod_ref.items.by_name[name]
|
||||
if fun_ref then return mod_ref,fun_ref
|
||||
else
|
||||
item:warning("function not found: "..s.." in "..mod_ref.name)
|
||||
end
|
||||
else -- plain jane name; module in this package, function in this module
|
||||
mod_ref = modules.by_name[self.package..s]
|
||||
if mod_ref then return mod_ref,nil end
|
||||
fun_ref = self.items.by_name[s]
|
||||
if fun_ref then return self,fun_ref
|
||||
else
|
||||
item:warning("function not found: "..s.." in this module")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for item in self.items:iter() do
|
||||
local see = item.tags.see
|
||||
if see then -- this guy has @see references
|
||||
item.see = List()
|
||||
for s in see:iter() do
|
||||
local mod_ref, item_ref = process_see_reference(item,see,s)
|
||||
if mod_ref then
|
||||
local name = item_ref and item_ref.name or ''
|
||||
item.see:append {mod=mod_ref.name,name=name,label=s}
|
||||
found:append{item,s}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- mark as found, so we don't waste time re-searching
|
||||
for f in found:iter() do
|
||||
f[1].tags.see:remove_value(f[2])
|
||||
end
|
||||
end
|
||||
|
||||
-- make a text dump of the contents of this File object.
|
||||
-- The level of detail is controlled by the 'verbose' parameter.
|
||||
-- Primarily intended as a debugging tool.
|
||||
function File:dump(verbose)
|
||||
for mod in self.modules:iter() do
|
||||
print('Module:',mod.name,mod.summary,mod.description)
|
||||
for item in mod.items:iter() do
|
||||
item:dump(verbose)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Item:dump(verbose)
|
||||
local tags = self.tags
|
||||
local name = self.name
|
||||
if self.type == 'function' then
|
||||
name = name .. self.args
|
||||
end
|
||||
if verbose then
|
||||
print(self.type,name,self.summary)
|
||||
print(self.description)
|
||||
for p in self.params:iter() do
|
||||
print(p,self.params[p])
|
||||
end
|
||||
for tag, value in pairs(self.tags) do
|
||||
print(tag,value)
|
||||
end
|
||||
else
|
||||
print('* '..name..' - '..self.summary)
|
||||
end
|
||||
end
|
||||
|
||||
return doc
|
||||
|
||||
------
|
||||
-- Defining the ldoc document model.
|
||||
|
||||
|
||||
require 'pl'
|
||||
|
||||
local doc = {}
|
||||
|
||||
local tools = require 'tools'
|
||||
local split_dotted_name = tools.split_dotted_name
|
||||
|
||||
-- these are the basic tags known to ldoc. They come in several varieties:
|
||||
-- - tags with multiple values like 'param' (TAG_MULTI)
|
||||
-- - tags which are identifiers, like 'name' (TAG_ID)
|
||||
-- - tags with a single value, like 'release' (TAG_SINGLE)
|
||||
-- - tags which represent a type, like 'function' (TAG_TYPE)
|
||||
local known_tags = {
|
||||
param = 'M', see = 'M', usage = 'M', ['return'] = 'M', field = 'M', author='M';
|
||||
class = 'id', name = 'id', pragma = 'id';
|
||||
copyright = 'S', description = 'S', release = 'S';
|
||||
module = 'T', script = 'T',['function'] = 'T', table = 'T'
|
||||
}
|
||||
known_tags._alias = {}
|
||||
known_tags._project_level = {
|
||||
module = true,
|
||||
script = true
|
||||
}
|
||||
|
||||
local TAG_MULTI,TAG_ID,TAG_SINGLE,TAG_TYPE = 'M','id','S','T'
|
||||
doc.TAG_MULTI,doc.TAG_ID,doc.TAG_SINGLE,doc.TAG_TYPE = TAG_MULTI,TAG_ID,TAG_SINGLE,TAG_TYPE
|
||||
|
||||
-- add a new tag.
|
||||
function doc.add_tag(tag,type,project_level)
|
||||
if not known_tags[tag] then
|
||||
known_tags[tag] = type
|
||||
known_tags._project_level[tag] = project_level
|
||||
end
|
||||
end
|
||||
|
||||
-- add an alias to an existing tag (exposed through ldoc API)
|
||||
function doc.add_alias (a,tag)
|
||||
known_tags._alias[a] = tag
|
||||
end
|
||||
|
||||
-- get the tag alias value, if it exists.
|
||||
function doc.get_alias(tag)
|
||||
return known_tags._alias[tag]
|
||||
end
|
||||
|
||||
-- is it a'project level' tag, such as 'module' or 'script'?
|
||||
function doc.project_level(tag)
|
||||
return known_tags._project_level[tag]
|
||||
end
|
||||
|
||||
-- we process each file, resulting in a File object, which has a list of Item objects.
|
||||
-- Items can be modules, scripts ('project level') or functions, tables, etc.
|
||||
-- (In the code 'module' refers to any project level tag.)
|
||||
-- When the File object is finalized, we specialize some items as modules which
|
||||
-- are 'container' types containing functions and tables, etc.
|
||||
|
||||
local File = class()
|
||||
local Item = class()
|
||||
local Module = class(Item) -- a specialized kind of Item
|
||||
|
||||
doc.File = File
|
||||
doc.Item = Item
|
||||
doc.Module = Module
|
||||
|
||||
function File:_init(filename)
|
||||
self.filename = filename
|
||||
self.items = List()
|
||||
self.modules = List()
|
||||
end
|
||||
|
||||
function File:new_item(tags,line)
|
||||
local item = Item(tags)
|
||||
self.items:append(item)
|
||||
item.file = self
|
||||
item.lineno = line
|
||||
return item
|
||||
end
|
||||
|
||||
function File:finish()
|
||||
local this_mod
|
||||
local items = self.items
|
||||
for item in items:iter() do
|
||||
item:finish()
|
||||
if doc.project_level(item.type) then
|
||||
this_mod = item
|
||||
-- if name is 'package.mod', then mod_name is 'mod'
|
||||
local package,mname = split_dotted_name(this_mod.name)
|
||||
if not package then
|
||||
mname = this_mod.name
|
||||
package = ''
|
||||
else
|
||||
package = package .. '.'
|
||||
end
|
||||
self.modules:append(this_mod)
|
||||
this_mod.package = package
|
||||
this_mod.mod_name = mname
|
||||
this_mod.kinds = ModuleMap() -- the iterator over the module contents
|
||||
else -- add the item to the module's item list
|
||||
if this_mod then
|
||||
-- new-style modules will have qualified names like 'mod.foo'
|
||||
local mod,fname = split_dotted_name(item.name)
|
||||
-- warning for inferred unqualified names in new style modules
|
||||
-- (retired until we handle methods like Set:unset() properly)
|
||||
if not mod and not this_mod.old_style and item.inferred then
|
||||
--item:warning(item.name .. ' is declared in global scope')
|
||||
end
|
||||
-- if that's the mod_name, then we want to only use 'foo'
|
||||
if mod == this_mod.mod_name and this_mod.tags.pragma ~= 'nostrip' then
|
||||
item.name = fname
|
||||
end
|
||||
item.module = this_mod
|
||||
local these_items = this_mod.items
|
||||
these_items.by_name[item.name] = item
|
||||
these_items:append(item)
|
||||
|
||||
-- register this item with the iterator
|
||||
this_mod.kinds:add(item,these_items)
|
||||
|
||||
else
|
||||
-- must be a free-standing function (sometimes a problem...)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Item:_init(tags)
|
||||
self.summary = tags.summary
|
||||
self.description = tags.description
|
||||
tags.summary = nil
|
||||
tags.description = nil
|
||||
self.tags = {}
|
||||
self.formal_args = tags.formal_args
|
||||
tags.formal_args = nil
|
||||
for tag,value in pairs(tags) do
|
||||
local ttype = known_tags[tag]
|
||||
if ttype == TAG_MULTI then
|
||||
if type(value) == 'string' then
|
||||
value = List{value}
|
||||
end
|
||||
self.tags[tag] = value
|
||||
elseif ttype == TAG_ID then
|
||||
if type(value) ~= 'string' then
|
||||
-- such tags are _not_ multiple, e.g. name
|
||||
self:error(tag..' cannot have multiple values')
|
||||
else
|
||||
self.tags[tag] = tools.extract_identifier(value)
|
||||
end
|
||||
elseif ttype == TAG_SINGLE then
|
||||
self.tags[tag] = value
|
||||
else
|
||||
self:warning ('unknown tag: '..tag)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- preliminary processing of tags. We check for any aliases, and for tags
|
||||
-- which represent types. This implements the shortcut notation.
|
||||
function Item.check_tag(tags,tag)
|
||||
tag = doc.get_alias(tag) or tag
|
||||
local ttype = known_tags[tag]
|
||||
if ttype == TAG_TYPE then
|
||||
tags.class = tag
|
||||
tag = 'name'
|
||||
end
|
||||
return tag
|
||||
end
|
||||
|
||||
|
||||
function Item:finish()
|
||||
local tags = self.tags
|
||||
self.name = tags.name
|
||||
self.type = tags.class
|
||||
self.usage = tags.usage
|
||||
tags.name = nil
|
||||
tags.class = nil
|
||||
tags.usage = nil
|
||||
-- see tags are multiple, but they may also be comma-separated
|
||||
if tags.see then
|
||||
tags.see = tools.expand_comma_list(tags.see)
|
||||
end
|
||||
if doc.project_level(self.type) then
|
||||
-- we are a module, so become one!
|
||||
self.items = List()
|
||||
self.items.by_name = {}
|
||||
setmetatable(self,Module)
|
||||
else
|
||||
-- params are either a function's arguments, or a table's fields, etc.
|
||||
local params
|
||||
if self.type == 'function' then
|
||||
params = tags.param or List()
|
||||
if tags['return'] then
|
||||
self.ret = tags['return']
|
||||
end
|
||||
else
|
||||
params = tags.field or List()
|
||||
end
|
||||
tags.param = nil
|
||||
local names,comments = List(),List()
|
||||
for p in params:iter() do
|
||||
local name,comment = p:match('%s*([%w_%.:]+)(.*)')
|
||||
names:append(name)
|
||||
comments:append(comment)
|
||||
end
|
||||
-- not all arguments may be commented --
|
||||
if self.formal_args then
|
||||
-- however, ldoc allows comments in the arg list to be used
|
||||
local fargs = self.formal_args
|
||||
for a in fargs:iter() do
|
||||
if not names:index(a) then
|
||||
names:append(a)
|
||||
comments:append (fargs.comments[a] or '')
|
||||
end
|
||||
end
|
||||
end
|
||||
self.params = names
|
||||
for i,name in ipairs(self.params) do
|
||||
self.params[name] = comments[i]
|
||||
end
|
||||
self.args = '('..self.params:join(', ')..')'
|
||||
end
|
||||
end
|
||||
|
||||
function Item:warning(msg)
|
||||
local name = self.file and self.file.filename
|
||||
if type(name) == 'table' then pretty.dump(name); name = '?' end
|
||||
name = name or '?'
|
||||
io.stderr:write(name,':',self.lineno or '?',' ',msg,'\n')
|
||||
end
|
||||
|
||||
-- resolving @see references. A word may be either a function in this module,
|
||||
-- or a module in this package. A MOD.NAME reference is within this package.
|
||||
-- Otherwise, the full qualified name must be used.
|
||||
-- First, check whether it is already a fully qualified module name.
|
||||
-- Then split it and see if the module part is a qualified module
|
||||
-- and try look up the name part in that module.
|
||||
-- If this isn't successful then try prepending the current package to the reference,
|
||||
-- and try to to resolve this.
|
||||
function Module:resolve_references(modules)
|
||||
local found = List()
|
||||
|
||||
local function process_see_reference (item,see,s)
|
||||
local mod_ref,fun_ref,name,packmod
|
||||
-- is this a fully qualified module name?
|
||||
local mod_ref = modules.by_name[s]
|
||||
if mod_ref then return mod_ref,nil end
|
||||
local packmod,name = split_dotted_name(s) -- e.g. 'pl.utils','split'
|
||||
if packmod then -- qualified name
|
||||
mod_ref = modules.by_name[packmod] -- fully qualified mod name?
|
||||
if not mod_ref then
|
||||
mod_ref = modules.by_name[self.package..packmod]
|
||||
end
|
||||
if not mod_ref then
|
||||
item:warning("module not found: "..packmod)
|
||||
return nil
|
||||
end
|
||||
fun_ref = mod_ref.items.by_name[name]
|
||||
if fun_ref then return mod_ref,fun_ref
|
||||
else
|
||||
item:warning("function not found: "..s.." in "..mod_ref.name)
|
||||
end
|
||||
else -- plain jane name; module in this package, function in this module
|
||||
mod_ref = modules.by_name[self.package..s]
|
||||
if mod_ref then return mod_ref,nil end
|
||||
fun_ref = self.items.by_name[s]
|
||||
if fun_ref then return self,fun_ref
|
||||
else
|
||||
item:warning("function not found: "..s.." in this module")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for item in self.items:iter() do
|
||||
local see = item.tags.see
|
||||
if see then -- this guy has @see references
|
||||
item.see = List()
|
||||
for s in see:iter() do
|
||||
local mod_ref, item_ref = process_see_reference(item,see,s)
|
||||
if mod_ref then
|
||||
local name = item_ref and item_ref.name or ''
|
||||
item.see:append {mod=mod_ref.name,name=name,label=s}
|
||||
found:append{item,s}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- mark as found, so we don't waste time re-searching
|
||||
for f in found:iter() do
|
||||
f[1].tags.see:remove_value(f[2])
|
||||
end
|
||||
end
|
||||
|
||||
-- make a text dump of the contents of this File object.
|
||||
-- The level of detail is controlled by the 'verbose' parameter.
|
||||
-- Primarily intended as a debugging tool.
|
||||
function File:dump(verbose)
|
||||
for mod in self.modules:iter() do
|
||||
print('Module:',mod.name,mod.summary,mod.description)
|
||||
for item in mod.items:iter() do
|
||||
item:dump(verbose)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Item:dump(verbose)
|
||||
local tags = self.tags
|
||||
local name = self.name
|
||||
if self.type == 'function' then
|
||||
name = name .. self.args
|
||||
end
|
||||
if verbose then
|
||||
print(self.type,name,self.summary)
|
||||
print(self.description)
|
||||
for p in self.params:iter() do
|
||||
print(p,self.params[p])
|
||||
end
|
||||
for tag, value in pairs(self.tags) do
|
||||
print(tag,value)
|
||||
end
|
||||
else
|
||||
print('* '..name..' - '..self.summary)
|
||||
end
|
||||
end
|
||||
|
||||
return doc
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
-- ldoc configuration file
|
||||
title = "testmod docs"
|
||||
project = "testmod"
|
||||
|
||||
description = [[
|
||||
This description applies to the project as a whole.
|
||||
]]
|
||||
|
||||
alias("p","param")
|
||||
|
||||
new_type("macro","Macros")
|
||||
|
||||
-- ldoc configuration file
|
||||
title = "testmod docs"
|
||||
project = "testmod"
|
||||
|
||||
description = [[
|
||||
This description applies to the project as a whole.
|
||||
]]
|
||||
|
||||
alias("p","param")
|
||||
|
||||
new_type("macro","Macros")
|
||||
|
||||
|
|
|
@ -1,60 +1,60 @@
|
|||
---------------------------
|
||||
-- Test module providing bonzo.dog.
|
||||
-- Rest is a longer description
|
||||
-- @class module
|
||||
-- @name mod1
|
||||
|
||||
--- zero function. Two new ldoc features here; item types
|
||||
-- can be used directly as tags, and aliases for tags
|
||||
-- can be defined in config.lp.
|
||||
-- @function zero_fun
|
||||
-- @p k1 first
|
||||
-- @p k2 second
|
||||
|
||||
--- first function. Some description
|
||||
-- @param p1 first parameter
|
||||
-- @param p2 second parameter
|
||||
function mod1.first_fun(p1,p2)
|
||||
end
|
||||
|
||||
-------------------------
|
||||
-- second function.
|
||||
-- @param ... var args!
|
||||
function mod1.second_function(...)
|
||||
end
|
||||
|
||||
------------
|
||||
-- third function. Can also provide parameter comments inline,
|
||||
-- provided they follow this pattern.
|
||||
function mod1.third_function(
|
||||
alpha, -- correction A
|
||||
beta, -- correction B
|
||||
gamma -- factor C
|
||||
)
|
||||
end
|
||||
|
||||
-----
|
||||
-- A useful macro. This is an example of a custom 'kind'.
|
||||
-- @macro first_macro
|
||||
-- @see second_function
|
||||
|
||||
---- general configuration table
|
||||
-- @table config
|
||||
-- @field A alpha
|
||||
-- @field B beta
|
||||
-- @field C gamma
|
||||
mod1.config = {
|
||||
A = 1,
|
||||
B = 2,
|
||||
C = 3
|
||||
}
|
||||
|
||||
--[[--
|
||||
Another function. Using a Lua block comment
|
||||
@param p a parameter
|
||||
]]
|
||||
function mod1.zero_function(p)
|
||||
end
|
||||
|
||||
|
||||
|
||||
---------------------------
|
||||
-- Test module providing bonzo.dog.
|
||||
-- Rest is a longer description
|
||||
-- @class module
|
||||
-- @name mod1
|
||||
|
||||
--- zero function. Two new ldoc features here; item types
|
||||
-- can be used directly as tags, and aliases for tags
|
||||
-- can be defined in config.lp.
|
||||
-- @function zero_fun
|
||||
-- @p k1 first
|
||||
-- @p k2 second
|
||||
|
||||
--- first function. Some description
|
||||
-- @param p1 first parameter
|
||||
-- @param p2 second parameter
|
||||
function mod1.first_fun(p1,p2)
|
||||
end
|
||||
|
||||
-------------------------
|
||||
-- second function.
|
||||
-- @param ... var args!
|
||||
function mod1.second_function(...)
|
||||
end
|
||||
|
||||
------------
|
||||
-- third function. Can also provide parameter comments inline,
|
||||
-- provided they follow this pattern.
|
||||
function mod1.third_function(
|
||||
alpha, -- correction A
|
||||
beta, -- correction B
|
||||
gamma -- factor C
|
||||
)
|
||||
end
|
||||
|
||||
-----
|
||||
-- A useful macro. This is an example of a custom 'kind'.
|
||||
-- @macro first_macro
|
||||
-- @see second_function
|
||||
|
||||
---- general configuration table
|
||||
-- @table config
|
||||
-- @field A alpha
|
||||
-- @field B beta
|
||||
-- @field C gamma
|
||||
mod1.config = {
|
||||
A = 1,
|
||||
B = 2,
|
||||
C = 3
|
||||
}
|
||||
|
||||
--[[--
|
||||
Another function. Using a Lua block comment
|
||||
@param p a parameter
|
||||
]]
|
||||
function mod1.zero_function(p)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,62 +1,62 @@
|
|||
/// A sample C extension.
|
||||
// Demonstrates using ldoc's C/C++ support. Can either use /// or /*** */ etc.
|
||||
// @module mylib
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
// includes for Lua
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
|
||||
/***
|
||||
Create a table with given array and hash slots.
|
||||
@function createtable
|
||||
@param narr initial array slots, default 0
|
||||
@param nrec initial hash slots, default 0
|
||||
*/
|
||||
static int l_createtable (lua_State *L) {
|
||||
int narr = luaL_optint(L,1,0);
|
||||
int nrec = luaL_optint(L,2,0);
|
||||
lua_createtable(L,narr,nrec);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***
|
||||
Solve a quadratic equation.
|
||||
@function solve
|
||||
@param a coefficient of x^2
|
||||
@param b coefficient of x
|
||||
@param c constant
|
||||
@return first root
|
||||
@return second root
|
||||
*/
|
||||
static int l_solve (lua_State *L) {
|
||||
double a = lua_tonumber(L,1); // coeff of x*x
|
||||
double b = lua_tonumber(L,2); // coef of x
|
||||
double c = lua_tonumber(L,3); // constant
|
||||
double abc = b*b - 4*a*c;
|
||||
if (abc < 0.0) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L,"imaginary roots!");
|
||||
return 2;
|
||||
} else {
|
||||
abc = sqrt(abc);
|
||||
a = 2*a;
|
||||
lua_pushnumber(L,(-b + abc)/a);
|
||||
lua_pushnumber(L,(+b - abc)/a);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
static const luaL_reg mylib[] = {
|
||||
{"createtable",l_createtable},
|
||||
{"solve",l_solve},
|
||||
{NULL,NULL}
|
||||
};
|
||||
|
||||
int luaopen_mylib(lua_State *L)
|
||||
{
|
||||
luaL_register (L, "mylib", mylib);
|
||||
return 1;
|
||||
}
|
||||
/// A sample C extension.
|
||||
// Demonstrates using ldoc's C/C++ support. Can either use /// or /*** */ etc.
|
||||
// @module mylib
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
// includes for Lua
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
|
||||
/***
|
||||
Create a table with given array and hash slots.
|
||||
@function createtable
|
||||
@param narr initial array slots, default 0
|
||||
@param nrec initial hash slots, default 0
|
||||
*/
|
||||
static int l_createtable (lua_State *L) {
|
||||
int narr = luaL_optint(L,1,0);
|
||||
int nrec = luaL_optint(L,2,0);
|
||||
lua_createtable(L,narr,nrec);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***
|
||||
Solve a quadratic equation.
|
||||
@function solve
|
||||
@param a coefficient of x^2
|
||||
@param b coefficient of x
|
||||
@param c constant
|
||||
@return first root
|
||||
@return second root
|
||||
*/
|
||||
static int l_solve (lua_State *L) {
|
||||
double a = lua_tonumber(L,1); // coeff of x*x
|
||||
double b = lua_tonumber(L,2); // coef of x
|
||||
double c = lua_tonumber(L,3); // constant
|
||||
double abc = b*b - 4*a*c;
|
||||
if (abc < 0.0) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L,"imaginary roots!");
|
||||
return 2;
|
||||
} else {
|
||||
abc = sqrt(abc);
|
||||
a = 2*a;
|
||||
lua_pushnumber(L,(-b + abc)/a);
|
||||
lua_pushnumber(L,(+b - abc)/a);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
static const luaL_reg mylib[] = {
|
||||
{"createtable",l_createtable},
|
||||
{"solve",l_solve},
|
||||
{NULL,NULL}
|
||||
};
|
||||
|
||||
int luaopen_mylib(lua_State *L)
|
||||
{
|
||||
luaL_register (L, "mylib", mylib);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
project = 'md-test'
|
||||
title = 'Markdown Docs'
|
||||
format = 'markdown'
|
||||
project = 'md-test'
|
||||
title = 'Markdown Docs'
|
||||
format = 'markdown'
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
------------
|
||||
-- A little old-style module
|
||||
local io = io
|
||||
-- we'll look for this
|
||||
module 'simple'
|
||||
|
||||
-- if it were 'module (...)' then the name has to be deduced.
|
||||
|
||||
--- return the answer.
|
||||
function answer()
|
||||
return 42
|
||||
end
|
||||
------------
|
||||
-- A little old-style module
|
||||
local io = io
|
||||
-- we'll look for this
|
||||
module 'simple'
|
||||
|
||||
-- if it were 'module (...)' then the name has to be deduced.
|
||||
|
||||
--- return the answer.
|
||||
function answer()
|
||||
return 42
|
||||
end
|
||||
|
|
530
tools.lua
530
tools.lua
|
@ -1,265 +1,265 @@
|
|||
---------
|
||||
-- General utility functions for ldoc
|
||||
-- @module tools
|
||||
|
||||
require 'pl'
|
||||
local tools = {}
|
||||
local M = tools
|
||||
local append = table.insert
|
||||
local lexer = require 'lexer'
|
||||
local quit = utils.quit
|
||||
|
||||
-- this constructs an iterator over a list of objects which returns only
|
||||
-- those objects where a field has a certain value. It's used to iterate
|
||||
-- only over functions or tables, etc.
|
||||
-- (something rather similar exists in LuaDoc)
|
||||
function M.type_iterator (list,field,value)
|
||||
return function()
|
||||
local i = 1
|
||||
return function()
|
||||
local val = list[i]
|
||||
while val and val[field] ~= value do
|
||||
i = i + 1
|
||||
val = list[i]
|
||||
end
|
||||
i = i + 1
|
||||
if val then return val end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- KindMap is used to iterate over a set of categories, called _kinds_,
|
||||
-- and the associated iterator over all items in that category.
|
||||
-- For instance, a module contains functions, tables, etc and we will
|
||||
-- want to iterate over these categories in a specified order:
|
||||
--
|
||||
-- for kind, items in module.kinds() do
|
||||
-- print('kind',kind)
|
||||
-- for item in items() do print(item.name) end
|
||||
-- end
|
||||
--
|
||||
-- The kind is typically used as a label or a Title, so for type 'function' the
|
||||
-- kind is 'Functions' and so on.
|
||||
|
||||
local KindMap = class()
|
||||
M.KindMap = KindMap
|
||||
|
||||
-- calling a KindMap returns an iterator. This returns the kind, the iterator
|
||||
-- over the items of that type, and the corresponding type.
|
||||
function KindMap:__call ()
|
||||
local i = 1
|
||||
local klass = self.klass
|
||||
return function()
|
||||
local kind = klass.kinds[i]
|
||||
if not kind then return nil end -- no more kinds
|
||||
while not self[kind] do
|
||||
i = i + 1
|
||||
kind = klass.kinds[i]
|
||||
if not kind then return nil end
|
||||
end
|
||||
i = i + 1
|
||||
return kind, self[kind], klass.types_by_kind[kind]
|
||||
end
|
||||
end
|
||||
|
||||
-- called for each new item. It does not actually create separate lists,
|
||||
-- (although that would not break the interface) but creates iterators
|
||||
-- for that item type if not already created.
|
||||
function KindMap:add (item,items)
|
||||
local kname = self.klass.types_by_tag[item.type]
|
||||
if not self[kname] then
|
||||
self[kname] = M.type_iterator (items,'type',item.type)
|
||||
end
|
||||
end
|
||||
|
||||
-- KindMap has a 'class constructor' which is used to modify
|
||||
-- any new base class.
|
||||
function KindMap._class_init (klass)
|
||||
klass.kinds = {} -- list in correct order of kinds
|
||||
klass.types_by_tag = {} -- indexed by tag
|
||||
klass.types_by_kind = {} -- indexed by kind
|
||||
end
|
||||
|
||||
|
||||
function KindMap.add_kind (klass,tag,kind,subnames)
|
||||
klass.types_by_tag[tag] = kind
|
||||
klass.types_by_kind[kind] = {type=tag,subnames=subnames}
|
||||
append(klass.kinds,kind)
|
||||
end
|
||||
|
||||
|
||||
----- some useful utility functions ------
|
||||
|
||||
function M.module_basepath()
|
||||
local lpath = List.split(package.path,';')
|
||||
for p in lpath:iter() do
|
||||
local p = path.dirname(p)
|
||||
if path.isabs(p) then
|
||||
return p
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- split a qualified name into the module part and the name part,
|
||||
-- e.g 'pl.utils.split' becomes 'pl.utils' and 'split'
|
||||
function M.split_dotted_name (s)
|
||||
local s1,s2 = path.splitext(s)
|
||||
if s2=='' then return nil
|
||||
else return s1,s2:sub(2)
|
||||
end
|
||||
end
|
||||
|
||||
-- expand lists of possibly qualified identifiers
|
||||
-- given something like {'one , two.2','three.drei.drie)'}
|
||||
-- it will output {"one","two.2","three.drei.drie"}
|
||||
function M.expand_comma_list (ls)
|
||||
local new_ls = List()
|
||||
for s in ls:iter() do
|
||||
s = s:gsub('[^%.:%w]*$','')
|
||||
if s:find ',' then
|
||||
new_ls:extend(List.split(s,'%s*,%s*'))
|
||||
else
|
||||
new_ls:append(s)
|
||||
end
|
||||
end
|
||||
return new_ls
|
||||
end
|
||||
|
||||
function M.extract_identifier (value)
|
||||
return value:match('([%.:_%w]+)')
|
||||
end
|
||||
|
||||
function M.strip (s)
|
||||
return s:gsub('^%s+',''):gsub('%s+$','')
|
||||
end
|
||||
|
||||
function M.check_directory(d)
|
||||
if not path.isdir(d) then
|
||||
lfs.mkdir(d)
|
||||
end
|
||||
end
|
||||
|
||||
function M.check_file (f,original)
|
||||
if not path.exists(f) then
|
||||
dir.copyfile(original,f)
|
||||
end
|
||||
end
|
||||
|
||||
function M.writefile(name,text)
|
||||
local ok,err = utils.writefile(name,text)
|
||||
if err then quit(err) end
|
||||
end
|
||||
|
||||
function M.name_of (lpath)
|
||||
lpath,ext = path.splitext(lpath)
|
||||
return lpath
|
||||
end
|
||||
|
||||
function M.this_module_name (basename,fname)
|
||||
local ext
|
||||
if basename == '' then
|
||||
--quit("module(...) needs package basename")
|
||||
return M.name_of(fname)
|
||||
end
|
||||
basename = path.abspath(basename)
|
||||
if basename:sub(-1,-1) ~= path.sep then
|
||||
basename = basename..path.sep
|
||||
end
|
||||
local lpath,cnt = fname:gsub('^'..utils.escape(basename),'')
|
||||
if cnt ~= 1 then quit("module(...) name deduction failed: base "..basename.." "..fname) end
|
||||
lpath = lpath:gsub(path.sep,'.')
|
||||
return M.name_of(lpath)
|
||||
end
|
||||
|
||||
|
||||
--------- lexer tools -----
|
||||
|
||||
local tnext = lexer.skipws
|
||||
|
||||
local function type_of (tok) return tok[1] end
|
||||
local function value_of (tok) return tok[2] end
|
||||
|
||||
-- This parses Lua formal argument lists. It will return a list of argument
|
||||
-- names, which also has a comments field, which will contain any commments
|
||||
-- following the arguments. ldoc will use these in addition to explicit
|
||||
-- param tags.
|
||||
|
||||
function M.get_parameters (tok)
|
||||
local args = List()
|
||||
args.comments = {}
|
||||
local ltl = lexer.get_separated_list(tok)
|
||||
|
||||
if #ltl[1] == 0 then return args end -- no arguments
|
||||
|
||||
local function set_comment (idx,tok)
|
||||
args.comments[args[idx]] = value_of(tok)
|
||||
end
|
||||
|
||||
for i = 1,#ltl do
|
||||
local tl = ltl[i]
|
||||
if type_of(tl[1]) == 'comment' then
|
||||
if i > 1 then set_comment(i-1,tl[1]) end
|
||||
if #tl > 1 then
|
||||
args:append(value_of(tl[2]))
|
||||
end
|
||||
else
|
||||
args:append(value_of(tl[1]))
|
||||
end
|
||||
if i == #ltl then
|
||||
local last_tok = tl[#tl]
|
||||
if #tl > 1 and type_of(last_tok) == 'comment' then
|
||||
set_comment(i,last_tok)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return args
|
||||
end
|
||||
|
||||
-- parse a Lua identifier - contains names separated by . and :.
|
||||
function M.get_fun_name (tok)
|
||||
local res = {}
|
||||
local _,name = tnext(tok)
|
||||
_,sep = tnext(tok)
|
||||
while sep == '.' or sep == ':' do
|
||||
append(res,name)
|
||||
append(res,sep)
|
||||
_,name = tnext(tok)
|
||||
_,sep = tnext(tok)
|
||||
end
|
||||
append(res,name)
|
||||
return table.concat(res)
|
||||
end
|
||||
|
||||
-- space-skipping version of token iterator
|
||||
function M.space_skip_getter(tok)
|
||||
return function ()
|
||||
local t,v = tok()
|
||||
while t and t == 'space' do
|
||||
t,v = tok()
|
||||
end
|
||||
return t,v
|
||||
end
|
||||
end
|
||||
|
||||
-- an embarassing function. The PL Lua lexer does not do block comments
|
||||
-- when used in line-grabbing mode, and in fact (0.9.4) does not even
|
||||
-- do them properly in full-text mode, due to a ordering mistake.
|
||||
-- So, we do what we can ;)
|
||||
function M.grab_block_comment (v,tok,end1,end2)
|
||||
local res = {v}
|
||||
local t,last_v
|
||||
repeat
|
||||
last_v = v
|
||||
t,v = tok()
|
||||
append(res,v)
|
||||
until last_v == end1 and v == end2
|
||||
table.remove(res)
|
||||
table.remove(res)
|
||||
res = table.concat(res)
|
||||
return 'comment',res
|
||||
end
|
||||
|
||||
|
||||
|
||||
return tools
|
||||
---------
|
||||
-- General utility functions for ldoc
|
||||
-- @module tools
|
||||
|
||||
require 'pl'
|
||||
local tools = {}
|
||||
local M = tools
|
||||
local append = table.insert
|
||||
local lexer = require 'lexer'
|
||||
local quit = utils.quit
|
||||
|
||||
-- this constructs an iterator over a list of objects which returns only
|
||||
-- those objects where a field has a certain value. It's used to iterate
|
||||
-- only over functions or tables, etc.
|
||||
-- (something rather similar exists in LuaDoc)
|
||||
function M.type_iterator (list,field,value)
|
||||
return function()
|
||||
local i = 1
|
||||
return function()
|
||||
local val = list[i]
|
||||
while val and val[field] ~= value do
|
||||
i = i + 1
|
||||
val = list[i]
|
||||
end
|
||||
i = i + 1
|
||||
if val then return val end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- KindMap is used to iterate over a set of categories, called _kinds_,
|
||||
-- and the associated iterator over all items in that category.
|
||||
-- For instance, a module contains functions, tables, etc and we will
|
||||
-- want to iterate over these categories in a specified order:
|
||||
--
|
||||
-- for kind, items in module.kinds() do
|
||||
-- print('kind',kind)
|
||||
-- for item in items() do print(item.name) end
|
||||
-- end
|
||||
--
|
||||
-- The kind is typically used as a label or a Title, so for type 'function' the
|
||||
-- kind is 'Functions' and so on.
|
||||
|
||||
local KindMap = class()
|
||||
M.KindMap = KindMap
|
||||
|
||||
-- calling a KindMap returns an iterator. This returns the kind, the iterator
|
||||
-- over the items of that type, and the corresponding type.
|
||||
function KindMap:__call ()
|
||||
local i = 1
|
||||
local klass = self.klass
|
||||
return function()
|
||||
local kind = klass.kinds[i]
|
||||
if not kind then return nil end -- no more kinds
|
||||
while not self[kind] do
|
||||
i = i + 1
|
||||
kind = klass.kinds[i]
|
||||
if not kind then return nil end
|
||||
end
|
||||
i = i + 1
|
||||
return kind, self[kind], klass.types_by_kind[kind]
|
||||
end
|
||||
end
|
||||
|
||||
-- called for each new item. It does not actually create separate lists,
|
||||
-- (although that would not break the interface) but creates iterators
|
||||
-- for that item type if not already created.
|
||||
function KindMap:add (item,items)
|
||||
local kname = self.klass.types_by_tag[item.type]
|
||||
if not self[kname] then
|
||||
self[kname] = M.type_iterator (items,'type',item.type)
|
||||
end
|
||||
end
|
||||
|
||||
-- KindMap has a 'class constructor' which is used to modify
|
||||
-- any new base class.
|
||||
function KindMap._class_init (klass)
|
||||
klass.kinds = {} -- list in correct order of kinds
|
||||
klass.types_by_tag = {} -- indexed by tag
|
||||
klass.types_by_kind = {} -- indexed by kind
|
||||
end
|
||||
|
||||
|
||||
function KindMap.add_kind (klass,tag,kind,subnames)
|
||||
klass.types_by_tag[tag] = kind
|
||||
klass.types_by_kind[kind] = {type=tag,subnames=subnames}
|
||||
append(klass.kinds,kind)
|
||||
end
|
||||
|
||||
|
||||
----- some useful utility functions ------
|
||||
|
||||
function M.module_basepath()
|
||||
local lpath = List.split(package.path,';')
|
||||
for p in lpath:iter() do
|
||||
local p = path.dirname(p)
|
||||
if path.isabs(p) then
|
||||
return p
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- split a qualified name into the module part and the name part,
|
||||
-- e.g 'pl.utils.split' becomes 'pl.utils' and 'split'
|
||||
function M.split_dotted_name (s)
|
||||
local s1,s2 = path.splitext(s)
|
||||
if s2=='' then return nil
|
||||
else return s1,s2:sub(2)
|
||||
end
|
||||
end
|
||||
|
||||
-- expand lists of possibly qualified identifiers
|
||||
-- given something like {'one , two.2','three.drei.drie)'}
|
||||
-- it will output {"one","two.2","three.drei.drie"}
|
||||
function M.expand_comma_list (ls)
|
||||
local new_ls = List()
|
||||
for s in ls:iter() do
|
||||
s = s:gsub('[^%.:%w]*$','')
|
||||
if s:find ',' then
|
||||
new_ls:extend(List.split(s,'%s*,%s*'))
|
||||
else
|
||||
new_ls:append(s)
|
||||
end
|
||||
end
|
||||
return new_ls
|
||||
end
|
||||
|
||||
function M.extract_identifier (value)
|
||||
return value:match('([%.:_%w]+)')
|
||||
end
|
||||
|
||||
function M.strip (s)
|
||||
return s:gsub('^%s+',''):gsub('%s+$','')
|
||||
end
|
||||
|
||||
function M.check_directory(d)
|
||||
if not path.isdir(d) then
|
||||
lfs.mkdir(d)
|
||||
end
|
||||
end
|
||||
|
||||
function M.check_file (f,original)
|
||||
if not path.exists(f) then
|
||||
dir.copyfile(original,f)
|
||||
end
|
||||
end
|
||||
|
||||
function M.writefile(name,text)
|
||||
local ok,err = utils.writefile(name,text)
|
||||
if err then quit(err) end
|
||||
end
|
||||
|
||||
function M.name_of (lpath)
|
||||
lpath,ext = path.splitext(lpath)
|
||||
return lpath
|
||||
end
|
||||
|
||||
function M.this_module_name (basename,fname)
|
||||
local ext
|
||||
if basename == '' then
|
||||
--quit("module(...) needs package basename")
|
||||
return M.name_of(fname)
|
||||
end
|
||||
basename = path.abspath(basename)
|
||||
if basename:sub(-1,-1) ~= path.sep then
|
||||
basename = basename..path.sep
|
||||
end
|
||||
local lpath,cnt = fname:gsub('^'..utils.escape(basename),'')
|
||||
if cnt ~= 1 then quit("module(...) name deduction failed: base "..basename.." "..fname) end
|
||||
lpath = lpath:gsub(path.sep,'.')
|
||||
return M.name_of(lpath)
|
||||
end
|
||||
|
||||
|
||||
--------- lexer tools -----
|
||||
|
||||
local tnext = lexer.skipws
|
||||
|
||||
local function type_of (tok) return tok[1] end
|
||||
local function value_of (tok) return tok[2] end
|
||||
|
||||
-- This parses Lua formal argument lists. It will return a list of argument
|
||||
-- names, which also has a comments field, which will contain any commments
|
||||
-- following the arguments. ldoc will use these in addition to explicit
|
||||
-- param tags.
|
||||
|
||||
function M.get_parameters (tok)
|
||||
local args = List()
|
||||
args.comments = {}
|
||||
local ltl = lexer.get_separated_list(tok)
|
||||
|
||||
if #ltl[1] == 0 then return args end -- no arguments
|
||||
|
||||
local function set_comment (idx,tok)
|
||||
args.comments[args[idx]] = value_of(tok)
|
||||
end
|
||||
|
||||
for i = 1,#ltl do
|
||||
local tl = ltl[i]
|
||||
if type_of(tl[1]) == 'comment' then
|
||||
if i > 1 then set_comment(i-1,tl[1]) end
|
||||
if #tl > 1 then
|
||||
args:append(value_of(tl[2]))
|
||||
end
|
||||
else
|
||||
args:append(value_of(tl[1]))
|
||||
end
|
||||
if i == #ltl then
|
||||
local last_tok = tl[#tl]
|
||||
if #tl > 1 and type_of(last_tok) == 'comment' then
|
||||
set_comment(i,last_tok)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return args
|
||||
end
|
||||
|
||||
-- parse a Lua identifier - contains names separated by . and :.
|
||||
function M.get_fun_name (tok)
|
||||
local res = {}
|
||||
local _,name = tnext(tok)
|
||||
_,sep = tnext(tok)
|
||||
while sep == '.' or sep == ':' do
|
||||
append(res,name)
|
||||
append(res,sep)
|
||||
_,name = tnext(tok)
|
||||
_,sep = tnext(tok)
|
||||
end
|
||||
append(res,name)
|
||||
return table.concat(res)
|
||||
end
|
||||
|
||||
-- space-skipping version of token iterator
|
||||
function M.space_skip_getter(tok)
|
||||
return function ()
|
||||
local t,v = tok()
|
||||
while t and t == 'space' do
|
||||
t,v = tok()
|
||||
end
|
||||
return t,v
|
||||
end
|
||||
end
|
||||
|
||||
-- an embarassing function. The PL Lua lexer does not do block comments
|
||||
-- when used in line-grabbing mode, and in fact (0.9.4) does not even
|
||||
-- do them properly in full-text mode, due to a ordering mistake.
|
||||
-- So, we do what we can ;)
|
||||
function M.grab_block_comment (v,tok,end1,end2)
|
||||
local res = {v}
|
||||
local t,last_v
|
||||
repeat
|
||||
last_v = v
|
||||
t,v = tok()
|
||||
append(res,v)
|
||||
until last_v == end1 and v == end2
|
||||
table.remove(res)
|
||||
table.remove(res)
|
||||
res = table.concat(res)
|
||||
return 'comment',res
|
||||
end
|
||||
|
||||
|
||||
|
||||
return tools
|
||||
|
|
Loading…
Reference in New Issue