From ce9f6632635222b38ef0b37b4b1273da4a1877b4 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Fri, 16 Apr 2021 17:25:39 +0800 Subject: fix small issues, add web assembly support. --- src/lua/ltable.c | 61 ++-- src/lua/lua.c | 659 +++++++++++++++++++++++++++++++++++++ src/lua/lua.hpp | 9 + src/lua/luac.c | 724 +++++++++++++++++++++++++++++++++++++++++ src/lua/luaconf.h | 6 +- src/lua/makefile | 222 +++++++------ src/yue_wasm.cpp | 160 +++++++++ src/yuescript/ast.hpp | 2 +- src/yuescript/yue_ast.h | 5 + src/yuescript/yue_compiler.cpp | 16 +- src/yuescript/yue_parser.cpp | 11 +- src/yuescript/yue_parser.h | 1 + 12 files changed, 1747 insertions(+), 129 deletions(-) create mode 100644 src/lua/lua.c create mode 100644 src/lua/lua.hpp create mode 100644 src/lua/luac.c create mode 100644 src/yue_wasm.cpp (limited to 'src') diff --git a/src/lua/ltable.c b/src/lua/ltable.c index b520cdf..33c1ab3 100644 --- a/src/lua/ltable.c +++ b/src/lua/ltable.c @@ -68,20 +68,25 @@ #define MAXHSIZE luaM_limitN(1u << MAXHBITS, Node) +/* +** When the original hash value is good, hashing by a power of 2 +** avoids the cost of '%'. +*/ #define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) -#define hashstr(t,str) hashpow2(t, (str)->hash) -#define hashboolean(t,p) hashpow2(t, p) -#define hashint(t,i) hashpow2(t, i) - - /* -** for some types, it is better to avoid modulus by power of 2, as -** they tend to have many 2 factors. +** for other types, it is better to avoid modulo by power of 2, as +** they can have many 2 factors. */ #define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) +#define hashstr(t,str) hashpow2(t, (str)->hash) +#define hashboolean(t,p) hashpow2(t, p) + +#define hashint(t,i) hashpow2(t, i) + + #define hashpointer(t,p) hashmod(t, point2uint(p)) @@ -135,24 +140,38 @@ static int l_hashfloat (lua_Number n) { */ static Node *mainposition (const Table *t, int ktt, const Value *kvl) { switch (withvariant(ktt)) { - case LUA_VNUMINT: - return hashint(t, ivalueraw(*kvl)); - case LUA_VNUMFLT: - return hashmod(t, l_hashfloat(fltvalueraw(*kvl))); - case LUA_VSHRSTR: - return hashstr(t, tsvalueraw(*kvl)); - case LUA_VLNGSTR: - return hashpow2(t, luaS_hashlongstr(tsvalueraw(*kvl))); + case LUA_VNUMINT: { + lua_Integer key = ivalueraw(*kvl); + return hashint(t, key); + } + case LUA_VNUMFLT: { + lua_Number n = fltvalueraw(*kvl); + return hashmod(t, l_hashfloat(n)); + } + case LUA_VSHRSTR: { + TString *ts = tsvalueraw(*kvl); + return hashstr(t, ts); + } + case LUA_VLNGSTR: { + TString *ts = tsvalueraw(*kvl); + return hashpow2(t, luaS_hashlongstr(ts)); + } case LUA_VFALSE: return hashboolean(t, 0); case LUA_VTRUE: return hashboolean(t, 1); - case LUA_VLIGHTUSERDATA: - return hashpointer(t, pvalueraw(*kvl)); - case LUA_VLCF: - return hashpointer(t, fvalueraw(*kvl)); - default: - return hashpointer(t, gcvalueraw(*kvl)); + case LUA_VLIGHTUSERDATA: { + void *p = pvalueraw(*kvl); + return hashpointer(t, p); + } + case LUA_VLCF: { + lua_CFunction f = fvalueraw(*kvl); + return hashpointer(t, f); + } + default: { + GCObject *o = gcvalueraw(*kvl); + return hashpointer(t, o); + } } } diff --git a/src/lua/lua.c b/src/lua/lua.c new file mode 100644 index 0000000..46b48db --- /dev/null +++ b/src/lua/lua.c @@ -0,0 +1,659 @@ +/* +** $Id: lua.c $ +** Lua stand-alone interpreter +** See Copyright Notice in lua.h +*/ + +#define lua_c + +#include "lprefix.h" + + +#include +#include +#include + +#include + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#if !defined(LUA_PROGNAME) +#define LUA_PROGNAME "lua" +#endif + +#if !defined(LUA_INIT_VAR) +#define LUA_INIT_VAR "LUA_INIT" +#endif + +#define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX + + +static lua_State *globalL = NULL; + +static const char *progname = LUA_PROGNAME; + + +#if defined(LUA_USE_POSIX) /* { */ + +/* +** Use 'sigaction' when available. +*/ +static void setsignal (int sig, void (*handler)(int)) { + struct sigaction sa; + sa.sa_handler = handler; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); /* do not mask any signal */ + sigaction(sig, &sa, NULL); +} + +#else /* }{ */ + +#define setsignal signal + +#endif /* } */ + + +/* +** Hook set by signal function to stop the interpreter. +*/ +static 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. +*/ +static void laction (int i) { + int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT; + setsignal(i, SIG_DFL); /* if another SIGINT happens, terminate process */ + lua_sethook(globalL, lstop, flag, 1); +} + + +static void print_usage (const char *badoption) { + lua_writestringerror("%s: ", progname); + if (badoption[1] == 'e' || badoption[1] == 'l') + lua_writestringerror("'%s' needs argument\n", badoption); + else + lua_writestringerror("unrecognized option '%s'\n", badoption); + lua_writestringerror( + "usage: %s [options] [script [args]]\n" + "Available options are:\n" + " -e stat execute string 'stat'\n" + " -i enter interactive mode after executing 'script'\n" + " -l name require library 'name' into global 'name'\n" + " -v show version information\n" + " -E ignore environment variables\n" + " -W turn warnings on\n" + " -- stop handling options\n" + " - stop handling options and execute stdin\n" + , + progname); +} + + +/* +** Prints an error message, adding the program name in front of it +** (if present) +*/ +static 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'. +*/ +static 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 +*/ +static 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. +*/ +static 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' */ + setsignal(SIGINT, laction); /* set C-signal handler */ + status = lua_pcall(L, narg, nres, base); + setsignal(SIGINT, SIG_DFL); /* reset C-signal handler */ + lua_remove(L, base); /* remove message handler from the stack */ + return status; +} + + +static void print_version (void) { + lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT)); + lua_writeline(); +} + + +/* +** Create the 'arg' table, which stores all arguments from the +** command line ('argv'). It should be aligned so that, at index 0, +** it has 'argv[script]', which is the script name. The arguments +** to the script (everything after 'script') go to positive indices; +** other arguments (before the script name) go to negative indices. +** If there is no script name, assume interpreter's name as base. +*/ +static void createargtable (lua_State *L, char **argv, int argc, int script) { + int i, narg; + if (script == argc) script = 0; /* no script name? */ + narg = argc - (script + 1); /* number of positive indices */ + lua_createtable(L, narg, script + 1); + for (i = 0; i < argc; i++) { + lua_pushstring(L, argv[i]); + lua_rawseti(L, -2, i - script); + } + lua_setglobal(L, "arg"); +} + + +static int dochunk (lua_State *L, int status) { + if (status == LUA_OK) status = docall(L, 0, 0); + return report(L, status); +} + + +static int dofile (lua_State *L, const char *name) { + return dochunk(L, luaL_loadfile(L, name)); +} + + +static int dostring (lua_State *L, const char *s, const char *name) { + return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name)); +} + + +/* +** Calls 'require(name)' and stores the result in a global variable +** with the given name. +*/ +static int dolibrary (lua_State *L, const char *name) { + int status; + lua_getglobal(L, "require"); + lua_pushstring(L, name); + status = docall(L, 1, 1); /* call 'require(name)' */ + if (status == LUA_OK) + lua_setglobal(L, name); /* global[name] = require return */ + return report(L, status); +} + + +/* +** Push on the stack the contents of table 'arg' from 1 to #arg +*/ +static int pushargs (lua_State *L) { + int i, n; + if (lua_getglobal(L, "arg") != LUA_TTABLE) + luaL_error(L, "'arg' is not a table"); + n = (int)luaL_len(L, -1); + luaL_checkstack(L, n + 3, "too many arguments to script"); + for (i = 1; i <= n; i++) + lua_rawgeti(L, -i, i); + lua_remove(L, -i); /* remove table from the stack */ + return n; +} + + +static int handle_script (lua_State *L, char **argv) { + int status; + const char *fname = argv[0]; + if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0) + fname = NULL; /* stdin */ + status = luaL_loadfile(L, fname); + if (status == LUA_OK) { + int n = pushargs(L); /* push arguments to script */ + status = docall(L, n, LUA_MULTRET); + } + return report(L, status); +} + + +/* bits of various argument indicators in 'args' */ +#define has_error 1 /* bad option */ +#define has_i 2 /* -i */ +#define has_v 4 /* -v */ +#define has_e 8 /* -e */ +#define has_E 16 /* -E */ + + +/* +** Traverses all arguments from 'argv', returning a mask with those +** needed before running any Lua code (or an error code if it finds +** any invalid argument). 'first' returns the first not-handled argument +** (either the script name or a bad argument in case of error). +*/ +static int collectargs (char **argv, int *first) { + int args = 0; + int i; + for (i = 1; argv[i] != NULL; i++) { + *first = i; + if (argv[i][0] != '-') /* not an option? */ + return args; /* stop handling options */ + switch (argv[i][1]) { /* else check option */ + case '-': /* '--' */ + if (argv[i][2] != '\0') /* extra characters after '--'? */ + return has_error; /* invalid option */ + *first = i + 1; + return args; + case '\0': /* '-' */ + return args; /* script "name" is '-' */ + case 'E': + if (argv[i][2] != '\0') /* extra characters? */ + return has_error; /* invalid option */ + args |= has_E; + break; + case 'W': + if (argv[i][2] != '\0') /* extra characters? */ + return has_error; /* invalid option */ + break; + case 'i': + args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */ + case 'v': + if (argv[i][2] != '\0') /* extra characters? */ + return has_error; /* invalid option */ + args |= has_v; + break; + case 'e': + args |= has_e; /* FALLTHROUGH */ + case 'l': /* both options need an argument */ + if (argv[i][2] == '\0') { /* no concatenated argument? */ + i++; /* try next 'argv' */ + if (argv[i] == NULL || argv[i][0] == '-') + return has_error; /* no next argument or it is another option */ + } + break; + default: /* invalid option */ + return has_error; + } + } + *first = i; /* no script name */ + return args; +} + + +/* +** Processes options 'e' and 'l', which involve running Lua code, and +** 'W', which also affects the state. +** Returns 0 if some code raises an error. +*/ +static int runargs (lua_State *L, char **argv, int n) { + int i; + for (i = 1; i < n; i++) { + int option = argv[i][1]; + lua_assert(argv[i][0] == '-'); /* already checked */ + switch (option) { + case 'e': case 'l': { + int status; + const char *extra = argv[i] + 2; /* both options need an argument */ + if (*extra == '\0') extra = argv[++i]; + lua_assert(extra != NULL); + status = (option == 'e') + ? dostring(L, extra, "=(command line)") + : dolibrary(L, extra); + if (status != LUA_OK) return 0; + break; + } + case 'W': + lua_warning(L, "@on", 0); /* warnings on */ + break; + } + } + return 1; +} + + +static int handle_luainit (lua_State *L) { + const char *name = "=" LUA_INITVARVERSION; + const char *init = getenv(name + 1); + if (init == NULL) { + name = "=" LUA_INIT_VAR; + init = getenv(name + 1); /* try alternative name */ + } + if (init == NULL) return LUA_OK; + else if (init[0] == '@') + return dofile(L, init+1); + else + return dostring(L, init, name); +} + + +/* +** {================================================================== +** Read-Eval-Print Loop (REPL) +** =================================================================== +*/ + +#if !defined(LUA_PROMPT) +#define LUA_PROMPT "> " +#define LUA_PROMPT2 ">> " +#endif + +#if !defined(LUA_MAXINPUT) +#define LUA_MAXINPUT 512 +#endif + + +/* +** 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 +#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. +*/ +#if !defined(lua_readline) /* { */ + +#if defined(LUA_USE_READLINE) /* { */ + +#include +#include +#define lua_initreadline(L) ((void)L, rl_readline_name="lua") +#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)) + +#else /* }{ */ + +#define lua_initreadline(L) ((void)L) +#define lua_readline(L,b,p) \ + ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ + fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ +#define lua_saveline(L,line) { (void)L; (void)line; } +#define lua_freeline(L,b) { (void)L; (void)b; } + +#endif /* } */ + +#endif /* } */ + + +/* +** Return the string to be used as a prompt by the interpreter. Leave +** the string (or nil, if using the default value) on the stack, to keep +** it anchored. +*/ +static const char *get_prompt (lua_State *L, int firstline) { + if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL) + return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */ + else { /* apply 'tostring' over the value */ + const char *p = luaL_tolstring(L, -1, NULL); + lua_remove(L, -2); /* remove original value */ + 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. +*/ +static 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. +*/ +static 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). +*/ +static 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 +*/ +static 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. +*/ +static 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 +*/ +static 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. +*/ +static void doREPL (lua_State *L) { + int status; + const char *oldprogname = progname; + progname = NULL; /* no 'progname' on errors in interactive mode */ + lua_initreadline(L); + 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; +} + +/* }================================================================== */ + + +/* +** Main body of stand-alone interpreter (to be called in protected mode). +** Reads the options and handles them all. +*/ +static int pmain (lua_State *L) { + int argc = (int)lua_tointeger(L, 1); + char **argv = (char **)lua_touserdata(L, 2); + int script; + int args = collectargs(argv, &script); + luaL_checkversion(L); /* check that interpreter has correct version */ + if (argv[0] && argv[0][0]) progname = argv[0]; + if (args == has_error) { /* bad arg? */ + print_usage(argv[script]); /* 'script' has index of bad arg. */ + return 0; + } + if (args & has_v) /* option '-v'? */ + print_version(); + if (args & has_E) { /* option '-E'? */ + lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */ + lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); + } + luaL_openlibs(L); /* open standard libraries */ + createargtable(L, argv, argc, script); /* create table 'arg' */ + lua_gc(L, LUA_GCGEN, 0, 0); /* GC in generational mode */ + if (!(args & has_E)) { /* no option '-E'? */ + if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ + return 0; /* error running LUA_INIT */ + } + if (!runargs(L, argv, script)) /* execute arguments -e and -l */ + return 0; /* something failed */ + if (script < argc && /* execute main script (if there is one) */ + handle_script(L, argv + script) != LUA_OK) + return 0; + if (args & has_i) /* -i option? */ + doREPL(L); /* do read-eval-print loop */ + else if (script == argc && !(args & (has_e | has_v))) { /* no arguments? */ + if (lua_stdin_is_tty()) { /* running in interactive mode? */ + print_version(); + 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 */ + lua_pushinteger(L, argc); /* 1st argument */ + lua_pushlightuserdata(L, argv); /* 2nd argument */ + status = lua_pcall(L, 2, 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; +} + diff --git a/src/lua/lua.hpp b/src/lua/lua.hpp new file mode 100644 index 0000000..ec417f5 --- /dev/null +++ b/src/lua/lua.hpp @@ -0,0 +1,9 @@ +// lua.hpp +// Lua header files for C++ +// <> not supplied automatically because Lua also compiles as C++ + +extern "C" { +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +} diff --git a/src/lua/luac.c b/src/lua/luac.c new file mode 100644 index 0000000..56ddc41 --- /dev/null +++ b/src/lua/luac.c @@ -0,0 +1,724 @@ +/* +** $Id: luac.c $ +** Lua compiler (saves bytecodes to files; also lists bytecodes) +** See Copyright Notice in lua.h +*/ + +#define luac_c +#define LUA_CORE + +#include "lprefix.h" + +#include +#include +#include +#include +#include + +#include "lua.h" +#include "lauxlib.h" + +#include "ldebug.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lopnames.h" +#include "lstate.h" +#include "lundump.h" + +static void PrintFunction(const Proto* f, int full); +#define luaU_print PrintFunction + +#define PROGNAME "luac" /* default program name */ +#define OUTPUT PROGNAME ".out" /* default output file */ + +static int listing=0; /* list bytecodes? */ +static int dumping=1; /* dump bytecodes? */ +static int stripping=0; /* strip debug information? */ +static char Output[]={ OUTPUT }; /* default output file name */ +static const char* output=Output; /* actual output file name */ +static const char* progname=PROGNAME; /* actual program name */ +static TString **tmname; + +static void fatal(const char* message) +{ + fprintf(stderr,"%s: %s\n",progname,message); + exit(EXIT_FAILURE); +} + +static void cannot(const char* what) +{ + fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno)); + exit(EXIT_FAILURE); +} + +static void usage(const char* message) +{ + if (*message=='-') + fprintf(stderr,"%s: unrecognized option '%s'\n",progname,message); + else + fprintf(stderr,"%s: %s\n",progname,message); + fprintf(stderr, + "usage: %s [options] [filenames]\n" + "Available options are:\n" + " -l list (use -l -l for full listing)\n" + " -o name output to file 'name' (default is \"%s\")\n" + " -p parse only\n" + " -s strip debug information\n" + " -v show version information\n" + " -- stop handling options\n" + " - stop handling options and process stdin\n" + ,progname,Output); + exit(EXIT_FAILURE); +} + +#define IS(s) (strcmp(argv[i],s)==0) + +static int doargs(int argc, char* argv[]) +{ + int i; + int version=0; + if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0]; + for (i=1; itop+(i))) + +static const Proto* combine(lua_State* L, int n) +{ + if (n==1) + return toproto(L,-1); + else + { + Proto* f; + int i=n; + if (lua_load(L,reader,&i,"=(" PROGNAME ")",NULL)!=LUA_OK) fatal(lua_tostring(L,-1)); + f=toproto(L,-1); + for (i=0; ip[i]=toproto(L,i-n-1); + if (f->p[i]->sizeupvalues>0) f->p[i]->upvalues[0].instack=0; + } + f->sizelineinfo=0; + return f; + } +} + +static int writer(lua_State* L, const void* p, size_t size, void* u) +{ + UNUSED(L); + return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0); +} + +static int pmain(lua_State* L) +{ + int argc=(int)lua_tointeger(L,1); + char** argv=(char**)lua_touserdata(L,2); + const Proto* f; + int i; + tmname=G(L)->tmname; + if (!lua_checkstack(L,argc)) fatal("too many input files"); + for (i=0; i1); + if (dumping) + { + FILE* D= (output==NULL) ? stdout : fopen(output,"wb"); + if (D==NULL) cannot("open"); + lua_lock(L); + luaU_dump(L,f,writer,D,stripping); + lua_unlock(L); + if (ferror(D)) cannot("write"); + if (fclose(D)) cannot("close"); + } + return 0; +} + +int main(int argc, char* argv[]) +{ + lua_State* L; + int i=doargs(argc,argv); + argc-=i; argv+=i; + if (argc<=0) usage("no input files given"); + L=luaL_newstate(); + if (L==NULL) fatal("cannot create state: not enough memory"); + lua_pushcfunction(L,&pmain); + lua_pushinteger(L,argc); + lua_pushlightuserdata(L,argv); + if (lua_pcall(L,2,0,0)!=LUA_OK) fatal(lua_tostring(L,-1)); + lua_close(L); + return EXIT_SUCCESS; +} + +/* +** print bytecodes +*/ + +#define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-") +#define VOID(p) ((const void*)(p)) +#define eventname(i) (getstr(tmname[i])) + +static void PrintString(const TString* ts) +{ + const char* s=getstr(ts); + size_t i,n=tsslen(ts); + printf("\""); + for (i=0; ik[i]; + switch (ttypetag(o)) + { + case LUA_VNIL: + printf("N"); + break; + case LUA_VFALSE: + case LUA_VTRUE: + printf("B"); + break; + case LUA_VNUMFLT: + printf("F"); + break; + case LUA_VNUMINT: + printf("I"); + break; + case LUA_VSHRSTR: + case LUA_VLNGSTR: + printf("S"); + break; + default: /* cannot happen */ + printf("?%d",ttypetag(o)); + break; + } + printf("\t"); +} + +static void PrintConstant(const Proto* f, int i) +{ + const TValue* o=&f->k[i]; + switch (ttypetag(o)) + { + case LUA_VNIL: + printf("nil"); + break; + case LUA_VFALSE: + printf("false"); + break; + case LUA_VTRUE: + printf("true"); + break; + case LUA_VNUMFLT: + { + char buff[100]; + sprintf(buff,LUA_NUMBER_FMT,fltvalue(o)); + printf("%s",buff); + if (buff[strspn(buff,"-0123456789")]=='\0') printf(".0"); + break; + } + case LUA_VNUMINT: + printf(LUA_INTEGER_FMT,ivalue(o)); + break; + case LUA_VSHRSTR: + case LUA_VLNGSTR: + PrintString(tsvalue(o)); + break; + default: /* cannot happen */ + printf("?%d",ttypetag(o)); + break; + } +} + +#define COMMENT "\t; " +#define EXTRAARG GETARG_Ax(code[pc+1]) +#define EXTRAARGC (EXTRAARG*(MAXARG_C+1)) +#define ISK (isk ? "k" : "") + +static void PrintCode(const Proto* f) +{ + const Instruction* code=f->code; + int pc,n=f->sizecode; + for (pc=0; pc0) printf("[%d]\t",line); else printf("[-]\t"); + printf("%-9s\t",opnames[o]); + switch (o) + { + case OP_MOVE: + printf("%d %d",a,b); + break; + case OP_LOADI: + printf("%d %d",a,sbx); + break; + case OP_LOADF: + printf("%d %d",a,sbx); + break; + case OP_LOADK: + printf("%d %d",a,bx); + printf(COMMENT); PrintConstant(f,bx); + break; + case OP_LOADKX: + printf("%d",a); + printf(COMMENT); PrintConstant(f,EXTRAARG); + break; + case OP_LOADFALSE: + printf("%d",a); + break; + case OP_LFALSESKIP: + printf("%d",a); + break; + case OP_LOADTRUE: + printf("%d",a); + break; + case OP_LOADNIL: + printf("%d %d",a,b); + printf(COMMENT "%d out",b+1); + break; + case OP_GETUPVAL: + printf("%d %d",a,b); + printf(COMMENT "%s",UPVALNAME(b)); + break; + case OP_SETUPVAL: + printf("%d %d",a,b); + printf(COMMENT "%s",UPVALNAME(b)); + break; + case OP_GETTABUP: + printf("%d %d %d",a,b,c); + printf(COMMENT "%s",UPVALNAME(b)); + printf(" "); PrintConstant(f,c); + break; + case OP_GETTABLE: + printf("%d %d %d",a,b,c); + break; + case OP_GETI: + printf("%d %d %d",a,b,c); + break; + case OP_GETFIELD: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_SETTABUP: + printf("%d %d %d%s",a,b,c,ISK); + printf(COMMENT "%s",UPVALNAME(a)); + printf(" "); PrintConstant(f,b); + if (isk) { printf(" "); PrintConstant(f,c); } + break; + case OP_SETTABLE: + printf("%d %d %d%s",a,b,c,ISK); + if (isk) { printf(COMMENT); PrintConstant(f,c); } + break; + case OP_SETI: + printf("%d %d %d%s",a,b,c,ISK); + if (isk) { printf(COMMENT); PrintConstant(f,c); } + break; + case OP_SETFIELD: + printf("%d %d %d%s",a,b,c,ISK); + printf(COMMENT); PrintConstant(f,b); + if (isk) { printf(" "); PrintConstant(f,c); } + break; + case OP_NEWTABLE: + printf("%d %d %d",a,b,c); + printf(COMMENT "%d",c+EXTRAARGC); + break; + case OP_SELF: + printf("%d %d %d%s",a,b,c,ISK); + if (isk) { printf(COMMENT); PrintConstant(f,c); } + break; + case OP_ADDI: + printf("%d %d %d",a,b,sc); + break; + case OP_ADDK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_SUBK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_MULK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_MODK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_POWK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_DIVK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_IDIVK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_BANDK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_BORK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_BXORK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_SHRI: + printf("%d %d %d",a,b,sc); + break; + case OP_SHLI: + printf("%d %d %d",a,b,sc); + break; + case OP_ADD: + printf("%d %d %d",a,b,c); + break; + case OP_SUB: + printf("%d %d %d",a,b,c); + break; + case OP_MUL: + printf("%d %d %d",a,b,c); + break; + case OP_MOD: + printf("%d %d %d",a,b,c); + break; + case OP_POW: + printf("%d %d %d",a,b,c); + break; + case OP_DIV: + printf("%d %d %d",a,b,c); + break; + case OP_IDIV: + printf("%d %d %d",a,b,c); + break; + case OP_BAND: + printf("%d %d %d",a,b,c); + break; + case OP_BOR: + printf("%d %d %d",a,b,c); + break; + case OP_BXOR: + printf("%d %d %d",a,b,c); + break; + case OP_SHL: + printf("%d %d %d",a,b,c); + break; + case OP_SHR: + printf("%d %d %d",a,b,c); + break; + case OP_MMBIN: + printf("%d %d %d",a,b,c); + printf(COMMENT "%s",eventname(c)); + break; + case OP_MMBINI: + printf("%d %d %d %d",a,sb,c,isk); + printf(COMMENT "%s",eventname(c)); + if (isk) printf(" flip"); + break; + case OP_MMBINK: + printf("%d %d %d %d",a,b,c,isk); + printf(COMMENT "%s ",eventname(c)); PrintConstant(f,b); + if (isk) printf(" flip"); + break; + case OP_UNM: + printf("%d %d",a,b); + break; + case OP_BNOT: + printf("%d %d",a,b); + break; + case OP_NOT: + printf("%d %d",a,b); + break; + case OP_LEN: + printf("%d %d",a,b); + break; + case OP_CONCAT: + printf("%d %d",a,b); + break; + case OP_CLOSE: + printf("%d",a); + break; + case OP_TBC: + printf("%d",a); + break; + case OP_JMP: + printf("%d",GETARG_sJ(i)); + printf(COMMENT "to %d",GETARG_sJ(i)+pc+2); + break; + case OP_EQ: + printf("%d %d %d",a,b,isk); + break; + case OP_LT: + printf("%d %d %d",a,b,isk); + break; + case OP_LE: + printf("%d %d %d",a,b,isk); + break; + case OP_EQK: + printf("%d %d %d",a,b,isk); + printf(COMMENT); PrintConstant(f,b); + break; + case OP_EQI: + printf("%d %d %d",a,sb,isk); + break; + case OP_LTI: + printf("%d %d %d",a,sb,isk); + break; + case OP_LEI: + printf("%d %d %d",a,sb,isk); + break; + case OP_GTI: + printf("%d %d %d",a,sb,isk); + break; + case OP_GEI: + printf("%d %d %d",a,sb,isk); + break; + case OP_TEST: + printf("%d %d",a,isk); + break; + case OP_TESTSET: + printf("%d %d %d",a,b,isk); + break; + case OP_CALL: + printf("%d %d %d",a,b,c); + printf(COMMENT); + if (b==0) printf("all in "); else printf("%d in ",b-1); + if (c==0) printf("all out"); else printf("%d out",c-1); + break; + case OP_TAILCALL: + printf("%d %d %d",a,b,c); + printf(COMMENT "%d in",b-1); + break; + case OP_RETURN: + printf("%d %d %d",a,b,c); + printf(COMMENT); + if (b==0) printf("all out"); else printf("%d out",b-1); + break; + case OP_RETURN0: + break; + case OP_RETURN1: + printf("%d",a); + break; + case OP_FORLOOP: + printf("%d %d",a,bx); + printf(COMMENT "to %d",pc-bx+2); + break; + case OP_FORPREP: + printf("%d %d",a,bx); + printf(COMMENT "to %d",pc+bx+2); + break; + case OP_TFORPREP: + printf("%d %d",a,bx); + printf(COMMENT "to %d",pc+bx+2); + break; + case OP_TFORCALL: + printf("%d %d",a,c); + break; + case OP_TFORLOOP: + printf("%d %d",a,bx); + printf(COMMENT "to %d",pc-bx+2); + break; + case OP_SETLIST: + printf("%d %d %d",a,b,c); + if (isk) printf(COMMENT "%d",c+EXTRAARGC); + break; + case OP_CLOSURE: + printf("%d %d",a,bx); + printf(COMMENT "%p",VOID(f->p[bx])); + break; + case OP_VARARG: + printf("%d %d",a,c); + printf(COMMENT); + if (c==0) printf("all out"); else printf("%d out",c-1); + break; + case OP_VARARGPREP: + printf("%d",a); + break; + case OP_EXTRAARG: + printf("%d",ax); + break; +#if 0 + default: + printf("%d %d %d",a,b,c); + printf(COMMENT "not handled"); + break; +#endif + } + printf("\n"); + } +} + + +#define SS(x) ((x==1)?"":"s") +#define S(x) (int)(x),SS(x) + +static void PrintHeader(const Proto* f) +{ + const char* s=f->source ? getstr(f->source) : "=?"; + if (*s=='@' || *s=='=') + s++; + else if (*s==LUA_SIGNATURE[0]) + s="(bstring)"; + else + s="(string)"; + printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n", + (f->linedefined==0)?"main":"function",s, + f->linedefined,f->lastlinedefined, + S(f->sizecode),VOID(f)); + printf("%d%s param%s, %d slot%s, %d upvalue%s, ", + (int)(f->numparams),f->is_vararg?"+":"",SS(f->numparams), + S(f->maxstacksize),S(f->sizeupvalues)); + printf("%d local%s, %d constant%s, %d function%s\n", + S(f->sizelocvars),S(f->sizek),S(f->sizep)); +} + +static void PrintDebug(const Proto* f) +{ + int i,n; + n=f->sizek; + printf("constants (%d) for %p:\n",n,VOID(f)); + for (i=0; isizelocvars; + printf("locals (%d) for %p:\n",n,VOID(f)); + for (i=0; ilocvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1); + } + n=f->sizeupvalues; + printf("upvalues (%d) for %p:\n",n,VOID(f)); + for (i=0; iupvalues[i].instack,f->upvalues[i].idx); + } +} + +static void PrintFunction(const Proto* f, int full) +{ + int i,n=f->sizep; + PrintHeader(f); + PrintCode(f); + if (full) PrintDebug(f); + for (i=0; ip[i],full); +} diff --git a/src/lua/luaconf.h b/src/lua/luaconf.h index 38e14ed..e64d2ee 100644 --- a/src/lua/luaconf.h +++ b/src/lua/luaconf.h @@ -663,11 +663,13 @@ /* ** macros to improve jump prediction, used mostly for error handling -** and debug facilities. +** and debug facilities. (Some macros in the Lua API use these macros. +** Define LUA_NOBUILTIN if you do not want '__builtin_expect' in your +** code.) */ #if !defined(luai_likely) -#if defined(__GNUC__) +#if defined(__GNUC__) && !defined(LUA_NOBUILTIN) #define luai_likely(x) (__builtin_expect(((x) != 0), 1)) #define luai_unlikely(x) (__builtin_expect(((x) != 0), 0)) #else diff --git a/src/lua/makefile b/src/lua/makefile index ccd5315..f78c0b8 100644 --- a/src/lua/makefile +++ b/src/lua/makefile @@ -1,89 +1,54 @@ -# makefile for building Lua -# see INSTALL for installation instructions -# see ../Makefile and luaconf.h for further customization +# Makefile for building Lua +# See ../doc/readme.html for installation and customization instructions. # == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT ======================= -# Warnings valid for both C and C++ -CWARNSCPP= \ - -Wextra \ - -Wshadow \ - -Wsign-compare \ - -Wundef \ - -Wwrite-strings \ - -Wredundant-decls \ - -Wdisabled-optimization \ - -Wdouble-promotion \ - # the next warnings might be useful sometimes, - # but usually they generate too much noise - # -Werror \ - # -pedantic # warns if we use jump tables \ - # -Wconversion \ - # -Wsign-conversion \ - # -Wstrict-overflow=2 \ - # -Wformat=2 \ - # -Wcast-qual \ - -# The next warnings are neither valid nor needed for C++ -CWARNSC= -Wdeclaration-after-statement \ - -Wmissing-prototypes \ - -Wnested-externs \ - -Wstrict-prototypes \ - -Wc++-compat \ - -Wold-style-definition \ - - -CWARNS= $(CWARNSCPP) $(CWARNSC) - - -# -DEXTERNMEMCHECK -DHARDSTACKTESTS -DHARDMEMTESTS -DTRACEMEM='"tempmem"' -# -DMAXINDEXRK=1 -DLUA_COMPAT_5_3 -# -g -DLUA_USER_H='"ltests.h"' -# -pg -malign-double -# -DLUA_USE_CTYPE -DLUA_USE_APICHECK -# ('-ftrapv' for runtime checks of integer overflows) -# -fsanitize=undefined -ftrapv -fno-inline - - -LOCAL = $(CWARNS) - - -# enable Linux goodies -MYCFLAGS= $(LOCAL) -std=c99 - -UNAME_S:=$(shell uname -s) -ifeq ($(UNAME_S),Darwin) - MYCFLAGS += -DLUA_USE_MACOSX - MYLDFLAGS= $(LOCAL) -Wl -else - MYCFLAGS += -DLUA_USE_LINUX - MYLDFLAGS= $(LOCAL) -Wl,-E -endif - -CC= gcc -CFLAGS= -Wall -O2 $(MYCFLAGS) -fno-stack-protector -fno-common -AR= ar rc +# Your platform. See PLATS for possible values. +PLAT= guess + +CC= gcc -std=gnu99 +CFLAGS= -O2 -Wall -Wextra -DLUA_COMPAT_5_3 $(SYSCFLAGS) $(MYCFLAGS) +LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS) +LIBS= -lm $(SYSLIBS) $(MYLIBS) + +AR= ar rcu RANLIB= ranlib RM= rm -f +UNAME= uname + +SYSCFLAGS= +SYSLDFLAGS= +SYSLIBS= + +MYCFLAGS= +MYLDFLAGS= +MYLIBS= +MYOBJS= +# Special flags for compiler modules; -Os reduces code size. +CMCFLAGS= +# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= -# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE ========= +PLATS= guess aix bsd c89 freebsd generic linux linux-readline macosx mingw posix solaris +LUA_A= liblua.a +CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o +LIB_O= lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o +BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS) -LIBS = -lm +LUA_T= lua +LUA_O= lua.o -CORE_T= liblua.a -CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o \ - lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o \ - ltm.o lundump.o lvm.o lzio.o -AUX_O= lauxlib.o -LIB_O= lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o lstrlib.o \ - lutf8lib.o loadlib.o lcorolib.o linit.o +LUAC_T= luac +LUAC_O= luac.o -ALL_T= $(CORE_T) -ALL_O= $(CORE_O) $(AUX_O) $(LIB_O) -ALL_A= $(CORE_T) +ALL_O= $(BASE_O) $(LUA_O) $(LUAC_O) +ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T) +ALL_A= $(LUA_A) + +# Targets start here. +default: $(PLAT) all: $(ALL_T) @@ -91,40 +56,104 @@ o: $(ALL_O) a: $(ALL_A) -$(CORE_T): $(CORE_O) $(AUX_O) $(LIB_O) - $(AR) $@ $? +$(LUA_A): $(BASE_O) + $(AR) $@ $(BASE_O) $(RANLIB) $@ -llex.o: - $(CC) $(CFLAGS) -Os -c llex.c +$(LUA_T): $(LUA_O) $(LUA_A) + $(CC) -o $@ $(LDFLAGS) $(LUA_O) $(LUA_A) $(LIBS) -lparser.o: - $(CC) $(CFLAGS) -Os -c lparser.c - -lcode.o: - $(CC) $(CFLAGS) -Os -c lcode.c +$(LUAC_T): $(LUAC_O) $(LUA_A) + $(CC) -o $@ $(LDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS) +test: + ./$(LUA_T) -v clean: $(RM) $(ALL_T) $(ALL_O) depend: - @$(CC) $(CFLAGS) -MM *.c + @$(CC) $(CFLAGS) -MM l*.c echo: - @echo "CC = $(CC)" - @echo "CFLAGS = $(CFLAGS)" - @echo "AR = $(AR)" - @echo "RANLIB = $(RANLIB)" - @echo "RM = $(RM)" - @echo "MYCFLAGS = $(MYCFLAGS)" - @echo "MYLDFLAGS = $(MYLDFLAGS)" - @echo "DL = $(DL)" + @echo "PLAT= $(PLAT)" + @echo "CC= $(CC)" + @echo "CFLAGS= $(CFLAGS)" + @echo "LDFLAGS= $(SYSLDFLAGS)" + @echo "LIBS= $(LIBS)" + @echo "AR= $(AR)" + @echo "RANLIB= $(RANLIB)" + @echo "RM= $(RM)" + @echo "UNAME= $(UNAME)" + +# Convenience targets for popular platforms. +ALL= all + +help: + @echo "Do 'make PLATFORM' where PLATFORM is one of these:" + @echo " $(PLATS)" + @echo "See doc/readme.html for complete instructions." + +guess: + @echo Guessing `$(UNAME)` + @$(MAKE) `$(UNAME)` + +AIX aix: + $(MAKE) $(ALL) CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-ldl" SYSLDFLAGS="-brtl -bexpall" + +bsd: + $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-Wl,-E" + +c89: + $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_C89" CC="gcc -std=c89" + @echo '' + @echo '*** C89 does not guarantee 64-bit integers for Lua.' + @echo '*** Make sure to compile all external Lua libraries' + @echo '*** with LUA_USE_C89 to ensure consistency' + @echo '' -$(ALL_O): makefile +FreeBSD NetBSD OpenBSD freebsd: + $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE -I/usr/include/edit" SYSLIBS="-Wl,-E -ledit" CC="cc" + +generic: $(ALL) + +Linux linux: linux-noreadline + +linux-noreadline: + $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -ldl" + +linux-readline: + $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE" SYSLIBS="-Wl,-E -ldl -lreadline" + +Darwin macos macosx: + $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_MACOSX -DLUA_USE_READLINE" SYSLIBS="-lreadline" + +mingw: + $(MAKE) "LUA_A=lua54.dll" "LUA_T=lua.exe" \ + "AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \ + "SYSCFLAGS=-DLUA_BUILD_AS_DLL" "SYSLIBS=" "SYSLDFLAGS=-s" lua.exe + $(MAKE) "LUAC_T=luac.exe" luac.exe + +posix: + $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX" + +SunOS solaris: + $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN -D_REENTRANT" SYSLIBS="-ldl" + +# Targets that do not create files (not all makes understand .PHONY). +.PHONY: all $(PLATS) help test clean default o a depend echo + +# Compiler modules may use special flags. +llex.o: + $(CC) $(CFLAGS) $(CMCFLAGS) -c llex.c + +lparser.o: + $(CC) $(CFLAGS) $(CMCFLAGS) -c lparser.c + +lcode.o: + $(CC) $(CFLAGS) $(CMCFLAGS) -c lcode.c -# DO NOT EDIT -# automatically made with 'gcc -MM l*.c' +# DO NOT DELETE lapi.o: lapi.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lstring.h \ @@ -177,6 +206,9 @@ ltable.o: ltable.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h ltm.o: ltm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h +lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +luac.o: luac.c lprefix.h lua.h luaconf.h lauxlib.h ldebug.h lstate.h \ + lobject.h llimits.h ltm.h lzio.h lmem.h lopcodes.h lopnames.h lundump.h lundump.o: lundump.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \ lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h \ lundump.h diff --git a/src/yue_wasm.cpp b/src/yue_wasm.cpp new file mode 100644 index 0000000..fd8b150 --- /dev/null +++ b/src/yue_wasm.cpp @@ -0,0 +1,160 @@ +#include "yuescript/yue_compiler.h" + +extern "C" { +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +int luaopen_yue(lua_State* L); +} // extern "C" + +static void openlibs(void* state) { + lua_State* L = static_cast(state); + luaL_openlibs(L); + luaopen_yue(L); +} + +#define YUE_ARGS nullptr,openlibs + +#include +using namespace std::string_view_literals; +#include +using namespace emscripten; + +struct YueResult +{ + std::string codes; + std::string err; +}; + +YueResult tolua(const std::string& codes, bool reserveLineNumber = true, bool implicitReturnRoot = true, bool useSpaceOverTab = true) { + yue::YueConfig config; + config.reserveLineNumber = reserveLineNumber; + config.implicitReturnRoot = implicitReturnRoot; + config.useSpaceOverTab = useSpaceOverTab; + auto result = yue::YueCompiler{YUE_ARGS}.compile(codes, config); + return {result.codes, result.error}; +} + +std::string version() { return std::string(yue::version); } + +#define _DEFER(code,line) std::shared_ptr _defer_##line(nullptr, [&](auto){code;}) +#define DEFER(code) _DEFER(code,__LINE__) + +void pushYue(lua_State* L, std::string_view name) { + lua_getglobal(L, "package"); // package + lua_getfield(L, -1, "loaded"); // package loaded + lua_getfield(L, -1, "yue"); // package loaded yue + lua_pushlstring(L, &name.front(), name.size()); // package loaded yue name + lua_gettable(L, -2); // loaded[name], package loaded yue item + lua_insert(L, -4); // item package loaded yue + lua_pop(L, 3); // item +} + +void pushOptions(lua_State* L, int lineOffset) { + lua_newtable(L); + lua_pushliteral(L, "lint_global"); + lua_pushboolean(L, 0); + lua_rawset(L, -3); + lua_pushliteral(L, "implicit_return_root"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pushliteral(L, "reserve_line_number"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pushliteral(L, "space_over_tab"); + lua_pushboolean(L, 0); + lua_rawset(L, -3); + lua_pushliteral(L, "same_module"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pushliteral(L, "line_offset"); + lua_pushinteger(L, lineOffset); + lua_rawset(L, -3); +} + +std::string exec(const std::string& codes) { + lua_State* L = luaL_newstate(); + openlibs(L); + DEFER(lua_close(L)); + auto execStr = [&](const std::string& s) { + std::string result; + pushYue(L, "insert_loader"sv); + if (lua_pcall(L, 0, 0, 0) != 0) { + result += lua_tostring(L, -1); + result += '\n'; + return result; + } + pushYue(L, "loadstring"sv); + lua_pushlstring(L, s.c_str(), s.size()); + lua_pushliteral(L, "=(eval str)"); + if (lua_pcall(L, 2, 2, 0) != 0) { + result += lua_tostring(L, -1); + result += '\n'; + return result; + } + if (lua_isnil(L, -2) != 0) { + result += lua_tostring(L, -1); + result += '\n'; + return result; + } + lua_pop(L, 1); + pushYue(L, "pcall"sv); + lua_insert(L, -2); + int last = lua_gettop(L) - 2; + if (lua_pcall(L, 1, LUA_MULTRET, 0) != 0) { + result = lua_tostring(L, -1); + result += '\n'; + return result; + } + int cur = lua_gettop(L); + int retCount = cur - last; + bool success = lua_toboolean(L, -retCount) != 0; + if (success) { + if (retCount > 1) { + for (int i = 1; i < retCount; ++i) { + result += luaL_tolstring(L, -retCount + i, nullptr); + result += '\n'; + lua_pop(L, 1); + } + } + } else { + result += lua_tostring(L, -1); + result += '\n'; + } + return result; + }; + + execStr(R"yuescript( +_G.__output = {} +_G.print = (...)-> + len = select "#", ... + strs = {} + for i = 1, len + strs[#strs + 1] = tostring select i, ... + _G.__output[#_G.__output + 1] = table.concat strs, "\n" + )yuescript"); + std::string res2 = execStr(codes); + std::string res1 = execStr(R"yuescript( +res = table.concat _G.__output, "\n" +return res if res ~= "" + )yuescript"); + if (res1.empty()) { + return res2; + } else { + if (res2.empty()) { + return res1; + } else { + return res1 + res2; + } + } +} + +EMSCRIPTEN_BINDINGS(yue) { + value_array("YueResult") + .element(&YueResult::codes) + .element(&YueResult::err); + function("tolua", &tolua); + function("exec", &exec); + function("version", &version); +} + diff --git a/src/yuescript/ast.hpp b/src/yuescript/ast.hpp index 162a82e..0636a74 100644 --- a/src/yuescript/ast.hpp +++ b/src/yuescript/ast.hpp @@ -238,9 +238,9 @@ public: m_ptr = nullptr; } else { assert(accept(node)); + node->retain(); if (m_ptr) m_ptr->release(); m_ptr = node; - node->retain(); } } diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index 96968c9..4c33759 100644 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h @@ -763,6 +763,11 @@ AST_NODE(Block) AST_MEMBER(Block, &sep, &statements) AST_END(Block) +AST_NODE(BlockEnd) + ast_ptr block; + AST_MEMBER(BlockEnd, &block) +AST_END(BlockEnd) + AST_NODE(File) ast_ptr block; AST_MEMBER(File, &block) diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 6627189..caa5b9e 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -59,7 +59,7 @@ inline std::string s(std::string_view sv) { return std::string(sv); } -const std::string_view version = "0.7.5"sv; +const std::string_view version = "0.7.6"sv; const std::string_view extension = "yue"sv; class YueCompilerImpl { @@ -3217,7 +3217,11 @@ private: auto x = chainList.front(); auto chain = x->new_ptr(); if (opIt == chainList.begin() && ast_is(x)) { - chain->items.push_back(toAst(_withVars.top(), x)); + if (_withVars.empty()) { + throw std::logic_error(_info.errorMessage("short dot/colon syntax must be called within a with block"sv, x)); + } else { + chain->items.push_back(toAst(_withVars.top(), x)); + } } for (auto it = chainList.begin(); it != opIt; ++it) { chain->items.push_back(*it); @@ -3632,7 +3636,7 @@ private: } else { if (!codes.empty()) { if (isBlock) { - info = _parser.parse(codes); + info = _parser.parse(codes); if (!info.node) { info.error = info.error.substr(info.error.find(':') + 2); throw std::logic_error(_info.errorMessage(s("failed to expanded macro as block: "sv) + info.error, x)); @@ -3640,7 +3644,7 @@ private: } else { info = _parser.parse(codes); if (!info.node && allowBlockMacroReturn) { - info = _parser.parse(codes); + info = _parser.parse(codes); if (!info.node) { info.error = info.error.substr(info.error.find(':') + 2); throw std::logic_error(_info.errorMessage(s("failed to expanded macro as expr or block: "sv) + info.error, x)); @@ -3693,7 +3697,9 @@ private: info.node.set(exp); } } - if (auto block = info.node.as()) { + if (auto blockEnd = info.node.as()) { + auto block = blockEnd->block.get(); + info.node.set(block); for (auto stmt_ : block->statements.objects()) { auto stmt = static_cast(stmt_); if (auto global = stmt->content.as()) { diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index 2234a59..c76b574 100644 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp @@ -495,13 +495,13 @@ YueParser::YueParser() { State* st = reinterpret_cast(item.user_data); st->exportCount++; return true; - }) >> ((pl::user(export_default, [](const item_t& item) { + }) >> (pl::user(export_default >> Exp, [](const item_t& item) { State* st = reinterpret_cast(item.user_data); bool isValid = !st->exportDefault && st->exportCount == 1; st->exportDefault = true; return isValid; - }) >> Exp) - | (pl::user(true_(), [](const item_t& item) { + }) + | (not_(export_default) >> pl::user(true_(), [](const item_t& item) { State* st = reinterpret_cast(item.user_data); if (st->exportDefault && st->exportCount > 1) { return false; @@ -611,12 +611,13 @@ YueParser::YueParser() { Body = InBlock | Statement; - empty_line_stop = Space >> and_(Stop); + empty_line_stop = Space >> and_(Break); Line = and_(check_indent >> Space >> not_(BackcallOperator)) >> Statement | Advance >> ensure(and_(Space >> BackcallOperator) >> Statement, PopIndent) | empty_line_stop; Block = Seperator >> Line >> *(+Break >> Line); Shebang = expr("#!") >> *(not_(Stop) >> Any); - File = White >> -Shebang >> Block >> eof(); + BlockEnd = Block >> -(+Break >> Space >> and_(Stop)) >> Stop; + File = White >> -Shebang >> Block >> -(+Break >> Space >> and_(eof())) >> eof(); } ParseInfo YueParser::parse(std::string_view codes, rule& r) { diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h index 729304a..a614d01 100644 --- a/src/yuescript/yue_parser.h +++ b/src/yuescript/yue_parser.h @@ -310,6 +310,7 @@ private: AST_RULE(Statement) AST_RULE(Body) AST_RULE(Block) + AST_RULE(BlockEnd) AST_RULE(File) }; -- cgit v1.2.3-55-g6feb