-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathstrong.lua
245 lines (198 loc) · 5.24 KB
/
strong.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
-- strong 1.0.4 - Michael Ebens
-- Licensed under the zlib/libpng license. See LICENSE.txt.
-- PRIVATE FUNCTIONS --
local function swapChar(c)
if c:isUpper() then return c:lower() end
return c:upper()
end
local function iter(state)
state.i = state.i + 1
local v = state.t[state.i]
if v then return v end
end
local function byteIter(state)
state.i = state.i + 1
local v = state.t[state.i]
if v then return v:byte() end
end
-- METATABLE --
local mt = getmetatable("")
local string = mt.__index
-- OPERATORS --
function mt.__add(a, b) return a .. b end
function mt.__sub(a, b) return a:gsub(b, "") end
function mt.__mul(a, b) return a:rep(b) end
function mt.__div(a, b) return a:split(b, true) end
function mt.__mod(a, b)
if type(b) == "table" then
return a:format(unpack(b))
else
return a:format(b)
end
end
-- INDEXING --
function mt:__index(key)
if type(key) == "number" then
local len = #self
if key > len or key < -len or key == 0 then return nil end
return self:sub(key, key)
else
return string[key]
end
end
function mt:__call(i, j)
if type(i) == "string" then
return self:match(i, j)
else
local len = #self
if i > len or i < -len or i == 0 then return nil end
return self:sub(i, j or i)
end
end
-- METHODS --
function string:bytes(all)
if all then
return { self:byte(1, -1) }
else
return byteIter, { t = self, i = 0 }
end
end
function string:camelize(upper)
self = self:lower():gsub("[ \t_%-](.)", string.upper)
return upper and self:gsub("^%l", string.upper) or self
end
function string:capitalize()
return self:lower():gsub("^%l", string.upper)
end
function string:center(int, padstr)
local len = #self
local diff = padstr and math.floor((int - len) / #padstr) or int - len
local left = len + math.ceil(diff / 2)
return self:ljust(left, padstr):rjust(left + math.floor(diff / 2), padstr)
end
function string:chars()
return iter, { t = self, i = 0 }
end
function string:chomp(pat)
return self:gsub((pat or "[\n\r]") .. "+$", "")
end
function string:endsWith(suffix)
return self:sub(-#suffix, -1) == suffix
end
function string:includes(pat, plain)
return self:find(pat, 1, plain) ~= nil
end
-- insertion postion:
-- ---------------------
-- | A | B | C | D | E |
-- ---------------------
-- 1 2 3 4 5 6
-- -6 -5 -4 -3 -2 -1
function string:insert(index, other)
index = (index < 0 and index + 1 or index) % (#self + 1)
if index == 1 then
return other .. self
elseif index == 0 then
return self .. other
else
return self:sub(1, index - 1) .. other .. self:sub(index)
end
end
function string:isLower()
return self:match("^%l+$") ~= nil
end
function string:isUpper()
return self:match("^%u+$") ~= nil
end
-- this doesn't behave like Ruby in that it discards the separator
-- it's method of detecting newlines is a bit dodgy too: \n\n would count as 1 separator
function string:lines(sep, all)
if type(sep) == "boolean" then all, sep = sep, nil end
local lines = self:split(sep or "[\n\r]+")
if all then
return lines
else
return iter, { t = lines, i = 0 }
end
end
function string:ljust(int, padstr)
local len = #self
if int > len then
local num = padstr and math.floor((int - len) / #padstr) or int - len
self = self .. (padstr or " ") * num
len = #self
if len < int then self = self .. padstr:sub(1, int - len) end
end
return self
end
function string:lstrip()
return self:gsub("^%s+", "")
end
-- note: this doesn't behave like Ruby's String#next method
function string:next()
if #self == 1 then
return string.char(self:byte() + 1)
else
local bytes = self:bytes(true)
for i = 1, #bytes do bytes[i] = bytes[i] + 1 end
return string.char(unpack(bytes))
end
end
function string:rjust(int, padstr)
local len = #self
if int > len then
local num = padstr and math.floor((int - len) / #padstr) or int - len
self = ((padstr or " ") * num) .. self
len = #self
if len < int then self = padstr:sub(1, int - len) .. self end
end
return self
end
function string:rstrip()
return self:gsub("%s+$", "")
end
-- credits go to thelinx for most of this function
-- https://github.com/TheLinx/loveclass/blob/master/stringextensions.lua#L6
function string:split(pat, plain)
local t = {}
while true do
local pos1, pos2 = self:find(pat, 1, plain or false)
if not pos1 or pos1 > pos2 then
t[#t + 1] = self
return t
end
t[#t + 1] = self:sub(1, pos1 - 1)
self = self:sub(pos2 + 1)
end
end
function string:squeeze(other)
if other then
return self:gsub(other .. other .. "+", other)
else
local last, current
local buffer = {}
for i = 1, #self do
current = self[i]
if current ~= last then
table.insert(buffer, current)
last = current
end
end
return table.concat(buffer)
end
end
function string:startsWith(prefix)
return self:sub(1, #prefix) == prefix
end
function string:strip()
return self:lstrip():rstrip()
end
function string:swapcase()
return self:gsub("%a", swapChar)
end
function string:underscore()
return self:gsub("([A-Z]+)([A-Z][a-z])", "%1_%2"):
gsub("([a-z%d])([A-Z])", "%1_%2"):
gsub("[ \t]", "_"):
lower()
end