luafaudes.cpp

Go to the documentation of this file.
00001 /** @file luafaudes.cpp Simple Lua stand-alone interpreter for lua/faudes
00002  
00003    This lua interpreter is almost a plain copy of the original lua.c 
00004    provided with the lua 5.1.3 distribution  and under an MIT license;
00005    see original lua.h or http://www.lua.org
00006 
00007    The Advanced-Readline patch has been applied, also with lua license,
00008    copyright see below. SWIG based Lua bindings and minor adjustments 
00009    (wellcome string, logging) have been added (and signed "luafaudes").
00010    Thomas Moor, 2008.
00011 
00012 
00013 @ingroup Tutorials
00014 
00015 
00016 */
00017 
00018 
00019 // luafaudes: skip doxygen
00020 #ifndef FAUDES_DOXYGEN
00021 
00022 /*
00023 ** $Id: lua.c,v 1.160.1.2 2007/12/28 15:32:23 roberto Exp $
00024 ** Lua stand-alone interpreter
00025 ** See Copyright Notice in lua.h
00026 */
00027 
00028 
00029 // luafaudes: include c libs since we dont use lua.h
00030 #include <csignal>
00031 #include <cstdio>
00032 #include <cstdlib>
00033 #include <cstring>
00034 
00035 
00036 // luafaudes: use my include file
00037 #include "libluafaudes.h"
00038 
00039 // below this line: all from lua provided interpreter lua.cpp, except tagged "luafaudes"
00040 
00041 static lua_State *globalL = NULL;
00042 static const char *progname = LUA_PROGNAME;
00043 
00044 
00045 static void lstop (lua_State *L, lua_Debug *ar) {
00046   (void)ar;  /* unused arg. */
00047   lua_sethook(L, NULL, 0, 0);
00048   luaL_error(L, "interrupted!");
00049 }
00050 
00051 
00052 static void laction (int i) {
00053   signal(i, SIG_DFL); /* if another SIGINT happens before lstop,
00054                               terminate process (default action) */
00055   lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
00056 }
00057 
00058 
00059 static void print_usage (void) {
00060   fprintf(stderr,
00061   "usage: %s [options] [script [args]].\n"
00062   "Available options are:\n"
00063   "  -e stat  execute string " LUA_QL("stat") "\n"
00064   "  -l name  require library " LUA_QL("name") "\n"
00065   "  -i       enter interactive mode after executing " LUA_QL("script") "\n"
00066   "  -v       show version information\n"
00067   "  -d       pass on libFAUDES messages to console\n"   /* luafaudes: d-option */
00068   "  -x       load libFAUDES extension\n"                /* luafaudes: x-option */
00069   "  --       stop handling options\n"
00070   "  -        execute stdin and stop handling options\n"
00071   ,
00072   progname);
00073   fflush(stderr);
00074 }
00075 
00076 
00077 static void l_message (const char *pname, const char *msg) {
00078   if (pname) fprintf(stderr, "%s: ", pname);
00079   fprintf(stderr, "%s\n", msg);
00080   fflush(stderr);
00081 }
00082 
00083 
00084 static int report (lua_State *L, int status) {
00085   if (status && !lua_isnil(L, -1)) {
00086     const char *msg = lua_tostring(L, -1);
00087     if (msg == NULL) msg = "(error object is not a string)";
00088     l_message(progname, msg);
00089     lua_pop(L, 1);
00090   }
00091   return status;
00092 }
00093 
00094 
00095 static int traceback (lua_State *L) {
00096   if (!lua_isstring(L, 1))  /* 'message' not a string? */
00097     return 1;  /* keep it intact */
00098   lua_getfield(L, LUA_GLOBALSINDEX, "debug");
00099   if (!lua_istable(L, -1)) {
00100     lua_pop(L, 1);
00101     return 1;
00102   }
00103   lua_getfield(L, -1, "traceback");
00104   if (!lua_isfunction(L, -1)) {
00105     lua_pop(L, 2);
00106     return 1;
00107   }
00108   lua_pushvalue(L, 1);  /* pass error message */
00109   lua_pushinteger(L, 2);  /* skip this function and traceback */
00110   lua_call(L, 2, 1);  /* call debug.traceback */
00111   return 1;
00112 }
00113 
00114 
00115 static int docall (lua_State *L, int narg, int clear) {
00116   int status;
00117   int base = lua_gettop(L) - narg;  /* function index */
00118   lua_pushcfunction(L, traceback);  /* push traceback function */
00119   lua_insert(L, base);  /* put it under chunk and args */
00120   signal(SIGINT, laction);
00121   status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
00122   signal(SIGINT, SIG_DFL);
00123   lua_remove(L, base);  /* remove traceback function */
00124   /* force a complete garbage collection in case of errors */
00125   if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
00126   return status;
00127 }
00128 
00129 
00130 // luafaudes: my print version
00131 static void print_version (void) {
00132   std::stringstream sstr;
00133   sstr 
00134     << "Welcome to luafaudes console." << std::endl
00135     << "Versions: " << faudes::VersionString() << " / " << LUA_VERSION << std::endl
00136     << "Plug-Ins: " << faudes::PluginsString() << std::endl
00137     << "Credits: This libFAUDES interpreter is based on the projects Lua and SWIG." << std::endl
00138     << "Type 'faudes.Help()' for a list of faudes related types and functions." << std::endl
00139     << "Enter Ctrl-C to exit the luafaudes interpreter" << std::endl;
00140   l_message(NULL, sstr.str().c_str());
00141 }
00142 
00143 
00144 static int getargs (lua_State *L, char **argv, int n) {
00145   int narg;
00146   int i;
00147   int argc = 0;
00148   while (argv[argc]) argc++;  /* count total number of arguments */
00149   narg = argc - (n + 1);  /* number of arguments to the script */
00150   luaL_checkstack(L, narg + 3, "too many arguments to script");
00151   for (i=n+1; i < argc; i++)
00152     lua_pushstring(L, argv[i]);
00153   lua_createtable(L, narg, n + 1);
00154   for (i=0; i < argc; i++) {
00155     lua_pushstring(L, argv[i]);
00156     lua_rawseti(L, -2, i - n);
00157   }
00158   return narg;
00159 }
00160 
00161 
00162 static int dofile (lua_State *L, const char *name) {
00163   int status = luaL_loadfile(L, name) || docall(L, 0, 1);
00164   return report(L, status);
00165 }
00166 
00167 
00168 static int dostring (lua_State *L, const char *s, const char *name) {
00169   int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);
00170   return report(L, status);
00171 }
00172 
00173 
00174 static int dolibrary (lua_State *L, const char *name) {
00175   lua_getglobal(L, "require");
00176   lua_pushstring(L, name);
00177   return report(L, docall(L, 1, 1));
00178 }
00179 
00180 
00181 
00182 /* ------------------------------------------------------------------------ */
00183 
00184 #ifdef LUA_USE_READLINE
00185 /*
00186 
00187 ** luafaudes: here used to be advanced readline support patch by Mike Pall. However,
00188 ** we required some variations for SWIG __index functions and we also wanted identifier-
00189 ** completion in DESTool. Thus, we moved the original completer code to LuaState 
00190 ** (see lbp_function.cpp and lbp_completer.cpp) and provide a C interface via
00191 ** luafaudes_complete() (see common.i). The below code is reduced to the libreadline 
00192 ** interface. The original patch still ships with libFAUDES for reference and can be 
00193 ** inspected in lua.c in the Lua source tree. TMoor. 
00194 
00195 ** Advanced readline support for the GNU readline and history libraries
00196 ** or compatible replacements.
00197 **
00198 ** Copyright (C) 2004-2006 Mike Pall. Same license as Lua. See lua.h.
00199 **
00200 ** The (original) patch has been successfully tested with:
00201 **
00202 ** GNU    readline 2.2.1  (1998-07-17)
00203 ** GNU    readline 4.0    (1999-02-18) [harmless compiler warning]
00204 ** GNU    readline 4.3    (2002-07-16)
00205 ** GNU    readline 5.0    (2004-07-27)
00206 ** GNU    readline 5.1    (2005-12-07)
00207 ** NETBSD libedit  2.6.5  (2002-03-25)
00208 ** NETBSD libedit  2.6.9  (2004-05-01)
00209 */
00210 
00211 #include <ctype.h>
00212 
00213 static char *lua_rl_hist;
00214 static int lua_rl_histsize;
00215 
00216 static lua_State *lua_rl_L;  /* User data is not passed to rl callbacks. */
00217 
00218 /* luafaudes: pass on global state */
00219 static char **luafaudes_complete_L(const char *text, int start, int end) {
00220   return luafaudes_complete(lua_rl_L,text,start,end);
00221 }  
00222 
00223 /* Initialize readline library. */
00224 static void lua_rl_init(lua_State *L)
00225 {
00226   char *s;
00227 
00228   lua_rl_L = L;
00229 
00230   /* This allows for $if lua ... $endif in ~/.inputrc. */
00231   rl_readline_name = "lua";
00232   /* Break words at every non-identifier character except '.' and ':'. */
00233   rl_completer_word_break_characters = (char *) 
00234     "\t\r\n !\"#$%&'()*+,-/;<=>?@[\\]^`{|}~"; // luafaudes: pragmatic avoidance of compiler warning
00235 
00236   rl_completer_quote_characters = "\"'";
00237   rl_completion_append_character = '\0';
00238   rl_attempted_completion_function = luafaudes_complete_L; // luafaudes: use libFAUDES variant
00239   rl_initialize();
00240 
00241   /* Start using history, optionally set history size and load history file. */
00242   using_history();
00243   if ((s = getenv("LUA_HISTSIZE")) &&
00244       (lua_rl_histsize = atoi(s))) stifle_history(lua_rl_histsize);
00245   if ((lua_rl_hist = getenv("LUA_HISTORY"))) read_history(lua_rl_hist);
00246 }
00247 
00248 /* Finalize readline library. */
00249 static void lua_rl_exit(lua_State *L)
00250 {
00251   /* Optionally save history file. */
00252   if (lua_rl_hist) write_history(lua_rl_hist);
00253 }
00254 #else
00255 #define lua_rl_init(L)    ((void)L)
00256 #define lua_rl_exit(L)    ((void)L)
00257 #endif
00258 
00259 /* ------------------------------------------------------------------------ */
00260 
00261 
00262 static const char *get_prompt (lua_State *L, int firstline) {
00263   const char *p;
00264   lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
00265   p = lua_tostring(L, -1);
00266   if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);
00267   lua_pop(L, 1);  /* remove global */
00268   return p;
00269 }
00270 
00271 
00272 static int incomplete (lua_State *L, int status) {
00273   if (status == LUA_ERRSYNTAX) {
00274     size_t lmsg;
00275     const char *msg = lua_tolstring(L, -1, &lmsg);
00276     const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1);
00277     if (strstr(msg, LUA_QL("<eof>")) == tp) {
00278       lua_pop(L, 1);
00279       return 1;
00280     }
00281   }
00282   return 0;  /* else... */
00283 }
00284 
00285 
00286 static int pushline (lua_State *L, int firstline) {
00287   char buffer[LUA_MAXINPUT];
00288   char *b = buffer;
00289   size_t l;
00290   const char *prmt = get_prompt(L, firstline);
00291   if (lua_readline(L, b, prmt) == 0)
00292     return 0;  /* no input */
00293   l = strlen(b);
00294   luafaudes_lastline=b; // luafaudes: logging
00295   if (l > 0 && b[l-1] == '\n')  /* line ends with newline? */
00296     b[l-1] = '\0';  /* remove it */
00297   if (firstline && b[0] == '=')  /* first line starts with `=' ? */
00298     lua_pushfstring(L, "return %s", b+1);  /* change it to `return' */
00299   else
00300     lua_pushstring(L, b);
00301   lua_freeline(L, b);
00302   return 1;
00303 }
00304 
00305 
00306 static int loadline (lua_State *L) {
00307   int status;
00308   lua_settop(L, 0);
00309   if (!pushline(L, 1))
00310     return -1;  /* no input */
00311   for (;;) {  /* repeat until gets a complete line */
00312     status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
00313     if (!incomplete(L, status)) break;  /* cannot try to add lines? */
00314     if (!pushline(L, 0))  /* no more input? */
00315       return -1;
00316     lua_pushliteral(L, "\n");  /* add a new line... */
00317     lua_insert(L, -2);  /* ...between the two lines */
00318     lua_concat(L, 3);  /* join them */
00319   }
00320   lua_saveline(L, 1);
00321   lua_remove(L, 1);  /* remove line */
00322   return status;
00323 }
00324 
00325 
00326 static void dotty (lua_State *L) {
00327   int status;
00328   const char *oldprogname = progname;
00329   progname = NULL;
00330   lua_rl_init(L);
00331   while ((status = loadline(L)) != -1) {
00332     if (status == 0) status = docall(L, 0, 0);
00333     luafaudes_logwrite(luafaudes_lastline); // luafaudes: logging 
00334     report(L, status);
00335     if (status == 0 && lua_gettop(L) > 0) {  /* any result to print? */
00336       lua_getglobal(L, "print");
00337       lua_insert(L, 1);
00338       if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
00339         l_message(progname, lua_pushfstring(L,
00340                                "error calling " LUA_QL("print") " (%s)",
00341                                lua_tostring(L, -1)));
00342     }
00343   }
00344   lua_settop(L, 0);  /* clear stack */
00345   fputs("\n", stdout);
00346   fflush(stdout);
00347   lua_rl_exit(L);
00348   progname = oldprogname;
00349 }
00350 
00351 static int handle_script (lua_State *L, char **argv, int n) {
00352   int status;
00353   const char *fname;
00354   int narg = getargs(L, argv, n);  /* collect arguments */
00355   lua_setglobal(L, "arg");
00356   fname = argv[n];
00357   if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0) 
00358     fname = NULL;  /* stdin */
00359   status = luaL_loadfile(L, fname);
00360   lua_insert(L, -(narg+1));
00361   if (status == 0)
00362     status = docall(L, narg, 0);
00363   else
00364     lua_pop(L, narg);      
00365   return report(L, status);
00366 }
00367 
00368 
00369 /* check that argument has no extra characters at the end */
00370 #define notail(x) {if ((x)[2] != '\0') return -1;}
00371 
00372 
00373 // luafaudes: have extra options -d and -x
00374 static int collectargs (char **argv, int *pi, int *pv, int *pe, int *pd, int *px) {
00375   int i;
00376   for (i = 1; argv[i] != NULL; i++) {
00377     if (argv[i][0] != '-')  /* not an option? */
00378         return i;
00379     switch (argv[i][1]) {  /* option */
00380       case '-':
00381         notail(argv[i]);
00382         return (argv[i+1] != NULL ? i+1 : 0);
00383       case '\0':
00384         return i;
00385       case 'i':
00386         notail(argv[i]);
00387         *pi = 1;  /* go through */
00388       case 'v':
00389         notail(argv[i]);
00390         *pv = 1;
00391         break;
00392       case 'e':
00393         *pe = 1;  /* go through */
00394       case 'l':
00395         if (argv[i][2] == '\0') {
00396           i++;
00397           if (argv[i] == NULL) return -1;
00398         }
00399         break;
00400       case 'd': // luafaudes: d-option
00401         notail(argv[i]);
00402         *pd = 1;
00403         break;
00404       case 'x': // luafaudes: x-option
00405         if (argv[i][2] == '\0') {
00406           i++;
00407           if (argv[i] == NULL) return -1;
00408         }
00409         *px = 1;
00410         break;
00411       default: return -1;  /* invalid option */
00412     }
00413   }
00414   return 0;
00415 }
00416 
00417 
00418 // luafaudes: have extra option -x
00419 static int runargs (lua_State *L, char **argv, int n) {
00420   int i;
00421   for (i = 1; i < n; i++) {
00422     if (argv[i] == NULL) continue;
00423     lua_assert(argv[i][0] == '-');
00424     switch (argv[i][1]) {  /* option */
00425       case 'e': {
00426         const char *chunk = argv[i] + 2;
00427         if (*chunk == '\0') chunk = argv[++i];
00428         lua_assert(chunk != NULL);
00429         if (dostring(L, chunk, "=(command line)") != 0)
00430           return 1;
00431         break;
00432       }
00433       case 'l': {
00434         const char *filename = argv[i] + 2;
00435         if (*filename == '\0') filename = argv[++i];
00436         lua_assert(filename != NULL);
00437         if (dolibrary(L, filename))
00438           return 1;  /* stop if file fails */
00439         break;
00440       }
00441       case 'x': {  // luafaudes option x
00442         const char *filename = argv[i] + 2;
00443         if (*filename == '\0') filename = argv[++i];
00444         lua_assert(filename != NULL);
00445         if (luafaudes_loadext(L, filename)) {
00446           l_message("fatal error: failed to load extension", filename);
00447           return 1;  /* stop if file fails */
00448         }
00449         break;
00450       }
00451       default: break;
00452     }
00453   }
00454   return 0;
00455 }
00456 
00457 
00458 static int handle_luainit (lua_State *L) {
00459   const char *init = getenv(LUA_INIT);
00460   if (init == NULL) return 0;  /* status OK */
00461   else if (init[0] == '@')
00462     return dofile(L, init+1);
00463   else
00464     return dostring(L, init, "=" LUA_INIT);
00465 }
00466 
00467 
00468 struct Smain {
00469   int argc;
00470   char **argv;
00471   int status;
00472 };
00473 
00474 
00475 static int pmain (lua_State *L) {
00476   struct Smain *s = (struct Smain *)lua_touserdata(L, 1);
00477   char **argv = s->argv;
00478   int script;
00479   int has_i = 0, has_v = 0, has_e = 0, has_d=0, has_x=0; // luafaudes: -d and -x option
00480   globalL = L;
00481   if (argv[0] && argv[0][0]) progname = argv[0];
00482   luafaudes_initialize(L); // luafaudes: all of the below plus my namespace
00483   /*
00484   // original lua
00485   lua_gc(L, LUA_GCSTOP, 0);  
00486   luaL_openlibs(L);  
00487   lua_gc(L, LUA_GCRESTART, 0);
00488   */
00489   s->status = handle_luainit(L);
00490   if (s->status != 0) return 0;
00491   script = collectargs(argv, &has_i, &has_v, &has_e, &has_d, &has_x); // luafaudes: -d and -x option
00492   if (script < 0) {  /* invalid args? */
00493     print_usage();
00494     s->status = 1;
00495     return 0;
00496   }
00497   if (has_v) print_version(); 
00498   if (!has_d) luafaudes_mute(true); /* luafaudes: mute */
00499   if (!has_x) luafaudes_loaddefext(L, argv[0]);  /* luafaudes: default extension */
00500   s->status = runargs(L, argv, (script > 0) ? script : s->argc);
00501   if (s->status != 0) return 0;
00502   if (script)
00503     s->status = handle_script(L, argv, script);
00504   if (s->status != 0) return 0;
00505   if (has_i)
00506     dotty(L);
00507   else if (script == 0 && !has_e && !has_v) {
00508     if (lua_stdin_is_tty()) {
00509       print_version(); 
00510       dotty(L);
00511     }
00512     else dofile(L, NULL);  /* executes stdin as a file */
00513   }
00514   return 0;
00515 }
00516 
00517 
00518 int main (int argc, char **argv) {
00519   int status;
00520   struct Smain s;
00521   lua_State *L = lua_open();  /* create state */
00522   if (L == NULL) {
00523     l_message(argv[0], "cannot create state: not enough memory");
00524     return EXIT_FAILURE;
00525   }
00526   s.argc = argc;
00527   s.argv = argv;
00528   status = lua_cpcall(L, &pmain, &s);
00529   report(L, status);
00530   lua_close(L);
00531   return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;
00532 }
00533 
00534 
00535 // luafaudes: end skip doxygen
00536 #endif

libFAUDES 2.23h --- 2014.04.03 --- c++ api documentaion by doxygen