summaryrefslogtreecommitdiff
path: root/Data/BuiltIn/Libraries/lua-stdlib/string.lua
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2021-11-15 21:59:41 +0800
committerchai <chaifix@163.com>2021-11-15 21:59:41 +0800
commit6530ce383ead7a2cb227ed898ee4563a4c078b24 (patch)
tree054cb5c72f865055c9095366079a4958d5b5cc2c /Data/BuiltIn/Libraries/lua-stdlib/string.lua
parentb70f10f2988771d69f73cf36364bba77d0061d28 (diff)
+tuple
Diffstat (limited to 'Data/BuiltIn/Libraries/lua-stdlib/string.lua')
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/string.lua505
1 files changed, 505 insertions, 0 deletions
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/string.lua b/Data/BuiltIn/Libraries/lua-stdlib/string.lua
new file mode 100644
index 0000000..6ad9014
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/string.lua
@@ -0,0 +1,505 @@
+--[[
+ General Lua Libraries for Lua 5.1, 5.2 & 5.3
+ Copyright (C) 2002-2018 stdlib authors
+]]
+--[[--
+ Additions to the core string module.
+
+ The module table returned by `std.string` also contains all of the entries
+ from the core string table. An hygienic way to import this module, then, is
+ simply to override the core `string` locally:
+
+ local string = require 'std.string'
+
+ @corelibrary std.string
+]]
+
+
+local _ = require 'std._base'
+
+local argscheck = _.typecheck and _.std.typecheck.argscheck
+local escape_pattern = _.string.escape_pattern
+local split = _.string.split
+
+_ = nil
+
+
+local _ENV = require 'std.normalize' {
+ 'string',
+ abs = 'math.abs',
+ concat = 'table.concat',
+ find = 'string.find',
+ floor = 'math.floor',
+ format = 'string.format',
+ gsub = 'string.gsub',
+ insert = 'table.insert',
+ match = 'string.match',
+ merge = 'table.merge',
+ render = 'string.render',
+ sort = 'table.sort',
+ sub = 'string.sub',
+ upper = 'string.upper',
+}
+
+
+
+--[[ =============== ]]--
+--[[ Implementation. ]]--
+--[[ =============== ]]--
+
+
+local M
+
+
+local function toqstring(x, xstr)
+ if type(x) ~= 'string' then
+ return xstr
+ end
+ return format('%q', x)
+end
+
+
+local concatvfns = {
+ elem = tostring,
+ term = function(x)
+ return type(x) ~= 'table' or getmetamethod(x, '__tostring')
+ end,
+ sort = function(keys)
+ return keys
+ end,
+ open = function(x) return '{' end,
+ close = function(x) return '}' end,
+ pair = function(x, kp, vp, k, v, kstr, vstr, seqp)
+ return toqstring(k, kstr) .. '=' .. toqstring(v, vstr)
+ end,
+ sep = function(x, kp, vp, kn, vn, seqp)
+ return kp ~= nil and kn ~= nil and ',' or ''
+ end,
+}
+
+
+local function __concat(s, o)
+ -- Don't use normalize.str here, because we don't want ASCII escape rendering.
+ return render(s, concatvfns) .. render(o, concatvfns)
+end
+
+
+local function __index(s, i)
+ if type(i) == 'number' then
+ return sub(s, i, i)
+ else
+ -- Fall back to module metamethods
+ return M[i]
+ end
+end
+
+
+local _format = string.format
+
+local function format(f, arg1, ...)
+ return(arg1 ~= nil) and _format(f, arg1, ...) or f
+end
+
+
+local function tpack(from, to, ...)
+ return from, to, {...}
+end
+
+local function tfind(s, ...)
+ return tpack(find(s, ...))
+end
+
+
+local function finds(s, p, i, ...)
+ i = i or 1
+ local l = {}
+ local from, to, r
+ repeat
+ from, to, r = tfind(s, p, i, ...)
+ if from ~= nil then
+ insert(l, {from, to, capt=r})
+ i = to + 1
+ end
+ until not from
+ return l
+end
+
+
+local function caps(s)
+ return(gsub(s, '(%w)([%w]*)', function(l, ls)
+ return upper(l) .. ls
+ end))
+end
+
+
+local function escape_shell(s)
+ return(gsub(s, '([ %(%)%\\%[%]\'"])', '\\%1'))
+end
+
+
+local function ordinal_suffix(n)
+ n = abs(n) % 100
+ local d = n % 10
+ if d == 1 and n ~= 11 then
+ return 'st'
+ elseif d == 2 and n ~= 12 then
+ return 'nd'
+ elseif d == 3 and n ~= 13 then
+ return 'rd'
+ else
+ return 'th'
+ end
+end
+
+
+local function pad(s, w, p)
+ p = string.rep(p or ' ', abs(w))
+ if w < 0 then
+ return string.sub(p .. s, w)
+ end
+ return string.sub(s .. p, 1, w)
+end
+
+
+local function wrap(s, w, ind, ind1)
+ w = w or 78
+ ind = ind or 0
+ ind1 = ind1 or ind
+ assert(ind1 < w and ind < w,
+ 'the indents must be less than the line width')
+ local r = {string.rep(' ', ind1)}
+ local i, lstart, lens = 1, ind1, len(s)
+ while i <= lens do
+ local j = i + w - lstart
+ while len(s[j]) > 0 and s[j] ~= ' ' and j > i do
+ j = j - 1
+ end
+ local ni = j + 1
+ while s[j] == ' ' do
+ j = j - 1
+ end
+ insert(r, sub(s, i, j))
+ i = ni
+ if i < lens then
+ insert(r, '\n' .. string.rep(' ', ind))
+ lstart = ind
+ end
+ end
+ return concat(r)
+end
+
+
+local function numbertosi(n)
+ local SIprefix = {
+ [-8]='y', [-7]='z', [-6]='a', [-5]='f',
+ [-4]='p', [-3]='n', [-2]='mu', [-1]='m',
+ [0]='', [1]='k', [2]='M', [3]='G',
+ [4]='T', [5]='P', [6]='E', [7]='Z',
+ [8]='Y'
+ }
+ local t = _format('% #.2e', n)
+ local _, _, m, e = find(t, '.(.%...)e(.+)')
+ local man, exp = tonumber(m), tonumber(e)
+ local siexp = floor(exp / 3)
+ local shift = exp - siexp * 3
+ local s = SIprefix[siexp] or 'e' .. tostring(siexp)
+ man = man *(10 ^ shift)
+ return _format('%0.f', man) .. s
+end
+
+
+-- Ordor numbers first then asciibetically.
+local function keycmp(a, b)
+ if type(a) == 'number' then
+ return type(b) ~= 'number' or a < b
+ end
+ return type(b) ~= 'number' and tostring(a) < tostring(b)
+end
+
+
+local render_fallbacks = {
+ __index = concatvfns,
+}
+
+
+local function prettytostring(x, indent, spacing)
+ indent = indent or '\t'
+ spacing = spacing or ''
+ return render(x, setmetatable({
+ elem = function(x)
+ if type(x) ~= 'string' then
+ return tostring(x)
+ end
+ return format('%q', x)
+ end,
+
+ sort = function(keylist)
+ sort(keylist, keycmp)
+ return keylist
+ end,
+
+ open = function()
+ local s = spacing .. '{'
+ spacing = spacing .. indent
+ return s
+ end,
+
+ close = function()
+ spacing = string.gsub(spacing, indent .. '$', '')
+ return spacing .. '}'
+ end,
+
+ pair = function(x, _, _, k, v, kstr, vstr)
+ local type_k = type(k)
+ local s = spacing
+ if type_k ~= 'string' or match(k, '[^%w_]') then
+ s = s .. '['
+ if type_k == 'table' then
+ s = s .. '\n'
+ end
+ s = s .. kstr
+ if type_k == 'table' then
+ s = s .. '\n'
+ end
+ s = s .. ']'
+ else
+ s = s .. k
+ end
+ s = s .. ' ='
+ if type(v) == 'table' then
+ s = s .. '\n'
+ else
+ s = s .. ' '
+ end
+ s = s .. vstr
+ return s
+ end,
+
+ sep = function(_, k)
+ local s = '\n'
+ if k then
+ s = ',' .. s
+ end
+ return s
+ end,
+ }, render_fallbacks))
+end
+
+
+local function trim(s, r)
+ r = r or '%s+'
+ return (gsub(gsub(s, '^' .. r, ''), r .. '$', ''))
+end
+
+
+
+--[[ ================= ]]--
+--[[ Public Interface. ]]--
+--[[ ================= ]]--
+
+
+local function X(decl, fn)
+ return argscheck and argscheck('std.string.' .. decl, fn) or fn
+end
+
+M = {
+ --- Metamethods
+ -- @section metamethods
+
+ --- String concatenation operation.
+ -- @function __concat
+ -- @string s initial string
+ -- @param o object to stringify and concatenate
+ -- @return s .. tostring(o)
+ -- @usage
+ -- local string = setmetatable('', require 'std.string')
+ -- concatenated = 'foo' .. {'bar'}
+ __concat = __concat,
+
+ --- String subscript operation.
+ -- @function __index
+ -- @string s string
+ -- @tparam int|string i index or method name
+ -- @return `sub(s, i, i)` if i is a number, otherwise
+ -- fall back to a `std.string` metamethod(if any).
+ -- @usage
+ -- getmetatable('').__index = require 'std.string'.__index
+ -- third =('12345')[3]
+ __index = __index,
+
+
+ --- Core Functions
+ -- @section corefuncs
+
+ --- Capitalise each word in a string.
+ -- @function caps
+ -- @string s any string
+ -- @treturn string *s* with each word capitalized
+ -- @usage
+ -- userfullname = caps(input_string)
+ caps = X('caps(string)', caps),
+
+ --- Remove any final newline from a string.
+ -- @function chomp
+ -- @string s any string
+ -- @treturn string *s* with any single trailing newline removed
+ -- @usage
+ -- line = chomp(line)
+ chomp = X('chomp(string)', function(s)
+ return(gsub(s, '\n$', ''))
+ end),
+
+ --- Escape a string to be used as a pattern.
+ -- @function escape_pattern
+ -- @string s any string
+ -- @treturn string *s* with active pattern characters escaped
+ -- @usage
+ -- substr = match(inputstr, escape_pattern(literal))
+ escape_pattern = X('escape_pattern(string)', escape_pattern),
+
+ --- Escape a string to be used as a shell token.
+ -- Quotes spaces, parentheses, brackets, quotes, apostrophes and
+ -- whitespace.
+ -- @function escape_shell
+ -- @string s any string
+ -- @treturn string *s* with active shell characters escaped
+ -- @usage
+ -- os.execute('echo ' .. escape_shell(outputstr))
+ escape_shell = X('escape_shell(string)', escape_shell),
+
+ --- Repeatedly `string.find` until target string is exhausted.
+ -- @function finds
+ -- @string s target string
+ -- @string pattern pattern to match in *s*
+ -- @int[opt=1] init start position
+ -- @bool[opt] plain inhibit magic characters
+ -- @return list of `{from, to; capt={captures}}`
+ -- @see std.string.tfind
+ -- @usage
+ -- for t in std.elems(finds('the target string', '%S+')) do
+ -- print(tostring(t.capt))
+ -- end
+ finds = X('finds(string, string, ?int, ?boolean|:plain)', finds),
+
+ --- Extend to work better with one argument.
+ -- If only one argument is passed, no formatting is attempted.
+ -- @function format
+ -- @string f format string
+ -- @param[opt] ... arguments to format
+ -- @return formatted string
+ -- @usage
+ -- print(format '100% stdlib!')
+ format = X('format(string, [any...])', format),
+
+ --- Remove leading matter from a string.
+ -- @function ltrim
+ -- @string s any string
+ -- @string[opt='%s+'] r leading pattern
+ -- @treturn string *s* with leading *r* stripped
+ -- @usage
+ -- print('got: ' .. ltrim(userinput))
+ ltrim = X('ltrim(string, ?string)', function(s, r)
+ return (gsub(s, '^' ..(r or '%s+'), ''))
+ end),
+
+ --- Write a number using SI suffixes.
+ -- The number is always written to 3 s.f.
+ -- @function numbertosi
+ -- @tparam number|string n any numeric value
+ -- @treturn string *n* simplifed using largest available SI suffix.
+ -- @usage
+ -- print(numbertosi(bitspersecond) .. 'bps')
+ numbertosi = X('numbertosi(number|string)', numbertosi),
+
+ --- Return the English suffix for an ordinal.
+ -- @function ordinal_suffix
+ -- @tparam int|string n any integer value
+ -- @treturn string English suffix for *n*
+ -- @usage
+ -- local now = os.date '*t'
+ -- print('%d%s day of the week', now.day, ordinal_suffix(now.day))
+ ordinal_suffix = X('ordinal_suffix(int|string)', ordinal_suffix),
+
+ --- Justify a string.
+ -- When the string is longer than w, it is truncated(left or right
+ -- according to the sign of w).
+ -- @function pad
+ -- @string s a string to justify
+ -- @int w width to justify to(-ve means right-justify; +ve means
+ -- left-justify)
+ -- @string[opt=' '] p string to pad with
+ -- @treturn string *s* justified to *w* characters wide
+ -- @usage
+ -- print(pad(trim(outputstr, 78)) .. '\n')
+ pad = X('pad(string, int, ?string)', pad),
+
+ --- Pretty-print a table, or other object.
+ -- @function prettytostring
+ -- @param x object to convert to string
+ -- @string[opt='\t'] indent indent between levels
+ -- @string[opt=''] spacing space before every line
+ -- @treturn string pretty string rendering of *x*
+ -- @usage
+ -- print(prettytostring(std, ' '))
+ prettytostring = X('prettytostring(?any, ?string, ?string)', prettytostring),
+
+ --- Remove trailing matter from a string.
+ -- @function rtrim
+ -- @string s any string
+ -- @string[opt='%s+'] r trailing pattern
+ -- @treturn string *s* with trailing *r* stripped
+ -- @usage
+ -- print('got: ' .. rtrim(userinput))
+ rtrim = X('rtrim(string, ?string)', function(s, r)
+ return (gsub(s, (r or '%s+') .. '$', ''))
+ end),
+
+ --- Split a string at a given separator.
+ -- Separator is a Lua pattern, so you have to escape active characters,
+ -- `^$()%.[]*+-?` with a `%` prefix to match a literal character in *s*.
+ -- @function split
+ -- @string s to split
+ -- @string[opt='%s+'] sep separator pattern
+ -- @return list of strings
+ -- @usage
+ -- words = split 'a very short sentence'
+ split = X('split(string, ?string)', split),
+
+ --- Do `string.find`, returning a table of captures.
+ -- @function tfind
+ -- @string s target string
+ -- @string pattern pattern to match in *s*
+ -- @int[opt=1] init start position
+ -- @bool[opt] plain inhibit magic characters
+ -- @treturn int start of match
+ -- @treturn int end of match
+ -- @treturn table list of captured strings
+ -- @see std.string.finds
+ -- @usage
+ -- b, e, captures = tfind('the target string', '%s', 10)
+ tfind = X('tfind(string, string, ?int, ?boolean|:plain)', tfind),
+
+ --- Remove leading and trailing matter from a string.
+ -- @function trim
+ -- @string s any string
+ -- @string[opt='%s+'] r trailing pattern
+ -- @treturn string *s* with leading and trailing *r* stripped
+ -- @usage
+ -- print('got: ' .. trim(userinput))
+ trim = X('trim(string, ?string)', trim),
+
+ --- Wrap a string into a paragraph.
+ -- @function wrap
+ -- @string s a paragraph of text
+ -- @int[opt=78] w width to wrap to
+ -- @int[opt=0] ind indent
+ -- @int[opt=ind] ind1 indent of first line
+ -- @treturn string *s* wrapped to *w* columns
+ -- @usage
+ -- print(wrap(copyright, 72, 4))
+ wrap = X('wrap(string, ?int, ?int, ?int)', wrap),
+}
+
+
+return merge(string, M)
+