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