github.com/qiniu/gopher-lua@v0.2017.11/stringlib.go (about) 1 package lua 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/yuin/gopher-lua/pm" 8 ) 9 10 func OpenString(L *LState) int { 11 var mod *LTable 12 //_, ok := L.G.builtinMts[int(LTString)] 13 //if !ok { 14 mod = L.RegisterModule(StringLibName, strFuncs).(*LTable) 15 gmatch := L.NewClosure(strGmatch, L.NewFunction(strGmatchIter)) 16 mod.RawSetString("gmatch", gmatch) 17 mod.RawSetString("gfind", gmatch) 18 mod.RawSetString("__index", mod) 19 L.G.builtinMts[int(LTString)] = mod 20 //} 21 L.Push(mod) 22 return 1 23 } 24 25 var strFuncs = map[string]LGFunction{ 26 "byte": strByte, 27 "char": strChar, 28 "dump": strDump, 29 "find": strFind, 30 "format": strFormat, 31 "gsub": strGsub, 32 "len": strLen, 33 "lower": strLower, 34 "match": strMatch, 35 "rep": strRep, 36 "reverse": strReverse, 37 "sub": strSub, 38 "upper": strUpper, 39 } 40 41 func strByte(L *LState) int { 42 str := L.CheckString(1) 43 start := L.OptInt(2, 1) - 1 44 end := L.OptInt(3, -1) 45 l := len(str) 46 if start < 0 { 47 start = l + start + 1 48 } 49 if end < 0 { 50 end = l + end + 1 51 } 52 53 if L.GetTop() == 2 { 54 if start < 0 || start >= l { 55 return 0 56 } 57 L.Push(LNumber(str[start])) 58 return 1 59 } 60 61 start = intMax(start, 0) 62 end = intMin(end, l) 63 if end < 0 || end <= start || start >= l { 64 return 0 65 } 66 67 for i := start; i < end; i++ { 68 L.Push(LNumber(str[i])) 69 } 70 return end - start 71 } 72 73 func strChar(L *LState) int { 74 top := L.GetTop() 75 bytes := make([]byte, L.GetTop()) 76 for i := 1; i <= top; i++ { 77 bytes[i-1] = uint8(L.CheckInt(i)) 78 } 79 L.Push(LString(string(bytes))) 80 return 1 81 } 82 83 func strDump(L *LState) int { 84 L.RaiseError("GopherLua does not support the string.dump") 85 return 0 86 } 87 88 func strFind(L *LState) int { 89 str := L.CheckString(1) 90 pattern := L.CheckString(2) 91 if len(pattern) == 0 { 92 L.Push(LNumber(1)) 93 L.Push(LNumber(0)) 94 return 2 95 } 96 init := luaIndex2StringIndex(str, L.OptInt(3, 1), true) 97 plain := false 98 if L.GetTop() == 4 { 99 plain = LVAsBool(L.Get(4)) 100 } 101 102 if plain { 103 pos := strings.Index(str[init:], pattern) 104 if pos < 0 { 105 L.Push(LNil) 106 return 1 107 } 108 L.Push(LNumber(init+pos) + 1) 109 L.Push(LNumber(init + pos + len(pattern))) 110 return 2 111 } 112 113 mds, err := pm.Find(pattern, unsafeFastStringToReadOnlyBytes(str), init, 1) 114 if err != nil { 115 L.RaiseError(err.Error()) 116 } 117 if len(mds) == 0 { 118 L.Push(LNil) 119 return 1 120 } 121 md := mds[0] 122 L.Push(LNumber(md.Capture(0) + 1)) 123 L.Push(LNumber(md.Capture(1))) 124 for i := 2; i < md.CaptureLength(); i += 2 { 125 if md.IsPosCapture(i) { 126 L.Push(LNumber(md.Capture(i))) 127 } else { 128 L.Push(LString(str[md.Capture(i):md.Capture(i+1)])) 129 } 130 } 131 return md.CaptureLength()/2 + 1 132 } 133 134 func strFormat(L *LState) int { 135 str := L.CheckString(1) 136 args := make([]interface{}, L.GetTop()-1) 137 top := L.GetTop() 138 for i := 2; i <= top; i++ { 139 args[i-2] = L.Get(i) 140 } 141 npat := strings.Count(str, "%") - strings.Count(str, "%%") 142 L.Push(LString(fmt.Sprintf(str, args[:intMin(npat, len(args))]...))) 143 return 1 144 } 145 146 func strGsub(L *LState) int { 147 str := L.CheckString(1) 148 pat := L.CheckString(2) 149 L.CheckTypes(3, LTString, LTTable, LTFunction) 150 repl := L.CheckAny(3) 151 limit := L.OptInt(4, -1) 152 153 mds, err := pm.Find(pat, unsafeFastStringToReadOnlyBytes(str), 0, limit) 154 if err != nil { 155 L.RaiseError(err.Error()) 156 } 157 if len(mds) == 0 { 158 L.SetTop(1) 159 L.Push(LNumber(0)) 160 return 2 161 } 162 switch lv := repl.(type) { 163 case LString: 164 L.Push(LString(strGsubStr(L, str, string(lv), mds))) 165 case *LTable: 166 L.Push(LString(strGsubTable(L, str, lv, mds))) 167 case *LFunction: 168 L.Push(LString(strGsubFunc(L, str, lv, mds))) 169 } 170 L.Push(LNumber(len(mds))) 171 return 2 172 } 173 174 type replaceInfo struct { 175 Indicies []int 176 String string 177 } 178 179 func checkCaptureIndex(L *LState, m *pm.MatchData, idx int) { 180 if idx <= 2 { 181 return 182 } 183 if idx >= m.CaptureLength() { 184 L.RaiseError("invalid capture index") 185 } 186 } 187 188 func capturedString(L *LState, m *pm.MatchData, str string, idx int) string { 189 checkCaptureIndex(L, m, idx) 190 if idx >= m.CaptureLength() && idx == 2 { 191 idx = 0 192 } 193 if m.IsPosCapture(idx) { 194 return fmt.Sprint(m.Capture(idx)) 195 } else { 196 return str[m.Capture(idx):m.Capture(idx+1)] 197 } 198 199 } 200 201 func strGsubDoReplace(str string, info []replaceInfo) string { 202 offset := 0 203 buf := []byte(str) 204 for _, replace := range info { 205 oldlen := len(buf) 206 b1 := append([]byte(""), buf[0:offset+replace.Indicies[0]]...) 207 b2 := []byte("") 208 index2 := offset + replace.Indicies[1] 209 if index2 <= len(buf) { 210 b2 = append(b2, buf[index2:len(buf)]...) 211 } 212 buf = append(b1, replace.String...) 213 buf = append(buf, b2...) 214 offset += len(buf) - oldlen 215 } 216 return string(buf) 217 } 218 219 func strGsubStr(L *LState, str string, repl string, matches []*pm.MatchData) string { 220 infoList := make([]replaceInfo, 0, len(matches)) 221 for _, match := range matches { 222 start, end := match.Capture(0), match.Capture(1) 223 sc := newFlagScanner('%', "", "", repl) 224 for c, eos := sc.Next(); !eos; c, eos = sc.Next() { 225 if !sc.ChangeFlag { 226 if sc.HasFlag { 227 if c >= '0' && c <= '9' { 228 sc.AppendString(capturedString(L, match, str, 2*(int(c)-48))) 229 } else { 230 sc.AppendChar('%') 231 sc.AppendChar(c) 232 } 233 sc.HasFlag = false 234 } else { 235 sc.AppendChar(c) 236 } 237 } 238 } 239 infoList = append(infoList, replaceInfo{[]int{start, end}, sc.String()}) 240 } 241 242 return strGsubDoReplace(str, infoList) 243 } 244 245 func strGsubTable(L *LState, str string, repl *LTable, matches []*pm.MatchData) string { 246 infoList := make([]replaceInfo, 0, len(matches)) 247 for _, match := range matches { 248 idx := 0 249 if match.CaptureLength() > 2 { // has captures 250 idx = 2 251 } 252 var value LValue 253 if match.IsPosCapture(idx) { 254 value = L.GetTable(repl, LNumber(match.Capture(idx))) 255 } else { 256 value = L.GetField(repl, str[match.Capture(idx):match.Capture(idx+1)]) 257 } 258 if !LVIsFalse(value) { 259 infoList = append(infoList, replaceInfo{[]int{match.Capture(0), match.Capture(1)}, LVAsString(value)}) 260 } 261 } 262 return strGsubDoReplace(str, infoList) 263 } 264 265 func strGsubFunc(L *LState, str string, repl *LFunction, matches []*pm.MatchData) string { 266 infoList := make([]replaceInfo, 0, len(matches)) 267 for _, match := range matches { 268 start, end := match.Capture(0), match.Capture(1) 269 L.Push(repl) 270 nargs := 0 271 if match.CaptureLength() > 2 { // has captures 272 for i := 2; i < match.CaptureLength(); i += 2 { 273 if match.IsPosCapture(i) { 274 L.Push(LNumber(match.Capture(i))) 275 } else { 276 L.Push(LString(capturedString(L, match, str, i))) 277 } 278 nargs++ 279 } 280 } else { 281 L.Push(LString(capturedString(L, match, str, 0))) 282 nargs++ 283 } 284 L.Call(nargs, 1) 285 ret := L.reg.Pop() 286 if !LVIsFalse(ret) { 287 infoList = append(infoList, replaceInfo{[]int{start, end}, LVAsString(ret)}) 288 } 289 } 290 return strGsubDoReplace(str, infoList) 291 } 292 293 type strMatchData struct { 294 str string 295 pos int 296 matches []*pm.MatchData 297 } 298 299 func strGmatchIter(L *LState) int { 300 md := L.CheckUserData(1).Value.(*strMatchData) 301 str := md.str 302 matches := md.matches 303 idx := md.pos 304 md.pos += 1 305 if idx == len(matches) { 306 return 0 307 } 308 L.Push(L.Get(1)) 309 match := matches[idx] 310 if match.CaptureLength() == 2 { 311 L.Push(LString(str[match.Capture(0):match.Capture(1)])) 312 return 1 313 } 314 315 for i := 2; i < match.CaptureLength(); i += 2 { 316 if match.IsPosCapture(i) { 317 L.Push(LNumber(match.Capture(i))) 318 } else { 319 L.Push(LString(str[match.Capture(i):match.Capture(i+1)])) 320 } 321 } 322 return match.CaptureLength()/2 - 1 323 } 324 325 func strGmatch(L *LState) int { 326 str := L.CheckString(1) 327 pattern := L.CheckString(2) 328 mds, err := pm.Find(pattern, []byte(str), 0, -1) 329 if err != nil { 330 L.RaiseError(err.Error()) 331 } 332 L.Push(L.Get(UpvalueIndex(1))) 333 ud := L.NewUserData() 334 ud.Value = &strMatchData{str, 0, mds} 335 L.Push(ud) 336 return 2 337 } 338 339 func strLen(L *LState) int { 340 str := L.CheckString(1) 341 L.Push(LNumber(len(str))) 342 return 1 343 } 344 345 func strLower(L *LState) int { 346 str := L.CheckString(1) 347 L.Push(LString(strings.ToLower(str))) 348 return 1 349 } 350 351 func strMatch(L *LState) int { 352 str := L.CheckString(1) 353 pattern := L.CheckString(2) 354 offset := L.OptInt(3, 1) 355 l := len(str) 356 if offset < 0 { 357 offset = l + offset + 1 358 } 359 offset-- 360 if offset < 0 { 361 offset = 0 362 } 363 364 mds, err := pm.Find(pattern, unsafeFastStringToReadOnlyBytes(str), offset, 1) 365 if err != nil { 366 L.RaiseError(err.Error()) 367 } 368 if len(mds) == 0 { 369 L.Push(LNil) 370 return 0 371 } 372 md := mds[0] 373 nsubs := md.CaptureLength() / 2 374 switch nsubs { 375 case 1: 376 L.Push(LString(str[md.Capture(0):md.Capture(1)])) 377 return 1 378 default: 379 for i := 2; i < md.CaptureLength(); i += 2 { 380 if md.IsPosCapture(i) { 381 L.Push(LNumber(md.Capture(i))) 382 } else { 383 L.Push(LString(str[md.Capture(i):md.Capture(i+1)])) 384 } 385 } 386 return nsubs - 1 387 } 388 } 389 390 func strRep(L *LState) int { 391 str := L.CheckString(1) 392 n := L.CheckInt(2) 393 L.Push(LString(strings.Repeat(str, n))) 394 return 1 395 } 396 397 func strReverse(L *LState) int { 398 str := L.CheckString(1) 399 bts := []byte(str) 400 out := make([]byte, len(bts)) 401 for i, j := 0, len(bts)-1; j >= 0; i, j = i+1, j-1 { 402 out[i] = bts[j] 403 } 404 L.Push(LString(string(out))) 405 return 1 406 } 407 408 func strSub(L *LState) int { 409 str := L.CheckString(1) 410 start := luaIndex2StringIndex(str, L.CheckInt(2), true) 411 end := luaIndex2StringIndex(str, L.OptInt(3, -1), false) 412 l := len(str) 413 if start >= l || end < start { 414 L.Push(LString("")) 415 } else { 416 L.Push(LString(str[start:end])) 417 } 418 return 1 419 } 420 421 func strUpper(L *LState) int { 422 str := L.CheckString(1) 423 L.Push(LString(strings.ToUpper(str))) 424 return 1 425 } 426 427 func luaIndex2StringIndex(str string, i int, start bool) int { 428 if start && i != 0 { 429 i -= 1 430 } 431 l := len(str) 432 if i < 0 { 433 i = l + i + 1 434 } 435 i = intMax(0, i) 436 if !start && i > l { 437 i = l 438 } 439 return i 440 } 441 442 //