|sclass=2 – for ship classes only; displays class name without italics (parameter name is loosely similar to which does the same thing); use when |infobox caption=class
|adj=off – for ship classes only; displays class name as a noun (no hyphen, no ship type); use when |infobox caption=class  
Arguments are passed in a table.  
to call this function locally:
do_ship_name_format ({['name=name'], ['dab']=dab, ['sclass']=sclass, ['adj']=adj, ['showerrs']=showerrs}) or
args = {['name=name'], ['dab']=dab, ['sclass']=sclass, ['adj']=adj, ['showerrs']=showerrs};
do_ship_name_format (args)  
The function returns the formatted name or, if unable to format the name, the original name and an unformatted error message.  
local function do_ship_name_format (args)
local name_sans_dab; -- the ship or class name without a trailing parenthetical dab
local dab; -- the dab stripped from the name
local fragments = {}; -- a table of words that make up name_sans_dab
local ship_type; -- a word or phrase that describes a ship
local type_len; -- the number of words that describe a ship
local nat_len; -- the number of words used to specify a ship's nationality
local name = ''; -- the reassembles and formatted ship name
local error_msg = ''; -- a repository for error messages if any  
-- args.name = mw.text.decode (args.name); -- replace html entities in title with their characters; doesn't work for &amp; and &#38; in prefix
args.name = args.name:gsub ("&#39;", "\'"); -- replace html appostrophe with the character
-- args.name = args.name:gsub ("&amp;", "&");
-- args.name = args.name:gsub ("&#38;", "&");
-- args.name = args.name:gsub ("&amp;", "&#38;");  
if args.name:match ('.+%-class%s+%a+') then -- if a ship-class
fragments = mw.text.split (args.name, '-class' ); -- split at -class
if '2' == args.sclass then -- for DISPLAYTITLE and infobox caption when class not named for a member of the class
if 'off' == args.adj then
return fragments[1] .. ' class'; -- for infobox caption do noun form <name> class (no hyphen, no ship type)
end
return args.name; -- nothing to do so return original unformatted name
end
if 'off' == args.adj then
return "*" .. fragments[1] .. "* class"; -- for infobox caption do noun form <name> class (no hyphen, no ship type)
end
return "*" .. fragments[1] .. "*-class" .. fragments[2]; -- and return formatted adjectival name
end
-- not a ship class so try to format a ship name
name_sans_dab, dab = args.name:match('^(.+)%s+(%b)%s*$'); -- split name into name_sans_dab and dab
if is_set (dab) then
dab = ' ' .. dab; -- insert a space for later reassembly
else
name_sans_dab = args.name; -- because without a dab, the string.match returns nil
dab = ''; -- empty string for concatenation
end  
fragments = mw.text.split (name_sans_dab, '%s' ); -- split into a table of separate words  
nat_len = sizeof_nationality (fragments, #fragments); -- get the number of words in the ship's nationality
if 0 < nat_len then -- if not zero we have a valid nationality
type_len = sizeof_ship_type (fragments, #fragments, nat_len); -- get the number of words in the ship type
if 0 < type_len then -- if not zero, ship type is valid; nationality and type not italics, the rest is name
name = "*" .. table.concat (fragments, ' ', nat_len + type_len + 1) .. "*"; -- format name
if '' == args.dab then -- for |infobox caption=nodab
return name; -- return the formatted name without the nationality or ship type or dab
end
name = table.concat (fragments, ' ', 1, nat_len + type_len) .. " " .. name; -- assemble everything but dab
else
error_msg = ' unrecognized ship type;'; -- valid nationality, invalid ship type
end
elseif ship_prefix_list[fragments[1]] then -- if the first fragment is a ship prefix
name = table.remove (fragments, 1); -- fetch it from the table
name = name .. " *" .. table.concat (fragments, ' ') .. "*"; -- assemble formatted name
else
error_msg = ' no nationality or prefix;'; -- invalid nationality and first word in ship name not a valid prefix
end  
if is_set (name) then -- name will be set if we were able to format it
if '' == args.dab then -- for |infobox caption=nodab
return name; -- return the formatted name without the dab
end
return name .. dab; -- return the formatted name with the dab
end  
if is_set (dab) then
if dab:match ('%(%u+[%- ]?%d+%)') or -- one or more uppercase letters, optional space or hyphen, one or more digits
dab:match ('%(%d+[%- ]?%u+%)') or -- one or more digits, optional space or hyphen, one or more uppercase letters
dab:match ('%(%u[%u%-]*%-%d+%)') or -- one or more uppercase letters with hyphens, a hyphen, one or more digits (e.g., T-AO-157)
dab:match ('%([12]%d%d%d%)') then -- four digits representing year in the range 1000–2999
name = "*" .. table.concat (fragments, ' ') .. "*"; -- format the name
if '' == args.dab then -- for |infobox caption=nodab
return name; -- return the formatted name without the dab
end
return name .. dab; -- return the formatted name with the dab
end
-- last chance, is there a ship type in the dab?
for key, _ in pairs (ship_type_list) do -- spin through the ship type list and see if there is a ship type (key) in the dab
if dab:find ('%f[%a]' .. key .. '%f[^%a]') then -- avoid matches that are not whole word
name = "*" .. table.concat (fragments, ' ') .. "*"; -- format the name
if '' == args.dab then -- for |infobox caption=nodab
return name; -- return the formatted name without the dab
end
return name .. dab; -- return the formatted name with the dab
end
end
error_msg = error_msg .. ' no ship type in dab;';  
if '' == args.dab then -- for |infobox caption=nodab
return table.concat (fragments, ' '), error_msg; -- return the unformatted name without the dab, and an error message
end
end  
return args.name, error_msg; -- return original un-formatted name with unformatted error message if any
end  
--name= (required): a name is required; if missing or empty, this function returns an error message (may or may not be visible depending on where it is used)
used in to provide a value for and to provide a value for |infobox caption=  
Optional parameters to support :
|dab= – displays ship name without parenthetical disambiguator; use when |infobox caption=nodab
|sclass=2 – for ship classes only; displays class name without italics (parameter name is loosely similar to which does the same thing); use when |infobox caption=class
|adj=off – for ship classes only; displays class name as a noun (no hyphen, no ship type); use when |infobox caption=class
Other optional parameters:
|showerrs=yes – marginally useful; can display error messages if the module invocation is not buried in a template  
Values from the above parameters are placed in a table and that table passed as an argument in the call to do_ship_name_format.  
do_ship_name_format returns two strings: a name and an error message. If do_ship_name_format could format the name, it returns the formatted name and
an empty string for the error message. If it could not format the name, do_ship_name_format returns the original name and an error message.  
Formatting of the error message, in response to |showerrs=yes is the responsibility of the calling function.  
function wpsu.ship_name_format
local name = ''; -- destination of the formatted ship name
local error_msg = ''; -- destination of any error message  
if not is_set (.args.name) then -- if a ship name not provided
if 'yes' == .args.showerrs then -- and we're supposed to show errors
error_msg = '<span>Empty name</span>'; -- return an empty string error message if there is no name
end
else
name, error_msg = do_ship_name_format (.args); -- get formatted name and error message
if is_set (error_msg) and 'yes' == .args.showerrs then -- if appropriate, show error message
error_msg = '<span>' .. error_msg .. '</span>';
else
error_msg = ''; -- for concatenation
end
end  
return name .. error_msg; -- return name and error message
end