shithub: lu9

ref: 57ac5ca96be7288ce98a096c250ec7cb8ad31c48
dir: /lu9.c/

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

extern int luaopen_p9(lua_State*);
extern int luaopen_p9_note(lua_State*);
extern int luaopen_lpeg(lua_State*);

luaL_Reg preloadlibs[] = {
	{"p9", luaopen_p9},
	{"p9.note", luaopen_p9_note},
	{"lpeg", luaopen_lpeg},
	{nil, nil}
};

char flag[] = {
	['c'] = 0, /* bytecode dump */
	['i'] = 0, /* interactive */
	['v'] = 0, /* print version */
	['w'] = 0, /* enable warnings */
};

void
usage(void)
{
	fprint(2, "usage: %s [-ivw] [script] [arg ...]\n", argv0);
	exits("usage");
}

int
iscons(int fd)
{
	int n, c;
	char buf[64];
	
	if(fd2path(fd, buf, sizeof buf) != 0)
		sysfatal("fd2path: %r");
	n = strlen(buf);
	c = strlen("/dev/cons");
	return n >= c && strcmp(buf + n - c, "/dev/cons") == 0;
}

void
luaerror(lua_State *L)
{
	fprint(2, "%s: %s\n", argv0, lua_tostring(L, -1));
	lua_pop(L, 1);
}

int
pcallerror(lua_State *L)
{
	const char *m;
	
	m = lua_tostring(L, 1);
	if(m == nil){
		if(luaL_callmeta(L, 1, "__tostring") && lua_type(L, -1) == LUA_TSTRING)
			return 1;
		else
			m = lua_pushfstring(L,
				"(error object is a %s value)", luaL_typename(L, 1));
	}
	luaL_traceback(L, L, m, 1);
	return 1;
}

int
pcall(lua_State *L, int narg, int nres)
{
	int r, base;
	
	base = lua_gettop(L) - narg;
	lua_pushcfunction(L, pcallerror);
	lua_insert(L, base);
	r = lua_pcall(L, narg, nres, base);
	lua_remove(L, base);
	return r;
}

int
pushargs(lua_State *L)
{
	int i, n;
	
	/* Push the 'arg' array to stack */
	if(lua_getglobal(L, "arg") != LUA_TTABLE)
		luaL_error(L, "'arg' is not a table");
	n = luaL_len(L, -1);
	luaL_checkstack(L, n + 1, "too many arguments");
	for(i = 1; i <= n; i++)
		lua_rawgeti(L, -i, i);
	lua_remove(L, -i);
	return n;
}

int
runrepl(lua_State *L)
{
	int r, narg;
	char *ln;
	Biobuf bin;
	
	if(Binit(&bin, 0, OREAD) == -1)
		sysfatal("Binit: %r");
	r = LUA_OK;
	while(fprint(2, "> "), (ln = Brdstr(&bin, '\n', 1)) != nil){
		if(luaL_loadstring(L, ln) != LUA_OK){
			luaerror(L);
			continue;
		}
		narg = pushargs(L);
		if((r = pcall(L, narg, LUA_MULTRET)) != LUA_OK)
			luaerror(L);
	}
	Bterm(&bin);
	return r;
}

int
runfile(lua_State *L, char *file)
{
	int narg, r;
	
	if((r = luaL_loadfile(L, file)) == LUA_OK){
		narg = pushargs(L);
		r = pcall(L, narg, LUA_MULTRET);
	}
	if(r != LUA_OK)
		luaerror(L);
	return r;
}

int
dumpwriter(lua_State *, const void *p, size_t sz, void *data)
{
	Biobuf *w;
	
	w = data;
	if(sz != 0 && Bwrite(w, p, sz) != sz)
		return -1;
	return 0;
}

int
dumpfile(lua_State *L, char *file)
{
	int r;
	Biobuf w;
	
	if((r = luaL_loadfile(L, file)) != LUA_OK)
		luaerror(L);
	if(Binit(&w, 1, OWRITE) == -1)
		sysfatal("Binit: %r");
	if(lua_dump(L, dumpwriter, &w, 0) == -1)
		sysfatal("dump: %r");
	Bterm(&w);
	return r;
}

int
luamain(lua_State *L)
{
	int argc, i;
	char **argv, *file;
	
	argc = lua_tointeger(L, 1);
	argv = lua_touserdata(L, 2);
	file = argv[0];
	
	/* GC in generational mode */
	lua_gc(L, LUA_GCGEN, 0, 0);
	
	if(flag['c']){
		dumpfile(L, file);
		lua_pushboolean(L, 1);
		return 1;
	}
	
	if(flag['w'])
		lua_warning(L, "@on", 0);

	/* Signal for libraries to ignore LUA_* env. vars */
	lua_pushboolean(L, 1);
	lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
	
	luaL_openlibs(L);
	/*
	 * Preload additional libraries.
	 * Because dynamic loading of C libraries is not
	 * supported these must be manually loaded by the host,
	 * as in the case of standard libraries above.
	 * An alternative is to register library loaders in the
	 * package.preload table, which makes them run at the
	 * time the program requires the library.
	 */
	luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
	for(luaL_Reg *lib = preloadlibs; lib->func; lib++){
		lua_pushcfunction(L, lib->func);
		lua_setfield(L, -2, lib->name);
	}
	lua_pop(L, 1);
	
	/* Create global 'arg' table */
	lua_createtable(L, argc, 0);
	lua_pushstring(L, argv0);
	lua_rawseti(L, -2, 0);
	for(i = 1; i < argc; i++){
		lua_pushstring(L, argv[i]);
		lua_rawseti(L, -2, i);
	}
	lua_setglobal(L, "arg");
	
	if(file == nil){
		file = "/fd/0";
		if(flag['i'] && iscons(0))
			file = nil;
	}
	if(file != nil)
		runfile(L, file);
	if(flag['i'])
		runrepl(L);

	lua_pushboolean(L, 1);
	return 1;
}

void
main(int argc, char *argv[])
{
	int r, v;
	lua_State *L;
	
	ARGBEGIN{
	case 'c': flag['c'] = 1; break;
	case 'i': flag['i'] = 1; break;
	case 'v': flag['v'] += 1; break;
	case 'w': flag['w'] = 1; break;
	default: usage();
	}ARGEND;
	if(flag['v']){
		if(flag['v'] == 1)
			print("%s\n", LUA_VERSION_MAJOR "." LUA_VERSION_MINOR);
		else
			print("%s\n", LUA_RELEASE);
		exits(nil);
	}

	setfcr(getfcr() & ~(FPZDIV | FPOVFL | FPINVAL));
	if((L = luaL_newstate()) == nil)
		sysfatal("out of memory");
	lua_pushcfunction(L, luamain);
	lua_pushinteger(L, argc);
	lua_pushlightuserdata(L, argv);
	r = lua_pcall(L, 2, 1, 0);
	if(r != LUA_OK)
		luaerror(L);
	v = lua_toboolean(L, -1);
	lua_close(L);
	exits(r == LUA_OK && v == 1 ? nil : "errors");
}