mirror of https://github.com/tidwall/tile38.git
372 lines
7.8 KiB
Go
372 lines
7.8 KiB
Go
|
package lua
|
||
|
|
||
|
const defaultArrayCap = 32
|
||
|
const defaultHashCap = 32
|
||
|
|
||
|
type lValueArraySorter struct {
|
||
|
L *LState
|
||
|
Fn *LFunction
|
||
|
Values []LValue
|
||
|
}
|
||
|
|
||
|
func (lv lValueArraySorter) Len() int {
|
||
|
return len(lv.Values)
|
||
|
}
|
||
|
|
||
|
func (lv lValueArraySorter) Swap(i, j int) {
|
||
|
lv.Values[i], lv.Values[j] = lv.Values[j], lv.Values[i]
|
||
|
}
|
||
|
|
||
|
func (lv lValueArraySorter) Less(i, j int) bool {
|
||
|
if lv.Fn != nil {
|
||
|
lv.L.Push(lv.Fn)
|
||
|
lv.L.Push(lv.Values[i])
|
||
|
lv.L.Push(lv.Values[j])
|
||
|
lv.L.Call(2, 1)
|
||
|
return LVAsBool(lv.L.reg.Pop())
|
||
|
}
|
||
|
return lessThan(lv.L, lv.Values[i], lv.Values[j])
|
||
|
}
|
||
|
|
||
|
func newLTable(acap int, hcap int) *LTable {
|
||
|
if acap < 0 {
|
||
|
acap = 0
|
||
|
}
|
||
|
if hcap < 0 {
|
||
|
hcap = 0
|
||
|
}
|
||
|
tb := <able{}
|
||
|
tb.Metatable = LNil
|
||
|
if acap != 0 {
|
||
|
tb.array = make([]LValue, 0, acap)
|
||
|
}
|
||
|
if hcap != 0 {
|
||
|
tb.strdict = make(map[string]LValue, hcap)
|
||
|
}
|
||
|
return tb
|
||
|
}
|
||
|
|
||
|
// Len returns length of this LTable.
|
||
|
func (tb *LTable) Len() int {
|
||
|
if tb.array == nil {
|
||
|
return 0
|
||
|
}
|
||
|
var prev LValue = LNil
|
||
|
for i := len(tb.array) - 1; i >= 0; i-- {
|
||
|
v := tb.array[i]
|
||
|
if prev == LNil && v != LNil {
|
||
|
return i + 1
|
||
|
}
|
||
|
prev = v
|
||
|
}
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
// Append appends a given LValue to this LTable.
|
||
|
func (tb *LTable) Append(value LValue) {
|
||
|
if tb.array == nil {
|
||
|
tb.array = make([]LValue, 0, defaultArrayCap)
|
||
|
}
|
||
|
tb.array = append(tb.array, value)
|
||
|
}
|
||
|
|
||
|
// Insert inserts a given LValue at position `i` in this table.
|
||
|
func (tb *LTable) Insert(i int, value LValue) {
|
||
|
if tb.array == nil {
|
||
|
tb.array = make([]LValue, 0, defaultArrayCap)
|
||
|
}
|
||
|
if i > len(tb.array) {
|
||
|
tb.RawSetInt(i, value)
|
||
|
return
|
||
|
}
|
||
|
if i <= 0 {
|
||
|
tb.RawSet(LNumber(i), value)
|
||
|
return
|
||
|
}
|
||
|
i -= 1
|
||
|
tb.array = append(tb.array, LNil)
|
||
|
copy(tb.array[i+1:], tb.array[i:])
|
||
|
tb.array[i] = value
|
||
|
}
|
||
|
|
||
|
// MaxN returns a maximum number key that nil value does not exist before it.
|
||
|
func (tb *LTable) MaxN() int {
|
||
|
if tb.array == nil {
|
||
|
return 0
|
||
|
}
|
||
|
for i := len(tb.array) - 1; i >= 0; i-- {
|
||
|
if tb.array[i] != LNil {
|
||
|
return i + 1
|
||
|
}
|
||
|
}
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
// Remove removes from this table the element at a given position.
|
||
|
func (tb *LTable) Remove(pos int) LValue {
|
||
|
if tb.array == nil {
|
||
|
return LNil
|
||
|
}
|
||
|
i := pos - 1
|
||
|
larray := len(tb.array)
|
||
|
oldval := LNil
|
||
|
switch {
|
||
|
case i >= larray:
|
||
|
// nothing to do
|
||
|
case i == larray-1 || i < 0:
|
||
|
oldval = tb.array[larray-1]
|
||
|
tb.array = tb.array[:larray-1]
|
||
|
default:
|
||
|
oldval = tb.array[i]
|
||
|
copy(tb.array[i:], tb.array[i+1:])
|
||
|
tb.array[larray-1] = nil
|
||
|
tb.array = tb.array[:larray-1]
|
||
|
}
|
||
|
return oldval
|
||
|
}
|
||
|
|
||
|
// RawSet sets a given LValue to a given index without the __newindex metamethod.
|
||
|
// It is recommended to use `RawSetString` or `RawSetInt` for performance
|
||
|
// if you already know the given LValue is a string or number.
|
||
|
func (tb *LTable) RawSet(key LValue, value LValue) {
|
||
|
switch v := key.(type) {
|
||
|
case LNumber:
|
||
|
if isArrayKey(v) {
|
||
|
if tb.array == nil {
|
||
|
tb.array = make([]LValue, 0, defaultArrayCap)
|
||
|
}
|
||
|
index := int(v) - 1
|
||
|
alen := len(tb.array)
|
||
|
switch {
|
||
|
case index == alen:
|
||
|
tb.array = append(tb.array, value)
|
||
|
case index > alen:
|
||
|
for i := 0; i < (index - alen); i++ {
|
||
|
tb.array = append(tb.array, LNil)
|
||
|
}
|
||
|
tb.array = append(tb.array, value)
|
||
|
case index < alen:
|
||
|
tb.array[index] = value
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
case LString:
|
||
|
tb.RawSetString(string(v), value)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
tb.RawSetH(key, value)
|
||
|
}
|
||
|
|
||
|
// RawSetInt sets a given LValue at a position `key` without the __newindex metamethod.
|
||
|
func (tb *LTable) RawSetInt(key int, value LValue) {
|
||
|
if key < 1 || key >= MaxArrayIndex {
|
||
|
tb.RawSetH(LNumber(key), value)
|
||
|
return
|
||
|
}
|
||
|
if tb.array == nil {
|
||
|
tb.array = make([]LValue, 0, 32)
|
||
|
}
|
||
|
index := key - 1
|
||
|
alen := len(tb.array)
|
||
|
switch {
|
||
|
case index == alen:
|
||
|
tb.array = append(tb.array, value)
|
||
|
case index > alen:
|
||
|
for i := 0; i < (index - alen); i++ {
|
||
|
tb.array = append(tb.array, LNil)
|
||
|
}
|
||
|
tb.array = append(tb.array, value)
|
||
|
case index < alen:
|
||
|
tb.array[index] = value
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RawSetString sets a given LValue to a given string index without the __newindex metamethod.
|
||
|
func (tb *LTable) RawSetString(key string, value LValue) {
|
||
|
if tb.strdict == nil {
|
||
|
tb.strdict = make(map[string]LValue, defaultHashCap)
|
||
|
}
|
||
|
if tb.keys == nil {
|
||
|
tb.keys = []LValue{}
|
||
|
tb.k2i = map[LValue]int{}
|
||
|
}
|
||
|
|
||
|
if value == LNil {
|
||
|
// TODO tb.keys and tb.k2i should also be removed
|
||
|
delete(tb.strdict, key)
|
||
|
} else {
|
||
|
tb.strdict[key] = value
|
||
|
lkey := LString(key)
|
||
|
if _, ok := tb.k2i[lkey]; !ok {
|
||
|
tb.k2i[lkey] = len(tb.keys)
|
||
|
tb.keys = append(tb.keys, lkey)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RawSetH sets a given LValue to a given index without the __newindex metamethod.
|
||
|
func (tb *LTable) RawSetH(key LValue, value LValue) {
|
||
|
if s, ok := key.(LString); ok {
|
||
|
tb.RawSetString(string(s), value)
|
||
|
return
|
||
|
}
|
||
|
if tb.dict == nil {
|
||
|
tb.dict = make(map[LValue]LValue, len(tb.strdict))
|
||
|
}
|
||
|
if tb.keys == nil {
|
||
|
tb.keys = []LValue{}
|
||
|
tb.k2i = map[LValue]int{}
|
||
|
}
|
||
|
|
||
|
if value == LNil {
|
||
|
// TODO tb.keys and tb.k2i should also be removed
|
||
|
delete(tb.dict, key)
|
||
|
} else {
|
||
|
tb.dict[key] = value
|
||
|
if _, ok := tb.k2i[key]; !ok {
|
||
|
tb.k2i[key] = len(tb.keys)
|
||
|
tb.keys = append(tb.keys, key)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RawGet returns an LValue associated with a given key without __index metamethod.
|
||
|
func (tb *LTable) RawGet(key LValue) LValue {
|
||
|
switch v := key.(type) {
|
||
|
case LNumber:
|
||
|
if isArrayKey(v) {
|
||
|
if tb.array == nil {
|
||
|
return LNil
|
||
|
}
|
||
|
index := int(v) - 1
|
||
|
if index >= len(tb.array) {
|
||
|
return LNil
|
||
|
}
|
||
|
return tb.array[index]
|
||
|
}
|
||
|
case LString:
|
||
|
if tb.strdict == nil {
|
||
|
return LNil
|
||
|
}
|
||
|
if ret, ok := tb.strdict[string(v)]; ok {
|
||
|
return ret
|
||
|
}
|
||
|
return LNil
|
||
|
}
|
||
|
if tb.dict == nil {
|
||
|
return LNil
|
||
|
}
|
||
|
if v, ok := tb.dict[key]; ok {
|
||
|
return v
|
||
|
}
|
||
|
return LNil
|
||
|
}
|
||
|
|
||
|
// RawGetInt returns an LValue at position `key` without __index metamethod.
|
||
|
func (tb *LTable) RawGetInt(key int) LValue {
|
||
|
if tb.array == nil {
|
||
|
return LNil
|
||
|
}
|
||
|
index := int(key) - 1
|
||
|
if index >= len(tb.array) || index < 0 {
|
||
|
return LNil
|
||
|
}
|
||
|
return tb.array[index]
|
||
|
}
|
||
|
|
||
|
// RawGet returns an LValue associated with a given key without __index metamethod.
|
||
|
func (tb *LTable) RawGetH(key LValue) LValue {
|
||
|
if s, sok := key.(LString); sok {
|
||
|
if tb.strdict == nil {
|
||
|
return LNil
|
||
|
}
|
||
|
if v, vok := tb.strdict[string(s)]; vok {
|
||
|
return v
|
||
|
}
|
||
|
return LNil
|
||
|
}
|
||
|
if tb.dict == nil {
|
||
|
return LNil
|
||
|
}
|
||
|
if v, ok := tb.dict[key]; ok {
|
||
|
return v
|
||
|
}
|
||
|
return LNil
|
||
|
}
|
||
|
|
||
|
// RawGetString returns an LValue associated with a given key without __index metamethod.
|
||
|
func (tb *LTable) RawGetString(key string) LValue {
|
||
|
if tb.strdict == nil {
|
||
|
return LNil
|
||
|
}
|
||
|
if v, vok := tb.strdict[string(key)]; vok {
|
||
|
return v
|
||
|
}
|
||
|
return LNil
|
||
|
}
|
||
|
|
||
|
// ForEach iterates over this table of elements, yielding each in turn to a given function.
|
||
|
func (tb *LTable) ForEach(cb func(LValue, LValue)) {
|
||
|
if tb.array != nil {
|
||
|
for i, v := range tb.array {
|
||
|
if v != LNil {
|
||
|
cb(LNumber(i+1), v)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if tb.strdict != nil {
|
||
|
for k, v := range tb.strdict {
|
||
|
if v != LNil {
|
||
|
cb(LString(k), v)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if tb.dict != nil {
|
||
|
for k, v := range tb.dict {
|
||
|
if v != LNil {
|
||
|
cb(k, v)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This function is equivalent to lua_next ( http://www.lua.org/manual/5.1/manual.html#lua_next ).
|
||
|
func (tb *LTable) Next(key LValue) (LValue, LValue) {
|
||
|
init := false
|
||
|
if key == LNil {
|
||
|
key = LNumber(0)
|
||
|
init = true
|
||
|
}
|
||
|
|
||
|
if init || key != LNumber(0) {
|
||
|
if kv, ok := key.(LNumber); ok && isInteger(kv) && int(kv) >= 0 && kv < LNumber(MaxArrayIndex) {
|
||
|
index := int(kv)
|
||
|
if tb.array != nil {
|
||
|
for ; index < len(tb.array); index++ {
|
||
|
if v := tb.array[index]; v != LNil {
|
||
|
return LNumber(index + 1), v
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if tb.array == nil || index == len(tb.array) {
|
||
|
if (tb.dict == nil || len(tb.dict) == 0) && (tb.strdict == nil || len(tb.strdict) == 0) {
|
||
|
return LNil, LNil
|
||
|
}
|
||
|
key = tb.keys[0]
|
||
|
if v := tb.RawGetH(key); v != LNil {
|
||
|
return key, v
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for i := tb.k2i[key] + 1; i < len(tb.keys); i++ {
|
||
|
key := tb.keys[i]
|
||
|
if v := tb.RawGetH(key); v != LNil {
|
||
|
return key, v
|
||
|
}
|
||
|
}
|
||
|
return LNil, LNil
|
||
|
}
|