diff -ur lua-5.1.5-cc-form/lauxlib.cc lua_src/lauxlib.cc --- lua-5.1.5-cc-form/lauxlib.cc 2023-12-26 01:09:24.564618500 -0800 +++ lua_src/lauxlib.cc 2023-12-09 23:33:55.564519700 -0800 @@ -574,8 +574,8 @@ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ if (lf.f == NULL) return errfile(L, "reopen", fnameindex); /* skip eventual `#!...' */ - while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ; - lf.extraline = 0; + while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) {} + lf.extraline = 0; } ungetc(c, lf.f); status = lua_load(L, getF, &lf, lua_tostring(L, -1)); diff -ur lua-5.1.5-cc-form/lbaselib.cc lua_src/lbaselib.cc --- lua-5.1.5-cc-form/lbaselib.cc 2023-12-26 01:09:24.612653600 -0800 +++ lua_src/lbaselib.cc 2023-12-09 23:33:55.592547700 -0800 @@ -631,7 +631,7 @@ luaL_register(L, "_G", base_funcs); lua_pushliteral(L, LUA_VERSION); lua_setglobal(L, "_VERSION"); /* set global _VERSION */ - /* `ipairs' and `pairs' need auxiliary functions as upvalues */ + /* `ipairs' and `pairs' need auxliliary functions as upvalues */ auxopen(L, "ipairs", luaB_ipairs, ipairsaux); auxopen(L, "pairs", luaB_pairs, luaB_next); /* `newproxy' needs a weaktable as upvalue */ diff -ur lua-5.1.5-cc-form/lcode.cc lua_src/lcode.cc --- lua-5.1.5-cc-form/lcode.cc 2023-12-26 01:09:24.635738100 -0800 +++ lua_src/lcode.cc 2023-12-09 23:33:56.178547700 -0800 @@ -1,5 +1,5 @@ /* -** $Id: lcode.c,v 2.25.1.5 2011/01/31 14:53:16 roberto Exp $ +** $Id: lcode.c,v 2.25.1.3 2007/12/28 15:32:23 roberto Exp $ ** Code generator for Lua ** See Copyright Notice in lua.h */ @@ -411,6 +411,13 @@ } +void luaK_exp2reg (FuncState *fs, expdesc *e, int reg) { + luaK_dischargevars(fs, e); + freeexp(fs, e); + exp2reg(fs, e, reg); +} + + void luaK_exp2nextreg (FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); freeexp(fs, e); diff -ur lua-5.1.5-cc-form/lcode.h lua_src/lcode.h --- lua-5.1.5-cc-form/lcode.h 2023-12-26 01:09:24.663593600 -0800 +++ lua_src/lcode.h 2023-12-09 23:33:55.843620800 -0800 @@ -52,6 +52,7 @@ LUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r); LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2reg (FuncState *fs, expdesc *e, int reg); LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e); diff -ur lua-5.1.5-cc-form/llex.cc lua_src/llex.cc --- lua-5.1.5-cc-form/llex.cc 2023-12-26 01:09:25.016396300 -0800 +++ lua_src/llex.cc 2023-12-09 23:33:55.906615800 -0800 @@ -1,5 +1,5 @@ /* -** $Id: llex.c,v 2.20.1.2 2009/11/23 14:58:22 roberto Exp $ +** $Id: llex.c,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ @@ -35,11 +35,11 @@ /* ORDER RESERVED */ const char *const luaX_tokens [] = { - "and", "break", "do", "else", "elseif", + "and", "break", "continue", "each", "do", "else", "elseif", "end", "false", "for", "function", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", - "..", "...", "==", ">=", "<=", "~=", + "..", "...", "==", ">=", "<=", "~=", "!=", "", "", "", "", NULL }; @@ -388,6 +388,11 @@ if (ls->current != '=') return '~'; else { next(ls); return TK_NE; } } + case '!': { + next(ls); + if (ls->current != '=') return '!'; + else { next(ls); return TK_NE; } + } case '"': case '\'': { read_string(ls, ls->current, seminfo); diff -ur lua-5.1.5-cc-form/llex.h lua_src/llex.h --- lua-5.1.5-cc-form/llex.h 2023-12-26 01:09:25.048415200 -0800 +++ lua_src/llex.h 2023-12-09 23:33:55.406530700 -0800 @@ -23,13 +23,13 @@ */ enum RESERVED { /* terminal symbols denoted by reserved words */ - TK_AND = FIRST_RESERVED, TK_BREAK, + TK_AND = FIRST_RESERVED, TK_BREAK, TK_CONTINUE, TK_EACH, TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, /* other terminal symbols */ - TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER, - TK_NAME, TK_STRING, TK_EOS + TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_CNE, + TK_NUMBER, TK_NAME, TK_STRING, TK_EOS }; /* number of reserved words */ diff -ur lua-5.1.5-cc-form/lparser.cc lua_src/lparser.cc --- lua-5.1.5-cc-form/lparser.cc 2023-12-26 01:09:25.332105800 -0800 +++ lua_src/lparser.cc 2023-12-09 23:33:56.064573500 -0800 @@ -1,5 +1,5 @@ /* -** $Id: lparser.c,v 2.42.1.4 2011/10/21 19:31:42 roberto Exp $ +** $Id: lparser.c,v 2.42.1.3 2007/12/28 15:32:23 roberto Exp $ ** Lua Parser ** See Copyright Notice in lua.h */ @@ -40,6 +40,7 @@ typedef struct BlockCnt { struct BlockCnt *previous; /* chain */ int breaklist; /* list of jumps out of this loop */ + int continuelist; /* list of jumps to the loop's test */ lu_byte nactvar; /* # active locals outside the breakable structure */ lu_byte upval; /* true if some variable in the block is an upvalue */ lu_byte isbreakable; /* true if `block' is a loop */ @@ -284,6 +285,7 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) { bl->breaklist = NO_JUMP; + bl->continuelist = NO_JUMP; bl->isbreakable = isbreakable; bl->nactvar = fs->nactvar; bl->upval = 0; @@ -507,7 +509,8 @@ init_exp(&cc.v, VVOID, 0); /* no value (yet) */ luaK_exp2nextreg(ls->fs, t); /* fix it at stack top (for gc) */ checknext(ls, '{'); - do { + for (;;) { + int itemline = ls->linenumber; lua_assert(cc.v.k == VVOID || cc.tostore > 0); if (ls->t.token == '}') break; closelistfield(fs, &cc); @@ -529,7 +532,11 @@ break; } } - } while (testnext(ls, ',') || testnext(ls, ';')); + // -AJA- 2011/04/14: data tables: optional commas at end of a line + if (! (testnext(ls, ',') || testnext(ls, ';'))) + if (ls->linenumber == itemline) + break; + } check_match(ls, '}', '{', line); lastlistfield(fs, &cc); SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */ @@ -988,6 +995,23 @@ } +static void continuestat (LexState *ls) { + FuncState *fs = ls->fs; + BlockCnt *bl = fs->bl; + int upval = 0; + while (bl && !bl->isbreakable) { + upval |= bl->upval; + bl = bl->previous; + } + if (!bl) + luaX_syntaxerror(ls, "no loop to continue"); + if (upval) + luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); + luaK_concat(fs, &bl->continuelist, luaK_jump(fs)); +} + + + static void whilestat (LexState *ls, int line) { /* whilestat -> WHILE cond DO block END */ FuncState *fs = ls->fs; @@ -1001,6 +1025,7 @@ checknext(ls, TK_DO); block(ls); luaK_patchlist(fs, luaK_jump(fs), whileinit); + luaK_patchlist(fs, bl.continuelist, whileinit); /* continue goes to start, too */ check_match(ls, TK_END, TK_WHILE, line); leaveblock(fs); luaK_patchtohere(fs, condexit); /* false conditions finish the loop */ @@ -1017,6 +1042,7 @@ enterblock(fs, &bl2, 0); /* scope block */ luaX_next(ls); /* skip REPEAT */ chunk(ls); + luaK_patchtohere(fs, bl1.continuelist); check_match(ls, TK_UNTIL, TK_REPEAT, line); condexit = cond(ls); /* read condition (inside scope block) */ if (!bl2.upval) { /* no upvalues? */ @@ -1057,6 +1083,7 @@ block(ls); leaveblock(fs); /* end of scope for declared variables */ luaK_patchtohere(fs, prep); + luaK_patchtohere(fs, bl.previous->continuelist); /* continue, if any, jumps to here */ endfor = (isnum) ? luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP) : luaK_codeABC(fs, OP_TFORLOOP, base, 0, nvars); luaK_fixline(fs, line); /* pretend that `OP_FOR' starts the loop */ @@ -1127,6 +1154,95 @@ } +static int eachexpr (LexState *ls, expdesc *v, int islist) +{ + // -AJA- parse an expression and call 'ipairs' or 'pairs' with the + // expression as its single argument. + + const char *funcname = islist ? "ipairs" : "pairs"; + TString *t_funcname = luaX_newstring(ls, funcname, islist ? 6 : 5); + + FuncState *fs = ls->fs; + expdesc f; + expdesc args; + int base; + int nparams; + int line; + + init_exp(&f, VGLOBAL, NO_REG); + f.u.s.info = luaK_stringK(fs, t_funcname); + luaK_exp2nextreg(fs, &f); + +///--- funcargs(ls, &f); +///--- memcpy(v, &f, sizeof(*v)); + + line = ls->linenumber; + expr(ls, &args); + luaK_exp2nextreg(fs, &args); + luaK_setmultret(fs, &args); + + lua_assert(f.k == VNONRELOC); + lua_assert(hasmultret(args.k)); + base = f.u.s.info; // base register for call + nparams = LUA_MULTRET; // open call + + init_exp(v, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); + luaK_fixline(fs, line); + fs->freereg = base+1; + + return 1; +} + + +// -AJA- 2011/04/16: implemented this syntactic sugar. +// Equivalent to normal for loop with 'ipairs' if one variable is +// present or 'pairs' if two variables are given. +static void eachstat (LexState *ls, int line) { + /* eachstat -> EACH [ key, ] val IN expr DO */ + FuncState *fs = ls->fs; + TString *indexname; + TString *valuename = NULL; + int islist; + BlockCnt bl; + + enterblock(fs, &bl, 1); /* scope for loop and control variables */ + luaX_next(ls); /* skip `each' */ + indexname = str_checkname(ls); /* first variable name */ + if (ls->t.token == ',') { + luaX_next(ls); + valuename = str_checkname(ls); /* second variable name */ + islist = 0; + } else { + valuename = indexname; + indexname = luaX_newstring(ls, "_index", 6); + islist = 1; + } + + { + FuncState *fs = ls->fs; + expdesc e; + int nvars = 0; + int line; + int base = fs->freereg; + /* create control variables */ + new_localvarliteral(ls, "(for generator)", nvars++); + new_localvarliteral(ls, "(for state)", nvars++); + new_localvarliteral(ls, "(for control)", nvars++); + /* create declared variables */ + new_localvar(ls, indexname, nvars++); + new_localvar(ls, valuename, nvars++); + checknext(ls, TK_IN); + line = ls->linenumber; + adjust_assign(ls, 3, eachexpr(ls, &e, islist), &e); + luaK_checkstack(fs, 3); /* extra space to call generator */ + forbody(ls, base, line, nvars - 3, 0); + } + + check_match(ls, TK_END, TK_EACH, line); + leaveblock(fs); /* loop scope (`break' jumps to this point) */ +} + + static int test_then_block (LexState *ls) { /* test_then_block -> [IF | ELSEIF] cond THEN block */ int condexit; @@ -1289,6 +1405,10 @@ forstat(ls, line); return 0; } + case TK_EACH: { /* stat -> eachstat */ + eachstat(ls, line); + return 0; + } case TK_REPEAT: { /* stat -> repeatstat */ repeatstat(ls, line); return 0; @@ -1314,6 +1434,11 @@ breakstat(ls); return 1; /* must be last statement */ } + case TK_CONTINUE: { /* stat -> continuestat */ + luaX_next(ls); /* skip CONTINUE */ + continuestat(ls); + return 1; /* must be last statement */ + } default: { exprstat(ls); return 0; /* to avoid warnings */ diff -ur lua-5.1.5-cc-form/lstrlib.cc lua_src/lstrlib.cc --- lua-5.1.5-cc-form/lstrlib.cc 2023-12-26 01:09:25.509866300 -0800 +++ lua_src/lstrlib.cc 2023-12-09 23:33:56.278521300 -0800 @@ -1,5 +1,5 @@ /* -** $Id: lstrlib.c,v 1.132.1.5 2010/05/14 15:34:19 roberto Exp $ +** $Id: lstrlib.c,v 1.132.1.4 2008/07/11 17:27:21 roberto Exp $ ** Standard library for string operations and pattern-matching ** See Copyright Notice in lua.h */ @@ -797,6 +797,15 @@ continue; /* skip the 'addsize' at the end */ } case 's': { + // -AJA- 2017/07/01: allow '%s' to call __tostring metamethod + // on tables. A power patch by Doug Currie. + if (!lua_isstring(L, arg)) + { + lua_getglobal(L, "tostring"); + lua_pushvalue(L, arg); + lua_call(L, 1, 1); + lua_replace(L, arg); + } size_t l; const char *s = luaL_checklstring(L, arg, &l); if (!strchr(form, '.') && l >= 100) { diff -ur lua-5.1.5-cc-form/ltablib.cc lua_src/ltablib.cc --- lua-5.1.5-cc-form/ltablib.cc 2023-12-26 01:09:25.605850700 -0800 +++ lua_src/ltablib.cc 2023-12-09 23:33:56.406483200 -0800 @@ -137,7 +137,7 @@ if (!lua_isstring(L, -1)) luaL_error(L, "invalid value (%s) at index %d in table for " LUA_QL("concat"), luaL_typename(L, -1), i); - luaL_addvalue(b); + luaL_addvalue(b); }