123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 |
- package lua
- import (
- "fmt"
- "strings"
- "github.com/yuin/gopher-lua/pm"
- )
- const emptyLString LString = LString("")
- func OpenString(L *LState) int {
- var mod *LTable
- //_, ok := L.G.builtinMts[int(LTString)]
- //if !ok {
- mod = L.RegisterModule(StringLibName, strFuncs).(*LTable)
- gmatch := L.NewClosure(strGmatch, L.NewFunction(strGmatchIter))
- mod.RawSetString("gmatch", gmatch)
- mod.RawSetString("gfind", gmatch)
- mod.RawSetString("__index", mod)
- L.G.builtinMts[int(LTString)] = mod
- //}
- L.Push(mod)
- return 1
- }
- var strFuncs = map[string]LGFunction{
- "byte": strByte,
- "char": strChar,
- "dump": strDump,
- "find": strFind,
- "format": strFormat,
- "gsub": strGsub,
- "len": strLen,
- "lower": strLower,
- "match": strMatch,
- "rep": strRep,
- "reverse": strReverse,
- "sub": strSub,
- "upper": strUpper,
- }
- func strByte(L *LState) int {
- str := L.CheckString(1)
- start := L.OptInt(2, 1) - 1
- end := L.OptInt(3, -1)
- l := len(str)
- if start < 0 {
- start = l + start + 1
- }
- if end < 0 {
- end = l + end + 1
- }
- if L.GetTop() == 2 {
- if start < 0 || start >= l {
- return 0
- }
- L.Push(LNumber(str[start]))
- return 1
- }
- start = intMax(start, 0)
- end = intMin(end, l)
- if end < 0 || end <= start || start >= l {
- return 0
- }
- for i := start; i < end; i++ {
- L.Push(LNumber(str[i]))
- }
- return end - start
- }
- func strChar(L *LState) int {
- top := L.GetTop()
- bytes := make([]byte, L.GetTop())
- for i := 1; i <= top; i++ {
- bytes[i-1] = uint8(L.CheckInt(i))
- }
- L.Push(LString(string(bytes)))
- return 1
- }
- func strDump(L *LState) int {
- L.RaiseError("GopherLua does not support the string.dump")
- return 0
- }
- func strFind(L *LState) int {
- str := L.CheckString(1)
- pattern := L.CheckString(2)
- if len(pattern) == 0 {
- L.Push(LNumber(1))
- L.Push(LNumber(0))
- return 2
- }
- init := luaIndex2StringIndex(str, L.OptInt(3, 1), true)
- plain := false
- if L.GetTop() == 4 {
- plain = LVAsBool(L.Get(4))
- }
- if plain {
- pos := strings.Index(str[init:], pattern)
- if pos < 0 {
- L.Push(LNil)
- return 1
- }
- L.Push(LNumber(init+pos) + 1)
- L.Push(LNumber(init + pos + len(pattern)))
- return 2
- }
- mds, err := pm.Find(pattern, unsafeFastStringToReadOnlyBytes(str), init, 1)
- if err != nil {
- L.RaiseError(err.Error())
- }
- if len(mds) == 0 {
- L.Push(LNil)
- return 1
- }
- md := mds[0]
- L.Push(LNumber(md.Capture(0) + 1))
- L.Push(LNumber(md.Capture(1)))
- for i := 2; i < md.CaptureLength(); i += 2 {
- if md.IsPosCapture(i) {
- L.Push(LNumber(md.Capture(i)))
- } else {
- L.Push(LString(str[md.Capture(i):md.Capture(i+1)]))
- }
- }
- return md.CaptureLength()/2 + 1
- }
- func strFormat(L *LState) int {
- str := L.CheckString(1)
- args := make([]interface{}, L.GetTop()-1)
- top := L.GetTop()
- for i := 2; i <= top; i++ {
- args[i-2] = L.Get(i)
- }
- npat := strings.Count(str, "%") - strings.Count(str, "%%")
- L.Push(LString(fmt.Sprintf(str, args[:intMin(npat, len(args))]...)))
- return 1
- }
- func strGsub(L *LState) int {
- str := L.CheckString(1)
- pat := L.CheckString(2)
- L.CheckTypes(3, LTString, LTTable, LTFunction)
- repl := L.CheckAny(3)
- limit := L.OptInt(4, -1)
- mds, err := pm.Find(pat, unsafeFastStringToReadOnlyBytes(str), 0, limit)
- if err != nil {
- L.RaiseError(err.Error())
- }
- if len(mds) == 0 {
- L.SetTop(1)
- L.Push(LNumber(0))
- return 2
- }
- switch lv := repl.(type) {
- case LString:
- L.Push(LString(strGsubStr(L, str, string(lv), mds)))
- case *LTable:
- L.Push(LString(strGsubTable(L, str, lv, mds)))
- case *LFunction:
- L.Push(LString(strGsubFunc(L, str, lv, mds)))
- }
- L.Push(LNumber(len(mds)))
- return 2
- }
- type replaceInfo struct {
- Indicies []int
- String string
- }
- func checkCaptureIndex(L *LState, m *pm.MatchData, idx int) {
- if idx <= 2 {
- return
- }
- if idx >= m.CaptureLength() {
- L.RaiseError("invalid capture index")
- }
- }
- func capturedString(L *LState, m *pm.MatchData, str string, idx int) string {
- checkCaptureIndex(L, m, idx)
- if idx >= m.CaptureLength() && idx == 2 {
- idx = 0
- }
- if m.IsPosCapture(idx) {
- return fmt.Sprint(m.Capture(idx))
- } else {
- return str[m.Capture(idx):m.Capture(idx+1)]
- }
- }
- func strGsubDoReplace(str string, info []replaceInfo) string {
- offset := 0
- buf := []byte(str)
- for _, replace := range info {
- oldlen := len(buf)
- b1 := append([]byte(""), buf[0:offset+replace.Indicies[0]]...)
- b2 := []byte("")
- index2 := offset + replace.Indicies[1]
- if index2 <= len(buf) {
- b2 = append(b2, buf[index2:len(buf)]...)
- }
- buf = append(b1, replace.String...)
- buf = append(buf, b2...)
- offset += len(buf) - oldlen
- }
- return string(buf)
- }
- func strGsubStr(L *LState, str string, repl string, matches []*pm.MatchData) string {
- infoList := make([]replaceInfo, 0, len(matches))
- for _, match := range matches {
- start, end := match.Capture(0), match.Capture(1)
- sc := newFlagScanner('%', "", "", repl)
- for c, eos := sc.Next(); !eos; c, eos = sc.Next() {
- if !sc.ChangeFlag {
- if sc.HasFlag {
- if c >= '0' && c <= '9' {
- sc.AppendString(capturedString(L, match, str, 2*(int(c)-48)))
- } else {
- sc.AppendChar('%')
- sc.AppendChar(c)
- }
- sc.HasFlag = false
- } else {
- sc.AppendChar(c)
- }
- }
- }
- infoList = append(infoList, replaceInfo{[]int{start, end}, sc.String()})
- }
- return strGsubDoReplace(str, infoList)
- }
- func strGsubTable(L *LState, str string, repl *LTable, matches []*pm.MatchData) string {
- infoList := make([]replaceInfo, 0, len(matches))
- for _, match := range matches {
- idx := 0
- if match.CaptureLength() > 2 { // has captures
- idx = 2
- }
- var value LValue
- if match.IsPosCapture(idx) {
- value = L.GetTable(repl, LNumber(match.Capture(idx)))
- } else {
- value = L.GetField(repl, str[match.Capture(idx):match.Capture(idx+1)])
- }
- if !LVIsFalse(value) {
- infoList = append(infoList, replaceInfo{[]int{match.Capture(0), match.Capture(1)}, LVAsString(value)})
- }
- }
- return strGsubDoReplace(str, infoList)
- }
- func strGsubFunc(L *LState, str string, repl *LFunction, matches []*pm.MatchData) string {
- infoList := make([]replaceInfo, 0, len(matches))
- for _, match := range matches {
- start, end := match.Capture(0), match.Capture(1)
- L.Push(repl)
- nargs := 0
- if match.CaptureLength() > 2 { // has captures
- for i := 2; i < match.CaptureLength(); i += 2 {
- if match.IsPosCapture(i) {
- L.Push(LNumber(match.Capture(i)))
- } else {
- L.Push(LString(capturedString(L, match, str, i)))
- }
- nargs++
- }
- } else {
- L.Push(LString(capturedString(L, match, str, 0)))
- nargs++
- }
- L.Call(nargs, 1)
- ret := L.reg.Pop()
- if !LVIsFalse(ret) {
- infoList = append(infoList, replaceInfo{[]int{start, end}, LVAsString(ret)})
- }
- }
- return strGsubDoReplace(str, infoList)
- }
- type strMatchData struct {
- str string
- pos int
- matches []*pm.MatchData
- }
- func strGmatchIter(L *LState) int {
- md := L.CheckUserData(1).Value.(*strMatchData)
- str := md.str
- matches := md.matches
- idx := md.pos
- md.pos += 1
- if idx == len(matches) {
- return 0
- }
- L.Push(L.Get(1))
- match := matches[idx]
- if match.CaptureLength() == 2 {
- L.Push(LString(str[match.Capture(0):match.Capture(1)]))
- return 1
- }
- for i := 2; i < match.CaptureLength(); i += 2 {
- if match.IsPosCapture(i) {
- L.Push(LNumber(match.Capture(i)))
- } else {
- L.Push(LString(str[match.Capture(i):match.Capture(i+1)]))
- }
- }
- return match.CaptureLength()/2 - 1
- }
- func strGmatch(L *LState) int {
- str := L.CheckString(1)
- pattern := L.CheckString(2)
- mds, err := pm.Find(pattern, []byte(str), 0, -1)
- if err != nil {
- L.RaiseError(err.Error())
- }
- L.Push(L.Get(UpvalueIndex(1)))
- ud := L.NewUserData()
- ud.Value = &strMatchData{str, 0, mds}
- L.Push(ud)
- return 2
- }
- func strLen(L *LState) int {
- str := L.CheckString(1)
- L.Push(LNumber(len(str)))
- return 1
- }
- func strLower(L *LState) int {
- str := L.CheckString(1)
- L.Push(LString(strings.ToLower(str)))
- return 1
- }
- func strMatch(L *LState) int {
- str := L.CheckString(1)
- pattern := L.CheckString(2)
- offset := L.OptInt(3, 1)
- l := len(str)
- if offset < 0 {
- offset = l + offset + 1
- }
- offset--
- if offset < 0 {
- offset = 0
- }
- mds, err := pm.Find(pattern, unsafeFastStringToReadOnlyBytes(str), offset, 1)
- if err != nil {
- L.RaiseError(err.Error())
- }
- if len(mds) == 0 {
- L.Push(LNil)
- return 0
- }
- md := mds[0]
- nsubs := md.CaptureLength() / 2
- switch nsubs {
- case 1:
- L.Push(LString(str[md.Capture(0):md.Capture(1)]))
- return 1
- default:
- for i := 2; i < md.CaptureLength(); i += 2 {
- if md.IsPosCapture(i) {
- L.Push(LNumber(md.Capture(i)))
- } else {
- L.Push(LString(str[md.Capture(i):md.Capture(i+1)]))
- }
- }
- return nsubs - 1
- }
- }
- func strRep(L *LState) int {
- str := L.CheckString(1)
- n := L.CheckInt(2)
- if n < 0 {
- L.Push(emptyLString)
- } else {
- L.Push(LString(strings.Repeat(str, n)))
- }
- return 1
- }
- func strReverse(L *LState) int {
- str := L.CheckString(1)
- bts := []byte(str)
- out := make([]byte, len(bts))
- for i, j := 0, len(bts)-1; j >= 0; i, j = i+1, j-1 {
- out[i] = bts[j]
- }
- L.Push(LString(string(out)))
- return 1
- }
- func strSub(L *LState) int {
- str := L.CheckString(1)
- start := luaIndex2StringIndex(str, L.CheckInt(2), true)
- end := luaIndex2StringIndex(str, L.OptInt(3, -1), false)
- l := len(str)
- if start >= l || end < start {
- L.Push(emptyLString)
- } else {
- L.Push(LString(str[start:end]))
- }
- return 1
- }
- func strUpper(L *LState) int {
- str := L.CheckString(1)
- L.Push(LString(strings.ToUpper(str)))
- return 1
- }
- func luaIndex2StringIndex(str string, i int, start bool) int {
- if start && i != 0 {
- i -= 1
- }
- l := len(str)
- if i < 0 {
- i = l + i + 1
- }
- i = intMax(0, i)
- if !start && i > l {
- i = l
- }
- return i
- }
- //
|