shithub: lu9

Download patch

ref: 79b5ce7a3fce5e31f1060e57b2a1ddc5c21c1a43
parent: 7ac058376998864f82026ce1bb58944be142fb9a
author: kvik <kvik@a-b.xyz>
date: Tue Feb 23 15:21:14 EST 2021

lua9: flesh out standalone interpreter

--- a/lua.c
+++ b/lua.c
@@ -1,39 +1,199 @@
 #include <u.h>
 #include <libc.h>
+#include <bio.h>
 
 #include <lua.h>
 #include <lauxlib.h>
 #include <lualib.h>
 
-extern int luaopen_lpeg(lua_State *L);
+char flag[] = {
+	['i'] = 0, /* interactive */
+	['v'] = 0, /* print version */
+	['w'] = 0, /* enable warnings */
+};
 
 void
-main(int argc, char *argv[])
+usage(void)
 {
-	lua_State *L;
+	fprint(2, "usage: %s [-ivw] [script] [arg ...]\n", argv0);
+	exits("usage");
+}
+
+int
+iscons(int fd)
+{
+	int n, c;
+	char buf[64];
 	
-	setfcr(getfcr() & ~(FPZDIV | FPOVFL | FPINVAL));
-	
-	if((L = luaL_newstate()) == NULL)
-		sysfatal("out of memory");
-	luaL_openlibs(L);
-	luaL_requiref(L, "lpeg", luaopen_lpeg, 1);
+	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
+luamain(lua_State *L)
+{
+	int argc, i;
+	char **argv, *file;
+	
+	argc = lua_tointeger(L, 1);
+	argv = lua_touserdata(L, 2);
+	file = argv[0];
+	
+	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);
+	
+	/* GC in generational mode */
+	lua_gc(L, LUA_GCGEN, 0, 0);
+	
+	/* Create global 'arg' table */
 	lua_createtable(L, argc, 0);
-	for(int i = 0; i < argc; i++){
+	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);
 
-	if(luaL_loadfile(L, argv[1]) != LUA_OK)
-		sysfatal("%s", lua_tostring(L, -1));
-	for(int i = 1; i < argc; i++)
-		lua_pushstring(L, argv[i]);
-	if(lua_pcall(L, argc-1, LUA_MULTRET, 0) != LUA_OK)
-		sysfatal("%s", lua_tostring(L, -1));
-	lua_close(L);
+	lua_pushboolean(L, 1);
+	return 1;
+}
+
+void
+main(int argc, char *argv[])
+{
+	int r, v;
+	lua_State *L;
 	
-	exits(nil);
+	ARGBEGIN{
+	case 'i': flag['i'] = 1; break;
+	case 'v': flag['v'] = 1; break;
+	case 'w': flag['w'] = 1; break;
+	default: usage();
+	}ARGEND;
+	if(flag['v']){
+		print("%s\n", LUA_COPYRIGHT);
+		exits(nil);
+	}
+
+	setfcr(getfcr() & ~(FPZDIV | FPOVFL | FPINVAL));
+	if((L = luaL_newstate()) == NULL)
+		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");
 }