248 lines
7.3 KiB
Go
248 lines
7.3 KiB
Go
package lua
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
type LValueType int
|
|
|
|
const (
|
|
LTNil LValueType = iota
|
|
LTBool
|
|
LTNumber
|
|
LTString
|
|
LTFunction
|
|
LTUserData
|
|
LTThread
|
|
LTTable
|
|
LTChannel
|
|
)
|
|
|
|
var lValueNames = [9]string{"nil", "boolean", "number", "string", "function", "userdata", "thread", "table", "channel"}
|
|
|
|
func (vt LValueType) String() string {
|
|
return lValueNames[int(vt)]
|
|
}
|
|
|
|
type LValue interface {
|
|
String() string
|
|
Type() LValueType
|
|
// to reduce `runtime.assertI2T2` costs, this method should be used instead of the type assertion in heavy paths(typically inside the VM).
|
|
assertFloat64() (float64, bool)
|
|
// to reduce `runtime.assertI2T2` costs, this method should be used instead of the type assertion in heavy paths(typically inside the VM).
|
|
assertString() (string, bool)
|
|
// to reduce `runtime.assertI2T2` costs, this method should be used instead of the type assertion in heavy paths(typically inside the VM).
|
|
assertFunction() (*LFunction, bool)
|
|
}
|
|
|
|
// LVIsFalse returns true if a given LValue is a nil or false otherwise false.
|
|
func LVIsFalse(v LValue) bool { return v == LNil || v == LFalse }
|
|
|
|
// LVIsFalse returns false if a given LValue is a nil or false otherwise true.
|
|
func LVAsBool(v LValue) bool { return v != LNil && v != LFalse }
|
|
|
|
// LVAsString returns string representation of a given LValue
|
|
// if the LValue is a string or number, otherwise an empty string.
|
|
func LVAsString(v LValue) string {
|
|
switch sn := v.(type) {
|
|
case LString, LNumber:
|
|
return sn.String()
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
// LVCanConvToString returns true if a given LValue is a string or number
|
|
// otherwise false.
|
|
func LVCanConvToString(v LValue) bool {
|
|
switch v.(type) {
|
|
case LString, LNumber:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// LVAsNumber tries to convert a given LValue to a number.
|
|
func LVAsNumber(v LValue) LNumber {
|
|
switch lv := v.(type) {
|
|
case LNumber:
|
|
return lv
|
|
case LString:
|
|
if num, err := parseNumber(string(lv)); err == nil {
|
|
return num
|
|
}
|
|
}
|
|
return LNumber(0)
|
|
}
|
|
|
|
type LNilType struct{}
|
|
|
|
func (nl *LNilType) String() string { return "nil" }
|
|
func (nl *LNilType) Type() LValueType { return LTNil }
|
|
func (nl *LNilType) assertFloat64() (float64, bool) { return 0, false }
|
|
func (nl *LNilType) assertString() (string, bool) { return "", false }
|
|
func (nl *LNilType) assertFunction() (*LFunction, bool) { return nil, false }
|
|
|
|
var LNil = LValue(&LNilType{})
|
|
|
|
type LBool bool
|
|
|
|
func (bl LBool) String() string {
|
|
if bool(bl) {
|
|
return "true"
|
|
}
|
|
return "false"
|
|
}
|
|
func (bl LBool) Type() LValueType { return LTBool }
|
|
func (bl LBool) assertFloat64() (float64, bool) { return 0, false }
|
|
func (bl LBool) assertString() (string, bool) { return "", false }
|
|
func (bl LBool) assertFunction() (*LFunction, bool) { return nil, false }
|
|
|
|
var LTrue = LBool(true)
|
|
var LFalse = LBool(false)
|
|
|
|
type LString string
|
|
|
|
func (st LString) String() string { return string(st) }
|
|
func (st LString) Type() LValueType { return LTString }
|
|
func (st LString) assertFloat64() (float64, bool) { return 0, false }
|
|
func (st LString) assertString() (string, bool) { return string(st), true }
|
|
func (st LString) assertFunction() (*LFunction, bool) { return nil, false }
|
|
|
|
// fmt.Formatter interface
|
|
func (st LString) Format(f fmt.State, c rune) {
|
|
switch c {
|
|
case 'd', 'i':
|
|
if nm, err := parseNumber(string(st)); err != nil {
|
|
defaultFormat(nm, f, 'd')
|
|
} else {
|
|
defaultFormat(string(st), f, 's')
|
|
}
|
|
default:
|
|
defaultFormat(string(st), f, c)
|
|
}
|
|
}
|
|
|
|
func (nm LNumber) String() string {
|
|
if isInteger(nm) {
|
|
return fmt.Sprint(int64(nm))
|
|
}
|
|
return fmt.Sprint(float64(nm))
|
|
}
|
|
|
|
func (nm LNumber) Type() LValueType { return LTNumber }
|
|
func (nm LNumber) assertFloat64() (float64, bool) { return float64(nm), true }
|
|
func (nm LNumber) assertString() (string, bool) { return "", false }
|
|
func (nm LNumber) assertFunction() (*LFunction, bool) { return nil, false }
|
|
|
|
// fmt.Formatter interface
|
|
func (nm LNumber) Format(f fmt.State, c rune) {
|
|
switch c {
|
|
case 'q', 's':
|
|
defaultFormat(nm.String(), f, c)
|
|
case 'b', 'c', 'd', 'o', 'x', 'X', 'U':
|
|
defaultFormat(int64(nm), f, c)
|
|
case 'e', 'E', 'f', 'F', 'g', 'G':
|
|
defaultFormat(float64(nm), f, c)
|
|
case 'i':
|
|
defaultFormat(int64(nm), f, 'd')
|
|
default:
|
|
if isInteger(nm) {
|
|
defaultFormat(int64(nm), f, c)
|
|
} else {
|
|
defaultFormat(float64(nm), f, c)
|
|
}
|
|
}
|
|
}
|
|
|
|
type LTable struct {
|
|
Metatable LValue
|
|
|
|
array []LValue
|
|
dict map[LValue]LValue
|
|
strdict map[string]LValue
|
|
keys []LValue
|
|
k2i map[LValue]int
|
|
}
|
|
|
|
func (tb *LTable) String() string { return fmt.Sprintf("table: %p", tb) }
|
|
func (tb *LTable) Type() LValueType { return LTTable }
|
|
func (tb *LTable) assertFloat64() (float64, bool) { return 0, false }
|
|
func (tb *LTable) assertString() (string, bool) { return "", false }
|
|
func (tb *LTable) assertFunction() (*LFunction, bool) { return nil, false }
|
|
|
|
type LFunction struct {
|
|
IsG bool
|
|
Env *LTable
|
|
Proto *FunctionProto
|
|
GFunction LGFunction
|
|
Upvalues []*Upvalue
|
|
}
|
|
type LGFunction func(*LState) int
|
|
|
|
func (fn *LFunction) String() string { return fmt.Sprintf("function: %p", fn) }
|
|
func (fn *LFunction) Type() LValueType { return LTFunction }
|
|
func (fn *LFunction) assertFloat64() (float64, bool) { return 0, false }
|
|
func (fn *LFunction) assertString() (string, bool) { return "", false }
|
|
func (fn *LFunction) assertFunction() (*LFunction, bool) { return fn, true }
|
|
|
|
type Global struct {
|
|
MainThread *LState
|
|
CurrentThread *LState
|
|
Registry *LTable
|
|
Global *LTable
|
|
|
|
builtinMts map[int]LValue
|
|
tempFiles []*os.File
|
|
gccount int32
|
|
}
|
|
|
|
type LState struct {
|
|
G *Global
|
|
Parent *LState
|
|
Env *LTable
|
|
Panic func(*LState)
|
|
Dead bool
|
|
Options Options
|
|
|
|
stop int32
|
|
reg *registry
|
|
stack *callFrameStack
|
|
alloc *allocator
|
|
currentFrame *callFrame
|
|
wrapped bool
|
|
uvcache *Upvalue
|
|
hasErrorFunc bool
|
|
mainLoop func(*LState, *callFrame)
|
|
ctx context.Context
|
|
}
|
|
|
|
func (ls *LState) String() string { return fmt.Sprintf("thread: %p", ls) }
|
|
func (ls *LState) Type() LValueType { return LTThread }
|
|
func (ls *LState) assertFloat64() (float64, bool) { return 0, false }
|
|
func (ls *LState) assertString() (string, bool) { return "", false }
|
|
func (ls *LState) assertFunction() (*LFunction, bool) { return nil, false }
|
|
|
|
type LUserData struct {
|
|
Value interface{}
|
|
Env *LTable
|
|
Metatable LValue
|
|
}
|
|
|
|
func (ud *LUserData) String() string { return fmt.Sprintf("userdata: %p", ud) }
|
|
func (ud *LUserData) Type() LValueType { return LTUserData }
|
|
func (ud *LUserData) assertFloat64() (float64, bool) { return 0, false }
|
|
func (ud *LUserData) assertString() (string, bool) { return "", false }
|
|
func (ud *LUserData) assertFunction() (*LFunction, bool) { return nil, false }
|
|
|
|
type LChannel chan LValue
|
|
|
|
func (ch LChannel) String() string { return fmt.Sprintf("channel: %p", ch) }
|
|
func (ch LChannel) Type() LValueType { return LTChannel }
|
|
func (ch LChannel) assertFloat64() (float64, bool) { return 0, false }
|
|
func (ch LChannel) assertString() (string, bool) { return "", false }
|
|
func (ch LChannel) assertFunction() (*LFunction, bool) { return nil, false }
|