/* ** lx: Lua Extended, based on the Lua stand-alone interpreter */ #define lua_c #include #include #include #include #include #include #include #include #include "lxlib.h" #define LUA_PROMPT "> " #define LUA_PROMPT2 ">> " #define LUA_PROGNAME "lx" #define LUA_MAXINPUT 512 #define LUA_INIT_VAR "LUA_INIT" #define LUA_INITVARVERSION \ LUA_INIT_VAR "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR #define LUA_INITSTR "require 'lx.lxinit'" /* ** lua_stdin_is_tty detects whether the standard input is a 'tty' (that ** is, whether we're running lua interactively). */ #if !defined(lua_stdin_is_tty) /* { */ #if defined(LUA_USE_POSIX) /* { */ #include #define lua_stdin_is_tty() isatty(0) #elif defined(LUA_USE_WINDOWS) /* }{ */ #include #define lua_stdin_is_tty() _isatty(_fileno(stdin)) #else /* }{ */ /* ISO C definition */ #define lua_stdin_is_tty() 1 /* assume stdin is a tty */ #endif /* } */ #endif /* } */ /* ** lua_readline defines how to show a prompt and then read a line from ** the standard input. ** lua_saveline defines how to "save" a read line in a "history". ** lua_freeline defines how to free a line read by lua_readline. */ #include #include #define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) #define lua_saveline(L,line) ((void)L, add_history(line)) #define lua_freeline(L,b) ((void)L, free(b)) lua_State *globalL = NULL; const char *progname = LUA_PROGNAME; /* ** Hook set by signal function to stop the interpreter. */ void lstop (lua_State *L, lua_Debug *ar) { (void)ar; /* unused arg. */ lua_sethook(L, NULL, 0, 0); /* reset hook */ luaL_error(L, "interrupted!"); } /* ** Function to be called at a C signal. Because a C signal cannot ** just change a Lua state (as there is no proper synchronization), ** this function only sets a hook that, when called, will stop the ** interpreter. */ void laction (int i) { signal(i, SIG_DFL); /* if another SIGINT happens, terminate process */ lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); } /* ** Prints an error message, adding the program name in front of it ** (if present) */ void l_message (const char *pname, const char *msg) { if (pname) lua_writestringerror("%s: ", pname); lua_writestringerror("%s\n", msg); } /* ** Check whether 'status' is not OK and, if so, prints the error ** message on the top of the stack. It assumes that the error object ** is a string, as it was either generated by Lua or by 'msghandler'. */ int report (lua_State *L, int status) { if (status != LUA_OK) { const char *msg = lua_tostring(L, -1); l_message(progname, msg); lua_pop(L, 1); /* remove message */ } return status; } /* ** Message handler used to run all chunks */ int msghandler (lua_State *L) { const char *msg = lua_tostring(L, 1); if (msg == NULL) { /* is error object not a string? */ if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */ lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */ return 1; /* that is the message */ else msg = lua_pushfstring(L, "(error object is a %s value)", luaL_typename(L, 1)); } luaL_traceback(L, L, msg, 1); /* append a standard traceback */ return 1; /* return the traceback */ } /* ** Interface to 'lua_pcall', which sets appropriate message function ** and C-signal handler. Used to run all chunks. */ int docall (lua_State *L, int narg, int nres) { int status; int base = lua_gettop(L) - narg; /* function index */ lua_pushcfunction(L, msghandler); /* push message handler */ lua_insert(L, base); /* put it under function and args */ globalL = L; /* to be available to 'laction' */ signal(SIGINT, laction); /* set C-signal handler */ status = lua_pcall(L, narg, nres, base); signal(SIGINT, SIG_DFL); /* reset C-signal handler */ lua_remove(L, base); /* remove message handler from the stack */ return status; } void print_version (void) { lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT)); lua_writeline(); } int dochunk (lua_State *L, int status) { if (status == LUA_OK) status = docall(L, 0, 0); return report(L, status); } int dofile (lua_State *L, const char *name) { return dochunk(L, luaL_loadfile(L, name)); } int dostring (lua_State *L, const char *s, const char *name) { return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name)); } /* ** Returns the string to be used as a prompt by the interpreter. */ const char *get_prompt (lua_State *L, int firstline) { const char *p; lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2"); p = lua_tostring(L, -1); if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2); return p; } /* mark in error messages for incomplete statements */ #define EOFMARK "" #define marklen (sizeof(EOFMARK)/sizeof(char) - 1) /* ** Check whether 'status' signals a syntax error and the error ** message at the top of the stack ends with the above mark for ** incomplete statements. */ int incomplete (lua_State *L, int status) { if (status == LUA_ERRSYNTAX) { size_t lmsg; const char *msg = lua_tolstring(L, -1, &lmsg); if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) { lua_pop(L, 1); return 1; } } return 0; /* else... */ } /* ** Prompt the user, read a line, and push it into the Lua stack. */ int pushline (lua_State *L, int firstline) { char buffer[LUA_MAXINPUT]; char *b = buffer; size_t l; const char *prmt = get_prompt(L, firstline); int readstatus = lua_readline(L, b, prmt); if (readstatus == 0) return 0; /* no input (prompt will be popped by caller) */ lua_pop(L, 1); /* remove prompt */ l = strlen(b); if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ b[--l] = '\0'; /* remove it */ if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */ lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */ else lua_pushlstring(L, b, l); lua_freeline(L, b); return 1; } /* ** Try to compile line on the stack as 'return ;'; on return, stack ** has either compiled chunk or original line (if compilation failed). */ int addreturn (lua_State *L) { const char *line = lua_tostring(L, -1); /* original line */ const char *retline = lua_pushfstring(L, "return %s;", line); int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin"); if (status == LUA_OK) { lua_remove(L, -2); /* remove modified line */ if (line[0] != '\0') /* non empty? */ lua_saveline(L, line); /* keep history */ } else lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */ return status; } /* ** Read multiple lines until a complete Lua statement */ int multiline (lua_State *L) { for (;;) { /* repeat until gets a complete statement */ size_t len; const char *line = lua_tolstring(L, 1, &len); /* get what it has */ int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */ if (!incomplete(L, status) || !pushline(L, 0)) { lua_saveline(L, line); /* keep history */ return status; /* cannot or should not try to add continuation line */ } lua_pushliteral(L, "\n"); /* add newline... */ lua_insert(L, -2); /* ...between the two lines */ lua_concat(L, 3); /* join them */ } } /* ** Read a line and try to load (compile) it first as an expression (by ** adding "return " in front of it) and second as a statement. Return ** the final status of load/call with the resulting function (if any) ** in the top of the stack. */ int loadline (lua_State *L) { int status; lua_settop(L, 0); if (!pushline(L, 1)) return -1; /* no input */ if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */ status = multiline(L); /* try as command, maybe with continuation lines */ lua_remove(L, 1); /* remove line from the stack */ lua_assert(lua_gettop(L) == 1); return status; } /* ** Prints (calling the Lua 'print' function) any values on the stack */ void l_print (lua_State *L) { int n = lua_gettop(L); if (n > 0) { /* any result to be printed? */ luaL_checkstack(L, LUA_MINSTACK, "too many results to print"); lua_getglobal(L, "print"); lua_insert(L, 1); if (lua_pcall(L, n, 0, 0) != LUA_OK) l_message(progname, lua_pushfstring(L, "error calling 'print' (%s)", lua_tostring(L, -1))); } } /* ** Do the REPL: repeatedly read (load) a line, evaluate (call) it, and ** print any results. */ void doREPL (lua_State *L) { int status; const char *oldprogname = progname; progname = NULL; /* no 'progname' on errors in interactive mode */ while ((status = loadline(L)) != -1) { if (status == LUA_OK) status = docall(L, 0, LUA_MULTRET); if (status == LUA_OK) l_print(L); else report(L, status); } lua_settop(L, 0); /* clear stack */ lua_writeline(); progname = oldprogname; } int handle_luainit (lua_State *L) { const char *name = "=" LUA_INITVARVERSION; const char *init = LUA_INITSTR; return dostring(L, init, name); } /* ** Main body of stand-alone interpreter (to be called in protected mode). ** Reads the options and handles them all. */ int pmain (lua_State *L) { int status; luaL_checkversion(L); /* check that interpreter has correct version */ print_version(); luaL_openlibs(L); /* open standard libraries */ lx_openlibs(L); if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ return 0; /* error running LUA_INIT */ lua_getglobal(L, "require"); lua_pushstring(L, "main"); status = docall(L, 1, 1); /* call 'require("main")' */ if (status != LUA_OK) { // no main, launch a REPL if (lua_stdin_is_tty()) { /* running in interactive mode? */ doREPL(L); /* do read-eval-print loop */ } else { dofile(L, NULL); /* executes stdin as a file */ } } lua_pushboolean(L, 1); /* signal no errors */ return 1; } int main (int argc, char **argv) { int status, result; lua_State *L = luaL_newstate(); /* create state */ if (L == NULL) { l_message(argv[0], "cannot create state: not enough memory"); return EXIT_FAILURE; } lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */ status = lua_pcall(L, 0, 1, 0); /* do the call */ result = lua_toboolean(L, -1); /* get result */ report(L, status); lua_close(L); return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE; } /* vim: set sts=2 ts=2 sw=2 tw=0 et :*/