gitee.com/KyleChenSource/lib-robot@v1.0.2/robottest/common/mlua/lua.go (about)

     1  package mlua
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"os"
     7  	"sync"
     8  
     9  	"gitee.com/KyleChenSource/lib-robot/robottest/log"
    10  	lua "github.com/yuin/gopher-lua"
    11  	"github.com/yuin/gopher-lua/parse"
    12  )
    13  
    14  const (
    15  	LuaPathPre = "lua"
    16  )
    17  
    18  var (
    19  	luaFiles sync.Map
    20  	luajson  *lua.FunctionProto
    21  )
    22  
    23  type LuaScript struct {
    24  	OnStart *lua.LFunction // OnStart(Action)
    25  	OnStop  *lua.LFunction // OnStop(Action)
    26  	OnMsg   *lua.LFunction // OnMsg(Action, msgId, msgJson)
    27  }
    28  
    29  type LuaCtx struct {
    30  	L      *lua.LState
    31  	Tables map[string]*LuaScript
    32  }
    33  
    34  func LuaCtxCreate() *LuaCtx {
    35  	ret := &LuaCtx{
    36  		L:      lua.NewState(),
    37  		Tables: make(map[string]*LuaScript),
    38  	}
    39  
    40  	if luajson == nil {
    41  		c, err := CompileLua(fmt.Sprintf("%s/json.lua", LuaPathPre))
    42  		if err != nil {
    43  			log.LogError("CompileLua json.lua err:%s", err.Error())
    44  			return nil
    45  		}
    46  		luajson = c
    47  	}
    48  
    49  	topB := ret.L.GetTop()
    50  	err := DoCompiledFile(ret.L, luajson)
    51  	if err != nil {
    52  		log.LogError("DoCompiledFile json.lua err:%s", err.Error())
    53  		return nil
    54  	}
    55  	topE := ret.L.GetTop()
    56  	if topE-topB != 1 {
    57  		log.LogError("DoCompiledFile json.lua return:%d", topE-topB)
    58  		return nil
    59  	}
    60  	table := ret.L.CheckTable(topE)
    61  	if table == nil {
    62  		log.LogError("DoCompiledFile json.lua return not table")
    63  		return nil
    64  	}
    65  
    66  	preload := ret.L.GetField(ret.L.GetField(ret.L.Get(lua.EnvironIndex), "package"), "loaded")
    67  	if _, ok := preload.(*lua.LTable); !ok {
    68  		log.LogError("no package[loaded]")
    69  		return nil
    70  	}
    71  	ret.L.SetField(preload, "json", table)
    72  
    73  	return ret
    74  }
    75  
    76  func (this *LuaCtx) Script(file string) (*LuaScript, error) {
    77  	t, ok := this.Tables[file]
    78  	if ok {
    79  		return t, nil
    80  	}
    81  
    82  	f, ok := luaFiles.Load(file)
    83  	if !ok {
    84  		c, err := CompileLua(fmt.Sprintf("%s/%s", LuaPathPre, file))
    85  		if err != nil {
    86  			return nil, err
    87  		}
    88  
    89  		f, _ = luaFiles.LoadOrStore(file, c)
    90  	}
    91  
    92  	p := f.(*lua.FunctionProto)
    93  	topB := this.L.GetTop()
    94  	err := DoCompiledFile(this.L, p)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	topE := this.L.GetTop()
    99  	if topE-topB != 1 {
   100  		return nil, fmt.Errorf("return param:%d", topE-topB)
   101  	}
   102  
   103  	table := this.L.CheckTable(topE)
   104  	if table == nil {
   105  		return nil, fmt.Errorf("return param:%s", this.L.Get(topE).Type().String())
   106  	}
   107  
   108  	this.L.SetGlobal(file, table)
   109  	script := LuaScript{}
   110  
   111  	function := table.RawGetString("onStart")
   112  	if function == nil {
   113  		return nil, fmt.Errorf("onStart nil")
   114  	}
   115  	if function.Type() != lua.LTFunction {
   116  		return nil, fmt.Errorf("onStart type:%s", function.Type().String())
   117  	}
   118  	script.OnStart = function.(*lua.LFunction)
   119  
   120  	function = table.RawGetString("onMsg")
   121  	if function == nil {
   122  		return nil, fmt.Errorf("onMsg nil")
   123  	}
   124  	if function.Type() != lua.LTFunction {
   125  		return nil, fmt.Errorf("onMsg type:%s", function.Type().String())
   126  	}
   127  	script.OnMsg = function.(*lua.LFunction)
   128  
   129  	function = table.RawGetString("onStop")
   130  	if function == nil {
   131  		return nil, fmt.Errorf("onStop nil")
   132  	}
   133  	if function.Type() != lua.LTFunction {
   134  		return nil, fmt.Errorf("onStop type:%s", function.Type().String())
   135  	}
   136  	script.OnStop = function.(*lua.LFunction)
   137  	this.Tables[file] = &script
   138  
   139  	return &script, nil
   140  }
   141  
   142  // CompileLua reads the passed lua file from disk and compiles it.
   143  func CompileLua(filePath string) (*lua.FunctionProto, error) {
   144  	file, err := os.Open(filePath)
   145  	defer file.Close()
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  	reader := bufio.NewReader(file)
   150  	chunk, err := parse.Parse(reader, filePath)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  	proto, err := lua.Compile(chunk, filePath)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	return proto, nil
   159  }
   160  
   161  // DoCompiledFile takes a FunctionProto, as returned by CompileLua, and runs it in the LState. It is equivalent
   162  // to calling DoFile on the LState with the original source file.
   163  func DoCompiledFile(L *lua.LState, proto *lua.FunctionProto) error {
   164  	lfunc := L.NewFunctionFromProto(proto)
   165  	L.Push(lfunc)
   166  	return L.PCall(0, lua.MultRet, nil)
   167  }