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  //