tile38/vendor/github.com/yuin/gopher-lua/stringlib.go

449 lines
9.4 KiB
Go

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