1package lua
2
3import (
4	"fmt"
5	"io"
6	"os"
7	"runtime"
8	"strconv"
9	"strings"
10)
11
12/* basic functions {{{ */
13
14func 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
26var 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
58func 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
66func baseCollectGarbage(L *LState) int {
67	runtime.GC()
68	return 0
69}
70
71func 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
84func 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
91func 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
130func baseGetMetatable(L *LState) int {
131	L.Push(L.GetMetatable(L.CheckAny(1)))
132	return 1
133}
134
135func 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
151func 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
159func 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
170func baseLoad(L *LState) int {
171	fn := L.CheckFunction(1)
172	chunkname := L.OptString(2, "?")
173	top := L.GetTop()
174	buf := []string{}
175	for {
176		L.SetTop(top)
177		L.Push(fn)
178		L.Call(0, 1)
179		ret := L.reg.Pop()
180		if ret == LNil {
181			break
182		} else if LVCanConvToString(ret) {
183			str := ret.String()
184			if len(str) > 0 {
185				buf = append(buf, string(str))
186			} else {
187				break
188			}
189		} else {
190			L.Push(LNil)
191			L.Push(LString("reader function must return a string"))
192			return 2
193		}
194	}
195	return loadaux(L, strings.NewReader(strings.Join(buf, "")), chunkname)
196}
197
198func baseLoadFile(L *LState) int {
199	var reader io.Reader
200	var chunkname string
201	var err error
202	if L.GetTop() < 1 {
203		reader = os.Stdin
204		chunkname = "<stdin>"
205	} else {
206		chunkname = L.CheckString(1)
207		reader, err = os.Open(chunkname)
208		if err != nil {
209			L.Push(LNil)
210			L.Push(LString(fmt.Sprintf("can not open file: %v", chunkname)))
211			return 2
212		}
213		defer reader.(*os.File).Close()
214	}
215	return loadaux(L, reader, chunkname)
216}
217
218func baseLoadString(L *LState) int {
219	return loadaux(L, strings.NewReader(L.CheckString(1)), L.OptString(2, "<string>"))
220}
221
222func baseNext(L *LState) int {
223	tb := L.CheckTable(1)
224	index := LNil
225	if L.GetTop() >= 2 {
226		index = L.Get(2)
227	}
228	key, value := tb.Next(index)
229	if key == LNil {
230		L.Push(LNil)
231		return 1
232	}
233	L.Push(key)
234	L.Push(value)
235	return 2
236}
237
238func pairsaux(L *LState) int {
239	tb := L.CheckTable(1)
240	key, value := tb.Next(L.Get(2))
241	if key == LNil {
242		return 0
243	} else {
244		L.Pop(1)
245		L.Push(key)
246		L.Push(key)
247		L.Push(value)
248		return 2
249	}
250}
251
252func basePairs(L *LState) int {
253	tb := L.CheckTable(1)
254	L.Push(L.Get(UpvalueIndex(1)))
255	L.Push(tb)
256	L.Push(LNil)
257	return 3
258}
259
260func basePCall(L *LState) int {
261	L.CheckAny(1)
262	v := L.Get(1)
263	if v.Type() != LTFunction {
264		L.Push(LFalse)
265		L.Push(LString("attempt to call a " + v.Type().String() + " value"))
266		return 2
267	}
268	nargs := L.GetTop() - 1
269	if err := L.PCall(nargs, MultRet, nil); err != nil {
270		L.Push(LFalse)
271		if aerr, ok := err.(*ApiError); ok {
272			L.Push(aerr.Object)
273		} else {
274			L.Push(LString(err.Error()))
275		}
276		return 2
277	} else {
278		L.Insert(LTrue, 1)
279		return L.GetTop()
280	}
281}
282
283func basePrint(L *LState) int {
284	top := L.GetTop()
285	for i := 1; i <= top; i++ {
286		fmt.Print(L.ToStringMeta(L.Get(i)).String())
287		if i != top {
288			fmt.Print("\t")
289		}
290	}
291	fmt.Println("")
292	return 0
293}
294
295func base_PrintRegs(L *LState) int {
296	L.printReg()
297	return 0
298}
299
300func baseRawEqual(L *LState) int {
301	if L.CheckAny(1) == L.CheckAny(2) {
302		L.Push(LTrue)
303	} else {
304		L.Push(LFalse)
305	}
306	return 1
307}
308
309func baseRawGet(L *LState) int {
310	L.Push(L.RawGet(L.CheckTable(1), L.CheckAny(2)))
311	return 1
312}
313
314func baseRawSet(L *LState) int {
315	L.RawSet(L.CheckTable(1), L.CheckAny(2), L.CheckAny(3))
316	return 0
317}
318
319func baseSelect(L *LState) int {
320	L.CheckTypes(1, LTNumber, LTString)
321	switch lv := L.Get(1).(type) {
322	case LNumber:
323		idx := int(lv)
324		num := L.reg.Top() - L.indexToReg(int(lv)) - 1
325		if idx < 0 {
326			num++
327		}
328		return num
329	case LString:
330		if string(lv) != "#" {
331			L.ArgError(1, "invalid string '"+string(lv)+"'")
332		}
333		L.Push(LNumber(L.GetTop() - 1))
334		return 1
335	}
336	return 0
337}
338
339func baseSetFEnv(L *LState) int {
340	var value LValue
341	if L.GetTop() == 0 {
342		value = LNumber(1)
343	} else {
344		value = L.Get(1)
345	}
346	env := L.CheckTable(2)
347
348	if fn, ok := value.(*LFunction); ok {
349		if fn.IsG {
350			L.RaiseError("cannot change the environment of given object")
351		} else {
352			fn.Env = env
353			L.Push(fn)
354			return 1
355		}
356	}
357
358	if number, ok := value.(LNumber); ok {
359		level := int(float64(number))
360		if level <= 0 {
361			L.Env = env
362			return 0
363		}
364
365		cf := L.currentFrame
366		for i := 0; i < level && cf != nil; i++ {
367			cf = cf.Parent
368		}
369		if cf == nil || cf.Fn.IsG {
370			L.RaiseError("cannot change the environment of given object")
371		} else {
372			cf.Fn.Env = env
373			L.Push(cf.Fn)
374			return 1
375		}
376	}
377
378	L.RaiseError("cannot change the environment of given object")
379	return 0
380}
381
382func baseSetMetatable(L *LState) int {
383	L.CheckTypes(2, LTNil, LTTable)
384	obj := L.Get(1)
385	if obj == LNil {
386		L.RaiseError("cannot set metatable to a nil object.")
387	}
388	mt := L.Get(2)
389	if m := L.metatable(obj, true); m != LNil {
390		if tb, ok := m.(*LTable); ok && tb.RawGetString("__metatable") != LNil {
391			L.RaiseError("cannot change a protected metatable")
392		}
393	}
394	L.SetMetatable(obj, mt)
395	L.SetTop(1)
396	return 1
397}
398
399func baseToNumber(L *LState) int {
400	base := L.OptInt(2, 10)
401	noBase := L.Get(2) == LNil
402
403	switch lv := L.CheckAny(1).(type) {
404	case LNumber:
405		L.Push(lv)
406	case LString:
407		str := strings.Trim(string(lv), " \n\t")
408		if strings.Index(str, ".") > -1 {
409			if v, err := strconv.ParseFloat(str, LNumberBit); err != nil {
410				L.Push(LNil)
411			} else {
412				L.Push(LNumber(v))
413			}
414		} else {
415			if noBase && strings.HasPrefix(strings.ToLower(str), "0x") {
416				base, str = 16, str[2:] // Hex number
417			}
418			if v, err := strconv.ParseInt(str, base, LNumberBit); err != nil {
419				L.Push(LNil)
420			} else {
421				L.Push(LNumber(v))
422			}
423		}
424	default:
425		L.Push(LNil)
426	}
427	return 1
428}
429
430func baseToString(L *LState) int {
431	v1 := L.CheckAny(1)
432	L.Push(L.ToStringMeta(v1))
433	return 1
434}
435
436func baseType(L *LState) int {
437	L.Push(LString(L.CheckAny(1).Type().String()))
438	return 1
439}
440
441func baseUnpack(L *LState) int {
442	tb := L.CheckTable(1)
443	start := L.OptInt(2, 1)
444	end := L.OptInt(3, tb.Len())
445	for i := start; i <= end; i++ {
446		L.Push(tb.RawGetInt(i))
447	}
448	ret := end - start + 1
449	if ret < 0 {
450		return 0
451	}
452	return ret
453}
454
455func baseXPCall(L *LState) int {
456	fn := L.CheckFunction(1)
457	errfunc := L.CheckFunction(2)
458
459	top := L.GetTop()
460	L.Push(fn)
461	if err := L.PCall(0, MultRet, errfunc); err != nil {
462		L.Push(LFalse)
463		if aerr, ok := err.(*ApiError); ok {
464			L.Push(aerr.Object)
465		} else {
466			L.Push(LString(err.Error()))
467		}
468		return 2
469	} else {
470		L.Insert(LTrue, top+1)
471		return L.GetTop() - top
472	}
473}
474
475/* }}} */
476
477/* load lib {{{ */
478
479func loModule(L *LState) int {
480	name := L.CheckString(1)
481	loaded := L.GetField(L.Get(RegistryIndex), "_LOADED")
482	tb := L.GetField(loaded, name)
483	if _, ok := tb.(*LTable); !ok {
484		tb = L.FindTable(L.Get(GlobalsIndex).(*LTable), name, 1)
485		if tb == LNil {
486			L.RaiseError("name conflict for module: %v", name)
487		}
488		L.SetField(loaded, name, tb)
489	}
490	if L.GetField(tb, "_NAME") == LNil {
491		L.SetField(tb, "_M", tb)
492		L.SetField(tb, "_NAME", LString(name))
493		names := strings.Split(name, ".")
494		pname := ""
495		if len(names) > 1 {
496			pname = strings.Join(names[:len(names)-1], ".") + "."
497		}
498		L.SetField(tb, "_PACKAGE", LString(pname))
499	}
500
501	caller := L.currentFrame.Parent
502	if caller == nil {
503		L.RaiseError("no calling stack.")
504	} else if caller.Fn.IsG {
505		L.RaiseError("module() can not be called from GFunctions.")
506	}
507	L.SetFEnv(caller.Fn, tb)
508
509	top := L.GetTop()
510	for i := 2; i <= top; i++ {
511		L.Push(L.Get(i))
512		L.Push(tb)
513		L.Call(1, 0)
514	}
515	L.Push(tb)
516	return 1
517}
518
519var loopdetection = &LUserData{}
520
521func loRequire(L *LState) int {
522	name := L.CheckString(1)
523	loaded := L.GetField(L.Get(RegistryIndex), "_LOADED")
524	lv := L.GetField(loaded, name)
525	if LVAsBool(lv) {
526		if lv == loopdetection {
527			L.RaiseError("loop or previous error loading module: %s", name)
528		}
529		L.Push(lv)
530		return 1
531	}
532	loaders, ok := L.GetField(L.Get(RegistryIndex), "_LOADERS").(*LTable)
533	if !ok {
534		L.RaiseError("package.loaders must be a table")
535	}
536	messages := []string{}
537	var modasfunc LValue
538	for i := 1; ; i++ {
539		loader := L.RawGetInt(loaders, i)
540		if loader == LNil {
541			L.RaiseError("module %s not found:\n\t%s, ", name, strings.Join(messages, "\n\t"))
542		}
543		L.Push(loader)
544		L.Push(LString(name))
545		L.Call(1, 1)
546		ret := L.reg.Pop()
547		switch retv := ret.(type) {
548		case *LFunction:
549			modasfunc = retv
550			goto loopbreak
551		case LString:
552			messages = append(messages, string(retv))
553		}
554	}
555loopbreak:
556	L.SetField(loaded, name, loopdetection)
557	L.Push(modasfunc)
558	L.Push(LString(name))
559	L.Call(1, 1)
560	ret := L.reg.Pop()
561	modv := L.GetField(loaded, name)
562	if ret != LNil && modv == loopdetection {
563		L.SetField(loaded, name, ret)
564		L.Push(ret)
565	} else if modv == loopdetection {
566		L.SetField(loaded, name, LTrue)
567		L.Push(LTrue)
568	} else {
569		L.Push(modv)
570	}
571	return 1
572}
573
574/* }}} */
575
576/* hidden features {{{ */
577
578func baseNewProxy(L *LState) int {
579	ud := L.NewUserData()
580	L.SetTop(1)
581	if L.Get(1) == LTrue {
582		L.SetMetatable(ud, L.NewTable())
583	} else if d, ok := L.Get(1).(*LUserData); ok {
584		L.SetMetatable(ud, L.GetMetatable(d))
585	}
586	L.Push(ud)
587	return 1
588}
589
590/* }}} */
591
592//
593