github.com/awirix/lua@v1.6.0/baselib.go (about)

     1  package lua
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"runtime"
     8  	"strconv"
     9  	"strings"
    10  )
    11  
    12  /* basic functions {{{ */
    13  
    14  func OpenBase(L *LState) int {
    15  	global := L.Get(GlobalsIndex).(*LTable)
    16  	L.SetGlobal("_G", global)
    17  	L.SetGlobal("_VERSION", LString(LuaVersion))
    18  	L.SetGlobal("_GOPHER_LUA_VERSION", LString(PackageName+" "+PackageVersion))
    19  	basemod := L.RegisterModule("_G", baseFuncs)
    20  	global.RawSetString("ipairs", L.NewClosure(baseIpairs, L.NewFunction(ipairsaux)))
    21  	global.RawSetString("pairs", L.NewClosure(basePairs, L.NewFunction(pairsaux)))
    22  	L.Push(basemod)
    23  	return 1
    24  }
    25  
    26  var baseFuncs = map[string]LGFunction{
    27  	"assert":         baseAssert,
    28  	"collectgarbage": baseCollectGarbage,
    29  	"dofile":         baseDoFile,
    30  	"error":          baseError,
    31  	"getfenv":        baseGetFEnv,
    32  	"getmetatable":   baseGetMetatable,
    33  	"load":           baseLoad,
    34  	"loadfile":       baseLoadFile,
    35  	"loadstring":     baseLoadString,
    36  	"next":           baseNext,
    37  	"pcall":          basePCall,
    38  	"print":          basePrint,
    39  	"rawequal":       baseRawEqual,
    40  	"rawget":         baseRawGet,
    41  	"rawset":         baseRawSet,
    42  	"select":         baseSelect,
    43  	"_printregs":     base_PrintRegs,
    44  	"setfenv":        baseSetFEnv,
    45  	"setmetatable":   baseSetMetatable,
    46  	"tonumber":       baseToNumber,
    47  	"tostring":       baseToString,
    48  	"type":           baseType,
    49  	"unpack":         baseUnpack,
    50  	"xpcall":         baseXPCall,
    51  	// loadlib
    52  	"module":  loModule,
    53  	"require": loRequire,
    54  	// hidden features
    55  	"newproxy": baseNewProxy,
    56  }
    57  
    58  func baseAssert(L *LState) int {
    59  	if !L.ToBool(1) {
    60  		L.RaiseError(L.OptString(2, "assertion failed!"))
    61  		return 0
    62  	}
    63  	return L.GetTop()
    64  }
    65  
    66  func baseCollectGarbage(L *LState) int {
    67  	runtime.GC()
    68  	return 0
    69  }
    70  
    71  func baseDoFile(L *LState) int {
    72  	src := L.ToString(1)
    73  	top := L.GetTop()
    74  	fn, err := L.LoadFile(src)
    75  	if err != nil {
    76  		L.Push(LString(err.Error()))
    77  		L.Panic(L)
    78  	}
    79  	L.Push(fn)
    80  	L.Call(0, MultRet)
    81  	return L.GetTop() - top
    82  }
    83  
    84  func baseError(L *LState) int {
    85  	obj := L.CheckAny(1)
    86  	level := L.OptInt(2, 1)
    87  	L.Error(obj, level)
    88  	return 0
    89  }
    90  
    91  func baseGetFEnv(L *LState) int {
    92  	var value LValue
    93  	if L.GetTop() == 0 {
    94  		value = LNumber(1)
    95  	} else {
    96  		value = L.Get(1)
    97  	}
    98  
    99  	if fn, ok := value.(*LFunction); ok {
   100  		if !fn.IsG {
   101  			L.Push(fn.Env)
   102  		} else {
   103  			L.Push(L.G.Global)
   104  		}
   105  		return 1
   106  	}
   107  
   108  	if number, ok := value.(LNumber); ok {
   109  		level := int(float64(number))
   110  		if level <= 0 {
   111  			L.Push(L.Env)
   112  		} else {
   113  			cf := L.currentFrame
   114  			for i := 0; i < level && cf != nil; i++ {
   115  				cf = cf.Parent
   116  			}
   117  			if cf == nil || cf.Fn.IsG {
   118  				L.Push(L.G.Global)
   119  			} else {
   120  				L.Push(cf.Fn.Env)
   121  			}
   122  		}
   123  		return 1
   124  	}
   125  
   126  	L.Push(L.G.Global)
   127  	return 1
   128  }
   129  
   130  func baseGetMetatable(L *LState) int {
   131  	L.Push(L.GetMetatable(L.CheckAny(1)))
   132  	return 1
   133  }
   134  
   135  func ipairsaux(L *LState) int {
   136  	tb := L.CheckTable(1)
   137  	i := L.CheckInt(2)
   138  	i++
   139  	v := tb.RawGetInt(i)
   140  	if v == LNil {
   141  		return 0
   142  	} else {
   143  		L.Pop(1)
   144  		L.Push(LNumber(i))
   145  		L.Push(LNumber(i))
   146  		L.Push(v)
   147  		return 2
   148  	}
   149  }
   150  
   151  func baseIpairs(L *LState) int {
   152  	tb := L.CheckTable(1)
   153  	L.Push(L.Get(UpvalueIndex(1)))
   154  	L.Push(tb)
   155  	L.Push(LNumber(0))
   156  	return 3
   157  }
   158  
   159  func loadaux(L *LState, reader io.Reader, chunkname string) int {
   160  	if fn, err := L.Load(reader, chunkname); err != nil {
   161  		L.Push(LNil)
   162  		L.Push(LString(err.Error()))
   163  		return 2
   164  	} else {
   165  		L.Push(fn)
   166  		return 1
   167  	}
   168  }
   169  
   170  func baseLoad(L *LState) int {
   171  	// TODO: support strings as the first argument
   172  	arg := L.CheckAny(1)
   173  	var (
   174  		fn  *LFunction
   175  		buf []string
   176  	)
   177  
   178  	if arg.Type() == LTFunction {
   179  		fn = arg.(*LFunction)
   180  	} else if arg.Type() == LTString {
   181  		buf = append(buf, arg.String())
   182  	} else {
   183  		L.RaiseError("load: argument must be a function or a string")
   184  		return 0
   185  	}
   186  
   187  	chunkname := L.OptString(2, "?")
   188  	top := L.GetTop()
   189  	if fn != nil {
   190  		for {
   191  			L.SetTop(top)
   192  			L.Push(fn)
   193  			L.Call(0, 1)
   194  			ret := L.reg.Pop()
   195  			if ret == LNil {
   196  				break
   197  			} else if LVCanConvToString(ret) {
   198  				str := ret.String()
   199  				if len(str) > 0 {
   200  					buf = append(buf, string(str))
   201  				} else {
   202  					break
   203  				}
   204  			} else {
   205  				L.Push(LNil)
   206  				L.Push(LString("reader function must return a string"))
   207  				return 2
   208  			}
   209  		}
   210  	}
   211  	return loadaux(L, strings.NewReader(strings.Join(buf, "")), chunkname)
   212  }
   213  
   214  func baseLoadFile(L *LState) int {
   215  	var reader io.Reader
   216  	var chunkname string
   217  	var err error
   218  	if L.GetTop() < 1 {
   219  		reader = os.Stdin
   220  		chunkname = "<stdin>"
   221  	} else {
   222  		chunkname = L.CheckString(1)
   223  		reader, err = os.Open(chunkname)
   224  		if err != nil {
   225  			L.Push(LNil)
   226  			L.Push(LString(fmt.Sprintf("can not open file: %v", chunkname)))
   227  			return 2
   228  		}
   229  		defer reader.(*os.File).Close()
   230  	}
   231  	return loadaux(L, reader, chunkname)
   232  }
   233  
   234  func baseLoadString(L *LState) int {
   235  	return loadaux(L, strings.NewReader(L.CheckString(1)), L.OptString(2, "<string>"))
   236  }
   237  
   238  func baseNext(L *LState) int {
   239  	tb := L.CheckTable(1)
   240  	index := LNil
   241  	if L.GetTop() >= 2 {
   242  		index = L.Get(2)
   243  	}
   244  	key, value := tb.Next(index)
   245  	if key == LNil {
   246  		L.Push(LNil)
   247  		return 1
   248  	}
   249  	L.Push(key)
   250  	L.Push(value)
   251  	return 2
   252  }
   253  
   254  func pairsaux(L *LState) int {
   255  	tb := L.CheckTable(1)
   256  	key, value := tb.Next(L.Get(2))
   257  	if key == LNil {
   258  		return 0
   259  	} else {
   260  		L.Pop(1)
   261  		L.Push(key)
   262  		L.Push(key)
   263  		L.Push(value)
   264  		return 2
   265  	}
   266  }
   267  
   268  func basePairs(L *LState) int {
   269  	tb := L.CheckTable(1)
   270  	L.Push(L.Get(UpvalueIndex(1)))
   271  	L.Push(tb)
   272  	L.Push(LNil)
   273  	return 3
   274  }
   275  
   276  func basePCall(L *LState) int {
   277  	L.CheckAny(1)
   278  	v := L.Get(1)
   279  	if v.Type() != LTFunction && L.GetMetaField(v, "__call").Type() != LTFunction {
   280  		L.Push(LFalse)
   281  		L.Push(LString("attempt to call a " + v.Type().String() + " value"))
   282  		return 2
   283  	}
   284  	nargs := L.GetTop() - 1
   285  	if err := L.PCall(nargs, MultRet, nil); err != nil {
   286  		L.Push(LFalse)
   287  		if aerr, ok := err.(*ApiError); ok {
   288  			L.Push(aerr.Object)
   289  		} else {
   290  			L.Push(LString(err.Error()))
   291  		}
   292  		return 2
   293  	} else {
   294  		L.Insert(LTrue, 1)
   295  		return L.GetTop()
   296  	}
   297  }
   298  
   299  func basePrint(L *LState) int {
   300  	top := L.GetTop()
   301  	for i := 1; i <= top; i++ {
   302  		fmt.Fprint(L.Options.Stdout, L.ToStringMeta(L.Get(i)).String())
   303  		if i != top {
   304  			fmt.Fprint(L.Options.Stdout, "\t")
   305  		}
   306  	}
   307  	fmt.Fprintln(L.Options.Stdout, "")
   308  	return 0
   309  }
   310  
   311  func base_PrintRegs(L *LState) int {
   312  	L.printReg()
   313  	return 0
   314  }
   315  
   316  func baseRawEqual(L *LState) int {
   317  	if L.CheckAny(1) == L.CheckAny(2) {
   318  		L.Push(LTrue)
   319  	} else {
   320  		L.Push(LFalse)
   321  	}
   322  	return 1
   323  }
   324  
   325  func baseRawGet(L *LState) int {
   326  	L.Push(L.RawGet(L.CheckTable(1), L.CheckAny(2)))
   327  	return 1
   328  }
   329  
   330  func baseRawSet(L *LState) int {
   331  	L.RawSet(L.CheckTable(1), L.CheckAny(2), L.CheckAny(3))
   332  	return 0
   333  }
   334  
   335  func baseSelect(L *LState) int {
   336  	L.CheckTypes(1, LTNumber, LTString)
   337  	switch lv := L.Get(1).(type) {
   338  	case LNumber:
   339  		idx := int(lv)
   340  		num := L.GetTop()
   341  		if idx < 0 {
   342  			idx = num + idx
   343  		} else if idx > num {
   344  			idx = num
   345  		}
   346  		if 1 > idx {
   347  			L.ArgError(1, "index out of range")
   348  		}
   349  		return num - idx
   350  	case LString:
   351  		if string(lv) != "#" {
   352  			L.ArgError(1, "invalid string '"+string(lv)+"'")
   353  		}
   354  		L.Push(LNumber(L.GetTop() - 1))
   355  		return 1
   356  	}
   357  	return 0
   358  }
   359  
   360  func baseSetFEnv(L *LState) int {
   361  	var value LValue
   362  	if L.GetTop() == 0 {
   363  		value = LNumber(1)
   364  	} else {
   365  		value = L.Get(1)
   366  	}
   367  	env := L.CheckTable(2)
   368  
   369  	if fn, ok := value.(*LFunction); ok {
   370  		if fn.IsG {
   371  			L.RaiseError("cannot change the environment of given object")
   372  		} else {
   373  			fn.Env = env
   374  			L.Push(fn)
   375  			return 1
   376  		}
   377  	}
   378  
   379  	if number, ok := value.(LNumber); ok {
   380  		level := int(float64(number))
   381  		if level <= 0 {
   382  			L.Env = env
   383  			return 0
   384  		}
   385  
   386  		cf := L.currentFrame
   387  		for i := 0; i < level && cf != nil; i++ {
   388  			cf = cf.Parent
   389  		}
   390  		if cf == nil || cf.Fn.IsG {
   391  			L.RaiseError("cannot change the environment of given object")
   392  		} else {
   393  			cf.Fn.Env = env
   394  			L.Push(cf.Fn)
   395  			return 1
   396  		}
   397  	}
   398  
   399  	L.RaiseError("cannot change the environment of given object")
   400  	return 0
   401  }
   402  
   403  func baseSetMetatable(L *LState) int {
   404  	L.CheckTypes(2, LTNil, LTTable)
   405  	obj := L.Get(1)
   406  	if obj == LNil {
   407  		L.RaiseError("cannot set metatable to a nil object.")
   408  	}
   409  	mt := L.Get(2)
   410  	if m := L.metatable(obj, true); m != LNil {
   411  		if tb, ok := m.(*LTable); ok && tb.RawGetString("__metatable") != LNil {
   412  			L.RaiseError("cannot change a protected metatable")
   413  		}
   414  	}
   415  	L.SetMetatable(obj, mt)
   416  	L.SetTop(1)
   417  	return 1
   418  }
   419  
   420  func baseToNumber(L *LState) int {
   421  	base := L.OptInt(2, 10)
   422  	noBase := L.Get(2) == LNil
   423  
   424  	switch lv := L.CheckAny(1).(type) {
   425  	case LNumber:
   426  		L.Push(lv)
   427  	case LString:
   428  		str := strings.Trim(string(lv), " \n\t")
   429  		if strings.Index(str, ".") > -1 {
   430  			if v, err := strconv.ParseFloat(str, LNumberBit); err != nil {
   431  				L.Push(LNil)
   432  			} else {
   433  				L.Push(LNumber(v))
   434  			}
   435  		} else {
   436  			if noBase && strings.HasPrefix(strings.ToLower(str), "0x") {
   437  				base, str = 16, str[2:] // Hex number
   438  			}
   439  			if v, err := strconv.ParseInt(str, base, LNumberBit); err != nil {
   440  				L.Push(LNil)
   441  			} else {
   442  				L.Push(LNumber(v))
   443  			}
   444  		}
   445  	default:
   446  		L.Push(LNil)
   447  	}
   448  	return 1
   449  }
   450  
   451  func baseToString(L *LState) int {
   452  	v1 := L.CheckAny(1)
   453  	L.Push(L.ToStringMeta(v1))
   454  	return 1
   455  }
   456  
   457  func baseType(L *LState) int {
   458  	L.Push(LString(L.CheckAny(1).Type().String()))
   459  	return 1
   460  }
   461  
   462  func baseUnpack(L *LState) int {
   463  	tb := L.CheckTable(1)
   464  	start := L.OptInt(2, 1)
   465  	end := L.OptInt(3, tb.Len())
   466  	for i := start; i <= end; i++ {
   467  		L.Push(tb.RawGetInt(i))
   468  	}
   469  	ret := end - start + 1
   470  	if ret < 0 {
   471  		return 0
   472  	}
   473  	return ret
   474  }
   475  
   476  func baseXPCall(L *LState) int {
   477  	fn := L.CheckFunction(1)
   478  	errfunc := L.CheckFunction(2)
   479  
   480  	top := L.GetTop()
   481  	L.Push(fn)
   482  	if err := L.PCall(0, MultRet, errfunc); err != nil {
   483  		L.Push(LFalse)
   484  		if aerr, ok := err.(*ApiError); ok {
   485  			L.Push(aerr.Object)
   486  		} else {
   487  			L.Push(LString(err.Error()))
   488  		}
   489  		return 2
   490  	} else {
   491  		L.Insert(LTrue, top+1)
   492  		return L.GetTop() - top
   493  	}
   494  }
   495  
   496  /* }}} */
   497  
   498  /* load lib {{{ */
   499  
   500  func loModule(L *LState) int {
   501  	name := L.CheckString(1)
   502  	loaded := L.GetField(L.Get(RegistryIndex), "_LOADED")
   503  	tb := L.GetField(loaded, name)
   504  	if _, ok := tb.(*LTable); !ok {
   505  		tb = L.FindTable(L.Get(GlobalsIndex).(*LTable), name, 1)
   506  		if tb == LNil {
   507  			L.RaiseError("name conflict for module: %v", name)
   508  		}
   509  		L.SetField(loaded, name, tb)
   510  	}
   511  	if L.GetField(tb, "_NAME") == LNil {
   512  		L.SetField(tb, "_M", tb)
   513  		L.SetField(tb, "_NAME", LString(name))
   514  		names := strings.Split(name, ".")
   515  		pname := ""
   516  		if len(names) > 1 {
   517  			pname = strings.Join(names[:len(names)-1], ".") + "."
   518  		}
   519  		L.SetField(tb, "_PACKAGE", LString(pname))
   520  	}
   521  
   522  	caller := L.currentFrame.Parent
   523  	if caller == nil {
   524  		L.RaiseError("no calling stack.")
   525  	} else if caller.Fn.IsG {
   526  		L.RaiseError("module() can not be called from GFunctions.")
   527  	}
   528  	L.SetFEnv(caller.Fn, tb)
   529  
   530  	top := L.GetTop()
   531  	for i := 2; i <= top; i++ {
   532  		L.Push(L.Get(i))
   533  		L.Push(tb)
   534  		L.Call(1, 0)
   535  	}
   536  	L.Push(tb)
   537  	return 1
   538  }
   539  
   540  var loopdetection = &LUserData{}
   541  
   542  func loRequire(L *LState) int {
   543  	name := L.CheckString(1)
   544  	loaded := L.GetField(L.Get(RegistryIndex), "_LOADED")
   545  	lv := L.GetField(loaded, name)
   546  	if LVAsBool(lv) {
   547  		if lv == loopdetection {
   548  			L.RaiseError("loop or previous error loading module: %s", name)
   549  		}
   550  		L.Push(lv)
   551  		return 1
   552  	}
   553  	loaders, ok := L.GetField(L.Get(RegistryIndex), "_LOADERS").(*LTable)
   554  	if !ok {
   555  		L.RaiseError("package.loaders must be a table")
   556  	}
   557  	messages := []string{}
   558  	var modasfunc LValue
   559  	for i := 1; ; i++ {
   560  		loader := L.RawGetInt(loaders, i)
   561  		if loader == LNil {
   562  			L.RaiseError("module %s not found:\n\t%s, ", name, strings.Join(messages, "\n\t"))
   563  		}
   564  		L.Push(loader)
   565  		L.Push(LString(name))
   566  		L.Call(1, 1)
   567  		ret := L.reg.Pop()
   568  		switch retv := ret.(type) {
   569  		case *LFunction:
   570  			modasfunc = retv
   571  			goto loopbreak
   572  		case LString:
   573  			messages = append(messages, string(retv))
   574  		}
   575  	}
   576  loopbreak:
   577  	L.SetField(loaded, name, loopdetection)
   578  	L.Push(modasfunc)
   579  	L.Push(LString(name))
   580  	L.Call(1, 1)
   581  	ret := L.reg.Pop()
   582  	modv := L.GetField(loaded, name)
   583  	if ret != LNil && modv == loopdetection {
   584  		L.SetField(loaded, name, ret)
   585  		L.Push(ret)
   586  	} else if modv == loopdetection {
   587  		L.SetField(loaded, name, LTrue)
   588  		L.Push(LTrue)
   589  	} else {
   590  		L.Push(modv)
   591  	}
   592  	return 1
   593  }
   594  
   595  /* }}} */
   596  
   597  /* hidden features {{{ */
   598  
   599  func baseNewProxy(L *LState) int {
   600  	ud := L.NewUserData()
   601  	L.SetTop(1)
   602  	if L.Get(1) == LTrue {
   603  		L.SetMetatable(ud, L.NewTable())
   604  	} else if d, ok := L.Get(1).(*LUserData); ok {
   605  		L.SetMetatable(ud, L.GetMetatable(d))
   606  	}
   607  	L.Push(ud)
   608  	return 1
   609  }
   610  
   611  /* }}} */
   612  
   613  //