github.com/yuin/gopher-lua@v1.1.2-0.20231212122839-2348fd042596/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  //