Merge pull request #2731 from jameszhangyukun/lua-layer

Resource Interpreter framework introduce Lua runtime layer
This commit is contained in:
karmada-bot 2022-11-08 11:21:25 +08:00 committed by GitHub
commit 31f97acae3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 19551 additions and 0 deletions

2
go.mod
View File

@ -19,6 +19,7 @@ require (
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0
github.com/vektra/mockery/v2 v2.10.0
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64
go.uber.org/atomic v1.7.0
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8
@ -40,6 +41,7 @@ require (
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42
k8s.io/kubectl v0.24.2
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf
sigs.k8s.io/cluster-api v1.0.1
sigs.k8s.io/controller-runtime v0.12.2
sigs.k8s.io/kind v0.15.0

4
go.sum
View File

@ -847,6 +847,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 h1:5mLPGnFdSsevFRFc9q3yYbBkB6tsm4aCwwQV/j1JQAQ=
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
@ -1582,6 +1584,8 @@ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc=
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf h1:rRz0YsF7VXj9fXRF6yQgFI7DzST+hsI3TeFSGupntu0=
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf/go.mod h1:ivKkcY8Zxw5ba0jldhZCYYQfGdb2K6u9tbYK1AwMIBc=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@ -0,0 +1,443 @@
package luavm
import (
"context"
"encoding/json"
"fmt"
"time"
lua "github.com/yuin/gopher-lua"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/klog/v2"
luajson "layeh.com/gopher-json"
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
"github.com/karmada-io/karmada/pkg/util/helper"
"github.com/karmada-io/karmada/pkg/util/lifted"
)
// VM Defines a struct that implements the luaVM.
type VM struct {
// UseOpenLibs flag to enable open libraries. Libraries are disabled by default while running, but enabled during testing to allow the use of print statements.
UseOpenLibs bool
}
// GetReplicas returns the desired replicas of the object as well as the requirements of each replica by lua script.
func (vm VM) GetReplicas(obj *unstructured.Unstructured, script string) (replica int32, requires *workv1alpha2.ReplicaRequirements, err error) {
l := lua.NewState(lua.Options{
SkipOpenLibs: !vm.UseOpenLibs,
})
defer l.Close()
// Opens table library to allow access to functions to manipulate tables
err = vm.setLib(l)
if err != nil {
return 0, nil, err
}
// preload our 'safe' version of the OS library. Allows the 'local os = require("os")' to work
l.PreloadModule(lua.OsLibName, lifted.SafeOsLoader)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
l.SetContext(ctx)
err = l.DoString(script)
f := l.GetGlobal("GetReplicas")
if f.Type() == lua.LTNil {
return 0, nil, fmt.Errorf("can't get function ReviseReplica pleace check the function name")
}
args := make([]lua.LValue, 1)
args[0] = decodeValue(l, obj.Object)
err = l.CallByParam(lua.P{Fn: f, NRet: 2, Protect: true}, args...)
if err != nil {
return 0, nil, err
}
replicaRequirementResult := l.Get(l.GetTop())
l.Pop(1)
requires = &workv1alpha2.ReplicaRequirements{}
if replicaRequirementResult.Type() == lua.LTTable {
err = ConvertLuaResultInto(replicaRequirementResult, requires)
if err != nil {
klog.Errorf("ConvertLuaResultToReplicaRequirements err %v", err.Error())
return 0, nil, err
}
} else if replicaRequirementResult.Type() == lua.LTNil {
requires = nil
} else {
return 0, nil, fmt.Errorf("expect the returned requires type is table but got %s", replicaRequirementResult.Type())
}
luaReplica := l.Get(l.GetTop())
replica, err = ConvertLuaResultToInt(luaReplica)
if err != nil {
return 0, nil, err
}
return
}
// ReviseReplica revises the replica of the given object by lua.
func (vm VM) ReviseReplica(object *unstructured.Unstructured, replica int64, script string) (*unstructured.Unstructured, error) {
l := lua.NewState(lua.Options{
SkipOpenLibs: !vm.UseOpenLibs,
})
defer l.Close()
// Opens table library to allow access to functions to manipulate tables
err := vm.setLib(l)
if err != nil {
return nil, err
}
// preload our 'safe' version of the OS library. Allows the 'local os = require("os")' to work
l.PreloadModule(lua.OsLibName, lifted.SafeOsLoader)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
l.SetContext(ctx)
err = l.DoString(script)
if err != nil {
return nil, err
}
reviseReplicaLuaFunc := l.GetGlobal("ReviseReplica")
if reviseReplicaLuaFunc.Type() == lua.LTNil {
return nil, fmt.Errorf("can't get function ReviseReplica pleace check the function name")
}
args := make([]lua.LValue, 2)
args[0] = decodeValue(l, object.Object)
args[1] = decodeValue(l, replica)
err = l.CallByParam(lua.P{Fn: reviseReplicaLuaFunc, NRet: 1, Protect: true}, args...)
if err != nil {
return nil, err
}
luaResult := l.Get(l.GetTop())
reviseReplicaResult := &unstructured.Unstructured{}
if luaResult.Type() == lua.LTTable {
err := ConvertLuaResultInto(luaResult, reviseReplicaResult)
if err != nil {
return nil, err
}
return reviseReplicaResult, nil
}
return nil, fmt.Errorf("expect the returned requires type is table but got %s", luaResult.Type())
}
func (vm VM) setLib(l *lua.LState) error {
for _, pair := range []struct {
n string
f lua.LGFunction
}{
{lua.LoadLibName, lua.OpenPackage},
{lua.BaseLibName, lua.OpenBase},
{lua.TabLibName, lua.OpenTable},
// load our 'safe' version of the OS library
{lua.OsLibName, lifted.OpenSafeOs},
} {
if err := l.CallByParam(lua.P{
Fn: l.NewFunction(pair.f),
NRet: 0,
Protect: true,
}, lua.LString(pair.n)); err != nil {
return err
}
}
return nil
}
// Retain returns the objects that based on the "desired" object but with values retained from the "observed" object by lua.
func (vm VM) Retain(desired *unstructured.Unstructured, observed *unstructured.Unstructured, script string) (retained *unstructured.Unstructured, err error) {
l := lua.NewState(lua.Options{
SkipOpenLibs: !vm.UseOpenLibs,
})
defer l.Close()
// Opens table library to allow access to functions to manipulate tables
err = vm.setLib(l)
if err != nil {
return nil, err
}
// preload our 'safe' version of the OS library. Allows the 'local os = require("os")' to work
l.PreloadModule(lua.OsLibName, lifted.SafeOsLoader)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
l.SetContext(ctx)
err = l.DoString(script)
if err != nil {
return nil, err
}
retainLuaFunc := l.GetGlobal("Retain")
if retainLuaFunc.Type() == lua.LTNil {
return nil, fmt.Errorf("can't get function Retatin pleace check the function ")
}
args := make([]lua.LValue, 2)
args[0] = decodeValue(l, desired.Object)
args[1] = decodeValue(l, observed.Object)
err = l.CallByParam(lua.P{Fn: retainLuaFunc, NRet: 1, Protect: true}, args...)
if err != nil {
return nil, err
}
luaResult := l.Get(l.GetTop())
retainResult := &unstructured.Unstructured{}
if luaResult.Type() == lua.LTTable {
err := ConvertLuaResultInto(luaResult, retainResult)
if err != nil {
return nil, err
}
return retainResult, nil
}
return nil, fmt.Errorf("expect the returned requires type is table but got %s", luaResult.Type())
}
// AggregateStatus returns the objects that based on the 'object' but with status aggregated by lua.
func (vm VM) AggregateStatus(object *unstructured.Unstructured, item []map[string]interface{}, script string) (*unstructured.Unstructured, error) {
l := lua.NewState(lua.Options{
SkipOpenLibs: !vm.UseOpenLibs,
})
defer l.Close()
// Opens table library to allow access to functions to manipulate tables
err := vm.setLib(l)
if err != nil {
return nil, err
}
// preload our 'safe' version of the OS library. Allows the 'local os = require("os")' to work
l.PreloadModule(lua.OsLibName, lifted.SafeOsLoader)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
l.SetContext(ctx)
err = l.DoString(script)
if err != nil {
return nil, err
}
f := l.GetGlobal("AggregateStatus")
if f.Type() == lua.LTNil {
return nil, fmt.Errorf("can't get function AggregateStatus pleace check the function ")
}
args := make([]lua.LValue, 2)
args[0] = decodeValue(l, object.Object)
args[1] = decodeValue(l, item)
err = l.CallByParam(lua.P{Fn: f, NRet: 1, Protect: true}, args...)
if err != nil {
return nil, err
}
luaResult := l.Get(l.GetTop())
aggregateStatus := &unstructured.Unstructured{}
if luaResult.Type() == lua.LTTable {
err := ConvertLuaResultInto(luaResult, aggregateStatus)
if err != nil {
return nil, err
}
return aggregateStatus, nil
}
return nil, fmt.Errorf("expect the returned requires type is table but got %s", luaResult.Type())
}
// InterpretHealth returns the health state of the object by lua.
func (vm VM) InterpretHealth(object *unstructured.Unstructured, script string) (bool, error) {
l := lua.NewState(lua.Options{
SkipOpenLibs: !vm.UseOpenLibs,
})
defer l.Close()
// Opens table library to allow access to functions to manipulate tables
err := vm.setLib(l)
if err != nil {
return false, err
}
// preload our 'safe' version of the OS library. Allows the 'local os = require("os")' to work
l.PreloadModule(lua.OsLibName, lifted.SafeOsLoader)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
l.SetContext(ctx)
err = l.DoString(script)
if err != nil {
return false, err
}
f := l.GetGlobal("InterpretHealth")
if f.Type() == lua.LTNil {
return false, fmt.Errorf("can't get function InterpretHealth pleace check the function ")
}
args := make([]lua.LValue, 1)
args[0] = decodeValue(l, object.Object)
err = l.CallByParam(lua.P{Fn: f, NRet: 1, Protect: true}, args...)
if err != nil {
return false, err
}
var health bool
luaResult := l.Get(l.GetTop())
health, err = ConvertLuaResultToBool(luaResult)
if err != nil {
return false, err
}
return health, nil
}
// ReflectStatus returns the status of the object by lua.
func (vm VM) ReflectStatus(object *unstructured.Unstructured, script string) (status *runtime.RawExtension, err error) {
l := lua.NewState(lua.Options{
SkipOpenLibs: !vm.UseOpenLibs,
})
defer l.Close()
// Opens table library to allow access to functions to manipulate tables
err = vm.setLib(l)
if err != nil {
return nil, err
}
// preload our 'safe' version of the OS library. Allows the 'local os = require("os")' to work
l.PreloadModule(lua.OsLibName, lifted.SafeOsLoader)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
l.SetContext(ctx)
err = l.DoString(script)
if err != nil {
return nil, err
}
f := l.GetGlobal("ReflectStatus")
if f.Type() == lua.LTNil {
return nil, fmt.Errorf("can't get function ReflectStatus pleace check the function ")
}
args := make([]lua.LValue, 1)
args[0] = decodeValue(l, object.Object)
err = l.CallByParam(lua.P{Fn: f, NRet: 2, Protect: true}, args...)
if err != nil {
return nil, err
}
luaStatusResult := l.Get(l.GetTop())
l.Pop(1)
if luaStatusResult.Type() != lua.LTTable {
return nil, fmt.Errorf("expect the returned replica type is table but got %s", luaStatusResult.Type())
}
luaExistResult := l.Get(l.GetTop())
var exist bool
exist, err = ConvertLuaResultToBool(luaExistResult)
if err != nil {
return nil, err
}
if exist {
resultMap := make(map[string]interface{})
jsonBytes, err := luajson.Encode(luaStatusResult)
if err != nil {
return nil, err
}
err = json.Unmarshal(jsonBytes, &resultMap)
if err != nil {
return nil, err
}
return helper.BuildStatusRawExtension(resultMap)
}
return nil, err
}
// GetDependencies returns the dependent resources of the given object by lua.
func (vm VM) GetDependencies(object *unstructured.Unstructured, script string) (dependencies []configv1alpha1.DependentObjectReference, err error) {
l := lua.NewState(lua.Options{
SkipOpenLibs: !vm.UseOpenLibs,
})
defer l.Close()
// Opens table library to allow access to functions to manipulate tables
err = vm.setLib(l)
if err != nil {
return nil, err
}
// preload our 'safe' version of the OS library. Allows the 'local os = require("os")' to work
l.PreloadModule(lua.OsLibName, lifted.SafeOsLoader)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
l.SetContext(ctx)
err = l.DoString(script)
if err != nil {
return nil, err
}
f := l.GetGlobal("GetDependencies")
if f.Type() == lua.LTNil {
return nil, fmt.Errorf("can't get function Retatin pleace check the function ")
}
args := make([]lua.LValue, 1)
args[0] = decodeValue(l, object.Object)
err = l.CallByParam(lua.P{Fn: f, NRet: 1, Protect: true}, args...)
if err != nil {
return nil, err
}
luaResult := l.Get(l.GetTop())
if luaResult.Type() == lua.LTTable {
jsonBytes, err := luajson.Encode(luaResult)
if err != nil {
return nil, err
}
err = json.Unmarshal(jsonBytes, &dependencies)
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("expect the returned requires type is table but got %s", luaResult.Type())
}
return
}
// Took logic from the link below and added the int, int32, and int64 types since the value would have type int64
// while actually running in the controller and it was not reproducible through testing.
// https://github.com/layeh/gopher-json/blob/97fed8db84274c421dbfffbb28ec859901556b97/json.go#L154
func decodeValue(L *lua.LState, value interface{}) lua.LValue {
switch converted := value.(type) {
case bool:
return lua.LBool(converted)
case float64:
return lua.LNumber(converted)
case string:
return lua.LString(converted)
case json.Number:
return lua.LString(converted)
case int:
return lua.LNumber(converted)
case int32:
return lua.LNumber(converted)
case int64:
return lua.LNumber(converted)
case []interface{}:
arr := L.CreateTable(len(converted), 0)
for _, item := range converted {
arr.Append(decodeValue(L, item))
}
return arr
case []map[string]interface{}:
arr := L.CreateTable(len(converted), 0)
for _, item := range converted {
arr.Append(decodeValue(L, item))
}
return arr
case map[string]interface{}:
tbl := L.CreateTable(0, len(converted))
for key, item := range converted {
tbl.RawSetH(lua.LString(key), decodeValue(L, item))
}
return tbl
case nil:
return lua.LNil
}
return lua.LNil
}

View File

@ -0,0 +1,50 @@
package luavm
import (
"encoding/json"
"fmt"
"reflect"
lua "github.com/yuin/gopher-lua"
"k8s.io/apimachinery/pkg/conversion"
luajson "layeh.com/gopher-json"
)
// ConvertLuaResultInto convert lua result to obj
func ConvertLuaResultInto(luaResult lua.LValue, obj interface{}) error {
t, err := conversion.EnforcePtr(obj)
if err != nil {
return fmt.Errorf("obj is not pointer")
}
jsonBytes, err := luajson.Encode(luaResult)
if err != nil {
return fmt.Errorf("json Encode obj eroor %v", err)
}
// for lua an empty object by json encode be [] not {}
if t.Kind() == reflect.Struct && len(jsonBytes) > 1 && jsonBytes[0] == '[' {
jsonBytes[0], jsonBytes[len(jsonBytes)-1] = '{', '}'
}
err = json.Unmarshal(jsonBytes, obj)
if err != nil {
return fmt.Errorf("can not unmarshal %v to %#v", string(jsonBytes), obj)
}
return nil
}
// ConvertLuaResultToInt convert lua result to int.
func ConvertLuaResultToInt(luaResult lua.LValue) (int32, error) {
if luaResult.Type() != lua.LTNumber {
return 0, fmt.Errorf("result type %#v is not number", luaResult.Type())
}
return int32(luaResult.(lua.LNumber)), nil
}
// ConvertLuaResultToBool convert lua result to bool.
func ConvertLuaResultToBool(luaResult lua.LValue) (bool, error) {
if luaResult.Type() != lua.LTBool {
return false, fmt.Errorf("result type %#v is not bool", luaResult.Type())
}
return bool(luaResult.(lua.LBool)), nil
}

View File

@ -0,0 +1,476 @@
package luavm
import (
"encoding/json"
"reflect"
"testing"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/klog/v2"
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
"github.com/karmada-io/karmada/pkg/util/helper"
)
func TestGetReplicas(t *testing.T) {
var replicas int32 = 1
//quantity := *resource.NewQuantity(1000, resource.BinarySI)
vm := VM{UseOpenLibs: false}
tests := []struct {
name string
deploy *appsv1.Deployment
luaScript string
expected bool
}{
{
name: "Test GetReplica",
deploy: &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "foo",
Name: "bar",
},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{},
},
},
},
},
},
expected: true,
luaScript: ` function GetReplicas(desiredObj)
nodeClaim = {}
resourceRequest = {}
result = {}
replica = desiredObj.spec.replicas
result.resourceRequest = desiredObj.spec.template.spec.containers[1].resources.limits
nodeClaim.nodeSelector = desiredObj.spec.template.spec.nodeSelector
nodeClaim.tolerations = desiredObj.spec.template.spec.tolerations
result.nodeClaim = {}
result.nodeClaim = nil
return replica, {}
end`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
toUnstructured, _ := helper.ToUnstructured(tt.deploy)
replicas, requires, err := vm.GetReplicas(toUnstructured, tt.luaScript)
klog.Infof("replicas %v", replicas)
klog.Infof("requires %v", requires)
if err != nil {
t.Errorf(err.Error())
}
})
}
}
func TestReviseDeploymentReplica(t *testing.T) {
tests := []struct {
name string
object *unstructured.Unstructured
replica int32
expected *unstructured.Unstructured
expectError bool
luaScript string
}{
{
name: "Test ReviseDeploymentReplica",
object: &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "fake-deployment",
},
"spec": map[string]interface{}{
"replicas": 1,
},
},
},
replica: 3,
expectError: true,
luaScript: `function ReviseReplica(desiredObj, desiredReplica)
desiredObj.spec.replicas = desiredReplica
return desiredObj
end`,
},
{
name: "revise deployment replica",
object: &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "fake-deployment",
},
"spec": map[string]interface{}{
"replicas": int64(1),
},
},
},
replica: 3,
expected: &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "fake-deployment",
},
"spec": map[string]interface{}{
"replicas": int64(2),
},
},
},
expectError: false,
luaScript: `function ReviseReplica(desiredObj, desiredReplica)
desiredObj.spec.replicas = desiredReplica
return desiredObj
end`,
},
}
vm := VM{UseOpenLibs: false}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res, err := vm.ReviseReplica(tt.object, int64(tt.replica), tt.luaScript)
if err != nil {
t.Errorf(err.Error())
}
deploy := &appsv1.Deployment{}
err = runtime.DefaultUnstructuredConverter.FromUnstructured(res.UnstructuredContent(), deploy)
if err == nil && *deploy.Spec.Replicas == tt.replica {
t.Log("Success Test")
}
if err != nil {
t.Errorf(err.Error())
}
})
}
}
func TestAggregateDeploymentStatus(t *testing.T) {
statusMap := map[string]interface{}{
"replicas": 0,
"readyReplicas": 0,
"updatedReplicas": 0,
"availableReplicas": 1,
"unavailableReplicas": 0,
}
raw, _ := helper.BuildStatusRawExtension(statusMap)
aggregatedStatusItems := []workv1alpha2.AggregatedStatusItem{
{ClusterName: "member1", Status: raw, Applied: true},
{ClusterName: "member2", Status: raw, Applied: true},
}
oldDeploy := &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
},
}
oldDeploy.Status = appsv1.DeploymentStatus{
Replicas: 0, ReadyReplicas: 1, UpdatedReplicas: 0, AvailableReplicas: 0, UnavailableReplicas: 0}
newDeploy := &appsv1.Deployment{Status: appsv1.DeploymentStatus{Replicas: 0, ReadyReplicas: 0, UpdatedReplicas: 0, AvailableReplicas: 2, UnavailableReplicas: 0}}
oldObj, _ := helper.ToUnstructured(oldDeploy)
newObj, _ := helper.ToUnstructured(newDeploy)
var aggregateItem []map[string]interface{}
for _, item := range aggregatedStatusItems {
if item.Status == nil {
continue
}
temp := make(map[string]interface{})
if err := json.Unmarshal(item.Status.Raw, &temp); err != nil {
t.Error(err.Error())
}
aggregateItem = append(aggregateItem, temp)
}
tests := []struct {
name string
curObj *unstructured.Unstructured
aggregatedStatusItems []map[string]interface{}
expectedObj *unstructured.Unstructured
luaScript string
}{
{
name: "Test AggregateDeploymentStatus",
curObj: oldObj,
aggregatedStatusItems: aggregateItem,
expectedObj: newObj,
luaScript: `function AggregateStatus(desiredObj, statusItems)
for i = 1, #statusItems do
desiredObj.status.readyReplicas = desiredObj.status.readyReplicas + statusItems[i].readyReplicas
end
return desiredObj
end`,
},
}
vm := VM{UseOpenLibs: false}
for _, tt := range tests {
actualObj, _ := vm.AggregateStatus(tt.curObj, tt.aggregatedStatusItems, tt.luaScript)
actualDeploy := appsv1.DeploymentStatus{}
err := helper.ConvertToTypedObject(actualObj.Object["status"], &actualDeploy)
if err != nil {
t.Error(err.Error())
}
expectDeploy := appsv1.DeploymentStatus{}
err = helper.ConvertToTypedObject(tt.expectedObj.Object["status"], &expectDeploy)
if err != nil {
t.Error(err.Error())
}
if reflect.DeepEqual(expectDeploy, actualDeploy) {
t.Log("Success \n")
}
}
}
func TestHealthDeploymentStatus(t *testing.T) {
var cnt int32 = 2
newDeploy := &appsv1.Deployment{
Spec: appsv1.DeploymentSpec{
Replicas: &cnt,
},
ObjectMeta: metav1.ObjectMeta{
Generation: 1,
},
Status: appsv1.DeploymentStatus{ObservedGeneration: 1, Replicas: 2, ReadyReplicas: 2, UpdatedReplicas: 2, AvailableReplicas: 2}}
newObj, _ := helper.ToUnstructured(newDeploy)
tests := []struct {
name string
curObj *unstructured.Unstructured
expectedObj bool
luaScript string
}{
{
name: "Test HealthDeploymentStatus",
curObj: newObj,
expectedObj: true,
luaScript: `function InterpretHealth(observedObj)
return (observedObj.status.updatedReplicas == observedObj.spec.replicas) and (observedObj.metadata.generation == observedObj.status.observedGeneration)
end `,
},
}
vm := VM{UseOpenLibs: false}
for _, tt := range tests {
flag, _ := vm.InterpretHealth(tt.curObj, tt.luaScript)
if reflect.DeepEqual(flag, tt.expectedObj) {
t.Log("Success \n")
}
}
}
func TestRetainDeployment(t *testing.T) {
tests := []struct {
name string
desiredObj *unstructured.Unstructured
observedObj *unstructured.Unstructured
expectError bool
luaScript string
}{
{
name: "Test RetainDeployment",
desiredObj: &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "fake-deployment",
},
"spec": map[string]interface{}{
"replicas": 1,
},
},
},
observedObj: &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "fake-deployment",
},
"spec": map[string]interface{}{
"replicas": int64(2),
},
},
},
expectError: true,
luaScript: "function Retain(desiredObj, observedObj)\n desiredObj = observedObj\n return desiredObj\n end",
},
{
name: "revise deployment replica",
desiredObj: &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "fake-deployment",
},
"spec": map[string]interface{}{
"replicas": int64(1),
},
},
},
observedObj: &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "fake-deployment",
},
"spec": map[string]interface{}{
"replicas": int64(2),
},
},
},
expectError: false,
luaScript: `function Retain(desiredObj, observedObj)
desiredObj = observedObj
return desiredObj
end`,
},
}
vm := VM{UseOpenLibs: false}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res, err := vm.Retain(tt.desiredObj, tt.observedObj, tt.luaScript)
if err != nil {
t.Errorf(err.Error())
}
deploy := &appsv1.Deployment{}
err = runtime.DefaultUnstructuredConverter.FromUnstructured(res.UnstructuredContent(), deploy)
if err == nil && reflect.DeepEqual(deploy, tt.observedObj) {
t.Log("Success Test")
}
if err != nil {
t.Errorf(err.Error())
}
})
}
}
func TestStatusReflection(t *testing.T) {
testMap := map[string]interface{}{"key": "value"}
wantRawExtension, _ := helper.BuildStatusRawExtension(testMap)
type args struct {
object *unstructured.Unstructured
}
tests := []struct {
name string
args args
want *runtime.RawExtension
wantErr bool
luaScript string
}{
{
"Test StatusReflection",
args{
&unstructured.Unstructured{
Object: map[string]interface{}{
"status": testMap,
},
},
},
wantRawExtension,
false,
`function ReflectStatus (observedObj)
if observedObj.status == nil then
return false, nil
end
return true, observedObj.status
end`,
},
}
vm := VM{UseOpenLibs: false}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := vm.ReflectStatus(tt.args.object, tt.luaScript)
if (err != nil) != tt.wantErr {
t.Errorf("reflectWholeStatus() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("reflectWholeStatus() got = %v, want %v", got, tt.want)
}
})
}
}
func TestGetDeployPodDependencies(t *testing.T) {
newDeploy := appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Deployment",
},
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "test",
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
ServiceAccountName: "test",
},
},
},
}
newObj, _ := helper.ToUnstructured(&newDeploy)
tests := []struct {
name string
curObj *unstructured.Unstructured
luaScript string
}{
{
name: "Get GetDeployPodDependencies",
curObj: newObj,
luaScript: `function GetDependencies(desiredObj)
dependentSas = {}
refs = {}
if desiredObj.spec.template.spec.serviceAccountName ~= \"\" and desiredObj.spec.template.spec.serviceAccountName ~= \"default\" then
dependentSas[desiredObj.spec.template.spec.serviceAccountName] = true
end
local idx = 1
for key, value in pairs(dependentSas) do
dependObj = {}
dependObj.apiVersion = \"v1\"
dependObj.kind = \"ServiceAccount\"
dependObj.name = key
dependObj.namespace = desiredObj.namespace
refs[idx] = {}
refs[idx] = dependObj
idx = idx + 1
end
return refs
end`,
},
}
vm := VM{UseOpenLibs: false}
for _, tt := range tests {
res, _ := vm.GetDependencies(tt.curObj, tt.luaScript)
t.Logf("res %v", res)
}
}

View File

@ -0,0 +1,192 @@
package lifted
// This code is directly lifted from the argo-cd codebase in order to avoid relying on the lua package.
// For reference:
// https://github.com/argoproj/argo-cd/blob/master/util/lua/oslib_safe.go
// oslib_safe contains a subset of the lua OS library. For security reasons, we do not expose
// the entirety of lua OS library to custom actions, such as ones which can exit, read files, etc.
// Only the safe functions like os.time(), os.date() are exposed. Implementation was copied from
// github.com/yuin/gopher-lua.
import (
"fmt"
"strings"
"time"
lua "github.com/yuin/gopher-lua"
)
// OpenSafeOs open safe os
func OpenSafeOs(L *lua.LState) int {
tabmod := L.RegisterModule(lua.TabLibName, osFuncs)
L.Push(tabmod)
return 1
}
// SafeOsLoader sofe laoder
func SafeOsLoader(L *lua.LState) int {
mod := L.SetFuncs(L.NewTable(), osFuncs)
L.Push(mod)
return 1
}
var osFuncs = map[string]lua.LGFunction{
"time": osTime,
"date": osDate,
}
func osTime(L *lua.LState) int {
if L.GetTop() == 0 {
L.Push(lua.LNumber(time.Now().Unix()))
} else {
tbl := L.CheckTable(1)
sec := getIntField(tbl, "sec", 0)
min := getIntField(tbl, "min", 0)
hour := getIntField(tbl, "hour", 12)
day := getIntField(tbl, "day", -1)
month := getIntField(tbl, "month", -1)
year := getIntField(tbl, "year", -1)
isdst := getBoolField(tbl, "isdst", false)
t := time.Date(year, time.Month(month), day, hour, min, sec, 0, time.Local)
// TODO dst
if false {
print(isdst)
}
L.Push(lua.LNumber(t.Unix()))
}
return 1
}
func getIntField(tb *lua.LTable, key string, v int) int {
ret := tb.RawGetString(key)
if ln, ok := ret.(lua.LNumber); ok {
return int(ln)
}
return v
}
func getBoolField(tb *lua.LTable, key string, v bool) bool {
ret := tb.RawGetString(key)
if lb, ok := ret.(lua.LBool); ok {
return bool(lb)
}
return v
}
func osDate(L *lua.LState) int {
t := time.Now()
cfmt := "%c"
if L.GetTop() >= 1 {
cfmt = L.CheckString(1)
if strings.HasPrefix(cfmt, "!") {
t = time.Now().UTC()
cfmt = strings.TrimLeft(cfmt, "!")
}
if L.GetTop() >= 2 {
t = time.Unix(L.CheckInt64(2), 0)
}
if strings.HasPrefix(cfmt, "*t") {
ret := L.NewTable()
ret.RawSetString("year", lua.LNumber(t.Year()))
ret.RawSetString("month", lua.LNumber(t.Month()))
ret.RawSetString("day", lua.LNumber(t.Day()))
ret.RawSetString("hour", lua.LNumber(t.Hour()))
ret.RawSetString("min", lua.LNumber(t.Minute()))
ret.RawSetString("sec", lua.LNumber(t.Second()))
ret.RawSetString("wday", lua.LNumber(t.Weekday()+1))
// TODO yday & dst
ret.RawSetString("yday", lua.LNumber(0))
ret.RawSetString("isdst", lua.LFalse)
L.Push(ret)
return 1
}
}
L.Push(lua.LString(strftime(t, cfmt)))
return 1
}
var cDateFlagToGo = map[byte]string{
'a': "mon", 'A': "Monday", 'b': "Jan", 'B': "January", 'c': "02 Jan 06 15:04 MST", 'd': "02",
'F': "2006-01-02", 'H': "15", 'I': "03", 'm': "01", 'M': "04", 'p': "PM", 'P': "pm", 'S': "05",
'x': "15/04/05", 'X': "15:04:05", 'y': "06", 'Y': "2006", 'z': "-0700", 'Z': "MST"}
func strftime(t time.Time, cfmt string) string {
sc := newFlagScanner('%', "", "", cfmt)
for c, eos := sc.Next(); !eos; c, eos = sc.Next() {
if !sc.ChangeFlag {
if sc.HasFlag {
if v, ok := cDateFlagToGo[c]; ok {
sc.AppendString(t.Format(v))
} else {
switch c {
case 'w':
sc.AppendString(fmt.Sprint(int(t.Weekday())))
default:
sc.AppendChar('%')
sc.AppendChar(c)
}
}
sc.HasFlag = false
} else {
sc.AppendChar(c)
}
}
}
return sc.String()
}
type flagScanner struct {
flag byte
start string
end string
buf []byte
str string
Length int
Pos int
HasFlag bool
ChangeFlag bool
}
func newFlagScanner(flag byte, start, end, str string) *flagScanner {
return &flagScanner{flag, start, end, make([]byte, 0, len(str)), str, len(str), 0, false, false}
}
// AppendString append string to fs.buf
func (fs *flagScanner) AppendString(str string) { fs.buf = append(fs.buf, str...) }
// AppendChar append char to fs.buf
func (fs *flagScanner) AppendChar(ch byte) { fs.buf = append(fs.buf, ch) }
// String return the fs.buf of string
func (fs *flagScanner) String() string { return string(fs.buf) }
// Next iterate fs
func (fs *flagScanner) Next() (byte, bool) {
c := byte('\000')
fs.ChangeFlag = false
if fs.Pos == fs.Length {
if fs.HasFlag {
fs.AppendString(fs.end)
}
return c, true
}
c = fs.str[fs.Pos]
if c == fs.flag {
if fs.Pos < (fs.Length-1) && fs.str[fs.Pos+1] == fs.flag {
fs.HasFlag = false
fs.AppendChar(fs.flag)
fs.Pos += 2
return fs.Next()
} else if fs.Pos != fs.Length-1 {
if fs.HasFlag {
fs.AppendString(fs.end)
}
fs.AppendString(fs.start)
fs.ChangeFlag = true
fs.HasFlag = true
}
}
fs.Pos++
return c, false
}

18
vendor/github.com/yuin/gopher-lua/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,18 @@
language: go
go:
- "1.9.x"
- "1.10.x"
- "1.11.x"
env:
global:
GO111MODULE=off
before_install:
- go get github.com/axw/gocov/gocov
- go get github.com/mattn/goveralls
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
install:
- go get -u -v $(go list -f '{{join .Imports "\n"}}{{"\n"}}{{join .TestImports "\n"}}' ./... | sort | uniq | grep '\.' | grep -v gopher-lua)
script:
- $HOME/gopath/bin/goveralls -service=travis-ci

21
vendor/github.com/yuin/gopher-lua/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Yusuke Inuzuka
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

10
vendor/github.com/yuin/gopher-lua/Makefile generated vendored Normal file
View File

@ -0,0 +1,10 @@
.PHONY: build test glua
build:
./_tools/go-inline *.go && go fmt . && go build
glua: *.go pm/*.go cmd/glua/glua.go
./_tools/go-inline *.go && go fmt . && go build cmd/glua/glua.go
test:
./_tools/go-inline *.go && go fmt . && go test

888
vendor/github.com/yuin/gopher-lua/README.rst generated vendored Normal file
View File

@ -0,0 +1,888 @@
===============================================================================
GopherLua: VM and compiler for Lua in Go.
===============================================================================
.. image:: https://godoc.org/github.com/yuin/gopher-lua?status.svg
:target: http://godoc.org/github.com/yuin/gopher-lua
.. image:: https://travis-ci.org/yuin/gopher-lua.svg
:target: https://travis-ci.org/yuin/gopher-lua
.. image:: https://coveralls.io/repos/yuin/gopher-lua/badge.svg
:target: https://coveralls.io/r/yuin/gopher-lua
.. image:: https://badges.gitter.im/Join%20Chat.svg
:alt: Join the chat at https://gitter.im/yuin/gopher-lua
:target: https://gitter.im/yuin/gopher-lua?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
GopherLua is a Lua5.1 VM and compiler written in Go. GopherLua has a same goal
with Lua: **Be a scripting language with extensible semantics** . It provides
Go APIs that allow you to easily embed a scripting language to your Go host
programs.
.. contents::
:depth: 1
----------------------------------------------------------------
Design principle
----------------------------------------------------------------
- Be a scripting language with extensible semantics.
- User-friendly Go API
- The stack based API like the one used in the original Lua
implementation will cause a performance improvements in GopherLua
(It will reduce memory allocations and concrete type <-> interface conversions).
GopherLua API is **not** the stack based API.
GopherLua give preference to the user-friendliness over the performance.
----------------------------------------------------------------
How about performance?
----------------------------------------------------------------
GopherLua is not fast but not too slow, I think.
GopherLua has almost equivalent ( or little bit better ) performance as Python3 on micro benchmarks.
There are some benchmarks on the `wiki page <https://github.com/yuin/gopher-lua/wiki/Benchmarks>`_ .
----------------------------------------------------------------
Installation
----------------------------------------------------------------
.. code-block:: bash
go get github.com/yuin/gopher-lua
GopherLua supports >= Go1.9.
----------------------------------------------------------------
Usage
----------------------------------------------------------------
GopherLua APIs perform in much the same way as Lua, **but the stack is used only
for passing arguments and receiving returned values.**
GopherLua supports channel operations. See **"Goroutines"** section.
Import a package.
.. code-block:: go
import (
"github.com/yuin/gopher-lua"
)
Run scripts in the VM.
.. code-block:: go
L := lua.NewState()
defer L.Close()
if err := L.DoString(`print("hello")`); err != nil {
panic(err)
}
.. code-block:: go
L := lua.NewState()
defer L.Close()
if err := L.DoFile("hello.lua"); err != nil {
panic(err)
}
Refer to `Lua Reference Manual <http://www.lua.org/manual/5.1/>`_ and `Go doc <http://godoc.org/github.com/yuin/gopher-lua>`_ for further information.
Note that elements that are not commented in `Go doc <http://godoc.org/github.com/yuin/gopher-lua>`_ equivalent to `Lua Reference Manual <http://www.lua.org/manual/5.1/>`_ , except GopherLua uses objects instead of Lua stack indices.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Data model
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
All data in a GopherLua program is an ``LValue`` . ``LValue`` is an interface
type that has following methods.
- ``String() string``
- ``Type() LValueType``
Objects implement an LValue interface are
================ ========================= ================== =======================
Type name Go type Type() value Constants
================ ========================= ================== =======================
``LNilType`` (constants) ``LTNil`` ``LNil``
``LBool`` (constants) ``LTBool`` ``LTrue``, ``LFalse``
``LNumber`` float64 ``LTNumber`` ``-``
``LString`` string ``LTString`` ``-``
``LFunction`` struct pointer ``LTFunction`` ``-``
``LUserData`` struct pointer ``LTUserData`` ``-``
``LState`` struct pointer ``LTThread`` ``-``
``LTable`` struct pointer ``LTTable`` ``-``
``LChannel`` chan LValue ``LTChannel`` ``-``
================ ========================= ================== =======================
You can test an object type in Go way(type assertion) or using a ``Type()`` value.
.. code-block:: go
lv := L.Get(-1) // get the value at the top of the stack
if str, ok := lv.(lua.LString); ok {
// lv is LString
fmt.Println(string(str))
}
if lv.Type() != lua.LTString {
panic("string required.")
}
.. code-block:: go
lv := L.Get(-1) // get the value at the top of the stack
if tbl, ok := lv.(*lua.LTable); ok {
// lv is LTable
fmt.Println(L.ObjLen(tbl))
}
Note that ``LBool`` , ``LNumber`` , ``LString`` is not a pointer.
To test ``LNilType`` and ``LBool``, You **must** use pre-defined constants.
.. code-block:: go
lv := L.Get(-1) // get the value at the top of the stack
if lv == lua.LTrue { // correct
}
if bl, ok := lv.(lua.LBool); ok && bool(bl) { // wrong
}
In Lua, both ``nil`` and ``false`` make a condition false. ``LVIsFalse`` and ``LVAsBool`` implement this specification.
.. code-block:: go
lv := L.Get(-1) // get the value at the top of the stack
if lua.LVIsFalse(lv) { // lv is nil or false
}
if lua.LVAsBool(lv) { // lv is neither nil nor false
}
Objects that based on go structs(``LFunction``. ``LUserData``, ``LTable``)
have some public methods and fields. You can use these methods and fields for
performance and debugging, but there are some limitations.
- Metatable does not work.
- No error handlings.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Callstack & Registry size
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The size of an ``LState``'s callstack controls the maximum call depth for Lua functions within a script (Go function calls do not count).
The registry of an ``LState`` implements stack storage for calling functions (both Lua and Go functions) and also for temporary variables in expressions. Its storage requirements will increase with callstack usage and also with code complexity.
Both the registry and the callstack can be set to either a fixed size or to auto size.
When you have a large number of ``LStates`` instantiated in a process, it's worth taking the time to tune the registry and callstack options.
+++++++++
Registry
+++++++++
The registry can have an initial size, a maximum size and a step size configured on a per ``LState`` basis. This will allow the registry to grow as needed. It will not shrink again after growing.
.. code-block:: go
L := lua.NewState(lua.Options{
RegistrySize: 1024 * 20, // this is the initial size of the registry
RegistryMaxSize: 1024 * 80, // this is the maximum size that the registry can grow to. If set to `0` (the default) then the registry will not auto grow
RegistryGrowStep: 32, // this is how much to step up the registry by each time it runs out of space. The default is `32`.
})
defer L.Close()
A registry which is too small for a given script will ultimately result in a panic. A registry which is too big will waste memory (which can be significant if many ``LStates`` are instantiated).
Auto growing registries incur a small performance hit at the point they are resized but will not otherwise affect performance.
+++++++++
Callstack
+++++++++
The callstack can operate in two different modes, fixed or auto size.
A fixed size callstack has the highest performance and has a fixed memory overhead.
An auto sizing callstack will allocate and release callstack pages on demand which will ensure the minimum amount of memory is in use at any time. The downside is it will incur a small performance impact every time a new page of callframes is allocated.
By default an ``LState`` will allocate and free callstack frames in pages of 8, so the allocation overhead is not incurred on every function call. It is very likely that the performance impact of an auto resizing callstack will be negligible for most use cases.
.. code-block:: go
L := lua.NewState(lua.Options{
CallStackSize: 120, // this is the maximum callstack size of this LState
MinimizeStackMemory: true, // Defaults to `false` if not specified. If set, the callstack will auto grow and shrink as needed up to a max of `CallStackSize`. If not set, the callstack will be fixed at `CallStackSize`.
})
defer L.Close()
++++++++++++++++
Option defaults
++++++++++++++++
The above examples show how to customize the callstack and registry size on a per ``LState`` basis. You can also adjust some defaults for when options are not specified by altering the values of ``lua.RegistrySize``, ``lua.RegistryGrowStep`` and ``lua.CallStackSize``.
An ``LState`` object that has been created by ``*LState#NewThread()`` inherits the callstack & registry size from the parent ``LState`` object.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Miscellaneous lua.NewState options
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **Options.SkipOpenLibs bool(default false)**
- By default, GopherLua opens all built-in libraries when new LState is created.
- You can skip this behaviour by setting this to ``true`` .
- Using the various `OpenXXX(L *LState) int` functions you can open only those libraries that you require, for an example see below.
- **Options.IncludeGoStackTrace bool(default false)**
- By default, GopherLua does not show Go stack traces when panics occur.
- You can get Go stack traces by setting this to ``true`` .
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
API
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Refer to `Lua Reference Manual <http://www.lua.org/manual/5.1/>`_ and `Go doc(LState methods) <http://godoc.org/github.com/yuin/gopher-lua>`_ for further information.
+++++++++++++++++++++++++++++++++++++++++
Calling Go from Lua
+++++++++++++++++++++++++++++++++++++++++
.. code-block:: go
func Double(L *lua.LState) int {
lv := L.ToInt(1) /* get argument */
L.Push(lua.LNumber(lv * 2)) /* push result */
return 1 /* number of results */
}
func main() {
L := lua.NewState()
defer L.Close()
L.SetGlobal("double", L.NewFunction(Double)) /* Original lua_setglobal uses stack... */
}
.. code-block:: lua
print(double(20)) -- > "40"
Any function registered with GopherLua is a ``lua.LGFunction``, defined in ``value.go``
.. code-block:: go
type LGFunction func(*LState) int
Working with coroutines.
.. code-block:: go
co, _ := L.NewThread() /* create a new thread */
fn := L.GetGlobal("coro").(*lua.LFunction) /* get function from lua */
for {
st, err, values := L.Resume(co, fn)
if st == lua.ResumeError {
fmt.Println("yield break(error)")
fmt.Println(err.Error())
break
}
for i, lv := range values {
fmt.Printf("%v : %v\n", i, lv)
}
if st == lua.ResumeOK {
fmt.Println("yield break(ok)")
break
}
}
+++++++++++++++++++++++++++++++++++++++++
Opening a subset of builtin modules
+++++++++++++++++++++++++++++++++++++++++
The following demonstrates how to open a subset of the built-in modules in Lua, say for example to avoid enabling modules with access to local files or system calls.
main.go
.. code-block:: go
func main() {
L := lua.NewState(lua.Options{SkipOpenLibs: true})
defer L.Close()
for _, pair := range []struct {
n string
f lua.LGFunction
}{
{lua.LoadLibName, lua.OpenPackage}, // Must be first
{lua.BaseLibName, lua.OpenBase},
{lua.TabLibName, lua.OpenTable},
} {
if err := L.CallByParam(lua.P{
Fn: L.NewFunction(pair.f),
NRet: 0,
Protect: true,
}, lua.LString(pair.n)); err != nil {
panic(err)
}
}
if err := L.DoFile("main.lua"); err != nil {
panic(err)
}
}
+++++++++++++++++++++++++++++++++++++++++
Creating a module by Go
+++++++++++++++++++++++++++++++++++++++++
mymodule.go
.. code-block:: go
package mymodule
import (
"github.com/yuin/gopher-lua"
)
func Loader(L *lua.LState) int {
// register functions to the table
mod := L.SetFuncs(L.NewTable(), exports)
// register other stuff
L.SetField(mod, "name", lua.LString("value"))
// returns the module
L.Push(mod)
return 1
}
var exports = map[string]lua.LGFunction{
"myfunc": myfunc,
}
func myfunc(L *lua.LState) int {
return 0
}
mymain.go
.. code-block:: go
package main
import (
"./mymodule"
"github.com/yuin/gopher-lua"
)
func main() {
L := lua.NewState()
defer L.Close()
L.PreloadModule("mymodule", mymodule.Loader)
if err := L.DoFile("main.lua"); err != nil {
panic(err)
}
}
main.lua
.. code-block:: lua
local m = require("mymodule")
m.myfunc()
print(m.name)
+++++++++++++++++++++++++++++++++++++++++
Calling Lua from Go
+++++++++++++++++++++++++++++++++++++++++
.. code-block:: go
L := lua.NewState()
defer L.Close()
if err := L.DoFile("double.lua"); err != nil {
panic(err)
}
if err := L.CallByParam(lua.P{
Fn: L.GetGlobal("double"),
NRet: 1,
Protect: true,
}, lua.LNumber(10)); err != nil {
panic(err)
}
ret := L.Get(-1) // returned value
L.Pop(1) // remove received value
If ``Protect`` is false, GopherLua will panic instead of returning an ``error`` value.
+++++++++++++++++++++++++++++++++++++++++
User-Defined types
+++++++++++++++++++++++++++++++++++++++++
You can extend GopherLua with new types written in Go.
``LUserData`` is provided for this purpose.
.. code-block:: go
type Person struct {
Name string
}
const luaPersonTypeName = "person"
// Registers my person type to given L.
func registerPersonType(L *lua.LState) {
mt := L.NewTypeMetatable(luaPersonTypeName)
L.SetGlobal("person", mt)
// static attributes
L.SetField(mt, "new", L.NewFunction(newPerson))
// methods
L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), personMethods))
}
// Constructor
func newPerson(L *lua.LState) int {
person := &Person{L.CheckString(1)}
ud := L.NewUserData()
ud.Value = person
L.SetMetatable(ud, L.GetTypeMetatable(luaPersonTypeName))
L.Push(ud)
return 1
}
// Checks whether the first lua argument is a *LUserData with *Person and returns this *Person.
func checkPerson(L *lua.LState) *Person {
ud := L.CheckUserData(1)
if v, ok := ud.Value.(*Person); ok {
return v
}
L.ArgError(1, "person expected")
return nil
}
var personMethods = map[string]lua.LGFunction{
"name": personGetSetName,
}
// Getter and setter for the Person#Name
func personGetSetName(L *lua.LState) int {
p := checkPerson(L)
if L.GetTop() == 2 {
p.Name = L.CheckString(2)
return 0
}
L.Push(lua.LString(p.Name))
return 1
}
func main() {
L := lua.NewState()
defer L.Close()
registerPersonType(L)
if err := L.DoString(`
p = person.new("Steeve")
print(p:name()) -- "Steeve"
p:name("Alice")
print(p:name()) -- "Alice"
`); err != nil {
panic(err)
}
}
+++++++++++++++++++++++++++++++++++++++++
Terminating a running LState
+++++++++++++++++++++++++++++++++++++++++
GopherLua supports the `Go Concurrency Patterns: Context <https://blog.golang.org/context>`_ .
.. code-block:: go
L := lua.NewState()
defer L.Close()
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// set the context to our LState
L.SetContext(ctx)
err := L.DoString(`
local clock = os.clock
function sleep(n) -- seconds
local t0 = clock()
while clock() - t0 <= n do end
end
sleep(3)
`)
// err.Error() contains "context deadline exceeded"
With coroutines
.. code-block:: go
L := lua.NewState()
defer L.Close()
ctx, cancel := context.WithCancel(context.Background())
L.SetContext(ctx)
defer cancel()
L.DoString(`
function coro()
local i = 0
while true do
coroutine.yield(i)
i = i+1
end
return i
end
`)
co, cocancel := L.NewThread()
defer cocancel()
fn := L.GetGlobal("coro").(*LFunction)
_, err, values := L.Resume(co, fn) // err is nil
cancel() // cancel the parent context
_, err, values = L.Resume(co, fn) // err is NOT nil : child context was canceled
**Note that using a context causes performance degradation.**
.. code-block::
time ./glua-with-context.exe fib.lua
9227465
0.01s user 0.11s system 1% cpu 7.505 total
time ./glua-without-context.exe fib.lua
9227465
0.01s user 0.01s system 0% cpu 5.306 total
+++++++++++++++++++++++++++++++++++++++++
Sharing Lua byte code between LStates
+++++++++++++++++++++++++++++++++++++++++
Calling ``DoFile`` will load a Lua script, compile it to byte code and run the byte code in a ``LState``.
If you have multiple ``LStates`` which are all required to run the same script, you can share the byte code between them,
which will save on memory.
Sharing byte code is safe as it is read only and cannot be altered by lua scripts.
.. code-block:: go
// CompileLua reads the passed lua file from disk and compiles it.
func CompileLua(filePath string) (*lua.FunctionProto, error) {
file, err := os.Open(filePath)
defer file.Close()
if err != nil {
return nil, err
}
reader := bufio.NewReader(file)
chunk, err := parse.Parse(reader, filePath)
if err != nil {
return nil, err
}
proto, err := lua.Compile(chunk, filePath)
if err != nil {
return nil, err
}
return proto, nil
}
// DoCompiledFile takes a FunctionProto, as returned by CompileLua, and runs it in the LState. It is equivalent
// to calling DoFile on the LState with the original source file.
func DoCompiledFile(L *lua.LState, proto *lua.FunctionProto) error {
lfunc := L.NewFunctionFromProto(proto)
L.Push(lfunc)
return L.PCall(0, lua.MultRet, nil)
}
// Example shows how to share the compiled byte code from a lua script between multiple VMs.
func Example() {
codeToShare := CompileLua("mylua.lua")
a := lua.NewState()
b := lua.NewState()
c := lua.NewState()
DoCompiledFile(a, codeToShare)
DoCompiledFile(b, codeToShare)
DoCompiledFile(c, codeToShare)
}
+++++++++++++++++++++++++++++++++++++++++
Goroutines
+++++++++++++++++++++++++++++++++++++++++
The ``LState`` is not goroutine-safe. It is recommended to use one LState per goroutine and communicate between goroutines by using channels.
Channels are represented by ``channel`` objects in GopherLua. And a ``channel`` table provides functions for performing channel operations.
Some objects can not be sent over channels due to having non-goroutine-safe objects inside itself.
- a thread(state)
- a function
- an userdata
- a table with a metatable
You **must not** send these objects from Go APIs to channels.
.. code-block:: go
func receiver(ch, quit chan lua.LValue) {
L := lua.NewState()
defer L.Close()
L.SetGlobal("ch", lua.LChannel(ch))
L.SetGlobal("quit", lua.LChannel(quit))
if err := L.DoString(`
local exit = false
while not exit do
channel.select(
{"|<-", ch, function(ok, v)
if not ok then
print("channel closed")
exit = true
else
print("received:", v)
end
end},
{"|<-", quit, function(ok, v)
print("quit")
exit = true
end}
)
end
`); err != nil {
panic(err)
}
}
func sender(ch, quit chan lua.LValue) {
L := lua.NewState()
defer L.Close()
L.SetGlobal("ch", lua.LChannel(ch))
L.SetGlobal("quit", lua.LChannel(quit))
if err := L.DoString(`
ch:send("1")
ch:send("2")
`); err != nil {
panic(err)
}
ch <- lua.LString("3")
quit <- lua.LTrue
}
func main() {
ch := make(chan lua.LValue)
quit := make(chan lua.LValue)
go receiver(ch, quit)
go sender(ch, quit)
time.Sleep(3 * time.Second)
}
'''''''''''''''
Go API
'''''''''''''''
``ToChannel``, ``CheckChannel``, ``OptChannel`` are available.
Refer to `Go doc(LState methods) <http://godoc.org/github.com/yuin/gopher-lua>`_ for further information.
'''''''''''''''
Lua API
'''''''''''''''
- **channel.make([buf:int]) -> ch:channel**
- Create new channel that has a buffer size of ``buf``. By default, ``buf`` is 0.
- **channel.select(case:table [, case:table, case:table ...]) -> {index:int, recv:any, ok}**
- Same as the ``select`` statement in Go. It returns the index of the chosen case and, if that
case was a receive operation, the value received and a boolean indicating whether the channel has been closed.
- ``case`` is a table that outlined below.
- receiving: `{"|<-", ch:channel [, handler:func(ok, data:any)]}`
- sending: `{"<-|", ch:channel, data:any [, handler:func(data:any)]}`
- default: `{"default" [, handler:func()]}`
``channel.select`` examples:
.. code-block:: lua
local idx, recv, ok = channel.select(
{"|<-", ch1},
{"|<-", ch2}
)
if not ok then
print("closed")
elseif idx == 1 then -- received from ch1
print(recv)
elseif idx == 2 then -- received from ch2
print(recv)
end
.. code-block:: lua
channel.select(
{"|<-", ch1, function(ok, data)
print(ok, data)
end},
{"<-|", ch2, "value", function(data)
print(data)
end},
{"default", function()
print("default action")
end}
)
- **channel:send(data:any)**
- Send ``data`` over the channel.
- **channel:receive() -> ok:bool, data:any**
- Receive some data over the channel.
- **channel:close()**
- Close the channel.
''''''''''''''''''''''''''''''
The LState pool pattern
''''''''''''''''''''''''''''''
To create per-thread LState instances, You can use the ``sync.Pool`` like mechanism.
.. code-block:: go
type lStatePool struct {
m sync.Mutex
saved []*lua.LState
}
func (pl *lStatePool) Get() *lua.LState {
pl.m.Lock()
defer pl.m.Unlock()
n := len(pl.saved)
if n == 0 {
return pl.New()
}
x := pl.saved[n-1]
pl.saved = pl.saved[0 : n-1]
return x
}
func (pl *lStatePool) New() *lua.LState {
L := lua.NewState()
// setting the L up here.
// load scripts, set global variables, share channels, etc...
return L
}
func (pl *lStatePool) Put(L *lua.LState) {
pl.m.Lock()
defer pl.m.Unlock()
pl.saved = append(pl.saved, L)
}
func (pl *lStatePool) Shutdown() {
for _, L := range pl.saved {
L.Close()
}
}
// Global LState pool
var luaPool = &lStatePool{
saved: make([]*lua.LState, 0, 4),
}
Now, you can get per-thread LState objects from the ``luaPool`` .
.. code-block:: go
func MyWorker() {
L := luaPool.Get()
defer luaPool.Put(L)
/* your code here */
}
func main() {
defer luaPool.Shutdown()
go MyWorker()
go MyWorker()
/* etc... */
}
----------------------------------------------------------------
Differences between Lua and GopherLua
----------------------------------------------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Goroutines
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- GopherLua supports channel operations.
- GopherLua has a type named ``channel``.
- The ``channel`` table provides functions for performing channel operations.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unsupported functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``string.dump``
- ``os.setlocale``
- ``lua_Debug.namewhat``
- ``package.loadlib``
- debug hooks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Miscellaneous notes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``collectgarbage`` does not take any arguments and runs the garbage collector for the entire Go program.
- ``file:setvbuf`` does not support a line buffering.
- Daylight saving time is not supported.
- GopherLua has a function to set an environment variable : ``os.setenv(name, value)``
----------------------------------------------------------------
Standalone interpreter
----------------------------------------------------------------
Lua has an interpreter called ``lua`` . GopherLua has an interpreter called ``glua`` .
.. code-block:: bash
go get github.com/yuin/gopher-lua/cmd/glua
``glua`` has same options as ``lua`` .
----------------------------------------------------------------
How to Contribute
----------------------------------------------------------------
See `Guidlines for contributors <https://github.com/yuin/gopher-lua/tree/master/.github/CONTRIBUTING.md>`_ .
----------------------------------------------------------------
Libraries for GopherLua
----------------------------------------------------------------
- `gopher-luar <https://github.com/layeh/gopher-luar>`_ : Simplifies data passing to and from gopher-lua
- `gluamapper <https://github.com/yuin/gluamapper>`_ : Mapping a Lua table to a Go struct
- `gluare <https://github.com/yuin/gluare>`_ : Regular expressions for gopher-lua
- `gluahttp <https://github.com/cjoudrey/gluahttp>`_ : HTTP request module for gopher-lua
- `gopher-json <https://github.com/layeh/gopher-json>`_ : A simple JSON encoder/decoder for gopher-lua
- `gluayaml <https://github.com/kohkimakimoto/gluayaml>`_ : Yaml parser for gopher-lua
- `glua-lfs <https://github.com/layeh/gopher-lfs>`_ : Partially implements the luafilesystem module for gopher-lua
- `gluaurl <https://github.com/cjoudrey/gluaurl>`_ : A url parser/builder module for gopher-lua
- `gluahttpscrape <https://github.com/felipejfc/gluahttpscrape>`_ : A simple HTML scraper module for gopher-lua
- `gluaxmlpath <https://github.com/ailncode/gluaxmlpath>`_ : An xmlpath module for gopher-lua
- `gmoonscript <https://github.com/rucuriousyet/gmoonscript>`_ : Moonscript Compiler for the Gopher Lua VM
- `loguago <https://github.com/rucuriousyet/loguago>`_ : Zerolog wrapper for Gopher-Lua
- `gluacrypto <https://github.com/tengattack/gluacrypto>`_ : A native Go implementation of crypto library for the GopherLua VM.
- `gluasql <https://github.com/tengattack/gluasql>`_ : A native Go implementation of SQL client for the GopherLua VM.
- `purr <https://github.com/leyafo/purr>`_ : A http mock testing tool.
- `vadv/gopher-lua-libs <https://github.com/vadv/gopher-lua-libs>`_ : Some usefull libraries for GopherLua VM.
- `gluaperiphery <https://github.com/BixData/gluaperiphery>`_ : A periphery library for the GopherLua VM (GPIO, SPI, I2C, MMIO, and Serial peripheral I/O for Linux).
- `glua-async <https://github.com/CuberL/glua-async>`_ : An async/await implement for gopher-lua.
- `gopherlua-debugger <https://github.com/edolphin-ydf/gopherlua-debugger>`_ : A debugger for gopher-lua
- `gluamahonia <https://github.com/super1207/gluamahonia>`_ : An encoding converter for gopher-lua
----------------------------------------------------------------
Donation
----------------------------------------------------------------
BTC: 1NEDSyUmo4SMTDP83JJQSWi1MvQUGGNMZB
----------------------------------------------------------------
License
----------------------------------------------------------------
MIT
----------------------------------------------------------------
Author
----------------------------------------------------------------
Yusuke Inuzuka

2085
vendor/github.com/yuin/gopher-lua/_state.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

1033
vendor/github.com/yuin/gopher-lua/_vm.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

79
vendor/github.com/yuin/gopher-lua/alloc.go generated vendored Normal file
View File

@ -0,0 +1,79 @@
package lua
import (
"reflect"
"unsafe"
)
// iface is an internal representation of the go-interface.
type iface struct {
itab unsafe.Pointer
word unsafe.Pointer
}
const preloadLimit LNumber = 128
var _fv float64
var _uv uintptr
var preloads [int(preloadLimit)]LValue
func init() {
for i := 0; i < int(preloadLimit); i++ {
preloads[i] = LNumber(i)
}
}
// allocator is a fast bulk memory allocator for the LValue.
type allocator struct {
size int
fptrs []float64
fheader *reflect.SliceHeader
scratchValue LValue
scratchValueP *iface
}
func newAllocator(size int) *allocator {
al := &allocator{
size: size,
fptrs: make([]float64, 0, size),
fheader: nil,
}
al.fheader = (*reflect.SliceHeader)(unsafe.Pointer(&al.fptrs))
al.scratchValue = LNumber(0)
al.scratchValueP = (*iface)(unsafe.Pointer(&al.scratchValue))
return al
}
// LNumber2I takes a number value and returns an interface LValue representing the same number.
// Converting an LNumber to a LValue naively, by doing:
// `var val LValue = myLNumber`
// will result in an individual heap alloc of 8 bytes for the float value. LNumber2I amortizes the cost and memory
// overhead of these allocs by allocating blocks of floats instead.
// The downside of this is that all of the floats on a given block have to become eligible for gc before the block
// as a whole can be gc-ed.
func (al *allocator) LNumber2I(v LNumber) LValue {
// first check for shared preloaded numbers
if v >= 0 && v < preloadLimit && float64(v) == float64(int64(v)) {
return preloads[int(v)]
}
// check if we need a new alloc page
if cap(al.fptrs) == len(al.fptrs) {
al.fptrs = make([]float64, 0, al.size)
al.fheader = (*reflect.SliceHeader)(unsafe.Pointer(&al.fptrs))
}
// alloc a new float, and store our value into it
al.fptrs = append(al.fptrs, float64(v))
fptr := &al.fptrs[len(al.fptrs)-1]
// hack our scratch LValue to point to our allocated value
// this scratch lvalue is copied when this function returns meaning the scratch value can be reused
// on the next call
al.scratchValueP.word = unsafe.Pointer(fptr)
return al.scratchValue
}

29
vendor/github.com/yuin/gopher-lua/ast/ast.go generated vendored Normal file
View File

@ -0,0 +1,29 @@
package ast
type PositionHolder interface {
Line() int
SetLine(int)
LastLine() int
SetLastLine(int)
}
type Node struct {
line int
lastline int
}
func (self *Node) Line() int {
return self.line
}
func (self *Node) SetLine(line int) {
self.line = line
}
func (self *Node) LastLine() int {
return self.lastline
}
func (self *Node) SetLastLine(line int) {
self.lastline = line
}

138
vendor/github.com/yuin/gopher-lua/ast/expr.go generated vendored Normal file
View File

@ -0,0 +1,138 @@
package ast
type Expr interface {
PositionHolder
exprMarker()
}
type ExprBase struct {
Node
}
func (expr *ExprBase) exprMarker() {}
/* ConstExprs {{{ */
type ConstExpr interface {
Expr
constExprMarker()
}
type ConstExprBase struct {
ExprBase
}
func (expr *ConstExprBase) constExprMarker() {}
type TrueExpr struct {
ConstExprBase
}
type FalseExpr struct {
ConstExprBase
}
type NilExpr struct {
ConstExprBase
}
type NumberExpr struct {
ConstExprBase
Value string
}
type StringExpr struct {
ConstExprBase
Value string
}
/* ConstExprs }}} */
type Comma3Expr struct {
ExprBase
AdjustRet bool
}
type IdentExpr struct {
ExprBase
Value string
}
type AttrGetExpr struct {
ExprBase
Object Expr
Key Expr
}
type TableExpr struct {
ExprBase
Fields []*Field
}
type FuncCallExpr struct {
ExprBase
Func Expr
Receiver Expr
Method string
Args []Expr
AdjustRet bool
}
type LogicalOpExpr struct {
ExprBase
Operator string
Lhs Expr
Rhs Expr
}
type RelationalOpExpr struct {
ExprBase
Operator string
Lhs Expr
Rhs Expr
}
type StringConcatOpExpr struct {
ExprBase
Lhs Expr
Rhs Expr
}
type ArithmeticOpExpr struct {
ExprBase
Operator string
Lhs Expr
Rhs Expr
}
type UnaryMinusOpExpr struct {
ExprBase
Expr Expr
}
type UnaryNotOpExpr struct {
ExprBase
Expr Expr
}
type UnaryLenOpExpr struct {
ExprBase
Expr Expr
}
type FunctionExpr struct {
ExprBase
ParList *ParList
Stmts []Stmt
}

17
vendor/github.com/yuin/gopher-lua/ast/misc.go generated vendored Normal file
View File

@ -0,0 +1,17 @@
package ast
type Field struct {
Key Expr
Value Expr
}
type ParList struct {
HasVargs bool
Names []string
}
type FuncName struct {
Func Expr
Receiver Expr
Method string
}

95
vendor/github.com/yuin/gopher-lua/ast/stmt.go generated vendored Normal file
View File

@ -0,0 +1,95 @@
package ast
type Stmt interface {
PositionHolder
stmtMarker()
}
type StmtBase struct {
Node
}
func (stmt *StmtBase) stmtMarker() {}
type AssignStmt struct {
StmtBase
Lhs []Expr
Rhs []Expr
}
type LocalAssignStmt struct {
StmtBase
Names []string
Exprs []Expr
}
type FuncCallStmt struct {
StmtBase
Expr Expr
}
type DoBlockStmt struct {
StmtBase
Stmts []Stmt
}
type WhileStmt struct {
StmtBase
Condition Expr
Stmts []Stmt
}
type RepeatStmt struct {
StmtBase
Condition Expr
Stmts []Stmt
}
type IfStmt struct {
StmtBase
Condition Expr
Then []Stmt
Else []Stmt
}
type NumberForStmt struct {
StmtBase
Name string
Init Expr
Limit Expr
Step Expr
Stmts []Stmt
}
type GenericForStmt struct {
StmtBase
Names []string
Exprs []Expr
Stmts []Stmt
}
type FuncDefStmt struct {
StmtBase
Name *FuncName
Func *FunctionExpr
}
type ReturnStmt struct {
StmtBase
Exprs []Expr
}
type BreakStmt struct {
StmtBase
}

22
vendor/github.com/yuin/gopher-lua/ast/token.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
package ast
import (
"fmt"
)
type Position struct {
Source string
Line int
Column int
}
type Token struct {
Type int
Name string
Str string
Pos Position
}
func (self *Token) String() string {
return fmt.Sprintf("<type:%v, str:%v>", self.Name, self.Str)
}

460
vendor/github.com/yuin/gopher-lua/auxlib.go generated vendored Normal file
View File

@ -0,0 +1,460 @@
package lua
import (
"bufio"
"fmt"
"io"
"os"
"strings"
)
/* checkType {{{ */
func (ls *LState) CheckAny(n int) LValue {
if n > ls.GetTop() {
ls.ArgError(n, "value expected")
}
return ls.Get(n)
}
func (ls *LState) CheckInt(n int) int {
v := ls.Get(n)
if intv, ok := v.(LNumber); ok {
return int(intv)
}
ls.TypeError(n, LTNumber)
return 0
}
func (ls *LState) CheckInt64(n int) int64 {
v := ls.Get(n)
if intv, ok := v.(LNumber); ok {
return int64(intv)
}
ls.TypeError(n, LTNumber)
return 0
}
func (ls *LState) CheckNumber(n int) LNumber {
v := ls.Get(n)
if lv, ok := v.(LNumber); ok {
return lv
}
ls.TypeError(n, LTNumber)
return 0
}
func (ls *LState) CheckString(n int) string {
v := ls.Get(n)
if lv, ok := v.(LString); ok {
return string(lv)
} else if LVCanConvToString(v) {
return ls.ToString(n)
}
ls.TypeError(n, LTString)
return ""
}
func (ls *LState) CheckBool(n int) bool {
v := ls.Get(n)
if lv, ok := v.(LBool); ok {
return bool(lv)
}
ls.TypeError(n, LTBool)
return false
}
func (ls *LState) CheckTable(n int) *LTable {
v := ls.Get(n)
if lv, ok := v.(*LTable); ok {
return lv
}
ls.TypeError(n, LTTable)
return nil
}
func (ls *LState) CheckFunction(n int) *LFunction {
v := ls.Get(n)
if lv, ok := v.(*LFunction); ok {
return lv
}
ls.TypeError(n, LTFunction)
return nil
}
func (ls *LState) CheckUserData(n int) *LUserData {
v := ls.Get(n)
if lv, ok := v.(*LUserData); ok {
return lv
}
ls.TypeError(n, LTUserData)
return nil
}
func (ls *LState) CheckThread(n int) *LState {
v := ls.Get(n)
if lv, ok := v.(*LState); ok {
return lv
}
ls.TypeError(n, LTThread)
return nil
}
func (ls *LState) CheckType(n int, typ LValueType) {
v := ls.Get(n)
if v.Type() != typ {
ls.TypeError(n, typ)
}
}
func (ls *LState) CheckTypes(n int, typs ...LValueType) {
vt := ls.Get(n).Type()
for _, typ := range typs {
if vt == typ {
return
}
}
buf := []string{}
for _, typ := range typs {
buf = append(buf, typ.String())
}
ls.ArgError(n, strings.Join(buf, " or ")+" expected, got "+ls.Get(n).Type().String())
}
func (ls *LState) CheckOption(n int, options []string) int {
str := ls.CheckString(n)
for i, v := range options {
if v == str {
return i
}
}
ls.ArgError(n, fmt.Sprintf("invalid option: %s (must be one of %s)", str, strings.Join(options, ",")))
return 0
}
/* }}} */
/* optType {{{ */
func (ls *LState) OptInt(n int, d int) int {
v := ls.Get(n)
if v == LNil {
return d
}
if intv, ok := v.(LNumber); ok {
return int(intv)
}
ls.TypeError(n, LTNumber)
return 0
}
func (ls *LState) OptInt64(n int, d int64) int64 {
v := ls.Get(n)
if v == LNil {
return d
}
if intv, ok := v.(LNumber); ok {
return int64(intv)
}
ls.TypeError(n, LTNumber)
return 0
}
func (ls *LState) OptNumber(n int, d LNumber) LNumber {
v := ls.Get(n)
if v == LNil {
return d
}
if lv, ok := v.(LNumber); ok {
return lv
}
ls.TypeError(n, LTNumber)
return 0
}
func (ls *LState) OptString(n int, d string) string {
v := ls.Get(n)
if v == LNil {
return d
}
if lv, ok := v.(LString); ok {
return string(lv)
}
ls.TypeError(n, LTString)
return ""
}
func (ls *LState) OptBool(n int, d bool) bool {
v := ls.Get(n)
if v == LNil {
return d
}
if lv, ok := v.(LBool); ok {
return bool(lv)
}
ls.TypeError(n, LTBool)
return false
}
func (ls *LState) OptTable(n int, d *LTable) *LTable {
v := ls.Get(n)
if v == LNil {
return d
}
if lv, ok := v.(*LTable); ok {
return lv
}
ls.TypeError(n, LTTable)
return nil
}
func (ls *LState) OptFunction(n int, d *LFunction) *LFunction {
v := ls.Get(n)
if v == LNil {
return d
}
if lv, ok := v.(*LFunction); ok {
return lv
}
ls.TypeError(n, LTFunction)
return nil
}
func (ls *LState) OptUserData(n int, d *LUserData) *LUserData {
v := ls.Get(n)
if v == LNil {
return d
}
if lv, ok := v.(*LUserData); ok {
return lv
}
ls.TypeError(n, LTUserData)
return nil
}
/* }}} */
/* error operations {{{ */
func (ls *LState) ArgError(n int, message string) {
ls.RaiseError("bad argument #%v to %v (%v)", n, ls.rawFrameFuncName(ls.currentFrame), message)
}
func (ls *LState) TypeError(n int, typ LValueType) {
ls.RaiseError("bad argument #%v to %v (%v expected, got %v)", n, ls.rawFrameFuncName(ls.currentFrame), typ.String(), ls.Get(n).Type().String())
}
/* }}} */
/* debug operations {{{ */
func (ls *LState) Where(level int) string {
return ls.where(level, false)
}
/* }}} */
/* table operations {{{ */
func (ls *LState) FindTable(obj *LTable, n string, size int) LValue {
names := strings.Split(n, ".")
curobj := obj
for _, name := range names {
if curobj.Type() != LTTable {
return LNil
}
nextobj := ls.RawGet(curobj, LString(name))
if nextobj == LNil {
tb := ls.CreateTable(0, size)
ls.RawSet(curobj, LString(name), tb)
curobj = tb
} else if nextobj.Type() != LTTable {
return LNil
} else {
curobj = nextobj.(*LTable)
}
}
return curobj
}
/* }}} */
/* register operations {{{ */
func (ls *LState) RegisterModule(name string, funcs map[string]LGFunction) LValue {
tb := ls.FindTable(ls.Get(RegistryIndex).(*LTable), "_LOADED", 1)
mod := ls.GetField(tb, name)
if mod.Type() != LTTable {
newmod := ls.FindTable(ls.Get(GlobalsIndex).(*LTable), name, len(funcs))
if newmodtb, ok := newmod.(*LTable); !ok {
ls.RaiseError("name conflict for module(%v)", name)
} else {
for fname, fn := range funcs {
newmodtb.RawSetString(fname, ls.NewFunction(fn))
}
ls.SetField(tb, name, newmodtb)
return newmodtb
}
}
return mod
}
func (ls *LState) SetFuncs(tb *LTable, funcs map[string]LGFunction, upvalues ...LValue) *LTable {
for fname, fn := range funcs {
tb.RawSetString(fname, ls.NewClosure(fn, upvalues...))
}
return tb
}
/* }}} */
/* metatable operations {{{ */
func (ls *LState) NewTypeMetatable(typ string) *LTable {
regtable := ls.Get(RegistryIndex)
mt := ls.GetField(regtable, typ)
if tb, ok := mt.(*LTable); ok {
return tb
}
mtnew := ls.NewTable()
ls.SetField(regtable, typ, mtnew)
return mtnew
}
func (ls *LState) GetMetaField(obj LValue, event string) LValue {
return ls.metaOp1(obj, event)
}
func (ls *LState) GetTypeMetatable(typ string) LValue {
return ls.GetField(ls.Get(RegistryIndex), typ)
}
func (ls *LState) CallMeta(obj LValue, event string) LValue {
op := ls.metaOp1(obj, event)
if op.Type() == LTFunction {
ls.reg.Push(op)
ls.reg.Push(obj)
ls.Call(1, 1)
return ls.reg.Pop()
}
return LNil
}
/* }}} */
/* load and function call operations {{{ */
func (ls *LState) LoadFile(path string) (*LFunction, error) {
var file *os.File
var err error
if len(path) == 0 {
file = os.Stdin
} else {
file, err = os.Open(path)
defer file.Close()
if err != nil {
return nil, newApiErrorE(ApiErrorFile, err)
}
}
reader := bufio.NewReader(file)
// get the first character.
c, err := reader.ReadByte()
if err != nil && err != io.EOF {
return nil, newApiErrorE(ApiErrorFile, err)
}
if c == byte('#') {
// Unix exec. file?
// skip first line
_, err, _ = readBufioLine(reader)
if err != nil {
return nil, newApiErrorE(ApiErrorFile, err)
}
}
if err != io.EOF {
// if the file is not empty,
// unread the first character of the file or newline character(readBufioLine's last byte).
err = reader.UnreadByte()
if err != nil {
return nil, newApiErrorE(ApiErrorFile, err)
}
}
return ls.Load(reader, path)
}
func (ls *LState) LoadString(source string) (*LFunction, error) {
return ls.Load(strings.NewReader(source), "<string>")
}
func (ls *LState) DoFile(path string) error {
if fn, err := ls.LoadFile(path); err != nil {
return err
} else {
ls.Push(fn)
return ls.PCall(0, MultRet, nil)
}
}
func (ls *LState) DoString(source string) error {
if fn, err := ls.LoadString(source); err != nil {
return err
} else {
ls.Push(fn)
return ls.PCall(0, MultRet, nil)
}
}
/* }}} */
/* GopherLua original APIs {{{ */
// ToStringMeta returns string representation of given LValue.
// This method calls the `__tostring` meta method if defined.
func (ls *LState) ToStringMeta(lv LValue) LValue {
if fn, ok := ls.metaOp1(lv, "__tostring").assertFunction(); ok {
ls.Push(fn)
ls.Push(lv)
ls.Call(1, 1)
return ls.reg.Pop()
} else {
return LString(lv.String())
}
}
// Set a module loader to the package.preload table.
func (ls *LState) PreloadModule(name string, loader LGFunction) {
preload := ls.GetField(ls.GetField(ls.Get(EnvironIndex), "package"), "preload")
if _, ok := preload.(*LTable); !ok {
ls.RaiseError("package.preload must be a table")
}
ls.SetField(preload, name, ls.NewFunction(loader))
}
// Checks whether the given index is an LChannel and returns this channel.
func (ls *LState) CheckChannel(n int) chan LValue {
v := ls.Get(n)
if ch, ok := v.(LChannel); ok {
return (chan LValue)(ch)
}
ls.TypeError(n, LTChannel)
return nil
}
// If the given index is a LChannel, returns this channel. If this argument is absent or is nil, returns ch. Otherwise, raises an error.
func (ls *LState) OptChannel(n int, ch chan LValue) chan LValue {
v := ls.Get(n)
if v == LNil {
return ch
}
if ch, ok := v.(LChannel); ok {
return (chan LValue)(ch)
}
ls.TypeError(n, LTChannel)
return nil
}
/* }}} */
//

597
vendor/github.com/yuin/gopher-lua/baselib.go generated vendored Normal file
View File

@ -0,0 +1,597 @@
package lua
import (
"fmt"
"io"
"os"
"runtime"
"strconv"
"strings"
)
/* basic functions {{{ */
func OpenBase(L *LState) int {
global := L.Get(GlobalsIndex).(*LTable)
L.SetGlobal("_G", global)
L.SetGlobal("_VERSION", LString(LuaVersion))
L.SetGlobal("_GOPHER_LUA_VERSION", LString(PackageName+" "+PackageVersion))
basemod := L.RegisterModule("_G", baseFuncs)
global.RawSetString("ipairs", L.NewClosure(baseIpairs, L.NewFunction(ipairsaux)))
global.RawSetString("pairs", L.NewClosure(basePairs, L.NewFunction(pairsaux)))
L.Push(basemod)
return 1
}
var baseFuncs = map[string]LGFunction{
"assert": baseAssert,
"collectgarbage": baseCollectGarbage,
"dofile": baseDoFile,
"error": baseError,
"getfenv": baseGetFEnv,
"getmetatable": baseGetMetatable,
"load": baseLoad,
"loadfile": baseLoadFile,
"loadstring": baseLoadString,
"next": baseNext,
"pcall": basePCall,
"print": basePrint,
"rawequal": baseRawEqual,
"rawget": baseRawGet,
"rawset": baseRawSet,
"select": baseSelect,
"_printregs": base_PrintRegs,
"setfenv": baseSetFEnv,
"setmetatable": baseSetMetatable,
"tonumber": baseToNumber,
"tostring": baseToString,
"type": baseType,
"unpack": baseUnpack,
"xpcall": baseXPCall,
// loadlib
"module": loModule,
"require": loRequire,
// hidden features
"newproxy": baseNewProxy,
}
func baseAssert(L *LState) int {
if !L.ToBool(1) {
L.RaiseError(L.OptString(2, "assertion failed!"))
return 0
}
return L.GetTop()
}
func baseCollectGarbage(L *LState) int {
runtime.GC()
return 0
}
func baseDoFile(L *LState) int {
src := L.ToString(1)
top := L.GetTop()
fn, err := L.LoadFile(src)
if err != nil {
L.Push(LString(err.Error()))
L.Panic(L)
}
L.Push(fn)
L.Call(0, MultRet)
return L.GetTop() - top
}
func baseError(L *LState) int {
obj := L.CheckAny(1)
level := L.OptInt(2, 1)
L.Error(obj, level)
return 0
}
func baseGetFEnv(L *LState) int {
var value LValue
if L.GetTop() == 0 {
value = LNumber(1)
} else {
value = L.Get(1)
}
if fn, ok := value.(*LFunction); ok {
if !fn.IsG {
L.Push(fn.Env)
} else {
L.Push(L.G.Global)
}
return 1
}
if number, ok := value.(LNumber); ok {
level := int(float64(number))
if level <= 0 {
L.Push(L.Env)
} else {
cf := L.currentFrame
for i := 0; i < level && cf != nil; i++ {
cf = cf.Parent
}
if cf == nil || cf.Fn.IsG {
L.Push(L.G.Global)
} else {
L.Push(cf.Fn.Env)
}
}
return 1
}
L.Push(L.G.Global)
return 1
}
func baseGetMetatable(L *LState) int {
L.Push(L.GetMetatable(L.CheckAny(1)))
return 1
}
func ipairsaux(L *LState) int {
tb := L.CheckTable(1)
i := L.CheckInt(2)
i++
v := tb.RawGetInt(i)
if v == LNil {
return 0
} else {
L.Pop(1)
L.Push(LNumber(i))
L.Push(LNumber(i))
L.Push(v)
return 2
}
}
func baseIpairs(L *LState) int {
tb := L.CheckTable(1)
L.Push(L.Get(UpvalueIndex(1)))
L.Push(tb)
L.Push(LNumber(0))
return 3
}
func loadaux(L *LState, reader io.Reader, chunkname string) int {
if fn, err := L.Load(reader, chunkname); err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
} else {
L.Push(fn)
return 1
}
}
func baseLoad(L *LState) int {
fn := L.CheckFunction(1)
chunkname := L.OptString(2, "?")
top := L.GetTop()
buf := []string{}
for {
L.SetTop(top)
L.Push(fn)
L.Call(0, 1)
ret := L.reg.Pop()
if ret == LNil {
break
} else if LVCanConvToString(ret) {
str := ret.String()
if len(str) > 0 {
buf = append(buf, string(str))
} else {
break
}
} else {
L.Push(LNil)
L.Push(LString("reader function must return a string"))
return 2
}
}
return loadaux(L, strings.NewReader(strings.Join(buf, "")), chunkname)
}
func baseLoadFile(L *LState) int {
var reader io.Reader
var chunkname string
var err error
if L.GetTop() < 1 {
reader = os.Stdin
chunkname = "<stdin>"
} else {
chunkname = L.CheckString(1)
reader, err = os.Open(chunkname)
if err != nil {
L.Push(LNil)
L.Push(LString(fmt.Sprintf("can not open file: %v", chunkname)))
return 2
}
defer reader.(*os.File).Close()
}
return loadaux(L, reader, chunkname)
}
func baseLoadString(L *LState) int {
return loadaux(L, strings.NewReader(L.CheckString(1)), L.OptString(2, "<string>"))
}
func baseNext(L *LState) int {
tb := L.CheckTable(1)
index := LNil
if L.GetTop() >= 2 {
index = L.Get(2)
}
key, value := tb.Next(index)
if key == LNil {
L.Push(LNil)
return 1
}
L.Push(key)
L.Push(value)
return 2
}
func pairsaux(L *LState) int {
tb := L.CheckTable(1)
key, value := tb.Next(L.Get(2))
if key == LNil {
return 0
} else {
L.Pop(1)
L.Push(key)
L.Push(key)
L.Push(value)
return 2
}
}
func basePairs(L *LState) int {
tb := L.CheckTable(1)
L.Push(L.Get(UpvalueIndex(1)))
L.Push(tb)
L.Push(LNil)
return 3
}
func basePCall(L *LState) int {
L.CheckAny(1)
v := L.Get(1)
if v.Type() != LTFunction && L.GetMetaField(v, "__call").Type() != LTFunction {
L.Push(LFalse)
L.Push(LString("attempt to call a " + v.Type().String() + " value"))
return 2
}
nargs := L.GetTop() - 1
if err := L.PCall(nargs, MultRet, nil); err != nil {
L.Push(LFalse)
if aerr, ok := err.(*ApiError); ok {
L.Push(aerr.Object)
} else {
L.Push(LString(err.Error()))
}
return 2
} else {
L.Insert(LTrue, 1)
return L.GetTop()
}
}
func basePrint(L *LState) int {
top := L.GetTop()
for i := 1; i <= top; i++ {
fmt.Print(L.ToStringMeta(L.Get(i)).String())
if i != top {
fmt.Print("\t")
}
}
fmt.Println("")
return 0
}
func base_PrintRegs(L *LState) int {
L.printReg()
return 0
}
func baseRawEqual(L *LState) int {
if L.CheckAny(1) == L.CheckAny(2) {
L.Push(LTrue)
} else {
L.Push(LFalse)
}
return 1
}
func baseRawGet(L *LState) int {
L.Push(L.RawGet(L.CheckTable(1), L.CheckAny(2)))
return 1
}
func baseRawSet(L *LState) int {
L.RawSet(L.CheckTable(1), L.CheckAny(2), L.CheckAny(3))
return 0
}
func baseSelect(L *LState) int {
L.CheckTypes(1, LTNumber, LTString)
switch lv := L.Get(1).(type) {
case LNumber:
idx := int(lv)
num := L.GetTop()
if idx < 0 {
idx = num + idx
} else if idx > num {
idx = num
}
if 1 > idx {
L.ArgError(1, "index out of range")
}
return num - idx
case LString:
if string(lv) != "#" {
L.ArgError(1, "invalid string '"+string(lv)+"'")
}
L.Push(LNumber(L.GetTop() - 1))
return 1
}
return 0
}
func baseSetFEnv(L *LState) int {
var value LValue
if L.GetTop() == 0 {
value = LNumber(1)
} else {
value = L.Get(1)
}
env := L.CheckTable(2)
if fn, ok := value.(*LFunction); ok {
if fn.IsG {
L.RaiseError("cannot change the environment of given object")
} else {
fn.Env = env
L.Push(fn)
return 1
}
}
if number, ok := value.(LNumber); ok {
level := int(float64(number))
if level <= 0 {
L.Env = env
return 0
}
cf := L.currentFrame
for i := 0; i < level && cf != nil; i++ {
cf = cf.Parent
}
if cf == nil || cf.Fn.IsG {
L.RaiseError("cannot change the environment of given object")
} else {
cf.Fn.Env = env
L.Push(cf.Fn)
return 1
}
}
L.RaiseError("cannot change the environment of given object")
return 0
}
func baseSetMetatable(L *LState) int {
L.CheckTypes(2, LTNil, LTTable)
obj := L.Get(1)
if obj == LNil {
L.RaiseError("cannot set metatable to a nil object.")
}
mt := L.Get(2)
if m := L.metatable(obj, true); m != LNil {
if tb, ok := m.(*LTable); ok && tb.RawGetString("__metatable") != LNil {
L.RaiseError("cannot change a protected metatable")
}
}
L.SetMetatable(obj, mt)
L.SetTop(1)
return 1
}
func baseToNumber(L *LState) int {
base := L.OptInt(2, 10)
noBase := L.Get(2) == LNil
switch lv := L.CheckAny(1).(type) {
case LNumber:
L.Push(lv)
case LString:
str := strings.Trim(string(lv), " \n\t")
if strings.Index(str, ".") > -1 {
if v, err := strconv.ParseFloat(str, LNumberBit); err != nil {
L.Push(LNil)
} else {
L.Push(LNumber(v))
}
} else {
if noBase && strings.HasPrefix(strings.ToLower(str), "0x") {
base, str = 16, str[2:] // Hex number
}
if v, err := strconv.ParseInt(str, base, LNumberBit); err != nil {
L.Push(LNil)
} else {
L.Push(LNumber(v))
}
}
default:
L.Push(LNil)
}
return 1
}
func baseToString(L *LState) int {
v1 := L.CheckAny(1)
L.Push(L.ToStringMeta(v1))
return 1
}
func baseType(L *LState) int {
L.Push(LString(L.CheckAny(1).Type().String()))
return 1
}
func baseUnpack(L *LState) int {
tb := L.CheckTable(1)
start := L.OptInt(2, 1)
end := L.OptInt(3, tb.Len())
for i := start; i <= end; i++ {
L.Push(tb.RawGetInt(i))
}
ret := end - start + 1
if ret < 0 {
return 0
}
return ret
}
func baseXPCall(L *LState) int {
fn := L.CheckFunction(1)
errfunc := L.CheckFunction(2)
top := L.GetTop()
L.Push(fn)
if err := L.PCall(0, MultRet, errfunc); err != nil {
L.Push(LFalse)
if aerr, ok := err.(*ApiError); ok {
L.Push(aerr.Object)
} else {
L.Push(LString(err.Error()))
}
return 2
} else {
L.Insert(LTrue, top+1)
return L.GetTop() - top
}
}
/* }}} */
/* load lib {{{ */
func loModule(L *LState) int {
name := L.CheckString(1)
loaded := L.GetField(L.Get(RegistryIndex), "_LOADED")
tb := L.GetField(loaded, name)
if _, ok := tb.(*LTable); !ok {
tb = L.FindTable(L.Get(GlobalsIndex).(*LTable), name, 1)
if tb == LNil {
L.RaiseError("name conflict for module: %v", name)
}
L.SetField(loaded, name, tb)
}
if L.GetField(tb, "_NAME") == LNil {
L.SetField(tb, "_M", tb)
L.SetField(tb, "_NAME", LString(name))
names := strings.Split(name, ".")
pname := ""
if len(names) > 1 {
pname = strings.Join(names[:len(names)-1], ".") + "."
}
L.SetField(tb, "_PACKAGE", LString(pname))
}
caller := L.currentFrame.Parent
if caller == nil {
L.RaiseError("no calling stack.")
} else if caller.Fn.IsG {
L.RaiseError("module() can not be called from GFunctions.")
}
L.SetFEnv(caller.Fn, tb)
top := L.GetTop()
for i := 2; i <= top; i++ {
L.Push(L.Get(i))
L.Push(tb)
L.Call(1, 0)
}
L.Push(tb)
return 1
}
var loopdetection = &LUserData{}
func loRequire(L *LState) int {
name := L.CheckString(1)
loaded := L.GetField(L.Get(RegistryIndex), "_LOADED")
lv := L.GetField(loaded, name)
if LVAsBool(lv) {
if lv == loopdetection {
L.RaiseError("loop or previous error loading module: %s", name)
}
L.Push(lv)
return 1
}
loaders, ok := L.GetField(L.Get(RegistryIndex), "_LOADERS").(*LTable)
if !ok {
L.RaiseError("package.loaders must be a table")
}
messages := []string{}
var modasfunc LValue
for i := 1; ; i++ {
loader := L.RawGetInt(loaders, i)
if loader == LNil {
L.RaiseError("module %s not found:\n\t%s, ", name, strings.Join(messages, "\n\t"))
}
L.Push(loader)
L.Push(LString(name))
L.Call(1, 1)
ret := L.reg.Pop()
switch retv := ret.(type) {
case *LFunction:
modasfunc = retv
goto loopbreak
case LString:
messages = append(messages, string(retv))
}
}
loopbreak:
L.SetField(loaded, name, loopdetection)
L.Push(modasfunc)
L.Push(LString(name))
L.Call(1, 1)
ret := L.reg.Pop()
modv := L.GetField(loaded, name)
if ret != LNil && modv == loopdetection {
L.SetField(loaded, name, ret)
L.Push(ret)
} else if modv == loopdetection {
L.SetField(loaded, name, LTrue)
L.Push(LTrue)
} else {
L.Push(modv)
}
return 1
}
/* }}} */
/* hidden features {{{ */
func baseNewProxy(L *LState) int {
ud := L.NewUserData()
L.SetTop(1)
if L.Get(1) == LTrue {
L.SetMetatable(ud, L.NewTable())
} else if d, ok := L.Get(1).(*LUserData); ok {
L.SetMetatable(ud, L.GetMetatable(d))
}
L.Push(ud)
return 1
}
/* }}} */
//

184
vendor/github.com/yuin/gopher-lua/channellib.go generated vendored Normal file
View File

@ -0,0 +1,184 @@
package lua
import (
"reflect"
)
func checkChannel(L *LState, idx int) reflect.Value {
ch := L.CheckChannel(idx)
return reflect.ValueOf(ch)
}
func checkGoroutineSafe(L *LState, idx int) LValue {
v := L.CheckAny(2)
if !isGoroutineSafe(v) {
L.ArgError(2, "can not send a function, userdata, thread or table that has a metatable")
}
return v
}
func OpenChannel(L *LState) int {
var mod LValue
//_, ok := L.G.builtinMts[int(LTChannel)]
// if !ok {
mod = L.RegisterModule(ChannelLibName, channelFuncs)
mt := L.SetFuncs(L.NewTable(), channelMethods)
mt.RawSetString("__index", mt)
L.G.builtinMts[int(LTChannel)] = mt
// }
L.Push(mod)
return 1
}
var channelFuncs = map[string]LGFunction{
"make": channelMake,
"select": channelSelect,
}
func channelMake(L *LState) int {
buffer := L.OptInt(1, 0)
L.Push(LChannel(make(chan LValue, buffer)))
return 1
}
func channelSelect(L *LState) int {
//TODO check case table size
cases := make([]reflect.SelectCase, L.GetTop())
top := L.GetTop()
for i := 0; i < top; i++ {
cas := reflect.SelectCase{
Dir: reflect.SelectSend,
Chan: reflect.ValueOf(nil),
Send: reflect.ValueOf(nil),
}
tbl := L.CheckTable(i + 1)
dir, ok1 := tbl.RawGetInt(1).(LString)
if !ok1 {
L.ArgError(i+1, "invalid select case")
}
switch string(dir) {
case "<-|":
ch, ok := tbl.RawGetInt(2).(LChannel)
if !ok {
L.ArgError(i+1, "invalid select case")
}
cas.Chan = reflect.ValueOf((chan LValue)(ch))
v := tbl.RawGetInt(3)
if !isGoroutineSafe(v) {
L.ArgError(i+1, "can not send a function, userdata, thread or table that has a metatable")
}
cas.Send = reflect.ValueOf(v)
case "|<-":
ch, ok := tbl.RawGetInt(2).(LChannel)
if !ok {
L.ArgError(i+1, "invalid select case")
}
cas.Chan = reflect.ValueOf((chan LValue)(ch))
cas.Dir = reflect.SelectRecv
case "default":
cas.Dir = reflect.SelectDefault
default:
L.ArgError(i+1, "invalid channel direction:"+string(dir))
}
cases[i] = cas
}
if L.ctx != nil {
cases = append(cases, reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(L.ctx.Done()),
Send: reflect.ValueOf(nil),
})
}
pos, recv, rok := reflect.Select(cases)
if L.ctx != nil && pos == L.GetTop() {
return 0
}
lv := LNil
if recv.Kind() != 0 {
lv, _ = recv.Interface().(LValue)
if lv == nil {
lv = LNil
}
}
tbl := L.Get(pos + 1).(*LTable)
last := tbl.RawGetInt(tbl.Len())
if last.Type() == LTFunction {
L.Push(last)
switch cases[pos].Dir {
case reflect.SelectRecv:
if rok {
L.Push(LTrue)
} else {
L.Push(LFalse)
}
L.Push(lv)
L.Call(2, 0)
case reflect.SelectSend:
L.Push(tbl.RawGetInt(3))
L.Call(1, 0)
case reflect.SelectDefault:
L.Call(0, 0)
}
}
L.Push(LNumber(pos + 1))
L.Push(lv)
if rok {
L.Push(LTrue)
} else {
L.Push(LFalse)
}
return 3
}
var channelMethods = map[string]LGFunction{
"receive": channelReceive,
"send": channelSend,
"close": channelClose,
}
func channelReceive(L *LState) int {
rch := checkChannel(L, 1)
var v reflect.Value
var ok bool
if L.ctx != nil {
cases := []reflect.SelectCase{{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(L.ctx.Done()),
Send: reflect.ValueOf(nil),
}, {
Dir: reflect.SelectRecv,
Chan: rch,
Send: reflect.ValueOf(nil),
}}
_, v, ok = reflect.Select(cases)
} else {
v, ok = rch.Recv()
}
if ok {
L.Push(LTrue)
L.Push(v.Interface().(LValue))
} else {
L.Push(LFalse)
L.Push(LNil)
}
return 2
}
func channelSend(L *LState) int {
rch := checkChannel(L, 1)
v := checkGoroutineSafe(L, 2)
rch.Send(reflect.ValueOf(v))
return 0
}
func channelClose(L *LState) int {
rch := checkChannel(L, 1)
rch.Close()
return 0
}
//

1676
vendor/github.com/yuin/gopher-lua/compile.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

43
vendor/github.com/yuin/gopher-lua/config.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
package lua
import (
"os"
)
var CompatVarArg = true
var FieldsPerFlush = 50
var RegistrySize = 256 * 20
var RegistryGrowStep = 32
var CallStackSize = 256
var MaxTableGetLoop = 100
var MaxArrayIndex = 67108864
type LNumber float64
const LNumberBit = 64
const LNumberScanFormat = "%f"
const LuaVersion = "Lua 5.1"
var LuaPath = "LUA_PATH"
var LuaLDir string
var LuaPathDefault string
var LuaOS string
var LuaDirSep string
var LuaPathSep = ";"
var LuaPathMark = "?"
var LuaExecDir = "!"
var LuaIgMark = "-"
func init() {
if os.PathSeparator == '/' { // unix-like
LuaOS = "unix"
LuaLDir = "/usr/local/share/lua/5.1"
LuaDirSep = "/"
LuaPathDefault = "./?.lua;" + LuaLDir + "/?.lua;" + LuaLDir + "/?/init.lua"
} else { // windows
LuaOS = "windows"
LuaLDir = "!\\lua"
LuaDirSep = "\\"
LuaPathDefault = ".\\?.lua;" + LuaLDir + "\\?.lua;" + LuaLDir + "\\?\\init.lua"
}
}

112
vendor/github.com/yuin/gopher-lua/coroutinelib.go generated vendored Normal file
View File

@ -0,0 +1,112 @@
package lua
func OpenCoroutine(L *LState) int {
// TODO: Tie module name to contents of linit.go?
mod := L.RegisterModule(CoroutineLibName, coFuncs)
L.Push(mod)
return 1
}
var coFuncs = map[string]LGFunction{
"create": coCreate,
"yield": coYield,
"resume": coResume,
"running": coRunning,
"status": coStatus,
"wrap": coWrap,
}
func coCreate(L *LState) int {
fn := L.CheckFunction(1)
newthread, _ := L.NewThread()
base := 0
newthread.stack.Push(callFrame{
Fn: fn,
Pc: 0,
Base: base,
LocalBase: base + 1,
ReturnBase: base,
NArgs: 0,
NRet: MultRet,
Parent: nil,
TailCall: 0,
})
L.Push(newthread)
return 1
}
func coYield(L *LState) int {
return -1
}
func coResume(L *LState) int {
th := L.CheckThread(1)
if L.G.CurrentThread == th {
msg := "can not resume a running thread"
if th.wrapped {
L.RaiseError(msg)
return 0
}
L.Push(LFalse)
L.Push(LString(msg))
return 2
}
if th.Dead {
msg := "can not resume a dead thread"
if th.wrapped {
L.RaiseError(msg)
return 0
}
L.Push(LFalse)
L.Push(LString(msg))
return 2
}
th.Parent = L
L.G.CurrentThread = th
if !th.isStarted() {
cf := th.stack.Last()
th.currentFrame = cf
th.SetTop(0)
nargs := L.GetTop() - 1
L.XMoveTo(th, nargs)
cf.NArgs = nargs
th.initCallFrame(cf)
th.Panic = panicWithoutTraceback
} else {
nargs := L.GetTop() - 1
L.XMoveTo(th, nargs)
}
top := L.GetTop()
threadRun(th)
return L.GetTop() - top
}
func coRunning(L *LState) int {
if L.G.MainThread == L {
L.Push(LNil)
return 1
}
L.Push(L.G.CurrentThread)
return 1
}
func coStatus(L *LState) int {
L.Push(LString(L.Status(L.CheckThread(1))))
return 1
}
func wrapaux(L *LState) int {
L.Insert(L.ToThread(UpvalueIndex(1)), 1)
return coResume(L)
}
func coWrap(L *LState) int {
coCreate(L)
L.CheckThread(L.GetTop()).wrapped = true
v := L.Get(L.GetTop())
L.Pop(1)
L.Push(L.NewClosure(wrapaux, v))
return 1
}
//

173
vendor/github.com/yuin/gopher-lua/debuglib.go generated vendored Normal file
View File

@ -0,0 +1,173 @@
package lua
import (
"fmt"
"strings"
)
func OpenDebug(L *LState) int {
dbgmod := L.RegisterModule(DebugLibName, debugFuncs)
L.Push(dbgmod)
return 1
}
var debugFuncs = map[string]LGFunction{
"getfenv": debugGetFEnv,
"getinfo": debugGetInfo,
"getlocal": debugGetLocal,
"getmetatable": debugGetMetatable,
"getupvalue": debugGetUpvalue,
"setfenv": debugSetFEnv,
"setlocal": debugSetLocal,
"setmetatable": debugSetMetatable,
"setupvalue": debugSetUpvalue,
"traceback": debugTraceback,
}
func debugGetFEnv(L *LState) int {
L.Push(L.GetFEnv(L.CheckAny(1)))
return 1
}
func debugGetInfo(L *LState) int {
L.CheckTypes(1, LTFunction, LTNumber)
arg1 := L.Get(1)
what := L.OptString(2, "Slunf")
var dbg *Debug
var fn LValue
var err error
var ok bool
switch lv := arg1.(type) {
case *LFunction:
dbg = &Debug{}
fn, err = L.GetInfo(">"+what, dbg, lv)
case LNumber:
dbg, ok = L.GetStack(int(lv))
if !ok {
L.Push(LNil)
return 1
}
fn, err = L.GetInfo(what, dbg, LNil)
}
if err != nil {
L.Push(LNil)
return 1
}
tbl := L.NewTable()
if len(dbg.Name) > 0 {
tbl.RawSetString("name", LString(dbg.Name))
} else {
tbl.RawSetString("name", LNil)
}
tbl.RawSetString("what", LString(dbg.What))
tbl.RawSetString("source", LString(dbg.Source))
tbl.RawSetString("currentline", LNumber(dbg.CurrentLine))
tbl.RawSetString("nups", LNumber(dbg.NUpvalues))
tbl.RawSetString("linedefined", LNumber(dbg.LineDefined))
tbl.RawSetString("lastlinedefined", LNumber(dbg.LastLineDefined))
tbl.RawSetString("func", fn)
L.Push(tbl)
return 1
}
func debugGetLocal(L *LState) int {
level := L.CheckInt(1)
idx := L.CheckInt(2)
dbg, ok := L.GetStack(level)
if !ok {
L.ArgError(1, "level out of range")
}
name, value := L.GetLocal(dbg, idx)
if len(name) > 0 {
L.Push(LString(name))
L.Push(value)
return 2
}
L.Push(LNil)
return 1
}
func debugGetMetatable(L *LState) int {
L.Push(L.GetMetatable(L.CheckAny(1)))
return 1
}
func debugGetUpvalue(L *LState) int {
fn := L.CheckFunction(1)
idx := L.CheckInt(2)
name, value := L.GetUpvalue(fn, idx)
if len(name) > 0 {
L.Push(LString(name))
L.Push(value)
return 2
}
L.Push(LNil)
return 1
}
func debugSetFEnv(L *LState) int {
L.SetFEnv(L.CheckAny(1), L.CheckAny(2))
return 0
}
func debugSetLocal(L *LState) int {
level := L.CheckInt(1)
idx := L.CheckInt(2)
value := L.CheckAny(3)
dbg, ok := L.GetStack(level)
if !ok {
L.ArgError(1, "level out of range")
}
name := L.SetLocal(dbg, idx, value)
if len(name) > 0 {
L.Push(LString(name))
} else {
L.Push(LNil)
}
return 1
}
func debugSetMetatable(L *LState) int {
L.CheckTypes(2, LTNil, LTTable)
obj := L.Get(1)
mt := L.Get(2)
L.SetMetatable(obj, mt)
L.SetTop(1)
return 1
}
func debugSetUpvalue(L *LState) int {
fn := L.CheckFunction(1)
idx := L.CheckInt(2)
value := L.CheckAny(3)
name := L.SetUpvalue(fn, idx, value)
if len(name) > 0 {
L.Push(LString(name))
} else {
L.Push(LNil)
}
return 1
}
func debugTraceback(L *LState) int {
msg := ""
level := L.OptInt(2, 1)
ls := L
if L.GetTop() > 0 {
if s, ok := L.Get(1).assertString(); ok {
msg = s
}
if l, ok := L.Get(1).(*LState); ok {
ls = l
msg = ""
}
}
traceback := strings.TrimSpace(ls.stackTrace(level))
if len(msg) > 0 {
traceback = fmt.Sprintf("%s\n%s", msg, traceback)
}
L.Push(LString(traceback))
return 1
}

193
vendor/github.com/yuin/gopher-lua/function.go generated vendored Normal file
View File

@ -0,0 +1,193 @@
package lua
import (
"fmt"
"strings"
)
const (
VarArgHasArg uint8 = 1
VarArgIsVarArg uint8 = 2
VarArgNeedsArg uint8 = 4
)
type DbgLocalInfo struct {
Name string
StartPc int
EndPc int
}
type DbgCall struct {
Name string
Pc int
}
type FunctionProto struct {
SourceName string
LineDefined int
LastLineDefined int
NumUpvalues uint8
NumParameters uint8
IsVarArg uint8
NumUsedRegisters uint8
Code []uint32
Constants []LValue
FunctionPrototypes []*FunctionProto
DbgSourcePositions []int
DbgLocals []*DbgLocalInfo
DbgCalls []DbgCall
DbgUpvalues []string
stringConstants []string
}
/* Upvalue {{{ */
type Upvalue struct {
next *Upvalue
reg *registry
index int
value LValue
closed bool
}
func (uv *Upvalue) Value() LValue {
//if uv.IsClosed() {
if uv.closed || uv.reg == nil {
return uv.value
}
//return uv.reg.Get(uv.index)
return uv.reg.array[uv.index]
}
func (uv *Upvalue) SetValue(value LValue) {
if uv.IsClosed() {
uv.value = value
} else {
uv.reg.Set(uv.index, value)
}
}
func (uv *Upvalue) Close() {
value := uv.Value()
uv.closed = true
uv.value = value
}
func (uv *Upvalue) IsClosed() bool {
return uv.closed || uv.reg == nil
}
func UpvalueIndex(i int) int {
return GlobalsIndex - i
}
/* }}} */
/* FunctionProto {{{ */
func newFunctionProto(name string) *FunctionProto {
return &FunctionProto{
SourceName: name,
LineDefined: 0,
LastLineDefined: 0,
NumUpvalues: 0,
NumParameters: 0,
IsVarArg: 0,
NumUsedRegisters: 2,
Code: make([]uint32, 0, 128),
Constants: make([]LValue, 0, 32),
FunctionPrototypes: make([]*FunctionProto, 0, 16),
DbgSourcePositions: make([]int, 0, 128),
DbgLocals: make([]*DbgLocalInfo, 0, 16),
DbgCalls: make([]DbgCall, 0, 128),
DbgUpvalues: make([]string, 0, 16),
stringConstants: make([]string, 0, 32),
}
}
func (fp *FunctionProto) String() string {
return fp.str(1, 0)
}
func (fp *FunctionProto) str(level int, count int) string {
indent := strings.Repeat(" ", level-1)
buf := []string{}
buf = append(buf, fmt.Sprintf("%v; function [%v] definition (level %v)\n",
indent, count, level))
buf = append(buf, fmt.Sprintf("%v; %v upvalues, %v params, %v stacks\n",
indent, fp.NumUpvalues, fp.NumParameters, fp.NumUsedRegisters))
for reg, linfo := range fp.DbgLocals {
buf = append(buf, fmt.Sprintf("%v.local %v ; %v\n", indent, linfo.Name, reg))
}
for reg, upvalue := range fp.DbgUpvalues {
buf = append(buf, fmt.Sprintf("%v.upvalue %v ; %v\n", indent, upvalue, reg))
}
for reg, conzt := range fp.Constants {
buf = append(buf, fmt.Sprintf("%v.const %v ; %v\n", indent, conzt.String(), reg))
}
buf = append(buf, "\n")
protono := 0
for no, code := range fp.Code {
inst := opGetOpCode(code)
if inst == OP_CLOSURE {
buf = append(buf, "\n")
buf = append(buf, fp.FunctionPrototypes[protono].str(level+1, protono))
buf = append(buf, "\n")
protono++
}
buf = append(buf, fmt.Sprintf("%v[%03d] %v (line:%v)\n",
indent, no+1, opToString(code), fp.DbgSourcePositions[no]))
}
buf = append(buf, fmt.Sprintf("%v; end of function\n", indent))
return strings.Join(buf, "")
}
/* }}} */
/* LFunction {{{ */
func newLFunctionL(proto *FunctionProto, env *LTable, nupvalue int) *LFunction {
return &LFunction{
IsG: false,
Env: env,
Proto: proto,
GFunction: nil,
Upvalues: make([]*Upvalue, nupvalue),
}
}
func newLFunctionG(gfunc LGFunction, env *LTable, nupvalue int) *LFunction {
return &LFunction{
IsG: true,
Env: env,
Proto: nil,
GFunction: gfunc,
Upvalues: make([]*Upvalue, nupvalue),
}
}
func (fn *LFunction) LocalName(regno, pc int) (string, bool) {
if fn.IsG {
return "", false
}
p := fn.Proto
for i := 0; i < len(p.DbgLocals) && p.DbgLocals[i].StartPc < pc; i++ {
if pc < p.DbgLocals[i].EndPc {
regno--
if regno == 0 {
return p.DbgLocals[i].Name, true
}
}
}
return "", false
}
/* }}} */

746
vendor/github.com/yuin/gopher-lua/iolib.go generated vendored Normal file
View File

@ -0,0 +1,746 @@
package lua
import (
"bufio"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"syscall"
)
var ioFuncs = map[string]LGFunction{
"close": ioClose,
"flush": ioFlush,
"lines": ioLines,
"input": ioInput,
"output": ioOutput,
"open": ioOpenFile,
"popen": ioPopen,
"read": ioRead,
"type": ioType,
"tmpfile": ioTmpFile,
"write": ioWrite,
}
const lFileClass = "FILE*"
type lFile struct {
fp *os.File
pp *exec.Cmd
writer io.Writer
reader *bufio.Reader
stdout io.ReadCloser
closed bool
}
type lFileType int
const (
lFileFile lFileType = iota
lFileProcess
)
const fileDefOutIndex = 1
const fileDefInIndex = 2
const fileDefaultWriteBuffer = 4096
const fileDefaultReadBuffer = 4096
func checkFile(L *LState) *lFile {
ud := L.CheckUserData(1)
if file, ok := ud.Value.(*lFile); ok {
return file
}
L.ArgError(1, "file expected")
return nil
}
func errorIfFileIsClosed(L *LState, file *lFile) {
if file.closed {
L.ArgError(1, "file is closed")
}
}
func newFile(L *LState, file *os.File, path string, flag int, perm os.FileMode, writable, readable bool) (*LUserData, error) {
ud := L.NewUserData()
var err error
if file == nil {
file, err = os.OpenFile(path, flag, perm)
if err != nil {
return nil, err
}
}
lfile := &lFile{fp: file, pp: nil, writer: nil, reader: nil, stdout: nil, closed: false}
ud.Value = lfile
if writable {
lfile.writer = file
}
if readable {
lfile.reader = bufio.NewReaderSize(file, fileDefaultReadBuffer)
}
L.SetMetatable(ud, L.GetTypeMetatable(lFileClass))
return ud, nil
}
func newProcess(L *LState, cmd string, writable, readable bool) (*LUserData, error) {
ud := L.NewUserData()
c, args := popenArgs(cmd)
pp := exec.Command(c, args...)
lfile := &lFile{fp: nil, pp: pp, writer: nil, reader: nil, stdout: nil, closed: false}
ud.Value = lfile
var err error
if writable {
lfile.writer, err = pp.StdinPipe()
}
if readable {
lfile.stdout, err = pp.StdoutPipe()
lfile.reader = bufio.NewReaderSize(lfile.stdout, fileDefaultReadBuffer)
}
if err != nil {
return nil, err
}
err = pp.Start()
if err != nil {
return nil, err
}
L.SetMetatable(ud, L.GetTypeMetatable(lFileClass))
return ud, nil
}
func (file *lFile) Type() lFileType {
if file.fp == nil {
return lFileProcess
}
return lFileFile
}
func (file *lFile) Name() string {
switch file.Type() {
case lFileFile:
return fmt.Sprintf("file %s", file.fp.Name())
case lFileProcess:
return fmt.Sprintf("process %s", file.pp.Path)
}
return ""
}
func (file *lFile) AbandonReadBuffer() error {
if file.Type() == lFileFile && file.reader != nil {
_, err := file.fp.Seek(-int64(file.reader.Buffered()), 1)
if err != nil {
return err
}
file.reader = bufio.NewReaderSize(file.fp, fileDefaultReadBuffer)
}
return nil
}
func fileDefOut(L *LState) *LUserData {
return L.Get(UpvalueIndex(1)).(*LTable).RawGetInt(fileDefOutIndex).(*LUserData)
}
func fileDefIn(L *LState) *LUserData {
return L.Get(UpvalueIndex(1)).(*LTable).RawGetInt(fileDefInIndex).(*LUserData)
}
func fileIsWritable(L *LState, file *lFile) int {
if file.writer == nil {
L.Push(LNil)
L.Push(LString(fmt.Sprintf("%s is opened for only reading.", file.Name())))
L.Push(LNumber(1)) // C-Lua compatibility: Original Lua pushes errno to the stack
return 3
}
return 0
}
func fileIsReadable(L *LState, file *lFile) int {
if file.reader == nil {
L.Push(LNil)
L.Push(LString(fmt.Sprintf("%s is opened for only writing.", file.Name())))
L.Push(LNumber(1)) // C-Lua compatibility: Original Lua pushes errno to the stack
return 3
}
return 0
}
var stdFiles = []struct {
name string
file *os.File
writable bool
readable bool
}{
{"stdout", os.Stdout, true, false},
{"stdin", os.Stdin, false, true},
{"stderr", os.Stderr, true, false},
}
func OpenIo(L *LState) int {
mod := L.RegisterModule(IoLibName, map[string]LGFunction{}).(*LTable)
mt := L.NewTypeMetatable(lFileClass)
mt.RawSetString("__index", mt)
L.SetFuncs(mt, fileMethods)
mt.RawSetString("lines", L.NewClosure(fileLines, L.NewFunction(fileLinesIter)))
for _, finfo := range stdFiles {
file, _ := newFile(L, finfo.file, "", 0, os.FileMode(0), finfo.writable, finfo.readable)
mod.RawSetString(finfo.name, file)
}
uv := L.CreateTable(2, 0)
uv.RawSetInt(fileDefOutIndex, mod.RawGetString("stdout"))
uv.RawSetInt(fileDefInIndex, mod.RawGetString("stdin"))
for name, fn := range ioFuncs {
mod.RawSetString(name, L.NewClosure(fn, uv))
}
mod.RawSetString("lines", L.NewClosure(ioLines, uv, L.NewClosure(ioLinesIter, uv)))
// Modifications are being made in-place rather than returned?
L.Push(mod)
return 1
}
var fileMethods = map[string]LGFunction{
"__tostring": fileToString,
"write": fileWrite,
"close": fileClose,
"flush": fileFlush,
"lines": fileLines,
"read": fileRead,
"seek": fileSeek,
"setvbuf": fileSetVBuf,
}
func fileToString(L *LState) int {
file := checkFile(L)
if file.Type() == lFileFile {
if file.closed {
L.Push(LString("file (closed)"))
} else {
L.Push(LString("file"))
}
} else {
if file.closed {
L.Push(LString("process (closed)"))
} else {
L.Push(LString("process"))
}
}
return 1
}
func fileWriteAux(L *LState, file *lFile, idx int) int {
if n := fileIsWritable(L, file); n != 0 {
return n
}
errorIfFileIsClosed(L, file)
top := L.GetTop()
out := file.writer
var err error
for i := idx; i <= top; i++ {
L.CheckTypes(i, LTNumber, LTString)
s := LVAsString(L.Get(i))
if _, err = out.Write(unsafeFastStringToReadOnlyBytes(s)); err != nil {
goto errreturn
}
}
file.AbandonReadBuffer()
L.Push(LTrue)
return 1
errreturn:
file.AbandonReadBuffer()
L.Push(LNil)
L.Push(LString(err.Error()))
L.Push(LNumber(1)) // C-Lua compatibility: Original Lua pushes errno to the stack
return 3
}
func fileCloseAux(L *LState, file *lFile) int {
file.closed = true
var err error
if file.writer != nil {
if bwriter, ok := file.writer.(*bufio.Writer); ok {
if err = bwriter.Flush(); err != nil {
goto errreturn
}
}
}
file.AbandonReadBuffer()
switch file.Type() {
case lFileFile:
if err = file.fp.Close(); err != nil {
goto errreturn
}
L.Push(LTrue)
return 1
case lFileProcess:
if file.stdout != nil {
file.stdout.Close() // ignore errors
}
err = file.pp.Wait()
var exitStatus int // Initialised to zero value = 0
if err != nil {
if e2, ok := err.(*exec.ExitError); ok {
if s, ok := e2.Sys().(syscall.WaitStatus); ok {
exitStatus = s.ExitStatus()
} else {
err = errors.New("Unimplemented for system where exec.ExitError.Sys() is not syscall.WaitStatus.")
}
}
} else {
exitStatus = 0
}
L.Push(LNumber(exitStatus))
return 1
}
errreturn:
L.RaiseError(err.Error())
return 0
}
func fileFlushAux(L *LState, file *lFile) int {
if n := fileIsWritable(L, file); n != 0 {
return n
}
errorIfFileIsClosed(L, file)
if bwriter, ok := file.writer.(*bufio.Writer); ok {
if err := bwriter.Flush(); err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
}
}
L.Push(LTrue)
return 1
}
func fileReadAux(L *LState, file *lFile, idx int) int {
if n := fileIsReadable(L, file); n != 0 {
return n
}
errorIfFileIsClosed(L, file)
if L.GetTop() == idx-1 {
L.Push(LString("*l"))
}
var err error
top := L.GetTop()
for i := idx; i <= top; i++ {
switch lv := L.Get(i).(type) {
case LNumber:
size := int64(lv)
if size == 0 {
_, err = file.reader.ReadByte()
if err == io.EOF {
L.Push(LNil)
goto normalreturn
}
file.reader.UnreadByte()
}
var buf []byte
var iseof bool
buf, err, iseof = readBufioSize(file.reader, size)
if iseof {
L.Push(LNil)
goto normalreturn
}
if err != nil {
goto errreturn
}
L.Push(LString(string(buf)))
case LString:
options := L.CheckString(i)
if len(options) > 0 && options[0] != '*' {
L.ArgError(2, "invalid options:"+options)
}
for _, opt := range options[1:] {
switch opt {
case 'n':
var v LNumber
_, err = fmt.Fscanf(file.reader, LNumberScanFormat, &v)
if err == io.EOF {
L.Push(LNil)
goto normalreturn
}
if err != nil {
goto errreturn
}
L.Push(v)
case 'a':
var buf []byte
buf, err = ioutil.ReadAll(file.reader)
if err == io.EOF {
L.Push(emptyLString)
goto normalreturn
}
if err != nil {
goto errreturn
}
L.Push(LString(string(buf)))
case 'l':
var buf []byte
var iseof bool
buf, err, iseof = readBufioLine(file.reader)
if iseof {
L.Push(LNil)
goto normalreturn
}
if err != nil {
goto errreturn
}
L.Push(LString(string(buf)))
default:
L.ArgError(2, "invalid options:"+string(opt))
}
}
}
}
normalreturn:
return L.GetTop() - top
errreturn:
L.RaiseError(err.Error())
//L.Push(LNil)
//L.Push(LString(err.Error()))
return 2
}
var fileSeekOptions = []string{"set", "cur", "end"}
func fileSeek(L *LState) int {
file := checkFile(L)
if file.Type() != lFileFile {
L.Push(LNil)
L.Push(LString("can not seek a process."))
return 2
}
top := L.GetTop()
if top == 1 {
L.Push(LString("cur"))
L.Push(LNumber(0))
} else if top == 2 {
L.Push(LNumber(0))
}
var pos int64
var err error
err = file.AbandonReadBuffer()
if err != nil {
goto errreturn
}
pos, err = file.fp.Seek(L.CheckInt64(3), L.CheckOption(2, fileSeekOptions))
if err != nil {
goto errreturn
}
L.Push(LNumber(pos))
return 1
errreturn:
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
}
func fileWrite(L *LState) int {
return fileWriteAux(L, checkFile(L), 2)
}
func fileClose(L *LState) int {
return fileCloseAux(L, checkFile(L))
}
func fileFlush(L *LState) int {
return fileFlushAux(L, checkFile(L))
}
func fileLinesIter(L *LState) int {
var file *lFile
if ud, ok := L.Get(1).(*LUserData); ok {
file = ud.Value.(*lFile)
} else {
file = L.Get(UpvalueIndex(2)).(*LUserData).Value.(*lFile)
}
buf, _, err := file.reader.ReadLine()
if err != nil {
if err == io.EOF {
L.Push(LNil)
return 1
}
L.RaiseError(err.Error())
}
L.Push(LString(string(buf)))
return 1
}
func fileLines(L *LState) int {
file := checkFile(L)
ud := L.CheckUserData(1)
if n := fileIsReadable(L, file); n != 0 {
return 0
}
L.Push(L.NewClosure(fileLinesIter, L.Get(UpvalueIndex(1)), ud))
return 1
}
func fileRead(L *LState) int {
return fileReadAux(L, checkFile(L), 2)
}
var filebufOptions = []string{"no", "full"}
func fileSetVBuf(L *LState) int {
var err error
var writer io.Writer
file := checkFile(L)
if n := fileIsWritable(L, file); n != 0 {
return n
}
switch filebufOptions[L.CheckOption(2, filebufOptions)] {
case "no":
switch file.Type() {
case lFileFile:
file.writer = file.fp
case lFileProcess:
file.writer, err = file.pp.StdinPipe()
if err != nil {
goto errreturn
}
}
case "full", "line": // TODO line buffer not supported
bufsize := L.OptInt(3, fileDefaultWriteBuffer)
switch file.Type() {
case lFileFile:
file.writer = bufio.NewWriterSize(file.fp, bufsize)
case lFileProcess:
writer, err = file.pp.StdinPipe()
if err != nil {
goto errreturn
}
file.writer = bufio.NewWriterSize(writer, bufsize)
}
}
L.Push(LTrue)
return 1
errreturn:
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
}
func ioInput(L *LState) int {
if L.GetTop() == 0 {
L.Push(fileDefIn(L))
return 1
}
switch lv := L.Get(1).(type) {
case LString:
file, err := newFile(L, nil, string(lv), os.O_RDONLY, 0600, false, true)
if err != nil {
L.RaiseError(err.Error())
}
L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefInIndex, file)
L.Push(file)
return 1
case *LUserData:
if _, ok := lv.Value.(*lFile); ok {
L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefInIndex, lv)
L.Push(lv)
return 1
}
}
L.ArgError(1, "string or file expedted, but got "+L.Get(1).Type().String())
return 0
}
func ioClose(L *LState) int {
if L.GetTop() == 0 {
return fileCloseAux(L, fileDefOut(L).Value.(*lFile))
}
return fileClose(L)
}
func ioFlush(L *LState) int {
return fileFlushAux(L, fileDefOut(L).Value.(*lFile))
}
func ioLinesIter(L *LState) int {
var file *lFile
toclose := false
if ud, ok := L.Get(1).(*LUserData); ok {
file = ud.Value.(*lFile)
} else {
file = L.Get(UpvalueIndex(2)).(*LUserData).Value.(*lFile)
toclose = true
}
buf, _, err := file.reader.ReadLine()
if err != nil {
if err == io.EOF {
if toclose {
fileCloseAux(L, file)
}
L.Push(LNil)
return 1
}
L.RaiseError(err.Error())
}
L.Push(LString(string(buf)))
return 1
}
func ioLines(L *LState) int {
if L.GetTop() == 0 {
L.Push(L.Get(UpvalueIndex(2)))
L.Push(fileDefIn(L))
return 2
}
path := L.CheckString(1)
ud, err := newFile(L, nil, path, os.O_RDONLY, os.FileMode(0600), false, true)
if err != nil {
return 0
}
L.Push(L.NewClosure(ioLinesIter, L.Get(UpvalueIndex(1)), ud))
return 1
}
var ioOpenOpions = []string{"r", "rb", "w", "wb", "a", "ab", "r+", "rb+", "w+", "wb+", "a+", "ab+"}
func ioOpenFile(L *LState) int {
path := L.CheckString(1)
if L.GetTop() == 1 {
L.Push(LString("r"))
}
mode := os.O_RDONLY
perm := 0600
writable := true
readable := true
switch ioOpenOpions[L.CheckOption(2, ioOpenOpions)] {
case "r", "rb":
mode = os.O_RDONLY
writable = false
case "w", "wb":
mode = os.O_WRONLY | os.O_CREATE
readable = false
case "a", "ab":
mode = os.O_WRONLY | os.O_APPEND | os.O_CREATE
case "r+", "rb+":
mode = os.O_RDWR
case "w+", "wb+":
mode = os.O_RDWR | os.O_TRUNC | os.O_CREATE
case "a+", "ab+":
mode = os.O_APPEND | os.O_RDWR | os.O_CREATE
}
file, err := newFile(L, nil, path, mode, os.FileMode(perm), writable, readable)
if err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
L.Push(LNumber(1)) // C-Lua compatibility: Original Lua pushes errno to the stack
return 3
}
L.Push(file)
return 1
}
var ioPopenOptions = []string{"r", "w"}
func ioPopen(L *LState) int {
cmd := L.CheckString(1)
if L.GetTop() == 1 {
L.Push(LString("r"))
}
var file *LUserData
var err error
switch ioPopenOptions[L.CheckOption(2, ioPopenOptions)] {
case "r":
file, err = newProcess(L, cmd, false, true)
case "w":
file, err = newProcess(L, cmd, true, false)
}
if err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
}
L.Push(file)
return 1
}
func ioRead(L *LState) int {
return fileReadAux(L, fileDefIn(L).Value.(*lFile), 1)
}
func ioType(L *LState) int {
ud, udok := L.Get(1).(*LUserData)
if !udok {
L.Push(LNil)
return 1
}
file, ok := ud.Value.(*lFile)
if !ok {
L.Push(LNil)
return 1
}
if file.closed {
L.Push(LString("closed file"))
return 1
}
L.Push(LString("file"))
return 1
}
func ioTmpFile(L *LState) int {
file, err := ioutil.TempFile("", "")
if err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
}
L.G.tempFiles = append(L.G.tempFiles, file)
ud, _ := newFile(L, file, "", 0, os.FileMode(0), true, true)
L.Push(ud)
return 1
}
func ioOutput(L *LState) int {
if L.GetTop() == 0 {
L.Push(fileDefOut(L))
return 1
}
switch lv := L.Get(1).(type) {
case LString:
file, err := newFile(L, nil, string(lv), os.O_WRONLY|os.O_CREATE, 0600, true, false)
if err != nil {
L.RaiseError(err.Error())
}
L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefOutIndex, file)
L.Push(file)
return 1
case *LUserData:
if _, ok := lv.Value.(*lFile); ok {
L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefOutIndex, lv)
L.Push(lv)
return 1
}
}
L.ArgError(1, "string or file expedted, but got "+L.Get(1).Type().String())
return 0
}
func ioWrite(L *LState) int {
return fileWriteAux(L, fileDefOut(L).Value.(*lFile), 1)
}
//

54
vendor/github.com/yuin/gopher-lua/linit.go generated vendored Normal file
View File

@ -0,0 +1,54 @@
package lua
const (
// BaseLibName is here for consistency; the base functions have no namespace/library.
BaseLibName = ""
// LoadLibName is here for consistency; the loading system has no namespace/library.
LoadLibName = "package"
// TabLibName is the name of the table Library.
TabLibName = "table"
// IoLibName is the name of the io Library.
IoLibName = "io"
// OsLibName is the name of the os Library.
OsLibName = "os"
// StringLibName is the name of the string Library.
StringLibName = "string"
// MathLibName is the name of the math Library.
MathLibName = "math"
// DebugLibName is the name of the debug Library.
DebugLibName = "debug"
// ChannelLibName is the name of the channel Library.
ChannelLibName = "channel"
// CoroutineLibName is the name of the coroutine Library.
CoroutineLibName = "coroutine"
)
type luaLib struct {
libName string
libFunc LGFunction
}
var luaLibs = []luaLib{
luaLib{LoadLibName, OpenPackage},
luaLib{BaseLibName, OpenBase},
luaLib{TabLibName, OpenTable},
luaLib{IoLibName, OpenIo},
luaLib{OsLibName, OpenOs},
luaLib{StringLibName, OpenString},
luaLib{MathLibName, OpenMath},
luaLib{DebugLibName, OpenDebug},
luaLib{ChannelLibName, OpenChannel},
luaLib{CoroutineLibName, OpenCoroutine},
}
// OpenLibs loads the built-in libraries. It is equivalent to running OpenLoad,
// then OpenBase, then iterating over the other OpenXXX functions in any order.
func (ls *LState) OpenLibs() {
// NB: Map iteration order in Go is deliberately randomised, so must open Load/Base
// prior to iterating.
for _, lib := range luaLibs {
ls.Push(ls.NewFunction(lib.libFunc))
ls.Push(LString(lib.libName))
ls.Call(1, 0)
}
}

128
vendor/github.com/yuin/gopher-lua/loadlib.go generated vendored Normal file
View File

@ -0,0 +1,128 @@
package lua
import (
"fmt"
"os"
"path/filepath"
"strings"
)
/* load lib {{{ */
var loLoaders = []LGFunction{loLoaderPreload, loLoaderLua}
func loGetPath(env string, defpath string) string {
path := os.Getenv(env)
if len(path) == 0 {
path = defpath
}
path = strings.Replace(path, ";;", ";"+defpath+";", -1)
if os.PathSeparator != '/' {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
panic(err)
}
path = strings.Replace(path, "!", dir, -1)
}
return path
}
func loFindFile(L *LState, name, pname string) (string, string) {
name = strings.Replace(name, ".", string(os.PathSeparator), -1)
lv := L.GetField(L.GetField(L.Get(EnvironIndex), "package"), pname)
path, ok := lv.(LString)
if !ok {
L.RaiseError("package.%s must be a string", pname)
}
messages := []string{}
for _, pattern := range strings.Split(string(path), ";") {
luapath := strings.Replace(pattern, "?", name, -1)
if _, err := os.Stat(luapath); err == nil {
return luapath, ""
} else {
messages = append(messages, err.Error())
}
}
return "", strings.Join(messages, "\n\t")
}
func OpenPackage(L *LState) int {
packagemod := L.RegisterModule(LoadLibName, loFuncs)
L.SetField(packagemod, "preload", L.NewTable())
loaders := L.CreateTable(len(loLoaders), 0)
for i, loader := range loLoaders {
L.RawSetInt(loaders, i+1, L.NewFunction(loader))
}
L.SetField(packagemod, "loaders", loaders)
L.SetField(L.Get(RegistryIndex), "_LOADERS", loaders)
loaded := L.NewTable()
L.SetField(packagemod, "loaded", loaded)
L.SetField(L.Get(RegistryIndex), "_LOADED", loaded)
L.SetField(packagemod, "path", LString(loGetPath(LuaPath, LuaPathDefault)))
L.SetField(packagemod, "cpath", emptyLString)
L.SetField(packagemod, "config", LString(LuaDirSep+"\n"+LuaPathSep+
"\n"+LuaPathMark+"\n"+LuaExecDir+"\n"+LuaIgMark+"\n"))
L.Push(packagemod)
return 1
}
var loFuncs = map[string]LGFunction{
"loadlib": loLoadLib,
"seeall": loSeeAll,
}
func loLoaderPreload(L *LState) int {
name := L.CheckString(1)
preload := L.GetField(L.GetField(L.Get(EnvironIndex), "package"), "preload")
if _, ok := preload.(*LTable); !ok {
L.RaiseError("package.preload must be a table")
}
lv := L.GetField(preload, name)
if lv == LNil {
L.Push(LString(fmt.Sprintf("no field package.preload['%s']", name)))
return 1
}
L.Push(lv)
return 1
}
func loLoaderLua(L *LState) int {
name := L.CheckString(1)
path, msg := loFindFile(L, name, "path")
if len(path) == 0 {
L.Push(LString(msg))
return 1
}
fn, err1 := L.LoadFile(path)
if err1 != nil {
L.RaiseError(err1.Error())
}
L.Push(fn)
return 1
}
func loLoadLib(L *LState) int {
L.RaiseError("loadlib is not supported")
return 0
}
func loSeeAll(L *LState) int {
mod := L.CheckTable(1)
mt := L.GetMetatable(mod)
if mt == LNil {
mt = L.CreateTable(0, 1)
L.SetMetatable(mod, mt)
}
L.SetField(mt, "__index", L.Get(GlobalsIndex))
return 0
}
/* }}} */
//

231
vendor/github.com/yuin/gopher-lua/mathlib.go generated vendored Normal file
View File

@ -0,0 +1,231 @@
package lua
import (
"math"
"math/rand"
)
func OpenMath(L *LState) int {
mod := L.RegisterModule(MathLibName, mathFuncs).(*LTable)
mod.RawSetString("pi", LNumber(math.Pi))
mod.RawSetString("huge", LNumber(math.MaxFloat64))
L.Push(mod)
return 1
}
var mathFuncs = map[string]LGFunction{
"abs": mathAbs,
"acos": mathAcos,
"asin": mathAsin,
"atan": mathAtan,
"atan2": mathAtan2,
"ceil": mathCeil,
"cos": mathCos,
"cosh": mathCosh,
"deg": mathDeg,
"exp": mathExp,
"floor": mathFloor,
"fmod": mathFmod,
"frexp": mathFrexp,
"ldexp": mathLdexp,
"log": mathLog,
"log10": mathLog10,
"max": mathMax,
"min": mathMin,
"mod": mathMod,
"modf": mathModf,
"pow": mathPow,
"rad": mathRad,
"random": mathRandom,
"randomseed": mathRandomseed,
"sin": mathSin,
"sinh": mathSinh,
"sqrt": mathSqrt,
"tan": mathTan,
"tanh": mathTanh,
}
func mathAbs(L *LState) int {
L.Push(LNumber(math.Abs(float64(L.CheckNumber(1)))))
return 1
}
func mathAcos(L *LState) int {
L.Push(LNumber(math.Acos(float64(L.CheckNumber(1)))))
return 1
}
func mathAsin(L *LState) int {
L.Push(LNumber(math.Asin(float64(L.CheckNumber(1)))))
return 1
}
func mathAtan(L *LState) int {
L.Push(LNumber(math.Atan(float64(L.CheckNumber(1)))))
return 1
}
func mathAtan2(L *LState) int {
L.Push(LNumber(math.Atan2(float64(L.CheckNumber(1)), float64(L.CheckNumber(2)))))
return 1
}
func mathCeil(L *LState) int {
L.Push(LNumber(math.Ceil(float64(L.CheckNumber(1)))))
return 1
}
func mathCos(L *LState) int {
L.Push(LNumber(math.Cos(float64(L.CheckNumber(1)))))
return 1
}
func mathCosh(L *LState) int {
L.Push(LNumber(math.Cosh(float64(L.CheckNumber(1)))))
return 1
}
func mathDeg(L *LState) int {
L.Push(LNumber(float64(L.CheckNumber(1)) * 180 / math.Pi))
return 1
}
func mathExp(L *LState) int {
L.Push(LNumber(math.Exp(float64(L.CheckNumber(1)))))
return 1
}
func mathFloor(L *LState) int {
L.Push(LNumber(math.Floor(float64(L.CheckNumber(1)))))
return 1
}
func mathFmod(L *LState) int {
L.Push(LNumber(math.Mod(float64(L.CheckNumber(1)), float64(L.CheckNumber(2)))))
return 1
}
func mathFrexp(L *LState) int {
v1, v2 := math.Frexp(float64(L.CheckNumber(1)))
L.Push(LNumber(v1))
L.Push(LNumber(v2))
return 2
}
func mathLdexp(L *LState) int {
L.Push(LNumber(math.Ldexp(float64(L.CheckNumber(1)), L.CheckInt(2))))
return 1
}
func mathLog(L *LState) int {
L.Push(LNumber(math.Log(float64(L.CheckNumber(1)))))
return 1
}
func mathLog10(L *LState) int {
L.Push(LNumber(math.Log10(float64(L.CheckNumber(1)))))
return 1
}
func mathMax(L *LState) int {
if L.GetTop() == 0 {
L.RaiseError("wrong number of arguments")
}
max := L.CheckNumber(1)
top := L.GetTop()
for i := 2; i <= top; i++ {
v := L.CheckNumber(i)
if v > max {
max = v
}
}
L.Push(max)
return 1
}
func mathMin(L *LState) int {
if L.GetTop() == 0 {
L.RaiseError("wrong number of arguments")
}
min := L.CheckNumber(1)
top := L.GetTop()
for i := 2; i <= top; i++ {
v := L.CheckNumber(i)
if v < min {
min = v
}
}
L.Push(min)
return 1
}
func mathMod(L *LState) int {
lhs := L.CheckNumber(1)
rhs := L.CheckNumber(2)
L.Push(luaModulo(lhs, rhs))
return 1
}
func mathModf(L *LState) int {
v1, v2 := math.Modf(float64(L.CheckNumber(1)))
L.Push(LNumber(v1))
L.Push(LNumber(v2))
return 2
}
func mathPow(L *LState) int {
L.Push(LNumber(math.Pow(float64(L.CheckNumber(1)), float64(L.CheckNumber(2)))))
return 1
}
func mathRad(L *LState) int {
L.Push(LNumber(float64(L.CheckNumber(1)) * math.Pi / 180))
return 1
}
func mathRandom(L *LState) int {
switch L.GetTop() {
case 0:
L.Push(LNumber(rand.Float64()))
case 1:
n := L.CheckInt(1)
L.Push(LNumber(rand.Intn(n) + 1))
default:
min := L.CheckInt(1)
max := L.CheckInt(2) + 1
L.Push(LNumber(rand.Intn(max-min) + min))
}
return 1
}
func mathRandomseed(L *LState) int {
rand.Seed(L.CheckInt64(1))
return 0
}
func mathSin(L *LState) int {
L.Push(LNumber(math.Sin(float64(L.CheckNumber(1)))))
return 1
}
func mathSinh(L *LState) int {
L.Push(LNumber(math.Sinh(float64(L.CheckNumber(1)))))
return 1
}
func mathSqrt(L *LState) int {
L.Push(LNumber(math.Sqrt(float64(L.CheckNumber(1)))))
return 1
}
func mathTan(L *LState) int {
L.Push(LNumber(math.Tan(float64(L.CheckNumber(1)))))
return 1
}
func mathTanh(L *LState) int {
L.Push(LNumber(math.Tanh(float64(L.CheckNumber(1)))))
return 1
}
//

371
vendor/github.com/yuin/gopher-lua/opcode.go generated vendored Normal file
View File

@ -0,0 +1,371 @@
package lua
import (
"fmt"
)
/*
gopherlua uses Lua 5.1.4's opcodes.
Lua 5.1.4 opcodes layout:
instruction = 32bit(fixed length)
+---------------------------------------------+
|0-5(6bits)|6-13(8bit)|14-22(9bit)|23-31(9bit)|
|==========+==========+===========+===========|
| opcode | A | C | B |
|----------+----------+-----------+-----------|
| opcode | A | Bx(unsigned) |
|----------+----------+-----------+-----------|
| opcode | A | sBx(signed) |
+---------------------------------------------+
*/
const opInvalidInstruction = ^uint32(0)
const opSizeCode = 6
const opSizeA = 8
const opSizeB = 9
const opSizeC = 9
const opSizeBx = 18
const opSizesBx = 18
const opMaxArgsA = (1 << opSizeA) - 1
const opMaxArgsB = (1 << opSizeB) - 1
const opMaxArgsC = (1 << opSizeC) - 1
const opMaxArgBx = (1 << opSizeBx) - 1
const opMaxArgSbx = opMaxArgBx >> 1
const (
OP_MOVE int = iota /* A B R(A) := R(B) */
OP_MOVEN /* A B R(A) := R(B); followed by R(C) MOVE ops */
OP_LOADK /* A Bx R(A) := Kst(Bx) */
OP_LOADBOOL /* A B C R(A) := (Bool)B; if (C) pc++ */
OP_LOADNIL /* A B R(A) := ... := R(B) := nil */
OP_GETUPVAL /* A B R(A) := UpValue[B] */
OP_GETGLOBAL /* A Bx R(A) := Gbl[Kst(Bx)] */
OP_GETTABLE /* A B C R(A) := R(B)[RK(C)] */
OP_GETTABLEKS /* A B C R(A) := R(B)[RK(C)] ; RK(C) is constant string */
OP_SETGLOBAL /* A Bx Gbl[Kst(Bx)] := R(A) */
OP_SETUPVAL /* A B UpValue[B] := R(A) */
OP_SETTABLE /* A B C R(A)[RK(B)] := RK(C) */
OP_SETTABLEKS /* A B C R(A)[RK(B)] := RK(C) ; RK(B) is constant string */
OP_NEWTABLE /* A B C R(A) := {} (size = BC) */
OP_SELF /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
OP_ADD /* A B C R(A) := RK(B) + RK(C) */
OP_SUB /* A B C R(A) := RK(B) - RK(C) */
OP_MUL /* A B C R(A) := RK(B) * RK(C) */
OP_DIV /* A B C R(A) := RK(B) / RK(C) */
OP_MOD /* A B C R(A) := RK(B) % RK(C) */
OP_POW /* A B C R(A) := RK(B) ^ RK(C) */
OP_UNM /* A B R(A) := -R(B) */
OP_NOT /* A B R(A) := not R(B) */
OP_LEN /* A B R(A) := length of R(B) */
OP_CONCAT /* A B C R(A) := R(B).. ... ..R(C) */
OP_JMP /* sBx pc+=sBx */
OP_EQ /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
OP_LT /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
OP_LE /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
OP_TEST /* A C if not (R(A) <=> C) then pc++ */
OP_TESTSET /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
OP_CALL /* A B C R(A) ... R(A+C-2) := R(A)(R(A+1) ... R(A+B-1)) */
OP_TAILCALL /* A B C return R(A)(R(A+1) ... R(A+B-1)) */
OP_RETURN /* A B return R(A) ... R(A+B-2) (see note) */
OP_FORLOOP /* A sBx R(A)+=R(A+2);
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
OP_FORPREP /* A sBx R(A)-=R(A+2); pc+=sBx */
OP_TFORLOOP /* A C R(A+3) ... R(A+3+C) := R(A)(R(A+1) R(A+2));
if R(A+3) ~= nil then { pc++; R(A+2)=R(A+3); } */
OP_SETLIST /* A B C R(A)[(C-1)*FPF+i] := R(A+i) 1 <= i <= B */
OP_CLOSE /* A close all variables in the stack up to (>=) R(A)*/
OP_CLOSURE /* A Bx R(A) := closure(KPROTO[Bx] R(A) ... R(A+n)) */
OP_VARARG /* A B R(A) R(A+1) ... R(A+B-1) = vararg */
OP_NOP /* NOP */
)
const opCodeMax = OP_NOP
type opArgMode int
const (
opArgModeN opArgMode = iota
opArgModeU
opArgModeR
opArgModeK
)
type opType int
const (
opTypeABC = iota
opTypeABx
opTypeASbx
)
type opProp struct {
Name string
IsTest bool
SetRegA bool
ModeArgB opArgMode
ModeArgC opArgMode
Type opType
}
var opProps = []opProp{
opProp{"MOVE", false, true, opArgModeR, opArgModeN, opTypeABC},
opProp{"MOVEN", false, true, opArgModeR, opArgModeN, opTypeABC},
opProp{"LOADK", false, true, opArgModeK, opArgModeN, opTypeABx},
opProp{"LOADBOOL", false, true, opArgModeU, opArgModeU, opTypeABC},
opProp{"LOADNIL", false, true, opArgModeR, opArgModeN, opTypeABC},
opProp{"GETUPVAL", false, true, opArgModeU, opArgModeN, opTypeABC},
opProp{"GETGLOBAL", false, true, opArgModeK, opArgModeN, opTypeABx},
opProp{"GETTABLE", false, true, opArgModeR, opArgModeK, opTypeABC},
opProp{"GETTABLEKS", false, true, opArgModeR, opArgModeK, opTypeABC},
opProp{"SETGLOBAL", false, false, opArgModeK, opArgModeN, opTypeABx},
opProp{"SETUPVAL", false, false, opArgModeU, opArgModeN, opTypeABC},
opProp{"SETTABLE", false, false, opArgModeK, opArgModeK, opTypeABC},
opProp{"SETTABLEKS", false, false, opArgModeK, opArgModeK, opTypeABC},
opProp{"NEWTABLE", false, true, opArgModeU, opArgModeU, opTypeABC},
opProp{"SELF", false, true, opArgModeR, opArgModeK, opTypeABC},
opProp{"ADD", false, true, opArgModeK, opArgModeK, opTypeABC},
opProp{"SUB", false, true, opArgModeK, opArgModeK, opTypeABC},
opProp{"MUL", false, true, opArgModeK, opArgModeK, opTypeABC},
opProp{"DIV", false, true, opArgModeK, opArgModeK, opTypeABC},
opProp{"MOD", false, true, opArgModeK, opArgModeK, opTypeABC},
opProp{"POW", false, true, opArgModeK, opArgModeK, opTypeABC},
opProp{"UNM", false, true, opArgModeR, opArgModeN, opTypeABC},
opProp{"NOT", false, true, opArgModeR, opArgModeN, opTypeABC},
opProp{"LEN", false, true, opArgModeR, opArgModeN, opTypeABC},
opProp{"CONCAT", false, true, opArgModeR, opArgModeR, opTypeABC},
opProp{"JMP", false, false, opArgModeR, opArgModeN, opTypeASbx},
opProp{"EQ", true, false, opArgModeK, opArgModeK, opTypeABC},
opProp{"LT", true, false, opArgModeK, opArgModeK, opTypeABC},
opProp{"LE", true, false, opArgModeK, opArgModeK, opTypeABC},
opProp{"TEST", true, true, opArgModeR, opArgModeU, opTypeABC},
opProp{"TESTSET", true, true, opArgModeR, opArgModeU, opTypeABC},
opProp{"CALL", false, true, opArgModeU, opArgModeU, opTypeABC},
opProp{"TAILCALL", false, true, opArgModeU, opArgModeU, opTypeABC},
opProp{"RETURN", false, false, opArgModeU, opArgModeN, opTypeABC},
opProp{"FORLOOP", false, true, opArgModeR, opArgModeN, opTypeASbx},
opProp{"FORPREP", false, true, opArgModeR, opArgModeN, opTypeASbx},
opProp{"TFORLOOP", true, false, opArgModeN, opArgModeU, opTypeABC},
opProp{"SETLIST", false, false, opArgModeU, opArgModeU, opTypeABC},
opProp{"CLOSE", false, false, opArgModeN, opArgModeN, opTypeABC},
opProp{"CLOSURE", false, true, opArgModeU, opArgModeN, opTypeABx},
opProp{"VARARG", false, true, opArgModeU, opArgModeN, opTypeABC},
opProp{"NOP", false, false, opArgModeR, opArgModeN, opTypeASbx},
}
func opGetOpCode(inst uint32) int {
return int(inst >> 26)
}
func opSetOpCode(inst *uint32, opcode int) {
*inst = (*inst & 0x3ffffff) | uint32(opcode<<26)
}
func opGetArgA(inst uint32) int {
return int(inst>>18) & 0xff
}
func opSetArgA(inst *uint32, arg int) {
*inst = (*inst & 0xfc03ffff) | uint32((arg&0xff)<<18)
}
func opGetArgB(inst uint32) int {
return int(inst & 0x1ff)
}
func opSetArgB(inst *uint32, arg int) {
*inst = (*inst & 0xfffffe00) | uint32(arg&0x1ff)
}
func opGetArgC(inst uint32) int {
return int(inst>>9) & 0x1ff
}
func opSetArgC(inst *uint32, arg int) {
*inst = (*inst & 0xfffc01ff) | uint32((arg&0x1ff)<<9)
}
func opGetArgBx(inst uint32) int {
return int(inst & 0x3ffff)
}
func opSetArgBx(inst *uint32, arg int) {
*inst = (*inst & 0xfffc0000) | uint32(arg&0x3ffff)
}
func opGetArgSbx(inst uint32) int {
return opGetArgBx(inst) - opMaxArgSbx
}
func opSetArgSbx(inst *uint32, arg int) {
opSetArgBx(inst, arg+opMaxArgSbx)
}
func opCreateABC(op int, a int, b int, c int) uint32 {
var inst uint32 = 0
opSetOpCode(&inst, op)
opSetArgA(&inst, a)
opSetArgB(&inst, b)
opSetArgC(&inst, c)
return inst
}
func opCreateABx(op int, a int, bx int) uint32 {
var inst uint32 = 0
opSetOpCode(&inst, op)
opSetArgA(&inst, a)
opSetArgBx(&inst, bx)
return inst
}
func opCreateASbx(op int, a int, sbx int) uint32 {
var inst uint32 = 0
opSetOpCode(&inst, op)
opSetArgA(&inst, a)
opSetArgSbx(&inst, sbx)
return inst
}
const opBitRk = 1 << (opSizeB - 1)
const opMaxIndexRk = opBitRk - 1
func opIsK(value int) bool {
return bool((value & opBitRk) != 0)
}
func opIndexK(value int) int {
return value & ^opBitRk
}
func opRkAsk(value int) int {
return value | opBitRk
}
func opToString(inst uint32) string {
op := opGetOpCode(inst)
if op > opCodeMax {
return ""
}
prop := &(opProps[op])
arga := opGetArgA(inst)
argb := opGetArgB(inst)
argc := opGetArgC(inst)
argbx := opGetArgBx(inst)
argsbx := opGetArgSbx(inst)
buf := ""
switch prop.Type {
case opTypeABC:
buf = fmt.Sprintf("%s | %d, %d, %d", prop.Name, arga, argb, argc)
case opTypeABx:
buf = fmt.Sprintf("%s | %d, %d", prop.Name, arga, argbx)
case opTypeASbx:
buf = fmt.Sprintf("%s | %d, %d", prop.Name, arga, argsbx)
}
switch op {
case OP_MOVE:
buf += fmt.Sprintf("; R(%v) := R(%v)", arga, argb)
case OP_MOVEN:
buf += fmt.Sprintf("; R(%v) := R(%v); followed by %v MOVE ops", arga, argb, argc)
case OP_LOADK:
buf += fmt.Sprintf("; R(%v) := Kst(%v)", arga, argbx)
case OP_LOADBOOL:
buf += fmt.Sprintf("; R(%v) := (Bool)%v; if (%v) pc++", arga, argb, argc)
case OP_LOADNIL:
buf += fmt.Sprintf("; R(%v) := ... := R(%v) := nil", arga, argb)
case OP_GETUPVAL:
buf += fmt.Sprintf("; R(%v) := UpValue[%v]", arga, argb)
case OP_GETGLOBAL:
buf += fmt.Sprintf("; R(%v) := Gbl[Kst(%v)]", arga, argbx)
case OP_GETTABLE:
buf += fmt.Sprintf("; R(%v) := R(%v)[RK(%v)]", arga, argb, argc)
case OP_GETTABLEKS:
buf += fmt.Sprintf("; R(%v) := R(%v)[RK(%v)] ; RK(%v) is constant string", arga, argb, argc, argc)
case OP_SETGLOBAL:
buf += fmt.Sprintf("; Gbl[Kst(%v)] := R(%v)", argbx, arga)
case OP_SETUPVAL:
buf += fmt.Sprintf("; UpValue[%v] := R(%v)", argb, arga)
case OP_SETTABLE:
buf += fmt.Sprintf("; R(%v)[RK(%v)] := RK(%v)", arga, argb, argc)
case OP_SETTABLEKS:
buf += fmt.Sprintf("; R(%v)[RK(%v)] := RK(%v) ; RK(%v) is constant string", arga, argb, argc, argb)
case OP_NEWTABLE:
buf += fmt.Sprintf("; R(%v) := {} (size = BC)", arga)
case OP_SELF:
buf += fmt.Sprintf("; R(%v+1) := R(%v); R(%v) := R(%v)[RK(%v)]", arga, argb, arga, argb, argc)
case OP_ADD:
buf += fmt.Sprintf("; R(%v) := RK(%v) + RK(%v)", arga, argb, argc)
case OP_SUB:
buf += fmt.Sprintf("; R(%v) := RK(%v) - RK(%v)", arga, argb, argc)
case OP_MUL:
buf += fmt.Sprintf("; R(%v) := RK(%v) * RK(%v)", arga, argb, argc)
case OP_DIV:
buf += fmt.Sprintf("; R(%v) := RK(%v) / RK(%v)", arga, argb, argc)
case OP_MOD:
buf += fmt.Sprintf("; R(%v) := RK(%v) %% RK(%v)", arga, argb, argc)
case OP_POW:
buf += fmt.Sprintf("; R(%v) := RK(%v) ^ RK(%v)", arga, argb, argc)
case OP_UNM:
buf += fmt.Sprintf("; R(%v) := -R(%v)", arga, argb)
case OP_NOT:
buf += fmt.Sprintf("; R(%v) := not R(%v)", arga, argb)
case OP_LEN:
buf += fmt.Sprintf("; R(%v) := length of R(%v)", arga, argb)
case OP_CONCAT:
buf += fmt.Sprintf("; R(%v) := R(%v).. ... ..R(%v)", arga, argb, argc)
case OP_JMP:
buf += fmt.Sprintf("; pc+=%v", argsbx)
case OP_EQ:
buf += fmt.Sprintf("; if ((RK(%v) == RK(%v)) ~= %v) then pc++", argb, argc, arga)
case OP_LT:
buf += fmt.Sprintf("; if ((RK(%v) < RK(%v)) ~= %v) then pc++", argb, argc, arga)
case OP_LE:
buf += fmt.Sprintf("; if ((RK(%v) <= RK(%v)) ~= %v) then pc++", argb, argc, arga)
case OP_TEST:
buf += fmt.Sprintf("; if not (R(%v) <=> %v) then pc++", arga, argc)
case OP_TESTSET:
buf += fmt.Sprintf("; if (R(%v) <=> %v) then R(%v) := R(%v) else pc++", argb, argc, arga, argb)
case OP_CALL:
buf += fmt.Sprintf("; R(%v) ... R(%v+%v-2) := R(%v)(R(%v+1) ... R(%v+%v-1))", arga, arga, argc, arga, arga, arga, argb)
case OP_TAILCALL:
buf += fmt.Sprintf("; return R(%v)(R(%v+1) ... R(%v+%v-1))", arga, arga, arga, argb)
case OP_RETURN:
buf += fmt.Sprintf("; return R(%v) ... R(%v+%v-2)", arga, arga, argb)
case OP_FORLOOP:
buf += fmt.Sprintf("; R(%v)+=R(%v+2); if R(%v) <?= R(%v+1) then { pc+=%v; R(%v+3)=R(%v) }", arga, arga, arga, arga, argsbx, arga, arga)
case OP_FORPREP:
buf += fmt.Sprintf("; R(%v)-=R(%v+2); pc+=%v", arga, arga, argsbx)
case OP_TFORLOOP:
buf += fmt.Sprintf("; R(%v+3) ... R(%v+3+%v) := R(%v)(R(%v+1) R(%v+2)); if R(%v+3) ~= nil then { pc++; R(%v+2)=R(%v+3); }", arga, arga, argc, arga, arga, arga, arga, arga, arga)
case OP_SETLIST:
buf += fmt.Sprintf("; R(%v)[(%v-1)*FPF+i] := R(%v+i) 1 <= i <= %v", arga, argc, arga, argb)
case OP_CLOSE:
buf += fmt.Sprintf("; close all variables in the stack up to (>=) R(%v)", arga)
case OP_CLOSURE:
buf += fmt.Sprintf("; R(%v) := closure(KPROTO[%v] R(%v) ... R(%v+n))", arga, argbx, arga, arga)
case OP_VARARG:
buf += fmt.Sprintf("; R(%v) R(%v+1) ... R(%v+%v-1) = vararg", arga, arga, arga, argb)
case OP_NOP:
/* nothing to do */
}
return buf
}

232
vendor/github.com/yuin/gopher-lua/oslib.go generated vendored Normal file
View File

@ -0,0 +1,232 @@
package lua
import (
"io/ioutil"
"os"
"strings"
"time"
)
var startedAt time.Time
func init() {
startedAt = time.Now()
}
func getIntField(L *LState, tb *LTable, key string, v int) int {
ret := tb.RawGetString(key)
switch lv := ret.(type) {
case LNumber:
return int(lv)
case LString:
slv := string(lv)
slv = strings.TrimLeft(slv, " ")
if strings.HasPrefix(slv, "0") && !strings.HasPrefix(slv, "0x") && !strings.HasPrefix(slv, "0X") {
//Standard lua interpreter only support decimal and hexadecimal
slv = strings.TrimLeft(slv, "0")
if slv == "" {
return 0
}
}
if num, err := parseNumber(slv); err == nil {
return int(num)
}
default:
return v
}
return v
}
func getBoolField(L *LState, tb *LTable, key string, v bool) bool {
ret := tb.RawGetString(key)
if lb, ok := ret.(LBool); ok {
return bool(lb)
}
return v
}
func OpenOs(L *LState) int {
osmod := L.RegisterModule(OsLibName, osFuncs)
L.Push(osmod)
return 1
}
var osFuncs = map[string]LGFunction{
"clock": osClock,
"difftime": osDiffTime,
"execute": osExecute,
"exit": osExit,
"date": osDate,
"getenv": osGetEnv,
"remove": osRemove,
"rename": osRename,
"setenv": osSetEnv,
"setlocale": osSetLocale,
"time": osTime,
"tmpname": osTmpname,
}
func osClock(L *LState) int {
L.Push(LNumber(float64(time.Now().Sub(startedAt)) / float64(time.Second)))
return 1
}
func osDiffTime(L *LState) int {
L.Push(LNumber(L.CheckInt64(1) - L.CheckInt64(2)))
return 1
}
func osExecute(L *LState) int {
var procAttr os.ProcAttr
procAttr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr}
cmd, args := popenArgs(L.CheckString(1))
args = append([]string{cmd}, args...)
process, err := os.StartProcess(cmd, args, &procAttr)
if err != nil {
L.Push(LNumber(1))
return 1
}
ps, err := process.Wait()
if err != nil || !ps.Success() {
L.Push(LNumber(1))
return 1
}
L.Push(LNumber(0))
return 1
}
func osExit(L *LState) int {
L.Close()
os.Exit(L.OptInt(1, 0))
return 1
}
func osDate(L *LState) int {
t := time.Now()
cfmt := "%c"
if L.GetTop() >= 1 {
cfmt = L.CheckString(1)
if strings.HasPrefix(cfmt, "!") {
t = time.Now().UTC()
cfmt = strings.TrimLeft(cfmt, "!")
}
if L.GetTop() >= 2 {
t = time.Unix(L.CheckInt64(2), 0)
}
if strings.HasPrefix(cfmt, "*t") {
ret := L.NewTable()
ret.RawSetString("year", LNumber(t.Year()))
ret.RawSetString("month", LNumber(t.Month()))
ret.RawSetString("day", LNumber(t.Day()))
ret.RawSetString("hour", LNumber(t.Hour()))
ret.RawSetString("min", LNumber(t.Minute()))
ret.RawSetString("sec", LNumber(t.Second()))
ret.RawSetString("wday", LNumber(t.Weekday()+1))
// TODO yday & dst
ret.RawSetString("yday", LNumber(0))
ret.RawSetString("isdst", LFalse)
L.Push(ret)
return 1
}
}
L.Push(LString(strftime(t, cfmt)))
return 1
}
func osGetEnv(L *LState) int {
v := os.Getenv(L.CheckString(1))
if len(v) == 0 {
L.Push(LNil)
} else {
L.Push(LString(v))
}
return 1
}
func osRemove(L *LState) int {
err := os.Remove(L.CheckString(1))
if err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
} else {
L.Push(LTrue)
return 1
}
}
func osRename(L *LState) int {
err := os.Rename(L.CheckString(1), L.CheckString(2))
if err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
} else {
L.Push(LTrue)
return 1
}
}
func osSetLocale(L *LState) int {
// setlocale is not supported
L.Push(LFalse)
return 1
}
func osSetEnv(L *LState) int {
err := os.Setenv(L.CheckString(1), L.CheckString(2))
if err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
} else {
L.Push(LTrue)
return 1
}
}
func osTime(L *LState) int {
if L.GetTop() == 0 {
L.Push(LNumber(time.Now().Unix()))
} else {
lv := L.CheckAny(1)
if lv == LNil {
L.Push(LNumber(time.Now().Unix()))
} else {
tbl, ok := lv.(*LTable)
if !ok {
L.TypeError(1, LTTable)
}
sec := getIntField(L, tbl, "sec", 0)
min := getIntField(L, tbl, "min", 0)
hour := getIntField(L, tbl, "hour", 12)
day := getIntField(L, tbl, "day", -1)
month := getIntField(L, tbl, "month", -1)
year := getIntField(L, tbl, "year", -1)
isdst := getBoolField(L, tbl, "isdst", false)
t := time.Date(year, time.Month(month), day, hour, min, sec, 0, time.Local)
// TODO dst
if false {
print(isdst)
}
L.Push(LNumber(t.Unix()))
}
}
return 1
}
func osTmpname(L *LState) int {
file, err := ioutil.TempFile("", "")
if err != nil {
L.RaiseError("unable to generate a unique filename")
}
file.Close()
os.Remove(file.Name()) // ignore errors
L.Push(LString(file.Name()))
return 1
}
//

7
vendor/github.com/yuin/gopher-lua/package.go generated vendored Normal file
View File

@ -0,0 +1,7 @@
// GopherLua: VM and compiler for Lua in Go
package lua
const PackageName = "GopherLua"
const PackageVersion = "0.1"
const PackageAuthors = "Yusuke Inuzuka"
const PackageCopyRight = PackageName + " " + PackageVersion + " Copyright (C) 2015 -2017 " + PackageAuthors

7
vendor/github.com/yuin/gopher-lua/parse/Makefile generated vendored Normal file
View File

@ -0,0 +1,7 @@
all : parser.go
parser.go : parser.go.y
goyacc -o $@ parser.go.y; [ -f y.output ] && ( rm -f y.output )
clean:
rm -f parser.go

539
vendor/github.com/yuin/gopher-lua/parse/lexer.go generated vendored Normal file
View File

@ -0,0 +1,539 @@
package parse
import (
"bufio"
"bytes"
"fmt"
"github.com/yuin/gopher-lua/ast"
"io"
"reflect"
"strconv"
"strings"
)
const EOF = -1
const whitespace1 = 1<<'\t' | 1<<' '
const whitespace2 = 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<' '
type Error struct {
Pos ast.Position
Message string
Token string
}
func (e *Error) Error() string {
pos := e.Pos
if pos.Line == EOF {
return fmt.Sprintf("%v at EOF: %s\n", pos.Source, e.Message)
} else {
return fmt.Sprintf("%v line:%d(column:%d) near '%v': %s\n", pos.Source, pos.Line, pos.Column, e.Token, e.Message)
}
}
func writeChar(buf *bytes.Buffer, c int) { buf.WriteByte(byte(c)) }
func isDecimal(ch int) bool { return '0' <= ch && ch <= '9' }
func isIdent(ch int, pos int) bool {
return ch == '_' || 'A' <= ch && ch <= 'Z' || 'a' <= ch && ch <= 'z' || isDecimal(ch) && pos > 0
}
func isDigit(ch int) bool {
return '0' <= ch && ch <= '9' || 'a' <= ch && ch <= 'f' || 'A' <= ch && ch <= 'F'
}
type Scanner struct {
Pos ast.Position
reader *bufio.Reader
}
func NewScanner(reader io.Reader, source string) *Scanner {
return &Scanner{
Pos: ast.Position{
Source: source,
Line: 1,
Column: 0,
},
reader: bufio.NewReaderSize(reader, 4096),
}
}
func (sc *Scanner) Error(tok string, msg string) *Error { return &Error{sc.Pos, msg, tok} }
func (sc *Scanner) TokenError(tok ast.Token, msg string) *Error { return &Error{tok.Pos, msg, tok.Str} }
func (sc *Scanner) readNext() int {
ch, err := sc.reader.ReadByte()
if err == io.EOF {
return EOF
}
return int(ch)
}
func (sc *Scanner) Newline(ch int) {
if ch < 0 {
return
}
sc.Pos.Line += 1
sc.Pos.Column = 0
next := sc.Peek()
if ch == '\n' && next == '\r' || ch == '\r' && next == '\n' {
sc.reader.ReadByte()
}
}
func (sc *Scanner) Next() int {
ch := sc.readNext()
switch ch {
case '\n', '\r':
sc.Newline(ch)
ch = int('\n')
case EOF:
sc.Pos.Line = EOF
sc.Pos.Column = 0
default:
sc.Pos.Column++
}
return ch
}
func (sc *Scanner) Peek() int {
ch := sc.readNext()
if ch != EOF {
sc.reader.UnreadByte()
}
return ch
}
func (sc *Scanner) skipWhiteSpace(whitespace int64) int {
ch := sc.Next()
for ; whitespace&(1<<uint(ch)) != 0; ch = sc.Next() {
}
return ch
}
func (sc *Scanner) skipComments(ch int) error {
// multiline comment
if sc.Peek() == '[' {
ch = sc.Next()
if sc.Peek() == '[' || sc.Peek() == '=' {
var buf bytes.Buffer
if err := sc.scanMultilineString(sc.Next(), &buf); err != nil {
return sc.Error(buf.String(), "invalid multiline comment")
}
return nil
}
}
for {
if ch == '\n' || ch == '\r' || ch < 0 {
break
}
ch = sc.Next()
}
return nil
}
func (sc *Scanner) scanIdent(ch int, buf *bytes.Buffer) error {
writeChar(buf, ch)
for isIdent(sc.Peek(), 1) {
writeChar(buf, sc.Next())
}
return nil
}
func (sc *Scanner) scanDecimal(ch int, buf *bytes.Buffer) error {
writeChar(buf, ch)
for isDecimal(sc.Peek()) {
writeChar(buf, sc.Next())
}
return nil
}
func (sc *Scanner) scanNumber(ch int, buf *bytes.Buffer) error {
if ch == '0' { // octal
if sc.Peek() == 'x' || sc.Peek() == 'X' {
writeChar(buf, ch)
writeChar(buf, sc.Next())
hasvalue := false
for isDigit(sc.Peek()) {
writeChar(buf, sc.Next())
hasvalue = true
}
if !hasvalue {
return sc.Error(buf.String(), "illegal hexadecimal number")
}
return nil
} else if sc.Peek() != '.' && isDecimal(sc.Peek()) {
ch = sc.Next()
}
}
sc.scanDecimal(ch, buf)
if sc.Peek() == '.' {
sc.scanDecimal(sc.Next(), buf)
}
if ch = sc.Peek(); ch == 'e' || ch == 'E' {
writeChar(buf, sc.Next())
if ch = sc.Peek(); ch == '-' || ch == '+' {
writeChar(buf, sc.Next())
}
sc.scanDecimal(sc.Next(), buf)
}
return nil
}
func (sc *Scanner) scanString(quote int, buf *bytes.Buffer) error {
ch := sc.Next()
for ch != quote {
if ch == '\n' || ch == '\r' || ch < 0 {
return sc.Error(buf.String(), "unterminated string")
}
if ch == '\\' {
if err := sc.scanEscape(ch, buf); err != nil {
return err
}
} else {
writeChar(buf, ch)
}
ch = sc.Next()
}
return nil
}
func (sc *Scanner) scanEscape(ch int, buf *bytes.Buffer) error {
ch = sc.Next()
switch ch {
case 'a':
buf.WriteByte('\a')
case 'b':
buf.WriteByte('\b')
case 'f':
buf.WriteByte('\f')
case 'n':
buf.WriteByte('\n')
case 'r':
buf.WriteByte('\r')
case 't':
buf.WriteByte('\t')
case 'v':
buf.WriteByte('\v')
case '\\':
buf.WriteByte('\\')
case '"':
buf.WriteByte('"')
case '\'':
buf.WriteByte('\'')
case '\n':
buf.WriteByte('\n')
case '\r':
buf.WriteByte('\n')
sc.Newline('\r')
default:
if '0' <= ch && ch <= '9' {
bytes := []byte{byte(ch)}
for i := 0; i < 2 && isDecimal(sc.Peek()); i++ {
bytes = append(bytes, byte(sc.Next()))
}
val, _ := strconv.ParseInt(string(bytes), 10, 32)
writeChar(buf, int(val))
} else {
writeChar(buf, ch)
}
}
return nil
}
func (sc *Scanner) countSep(ch int) (int, int) {
count := 0
for ; ch == '='; count = count + 1 {
ch = sc.Next()
}
return count, ch
}
func (sc *Scanner) scanMultilineString(ch int, buf *bytes.Buffer) error {
var count1, count2 int
count1, ch = sc.countSep(ch)
if ch != '[' {
return sc.Error(string(rune(ch)), "invalid multiline string")
}
ch = sc.Next()
if ch == '\n' || ch == '\r' {
ch = sc.Next()
}
for {
if ch < 0 {
return sc.Error(buf.String(), "unterminated multiline string")
} else if ch == ']' {
count2, ch = sc.countSep(sc.Next())
if count1 == count2 && ch == ']' {
goto finally
}
buf.WriteByte(']')
buf.WriteString(strings.Repeat("=", count2))
continue
}
writeChar(buf, ch)
ch = sc.Next()
}
finally:
return nil
}
var reservedWords = map[string]int{
"and": TAnd, "break": TBreak, "do": TDo, "else": TElse, "elseif": TElseIf,
"end": TEnd, "false": TFalse, "for": TFor, "function": TFunction,
"if": TIf, "in": TIn, "local": TLocal, "nil": TNil, "not": TNot, "or": TOr,
"return": TReturn, "repeat": TRepeat, "then": TThen, "true": TTrue,
"until": TUntil, "while": TWhile}
func (sc *Scanner) Scan(lexer *Lexer) (ast.Token, error) {
redo:
var err error
tok := ast.Token{}
newline := false
ch := sc.skipWhiteSpace(whitespace1)
if ch == '\n' || ch == '\r' {
newline = true
ch = sc.skipWhiteSpace(whitespace2)
}
if ch == '(' && lexer.PrevTokenType == ')' {
lexer.PNewLine = newline
} else {
lexer.PNewLine = false
}
var _buf bytes.Buffer
buf := &_buf
tok.Pos = sc.Pos
switch {
case isIdent(ch, 0):
tok.Type = TIdent
err = sc.scanIdent(ch, buf)
tok.Str = buf.String()
if err != nil {
goto finally
}
if typ, ok := reservedWords[tok.Str]; ok {
tok.Type = typ
}
case isDecimal(ch):
tok.Type = TNumber
err = sc.scanNumber(ch, buf)
tok.Str = buf.String()
default:
switch ch {
case EOF:
tok.Type = EOF
case '-':
if sc.Peek() == '-' {
err = sc.skipComments(sc.Next())
if err != nil {
goto finally
}
goto redo
} else {
tok.Type = ch
tok.Str = string(rune(ch))
}
case '"', '\'':
tok.Type = TString
err = sc.scanString(ch, buf)
tok.Str = buf.String()
case '[':
if c := sc.Peek(); c == '[' || c == '=' {
tok.Type = TString
err = sc.scanMultilineString(sc.Next(), buf)
tok.Str = buf.String()
} else {
tok.Type = ch
tok.Str = string(rune(ch))
}
case '=':
if sc.Peek() == '=' {
tok.Type = TEqeq
tok.Str = "=="
sc.Next()
} else {
tok.Type = ch
tok.Str = string(rune(ch))
}
case '~':
if sc.Peek() == '=' {
tok.Type = TNeq
tok.Str = "~="
sc.Next()
} else {
err = sc.Error("~", "Invalid '~' token")
}
case '<':
if sc.Peek() == '=' {
tok.Type = TLte
tok.Str = "<="
sc.Next()
} else {
tok.Type = ch
tok.Str = string(rune(ch))
}
case '>':
if sc.Peek() == '=' {
tok.Type = TGte
tok.Str = ">="
sc.Next()
} else {
tok.Type = ch
tok.Str = string(rune(ch))
}
case '.':
ch2 := sc.Peek()
switch {
case isDecimal(ch2):
tok.Type = TNumber
err = sc.scanNumber(ch, buf)
tok.Str = buf.String()
case ch2 == '.':
writeChar(buf, ch)
writeChar(buf, sc.Next())
if sc.Peek() == '.' {
writeChar(buf, sc.Next())
tok.Type = T3Comma
} else {
tok.Type = T2Comma
}
default:
tok.Type = '.'
}
tok.Str = buf.String()
case '+', '*', '/', '%', '^', '#', '(', ')', '{', '}', ']', ';', ':', ',':
tok.Type = ch
tok.Str = string(rune(ch))
default:
writeChar(buf, ch)
err = sc.Error(buf.String(), "Invalid token")
goto finally
}
}
finally:
tok.Name = TokenName(int(tok.Type))
return tok, err
}
// yacc interface {{{
type Lexer struct {
scanner *Scanner
Stmts []ast.Stmt
PNewLine bool
Token ast.Token
PrevTokenType int
}
func (lx *Lexer) Lex(lval *yySymType) int {
lx.PrevTokenType = lx.Token.Type
tok, err := lx.scanner.Scan(lx)
if err != nil {
panic(err)
}
if tok.Type < 0 {
return 0
}
lval.token = tok
lx.Token = tok
return int(tok.Type)
}
func (lx *Lexer) Error(message string) {
panic(lx.scanner.Error(lx.Token.Str, message))
}
func (lx *Lexer) TokenError(tok ast.Token, message string) {
panic(lx.scanner.TokenError(tok, message))
}
func Parse(reader io.Reader, name string) (chunk []ast.Stmt, err error) {
lexer := &Lexer{NewScanner(reader, name), nil, false, ast.Token{Str: ""}, TNil}
chunk = nil
defer func() {
if e := recover(); e != nil {
err, _ = e.(error)
}
}()
yyParse(lexer)
chunk = lexer.Stmts
return
}
// }}}
// Dump {{{
func isInlineDumpNode(rv reflect.Value) bool {
switch rv.Kind() {
case reflect.Struct, reflect.Slice, reflect.Interface, reflect.Ptr:
return false
default:
return true
}
}
func dump(node interface{}, level int, s string) string {
rt := reflect.TypeOf(node)
if fmt.Sprint(rt) == "<nil>" {
return strings.Repeat(s, level) + "<nil>"
}
rv := reflect.ValueOf(node)
buf := []string{}
switch rt.Kind() {
case reflect.Slice:
if rv.Len() == 0 {
return strings.Repeat(s, level) + "<empty>"
}
for i := 0; i < rv.Len(); i++ {
buf = append(buf, dump(rv.Index(i).Interface(), level, s))
}
case reflect.Ptr:
vt := rv.Elem()
tt := rt.Elem()
indicies := []int{}
for i := 0; i < tt.NumField(); i++ {
if strings.Index(tt.Field(i).Name, "Base") > -1 {
continue
}
indicies = append(indicies, i)
}
switch {
case len(indicies) == 0:
return strings.Repeat(s, level) + "<empty>"
case len(indicies) == 1 && isInlineDumpNode(vt.Field(indicies[0])):
for _, i := range indicies {
buf = append(buf, strings.Repeat(s, level)+"- Node$"+tt.Name()+": "+dump(vt.Field(i).Interface(), 0, s))
}
default:
buf = append(buf, strings.Repeat(s, level)+"- Node$"+tt.Name())
for _, i := range indicies {
if isInlineDumpNode(vt.Field(i)) {
inf := dump(vt.Field(i).Interface(), 0, s)
buf = append(buf, strings.Repeat(s, level+1)+tt.Field(i).Name+": "+inf)
} else {
buf = append(buf, strings.Repeat(s, level+1)+tt.Field(i).Name+": ")
buf = append(buf, dump(vt.Field(i).Interface(), level+2, s))
}
}
}
default:
buf = append(buf, strings.Repeat(s, level)+fmt.Sprint(node))
}
return strings.Join(buf, "\n")
}
func Dump(chunk []ast.Stmt) string {
return dump(chunk, 0, " ")
}
// }}

1360
vendor/github.com/yuin/gopher-lua/parse/parser.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

527
vendor/github.com/yuin/gopher-lua/parse/parser.go.y generated vendored Normal file
View File

@ -0,0 +1,527 @@
%{
package parse
import (
"github.com/yuin/gopher-lua/ast"
)
%}
%type<stmts> chunk
%type<stmts> chunk1
%type<stmts> block
%type<stmt> stat
%type<stmts> elseifs
%type<stmt> laststat
%type<funcname> funcname
%type<funcname> funcname1
%type<exprlist> varlist
%type<expr> var
%type<namelist> namelist
%type<exprlist> exprlist
%type<expr> expr
%type<expr> string
%type<expr> prefixexp
%type<expr> functioncall
%type<expr> afunctioncall
%type<exprlist> args
%type<expr> function
%type<funcexpr> funcbody
%type<parlist> parlist
%type<expr> tableconstructor
%type<fieldlist> fieldlist
%type<field> field
%type<fieldsep> fieldsep
%union {
token ast.Token
stmts []ast.Stmt
stmt ast.Stmt
funcname *ast.FuncName
funcexpr *ast.FunctionExpr
exprlist []ast.Expr
expr ast.Expr
fieldlist []*ast.Field
field *ast.Field
fieldsep string
namelist []string
parlist *ast.ParList
}
/* Reserved words */
%token<token> TAnd TBreak TDo TElse TElseIf TEnd TFalse TFor TFunction TIf TIn TLocal TNil TNot TOr TReturn TRepeat TThen TTrue TUntil TWhile
/* Literals */
%token<token> TEqeq TNeq TLte TGte T2Comma T3Comma TIdent TNumber TString '{' '('
/* Operators */
%left TOr
%left TAnd
%left '>' '<' TGte TLte TEqeq TNeq
%right T2Comma
%left '+' '-'
%left '*' '/' '%'
%right UNARY /* not # -(unary) */
%right '^'
%%
chunk:
chunk1 {
$$ = $1
if l, ok := yylex.(*Lexer); ok {
l.Stmts = $$
}
} |
chunk1 laststat {
$$ = append($1, $2)
if l, ok := yylex.(*Lexer); ok {
l.Stmts = $$
}
} |
chunk1 laststat ';' {
$$ = append($1, $2)
if l, ok := yylex.(*Lexer); ok {
l.Stmts = $$
}
}
chunk1:
{
$$ = []ast.Stmt{}
} |
chunk1 stat {
$$ = append($1, $2)
} |
chunk1 ';' {
$$ = $1
}
block:
chunk {
$$ = $1
}
stat:
varlist '=' exprlist {
$$ = &ast.AssignStmt{Lhs: $1, Rhs: $3}
$$.SetLine($1[0].Line())
} |
/* 'stat = functioncal' causes a reduce/reduce conflict */
prefixexp {
if _, ok := $1.(*ast.FuncCallExpr); !ok {
yylex.(*Lexer).Error("parse error")
} else {
$$ = &ast.FuncCallStmt{Expr: $1}
$$.SetLine($1.Line())
}
} |
TDo block TEnd {
$$ = &ast.DoBlockStmt{Stmts: $2}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($3.Pos.Line)
} |
TWhile expr TDo block TEnd {
$$ = &ast.WhileStmt{Condition: $2, Stmts: $4}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($5.Pos.Line)
} |
TRepeat block TUntil expr {
$$ = &ast.RepeatStmt{Condition: $4, Stmts: $2}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($4.Line())
} |
TIf expr TThen block elseifs TEnd {
$$ = &ast.IfStmt{Condition: $2, Then: $4}
cur := $$
for _, elseif := range $5 {
cur.(*ast.IfStmt).Else = []ast.Stmt{elseif}
cur = elseif
}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($6.Pos.Line)
} |
TIf expr TThen block elseifs TElse block TEnd {
$$ = &ast.IfStmt{Condition: $2, Then: $4}
cur := $$
for _, elseif := range $5 {
cur.(*ast.IfStmt).Else = []ast.Stmt{elseif}
cur = elseif
}
cur.(*ast.IfStmt).Else = $7
$$.SetLine($1.Pos.Line)
$$.SetLastLine($8.Pos.Line)
} |
TFor TIdent '=' expr ',' expr TDo block TEnd {
$$ = &ast.NumberForStmt{Name: $2.Str, Init: $4, Limit: $6, Stmts: $8}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($9.Pos.Line)
} |
TFor TIdent '=' expr ',' expr ',' expr TDo block TEnd {
$$ = &ast.NumberForStmt{Name: $2.Str, Init: $4, Limit: $6, Step:$8, Stmts: $10}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($11.Pos.Line)
} |
TFor namelist TIn exprlist TDo block TEnd {
$$ = &ast.GenericForStmt{Names:$2, Exprs:$4, Stmts: $6}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($7.Pos.Line)
} |
TFunction funcname funcbody {
$$ = &ast.FuncDefStmt{Name: $2, Func: $3}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($3.LastLine())
} |
TLocal TFunction TIdent funcbody {
$$ = &ast.LocalAssignStmt{Names:[]string{$3.Str}, Exprs: []ast.Expr{$4}}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($4.LastLine())
} |
TLocal namelist '=' exprlist {
$$ = &ast.LocalAssignStmt{Names: $2, Exprs:$4}
$$.SetLine($1.Pos.Line)
} |
TLocal namelist {
$$ = &ast.LocalAssignStmt{Names: $2, Exprs:[]ast.Expr{}}
$$.SetLine($1.Pos.Line)
}
elseifs:
{
$$ = []ast.Stmt{}
} |
elseifs TElseIf expr TThen block {
$$ = append($1, &ast.IfStmt{Condition: $3, Then: $5})
$$[len($$)-1].SetLine($2.Pos.Line)
}
laststat:
TReturn {
$$ = &ast.ReturnStmt{Exprs:nil}
$$.SetLine($1.Pos.Line)
} |
TReturn exprlist {
$$ = &ast.ReturnStmt{Exprs:$2}
$$.SetLine($1.Pos.Line)
} |
TBreak {
$$ = &ast.BreakStmt{}
$$.SetLine($1.Pos.Line)
}
funcname:
funcname1 {
$$ = $1
} |
funcname1 ':' TIdent {
$$ = &ast.FuncName{Func:nil, Receiver:$1.Func, Method: $3.Str}
}
funcname1:
TIdent {
$$ = &ast.FuncName{Func: &ast.IdentExpr{Value:$1.Str}}
$$.Func.SetLine($1.Pos.Line)
} |
funcname1 '.' TIdent {
key:= &ast.StringExpr{Value:$3.Str}
key.SetLine($3.Pos.Line)
fn := &ast.AttrGetExpr{Object: $1.Func, Key: key}
fn.SetLine($3.Pos.Line)
$$ = &ast.FuncName{Func: fn}
}
varlist:
var {
$$ = []ast.Expr{$1}
} |
varlist ',' var {
$$ = append($1, $3)
}
var:
TIdent {
$$ = &ast.IdentExpr{Value:$1.Str}
$$.SetLine($1.Pos.Line)
} |
prefixexp '[' expr ']' {
$$ = &ast.AttrGetExpr{Object: $1, Key: $3}
$$.SetLine($1.Line())
} |
prefixexp '.' TIdent {
key := &ast.StringExpr{Value:$3.Str}
key.SetLine($3.Pos.Line)
$$ = &ast.AttrGetExpr{Object: $1, Key: key}
$$.SetLine($1.Line())
}
namelist:
TIdent {
$$ = []string{$1.Str}
} |
namelist ',' TIdent {
$$ = append($1, $3.Str)
}
exprlist:
expr {
$$ = []ast.Expr{$1}
} |
exprlist ',' expr {
$$ = append($1, $3)
}
expr:
TNil {
$$ = &ast.NilExpr{}
$$.SetLine($1.Pos.Line)
} |
TFalse {
$$ = &ast.FalseExpr{}
$$.SetLine($1.Pos.Line)
} |
TTrue {
$$ = &ast.TrueExpr{}
$$.SetLine($1.Pos.Line)
} |
TNumber {
$$ = &ast.NumberExpr{Value: $1.Str}
$$.SetLine($1.Pos.Line)
} |
T3Comma {
$$ = &ast.Comma3Expr{}
$$.SetLine($1.Pos.Line)
} |
function {
$$ = $1
} |
prefixexp {
$$ = $1
} |
string {
$$ = $1
} |
tableconstructor {
$$ = $1
} |
expr TOr expr {
$$ = &ast.LogicalOpExpr{Lhs: $1, Operator: "or", Rhs: $3}
$$.SetLine($1.Line())
} |
expr TAnd expr {
$$ = &ast.LogicalOpExpr{Lhs: $1, Operator: "and", Rhs: $3}
$$.SetLine($1.Line())
} |
expr '>' expr {
$$ = &ast.RelationalOpExpr{Lhs: $1, Operator: ">", Rhs: $3}
$$.SetLine($1.Line())
} |
expr '<' expr {
$$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "<", Rhs: $3}
$$.SetLine($1.Line())
} |
expr TGte expr {
$$ = &ast.RelationalOpExpr{Lhs: $1, Operator: ">=", Rhs: $3}
$$.SetLine($1.Line())
} |
expr TLte expr {
$$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "<=", Rhs: $3}
$$.SetLine($1.Line())
} |
expr TEqeq expr {
$$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "==", Rhs: $3}
$$.SetLine($1.Line())
} |
expr TNeq expr {
$$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "~=", Rhs: $3}
$$.SetLine($1.Line())
} |
expr T2Comma expr {
$$ = &ast.StringConcatOpExpr{Lhs: $1, Rhs: $3}
$$.SetLine($1.Line())
} |
expr '+' expr {
$$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "+", Rhs: $3}
$$.SetLine($1.Line())
} |
expr '-' expr {
$$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "-", Rhs: $3}
$$.SetLine($1.Line())
} |
expr '*' expr {
$$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "*", Rhs: $3}
$$.SetLine($1.Line())
} |
expr '/' expr {
$$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "/", Rhs: $3}
$$.SetLine($1.Line())
} |
expr '%' expr {
$$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "%", Rhs: $3}
$$.SetLine($1.Line())
} |
expr '^' expr {
$$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "^", Rhs: $3}
$$.SetLine($1.Line())
} |
'-' expr %prec UNARY {
$$ = &ast.UnaryMinusOpExpr{Expr: $2}
$$.SetLine($2.Line())
} |
TNot expr %prec UNARY {
$$ = &ast.UnaryNotOpExpr{Expr: $2}
$$.SetLine($2.Line())
} |
'#' expr %prec UNARY {
$$ = &ast.UnaryLenOpExpr{Expr: $2}
$$.SetLine($2.Line())
}
string:
TString {
$$ = &ast.StringExpr{Value: $1.Str}
$$.SetLine($1.Pos.Line)
}
prefixexp:
var {
$$ = $1
} |
afunctioncall {
$$ = $1
} |
functioncall {
$$ = $1
} |
'(' expr ')' {
if ex, ok := $2.(*ast.Comma3Expr); ok {
ex.AdjustRet = true
}
$$ = $2
$$.SetLine($1.Pos.Line)
}
afunctioncall:
'(' functioncall ')' {
$2.(*ast.FuncCallExpr).AdjustRet = true
$$ = $2
}
functioncall:
prefixexp args {
$$ = &ast.FuncCallExpr{Func: $1, Args: $2}
$$.SetLine($1.Line())
} |
prefixexp ':' TIdent args {
$$ = &ast.FuncCallExpr{Method: $3.Str, Receiver: $1, Args: $4}
$$.SetLine($1.Line())
}
args:
'(' ')' {
if yylex.(*Lexer).PNewLine {
yylex.(*Lexer).TokenError($1, "ambiguous syntax (function call x new statement)")
}
$$ = []ast.Expr{}
} |
'(' exprlist ')' {
if yylex.(*Lexer).PNewLine {
yylex.(*Lexer).TokenError($1, "ambiguous syntax (function call x new statement)")
}
$$ = $2
} |
tableconstructor {
$$ = []ast.Expr{$1}
} |
string {
$$ = []ast.Expr{$1}
}
function:
TFunction funcbody {
$$ = &ast.FunctionExpr{ParList:$2.ParList, Stmts: $2.Stmts}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($2.LastLine())
}
funcbody:
'(' parlist ')' block TEnd {
$$ = &ast.FunctionExpr{ParList: $2, Stmts: $4}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($5.Pos.Line)
} |
'(' ')' block TEnd {
$$ = &ast.FunctionExpr{ParList: &ast.ParList{HasVargs: false, Names: []string{}}, Stmts: $3}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($4.Pos.Line)
}
parlist:
T3Comma {
$$ = &ast.ParList{HasVargs: true, Names: []string{}}
} |
namelist {
$$ = &ast.ParList{HasVargs: false, Names: []string{}}
$$.Names = append($$.Names, $1...)
} |
namelist ',' T3Comma {
$$ = &ast.ParList{HasVargs: true, Names: []string{}}
$$.Names = append($$.Names, $1...)
}
tableconstructor:
'{' '}' {
$$ = &ast.TableExpr{Fields: []*ast.Field{}}
$$.SetLine($1.Pos.Line)
} |
'{' fieldlist '}' {
$$ = &ast.TableExpr{Fields: $2}
$$.SetLine($1.Pos.Line)
}
fieldlist:
field {
$$ = []*ast.Field{$1}
} |
fieldlist fieldsep field {
$$ = append($1, $3)
} |
fieldlist fieldsep {
$$ = $1
}
field:
TIdent '=' expr {
$$ = &ast.Field{Key: &ast.StringExpr{Value:$1.Str}, Value: $3}
$$.Key.SetLine($1.Pos.Line)
} |
'[' expr ']' '=' expr {
$$ = &ast.Field{Key: $2, Value: $5}
} |
expr {
$$ = &ast.Field{Value: $1}
}
fieldsep:
',' {
$$ = ","
} |
';' {
$$ = ";"
}
%%
func TokenName(c int) string {
if c >= TAnd && c-TAnd < len(yyToknames) {
if yyToknames[c-TAnd] != "" {
return yyToknames[c-TAnd]
}
}
return string([]byte{byte(c)})
}

638
vendor/github.com/yuin/gopher-lua/pm/pm.go generated vendored Normal file
View File

@ -0,0 +1,638 @@
// Lua pattern match functions for Go
package pm
import (
"fmt"
)
const EOS = -1
const _UNKNOWN = -2
/* Error {{{ */
type Error struct {
Pos int
Message string
}
func newError(pos int, message string, args ...interface{}) *Error {
if len(args) == 0 {
return &Error{pos, message}
}
return &Error{pos, fmt.Sprintf(message, args...)}
}
func (e *Error) Error() string {
switch e.Pos {
case EOS:
return fmt.Sprintf("%s at EOS", e.Message)
case _UNKNOWN:
return fmt.Sprintf("%s", e.Message)
default:
return fmt.Sprintf("%s at %d", e.Message, e.Pos)
}
}
/* }}} */
/* MatchData {{{ */
type MatchData struct {
// captured positions
// layout
// xxxx xxxx xxxx xxx0 : caputured positions
// xxxx xxxx xxxx xxx1 : position captured positions
captures []uint32
}
func newMatchState() *MatchData { return &MatchData{[]uint32{}} }
func (st *MatchData) addPosCapture(s, pos int) {
for s+1 >= len(st.captures) {
st.captures = append(st.captures, 0)
}
st.captures[s] = (uint32(pos) << 1) | 1
st.captures[s+1] = (uint32(pos) << 1) | 1
}
func (st *MatchData) setCapture(s, pos int) uint32 {
for s >= len(st.captures) {
st.captures = append(st.captures, 0)
}
v := st.captures[s]
st.captures[s] = (uint32(pos) << 1)
return v
}
func (st *MatchData) restoreCapture(s int, pos uint32) { st.captures[s] = pos }
func (st *MatchData) CaptureLength() int { return len(st.captures) }
func (st *MatchData) IsPosCapture(idx int) bool { return (st.captures[idx] & 1) == 1 }
func (st *MatchData) Capture(idx int) int { return int(st.captures[idx] >> 1) }
/* }}} */
/* scanner {{{ */
type scannerState struct {
Pos int
started bool
}
type scanner struct {
src []byte
State scannerState
saved scannerState
}
func newScanner(src []byte) *scanner {
return &scanner{
src: src,
State: scannerState{
Pos: 0,
started: false,
},
saved: scannerState{},
}
}
func (sc *scanner) Length() int { return len(sc.src) }
func (sc *scanner) Next() int {
if !sc.State.started {
sc.State.started = true
if len(sc.src) == 0 {
sc.State.Pos = EOS
}
} else {
sc.State.Pos = sc.NextPos()
}
if sc.State.Pos == EOS {
return EOS
}
return int(sc.src[sc.State.Pos])
}
func (sc *scanner) CurrentPos() int {
return sc.State.Pos
}
func (sc *scanner) NextPos() int {
if sc.State.Pos == EOS || sc.State.Pos >= len(sc.src)-1 {
return EOS
}
if !sc.State.started {
return 0
} else {
return sc.State.Pos + 1
}
}
func (sc *scanner) Peek() int {
cureof := sc.State.Pos == EOS
ch := sc.Next()
if !cureof {
if sc.State.Pos == EOS {
sc.State.Pos = len(sc.src) - 1
} else {
sc.State.Pos--
if sc.State.Pos < 0 {
sc.State.Pos = 0
sc.State.started = false
}
}
}
return ch
}
func (sc *scanner) Save() { sc.saved = sc.State }
func (sc *scanner) Restore() { sc.State = sc.saved }
/* }}} */
/* bytecode {{{ */
type opCode int
const (
opChar opCode = iota
opMatch
opTailMatch
opJmp
opSplit
opSave
opPSave
opBrace
opNumber
)
type inst struct {
OpCode opCode
Class class
Operand1 int
Operand2 int
}
/* }}} */
/* classes {{{ */
type class interface {
Matches(ch int) bool
}
type dotClass struct{}
func (pn *dotClass) Matches(ch int) bool { return true }
type charClass struct {
Ch int
}
func (pn *charClass) Matches(ch int) bool { return pn.Ch == ch }
type singleClass struct {
Class int
}
func (pn *singleClass) Matches(ch int) bool {
ret := false
switch pn.Class {
case 'a', 'A':
ret = 'A' <= ch && ch <= 'Z' || 'a' <= ch && ch <= 'z'
case 'c', 'C':
ret = (0x00 <= ch && ch <= 0x1F) || ch == 0x7F
case 'd', 'D':
ret = '0' <= ch && ch <= '9'
case 'l', 'L':
ret = 'a' <= ch && ch <= 'z'
case 'p', 'P':
ret = (0x21 <= ch && ch <= 0x2f) || (0x3a <= ch && ch <= 0x40) || (0x5b <= ch && ch <= 0x60) || (0x7b <= ch && ch <= 0x7e)
case 's', 'S':
switch ch {
case ' ', '\f', '\n', '\r', '\t', '\v':
ret = true
}
case 'u', 'U':
ret = 'A' <= ch && ch <= 'Z'
case 'w', 'W':
ret = '0' <= ch && ch <= '9' || 'A' <= ch && ch <= 'Z' || 'a' <= ch && ch <= 'z'
case 'x', 'X':
ret = '0' <= ch && ch <= '9' || 'a' <= ch && ch <= 'f' || 'A' <= ch && ch <= 'F'
case 'z', 'Z':
ret = ch == 0
default:
return ch == pn.Class
}
if 'A' <= pn.Class && pn.Class <= 'Z' {
return !ret
}
return ret
}
type setClass struct {
IsNot bool
Classes []class
}
func (pn *setClass) Matches(ch int) bool {
for _, class := range pn.Classes {
if class.Matches(ch) {
return !pn.IsNot
}
}
return pn.IsNot
}
type rangeClass struct {
Begin class
End class
}
func (pn *rangeClass) Matches(ch int) bool {
switch begin := pn.Begin.(type) {
case *charClass:
end, ok := pn.End.(*charClass)
if !ok {
return false
}
return begin.Ch <= ch && ch <= end.Ch
}
return false
}
// }}}
// patterns {{{
type pattern interface{}
type singlePattern struct {
Class class
}
type seqPattern struct {
MustHead bool
MustTail bool
Patterns []pattern
}
type repeatPattern struct {
Type int
Class class
}
type posCapPattern struct{}
type capPattern struct {
Pattern pattern
}
type numberPattern struct {
N int
}
type bracePattern struct {
Begin int
End int
}
// }}}
/* parse {{{ */
func parseClass(sc *scanner, allowset bool) class {
ch := sc.Next()
switch ch {
case '%':
return &singleClass{sc.Next()}
case '.':
if allowset {
return &dotClass{}
}
return &charClass{ch}
case '[':
if allowset {
return parseClassSet(sc)
}
return &charClass{ch}
//case '^' '$', '(', ')', ']', '*', '+', '-', '?':
// panic(newError(sc.CurrentPos(), "invalid %c", ch))
case EOS:
panic(newError(sc.CurrentPos(), "unexpected EOS"))
default:
return &charClass{ch}
}
}
func parseClassSet(sc *scanner) class {
set := &setClass{false, []class{}}
if sc.Peek() == '^' {
set.IsNot = true
sc.Next()
}
isrange := false
for {
ch := sc.Peek()
switch ch {
// case '[':
// panic(newError(sc.CurrentPos(), "'[' can not be nested"))
case EOS:
panic(newError(sc.CurrentPos(), "unexpected EOS"))
case ']':
if len(set.Classes) > 0 {
sc.Next()
goto exit
}
fallthrough
case '-':
if len(set.Classes) > 0 {
sc.Next()
isrange = true
continue
}
fallthrough
default:
set.Classes = append(set.Classes, parseClass(sc, false))
}
if isrange {
begin := set.Classes[len(set.Classes)-2]
end := set.Classes[len(set.Classes)-1]
set.Classes = set.Classes[0 : len(set.Classes)-2]
set.Classes = append(set.Classes, &rangeClass{begin, end})
isrange = false
}
}
exit:
if isrange {
set.Classes = append(set.Classes, &charClass{'-'})
}
return set
}
func parsePattern(sc *scanner, toplevel bool) *seqPattern {
pat := &seqPattern{}
if toplevel {
if sc.Peek() == '^' {
sc.Next()
pat.MustHead = true
}
}
for {
ch := sc.Peek()
switch ch {
case '%':
sc.Save()
sc.Next()
switch sc.Peek() {
case '0':
panic(newError(sc.CurrentPos(), "invalid capture index"))
case '1', '2', '3', '4', '5', '6', '7', '8', '9':
pat.Patterns = append(pat.Patterns, &numberPattern{sc.Next() - 48})
case 'b':
sc.Next()
pat.Patterns = append(pat.Patterns, &bracePattern{sc.Next(), sc.Next()})
default:
sc.Restore()
pat.Patterns = append(pat.Patterns, &singlePattern{parseClass(sc, true)})
}
case '.', '[', ']':
pat.Patterns = append(pat.Patterns, &singlePattern{parseClass(sc, true)})
//case ']':
// panic(newError(sc.CurrentPos(), "invalid ']'"))
case ')':
if toplevel {
panic(newError(sc.CurrentPos(), "invalid ')'"))
}
return pat
case '(':
sc.Next()
if sc.Peek() == ')' {
sc.Next()
pat.Patterns = append(pat.Patterns, &posCapPattern{})
} else {
ret := &capPattern{parsePattern(sc, false)}
if sc.Peek() != ')' {
panic(newError(sc.CurrentPos(), "unfinished capture"))
}
sc.Next()
pat.Patterns = append(pat.Patterns, ret)
}
case '*', '+', '-', '?':
sc.Next()
if len(pat.Patterns) > 0 {
spat, ok := pat.Patterns[len(pat.Patterns)-1].(*singlePattern)
if ok {
pat.Patterns = pat.Patterns[0 : len(pat.Patterns)-1]
pat.Patterns = append(pat.Patterns, &repeatPattern{ch, spat.Class})
continue
}
}
pat.Patterns = append(pat.Patterns, &singlePattern{&charClass{ch}})
case '$':
if toplevel && (sc.NextPos() == sc.Length()-1 || sc.NextPos() == EOS) {
pat.MustTail = true
} else {
pat.Patterns = append(pat.Patterns, &singlePattern{&charClass{ch}})
}
sc.Next()
case EOS:
sc.Next()
goto exit
default:
sc.Next()
pat.Patterns = append(pat.Patterns, &singlePattern{&charClass{ch}})
}
}
exit:
return pat
}
type iptr struct {
insts []inst
capture int
}
func compilePattern(p pattern, ps ...*iptr) []inst {
var ptr *iptr
toplevel := false
if len(ps) == 0 {
toplevel = true
ptr = &iptr{[]inst{inst{opSave, nil, 0, -1}}, 2}
} else {
ptr = ps[0]
}
switch pat := p.(type) {
case *singlePattern:
ptr.insts = append(ptr.insts, inst{opChar, pat.Class, -1, -1})
case *seqPattern:
for _, cp := range pat.Patterns {
compilePattern(cp, ptr)
}
case *repeatPattern:
idx := len(ptr.insts)
switch pat.Type {
case '*':
ptr.insts = append(ptr.insts,
inst{opSplit, nil, idx + 1, idx + 3},
inst{opChar, pat.Class, -1, -1},
inst{opJmp, nil, idx, -1})
case '+':
ptr.insts = append(ptr.insts,
inst{opChar, pat.Class, -1, -1},
inst{opSplit, nil, idx, idx + 2})
case '-':
ptr.insts = append(ptr.insts,
inst{opSplit, nil, idx + 3, idx + 1},
inst{opChar, pat.Class, -1, -1},
inst{opJmp, nil, idx, -1})
case '?':
ptr.insts = append(ptr.insts,
inst{opSplit, nil, idx + 1, idx + 2},
inst{opChar, pat.Class, -1, -1})
}
case *posCapPattern:
ptr.insts = append(ptr.insts, inst{opPSave, nil, ptr.capture, -1})
ptr.capture += 2
case *capPattern:
c0, c1 := ptr.capture, ptr.capture+1
ptr.capture += 2
ptr.insts = append(ptr.insts, inst{opSave, nil, c0, -1})
compilePattern(pat.Pattern, ptr)
ptr.insts = append(ptr.insts, inst{opSave, nil, c1, -1})
case *bracePattern:
ptr.insts = append(ptr.insts, inst{opBrace, nil, pat.Begin, pat.End})
case *numberPattern:
ptr.insts = append(ptr.insts, inst{opNumber, nil, pat.N, -1})
}
if toplevel {
if p.(*seqPattern).MustTail {
ptr.insts = append(ptr.insts, inst{opSave, nil, 1, -1}, inst{opTailMatch, nil, -1, -1})
}
ptr.insts = append(ptr.insts, inst{opSave, nil, 1, -1}, inst{opMatch, nil, -1, -1})
}
return ptr.insts
}
/* }}} parse */
/* VM {{{ */
// Simple recursive virtual machine based on the
// "Regular Expression Matching: the Virtual Machine Approach" (https://swtch.com/~rsc/regexp/regexp2.html)
func recursiveVM(src []byte, insts []inst, pc, sp int, ms ...*MatchData) (bool, int, *MatchData) {
var m *MatchData
if len(ms) == 0 {
m = newMatchState()
} else {
m = ms[0]
}
redo:
inst := insts[pc]
switch inst.OpCode {
case opChar:
if sp >= len(src) || !inst.Class.Matches(int(src[sp])) {
return false, sp, m
}
pc++
sp++
goto redo
case opMatch:
return true, sp, m
case opTailMatch:
return sp >= len(src), sp, m
case opJmp:
pc = inst.Operand1
goto redo
case opSplit:
if ok, nsp, _ := recursiveVM(src, insts, inst.Operand1, sp, m); ok {
return true, nsp, m
}
pc = inst.Operand2
goto redo
case opSave:
s := m.setCapture(inst.Operand1, sp)
if ok, nsp, _ := recursiveVM(src, insts, pc+1, sp, m); ok {
return true, nsp, m
}
m.restoreCapture(inst.Operand1, s)
return false, sp, m
case opPSave:
m.addPosCapture(inst.Operand1, sp+1)
pc++
goto redo
case opBrace:
if sp >= len(src) || int(src[sp]) != inst.Operand1 {
return false, sp, m
}
count := 1
for sp = sp + 1; sp < len(src); sp++ {
if int(src[sp]) == inst.Operand2 {
count--
}
if count == 0 {
pc++
sp++
goto redo
}
if int(src[sp]) == inst.Operand1 {
count++
}
}
return false, sp, m
case opNumber:
idx := inst.Operand1 * 2
if idx >= m.CaptureLength()-1 {
panic(newError(_UNKNOWN, "invalid capture index"))
}
capture := src[m.Capture(idx):m.Capture(idx+1)]
for i := 0; i < len(capture); i++ {
if i+sp >= len(src) || capture[i] != src[i+sp] {
return false, sp, m
}
}
pc++
sp += len(capture)
goto redo
}
panic("should not reach here")
}
/* }}} */
/* API {{{ */
func Find(p string, src []byte, offset, limit int) (matches []*MatchData, err error) {
defer func() {
if v := recover(); v != nil {
if perr, ok := v.(*Error); ok {
err = perr
} else {
panic(v)
}
}
}()
pat := parsePattern(newScanner([]byte(p)), true)
insts := compilePattern(pat)
matches = []*MatchData{}
for sp := offset; sp <= len(src); {
ok, nsp, ms := recursiveVM(src, insts, 0, sp)
sp++
if ok {
if sp < nsp {
sp = nsp
}
matches = append(matches, ms)
}
if len(matches) == limit || pat.MustHead {
break
}
}
return
}
/* }}} */

2244
vendor/github.com/yuin/gopher-lua/state.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

448
vendor/github.com/yuin/gopher-lua/stringlib.go generated vendored Normal file
View File

@ -0,0 +1,448 @@
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
}
//

387
vendor/github.com/yuin/gopher-lua/table.go generated vendored Normal file
View File

@ -0,0 +1,387 @@
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 := &LTable{}
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 without using __len.
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 value == LNil {
return
}
if tb.array == nil {
tb.array = make([]LValue, 0, defaultArrayCap)
}
if len(tb.array) == 0 || tb.array[len(tb.array)-1] != LNil {
tb.array = append(tb.array, value)
} else {
i := len(tb.array) - 2
for ; i >= 0; i-- {
if tb.array[i] != LNil {
break
}
}
tb.array[i+1] = 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
}
larray := len(tb.array)
if larray == 0 {
return LNil
}
i := pos - 1
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
}

100
vendor/github.com/yuin/gopher-lua/tablelib.go generated vendored Normal file
View File

@ -0,0 +1,100 @@
package lua
import (
"sort"
)
func OpenTable(L *LState) int {
tabmod := L.RegisterModule(TabLibName, tableFuncs)
L.Push(tabmod)
return 1
}
var tableFuncs = map[string]LGFunction{
"getn": tableGetN,
"concat": tableConcat,
"insert": tableInsert,
"maxn": tableMaxN,
"remove": tableRemove,
"sort": tableSort,
}
func tableSort(L *LState) int {
tbl := L.CheckTable(1)
sorter := lValueArraySorter{L, nil, tbl.array}
if L.GetTop() != 1 {
sorter.Fn = L.CheckFunction(2)
}
sort.Sort(sorter)
return 0
}
func tableGetN(L *LState) int {
L.Push(LNumber(L.CheckTable(1).Len()))
return 1
}
func tableMaxN(L *LState) int {
L.Push(LNumber(L.CheckTable(1).MaxN()))
return 1
}
func tableRemove(L *LState) int {
tbl := L.CheckTable(1)
if L.GetTop() == 1 {
L.Push(tbl.Remove(-1))
} else {
L.Push(tbl.Remove(L.CheckInt(2)))
}
return 1
}
func tableConcat(L *LState) int {
tbl := L.CheckTable(1)
sep := LString(L.OptString(2, ""))
i := L.OptInt(3, 1)
j := L.OptInt(4, tbl.Len())
if L.GetTop() == 3 {
if i > tbl.Len() || i < 1 {
L.Push(emptyLString)
return 1
}
}
i = intMax(intMin(i, tbl.Len()), 1)
j = intMin(intMin(j, tbl.Len()), tbl.Len())
if i > j {
L.Push(emptyLString)
return 1
}
//TODO should flushing?
retbottom := L.GetTop()
for ; i <= j; i++ {
v := tbl.RawGetInt(i)
if !LVCanConvToString(v) {
L.RaiseError("invalid value (%s) at index %d in table for concat", v.Type().String(), i)
}
L.Push(v)
if i != j {
L.Push(sep)
}
}
L.Push(stringConcat(L, L.GetTop()-retbottom, L.reg.Top()-1))
return 1
}
func tableInsert(L *LState) int {
tbl := L.CheckTable(1)
nargs := L.GetTop()
if nargs == 1 {
L.RaiseError("wrong number of arguments")
}
if L.GetTop() == 2 {
tbl.Append(L.Get(2))
return 0
}
tbl.Insert(int(L.CheckInt(2)), L.CheckAny(3))
return 0
}
//

265
vendor/github.com/yuin/gopher-lua/utils.go generated vendored Normal file
View File

@ -0,0 +1,265 @@
package lua
import (
"bufio"
"fmt"
"io"
"reflect"
"strconv"
"strings"
"time"
"unsafe"
)
func intMin(a, b int) int {
if a < b {
return a
} else {
return b
}
}
func intMax(a, b int) int {
if a > b {
return a
} else {
return b
}
}
func defaultFormat(v interface{}, f fmt.State, c rune) {
buf := make([]string, 0, 10)
buf = append(buf, "%")
for i := 0; i < 128; i++ {
if f.Flag(i) {
buf = append(buf, string(rune(i)))
}
}
if w, ok := f.Width(); ok {
buf = append(buf, strconv.Itoa(w))
}
if p, ok := f.Precision(); ok {
buf = append(buf, "."+strconv.Itoa(p))
}
buf = append(buf, string(c))
format := strings.Join(buf, "")
fmt.Fprintf(f, format, v)
}
type flagScanner struct {
flag byte
start string
end string
buf []byte
str string
Length int
Pos int
HasFlag bool
ChangeFlag bool
}
func newFlagScanner(flag byte, start, end, str string) *flagScanner {
return &flagScanner{flag, start, end, make([]byte, 0, len(str)), str, len(str), 0, false, false}
}
func (fs *flagScanner) AppendString(str string) { fs.buf = append(fs.buf, str...) }
func (fs *flagScanner) AppendChar(ch byte) { fs.buf = append(fs.buf, ch) }
func (fs *flagScanner) String() string { return string(fs.buf) }
func (fs *flagScanner) Next() (byte, bool) {
c := byte('\000')
fs.ChangeFlag = false
if fs.Pos == fs.Length {
if fs.HasFlag {
fs.AppendString(fs.end)
}
return c, true
} else {
c = fs.str[fs.Pos]
if c == fs.flag {
if fs.Pos < (fs.Length-1) && fs.str[fs.Pos+1] == fs.flag {
fs.HasFlag = false
fs.AppendChar(fs.flag)
fs.Pos += 2
return fs.Next()
} else if fs.Pos != fs.Length-1 {
if fs.HasFlag {
fs.AppendString(fs.end)
}
fs.AppendString(fs.start)
fs.ChangeFlag = true
fs.HasFlag = true
}
}
}
fs.Pos++
return c, false
}
var cDateFlagToGo = map[byte]string{
'a': "mon", 'A': "Monday", 'b': "Jan", 'B': "January", 'c': "02 Jan 06 15:04 MST", 'd': "02",
'F': "2006-01-02", 'H': "15", 'I': "03", 'm': "01", 'M': "04", 'p': "PM", 'P': "pm", 'S': "05",
'x': "15/04/05", 'X': "15:04:05", 'y': "06", 'Y': "2006", 'z': "-0700", 'Z': "MST"}
func strftime(t time.Time, cfmt string) string {
sc := newFlagScanner('%', "", "", cfmt)
for c, eos := sc.Next(); !eos; c, eos = sc.Next() {
if !sc.ChangeFlag {
if sc.HasFlag {
if v, ok := cDateFlagToGo[c]; ok {
sc.AppendString(t.Format(v))
} else {
switch c {
case 'w':
sc.AppendString(fmt.Sprint(int(t.Weekday())))
default:
sc.AppendChar('%')
sc.AppendChar(c)
}
}
sc.HasFlag = false
} else {
sc.AppendChar(c)
}
}
}
return sc.String()
}
func isInteger(v LNumber) bool {
return float64(v) == float64(int64(v))
//_, frac := math.Modf(float64(v))
//return frac == 0.0
}
func isArrayKey(v LNumber) bool {
return isInteger(v) && v < LNumber(int((^uint(0))>>1)) && v > LNumber(0) && v < LNumber(MaxArrayIndex)
}
func parseNumber(number string) (LNumber, error) {
var value LNumber
number = strings.Trim(number, " \t\n")
if v, err := strconv.ParseInt(number, 0, LNumberBit); err != nil {
if v2, err2 := strconv.ParseFloat(number, LNumberBit); err2 != nil {
return LNumber(0), err2
} else {
value = LNumber(v2)
}
} else {
value = LNumber(v)
}
return value, nil
}
func popenArgs(arg string) (string, []string) {
cmd := "/bin/sh"
args := []string{"-c"}
if LuaOS == "windows" {
cmd = "C:\\Windows\\system32\\cmd.exe"
args = []string{"/c"}
}
args = append(args, arg)
return cmd, args
}
func isGoroutineSafe(lv LValue) bool {
switch v := lv.(type) {
case *LFunction, *LUserData, *LState:
return false
case *LTable:
return v.Metatable == LNil
default:
return true
}
}
func readBufioSize(reader *bufio.Reader, size int64) ([]byte, error, bool) {
result := []byte{}
read := int64(0)
var err error
var n int
for read != size {
buf := make([]byte, size-read)
n, err = reader.Read(buf)
if err != nil {
break
}
read += int64(n)
result = append(result, buf[:n]...)
}
e := err
if e != nil && e == io.EOF {
e = nil
}
return result, e, len(result) == 0 && err == io.EOF
}
func readBufioLine(reader *bufio.Reader) ([]byte, error, bool) {
result := []byte{}
var buf []byte
var err error
var isprefix bool = true
for isprefix {
buf, isprefix, err = reader.ReadLine()
if err != nil {
break
}
result = append(result, buf...)
}
e := err
if e != nil && e == io.EOF {
e = nil
}
return result, e, len(result) == 0 && err == io.EOF
}
func int2Fb(val int) int {
e := 0
x := val
for x >= 16 {
x = (x + 1) >> 1
e++
}
if x < 8 {
return x
}
return ((e + 1) << 3) | (x - 8)
}
func strCmp(s1, s2 string) int {
len1 := len(s1)
len2 := len(s2)
for i := 0; ; i++ {
c1 := -1
if i < len1 {
c1 = int(s1[i])
}
c2 := -1
if i != len2 {
c2 = int(s2[i])
}
switch {
case c1 < c2:
return -1
case c1 > c2:
return +1
case c1 < 0:
return 0
}
}
}
func unsafeFastStringToReadOnlyBytes(s string) (bs []byte) {
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
bh := (*reflect.SliceHeader)(unsafe.Pointer(&bs))
bh.Data = sh.Data
bh.Cap = sh.Len
bh.Len = sh.Len
return
}

247
vendor/github.com/yuin/gopher-lua/value.go generated vendored Normal file
View File

@ -0,0 +1,247 @@
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 }

1726
vendor/github.com/yuin/gopher-lua/vm.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

24
vendor/layeh.com/gopher-json/LICENSE generated vendored Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

7
vendor/layeh.com/gopher-json/README.md generated vendored Normal file
View File

@ -0,0 +1,7 @@
# gopher-json [![GoDoc](https://godoc.org/layeh.com/gopher-json?status.svg)](https://godoc.org/layeh.com/gopher-json)
Package json is a simple JSON encoder/decoder for [gopher-lua](https://github.com/yuin/gopher-lua).
## License
Public domain

33
vendor/layeh.com/gopher-json/doc.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
// Package json is a simple JSON encoder/decoder for gopher-lua.
//
// Documentation
//
// The following functions are exposed by the library:
// decode(string): Decodes a JSON string. Returns nil and an error string if
// the string could not be decoded.
// encode(value): Encodes a value into a JSON string. Returns nil and an error
// string if the value could not be encoded.
//
// The following types are supported:
//
// Lua | JSON
// ---------+-----
// nil | null
// number | number
// string | string
// table | object: when table is non-empty and has only string keys
// | array: when table is empty, or has only sequential numeric keys
// | starting from 1
//
// Attempting to encode any other Lua type will result in an error.
//
// Example
//
// Below is an example usage of the library:
// import (
// luajson "layeh.com/gopher-json"
// )
//
// L := lua.NewState()
// luajson.Preload(L)
package json // import "layeh.com/gopher-json"

181
vendor/layeh.com/gopher-json/json.go generated vendored Normal file
View File

@ -0,0 +1,181 @@
package json
import (
"encoding/json"
"errors"
"github.com/yuin/gopher-lua"
)
// Preload adds json to the given Lua state's package.preload table. After it
// has been preloaded, it can be loaded using require:
//
// local json = require("json")
func Preload(L *lua.LState) {
L.PreloadModule("json", Loader)
}
// Loader is the module loader function.
func Loader(L *lua.LState) int {
t := L.NewTable()
L.SetFuncs(t, api)
L.Push(t)
return 1
}
var api = map[string]lua.LGFunction{
"decode": apiDecode,
"encode": apiEncode,
}
func apiDecode(L *lua.LState) int {
str := L.CheckString(1)
value, err := Decode(L, []byte(str))
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
L.Push(value)
return 1
}
func apiEncode(L *lua.LState) int {
value := L.CheckAny(1)
data, err := Encode(value)
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
L.Push(lua.LString(string(data)))
return 1
}
var (
errNested = errors.New("cannot encode recursively nested tables to JSON")
errSparseArray = errors.New("cannot encode sparse array")
errInvalidKeys = errors.New("cannot encode mixed or invalid key types")
)
type invalidTypeError lua.LValueType
func (i invalidTypeError) Error() string {
return `cannot encode ` + lua.LValueType(i).String() + ` to JSON`
}
// Encode returns the JSON encoding of value.
func Encode(value lua.LValue) ([]byte, error) {
return json.Marshal(jsonValue{
LValue: value,
visited: make(map[*lua.LTable]bool),
})
}
type jsonValue struct {
lua.LValue
visited map[*lua.LTable]bool
}
func (j jsonValue) MarshalJSON() (data []byte, err error) {
switch converted := j.LValue.(type) {
case lua.LBool:
data, err = json.Marshal(bool(converted))
case lua.LNumber:
data, err = json.Marshal(float64(converted))
case *lua.LNilType:
data = []byte(`null`)
case lua.LString:
data, err = json.Marshal(string(converted))
case *lua.LTable:
if j.visited[converted] {
return nil, errNested
}
j.visited[converted] = true
key, value := converted.Next(lua.LNil)
switch key.Type() {
case lua.LTNil: // empty table
data = []byte(`[]`)
case lua.LTNumber:
arr := make([]jsonValue, 0, converted.Len())
expectedKey := lua.LNumber(1)
for key != lua.LNil {
if key.Type() != lua.LTNumber {
err = errInvalidKeys
return
}
if expectedKey != key {
err = errSparseArray
return
}
arr = append(arr, jsonValue{value, j.visited})
expectedKey++
key, value = converted.Next(key)
}
data, err = json.Marshal(arr)
case lua.LTString:
obj := make(map[string]jsonValue)
for key != lua.LNil {
if key.Type() != lua.LTString {
err = errInvalidKeys
return
}
obj[key.String()] = jsonValue{value, j.visited}
key, value = converted.Next(key)
}
data, err = json.Marshal(obj)
default:
err = errInvalidKeys
}
default:
err = invalidTypeError(j.LValue.Type())
}
return
}
// Decode converts the JSON encoded data to Lua values.
func Decode(L *lua.LState, data []byte) (lua.LValue, error) {
var value interface{}
err := json.Unmarshal(data, &value)
if err != nil {
return nil, err
}
return DecodeValue(L, value), nil
}
// DecodeValue converts the value to a Lua value.
//
// This function only converts values that the encoding/json package decodes to.
// All other values will return lua.LNil.
func DecodeValue(L *lua.LState, value interface{}) lua.LValue {
switch converted := value.(type) {
case bool:
return lua.LBool(converted)
case float64:
return lua.LNumber(converted)
case string:
return lua.LString(converted)
case json.Number:
return lua.LString(converted)
case []interface{}:
arr := L.CreateTable(len(converted), 0)
for _, item := range converted {
arr.Append(DecodeValue(L, item))
}
return arr
case map[string]interface{}:
tbl := L.CreateTable(0, len(converted))
for key, item := range converted {
tbl.RawSetH(lua.LString(key), DecodeValue(L, item))
}
return tbl
case nil:
return lua.LNil
}
return lua.LNil
}

9
vendor/modules.txt vendored
View File

@ -428,6 +428,12 @@ github.com/vektra/mockery/v2/pkg/logging
# github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca
## explicit
github.com/xlab/treeprint
# github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64
## explicit; go 1.17
github.com/yuin/gopher-lua
github.com/yuin/gopher-lua/ast
github.com/yuin/gopher-lua/parse
github.com/yuin/gopher-lua/pm
# go.etcd.io/etcd/api/v3 v3.5.1
## explicit; go 1.16
go.etcd.io/etcd/api/v3/authpb
@ -1491,6 +1497,9 @@ k8s.io/utils/path
k8s.io/utils/pointer
k8s.io/utils/strings/slices
k8s.io/utils/trace
# layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf
## explicit
layeh.com/gopher-json
# sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30
## explicit; go 1.17
sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client