route/vendor/github.com/yuin/gluare/gluare.go

285 lines
6.0 KiB
Go

package gluare
import (
"github.com/yuin/gopher-lua"
"regexp"
"strings"
)
func Loader(L *lua.LState) int {
mod := L.SetFuncs(L.NewTable(), exports)
mod.RawSetString("gmatch", L.NewClosure(reGmatch, L.NewFunction(reGmatchIter)))
L.Push(mod)
return 1
}
var exports = map[string]lua.LGFunction{
"find": reFind,
"gsub": reGsub,
"match": reMatch,
"quote": reQuote,
}
func reQuote(L *lua.LState) int {
str := L.CheckString(1)
L.Push(lua.LString(regexp.QuoteMeta(str)))
return 1
}
func reFind(L *lua.LState) int {
str := L.CheckString(1)
pattern := L.CheckString(2)
if len(str) == 0 && len(pattern) == 0 {
L.Push(lua.LNumber(1))
L.Push(lua.LNumber(0))
return 2
}
init := luaIndex2StringIndex(str, L.OptInt(3, 1), true)
plain := false
if L.GetTop() == 4 {
plain = lua.LVAsBool(L.Get(4))
}
if plain {
pos := strings.Index(str[init:], pattern)
if pos < 0 {
L.Push(lua.LNil)
return 1
}
L.Push(lua.LNumber(init+pos) + 1)
L.Push(lua.LNumber(init + pos + len(pattern)))
return 2
}
re, err := regexp.Compile(pattern)
if err != nil {
L.RaiseError(err.Error())
}
stroffset := str[init:]
positions := re.FindStringSubmatchIndex(stroffset)
if positions == nil || (len(positions) > 1 && positions[2] < 0) {
L.Push(lua.LNil)
return 1
}
npos := len(positions)
L.Push(lua.LNumber(init+positions[0]) + 1)
L.Push(lua.LNumber(init + positions[npos-1]))
for i := 2; i < npos; i += 2 {
L.Push(lua.LString(stroffset[positions[i]:positions[i+1]]))
}
return npos/2 + 1
}
func reGsub(L *lua.LState) int {
str := L.CheckString(1)
pat := L.CheckString(2)
L.CheckTypes(3, lua.LTString, lua.LTTable, lua.LTFunction)
repl := L.CheckAny(3)
limit := L.OptInt(4, -1)
re, err := regexp.Compile(pat)
if err != nil {
L.RaiseError(err.Error())
}
matches := re.FindAllStringSubmatchIndex(str, limit)
if matches == nil || len(matches) == 0 {
L.SetTop(1)
L.Push(lua.LNumber(0))
return 2
}
switch lv := repl.(type) {
case lua.LString:
L.Push(lua.LString(reGsubStr(str, re, string(lv), matches)))
case *lua.LTable:
L.Push(lua.LString(reGsubTable(L, str, lv, matches)))
case *lua.LFunction:
L.Push(lua.LString(reGsubFunc(L, str, lv, matches)))
}
L.Push(lua.LNumber(len(matches)))
return 2
}
type replaceInfo struct {
Indicies []int
String string
}
func reGsubDoReplace(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 reGsubStr(str string, re *regexp.Regexp, repl string, matches [][]int) string {
infoList := make([]replaceInfo, 0, len(matches))
for _, match := range matches {
start, end := match[0], match[1]
if end < 0 {
continue
}
buf := make([]byte, 0, end-start)
buf = re.ExpandString(buf, repl, str, match)
infoList = append(infoList, replaceInfo{[]int{start, end}, string(buf)})
}
return reGsubDoReplace(str, infoList)
}
func reGsubTable(L *lua.LState, str string, repl *lua.LTable, matches [][]int) string {
infoList := make([]replaceInfo, 0, len(matches))
for _, match := range matches {
var key string
start, end := match[0], match[1]
if end < 0 {
continue
}
if len(match) > 2 { // has captures
key = str[match[2]:match[3]]
} else {
key = str[match[0]:match[1]]
}
value := L.GetField(repl, key)
if !lua.LVIsFalse(value) {
infoList = append(infoList, replaceInfo{[]int{start, end}, lua.LVAsString(value)})
}
}
return reGsubDoReplace(str, infoList)
}
func reGsubFunc(L *lua.LState, str string, repl *lua.LFunction, matches [][]int) string {
infoList := make([]replaceInfo, 0, len(matches))
for _, match := range matches {
start, end := match[0], match[1]
if end < 0 {
continue
}
L.Push(repl)
nargs := 0
if len(match) > 2 { // has captures
for i := 2; i < len(match); i += 2 {
L.Push(lua.LString(str[match[i]:match[i+1]]))
nargs++
}
} else {
L.Push(lua.LString(str[start:end]))
nargs++
}
L.Call(nargs, 1)
ret := L.Get(-1)
L.Pop(1)
if !lua.LVIsFalse(ret) {
infoList = append(infoList, replaceInfo{[]int{start, end}, lua.LVAsString(ret)})
}
}
return reGsubDoReplace(str, infoList)
}
type reMatchData struct {
str string
pos int
matches [][]int
}
func reGmatchIter(L *lua.LState) int {
md := L.CheckUserData(1).Value.(*reMatchData)
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 len(match) == 2 {
L.Push(lua.LString(str[match[0]:match[1]]))
return 1
}
for i := 2; i < len(match); i += 2 {
L.Push(lua.LString(str[match[i]:match[i+1]]))
}
return len(match)/2 - 1
}
func reGmatch(L *lua.LState) int {
str := L.CheckString(1)
pattern := L.CheckString(2)
re, err := regexp.Compile(pattern)
if err != nil {
L.RaiseError(err.Error())
}
L.Push(L.Get(lua.UpvalueIndex(1)))
ud := L.NewUserData()
ud.Value = &reMatchData{str, 0, re.FindAllStringSubmatchIndex(str, -1)}
L.Push(ud)
return 2
}
func reMatch(L *lua.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
}
re, err := regexp.Compile(pattern)
if err != nil {
L.RaiseError(err.Error())
}
str = str[offset:]
subs := re.FindStringSubmatchIndex(str)
nsubs := len(subs) / 2
switch nsubs {
case 0:
L.Push(lua.LNil)
return 1
case 1:
L.Push(lua.LString(str[subs[0]:subs[1]]))
return 1
default:
for i := 2; i < len(subs); i += 2 {
L.Push(lua.LString(str[subs[i]:subs[i+1]]))
}
return nsubs - 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
}
if 0 > i {
i = 0
}
if !start && i > l {
i = l
}
return i
}
//