Module:String: Difference between revisions

1,180 bytes added ,  5 years ago
Implement the merge of Module:Join, Module:Str endswith, Module:PatternCount and Module:Text count into this module per their TfDs. Review and discussion is at https://en.wikipedia.org/w/index.php?title=Module_talk:String&oldid=899040020#Edit_request_to_implement_merges
(update renamed variable)
(Implement the merge of Module:Join, Module:Str endswith, Module:PatternCount and Module:Text count into this module per their TfDs. Review and discussion is at https://en.wikipedia.org/w/index.php?title=Module_talk:String&oldid=899040020#Edit_request_to_implement_merges)
Line 42: Line 42:
]]
]]
function str.len( frame )
function str.len( frame )
local new_args = str._getParameters( frame.args, {'s'} );
local new_args = str._getParameters( frame.args, {'s'} )
local s = new_args['s'] or '';
local s = new_args['s'] or ''
return mw.ustring.len( s )
return mw.ustring.len( s )
end
end
Line 71: Line 71:
]]
]]
function str.sub( frame )
function str.sub( frame )
local new_args = str._getParameters( frame.args, { 's', 'i', 'j' } );
local new_args = str._getParameters( frame.args, { 's', 'i', 'j' } )
local s = new_args['s'] or '';
local s = new_args['s'] or ''
local i = tonumber( new_args['i'] ) or 1;
local i = tonumber( new_args['i'] ) or 1
local j = tonumber( new_args['j'] ) or -1;
local j = tonumber( new_args['j'] ) or -1


local len = mw.ustring.len( s );
local len = mw.ustring.len( s )


-- Convert negatives for range checking
-- Convert negatives for range checking
if i < 0 then
if i < 0 then
i = len + i + 1;
i = len + i + 1
end
end
if j < 0 then
if j < 0 then
j = len + j + 1;
j = len + j + 1
end
end


if i > len or j > len or i < 1 or j < 1 then
if i > len or j > len or i < 1 or j < 1 then
return str._error( 'String subset index out of range' );
return str._error( 'String subset index out of range' )
end
end
if j < i then
if j < i then
return str._error( 'String subset indices out of order' );
return str._error( 'String subset indices out of order' )
end
end


Line 151: Line 151:
function str._match( s, pattern, start, match_index, plain_flag, nomatch )
function str._match( s, pattern, start, match_index, plain_flag, nomatch )
if s == '' then
if s == '' then
return str._error( 'Target string is empty' );
return str._error( 'Target string is empty' )
end
end
if pattern == '' then
if pattern == '' then
return str._error( 'Pattern string is empty' );
return str._error( 'Pattern string is empty' )
end
end
start = tonumber(start) or 1
start = tonumber(start) or 1
if math.abs(start) < 1 or math.abs(start) > mw.ustring.len( s ) then
if math.abs(start) < 1 or math.abs(start) > mw.ustring.len( s ) then
return str._error( 'Requested start is out of range' );
return str._error( 'Requested start is out of range' )
end
end
if match_index == 0 then
if match_index == 0 then
return str._error( 'Match index is out of range' );
return str._error( 'Match index is out of range' )
end
end
if plain_flag then
if plain_flag then
pattern = str._escapePattern( pattern );
pattern = str._escapePattern( pattern )
end
end


Line 173: Line 173:
else
else
if start > 1 then
if start > 1 then
s = mw.ustring.sub( s, start );
s = mw.ustring.sub( s, start )
end
end


local iterator = mw.ustring.gmatch(s, pattern);
local iterator = mw.ustring.gmatch(s, pattern)
if match_index > 0 then
if match_index > 0 then
-- Forward search
-- Forward search
for w in iterator do
for w in iterator do
match_index = match_index - 1;
match_index = match_index - 1
if match_index == 0 then
if match_index == 0 then
result = w;
result = w
break;
break
end
end
end
end
else
else
-- Reverse search
-- Reverse search
local result_table = {};
local result_table = {}
local count = 1;
local count = 1
for w in iterator do
for w in iterator do
result_table[count] = w;
result_table[count] = w
count = count + 1;
count = count + 1
end
end


result = result_table[ count + match_index ];
result = result_table[ count + match_index ]
end
end
end
end
Line 201: Line 201:
if result == nil then
if result == nil then
if nomatch == nil then
if nomatch == nil then
return str._error( 'Match not found' );
return str._error( 'Match not found' )
else
else
return nomatch;
return nomatch
end
end
else
else
return result;
return result
end
end
end
end
-- This is the entry point for #invoke:String|match
-- This is the entry point for #invoke:String|match
function str.match( frame )
function str.match( frame )
local new_args = str._getParameters( frame.args, {'s', 'pattern', 'start', 'match', 'plain', 'nomatch'} );
local new_args = str._getParameters( frame.args, {'s', 'pattern', 'start', 'match', 'plain', 'nomatch'} )
local s = new_args['s'] or '';
local s = new_args['s'] or ''
local start = tonumber( new_args['start'] ) or 1;
local start = tonumber( new_args['start'] ) or 1
local plain_flag = str._getBoolean( new_args['plain'] or false );
local plain_flag = str._getBoolean( new_args['plain'] or false )
local pattern = new_args['pattern'] or '';
local pattern = new_args['pattern'] or ''
local match_index = math.floor( tonumber(new_args['match']) or 1 );
local match_index = math.floor( tonumber(new_args['match']) or 1 )
local nomatch = new_args['nomatch'];
local nomatch = new_args['nomatch']


return str._match( s, pattern, start, match_index, plain_flag, nomatch )
return str._match( s, pattern, start, match_index, plain_flag, nomatch )
Line 248: Line 248:
]]
]]
function str.pos( frame )
function str.pos( frame )
local new_args = str._getParameters( frame.args, {'target', 'pos'} );
local new_args = str._getParameters( frame.args, {'target', 'pos'} )
local target_str = new_args['target'] or '';
local target_str = new_args['target'] or ''
local pos = tonumber( new_args['pos'] ) or 0;
local pos = tonumber( new_args['pos'] ) or 0


if pos == 0 or math.abs(pos) > mw.ustring.len( target_str ) then
if pos == 0 or math.abs(pos) > mw.ustring.len( target_str ) then
return str._error( 'String index out of range' );
return str._error( 'String index out of range' )
end
end


return mw.ustring.sub( target_str, pos, pos );
return mw.ustring.sub( target_str, pos, pos )
end
end


Line 274: Line 274:
]]
]]
function str.str_find( frame )
function str.str_find( frame )
local new_args = str._getParameters( frame.args, {'source', 'target'} );
local new_args = str._getParameters( frame.args, {'source', 'target'} )
local source_str = new_args['source'] or '';
local source_str = new_args['source'] or ''
local target_str = new_args['target'] or '';
local target_str = new_args['target'] or ''


if target_str == '' then
if target_str == '' then
return 1;
return 1
end
end


Line 320: Line 320:
]]
]]
function str.find( frame )
function str.find( frame )
local new_args = str._getParameters( frame.args, {'source', 'target', 'start', 'plain' } );
local new_args = str._getParameters( frame.args, {'source', 'target', 'start', 'plain' } )
local source_str = new_args['source'] or '';
local source_str = new_args['source'] or ''
local pattern = new_args['target'] or '';
local pattern = new_args['target'] or ''
local start_pos = tonumber(new_args['start']) or 1;
local start_pos = tonumber(new_args['start']) or 1
local plain = new_args['plain'] or true;
local plain = new_args['plain'] or true


if source_str == '' or pattern == '' then
if source_str == '' or pattern == '' then
return 0;
return 0
end
end


plain = str._getBoolean( plain );
plain = str._getBoolean( plain )


local start = mw.ustring.find( source_str, pattern, start_pos, plain )
local start = mw.ustring.find( source_str, pattern, start_pos, plain )
Line 361: Line 361:
]]
]]
function str.replace( frame )
function str.replace( frame )
local new_args = str._getParameters( frame.args, {'source', 'pattern', 'replace', 'count', 'plain' } );
local new_args = str._getParameters( frame.args, {'source', 'pattern', 'replace', 'count', 'plain' } )
local source_str = new_args['source'] or '';
local source_str = new_args['source'] or ''
local pattern = new_args['pattern'] or '';
local pattern = new_args['pattern'] or ''
local replace = new_args['replace'] or '';
local replace = new_args['replace'] or ''
local count = tonumber( new_args['count'] );
local count = tonumber( new_args['count'] )
local plain = new_args['plain'] or true;
local plain = new_args['plain'] or true


if source_str == '' or pattern == '' then
if source_str == '' or pattern == '' then
return source_str;
return source_str
end
end
plain = str._getBoolean( plain );
plain = str._getBoolean( plain )


if plain then
if plain then
pattern = str._escapePattern( pattern );
pattern = str._escapePattern( pattern )
replace = mw.ustring.gsub( replace, "%%", "%%%%" ); --Only need to escape replacement sequences.
replace = mw.ustring.gsub( replace, "%%", "%%%%" ) --Only need to escape replacement sequences.
end
end


local result;
local result


if count ~= nil then
if count ~= nil then
result = mw.ustring.gsub( source_str, pattern, replace, count );
result = mw.ustring.gsub( source_str, pattern, replace, count )
else
else
result = mw.ustring.gsub( source_str, pattern, replace );
result = mw.ustring.gsub( source_str, pattern, replace )
end
end


return result;
return result
end
end


Line 392: Line 392:
     simple function to pipe string.rep to templates.
     simple function to pipe string.rep to templates.
]]
]]
function str.rep( frame )
function str.rep( frame )
local repetitions = tonumber( frame.args[2] )
local repetitions = tonumber( frame.args[2] )
Line 418: Line 417:
local pattern_str = frame.args[1]
local pattern_str = frame.args[1]
if not pattern_str then
if not pattern_str then
return str._error( 'No pattern string specified' );
return str._error( 'No pattern string specified' )
end
end
local result = str._escapePattern( pattern_str )
local result = str._escapePattern( pattern_str )
return result
return result
end
--[[
count
This function counts the number of occurrences of one string in another.
]]
function str.count(frame)
local args = str._getParameters(frame.args, {'source', 'pattern', 'plain'})
local source = args.source or ''
local pattern = args.pattern or ''
local plain = str._getBoolean(args.plain or true)
if plain then
pattern = str._escapePattern(pattern)
end
local _, count = mw.ustring.gsub(source, pattern, '')
return count
end
--[[
endswith
This function determines whether a string ends with another string.
]]
function str.endswith(frame)
local args = str._getParameters(frame.args, {'source', 'pattern'})
local source = args.source or ''
local pattern = args.pattern or ''
if pattern == '' then
-- All strings end with the empty string.
return "yes"
end
if mw.ustring.sub(source, -mw.ustring.len(pattern), -1) == pattern then
return "yes"
else
return ""
end
end
--[[
join
Join all non empty arguments together; the first argument is the separator.
Usage:
{{#invoke:String|join|sep|one|two|three}}
]]
function str.join(frame)
local args = {}
local sep
for _, v in ipairs( frame.args ) do
if sep then
if v ~= '' then
table.insert(args, v)
end
else
sep = v
end
end
return table.concat( args, sep or '' )
end
end


Line 431: Line 487:
]]
]]
function str._getParameters( frame_args, arg_list )
function str._getParameters( frame_args, arg_list )
local new_args = {};
local new_args = {}
local index = 1;
local index = 1
local value;
local value


for i,arg in ipairs( arg_list ) do
for _, arg in ipairs( arg_list ) do
value = frame_args[arg]
value = frame_args[arg]
if value == nil then
if value == nil then
value = frame_args[index];
value = frame_args[index]
index = index + 1;
index = index + 1
end
end
new_args[arg] = value;
new_args[arg] = value
end
end


return new_args;
return new_args
end
end


Line 451: Line 507:
]]
]]
function str._error( error_str )
function str._error( error_str )
local frame = mw.getCurrentFrame();
local frame = mw.getCurrentFrame()
local error_category = frame.args.error_category or 'Errors reported by Module String';
local error_category = frame.args.error_category or 'Errors reported by Module String'
local ignore_errors = frame.args.ignore_errors or false;
local ignore_errors = frame.args.ignore_errors or false
local no_category = frame.args.no_category or false;
local no_category = frame.args.no_category or false


if str._getBoolean(ignore_errors) then
if str._getBoolean(ignore_errors) then
return '';
return ''
end
end


local error_str = '<strong class="error">String Module Error: ' .. error_str .. '</strong>';
local error_str = '<strong class="error">String Module Error: ' .. error_str .. '</strong>'
if error_category ~= '' and not str._getBoolean( no_category ) then
if error_category ~= '' and not str._getBoolean( no_category ) then
error_str = '[[Category:' .. error_category .. ']]' .. error_str;
error_str = '[[Category:' .. error_category .. ']]' .. error_str
end
end


return error_str;
return error_str
end
end


Line 472: Line 528:
]]
]]
function str._getBoolean( boolean_str )
function str._getBoolean( boolean_str )
local boolean_value;
local boolean_value


if type( boolean_str ) == 'string' then
if type( boolean_str ) == 'string' then
boolean_str = boolean_str:lower();
boolean_str = boolean_str:lower()
if boolean_str == 'false' or boolean_str == 'no' or boolean_str == '0'
if boolean_str == 'false' or boolean_str == 'no' or boolean_str == '0'
or boolean_str == '' then
or boolean_str == '' then
boolean_value = false;
boolean_value = false
else
else
boolean_value = true;
boolean_value = true
end
end
elseif type( boolean_str ) == 'boolean' then
elseif type( boolean_str ) == 'boolean' then
boolean_value = boolean_str;
boolean_value = boolean_str
else
else
error( 'No boolean value found' );
error( 'No boolean value found' )
end
end
return boolean_value
return boolean_value
Line 495: Line 551:
]]
]]
function str._escapePattern( pattern_str )
function str._escapePattern( pattern_str )
return mw.ustring.gsub( pattern_str, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" );
return mw.ustring.gsub( pattern_str, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" )
end
end


return str
return str
Anonymous user