Commit c52f02b8 authored by capresti@gmail.com's avatar capresti@gmail.com
Browse files

- Applied patch-lua-5.1.4-3 from lua.org

git-svn-id: http://luainterface.googlecode.com/svn/trunk@26 63eb109e-e254-0410-a61e-ed0b8f8614f5
parent ff873f0f
/*
** $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
*/
#include <stdlib.h>
#define lcode_c
#define LUA_CORE
#include "lua.h"
#include "lcode.h"
#include "ldebug.h"
#include "ldo.h"
#include "lgc.h"
#include "llex.h"
#include "lmem.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lparser.h"
#include "ltable.h"
#define hasjumps(e) ((e)->t != (e)->f)
static int isnumeral(expdesc *e) {
return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP);
}
void luaK_nil (FuncState *fs, int from, int n) {
Instruction *previous;
if (fs->pc > fs->lasttarget) { /* no jumps to current position? */
if (fs->pc == 0) { /* function start? */
if (from >= fs->nactvar)
return; /* positions are already clean */
}
else {
previous = &fs->f->code[fs->pc-1];
if (GET_OPCODE(*previous) == OP_LOADNIL) {
int pfrom = GETARG_A(*previous);
int pto = GETARG_B(*previous);
if (pfrom <= from && from <= pto+1) { /* can connect both? */
if (from+n-1 > pto)
SETARG_B(*previous, from+n-1);
return;
}
}
}
}
luaK_codeABC(fs, OP_LOADNIL, from, from+n-1, 0); /* else no optimization */
}
int luaK_jump (FuncState *fs) {
int jpc = fs->jpc; /* save list of jumps to here */
int j;
fs->jpc = NO_JUMP;
j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP);
luaK_concat(fs, &j, jpc); /* keep them on hold */
return j;
}
void luaK_ret (FuncState *fs, int first, int nret) {
luaK_codeABC(fs, OP_RETURN, first, nret+1, 0);
}
static int condjump (FuncState *fs, OpCode op, int A, int B, int C) {
luaK_codeABC(fs, op, A, B, C);
return luaK_jump(fs);
}
static void fixjump (FuncState *fs, int pc, int dest) {
Instruction *jmp = &fs->f->code[pc];
int offset = dest-(pc+1);
lua_assert(dest != NO_JUMP);
if (abs(offset) > MAXARG_sBx)
luaX_syntaxerror(fs->ls, "control structure too long");
SETARG_sBx(*jmp, offset);
}
/*
** returns current `pc' and marks it as a jump target (to avoid wrong
** optimizations with consecutive instructions not in the same basic block).
*/
int luaK_getlabel (FuncState *fs) {
fs->lasttarget = fs->pc;
return fs->pc;
}
static int getjump (FuncState *fs, int pc) {
int offset = GETARG_sBx(fs->f->code[pc]);
if (offset == NO_JUMP) /* point to itself represents end of list */
return NO_JUMP; /* end of list */
else
return (pc+1)+offset; /* turn offset into absolute position */
}
static Instruction *getjumpcontrol (FuncState *fs, int pc) {
Instruction *pi = &fs->f->code[pc];
if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1))))
return pi-1;
else
return pi;
}
/*
** check whether list has any jump that do not produce a value
** (or produce an inverted value)
*/
static int need_value (FuncState *fs, int list) {
for (; list != NO_JUMP; list = getjump(fs, list)) {
Instruction i = *getjumpcontrol(fs, list);
if (GET_OPCODE(i) != OP_TESTSET) return 1;
}
return 0; /* not found */
}
static int patchtestreg (FuncState *fs, int node, int reg) {
Instruction *i = getjumpcontrol(fs, node);
if (GET_OPCODE(*i) != OP_TESTSET)
return 0; /* cannot patch other instructions */
if (reg != NO_REG && reg != GETARG_B(*i))
SETARG_A(*i, reg);
else /* no register to put value or register already has the value */
*i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i));
return 1;
}
static void removevalues (FuncState *fs, int list) {
for (; list != NO_JUMP; list = getjump(fs, list))
patchtestreg(fs, list, NO_REG);
}
static void patchlistaux (FuncState *fs, int list, int vtarget, int reg,
int dtarget) {
while (list != NO_JUMP) {
int next = getjump(fs, list);
if (patchtestreg(fs, list, reg))
fixjump(fs, list, vtarget);
else
fixjump(fs, list, dtarget); /* jump to default target */
list = next;
}
}
static void dischargejpc (FuncState *fs) {
patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc);
fs->jpc = NO_JUMP;
}
void luaK_patchlist (FuncState *fs, int list, int target) {
if (target == fs->pc)
luaK_patchtohere(fs, list);
else {
lua_assert(target < fs->pc);
patchlistaux(fs, list, target, NO_REG, target);
}
}
void luaK_patchtohere (FuncState *fs, int list) {
luaK_getlabel(fs);
luaK_concat(fs, &fs->jpc, list);
}
void luaK_concat (FuncState *fs, int *l1, int l2) {
if (l2 == NO_JUMP) return;
else if (*l1 == NO_JUMP)
*l1 = l2;
else {
int list = *l1;
int next;
while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */
list = next;
fixjump(fs, list, l2);
}
}
void luaK_checkstack (FuncState *fs, int n) {
int newstack = fs->freereg + n;
if (newstack > fs->f->maxstacksize) {
if (newstack >= MAXSTACK)
luaX_syntaxerror(fs->ls, "function or expression too complex");
fs->f->maxstacksize = cast_byte(newstack);
}
}
void luaK_reserveregs (FuncState *fs, int n) {
luaK_checkstack(fs, n);
fs->freereg += n;
}
static void freereg (FuncState *fs, int reg) {
if (!ISK(reg) && reg >= fs->nactvar) {
fs->freereg--;
lua_assert(reg == fs->freereg);
}
}
static void freeexp (FuncState *fs, expdesc *e) {
if (e->k == VNONRELOC)
freereg(fs, e->u.s.info);
}
static int addk (FuncState *fs, TValue *k, TValue *v) {
lua_State *L = fs->L;
TValue *idx = luaH_set(L, fs->h, k);
Proto *f = fs->f;
int oldsize = f->sizek;
if (ttisnumber(idx)) {
lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v));
return cast_int(nvalue(idx));
}
else { /* constant not found; create a new entry */
setnvalue(idx, cast_num(fs->nk));
luaM_growvector(L, f->k, fs->nk, f->sizek, TValue,
MAXARG_Bx, "constant table overflow");
while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
setobj(L, &f->k[fs->nk], v);
luaC_barrier(L, f, v);
return fs->nk++;
}
}
int luaK_stringK (FuncState *fs, TString *s) {
TValue o;
setsvalue(fs->L, &o, s);
return addk(fs, &o, &o);
}
int luaK_numberK (FuncState *fs, lua_Number r) {
TValue o;
setnvalue(&o, r);
return addk(fs, &o, &o);
}
static int boolK (FuncState *fs, int b) {
TValue o;
setbvalue(&o, b);
return addk(fs, &o, &o);
}
static int nilK (FuncState *fs) {
TValue k, v;
setnilvalue(&v);
/* cannot use nil as key; instead use table itself to represent nil */
sethvalue(fs->L, &k, fs->h);
return addk(fs, &k, &v);
}
void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {
if (e->k == VCALL) { /* expression is an open function call? */
SETARG_C(getcode(fs, e), nresults+1);
}
else if (e->k == VVARARG) {
SETARG_B(getcode(fs, e), nresults+1);
SETARG_A(getcode(fs, e), fs->freereg);
luaK_reserveregs(fs, 1);
}
}
void luaK_setoneret (FuncState *fs, expdesc *e) {
if (e->k == VCALL) { /* expression is an open function call? */
e->k = VNONRELOC;
e->u.s.info = GETARG_A(getcode(fs, e));
}
else if (e->k == VVARARG) {
SETARG_B(getcode(fs, e), 2);
e->k = VRELOCABLE; /* can relocate its simple result */
}
}
void luaK_dischargevars (FuncState *fs, expdesc *e) {
switch (e->k) {
case VLOCAL: {
e->k = VNONRELOC;
break;
}
case VUPVAL: {
e->u.s.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.s.info, 0);
e->k = VRELOCABLE;
break;
}
case VGLOBAL: {
e->u.s.info = luaK_codeABx(fs, OP_GETGLOBAL, 0, e->u.s.info);
e->k = VRELOCABLE;
break;
}
case VINDEXED: {
freereg(fs, e->u.s.aux);
freereg(fs, e->u.s.info);
e->u.s.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.s.info, e->u.s.aux);
e->k = VRELOCABLE;
break;
}
case VVARARG:
case VCALL: {
luaK_setoneret(fs, e);
break;
}
default: break; /* there is one value available (somewhere) */
}
}
static int code_label (FuncState *fs, int A, int b, int jump) {
luaK_getlabel(fs); /* those instructions may be jump targets */
return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump);
}
static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
luaK_dischargevars(fs, e);
switch (e->k) {
case VNIL: {
luaK_nil(fs, reg, 1);
break;
}
case VFALSE: case VTRUE: {
luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);
break;
}
case VK: {
luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info);
break;
}
case VKNUM: {
luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval));
break;
}
case VRELOCABLE: {
Instruction *pc = &getcode(fs, e);
SETARG_A(*pc, reg);
break;
}
case VNONRELOC: {
if (reg != e->u.s.info)
luaK_codeABC(fs, OP_MOVE, reg, e->u.s.info, 0);
break;
}
default: {
lua_assert(e->k == VVOID || e->k == VJMP);
return; /* nothing to do... */
}
}
e->u.s.info = reg;
e->k = VNONRELOC;
}
static void discharge2anyreg (FuncState *fs, expdesc *e) {
if (e->k != VNONRELOC) {
luaK_reserveregs(fs, 1);
discharge2reg(fs, e, fs->freereg-1);
}
}
static void exp2reg (FuncState *fs, expdesc *e, int reg) {
discharge2reg(fs, e, reg);
if (e->k == VJMP)
luaK_concat(fs, &e->t, e->u.s.info); /* put this jump in `t' list */
if (hasjumps(e)) {
int final; /* position after whole expression */
int p_f = NO_JUMP; /* position of an eventual LOAD false */
int p_t = NO_JUMP; /* position of an eventual LOAD true */
if (need_value(fs, e->t) || need_value(fs, e->f)) {
int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs);
p_f = code_label(fs, reg, 0, 1);
p_t = code_label(fs, reg, 1, 0);
luaK_patchtohere(fs, fj);
}
final = luaK_getlabel(fs);
patchlistaux(fs, e->f, final, reg, p_f);
patchlistaux(fs, e->t, final, reg, p_t);
}
e->f = e->t = NO_JUMP;
e->u.s.info = reg;
e->k = VNONRELOC;
}
void luaK_exp2nextreg (FuncState *fs, expdesc *e) {
luaK_dischargevars(fs, e);
freeexp(fs, e);
luaK_reserveregs(fs, 1);
exp2reg(fs, e, fs->freereg - 1);
}
int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
luaK_dischargevars(fs, e);
if (e->k == VNONRELOC) {
if (!hasjumps(e)) return e->u.s.info; /* exp is already in a register */
if (e->u.s.info >= fs->nactvar) { /* reg. is not a local? */
exp2reg(fs, e, e->u.s.info); /* put value on it */
return e->u.s.info;
}
}
luaK_exp2nextreg(fs, e); /* default */
return e->u.s.info;
}
void luaK_exp2val (FuncState *fs, expdesc *e) {
if (hasjumps(e))
luaK_exp2anyreg(fs, e);
else
luaK_dischargevars(fs, e);
}
int luaK_exp2RK (FuncState *fs, expdesc *e) {
luaK_exp2val(fs, e);
switch (e->k) {
case VKNUM:
case VTRUE:
case VFALSE:
case VNIL: {
if (fs->nk <= MAXINDEXRK) { /* constant fit in RK operand? */
e->u.s.info = (e->k == VNIL) ? nilK(fs) :
(e->k == VKNUM) ? luaK_numberK(fs, e->u.nval) :
boolK(fs, (e->k == VTRUE));
e->k = VK;
return RKASK(e->u.s.info);
}
else break;
}
case VK: {
if (e->u.s.info <= MAXINDEXRK) /* constant fit in argC? */
return RKASK(e->u.s.info);
else break;
}
default: break;
}
/* not a constant in the right range: put it in a register */
return luaK_exp2anyreg(fs, e);
}
void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
switch (var->k) {
case VLOCAL: {
freeexp(fs, ex);
exp2reg(fs, ex, var->u.s.info);
return;
}
case VUPVAL: {
int e = luaK_exp2anyreg(fs, ex);
luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0);
break;
}
case VGLOBAL: {
int e = luaK_exp2anyreg(fs, ex);
luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);
break;
}
case VINDEXED: {
int e = luaK_exp2RK(fs, ex);
luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e);
break;
}
default: {
lua_assert(0); /* invalid var kind to store */
break;
}
}
freeexp(fs, ex);
}
void luaK_self (FuncState *fs, expdesc *e, expdesc *key) {
int func;
luaK_exp2anyreg(fs, e);
freeexp(fs, e);
func = fs->freereg;
luaK_reserveregs(fs, 2);
luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key));
freeexp(fs, key);
e->u.s.info = func;
e->k = VNONRELOC;
}
static void invertjump (FuncState *fs, expdesc *e) {
Instruction *pc = getjumpcontrol(fs, e->u.s.info);
lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET &&
GET_OPCODE(*pc) != OP_TEST);
SETARG_A(*pc, !(GETARG_A(*pc)));
}
static int jumponcond (FuncState *fs, expdesc *e, int cond) {
if (e->k == VRELOCABLE) {
Instruction ie = getcode(fs, e);
if (GET_OPCODE(ie) == OP_NOT) {
fs->pc--; /* remove previous OP_NOT */
return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond);
}
/* else go through */
}
discharge2anyreg(fs, e);
freeexp(fs, e);
return condjump(fs, OP_TESTSET, NO_REG, e->u.s.info, cond);
}
void luaK_goiftrue (FuncState *fs, expdesc *e) {
int pc; /* pc of last jump */
luaK_dischargevars(fs, e);
switch (e->k) {
case VK: case VKNUM: case VTRUE: {
pc = NO_JUMP; /* always true; do nothing */
break;
}
case VFALSE: {
pc = luaK_jump(fs); /* always jump */
break;
}
case VJMP: {
invertjump(fs, e);
pc = e->u.s.info;
break;
}
default: {
pc = jumponcond(fs, e, 0);
break;
}
}
luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */
luaK_patchtohere(fs, e->t);
e->t = NO_JUMP;
}
static void luaK_goiffalse (FuncState *fs, expdesc *e) {
int pc; /* pc of last jump */
luaK_dischargevars(fs, e);
switch (e->k) {
case VNIL: case VFALSE: {
pc = NO_JUMP; /* always false; do nothing */
break;
}
case VTRUE: {
pc = luaK_jump(fs); /* always jump */
break;
}
case VJMP: {
pc = e->u.s.info;
break;
}
default: {
pc = jumponcond(fs, e, 1);
break;
}
}
luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */
luaK_patchtohere(fs, e->f);
e->f = NO_JUMP;
}
static void codenot (FuncState *fs, expdesc *e) {
luaK_dischargevars(fs, e);
switch (e->k) {
case VNIL: case VFALSE: {
e->k = VTRUE;
break;
}
case VK: case VKNUM: case VTRUE: {
e->k = VFALSE;
break;
}
case VJMP: {
invertjump(fs, e);
break;
}
case VRELOCABLE:
case VNONRELOC: {
discharge2anyreg(fs, e);
freeexp(fs, e);
e->u.s.info = luaK_codeABC(fs, OP_NOT, 0, e->u.s.info, 0);
e->k = VRELOCABLE;
break;
}
default: {
lua_assert(0); /* cannot happen */
break;
}
}
/* interchange true and false lists */
{ int temp = e->f; e->f = e->t; e->t = temp; }
removevalues(fs, e->f);
removevalues(fs, e->t);
}
void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
t->u.s.aux = luaK_exp2RK(fs, k);
t->k = VINDEXED;
}
static int constfolding (OpCode op, expdesc *e1, expdesc *e2) {
lua_Number v1, v2, r;
if (!isnumeral(e1) || !isnumeral(e2)) return 0;
v1 = e1->u.nval;
v2 = e2->u.nval;
switch (op) {
case OP_ADD: r = luai_numadd(v1, v2); break;
case OP_SUB: r = luai_numsub(v1, v2); break;
case OP_MUL: r = luai_nummul(v1, v2); break;
case OP_DIV:
if (v2 == 0) return 0; /* do not attempt to divide by 0 */
r = luai_numdiv(v1, v2); break;
case OP_MOD:
if (v2 == 0) return 0; /* do not attempt to divide by 0 */
r = luai_nummod(v1, v2); break;
case OP_POW: r = luai_numpow(v1, v2); break;
case OP_UNM: r = luai_numunm(v1); break;
case OP_LEN: return 0; /* no constant folding for 'len' */
default: lua_assert(0); r = 0; break;
}
if (luai_numisnan(r)) return 0; /* do not attempt to produce NaN */
e1->u.nval = r;
return 1;
}
static void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
if (constfolding(op, e1, e2))
return;
else {
int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0;
int o1 = luaK_exp2RK(fs, e1);
if (o1 > o2) {
freeexp(fs, e1);
freeexp(fs, e2);
}
else {
freeexp(fs, e2);
freeexp(fs, e1);
}
e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2);
e1->k = VRELOCABLE;
}
}
static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1,
expdesc *e2) {
int o1 = luaK_exp2RK(fs, e1);
int o2 = luaK_exp2RK(fs, e2);
freeexp(fs, e2);
freeexp(fs, e1);
if (cond == 0 && op != OP_EQ) {
int temp; /* exchange args to replace by `<' or `<=' */
temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */
cond = 1;
}
e1->u.s.info = condjump(fs, op, cond, o1, o2);
e1->k = VJMP;
}
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) {
expdesc e2;
e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0;
switch (op) {
case OPR_MINUS: {
if (!isnumeral(e))
luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */
codearith(fs, OP_UNM, e, &e2);
break;
}
case OPR_NOT: codenot(fs, e); break;
case OPR_LEN: {
luaK_exp2anyreg(fs, e); /* cannot operate on constants */
codearith(fs, OP_LEN, e, &e2);
break;
}
default: lua_assert(0);
}
}
void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
switch (op) {
case OPR_AND: {
luaK_goiftrue(fs, v);
break;
}
case OPR_OR: {
luaK_goiffalse(fs, v);
break;
}
case OPR_CONCAT: {
luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */
break;
}
case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:
case OPR_MOD: case OPR_POW: {
if (!isnumeral(v)) luaK_exp2RK(fs, v);
break;
}
default: {
luaK_exp2RK(fs, v);
break;
}
}
}
void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) {
switch (op) {
case OPR_AND: {
lua_assert(e1->t == NO_JUMP); /* list must be closed */
luaK_dischargevars(fs, e2);
luaK_concat(fs, &e2->f, e1->f);
*e1 = *e2;
break;
}
case OPR_OR: {
lua_assert(e1->f == NO_JUMP); /* list must be closed */
luaK_dischargevars(fs, e2);
luaK_concat(fs, &e2->t, e1->t);
*e1 = *e2;
break;
}
case OPR_CONCAT: {
luaK_exp2val(fs, e2);
if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) {
lua_assert(e1->u.s.info == GETARG_B(getcode(fs, e2))-1);
freeexp(fs, e1);
SETARG_B(getcode(fs, e2), e1->u.s.info);
e1->k = VRELOCABLE; e1->u.s.info = e2->u.s.info;
}
else {
luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */
codearith(fs, OP_CONCAT, e1, e2);
}
break;
}
case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break;
case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break;
case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break;
case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break;
case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break;
case OPR_POW: codearith(fs, OP_POW, e1, e2); break;
case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break;
case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break;
case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break;
case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break;
case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break;
case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break;
default: lua_assert(0);
}
}
void luaK_fixline (FuncState *fs, int line) {
fs->f->lineinfo[fs->pc - 1] = line;
}
static int luaK_code (FuncState *fs, Instruction i, int line) {
Proto *f = fs->f;
dischargejpc(fs); /* `pc' will change */
/* put new instruction in code array */
luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction,
MAX_INT, "code size overflow");
f->code[fs->pc] = i;
/* save corresponding line information */
luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int,
MAX_INT, "code size overflow");
f->lineinfo[fs->pc] = line;
return fs->pc++;
}
int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) {
lua_assert(getOpMode(o) == iABC);
lua_assert(getBMode(o) != OpArgN || b == 0);
lua_assert(getCMode(o) != OpArgN || c == 0);
return luaK_code(fs, CREATE_ABC(o, a, b, c), fs->ls->lastline);
}
int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);
lua_assert(getCMode(o) == OpArgN);
return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline);
}
void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {
int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1;
int b = (tostore == LUA_MULTRET) ? 0 : tostore;
lua_assert(tostore != 0);
if (c <= MAXARG_C)
luaK_codeABC(fs, OP_SETLIST, base, b, c);
else {
luaK_codeABC(fs, OP_SETLIST, base, b, 0);
luaK_code(fs, cast(Instruction, c), fs->ls->lastline);
}
fs->freereg = base + 1; /* free registers with list values */
}
/*
** $Id: lcode.c,v 2.25.1.5 2011/01/31 14:53:16 roberto Exp $
** Code generator for Lua
** See Copyright Notice in lua.h
*/
#include <stdlib.h>
#define lcode_c
#define LUA_CORE
#include "lua.h"
#include "lcode.h"
#include "ldebug.h"
#include "ldo.h"
#include "lgc.h"
#include "llex.h"
#include "lmem.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lparser.h"
#include "ltable.h"
#define hasjumps(e) ((e)->t != (e)->f)
static int isnumeral(expdesc *e) {
return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP);
}
void luaK_nil (FuncState *fs, int from, int n) {
Instruction *previous;
if (fs->pc > fs->lasttarget) { /* no jumps to current position? */
if (fs->pc == 0) { /* function start? */
if (from >= fs->nactvar)
return; /* positions are already clean */
}
else {
previous = &fs->f->code[fs->pc-1];
if (GET_OPCODE(*previous) == OP_LOADNIL) {
int pfrom = GETARG_A(*previous);
int pto = GETARG_B(*previous);
if (pfrom <= from && from <= pto+1) { /* can connect both? */
if (from+n-1 > pto)
SETARG_B(*previous, from+n-1);
return;
}
}
}
}
luaK_codeABC(fs, OP_LOADNIL, from, from+n-1, 0); /* else no optimization */
}
int luaK_jump (FuncState *fs) {
int jpc = fs->jpc; /* save list of jumps to here */
int j;
fs->jpc = NO_JUMP;
j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP);
luaK_concat(fs, &j, jpc); /* keep them on hold */
return j;
}
void luaK_ret (FuncState *fs, int first, int nret) {
luaK_codeABC(fs, OP_RETURN, first, nret+1, 0);
}
static int condjump (FuncState *fs, OpCode op, int A, int B, int C) {
luaK_codeABC(fs, op, A, B, C);
return luaK_jump(fs);
}
static void fixjump (FuncState *fs, int pc, int dest) {
Instruction *jmp = &fs->f->code[pc];
int offset = dest-(pc+1);
lua_assert(dest != NO_JUMP);
if (abs(offset) > MAXARG_sBx)
luaX_syntaxerror(fs->ls, "control structure too long");
SETARG_sBx(*jmp, offset);
}
/*
** returns current `pc' and marks it as a jump target (to avoid wrong
** optimizations with consecutive instructions not in the same basic block).
*/
int luaK_getlabel (FuncState *fs) {
fs->lasttarget = fs->pc;
return fs->pc;
}
static int getjump (FuncState *fs, int pc) {
int offset = GETARG_sBx(fs->f->code[pc]);
if (offset == NO_JUMP) /* point to itself represents end of list */
return NO_JUMP; /* end of list */
else
return (pc+1)+offset; /* turn offset into absolute position */
}
static Instruction *getjumpcontrol (FuncState *fs, int pc) {
Instruction *pi = &fs->f->code[pc];
if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1))))
return pi-1;
else
return pi;
}
/*
** check whether list has any jump that do not produce a value
** (or produce an inverted value)
*/
static int need_value (FuncState *fs, int list) {
for (; list != NO_JUMP; list = getjump(fs, list)) {
Instruction i = *getjumpcontrol(fs, list);
if (GET_OPCODE(i) != OP_TESTSET) return 1;
}
return 0; /* not found */
}
static int patchtestreg (FuncState *fs, int node, int reg) {
Instruction *i = getjumpcontrol(fs, node);
if (GET_OPCODE(*i) != OP_TESTSET)
return 0; /* cannot patch other instructions */
if (reg != NO_REG && reg != GETARG_B(*i))
SETARG_A(*i, reg);
else /* no register to put value or register already has the value */
*i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i));
return 1;
}
static void removevalues (FuncState *fs, int list) {
for (; list != NO_JUMP; list = getjump(fs, list))
patchtestreg(fs, list, NO_REG);
}
static void patchlistaux (FuncState *fs, int list, int vtarget, int reg,
int dtarget) {
while (list != NO_JUMP) {
int next = getjump(fs, list);
if (patchtestreg(fs, list, reg))
fixjump(fs, list, vtarget);
else
fixjump(fs, list, dtarget); /* jump to default target */
list = next;
}
}
static void dischargejpc (FuncState *fs) {
patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc);
fs->jpc = NO_JUMP;
}
void luaK_patchlist (FuncState *fs, int list, int target) {
if (target == fs->pc)
luaK_patchtohere(fs, list);
else {
lua_assert(target < fs->pc);
patchlistaux(fs, list, target, NO_REG, target);
}
}
void luaK_patchtohere (FuncState *fs, int list) {
luaK_getlabel(fs);
luaK_concat(fs, &fs->jpc, list);
}
void luaK_concat (FuncState *fs, int *l1, int l2) {
if (l2 == NO_JUMP) return;
else if (*l1 == NO_JUMP)
*l1 = l2;
else {
int list = *l1;
int next;
while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */
list = next;
fixjump(fs, list, l2);
}
}
void luaK_checkstack (FuncState *fs, int n) {
int newstack = fs->freereg + n;
if (newstack > fs->f->maxstacksize) {
if (newstack >= MAXSTACK)
luaX_syntaxerror(fs->ls, "function or expression too complex");
fs->f->maxstacksize = cast_byte(newstack);
}
}
void luaK_reserveregs (FuncState *fs, int n) {
luaK_checkstack(fs, n);
fs->freereg += n;
}
static void freereg (FuncState *fs, int reg) {
if (!ISK(reg) && reg >= fs->nactvar) {
fs->freereg--;
lua_assert(reg == fs->freereg);
}
}
static void freeexp (FuncState *fs, expdesc *e) {
if (e->k == VNONRELOC)
freereg(fs, e->u.s.info);
}
static int addk (FuncState *fs, TValue *k, TValue *v) {
lua_State *L = fs->L;
TValue *idx = luaH_set(L, fs->h, k);
Proto *f = fs->f;
int oldsize = f->sizek;
if (ttisnumber(idx)) {
lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v));
return cast_int(nvalue(idx));
}
else { /* constant not found; create a new entry */
setnvalue(idx, cast_num(fs->nk));
luaM_growvector(L, f->k, fs->nk, f->sizek, TValue,
MAXARG_Bx, "constant table overflow");
while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
setobj(L, &f->k[fs->nk], v);
luaC_barrier(L, f, v);
return fs->nk++;
}
}
int luaK_stringK (FuncState *fs, TString *s) {
TValue o;
setsvalue(fs->L, &o, s);
return addk(fs, &o, &o);
}
int luaK_numberK (FuncState *fs, lua_Number r) {
TValue o;
setnvalue(&o, r);
return addk(fs, &o, &o);
}
static int boolK (FuncState *fs, int b) {
TValue o;
setbvalue(&o, b);
return addk(fs, &o, &o);
}
static int nilK (FuncState *fs) {
TValue k, v;
setnilvalue(&v);
/* cannot use nil as key; instead use table itself to represent nil */
sethvalue(fs->L, &k, fs->h);
return addk(fs, &k, &v);
}
void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {
if (e->k == VCALL) { /* expression is an open function call? */
SETARG_C(getcode(fs, e), nresults+1);
}
else if (e->k == VVARARG) {
SETARG_B(getcode(fs, e), nresults+1);
SETARG_A(getcode(fs, e), fs->freereg);
luaK_reserveregs(fs, 1);
}
}
void luaK_setoneret (FuncState *fs, expdesc *e) {
if (e->k == VCALL) { /* expression is an open function call? */
e->k = VNONRELOC;
e->u.s.info = GETARG_A(getcode(fs, e));
}
else if (e->k == VVARARG) {
SETARG_B(getcode(fs, e), 2);
e->k = VRELOCABLE; /* can relocate its simple result */
}
}
void luaK_dischargevars (FuncState *fs, expdesc *e) {
switch (e->k) {
case VLOCAL: {
e->k = VNONRELOC;
break;
}
case VUPVAL: {
e->u.s.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.s.info, 0);
e->k = VRELOCABLE;
break;
}
case VGLOBAL: {
e->u.s.info = luaK_codeABx(fs, OP_GETGLOBAL, 0, e->u.s.info);
e->k = VRELOCABLE;
break;
}
case VINDEXED: {
freereg(fs, e->u.s.aux);
freereg(fs, e->u.s.info);
e->u.s.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.s.info, e->u.s.aux);
e->k = VRELOCABLE;
break;
}
case VVARARG:
case VCALL: {
luaK_setoneret(fs, e);
break;
}
default: break; /* there is one value available (somewhere) */
}
}
static int code_label (FuncState *fs, int A, int b, int jump) {
luaK_getlabel(fs); /* those instructions may be jump targets */
return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump);
}
static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
luaK_dischargevars(fs, e);
switch (e->k) {
case VNIL: {
luaK_nil(fs, reg, 1);
break;
}
case VFALSE: case VTRUE: {
luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);
break;
}
case VK: {
luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info);
break;
}
case VKNUM: {
luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval));
break;
}
case VRELOCABLE: {
Instruction *pc = &getcode(fs, e);
SETARG_A(*pc, reg);
break;
}
case VNONRELOC: {
if (reg != e->u.s.info)
luaK_codeABC(fs, OP_MOVE, reg, e->u.s.info, 0);
break;
}
default: {
lua_assert(e->k == VVOID || e->k == VJMP);
return; /* nothing to do... */
}
}
e->u.s.info = reg;
e->k = VNONRELOC;
}
static void discharge2anyreg (FuncState *fs, expdesc *e) {
if (e->k != VNONRELOC) {
luaK_reserveregs(fs, 1);
discharge2reg(fs, e, fs->freereg-1);
}
}
static void exp2reg (FuncState *fs, expdesc *e, int reg) {
discharge2reg(fs, e, reg);
if (e->k == VJMP)
luaK_concat(fs, &e->t, e->u.s.info); /* put this jump in `t' list */
if (hasjumps(e)) {
int final; /* position after whole expression */
int p_f = NO_JUMP; /* position of an eventual LOAD false */
int p_t = NO_JUMP; /* position of an eventual LOAD true */
if (need_value(fs, e->t) || need_value(fs, e->f)) {
int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs);
p_f = code_label(fs, reg, 0, 1);
p_t = code_label(fs, reg, 1, 0);
luaK_patchtohere(fs, fj);
}
final = luaK_getlabel(fs);
patchlistaux(fs, e->f, final, reg, p_f);
patchlistaux(fs, e->t, final, reg, p_t);
}
e->f = e->t = NO_JUMP;
e->u.s.info = reg;
e->k = VNONRELOC;
}
void luaK_exp2nextreg (FuncState *fs, expdesc *e) {
luaK_dischargevars(fs, e);
freeexp(fs, e);
luaK_reserveregs(fs, 1);
exp2reg(fs, e, fs->freereg - 1);
}
int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
luaK_dischargevars(fs, e);
if (e->k == VNONRELOC) {
if (!hasjumps(e)) return e->u.s.info; /* exp is already in a register */
if (e->u.s.info >= fs->nactvar) { /* reg. is not a local? */
exp2reg(fs, e, e->u.s.info); /* put value on it */
return e->u.s.info;
}
}
luaK_exp2nextreg(fs, e); /* default */
return e->u.s.info;
}
void luaK_exp2val (FuncState *fs, expdesc *e) {
if (hasjumps(e))
luaK_exp2anyreg(fs, e);
else
luaK_dischargevars(fs, e);
}
int luaK_exp2RK (FuncState *fs, expdesc *e) {
luaK_exp2val(fs, e);
switch (e->k) {
case VKNUM:
case VTRUE:
case VFALSE:
case VNIL: {
if (fs->nk <= MAXINDEXRK) { /* constant fit in RK operand? */
e->u.s.info = (e->k == VNIL) ? nilK(fs) :
(e->k == VKNUM) ? luaK_numberK(fs, e->u.nval) :
boolK(fs, (e->k == VTRUE));
e->k = VK;
return RKASK(e->u.s.info);
}
else break;
}
case VK: {
if (e->u.s.info <= MAXINDEXRK) /* constant fit in argC? */
return RKASK(e->u.s.info);
else break;
}
default: break;
}
/* not a constant in the right range: put it in a register */
return luaK_exp2anyreg(fs, e);
}
void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
switch (var->k) {
case VLOCAL: {
freeexp(fs, ex);
exp2reg(fs, ex, var->u.s.info);
return;
}
case VUPVAL: {
int e = luaK_exp2anyreg(fs, ex);
luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0);
break;
}
case VGLOBAL: {
int e = luaK_exp2anyreg(fs, ex);
luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);
break;
}
case VINDEXED: {
int e = luaK_exp2RK(fs, ex);
luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e);
break;
}
default: {
lua_assert(0); /* invalid var kind to store */
break;
}
}
freeexp(fs, ex);
}
void luaK_self (FuncState *fs, expdesc *e, expdesc *key) {
int func;
luaK_exp2anyreg(fs, e);
freeexp(fs, e);
func = fs->freereg;
luaK_reserveregs(fs, 2);
luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key));
freeexp(fs, key);
e->u.s.info = func;
e->k = VNONRELOC;
}
static void invertjump (FuncState *fs, expdesc *e) {
Instruction *pc = getjumpcontrol(fs, e->u.s.info);
lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET &&
GET_OPCODE(*pc) != OP_TEST);
SETARG_A(*pc, !(GETARG_A(*pc)));
}
static int jumponcond (FuncState *fs, expdesc *e, int cond) {
if (e->k == VRELOCABLE) {
Instruction ie = getcode(fs, e);
if (GET_OPCODE(ie) == OP_NOT) {
fs->pc--; /* remove previous OP_NOT */
return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond);
}
/* else go through */
}
discharge2anyreg(fs, e);
freeexp(fs, e);
return condjump(fs, OP_TESTSET, NO_REG, e->u.s.info, cond);
}
void luaK_goiftrue (FuncState *fs, expdesc *e) {
int pc; /* pc of last jump */
luaK_dischargevars(fs, e);
switch (e->k) {
case VK: case VKNUM: case VTRUE: {
pc = NO_JUMP; /* always true; do nothing */
break;
}
case VJMP: {
invertjump(fs, e);
pc = e->u.s.info;
break;
}
default: {
pc = jumponcond(fs, e, 0);
break;
}
}
luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */
luaK_patchtohere(fs, e->t);
e->t = NO_JUMP;
}
static void luaK_goiffalse (FuncState *fs, expdesc *e) {
int pc; /* pc of last jump */
luaK_dischargevars(fs, e);
switch (e->k) {
case VNIL: case VFALSE: {
pc = NO_JUMP; /* always false; do nothing */
break;
}
case VJMP: {
pc = e->u.s.info;
break;
}
default: {
pc = jumponcond(fs, e, 1);
break;
}
}
luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */
luaK_patchtohere(fs, e->f);
e->f = NO_JUMP;
}
static void codenot (FuncState *fs, expdesc *e) {
luaK_dischargevars(fs, e);
switch (e->k) {
case VNIL: case VFALSE: {
e->k = VTRUE;
break;
}
case VK: case VKNUM: case VTRUE: {
e->k = VFALSE;
break;
}
case VJMP: {
invertjump(fs, e);
break;
}
case VRELOCABLE:
case VNONRELOC: {
discharge2anyreg(fs, e);
freeexp(fs, e);
e->u.s.info = luaK_codeABC(fs, OP_NOT, 0, e->u.s.info, 0);
e->k = VRELOCABLE;
break;
}
default: {
lua_assert(0); /* cannot happen */
break;
}
}
/* interchange true and false lists */
{ int temp = e->f; e->f = e->t; e->t = temp; }
removevalues(fs, e->f);
removevalues(fs, e->t);
}
void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
t->u.s.aux = luaK_exp2RK(fs, k);
t->k = VINDEXED;
}
static int constfolding (OpCode op, expdesc *e1, expdesc *e2) {
lua_Number v1, v2, r;
if (!isnumeral(e1) || !isnumeral(e2)) return 0;
v1 = e1->u.nval;
v2 = e2->u.nval;
switch (op) {
case OP_ADD: r = luai_numadd(v1, v2); break;
case OP_SUB: r = luai_numsub(v1, v2); break;
case OP_MUL: r = luai_nummul(v1, v2); break;
case OP_DIV:
if (v2 == 0) return 0; /* do not attempt to divide by 0 */
r = luai_numdiv(v1, v2); break;
case OP_MOD:
if (v2 == 0) return 0; /* do not attempt to divide by 0 */
r = luai_nummod(v1, v2); break;
case OP_POW: r = luai_numpow(v1, v2); break;
case OP_UNM: r = luai_numunm(v1); break;
case OP_LEN: return 0; /* no constant folding for 'len' */
default: lua_assert(0); r = 0; break;
}
if (luai_numisnan(r)) return 0; /* do not attempt to produce NaN */
e1->u.nval = r;
return 1;
}
static void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
if (constfolding(op, e1, e2))
return;
else {
int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0;
int o1 = luaK_exp2RK(fs, e1);
if (o1 > o2) {
freeexp(fs, e1);
freeexp(fs, e2);
}
else {
freeexp(fs, e2);
freeexp(fs, e1);
}
e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2);
e1->k = VRELOCABLE;
}
}
static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1,
expdesc *e2) {
int o1 = luaK_exp2RK(fs, e1);
int o2 = luaK_exp2RK(fs, e2);
freeexp(fs, e2);
freeexp(fs, e1);
if (cond == 0 && op != OP_EQ) {
int temp; /* exchange args to replace by `<' or `<=' */
temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */
cond = 1;
}
e1->u.s.info = condjump(fs, op, cond, o1, o2);
e1->k = VJMP;
}
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) {
expdesc e2;
e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0;
switch (op) {
case OPR_MINUS: {
if (!isnumeral(e))
luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */
codearith(fs, OP_UNM, e, &e2);
break;
}
case OPR_NOT: codenot(fs, e); break;
case OPR_LEN: {
luaK_exp2anyreg(fs, e); /* cannot operate on constants */
codearith(fs, OP_LEN, e, &e2);
break;
}
default: lua_assert(0);
}
}
void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
switch (op) {
case OPR_AND: {
luaK_goiftrue(fs, v);
break;
}
case OPR_OR: {
luaK_goiffalse(fs, v);
break;
}
case OPR_CONCAT: {
luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */
break;
}
case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:
case OPR_MOD: case OPR_POW: {
if (!isnumeral(v)) luaK_exp2RK(fs, v);
break;
}
default: {
luaK_exp2RK(fs, v);
break;
}
}
}
void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) {
switch (op) {
case OPR_AND: {
lua_assert(e1->t == NO_JUMP); /* list must be closed */
luaK_dischargevars(fs, e2);
luaK_concat(fs, &e2->f, e1->f);
*e1 = *e2;
break;
}
case OPR_OR: {
lua_assert(e1->f == NO_JUMP); /* list must be closed */
luaK_dischargevars(fs, e2);
luaK_concat(fs, &e2->t, e1->t);
*e1 = *e2;
break;
}
case OPR_CONCAT: {
luaK_exp2val(fs, e2);
if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) {
lua_assert(e1->u.s.info == GETARG_B(getcode(fs, e2))-1);
freeexp(fs, e1);
SETARG_B(getcode(fs, e2), e1->u.s.info);
e1->k = VRELOCABLE; e1->u.s.info = e2->u.s.info;
}
else {
luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */
codearith(fs, OP_CONCAT, e1, e2);
}
break;
}
case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break;
case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break;
case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break;
case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break;
case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break;
case OPR_POW: codearith(fs, OP_POW, e1, e2); break;
case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break;
case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break;
case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break;
case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break;
case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break;
case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break;
default: lua_assert(0);
}
}
void luaK_fixline (FuncState *fs, int line) {
fs->f->lineinfo[fs->pc - 1] = line;
}
static int luaK_code (FuncState *fs, Instruction i, int line) {
Proto *f = fs->f;
dischargejpc(fs); /* `pc' will change */
/* put new instruction in code array */
luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction,
MAX_INT, "code size overflow");
f->code[fs->pc] = i;
/* save corresponding line information */
luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int,
MAX_INT, "code size overflow");
f->lineinfo[fs->pc] = line;
return fs->pc++;
}
int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) {
lua_assert(getOpMode(o) == iABC);
lua_assert(getBMode(o) != OpArgN || b == 0);
lua_assert(getCMode(o) != OpArgN || c == 0);
return luaK_code(fs, CREATE_ABC(o, a, b, c), fs->ls->lastline);
}
int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);
lua_assert(getCMode(o) == OpArgN);
return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline);
}
void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {
int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1;
int b = (tostore == LUA_MULTRET) ? 0 : tostore;
lua_assert(tostore != 0);
if (c <= MAXARG_C)
luaK_codeABC(fs, OP_SETLIST, base, b, c);
else {
luaK_codeABC(fs, OP_SETLIST, base, b, 0);
luaK_code(fs, cast(Instruction, c), fs->ls->lastline);
}
fs->freereg = base + 1; /* free registers with list values */
}
/*
** $Id: ldblib.c,v 1.104.1.3 2008/01/21 13:11:21 roberto Exp $
** Interface from Lua to its debug API
** See Copyright Notice in lua.h
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ldblib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
static int db_getregistry (lua_State *L) {
lua_pushvalue(L, LUA_REGISTRYINDEX);
return 1;
}
static int db_getmetatable (lua_State *L) {
luaL_checkany(L, 1);
if (!lua_getmetatable(L, 1)) {
lua_pushnil(L); /* no metatable */
}
return 1;
}
static int db_setmetatable (lua_State *L) {
int t = lua_type(L, 2);
luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
"nil or table expected");
lua_settop(L, 2);
lua_pushboolean(L, lua_setmetatable(L, 1));
return 1;
}
static int db_getfenv (lua_State *L) {
lua_getfenv(L, 1);
return 1;
}
static int db_setfenv (lua_State *L) {
luaL_checktype(L, 2, LUA_TTABLE);
lua_settop(L, 2);
if (lua_setfenv(L, 1) == 0)
luaL_error(L, LUA_QL("setfenv")
" cannot change environment of given object");
return 1;
}
static void settabss (lua_State *L, const char *i, const char *v) {
lua_pushstring(L, v);
lua_setfield(L, -2, i);
}
static void settabsi (lua_State *L, const char *i, int v) {
lua_pushinteger(L, v);
lua_setfield(L, -2, i);
}
static lua_State *getthread (lua_State *L, int *arg) {
if (lua_isthread(L, 1)) {
*arg = 1;
return lua_tothread(L, 1);
}
else {
*arg = 0;
return L;
}
}
static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
if (L == L1) {
lua_pushvalue(L, -2);
lua_remove(L, -3);
}
else
lua_xmove(L1, L, 1);
lua_setfield(L, -2, fname);
}
static int db_getinfo (lua_State *L) {
lua_Debug ar;
int arg;
lua_State *L1 = getthread(L, &arg);
const char *options = luaL_optstring(L, arg+2, "flnSu");
if (lua_isnumber(L, arg+1)) {
if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) {
lua_pushnil(L); /* level out of range */
return 1;
}
}
else if (lua_isfunction(L, arg+1)) {
lua_pushfstring(L, ">%s", options);
options = lua_tostring(L, -1);
lua_pushvalue(L, arg+1);
lua_xmove(L, L1, 1);
}
else
return luaL_argerror(L, arg+1, "function or level expected");
if (!lua_getinfo(L1, options, &ar))
return luaL_argerror(L, arg+2, "invalid option");
lua_createtable(L, 0, 2);
if (strchr(options, 'S')) {
settabss(L, "source", ar.source);
settabss(L, "short_src", ar.short_src);
settabsi(L, "linedefined", ar.linedefined);
settabsi(L, "lastlinedefined", ar.lastlinedefined);
settabss(L, "what", ar.what);
}
if (strchr(options, 'l'))
settabsi(L, "currentline", ar.currentline);
if (strchr(options, 'u'))
settabsi(L, "nups", ar.nups);
if (strchr(options, 'n')) {
settabss(L, "name", ar.name);
settabss(L, "namewhat", ar.namewhat);
}
if (strchr(options, 'L'))
treatstackoption(L, L1, "activelines");
if (strchr(options, 'f'))
treatstackoption(L, L1, "func");
return 1; /* return table */
}
static int db_getlocal (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
const char *name;
if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
return luaL_argerror(L, arg+1, "level out of range");
name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2));
if (name) {
lua_xmove(L1, L, 1);
lua_pushstring(L, name);
lua_pushvalue(L, -2);
return 2;
}
else {
lua_pushnil(L);
return 1;
}
}
static int db_setlocal (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
return luaL_argerror(L, arg+1, "level out of range");
luaL_checkany(L, arg+3);
lua_settop(L, arg+3);
lua_xmove(L, L1, 1);
lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2)));
return 1;
}
static int auxupvalue (lua_State *L, int get) {
const char *name;
int n = luaL_checkint(L, 2);
luaL_checktype(L, 1, LUA_TFUNCTION);
if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */
name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
if (name == NULL) return 0;
lua_pushstring(L, name);
lua_insert(L, -(get+1));
return get + 1;
}
static int db_getupvalue (lua_State *L) {
return auxupvalue(L, 1);
}
static int db_setupvalue (lua_State *L) {
luaL_checkany(L, 3);
return auxupvalue(L, 0);
}
static const char KEY_HOOK = 'h';
static void hookf (lua_State *L, lua_Debug *ar) {
static const char *const hooknames[] =
{"call", "return", "line", "count", "tail return"};
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushlightuserdata(L, L);
lua_rawget(L, -2);
if (lua_isfunction(L, -1)) {
lua_pushstring(L, hooknames[(int)ar->event]);
if (ar->currentline >= 0)
lua_pushinteger(L, ar->currentline);
else lua_pushnil(L);
lua_assert(lua_getinfo(L, "lS", ar));
lua_call(L, 2, 0);
}
}
static int makemask (const char *smask, int count) {
int mask = 0;
if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
if (strchr(smask, 'r')) mask |= LUA_MASKRET;
if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
if (count > 0) mask |= LUA_MASKCOUNT;
return mask;
}
static char *unmakemask (int mask, char *smask) {
int i = 0;
if (mask & LUA_MASKCALL) smask[i++] = 'c';
if (mask & LUA_MASKRET) smask[i++] = 'r';
if (mask & LUA_MASKLINE) smask[i++] = 'l';
smask[i] = '\0';
return smask;
}
static void gethooktable (lua_State *L) {
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_rawget(L, LUA_REGISTRYINDEX);
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
lua_createtable(L, 0, 1);
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_pushvalue(L, -2);
lua_rawset(L, LUA_REGISTRYINDEX);
}
}
static int db_sethook (lua_State *L) {
int arg, mask, count;
lua_Hook func;
lua_State *L1 = getthread(L, &arg);
if (lua_isnoneornil(L, arg+1)) {
lua_settop(L, arg+1);
func = NULL; mask = 0; count = 0; /* turn off hooks */
}
else {
const char *smask = luaL_checkstring(L, arg+2);
luaL_checktype(L, arg+1, LUA_TFUNCTION);
count = luaL_optint(L, arg+3, 0);
func = hookf; mask = makemask(smask, count);
}
gethooktable(L);
lua_pushlightuserdata(L, L1);
lua_pushvalue(L, arg+1);
lua_rawset(L, -3); /* set new hook */
lua_pop(L, 1); /* remove hook table */
lua_sethook(L1, func, mask, count); /* set hooks */
return 0;
}
static int db_gethook (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
char buff[5];
int mask = lua_gethookmask(L1);
lua_Hook hook = lua_gethook(L1);
if (hook != NULL && hook != hookf) /* external hook? */
lua_pushliteral(L, "external hook");
else {
gethooktable(L);
lua_pushlightuserdata(L, L1);
lua_rawget(L, -2); /* get hook */
lua_remove(L, -2); /* remove hook table */
}
lua_pushstring(L, unmakemask(mask, buff));
lua_pushinteger(L, lua_gethookcount(L1));
return 3;
}
static int db_debug (lua_State *L) {
for (;;) {
char buffer[250];
fputs("lua_debug> ", stderr);
if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
strcmp(buffer, "cont\n") == 0)
return 0;
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
lua_pcall(L, 0, 0, 0)) {
fputs(lua_tostring(L, -1), stderr);
fputs("\n", stderr);
}
lua_settop(L, 0); /* remove eventual returns */
}
}
#define LEVELS1 12 /* size of the first part of the stack */
#define LEVELS2 10 /* size of the second part of the stack */
static int db_errorfb (lua_State *L) {
int level;
int firstpart = 1; /* still before eventual `...' */
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
if (lua_isnumber(L, arg+2)) {
level = (int)lua_tointeger(L, arg+2);
lua_pop(L, 1);
}
else
level = (L == L1) ? 1 : 0; /* level 0 may be this own function */
if (lua_gettop(L) == arg)
lua_pushliteral(L, "");
else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */
else lua_pushliteral(L, "\n");
lua_pushliteral(L, "stack traceback:");
while (lua_getstack(L1, level++, &ar)) {
if (level > LEVELS1 && firstpart) {
/* no more than `LEVELS2' more levels? */
if (!lua_getstack(L1, level+LEVELS2, &ar))
level--; /* keep going */
else {
lua_pushliteral(L, "\n\t..."); /* too many levels */
while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */
level++;
}
firstpart = 0;
continue;
}
lua_pushliteral(L, "\n\t");
lua_getinfo(L1, "Snl", &ar);
lua_pushfstring(L, "%s:", ar.short_src);
if (ar.currentline > 0)
lua_pushfstring(L, "%d:", ar.currentline);
if (*ar.namewhat != '\0') /* is there a name? */
lua_pushfstring(L, " in function " LUA_QS, ar.name);
else {
if (*ar.what == 'm') /* main? */
lua_pushfstring(L, " in main chunk");
else if (*ar.what == 'C' || *ar.what == 't')
lua_pushliteral(L, " ?"); /* C function or tail call */
else
lua_pushfstring(L, " in function <%s:%d>",
ar.short_src, ar.linedefined);
}
lua_concat(L, lua_gettop(L) - arg);
}
lua_concat(L, lua_gettop(L) - arg);
return 1;
}
static const luaL_Reg dblib[] = {
{"debug", db_debug},
{"getfenv", db_getfenv},
{"gethook", db_gethook},
{"getinfo", db_getinfo},
{"getlocal", db_getlocal},
{"getregistry", db_getregistry},
{"getmetatable", db_getmetatable},
{"getupvalue", db_getupvalue},
{"setfenv", db_setfenv},
{"sethook", db_sethook},
{"setlocal", db_setlocal},
{"setmetatable", db_setmetatable},
{"setupvalue", db_setupvalue},
{"traceback", db_errorfb},
{NULL, NULL}
};
LUALIB_API int luaopen_debug (lua_State *L) {
luaL_register(L, LUA_DBLIBNAME, dblib);
return 1;
}
/*
** $Id: ldblib.c,v 1.104.1.4 2009/08/04 18:50:18 roberto Exp $
** Interface from Lua to its debug API
** See Copyright Notice in lua.h
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ldblib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
static int db_getregistry (lua_State *L) {
lua_pushvalue(L, LUA_REGISTRYINDEX);
return 1;
}
static int db_getmetatable (lua_State *L) {
luaL_checkany(L, 1);
if (!lua_getmetatable(L, 1)) {
lua_pushnil(L); /* no metatable */
}
return 1;
}
static int db_setmetatable (lua_State *L) {
int t = lua_type(L, 2);
luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
"nil or table expected");
lua_settop(L, 2);
lua_pushboolean(L, lua_setmetatable(L, 1));
return 1;
}
static int db_getfenv (lua_State *L) {
luaL_checkany(L, 1);
lua_getfenv(L, 1);
return 1;
}
static int db_setfenv (lua_State *L) {
luaL_checktype(L, 2, LUA_TTABLE);
lua_settop(L, 2);
if (lua_setfenv(L, 1) == 0)
luaL_error(L, LUA_QL("setfenv")
" cannot change environment of given object");
return 1;
}
static void settabss (lua_State *L, const char *i, const char *v) {
lua_pushstring(L, v);
lua_setfield(L, -2, i);
}
static void settabsi (lua_State *L, const char *i, int v) {
lua_pushinteger(L, v);
lua_setfield(L, -2, i);
}
static lua_State *getthread (lua_State *L, int *arg) {
if (lua_isthread(L, 1)) {
*arg = 1;
return lua_tothread(L, 1);
}
else {
*arg = 0;
return L;
}
}
static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
if (L == L1) {
lua_pushvalue(L, -2);
lua_remove(L, -3);
}
else
lua_xmove(L1, L, 1);
lua_setfield(L, -2, fname);
}
static int db_getinfo (lua_State *L) {
lua_Debug ar;
int arg;
lua_State *L1 = getthread(L, &arg);
const char *options = luaL_optstring(L, arg+2, "flnSu");
if (lua_isnumber(L, arg+1)) {
if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) {
lua_pushnil(L); /* level out of range */
return 1;
}
}
else if (lua_isfunction(L, arg+1)) {
lua_pushfstring(L, ">%s", options);
options = lua_tostring(L, -1);
lua_pushvalue(L, arg+1);
lua_xmove(L, L1, 1);
}
else
return luaL_argerror(L, arg+1, "function or level expected");
if (!lua_getinfo(L1, options, &ar))
return luaL_argerror(L, arg+2, "invalid option");
lua_createtable(L, 0, 2);
if (strchr(options, 'S')) {
settabss(L, "source", ar.source);
settabss(L, "short_src", ar.short_src);
settabsi(L, "linedefined", ar.linedefined);
settabsi(L, "lastlinedefined", ar.lastlinedefined);
settabss(L, "what", ar.what);
}
if (strchr(options, 'l'))
settabsi(L, "currentline", ar.currentline);
if (strchr(options, 'u'))
settabsi(L, "nups", ar.nups);
if (strchr(options, 'n')) {
settabss(L, "name", ar.name);
settabss(L, "namewhat", ar.namewhat);
}
if (strchr(options, 'L'))
treatstackoption(L, L1, "activelines");
if (strchr(options, 'f'))
treatstackoption(L, L1, "func");
return 1; /* return table */
}
static int db_getlocal (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
const char *name;
if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
return luaL_argerror(L, arg+1, "level out of range");
name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2));
if (name) {
lua_xmove(L1, L, 1);
lua_pushstring(L, name);
lua_pushvalue(L, -2);
return 2;
}
else {
lua_pushnil(L);
return 1;
}
}
static int db_setlocal (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
return luaL_argerror(L, arg+1, "level out of range");
luaL_checkany(L, arg+3);
lua_settop(L, arg+3);
lua_xmove(L, L1, 1);
lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2)));
return 1;
}
static int auxupvalue (lua_State *L, int get) {
const char *name;
int n = luaL_checkint(L, 2);
luaL_checktype(L, 1, LUA_TFUNCTION);
if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */
name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
if (name == NULL) return 0;
lua_pushstring(L, name);
lua_insert(L, -(get+1));
return get + 1;
}
static int db_getupvalue (lua_State *L) {
return auxupvalue(L, 1);
}
static int db_setupvalue (lua_State *L) {
luaL_checkany(L, 3);
return auxupvalue(L, 0);
}
static const char KEY_HOOK = 'h';
static void hookf (lua_State *L, lua_Debug *ar) {
static const char *const hooknames[] =
{"call", "return", "line", "count", "tail return"};
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushlightuserdata(L, L);
lua_rawget(L, -2);
if (lua_isfunction(L, -1)) {
lua_pushstring(L, hooknames[(int)ar->event]);
if (ar->currentline >= 0)
lua_pushinteger(L, ar->currentline);
else lua_pushnil(L);
lua_assert(lua_getinfo(L, "lS", ar));
lua_call(L, 2, 0);
}
}
static int makemask (const char *smask, int count) {
int mask = 0;
if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
if (strchr(smask, 'r')) mask |= LUA_MASKRET;
if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
if (count > 0) mask |= LUA_MASKCOUNT;
return mask;
}
static char *unmakemask (int mask, char *smask) {
int i = 0;
if (mask & LUA_MASKCALL) smask[i++] = 'c';
if (mask & LUA_MASKRET) smask[i++] = 'r';
if (mask & LUA_MASKLINE) smask[i++] = 'l';
smask[i] = '\0';
return smask;
}
static void gethooktable (lua_State *L) {
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_rawget(L, LUA_REGISTRYINDEX);
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
lua_createtable(L, 0, 1);
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_pushvalue(L, -2);
lua_rawset(L, LUA_REGISTRYINDEX);
}
}
static int db_sethook (lua_State *L) {
int arg, mask, count;
lua_Hook func;
lua_State *L1 = getthread(L, &arg);
if (lua_isnoneornil(L, arg+1)) {
lua_settop(L, arg+1);
func = NULL; mask = 0; count = 0; /* turn off hooks */
}
else {
const char *smask = luaL_checkstring(L, arg+2);
luaL_checktype(L, arg+1, LUA_TFUNCTION);
count = luaL_optint(L, arg+3, 0);
func = hookf; mask = makemask(smask, count);
}
gethooktable(L);
lua_pushlightuserdata(L, L1);
lua_pushvalue(L, arg+1);
lua_rawset(L, -3); /* set new hook */
lua_pop(L, 1); /* remove hook table */
lua_sethook(L1, func, mask, count); /* set hooks */
return 0;
}
static int db_gethook (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
char buff[5];
int mask = lua_gethookmask(L1);
lua_Hook hook = lua_gethook(L1);
if (hook != NULL && hook != hookf) /* external hook? */
lua_pushliteral(L, "external hook");
else {
gethooktable(L);
lua_pushlightuserdata(L, L1);
lua_rawget(L, -2); /* get hook */
lua_remove(L, -2); /* remove hook table */
}
lua_pushstring(L, unmakemask(mask, buff));
lua_pushinteger(L, lua_gethookcount(L1));
return 3;
}
static int db_debug (lua_State *L) {
for (;;) {
char buffer[250];
fputs("lua_debug> ", stderr);
if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
strcmp(buffer, "cont\n") == 0)
return 0;
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
lua_pcall(L, 0, 0, 0)) {
fputs(lua_tostring(L, -1), stderr);
fputs("\n", stderr);
}
lua_settop(L, 0); /* remove eventual returns */
}
}
#define LEVELS1 12 /* size of the first part of the stack */
#define LEVELS2 10 /* size of the second part of the stack */
static int db_errorfb (lua_State *L) {
int level;
int firstpart = 1; /* still before eventual `...' */
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
if (lua_isnumber(L, arg+2)) {
level = (int)lua_tointeger(L, arg+2);
lua_pop(L, 1);
}
else
level = (L == L1) ? 1 : 0; /* level 0 may be this own function */
if (lua_gettop(L) == arg)
lua_pushliteral(L, "");
else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */
else lua_pushliteral(L, "\n");
lua_pushliteral(L, "stack traceback:");
while (lua_getstack(L1, level++, &ar)) {
if (level > LEVELS1 && firstpart) {
/* no more than `LEVELS2' more levels? */
if (!lua_getstack(L1, level+LEVELS2, &ar))
level--; /* keep going */
else {
lua_pushliteral(L, "\n\t..."); /* too many levels */
while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */
level++;
}
firstpart = 0;
continue;
}
lua_pushliteral(L, "\n\t");
lua_getinfo(L1, "Snl", &ar);
lua_pushfstring(L, "%s:", ar.short_src);
if (ar.currentline > 0)
lua_pushfstring(L, "%d:", ar.currentline);
if (*ar.namewhat != '\0') /* is there a name? */
lua_pushfstring(L, " in function " LUA_QS, ar.name);
else {
if (*ar.what == 'm') /* main? */
lua_pushfstring(L, " in main chunk");
else if (*ar.what == 'C' || *ar.what == 't')
lua_pushliteral(L, " ?"); /* C function or tail call */
else
lua_pushfstring(L, " in function <%s:%d>",
ar.short_src, ar.linedefined);
}
lua_concat(L, lua_gettop(L) - arg);
}
lua_concat(L, lua_gettop(L) - arg);
return 1;
}
static const luaL_Reg dblib[] = {
{"debug", db_debug},
{"getfenv", db_getfenv},
{"gethook", db_gethook},
{"getinfo", db_getinfo},
{"getlocal", db_getlocal},
{"getregistry", db_getregistry},
{"getmetatable", db_getmetatable},
{"getupvalue", db_getupvalue},
{"setfenv", db_setfenv},
{"sethook", db_sethook},
{"setlocal", db_setlocal},
{"setmetatable", db_setmetatable},
{"setupvalue", db_setupvalue},
{"traceback", db_errorfb},
{NULL, NULL}
};
LUALIB_API int luaopen_debug (lua_State *L) {
luaL_register(L, LUA_DBLIBNAME, dblib);
return 1;
}
/*
** $Id: liolib.c,v 2.73.1.3 2008/01/18 17:47:43 roberto Exp $
** Standard I/O (and system) library
** See Copyright Notice in lua.h
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define liolib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#define IO_INPUT 1
#define IO_OUTPUT 2
static const char *const fnames[] = {"input", "output"};
static int pushresult (lua_State *L, int i, const char *filename) {
int en = errno; /* calls to Lua API may change this value */
if (i) {
lua_pushboolean(L, 1);
return 1;
}
else {
lua_pushnil(L);
if (filename)
lua_pushfstring(L, "%s: %s", filename, strerror(en));
else
lua_pushfstring(L, "%s", strerror(en));
lua_pushinteger(L, en);
return 3;
}
}
static void fileerror (lua_State *L, int arg, const char *filename) {
lua_pushfstring(L, "%s: %s", filename, strerror(errno));
luaL_argerror(L, arg, lua_tostring(L, -1));
}
#define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
static int io_type (lua_State *L) {
void *ud;
luaL_checkany(L, 1);
ud = lua_touserdata(L, 1);
lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1))
lua_pushnil(L); /* not a file */
else if (*((FILE **)ud) == NULL)
lua_pushliteral(L, "closed file");
else
lua_pushliteral(L, "file");
return 1;
}
static FILE *tofile (lua_State *L) {
FILE **f = tofilep(L);
if (*f == NULL)
luaL_error(L, "attempt to use a closed file");
return *f;
}
/*
** When creating file handles, always creates a `closed' file handle
** before opening the actual file; so, if there is a memory error, the
** file is not left opened.
*/
static FILE **newfile (lua_State *L) {
FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *));
*pf = NULL; /* file handle is currently `closed' */
luaL_getmetatable(L, LUA_FILEHANDLE);
lua_setmetatable(L, -2);
return pf;
}
/*
** function to (not) close the standard files stdin, stdout, and stderr
*/
static int io_noclose (lua_State *L) {
lua_pushnil(L);
lua_pushliteral(L, "cannot close standard file");
return 2;
}
/*
** function to close 'popen' files
*/
static int io_pclose (lua_State *L) {
FILE **p = tofilep(L);
int ok = lua_pclose(L, *p);
*p = NULL;
return pushresult(L, ok, NULL);
}
/*
** function to close regular files
*/
static int io_fclose (lua_State *L) {
FILE **p = tofilep(L);
int ok = (fclose(*p) == 0);
*p = NULL;
return pushresult(L, ok, NULL);
}
static int aux_close (lua_State *L) {
lua_getfenv(L, 1);
lua_getfield(L, -1, "__close");
return (lua_tocfunction(L, -1))(L);
}
static int io_close (lua_State *L) {
if (lua_isnone(L, 1))
lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT);
tofile(L); /* make sure argument is a file */
return aux_close(L);
}
static int io_gc (lua_State *L) {
FILE *f = *tofilep(L);
/* ignore closed files */
if (f != NULL)
aux_close(L);
return 0;
}
static int io_tostring (lua_State *L) {
FILE *f = *tofilep(L);
if (f == NULL)
lua_pushliteral(L, "file (closed)");
else
lua_pushfstring(L, "file (%p)", f);
return 1;
}
static int io_open (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
FILE **pf = newfile(L);
*pf = fopen(filename, mode);
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
}
/*
** this function has a separated environment, which defines the
** correct __close for 'popen' files
*/
static int io_popen (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
FILE **pf = newfile(L);
*pf = lua_popen(L, filename, mode);
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
}
static int io_tmpfile (lua_State *L) {
FILE **pf = newfile(L);
*pf = tmpfile();
return (*pf == NULL) ? pushresult(L, 0, NULL) : 1;
}
static FILE *getiofile (lua_State *L, int findex) {
FILE *f;
lua_rawgeti(L, LUA_ENVIRONINDEX, findex);
f = *(FILE **)lua_touserdata(L, -1);
if (f == NULL)
luaL_error(L, "standard %s file is closed", fnames[findex - 1]);
return f;
}
static int g_iofile (lua_State *L, int f, const char *mode) {
if (!lua_isnoneornil(L, 1)) {
const char *filename = lua_tostring(L, 1);
if (filename) {
FILE **pf = newfile(L);
*pf = fopen(filename, mode);
if (*pf == NULL)
fileerror(L, 1, filename);
}
else {
tofile(L); /* check that it's a valid file handle */
lua_pushvalue(L, 1);
}
lua_rawseti(L, LUA_ENVIRONINDEX, f);
}
/* return current value */
lua_rawgeti(L, LUA_ENVIRONINDEX, f);
return 1;
}
static int io_input (lua_State *L) {
return g_iofile(L, IO_INPUT, "r");
}
static int io_output (lua_State *L) {
return g_iofile(L, IO_OUTPUT, "w");
}
static int io_readline (lua_State *L);
static void aux_lines (lua_State *L, int idx, int toclose) {
lua_pushvalue(L, idx);
lua_pushboolean(L, toclose); /* close/not close file when finished */
lua_pushcclosure(L, io_readline, 2);
}
static int f_lines (lua_State *L) {
tofile(L); /* check that it's a valid file handle */
aux_lines(L, 1, 0);
return 1;
}
static int io_lines (lua_State *L) {
if (lua_isnoneornil(L, 1)) { /* no arguments? */
/* will iterate over default input */
lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT);
return f_lines(L);
}
else {
const char *filename = luaL_checkstring(L, 1);
FILE **pf = newfile(L);
*pf = fopen(filename, "r");
if (*pf == NULL)
fileerror(L, 1, filename);
aux_lines(L, lua_gettop(L), 1);
return 1;
}
}
/*
** {======================================================
** READ
** =======================================================
*/
static int read_number (lua_State *L, FILE *f) {
lua_Number d;
if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
lua_pushnumber(L, d);
return 1;
}
else return 0; /* read fails */
}
static int test_eof (lua_State *L, FILE *f) {
int c = getc(f);
ungetc(c, f);
lua_pushlstring(L, NULL, 0);
return (c != EOF);
}
static int read_line (lua_State *L, FILE *f) {
luaL_Buffer b;
luaL_buffinit(L, &b);
for (;;) {
size_t l;
char *p = luaL_prepbuffer(&b);
if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */
luaL_pushresult(&b); /* close buffer */
return (lua_objlen(L, -1) > 0); /* check whether read something */
}
l = strlen(p);
if (l == 0 || p[l-1] != '\n')
luaL_addsize(&b, l);
else {
luaL_addsize(&b, l - 1); /* do not include `eol' */
luaL_pushresult(&b); /* close buffer */
return 1; /* read at least an `eol' */
}
}
}
static int read_chars (lua_State *L, FILE *f, size_t n) {
size_t rlen; /* how much to read */
size_t nr; /* number of chars actually read */
luaL_Buffer b;
luaL_buffinit(L, &b);
rlen = LUAL_BUFFERSIZE; /* try to read that much each time */
do {
char *p = luaL_prepbuffer(&b);
if (rlen > n) rlen = n; /* cannot read more than asked */
nr = fread(p, sizeof(char), rlen, f);
luaL_addsize(&b, nr);
n -= nr; /* still have to read `n' chars */
} while (n > 0 && nr == rlen); /* until end of count or eof */
luaL_pushresult(&b); /* close buffer */
return (n == 0 || lua_objlen(L, -1) > 0);
}
static int g_read (lua_State *L, FILE *f, int first) {
int nargs = lua_gettop(L) - 1;
int success;
int n;
clearerr(f);
if (nargs == 0) { /* no arguments? */
success = read_line(L, f);
n = first+1; /* to return 1 result */
}
else { /* ensure stack space for all results and for auxlib's buffer */
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
success = 1;
for (n = first; nargs-- && success; n++) {
if (lua_type(L, n) == LUA_TNUMBER) {
size_t l = (size_t)lua_tointeger(L, n);
success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
}
else {
const char *p = lua_tostring(L, n);
luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
switch (p[1]) {
case 'n': /* number */
success = read_number(L, f);
break;
case 'l': /* line */
success = read_line(L, f);
break;
case 'a': /* file */
read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */
success = 1; /* always success */
break;
default:
return luaL_argerror(L, n, "invalid format");
}
}
}
}
if (ferror(f))
return pushresult(L, 0, NULL);
if (!success) {
lua_pop(L, 1); /* remove last result */
lua_pushnil(L); /* push nil instead */
}
return n - first;
}
static int io_read (lua_State *L) {
return g_read(L, getiofile(L, IO_INPUT), 1);
}
static int f_read (lua_State *L) {
return g_read(L, tofile(L), 2);
}
static int io_readline (lua_State *L) {
FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1));
int sucess;
if (f == NULL) /* file is already closed? */
luaL_error(L, "file is already closed");
sucess = read_line(L, f);
if (ferror(f))
return luaL_error(L, "%s", strerror(errno));
if (sucess) return 1;
else { /* EOF */
if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */
lua_settop(L, 0);
lua_pushvalue(L, lua_upvalueindex(1));
aux_close(L); /* close it */
}
return 0;
}
}
/* }====================================================== */
static int g_write (lua_State *L, FILE *f, int arg) {
int nargs = lua_gettop(L) - 1;
int status = 1;
for (; nargs--; arg++) {
if (lua_type(L, arg) == LUA_TNUMBER) {
/* optimization: could be done exactly as for strings */
status = status &&
fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0;
}
else {
size_t l;
const char *s = luaL_checklstring(L, arg, &l);
status = status && (fwrite(s, sizeof(char), l, f) == l);
}
}
return pushresult(L, status, NULL);
}
static int io_write (lua_State *L) {
return g_write(L, getiofile(L, IO_OUTPUT), 1);
}
static int f_write (lua_State *L) {
return g_write(L, tofile(L), 2);
}
static int f_seek (lua_State *L) {
static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
static const char *const modenames[] = {"set", "cur", "end", NULL};
FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, "cur", modenames);
long offset = luaL_optlong(L, 3, 0);
op = fseek(f, offset, mode[op]);
if (op)
return pushresult(L, 0, NULL); /* error */
else {
lua_pushinteger(L, ftell(f));
return 1;
}
}
static int f_setvbuf (lua_State *L) {
static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
static const char *const modenames[] = {"no", "full", "line", NULL};
FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, NULL, modenames);
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
int res = setvbuf(f, NULL, mode[op], sz);
return pushresult(L, res == 0, NULL);
}
static int io_flush (lua_State *L) {
return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
}
static int f_flush (lua_State *L) {
return pushresult(L, fflush(tofile(L)) == 0, NULL);
}
static const luaL_Reg iolib[] = {
{"close", io_close},
{"flush", io_flush},
{"input", io_input},
{"lines", io_lines},
{"open", io_open},
{"output", io_output},
{"popen", io_popen},
{"read", io_read},
{"tmpfile", io_tmpfile},
{"type", io_type},
{"write", io_write},
{NULL, NULL}
};
static const luaL_Reg flib[] = {
{"close", io_close},
{"flush", f_flush},
{"lines", f_lines},
{"read", f_read},
{"seek", f_seek},
{"setvbuf", f_setvbuf},
{"write", f_write},
{"__gc", io_gc},
{"__tostring", io_tostring},
{NULL, NULL}
};
static void createmeta (lua_State *L) {
luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
lua_pushvalue(L, -1); /* push metatable */
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
luaL_register(L, NULL, flib); /* file methods */
}
static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) {
*newfile(L) = f;
if (k > 0) {
lua_pushvalue(L, -1);
lua_rawseti(L, LUA_ENVIRONINDEX, k);
}
lua_pushvalue(L, -2); /* copy environment */
lua_setfenv(L, -2); /* set it */
lua_setfield(L, -3, fname);
}
static void newfenv (lua_State *L, lua_CFunction cls) {
lua_createtable(L, 0, 1);
lua_pushcfunction(L, cls);
lua_setfield(L, -2, "__close");
}
LUALIB_API int luaopen_io (lua_State *L) {
createmeta(L);
/* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */
newfenv(L, io_fclose);
lua_replace(L, LUA_ENVIRONINDEX);
/* open library */
luaL_register(L, LUA_IOLIBNAME, iolib);
/* create (and set) default files */
newfenv(L, io_noclose); /* close function for default files */
createstdfile(L, stdin, IO_INPUT, "stdin");
createstdfile(L, stdout, IO_OUTPUT, "stdout");
createstdfile(L, stderr, 0, "stderr");
lua_pop(L, 1); /* pop environment for default files */
lua_getfield(L, -1, "popen");
newfenv(L, io_pclose); /* create environment for 'popen' */
lua_setfenv(L, -2); /* set fenv for 'popen' */
lua_pop(L, 1); /* pop 'popen' */
return 1;
}
/*
** $Id: liolib.c,v 2.73.1.4 2010/05/14 15:33:51 roberto Exp $
** Standard I/O (and system) library
** See Copyright Notice in lua.h
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define liolib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#define IO_INPUT 1
#define IO_OUTPUT 2
static const char *const fnames[] = {"input", "output"};
static int pushresult (lua_State *L, int i, const char *filename) {
int en = errno; /* calls to Lua API may change this value */
if (i) {
lua_pushboolean(L, 1);
return 1;
}
else {
lua_pushnil(L);
if (filename)
lua_pushfstring(L, "%s: %s", filename, strerror(en));
else
lua_pushfstring(L, "%s", strerror(en));
lua_pushinteger(L, en);
return 3;
}
}
static void fileerror (lua_State *L, int arg, const char *filename) {
lua_pushfstring(L, "%s: %s", filename, strerror(errno));
luaL_argerror(L, arg, lua_tostring(L, -1));
}
#define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
static int io_type (lua_State *L) {
void *ud;
luaL_checkany(L, 1);
ud = lua_touserdata(L, 1);
lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1))
lua_pushnil(L); /* not a file */
else if (*((FILE **)ud) == NULL)
lua_pushliteral(L, "closed file");
else
lua_pushliteral(L, "file");
return 1;
}
static FILE *tofile (lua_State *L) {
FILE **f = tofilep(L);
if (*f == NULL)
luaL_error(L, "attempt to use a closed file");
return *f;
}
/*
** When creating file handles, always creates a `closed' file handle
** before opening the actual file; so, if there is a memory error, the
** file is not left opened.
*/
static FILE **newfile (lua_State *L) {
FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *));
*pf = NULL; /* file handle is currently `closed' */
luaL_getmetatable(L, LUA_FILEHANDLE);
lua_setmetatable(L, -2);
return pf;
}
/*
** function to (not) close the standard files stdin, stdout, and stderr
*/
static int io_noclose (lua_State *L) {
lua_pushnil(L);
lua_pushliteral(L, "cannot close standard file");
return 2;
}
/*
** function to close 'popen' files
*/
static int io_pclose (lua_State *L) {
FILE **p = tofilep(L);
int ok = lua_pclose(L, *p);
*p = NULL;
return pushresult(L, ok, NULL);
}
/*
** function to close regular files
*/
static int io_fclose (lua_State *L) {
FILE **p = tofilep(L);
int ok = (fclose(*p) == 0);
*p = NULL;
return pushresult(L, ok, NULL);
}
static int aux_close (lua_State *L) {
lua_getfenv(L, 1);
lua_getfield(L, -1, "__close");
return (lua_tocfunction(L, -1))(L);
}
static int io_close (lua_State *L) {
if (lua_isnone(L, 1))
lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT);
tofile(L); /* make sure argument is a file */
return aux_close(L);
}
static int io_gc (lua_State *L) {
FILE *f = *tofilep(L);
/* ignore closed files */
if (f != NULL)
aux_close(L);
return 0;
}
static int io_tostring (lua_State *L) {
FILE *f = *tofilep(L);
if (f == NULL)
lua_pushliteral(L, "file (closed)");
else
lua_pushfstring(L, "file (%p)", f);
return 1;
}
static int io_open (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
FILE **pf = newfile(L);
*pf = fopen(filename, mode);
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
}
/*
** this function has a separated environment, which defines the
** correct __close for 'popen' files
*/
static int io_popen (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
FILE **pf = newfile(L);
*pf = lua_popen(L, filename, mode);
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
}
static int io_tmpfile (lua_State *L) {
FILE **pf = newfile(L);
*pf = tmpfile();
return (*pf == NULL) ? pushresult(L, 0, NULL) : 1;
}
static FILE *getiofile (lua_State *L, int findex) {
FILE *f;
lua_rawgeti(L, LUA_ENVIRONINDEX, findex);
f = *(FILE **)lua_touserdata(L, -1);
if (f == NULL)
luaL_error(L, "standard %s file is closed", fnames[findex - 1]);
return f;
}
static int g_iofile (lua_State *L, int f, const char *mode) {
if (!lua_isnoneornil(L, 1)) {
const char *filename = lua_tostring(L, 1);
if (filename) {
FILE **pf = newfile(L);
*pf = fopen(filename, mode);
if (*pf == NULL)
fileerror(L, 1, filename);
}
else {
tofile(L); /* check that it's a valid file handle */
lua_pushvalue(L, 1);
}
lua_rawseti(L, LUA_ENVIRONINDEX, f);
}
/* return current value */
lua_rawgeti(L, LUA_ENVIRONINDEX, f);
return 1;
}
static int io_input (lua_State *L) {
return g_iofile(L, IO_INPUT, "r");
}
static int io_output (lua_State *L) {
return g_iofile(L, IO_OUTPUT, "w");
}
static int io_readline (lua_State *L);
static void aux_lines (lua_State *L, int idx, int toclose) {
lua_pushvalue(L, idx);
lua_pushboolean(L, toclose); /* close/not close file when finished */
lua_pushcclosure(L, io_readline, 2);
}
static int f_lines (lua_State *L) {
tofile(L); /* check that it's a valid file handle */
aux_lines(L, 1, 0);
return 1;
}
static int io_lines (lua_State *L) {
if (lua_isnoneornil(L, 1)) { /* no arguments? */
/* will iterate over default input */
lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT);
return f_lines(L);
}
else {
const char *filename = luaL_checkstring(L, 1);
FILE **pf = newfile(L);
*pf = fopen(filename, "r");
if (*pf == NULL)
fileerror(L, 1, filename);
aux_lines(L, lua_gettop(L), 1);
return 1;
}
}
/*
** {======================================================
** READ
** =======================================================
*/
static int read_number (lua_State *L, FILE *f) {
lua_Number d;
if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
lua_pushnumber(L, d);
return 1;
}
else {
lua_pushnil(L); /* "result" to be removed */
return 0; /* read fails */
}
}
static int test_eof (lua_State *L, FILE *f) {
int c = getc(f);
ungetc(c, f);
lua_pushlstring(L, NULL, 0);
return (c != EOF);
}
static int read_line (lua_State *L, FILE *f) {
luaL_Buffer b;
luaL_buffinit(L, &b);
for (;;) {
size_t l;
char *p = luaL_prepbuffer(&b);
if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */
luaL_pushresult(&b); /* close buffer */
return (lua_objlen(L, -1) > 0); /* check whether read something */
}
l = strlen(p);
if (l == 0 || p[l-1] != '\n')
luaL_addsize(&b, l);
else {
luaL_addsize(&b, l - 1); /* do not include `eol' */
luaL_pushresult(&b); /* close buffer */
return 1; /* read at least an `eol' */
}
}
}
static int read_chars (lua_State *L, FILE *f, size_t n) {
size_t rlen; /* how much to read */
size_t nr; /* number of chars actually read */
luaL_Buffer b;
luaL_buffinit(L, &b);
rlen = LUAL_BUFFERSIZE; /* try to read that much each time */
do {
char *p = luaL_prepbuffer(&b);
if (rlen > n) rlen = n; /* cannot read more than asked */
nr = fread(p, sizeof(char), rlen, f);
luaL_addsize(&b, nr);
n -= nr; /* still have to read `n' chars */
} while (n > 0 && nr == rlen); /* until end of count or eof */
luaL_pushresult(&b); /* close buffer */
return (n == 0 || lua_objlen(L, -1) > 0);
}
static int g_read (lua_State *L, FILE *f, int first) {
int nargs = lua_gettop(L) - 1;
int success;
int n;
clearerr(f);
if (nargs == 0) { /* no arguments? */
success = read_line(L, f);
n = first+1; /* to return 1 result */
}
else { /* ensure stack space for all results and for auxlib's buffer */
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
success = 1;
for (n = first; nargs-- && success; n++) {
if (lua_type(L, n) == LUA_TNUMBER) {
size_t l = (size_t)lua_tointeger(L, n);
success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
}
else {
const char *p = lua_tostring(L, n);
luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
switch (p[1]) {
case 'n': /* number */
success = read_number(L, f);
break;
case 'l': /* line */
success = read_line(L, f);
break;
case 'a': /* file */
read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */
success = 1; /* always success */
break;
default:
return luaL_argerror(L, n, "invalid format");
}
}
}
}
if (ferror(f))
return pushresult(L, 0, NULL);
if (!success) {
lua_pop(L, 1); /* remove last result */
lua_pushnil(L); /* push nil instead */
}
return n - first;
}
static int io_read (lua_State *L) {
return g_read(L, getiofile(L, IO_INPUT), 1);
}
static int f_read (lua_State *L) {
return g_read(L, tofile(L), 2);
}
static int io_readline (lua_State *L) {
FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1));
int sucess;
if (f == NULL) /* file is already closed? */
luaL_error(L, "file is already closed");
sucess = read_line(L, f);
if (ferror(f))
return luaL_error(L, "%s", strerror(errno));
if (sucess) return 1;
else { /* EOF */
if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */
lua_settop(L, 0);
lua_pushvalue(L, lua_upvalueindex(1));
aux_close(L); /* close it */
}
return 0;
}
}
/* }====================================================== */
static int g_write (lua_State *L, FILE *f, int arg) {
int nargs = lua_gettop(L) - 1;
int status = 1;
for (; nargs--; arg++) {
if (lua_type(L, arg) == LUA_TNUMBER) {
/* optimization: could be done exactly as for strings */
status = status &&
fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0;
}
else {
size_t l;
const char *s = luaL_checklstring(L, arg, &l);
status = status && (fwrite(s, sizeof(char), l, f) == l);
}
}
return pushresult(L, status, NULL);
}
static int io_write (lua_State *L) {
return g_write(L, getiofile(L, IO_OUTPUT), 1);
}
static int f_write (lua_State *L) {
return g_write(L, tofile(L), 2);
}
static int f_seek (lua_State *L) {
static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
static const char *const modenames[] = {"set", "cur", "end", NULL};
FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, "cur", modenames);
long offset = luaL_optlong(L, 3, 0);
op = fseek(f, offset, mode[op]);
if (op)
return pushresult(L, 0, NULL); /* error */
else {
lua_pushinteger(L, ftell(f));
return 1;
}
}
static int f_setvbuf (lua_State *L) {
static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
static const char *const modenames[] = {"no", "full", "line", NULL};
FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, NULL, modenames);
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
int res = setvbuf(f, NULL, mode[op], sz);
return pushresult(L, res == 0, NULL);
}
static int io_flush (lua_State *L) {
return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
}
static int f_flush (lua_State *L) {
return pushresult(L, fflush(tofile(L)) == 0, NULL);
}
static const luaL_Reg iolib[] = {
{"close", io_close},
{"flush", io_flush},
{"input", io_input},
{"lines", io_lines},
{"open", io_open},
{"output", io_output},
{"popen", io_popen},
{"read", io_read},
{"tmpfile", io_tmpfile},
{"type", io_type},
{"write", io_write},
{NULL, NULL}
};
static const luaL_Reg flib[] = {
{"close", io_close},
{"flush", f_flush},
{"lines", f_lines},
{"read", f_read},
{"seek", f_seek},
{"setvbuf", f_setvbuf},
{"write", f_write},
{"__gc", io_gc},
{"__tostring", io_tostring},
{NULL, NULL}
};
static void createmeta (lua_State *L) {
luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
lua_pushvalue(L, -1); /* push metatable */
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
luaL_register(L, NULL, flib); /* file methods */
}
static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) {
*newfile(L) = f;
if (k > 0) {
lua_pushvalue(L, -1);
lua_rawseti(L, LUA_ENVIRONINDEX, k);
}
lua_pushvalue(L, -2); /* copy environment */
lua_setfenv(L, -2); /* set it */
lua_setfield(L, -3, fname);
}
static void newfenv (lua_State *L, lua_CFunction cls) {
lua_createtable(L, 0, 1);
lua_pushcfunction(L, cls);
lua_setfield(L, -2, "__close");
}
LUALIB_API int luaopen_io (lua_State *L) {
createmeta(L);
/* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */
newfenv(L, io_fclose);
lua_replace(L, LUA_ENVIRONINDEX);
/* open library */
luaL_register(L, LUA_IOLIBNAME, iolib);
/* create (and set) default files */
newfenv(L, io_noclose); /* close function for default files */
createstdfile(L, stdin, IO_INPUT, "stdin");
createstdfile(L, stdout, IO_OUTPUT, "stdout");
createstdfile(L, stderr, 0, "stderr");
lua_pop(L, 1); /* pop environment for default files */
lua_getfield(L, -1, "popen");
newfenv(L, io_pclose); /* create environment for 'popen' */
lua_setfenv(L, -2); /* set fenv for 'popen' */
lua_pop(L, 1); /* pop 'popen' */
return 1;
}
/*
** $Id: llex.c,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
** Lexical Analyzer
** See Copyright Notice in lua.h
*/
#include <ctype.h>
#include <locale.h>
#include <string.h>
#define llex_c
#define LUA_CORE
#include "lua.h"
#include "ldo.h"
#include "llex.h"
#include "lobject.h"
#include "lparser.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "lzio.h"
#define next(ls) (ls->current = zgetc(ls->z))
#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
/* ORDER RESERVED */
const char *const luaX_tokens [] = {
"and", "break", "do", "else", "elseif",
"end", "false", "for", "function", "if",
"in", "local", "nil", "not", "or", "repeat",
"return", "then", "true", "until", "while",
"..", "...", "==", ">=", "<=", "~=",
"<number>", "<name>", "<string>", "<eof>",
NULL
};
#define save_and_next(ls) (save(ls, ls->current), next(ls))
static void save (LexState *ls, int c) {
Mbuffer *b = ls->buff;
if (b->n + 1 > b->buffsize) {
size_t newsize;
if (b->buffsize >= MAX_SIZET/2)
luaX_lexerror(ls, "lexical element too long", 0);
newsize = b->buffsize * 2;
luaZ_resizebuffer(ls->L, b, newsize);
}
b->buffer[b->n++] = cast(char, c);
}
void luaX_init (lua_State *L) {
int i;
for (i=0; i<NUM_RESERVED; i++) {
TString *ts = luaS_new(L, luaX_tokens[i]);
luaS_fix(ts); /* reserved words are never collected */
lua_assert(strlen(luaX_tokens[i])+1 <= TOKEN_LEN);
ts->tsv.reserved = cast_byte(i+1); /* reserved word */
}
}
#define MAXSRC 80
const char *luaX_token2str (LexState *ls, int token) {
if (token < FIRST_RESERVED) {
lua_assert(token == cast(unsigned char, token));
return (iscntrl(token)) ? luaO_pushfstring(ls->L, "char(%d)", token) :
luaO_pushfstring(ls->L, "%c", token);
}
else
return luaX_tokens[token-FIRST_RESERVED];
}
static const char *txtToken (LexState *ls, int token) {
switch (token) {
case TK_NAME:
case TK_STRING:
case TK_NUMBER:
save(ls, '\0');
return luaZ_buffer(ls->buff);
default:
return luaX_token2str(ls, token);
}
}
void luaX_lexerror (LexState *ls, const char *msg, int token) {
char buff[MAXSRC];
luaO_chunkid(buff, getstr(ls->source), MAXSRC);
msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg);
if (token)
luaO_pushfstring(ls->L, "%s near " LUA_QS, msg, txtToken(ls, token));
luaD_throw(ls->L, LUA_ERRSYNTAX);
}
void luaX_syntaxerror (LexState *ls, const char *msg) {
luaX_lexerror(ls, msg, ls->t.token);
}
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
lua_State *L = ls->L;
TString *ts = luaS_newlstr(L, str, l);
TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */
if (ttisnil(o))
setbvalue(o, 1); /* make sure `str' will not be collected */
return ts;
}
static void inclinenumber (LexState *ls) {
int old = ls->current;
lua_assert(currIsNewline(ls));
next(ls); /* skip `\n' or `\r' */
if (currIsNewline(ls) && ls->current != old)
next(ls); /* skip `\n\r' or `\r\n' */
if (++ls->linenumber >= MAX_INT)
luaX_syntaxerror(ls, "chunk has too many lines");
}
void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) {
ls->decpoint = '.';
ls->L = L;
ls->lookahead.token = TK_EOS; /* no look-ahead token */
ls->z = z;
ls->fs = NULL;
ls->linenumber = 1;
ls->lastline = 1;
ls->source = source;
luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */
next(ls); /* read first char */
}
/*
** =======================================================
** LEXICAL ANALYZER
** =======================================================
*/
static int check_next (LexState *ls, const char *set) {
if (!strchr(set, ls->current))
return 0;
save_and_next(ls);
return 1;
}
static void buffreplace (LexState *ls, char from, char to) {
size_t n = luaZ_bufflen(ls->buff);
char *p = luaZ_buffer(ls->buff);
while (n--)
if (p[n] == from) p[n] = to;
}
static void trydecpoint (LexState *ls, SemInfo *seminfo) {
/* format error: try to update decimal point separator */
struct lconv *cv = localeconv();
char old = ls->decpoint;
ls->decpoint = (cv ? cv->decimal_point[0] : '.');
buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */
if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) {
/* format error with correct decimal point: no more options */
buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */
luaX_lexerror(ls, "malformed number", TK_NUMBER);
}
}
/* LUA_NUMBER */
static void read_numeral (LexState *ls, SemInfo *seminfo) {
lua_assert(isdigit(ls->current));
do {
save_and_next(ls);
} while (isdigit(ls->current) || ls->current == '.');
if (check_next(ls, "Ee")) /* `E'? */
check_next(ls, "+-"); /* optional exponent sign */
while (isalnum(ls->current) || ls->current == '_')
save_and_next(ls);
save(ls, '\0');
buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */
if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) /* format error? */
trydecpoint(ls, seminfo); /* try to update decimal point separator */
}
static int skip_sep (LexState *ls) {
int count = 0;
int s = ls->current;
lua_assert(s == '[' || s == ']');
save_and_next(ls);
while (ls->current == '=') {
save_and_next(ls);
count++;
}
return (ls->current == s) ? count : (-count) - 1;
}
static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {
int cont = 0;
(void)(cont); /* avoid warnings when `cont' is not used */
save_and_next(ls); /* skip 2nd `[' */
if (currIsNewline(ls)) /* string starts with a newline? */
inclinenumber(ls); /* skip it */
for (;;) {
switch (ls->current) {
case EOZ:
luaX_lexerror(ls, (seminfo) ? "unfinished long string" :
"unfinished long comment", TK_EOS);
break; /* to avoid warnings */
#if defined(LUA_COMPAT_LSTR)
case '[': {
if (skip_sep(ls) == sep) {
save_and_next(ls); /* skip 2nd `[' */
cont++;
#if LUA_COMPAT_LSTR == 1
if (sep == 0)
luaX_lexerror(ls, "nesting of [[...]] is deprecated", '[');
#endif
}
break;
}
#endif
case ']': {
if (skip_sep(ls) == sep) {
save_and_next(ls); /* skip 2nd `]' */
#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2
cont--;
if (sep == 0 && cont >= 0) break;
#endif
goto endloop;
}
break;
}
case '\n':
case '\r': {
save(ls, '\n');
inclinenumber(ls);
if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */
break;
}
default: {
if (seminfo) save_and_next(ls);
else next(ls);
}
}
} endloop:
if (seminfo)
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep),
luaZ_bufflen(ls->buff) - 2*(2 + sep));
}
static void read_string (LexState *ls, int del, SemInfo *seminfo) {
save_and_next(ls);
while (ls->current != del) {
switch (ls->current) {
case EOZ:
luaX_lexerror(ls, "unfinished string", TK_EOS);
continue; /* to avoid warnings */
case '\n':
case '\r':
luaX_lexerror(ls, "unfinished string", TK_STRING);
continue; /* to avoid warnings */
case '\\': {
int c;
next(ls); /* do not save the `\' */
switch (ls->current) {
case 'a': c = '\a'; break;
case 'b': c = '\b'; break;
case 'f': c = '\f'; break;
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'v': c = '\v'; break;
case '\n': /* go through */
case '\r': save(ls, '\n'); inclinenumber(ls); continue;
case EOZ: continue; /* will raise an error next loop */
default: {
if (!isdigit(ls->current))
save_and_next(ls); /* handles \\, \", \', and \? */
else { /* \xxx */
int i = 0;
c = 0;
do {
c = 10*c + (ls->current-'0');
next(ls);
} while (++i<3 && isdigit(ls->current));
if (c > UCHAR_MAX)
luaX_lexerror(ls, "escape sequence too large", TK_STRING);
save(ls, c);
}
continue;
}
}
save(ls, c);
next(ls);
continue;
}
default:
save_and_next(ls);
}
}
save_and_next(ls); /* skip delimiter */
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1,
luaZ_bufflen(ls->buff) - 2);
}
static int llex (LexState *ls, SemInfo *seminfo) {
luaZ_resetbuffer(ls->buff);
for (;;) {
switch (ls->current) {
case '\n':
case '\r': {
inclinenumber(ls);
continue;
}
case '-': {
next(ls);
if (ls->current != '-') return '-';
/* else is a comment */
next(ls);
if (ls->current == '[') {
int sep = skip_sep(ls);
luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */
if (sep >= 0) {
read_long_string(ls, NULL, sep); /* long comment */
luaZ_resetbuffer(ls->buff);
continue;
}
}
/* else short comment */
while (!currIsNewline(ls) && ls->current != EOZ)
next(ls);
continue;
}
case '[': {
int sep = skip_sep(ls);
if (sep >= 0) {
read_long_string(ls, seminfo, sep);
return TK_STRING;
}
else if (sep == -1) return '[';
else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING);
}
case '=': {
next(ls);
if (ls->current != '=') return '=';
else { next(ls); return TK_EQ; }
}
case '<': {
next(ls);
if (ls->current != '=') return '<';
else { next(ls); return TK_LE; }
}
case '>': {
next(ls);
if (ls->current != '=') return '>';
else { next(ls); return TK_GE; }
}
case '~': {
next(ls);
if (ls->current != '=') return '~';
else { next(ls); return TK_NE; }
}
case '"':
case '\'': {
read_string(ls, ls->current, seminfo);
return TK_STRING;
}
case '.': {
save_and_next(ls);
if (check_next(ls, ".")) {
if (check_next(ls, "."))
return TK_DOTS; /* ... */
else return TK_CONCAT; /* .. */
}
else if (!isdigit(ls->current)) return '.';
else {
read_numeral(ls, seminfo);
return TK_NUMBER;
}
}
case EOZ: {
return TK_EOS;
}
default: {
if (isspace(ls->current)) {
lua_assert(!currIsNewline(ls));
next(ls);
continue;
}
else if (isdigit(ls->current)) {
read_numeral(ls, seminfo);
return TK_NUMBER;
}
else if (isalpha(ls->current) || ls->current == '_') {
/* identifier or reserved word */
TString *ts;
do {
save_and_next(ls);
} while (isalnum(ls->current) || ls->current == '_');
ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
luaZ_bufflen(ls->buff));
if (ts->tsv.reserved > 0) /* reserved word? */
return ts->tsv.reserved - 1 + FIRST_RESERVED;
else {
seminfo->ts = ts;
return TK_NAME;
}
}
else {
int c = ls->current;
next(ls);
return c; /* single-char tokens (+ - / ...) */
}
}
}
}
}
void luaX_next (LexState *ls) {
ls->lastline = ls->linenumber;
if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
ls->t = ls->lookahead; /* use this one */
ls->lookahead.token = TK_EOS; /* and discharge it */
}
else
ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */
}
void luaX_lookahead (LexState *ls) {
lua_assert(ls->lookahead.token == TK_EOS);
ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
}
/*
** $Id: llex.c,v 2.20.1.2 2009/11/23 14:58:22 roberto Exp $
** Lexical Analyzer
** See Copyright Notice in lua.h
*/
#include <ctype.h>
#include <locale.h>
#include <string.h>
#define llex_c
#define LUA_CORE
#include "lua.h"
#include "ldo.h"
#include "llex.h"
#include "lobject.h"
#include "lparser.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "lzio.h"
#define next(ls) (ls->current = zgetc(ls->z))
#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
/* ORDER RESERVED */
const char *const luaX_tokens [] = {
"and", "break", "do", "else", "elseif",
"end", "false", "for", "function", "if",
"in", "local", "nil", "not", "or", "repeat",
"return", "then", "true", "until", "while",
"..", "...", "==", ">=", "<=", "~=",
"<number>", "<name>", "<string>", "<eof>",
NULL
};
#define save_and_next(ls) (save(ls, ls->current), next(ls))
static void save (LexState *ls, int c) {
Mbuffer *b = ls->buff;
if (b->n + 1 > b->buffsize) {
size_t newsize;
if (b->buffsize >= MAX_SIZET/2)
luaX_lexerror(ls, "lexical element too long", 0);
newsize = b->buffsize * 2;
luaZ_resizebuffer(ls->L, b, newsize);
}
b->buffer[b->n++] = cast(char, c);
}
void luaX_init (lua_State *L) {
int i;
for (i=0; i<NUM_RESERVED; i++) {
TString *ts = luaS_new(L, luaX_tokens[i]);
luaS_fix(ts); /* reserved words are never collected */
lua_assert(strlen(luaX_tokens[i])+1 <= TOKEN_LEN);
ts->tsv.reserved = cast_byte(i+1); /* reserved word */
}
}
#define MAXSRC 80
const char *luaX_token2str (LexState *ls, int token) {
if (token < FIRST_RESERVED) {
lua_assert(token == cast(unsigned char, token));
return (iscntrl(token)) ? luaO_pushfstring(ls->L, "char(%d)", token) :
luaO_pushfstring(ls->L, "%c", token);
}
else
return luaX_tokens[token-FIRST_RESERVED];
}
static const char *txtToken (LexState *ls, int token) {
switch (token) {
case TK_NAME:
case TK_STRING:
case TK_NUMBER:
save(ls, '\0');
return luaZ_buffer(ls->buff);
default:
return luaX_token2str(ls, token);
}
}
void luaX_lexerror (LexState *ls, const char *msg, int token) {
char buff[MAXSRC];
luaO_chunkid(buff, getstr(ls->source), MAXSRC);
msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg);
if (token)
luaO_pushfstring(ls->L, "%s near " LUA_QS, msg, txtToken(ls, token));
luaD_throw(ls->L, LUA_ERRSYNTAX);
}
void luaX_syntaxerror (LexState *ls, const char *msg) {
luaX_lexerror(ls, msg, ls->t.token);
}
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
lua_State *L = ls->L;
TString *ts = luaS_newlstr(L, str, l);
TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */
if (ttisnil(o)) {
setbvalue(o, 1); /* make sure `str' will not be collected */
luaC_checkGC(L);
}
return ts;
}
static void inclinenumber (LexState *ls) {
int old = ls->current;
lua_assert(currIsNewline(ls));
next(ls); /* skip `\n' or `\r' */
if (currIsNewline(ls) && ls->current != old)
next(ls); /* skip `\n\r' or `\r\n' */
if (++ls->linenumber >= MAX_INT)
luaX_syntaxerror(ls, "chunk has too many lines");
}
void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) {
ls->decpoint = '.';
ls->L = L;
ls->lookahead.token = TK_EOS; /* no look-ahead token */
ls->z = z;
ls->fs = NULL;
ls->linenumber = 1;
ls->lastline = 1;
ls->source = source;
luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */
next(ls); /* read first char */
}
/*
** =======================================================
** LEXICAL ANALYZER
** =======================================================
*/
static int check_next (LexState *ls, const char *set) {
if (!strchr(set, ls->current))
return 0;
save_and_next(ls);
return 1;
}
static void buffreplace (LexState *ls, char from, char to) {
size_t n = luaZ_bufflen(ls->buff);
char *p = luaZ_buffer(ls->buff);
while (n--)
if (p[n] == from) p[n] = to;
}
static void trydecpoint (LexState *ls, SemInfo *seminfo) {
/* format error: try to update decimal point separator */
struct lconv *cv = localeconv();
char old = ls->decpoint;
ls->decpoint = (cv ? cv->decimal_point[0] : '.');
buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */
if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) {
/* format error with correct decimal point: no more options */
buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */
luaX_lexerror(ls, "malformed number", TK_NUMBER);
}
}
/* LUA_NUMBER */
static void read_numeral (LexState *ls, SemInfo *seminfo) {
lua_assert(isdigit(ls->current));
do {
save_and_next(ls);
} while (isdigit(ls->current) || ls->current == '.');
if (check_next(ls, "Ee")) /* `E'? */
check_next(ls, "+-"); /* optional exponent sign */
while (isalnum(ls->current) || ls->current == '_')
save_and_next(ls);
save(ls, '\0');
buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */
if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) /* format error? */
trydecpoint(ls, seminfo); /* try to update decimal point separator */
}
static int skip_sep (LexState *ls) {
int count = 0;
int s = ls->current;
lua_assert(s == '[' || s == ']');
save_and_next(ls);
while (ls->current == '=') {
save_and_next(ls);
count++;
}
return (ls->current == s) ? count : (-count) - 1;
}
static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {
int cont = 0;
(void)(cont); /* avoid warnings when `cont' is not used */
save_and_next(ls); /* skip 2nd `[' */
if (currIsNewline(ls)) /* string starts with a newline? */
inclinenumber(ls); /* skip it */
for (;;) {
switch (ls->current) {
case EOZ:
luaX_lexerror(ls, (seminfo) ? "unfinished long string" :
"unfinished long comment", TK_EOS);
break; /* to avoid warnings */
#if defined(LUA_COMPAT_LSTR)
case '[': {
if (skip_sep(ls) == sep) {
save_and_next(ls); /* skip 2nd `[' */
cont++;
#if LUA_COMPAT_LSTR == 1
if (sep == 0)
luaX_lexerror(ls, "nesting of [[...]] is deprecated", '[');
#endif
}
break;
}
#endif
case ']': {
if (skip_sep(ls) == sep) {
save_and_next(ls); /* skip 2nd `]' */
#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2
cont--;
if (sep == 0 && cont >= 0) break;
#endif
goto endloop;
}
break;
}
case '\n':
case '\r': {
save(ls, '\n');
inclinenumber(ls);
if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */
break;
}
default: {
if (seminfo) save_and_next(ls);
else next(ls);
}
}
} endloop:
if (seminfo)
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep),
luaZ_bufflen(ls->buff) - 2*(2 + sep));
}
static void read_string (LexState *ls, int del, SemInfo *seminfo) {
save_and_next(ls);
while (ls->current != del) {
switch (ls->current) {
case EOZ:
luaX_lexerror(ls, "unfinished string", TK_EOS);
continue; /* to avoid warnings */
case '\n':
case '\r':
luaX_lexerror(ls, "unfinished string", TK_STRING);
continue; /* to avoid warnings */
case '\\': {
int c;
next(ls); /* do not save the `\' */
switch (ls->current) {
case 'a': c = '\a'; break;
case 'b': c = '\b'; break;
case 'f': c = '\f'; break;
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'v': c = '\v'; break;
case '\n': /* go through */
case '\r': save(ls, '\n'); inclinenumber(ls); continue;
case EOZ: continue; /* will raise an error next loop */
default: {
if (!isdigit(ls->current))
save_and_next(ls); /* handles \\, \", \', and \? */
else { /* \xxx */
int i = 0;
c = 0;
do {
c = 10*c + (ls->current-'0');
next(ls);
} while (++i<3 && isdigit(ls->current));
if (c > UCHAR_MAX)
luaX_lexerror(ls, "escape sequence too large", TK_STRING);
save(ls, c);
}
continue;
}
}
save(ls, c);
next(ls);
continue;
}
default:
save_and_next(ls);
}
}
save_and_next(ls); /* skip delimiter */
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1,
luaZ_bufflen(ls->buff) - 2);
}
static int llex (LexState *ls, SemInfo *seminfo) {
luaZ_resetbuffer(ls->buff);
for (;;) {
switch (ls->current) {
case '\n':
case '\r': {
inclinenumber(ls);
continue;
}
case '-': {
next(ls);
if (ls->current != '-') return '-';
/* else is a comment */
next(ls);
if (ls->current == '[') {
int sep = skip_sep(ls);
luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */
if (sep >= 0) {
read_long_string(ls, NULL, sep); /* long comment */
luaZ_resetbuffer(ls->buff);
continue;
}
}
/* else short comment */
while (!currIsNewline(ls) && ls->current != EOZ)
next(ls);
continue;
}
case '[': {
int sep = skip_sep(ls);
if (sep >= 0) {
read_long_string(ls, seminfo, sep);
return TK_STRING;
}
else if (sep == -1) return '[';
else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING);
}
case '=': {
next(ls);
if (ls->current != '=') return '=';
else { next(ls); return TK_EQ; }
}
case '<': {
next(ls);
if (ls->current != '=') return '<';
else { next(ls); return TK_LE; }
}
case '>': {
next(ls);
if (ls->current != '=') return '>';
else { next(ls); return TK_GE; }
}
case '~': {
next(ls);
if (ls->current != '=') return '~';
else { next(ls); return TK_NE; }
}
case '"':
case '\'': {
read_string(ls, ls->current, seminfo);
return TK_STRING;
}
case '.': {
save_and_next(ls);
if (check_next(ls, ".")) {
if (check_next(ls, "."))
return TK_DOTS; /* ... */
else return TK_CONCAT; /* .. */
}
else if (!isdigit(ls->current)) return '.';
else {
read_numeral(ls, seminfo);
return TK_NUMBER;
}
}
case EOZ: {
return TK_EOS;
}
default: {
if (isspace(ls->current)) {
lua_assert(!currIsNewline(ls));
next(ls);
continue;
}
else if (isdigit(ls->current)) {
read_numeral(ls, seminfo);
return TK_NUMBER;
}
else if (isalpha(ls->current) || ls->current == '_') {
/* identifier or reserved word */
TString *ts;
do {
save_and_next(ls);
} while (isalnum(ls->current) || ls->current == '_');
ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
luaZ_bufflen(ls->buff));
if (ts->tsv.reserved > 0) /* reserved word? */
return ts->tsv.reserved - 1 + FIRST_RESERVED;
else {
seminfo->ts = ts;
return TK_NAME;
}
}
else {
int c = ls->current;
next(ls);
return c; /* single-char tokens (+ - / ...) */
}
}
}
}
}
void luaX_next (LexState *ls) {
ls->lastline = ls->linenumber;
if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
ls->t = ls->lookahead; /* use this one */
ls->lookahead.token = TK_EOS; /* and discharge it */
}
else
ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */
}
void luaX_lookahead (LexState *ls) {
lua_assert(ls->lookahead.token == TK_EOS);
ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
}
/*
** $Id: loadlib.c,v 1.52.1.3 2008/08/06 13:29:28 roberto Exp $
** Dynamic library loader for Lua
** See Copyright Notice in lua.h
**
** This module contains an implementation of loadlib for Unix systems
** that have dlfcn, an implementation for Darwin (Mac OS X), an
** implementation for Windows, and a stub for other systems.
*/
#include <stdlib.h>
#include <string.h>
#define loadlib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
/* prefix for open functions in C libraries */
#define LUA_POF "luaopen_"
/* separator for open functions in C libraries */
#define LUA_OFSEP "_"
#define LIBPREFIX "LOADLIB: "
#define POF LUA_POF
#define LIB_FAIL "open"
/* error codes for ll_loadfunc */
#define ERRLIB 1
#define ERRFUNC 2
#define setprogdir(L) ((void)0)
static void ll_unloadlib (void *lib);
static void *ll_load (lua_State *L, const char *path);
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym);
#if defined(LUA_DL_DLOPEN)
/*
** {========================================================================
** This is an implementation of loadlib based on the dlfcn interface.
** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
** as an emulation layer on top of native functions.
** =========================================================================
*/
#include <dlfcn.h>
static void ll_unloadlib (void *lib) {
dlclose(lib);
}
static void *ll_load (lua_State *L, const char *path) {
void *lib = dlopen(path, RTLD_NOW);
if (lib == NULL) lua_pushstring(L, dlerror());
return lib;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
lua_CFunction f = (lua_CFunction)dlsym(lib, sym);
if (f == NULL) lua_pushstring(L, dlerror());
return f;
}
/* }====================================================== */
#elif defined(LUA_DL_DLL)
/*
** {======================================================================
** This is an implementation of loadlib for Windows using native functions.
** =======================================================================
*/
#include <windows.h>
#undef setprogdir
static void setprogdir (lua_State *L) {
char buff[MAX_PATH + 1];
char *lb;
DWORD nsize = sizeof(buff)/sizeof(char);
DWORD n = GetModuleFileNameA(NULL, buff, nsize);
if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL)
luaL_error(L, "unable to get ModuleFileName");
else {
*lb = '\0';
luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff);
lua_remove(L, -2); /* remove original string */
}
}
static void pusherror (lua_State *L) {
int error = GetLastError();
char buffer[128];
if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, error, 0, buffer, sizeof(buffer), NULL))
lua_pushstring(L, buffer);
else
lua_pushfstring(L, "system error %d\n", error);
}
static void ll_unloadlib (void *lib) {
FreeLibrary((HINSTANCE)lib);
}
static void *ll_load (lua_State *L, const char *path) {
HINSTANCE lib = LoadLibraryA(path);
if (lib == NULL) pusherror(L);
return lib;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym);
if (f == NULL) pusherror(L);
return f;
}
/* }====================================================== */
#elif defined(LUA_DL_DYLD)
/*
** {======================================================================
** Native Mac OS X / Darwin Implementation
** =======================================================================
*/
#include <mach-o/dyld.h>
/* Mac appends a `_' before C function names */
#undef POF
#define POF "_" LUA_POF
static void pusherror (lua_State *L) {
const char *err_str;
const char *err_file;
NSLinkEditErrors err;
int err_num;
NSLinkEditError(&err, &err_num, &err_file, &err_str);
lua_pushstring(L, err_str);
}
static const char *errorfromcode (NSObjectFileImageReturnCode ret) {
switch (ret) {
case NSObjectFileImageInappropriateFile:
return "file is not a bundle";
case NSObjectFileImageArch:
return "library is for wrong CPU type";
case NSObjectFileImageFormat:
return "bad format";
case NSObjectFileImageAccess:
return "cannot access file";
case NSObjectFileImageFailure:
default:
return "unable to load library";
}
}
static void ll_unloadlib (void *lib) {
NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES);
}
static void *ll_load (lua_State *L, const char *path) {
NSObjectFileImage img;
NSObjectFileImageReturnCode ret;
/* this would be a rare case, but prevents crashing if it happens */
if(!_dyld_present()) {
lua_pushliteral(L, "dyld not present");
return NULL;
}
ret = NSCreateObjectFileImageFromFile(path, &img);
if (ret == NSObjectFileImageSuccess) {
NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE |
NSLINKMODULE_OPTION_RETURN_ON_ERROR);
NSDestroyObjectFileImage(img);
if (mod == NULL) pusherror(L);
return mod;
}
lua_pushstring(L, errorfromcode(ret));
return NULL;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym);
if (nss == NULL) {
lua_pushfstring(L, "symbol " LUA_QS " not found", sym);
return NULL;
}
return (lua_CFunction)NSAddressOfSymbol(nss);
}
/* }====================================================== */
#else
/*
** {======================================================
** Fallback for other systems
** =======================================================
*/
#undef LIB_FAIL
#define LIB_FAIL "absent"
#define DLMSG "dynamic libraries not enabled; check your Lua installation"
static void ll_unloadlib (void *lib) {
(void)lib; /* to avoid warnings */
}
static void *ll_load (lua_State *L, const char *path) {
(void)path; /* to avoid warnings */
lua_pushliteral(L, DLMSG);
return NULL;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
(void)lib; (void)sym; /* to avoid warnings */
lua_pushliteral(L, DLMSG);
return NULL;
}
/* }====================================================== */
#endif
static void **ll_register (lua_State *L, const char *path) {
void **plib;
lua_pushfstring(L, "%s%s", LIBPREFIX, path);
lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */
if (!lua_isnil(L, -1)) /* is there an entry? */
plib = (void **)lua_touserdata(L, -1);
else { /* no entry yet; create one */
lua_pop(L, 1);
plib = (void **)lua_newuserdata(L, sizeof(const void *));
*plib = NULL;
luaL_getmetatable(L, "_LOADLIB");
lua_setmetatable(L, -2);
lua_pushfstring(L, "%s%s", LIBPREFIX, path);
lua_pushvalue(L, -2);
lua_settable(L, LUA_REGISTRYINDEX);
}
return plib;
}
/*
** __gc tag method: calls library's `ll_unloadlib' function with the lib
** handle
*/
static int gctm (lua_State *L) {
void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB");
if (*lib) ll_unloadlib(*lib);
*lib = NULL; /* mark library as closed */
return 0;
}
static int ll_loadfunc (lua_State *L, const char *path, const char *sym) {
void **reg = ll_register(L, path);
if (*reg == NULL) *reg = ll_load(L, path);
if (*reg == NULL)
return ERRLIB; /* unable to load library */
else {
lua_CFunction f = ll_sym(L, *reg, sym);
if (f == NULL)
return ERRFUNC; /* unable to find function */
lua_pushcfunction(L, f);
return 0; /* return function */
}
}
static int ll_loadlib (lua_State *L) {
const char *path = luaL_checkstring(L, 1);
const char *init = luaL_checkstring(L, 2);
int stat = ll_loadfunc(L, path, init);
if (stat == 0) /* no errors? */
return 1; /* return the loaded function */
else { /* error; error message is on stack top */
lua_pushnil(L);
lua_insert(L, -2);
lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init");
return 3; /* return nil, error message, and where */
}
}
/*
** {======================================================
** 'require' function
** =======================================================
*/
static int readable (const char *filename) {
FILE *f = fopen(filename, "r"); /* try to open file */
if (f == NULL) return 0; /* open failed */
fclose(f);
return 1;
}
static const char *pushnexttemplate (lua_State *L, const char *path) {
const char *l;
while (*path == *LUA_PATHSEP) path++; /* skip separators */
if (*path == '\0') return NULL; /* no more templates */
l = strchr(path, *LUA_PATHSEP); /* find next separator */
if (l == NULL) l = path + strlen(path);
lua_pushlstring(L, path, l - path); /* template */
return l;
}
static const char *findfile (lua_State *L, const char *name,
const char *pname) {
const char *path;
name = luaL_gsub(L, name, ".", LUA_DIRSEP);
lua_getfield(L, LUA_ENVIRONINDEX, pname);
path = lua_tostring(L, -1);
if (path == NULL)
luaL_error(L, LUA_QL("package.%s") " must be a string", pname);
lua_pushliteral(L, ""); /* error accumulator */
while ((path = pushnexttemplate(L, path)) != NULL) {
const char *filename;
filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name);
lua_remove(L, -2); /* remove path template */
if (readable(filename)) /* does file exist and is readable? */
return filename; /* return that file name */
lua_pushfstring(L, "\n\tno file " LUA_QS, filename);
lua_remove(L, -2); /* remove file name */
lua_concat(L, 2); /* add entry to possible error message */
}
return NULL; /* not found */
}
static void loaderror (lua_State *L, const char *filename) {
luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s",
lua_tostring(L, 1), filename, lua_tostring(L, -1));
}
static int loader_Lua (lua_State *L) {
const char *filename;
const char *name = luaL_checkstring(L, 1);
filename = findfile(L, name, "path");
if (filename == NULL) return 1; /* library not found in this path */
if (luaL_loadfile(L, filename) != 0)
loaderror(L, filename);
return 1; /* library loaded successfully */
}
static const char *mkfuncname (lua_State *L, const char *modname) {
const char *funcname;
const char *mark = strchr(modname, *LUA_IGMARK);
if (mark) modname = mark + 1;
funcname = luaL_gsub(L, modname, ".", LUA_OFSEP);
funcname = lua_pushfstring(L, POF"%s", funcname);
lua_remove(L, -2); /* remove 'gsub' result */
return funcname;
}
static int loader_C (lua_State *L) {
const char *funcname;
const char *name = luaL_checkstring(L, 1);
const char *filename = findfile(L, name, "cpath");
if (filename == NULL) return 1; /* library not found in this path */
funcname = mkfuncname(L, name);
if (ll_loadfunc(L, filename, funcname) != 0)
loaderror(L, filename);
return 1; /* library loaded successfully */
}
static int loader_Croot (lua_State *L) {
const char *funcname;
const char *filename;
const char *name = luaL_checkstring(L, 1);
const char *p = strchr(name, '.');
int stat;
if (p == NULL) return 0; /* is root */
lua_pushlstring(L, name, p - name);
filename = findfile(L, lua_tostring(L, -1), "cpath");
if (filename == NULL) return 1; /* root not found */
funcname = mkfuncname(L, name);
if ((stat = ll_loadfunc(L, filename, funcname)) != 0) {
if (stat != ERRFUNC) loaderror(L, filename); /* real error */
lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS,
name, filename);
return 1; /* function not found */
}
return 1;
}
static int loader_preload (lua_State *L) {
const char *name = luaL_checkstring(L, 1);
lua_getfield(L, LUA_ENVIRONINDEX, "preload");
if (!lua_istable(L, -1))
luaL_error(L, LUA_QL("package.preload") " must be a table");
lua_getfield(L, -1, name);
if (lua_isnil(L, -1)) /* not found? */
lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
return 1;
}
static const int sentinel_ = 0;
#define sentinel ((void *)&sentinel_)
static int ll_require (lua_State *L) {
const char *name = luaL_checkstring(L, 1);
int i;
lua_settop(L, 1); /* _LOADED table will be at index 2 */
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, 2, name);
if (lua_toboolean(L, -1)) { /* is it there? */
if (lua_touserdata(L, -1) == sentinel) /* check loops */
luaL_error(L, "loop or previous error loading module " LUA_QS, name);
return 1; /* package is already loaded */
}
/* else must load it; iterate over available loaders */
lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
if (!lua_istable(L, -1))
luaL_error(L, LUA_QL("package.loaders") " must be a table");
lua_pushliteral(L, ""); /* error message accumulator */
for (i=1; ; i++) {
lua_rawgeti(L, -2, i); /* get a loader */
if (lua_isnil(L, -1))
luaL_error(L, "module " LUA_QS " not found:%s",
name, lua_tostring(L, -2));
lua_pushstring(L, name);
lua_call(L, 1, 1); /* call it */
if (lua_isfunction(L, -1)) /* did it find module? */
break; /* module loaded successfully */
else if (lua_isstring(L, -1)) /* loader returned error message? */
lua_concat(L, 2); /* accumulate it */
else
lua_pop(L, 1);
}
lua_pushlightuserdata(L, sentinel);
lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */
lua_pushstring(L, name); /* pass name as argument to module */
lua_call(L, 1, 1); /* run loaded module */
if (!lua_isnil(L, -1)) /* non-nil return? */
lua_setfield(L, 2, name); /* _LOADED[name] = returned value */
lua_getfield(L, 2, name);
if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */
lua_pushboolean(L, 1); /* use true as result */
lua_pushvalue(L, -1); /* extra copy to be returned */
lua_setfield(L, 2, name); /* _LOADED[name] = true */
}
return 1;
}
/* }====================================================== */
/*
** {======================================================
** 'module' function
** =======================================================
*/
static void setfenv (lua_State *L) {
lua_Debug ar;
if (lua_getstack(L, 1, &ar) == 0 ||
lua_getinfo(L, "f", &ar) == 0 || /* get calling function */
lua_iscfunction(L, -1))
luaL_error(L, LUA_QL("module") " not called from a Lua function");
lua_pushvalue(L, -2);
lua_setfenv(L, -2);
lua_pop(L, 1);
}
static void dooptions (lua_State *L, int n) {
int i;
for (i = 2; i <= n; i++) {
lua_pushvalue(L, i); /* get option (a function) */
lua_pushvalue(L, -2); /* module */
lua_call(L, 1, 0);
}
}
static void modinit (lua_State *L, const char *modname) {
const char *dot;
lua_pushvalue(L, -1);
lua_setfield(L, -2, "_M"); /* module._M = module */
lua_pushstring(L, modname);
lua_setfield(L, -2, "_NAME");
dot = strrchr(modname, '.'); /* look for last dot in module name */
if (dot == NULL) dot = modname;
else dot++;
/* set _PACKAGE as package name (full module name minus last part) */
lua_pushlstring(L, modname, dot - modname);
lua_setfield(L, -2, "_PACKAGE");
}
static int ll_module (lua_State *L) {
const char *modname = luaL_checkstring(L, 1);
int loaded = lua_gettop(L) + 1; /* index of _LOADED table */
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, loaded, modname); /* get _LOADED[modname] */
if (!lua_istable(L, -1)) { /* not found? */
lua_pop(L, 1); /* remove previous result */
/* try global variable (and create one if it does not exist) */
if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL)
return luaL_error(L, "name conflict for module " LUA_QS, modname);
lua_pushvalue(L, -1);
lua_setfield(L, loaded, modname); /* _LOADED[modname] = new table */
}
/* check whether table already has a _NAME field */
lua_getfield(L, -1, "_NAME");
if (!lua_isnil(L, -1)) /* is table an initialized module? */
lua_pop(L, 1);
else { /* no; initialize it */
lua_pop(L, 1);
modinit(L, modname);
}
lua_pushvalue(L, -1);
setfenv(L);
dooptions(L, loaded - 1);
return 0;
}
static int ll_seeall (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
if (!lua_getmetatable(L, 1)) {
lua_createtable(L, 0, 1); /* create new metatable */
lua_pushvalue(L, -1);
lua_setmetatable(L, 1);
}
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setfield(L, -2, "__index"); /* mt.__index = _G */
return 0;
}
/* }====================================================== */
/* auxiliary mark (for internal use) */
#define AUXMARK "\1"
static void setpath (lua_State *L, const char *fieldname, const char *envname,
const char *def) {
const char *path = getenv(envname);
if (path == NULL) /* no environment variable? */
lua_pushstring(L, def); /* use default */
else {
/* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */
path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP,
LUA_PATHSEP AUXMARK LUA_PATHSEP);
luaL_gsub(L, path, AUXMARK, def);
lua_remove(L, -2);
}
setprogdir(L);
lua_setfield(L, -2, fieldname);
}
static const luaL_Reg pk_funcs[] = {
{"loadlib", ll_loadlib},
{"seeall", ll_seeall},
{NULL, NULL}
};
static const luaL_Reg ll_funcs[] = {
{"module", ll_module},
{"require", ll_require},
{NULL, NULL}
};
static const lua_CFunction loaders[] =
{loader_preload, loader_Lua, loader_C, loader_Croot, NULL};
LUALIB_API int luaopen_package (lua_State *L) {
int i;
/* create new type _LOADLIB */
luaL_newmetatable(L, "_LOADLIB");
lua_pushcfunction(L, gctm);
lua_setfield(L, -2, "__gc");
/* create `package' table */
luaL_register(L, LUA_LOADLIBNAME, pk_funcs);
#if defined(LUA_COMPAT_LOADLIB)
lua_getfield(L, -1, "loadlib");
lua_setfield(L, LUA_GLOBALSINDEX, "loadlib");
#endif
lua_pushvalue(L, -1);
lua_replace(L, LUA_ENVIRONINDEX);
/* create `loaders' table */
lua_createtable(L, 0, sizeof(loaders)/sizeof(loaders[0]) - 1);
/* fill it with pre-defined loaders */
for (i=0; loaders[i] != NULL; i++) {
lua_pushcfunction(L, loaders[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "loaders"); /* put it in field `loaders' */
setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT); /* set field `path' */
setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */
/* store config information */
lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n"
LUA_EXECDIR "\n" LUA_IGMARK);
lua_setfield(L, -2, "config");
/* set field `loaded' */
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2);
lua_setfield(L, -2, "loaded");
/* set field `preload' */
lua_newtable(L);
lua_setfield(L, -2, "preload");
lua_pushvalue(L, LUA_GLOBALSINDEX);
luaL_register(L, NULL, ll_funcs); /* open lib into global table */
lua_pop(L, 1);
return 1; /* return 'package' table */
}
/*
** $Id: loadlib.c,v 1.52.1.4 2009/09/09 13:17:16 roberto Exp $
** Dynamic library loader for Lua
** See Copyright Notice in lua.h
**
** This module contains an implementation of loadlib for Unix systems
** that have dlfcn, an implementation for Darwin (Mac OS X), an
** implementation for Windows, and a stub for other systems.
*/
#include <stdlib.h>
#include <string.h>
#define loadlib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
/* prefix for open functions in C libraries */
#define LUA_POF "luaopen_"
/* separator for open functions in C libraries */
#define LUA_OFSEP "_"
#define LIBPREFIX "LOADLIB: "
#define POF LUA_POF
#define LIB_FAIL "open"
/* error codes for ll_loadfunc */
#define ERRLIB 1
#define ERRFUNC 2
#define setprogdir(L) ((void)0)
static void ll_unloadlib (void *lib);
static void *ll_load (lua_State *L, const char *path);
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym);
#if defined(LUA_DL_DLOPEN)
/*
** {========================================================================
** This is an implementation of loadlib based on the dlfcn interface.
** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
** as an emulation layer on top of native functions.
** =========================================================================
*/
#include <dlfcn.h>
static void ll_unloadlib (void *lib) {
dlclose(lib);
}
static void *ll_load (lua_State *L, const char *path) {
void *lib = dlopen(path, RTLD_NOW);
if (lib == NULL) lua_pushstring(L, dlerror());
return lib;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
lua_CFunction f = (lua_CFunction)dlsym(lib, sym);
if (f == NULL) lua_pushstring(L, dlerror());
return f;
}
/* }====================================================== */
#elif defined(LUA_DL_DLL)
/*
** {======================================================================
** This is an implementation of loadlib for Windows using native functions.
** =======================================================================
*/
#include <windows.h>
#undef setprogdir
static void setprogdir (lua_State *L) {
char buff[MAX_PATH + 1];
char *lb;
DWORD nsize = sizeof(buff)/sizeof(char);
DWORD n = GetModuleFileNameA(NULL, buff, nsize);
if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL)
luaL_error(L, "unable to get ModuleFileName");
else {
*lb = '\0';
luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff);
lua_remove(L, -2); /* remove original string */
}
}
static void pusherror (lua_State *L) {
int error = GetLastError();
char buffer[128];
if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, error, 0, buffer, sizeof(buffer), NULL))
lua_pushstring(L, buffer);
else
lua_pushfstring(L, "system error %d\n", error);
}
static void ll_unloadlib (void *lib) {
FreeLibrary((HINSTANCE)lib);
}
static void *ll_load (lua_State *L, const char *path) {
HINSTANCE lib = LoadLibraryA(path);
if (lib == NULL) pusherror(L);
return lib;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym);
if (f == NULL) pusherror(L);
return f;
}
/* }====================================================== */
#elif defined(LUA_DL_DYLD)
/*
** {======================================================================
** Native Mac OS X / Darwin Implementation
** =======================================================================
*/
#include <mach-o/dyld.h>
/* Mac appends a `_' before C function names */
#undef POF
#define POF "_" LUA_POF
static void pusherror (lua_State *L) {
const char *err_str;
const char *err_file;
NSLinkEditErrors err;
int err_num;
NSLinkEditError(&err, &err_num, &err_file, &err_str);
lua_pushstring(L, err_str);
}
static const char *errorfromcode (NSObjectFileImageReturnCode ret) {
switch (ret) {
case NSObjectFileImageInappropriateFile:
return "file is not a bundle";
case NSObjectFileImageArch:
return "library is for wrong CPU type";
case NSObjectFileImageFormat:
return "bad format";
case NSObjectFileImageAccess:
return "cannot access file";
case NSObjectFileImageFailure:
default:
return "unable to load library";
}
}
static void ll_unloadlib (void *lib) {
NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES);
}
static void *ll_load (lua_State *L, const char *path) {
NSObjectFileImage img;
NSObjectFileImageReturnCode ret;
/* this would be a rare case, but prevents crashing if it happens */
if(!_dyld_present()) {
lua_pushliteral(L, "dyld not present");
return NULL;
}
ret = NSCreateObjectFileImageFromFile(path, &img);
if (ret == NSObjectFileImageSuccess) {
NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE |
NSLINKMODULE_OPTION_RETURN_ON_ERROR);
NSDestroyObjectFileImage(img);
if (mod == NULL) pusherror(L);
return mod;
}
lua_pushstring(L, errorfromcode(ret));
return NULL;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym);
if (nss == NULL) {
lua_pushfstring(L, "symbol " LUA_QS " not found", sym);
return NULL;
}
return (lua_CFunction)NSAddressOfSymbol(nss);
}
/* }====================================================== */
#else
/*
** {======================================================
** Fallback for other systems
** =======================================================
*/
#undef LIB_FAIL
#define LIB_FAIL "absent"
#define DLMSG "dynamic libraries not enabled; check your Lua installation"
static void ll_unloadlib (void *lib) {
(void)lib; /* to avoid warnings */
}
static void *ll_load (lua_State *L, const char *path) {
(void)path; /* to avoid warnings */
lua_pushliteral(L, DLMSG);
return NULL;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
(void)lib; (void)sym; /* to avoid warnings */
lua_pushliteral(L, DLMSG);
return NULL;
}
/* }====================================================== */
#endif
static void **ll_register (lua_State *L, const char *path) {
void **plib;
lua_pushfstring(L, "%s%s", LIBPREFIX, path);
lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */
if (!lua_isnil(L, -1)) /* is there an entry? */
plib = (void **)lua_touserdata(L, -1);
else { /* no entry yet; create one */
lua_pop(L, 1);
plib = (void **)lua_newuserdata(L, sizeof(const void *));
*plib = NULL;
luaL_getmetatable(L, "_LOADLIB");
lua_setmetatable(L, -2);
lua_pushfstring(L, "%s%s", LIBPREFIX, path);
lua_pushvalue(L, -2);
lua_settable(L, LUA_REGISTRYINDEX);
}
return plib;
}
/*
** __gc tag method: calls library's `ll_unloadlib' function with the lib
** handle
*/
static int gctm (lua_State *L) {
void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB");
if (*lib) ll_unloadlib(*lib);
*lib = NULL; /* mark library as closed */
return 0;
}
static int ll_loadfunc (lua_State *L, const char *path, const char *sym) {
void **reg = ll_register(L, path);
if (*reg == NULL) *reg = ll_load(L, path);
if (*reg == NULL)
return ERRLIB; /* unable to load library */
else {
lua_CFunction f = ll_sym(L, *reg, sym);
if (f == NULL)
return ERRFUNC; /* unable to find function */
lua_pushcfunction(L, f);
return 0; /* return function */
}
}
static int ll_loadlib (lua_State *L) {
const char *path = luaL_checkstring(L, 1);
const char *init = luaL_checkstring(L, 2);
int stat = ll_loadfunc(L, path, init);
if (stat == 0) /* no errors? */
return 1; /* return the loaded function */
else { /* error; error message is on stack top */
lua_pushnil(L);
lua_insert(L, -2);
lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init");
return 3; /* return nil, error message, and where */
}
}
/*
** {======================================================
** 'require' function
** =======================================================
*/
static int readable (const char *filename) {
FILE *f = fopen(filename, "r"); /* try to open file */
if (f == NULL) return 0; /* open failed */
fclose(f);
return 1;
}
static const char *pushnexttemplate (lua_State *L, const char *path) {
const char *l;
while (*path == *LUA_PATHSEP) path++; /* skip separators */
if (*path == '\0') return NULL; /* no more templates */
l = strchr(path, *LUA_PATHSEP); /* find next separator */
if (l == NULL) l = path + strlen(path);
lua_pushlstring(L, path, l - path); /* template */
return l;
}
static const char *findfile (lua_State *L, const char *name,
const char *pname) {
const char *path;
name = luaL_gsub(L, name, ".", LUA_DIRSEP);
lua_getfield(L, LUA_ENVIRONINDEX, pname);
path = lua_tostring(L, -1);
if (path == NULL)
luaL_error(L, LUA_QL("package.%s") " must be a string", pname);
lua_pushliteral(L, ""); /* error accumulator */
while ((path = pushnexttemplate(L, path)) != NULL) {
const char *filename;
filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name);
lua_remove(L, -2); /* remove path template */
if (readable(filename)) /* does file exist and is readable? */
return filename; /* return that file name */
lua_pushfstring(L, "\n\tno file " LUA_QS, filename);
lua_remove(L, -2); /* remove file name */
lua_concat(L, 2); /* add entry to possible error message */
}
return NULL; /* not found */
}
static void loaderror (lua_State *L, const char *filename) {
luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s",
lua_tostring(L, 1), filename, lua_tostring(L, -1));
}
static int loader_Lua (lua_State *L) {
const char *filename;
const char *name = luaL_checkstring(L, 1);
filename = findfile(L, name, "path");
if (filename == NULL) return 1; /* library not found in this path */
if (luaL_loadfile(L, filename) != 0)
loaderror(L, filename);
return 1; /* library loaded successfully */
}
static const char *mkfuncname (lua_State *L, const char *modname) {
const char *funcname;
const char *mark = strchr(modname, *LUA_IGMARK);
if (mark) modname = mark + 1;
funcname = luaL_gsub(L, modname, ".", LUA_OFSEP);
funcname = lua_pushfstring(L, POF"%s", funcname);
lua_remove(L, -2); /* remove 'gsub' result */
return funcname;
}
static int loader_C (lua_State *L) {
const char *funcname;
const char *name = luaL_checkstring(L, 1);
const char *filename = findfile(L, name, "cpath");
if (filename == NULL) return 1; /* library not found in this path */
funcname = mkfuncname(L, name);
if (ll_loadfunc(L, filename, funcname) != 0)
loaderror(L, filename);
return 1; /* library loaded successfully */
}
static int loader_Croot (lua_State *L) {
const char *funcname;
const char *filename;
const char *name = luaL_checkstring(L, 1);
const char *p = strchr(name, '.');
int stat;
if (p == NULL) return 0; /* is root */
lua_pushlstring(L, name, p - name);
filename = findfile(L, lua_tostring(L, -1), "cpath");
if (filename == NULL) return 1; /* root not found */
funcname = mkfuncname(L, name);
if ((stat = ll_loadfunc(L, filename, funcname)) != 0) {
if (stat != ERRFUNC) loaderror(L, filename); /* real error */
lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS,
name, filename);
return 1; /* function not found */
}
return 1;
}
static int loader_preload (lua_State *L) {
const char *name = luaL_checkstring(L, 1);
lua_getfield(L, LUA_ENVIRONINDEX, "preload");
if (!lua_istable(L, -1))
luaL_error(L, LUA_QL("package.preload") " must be a table");
lua_getfield(L, -1, name);
if (lua_isnil(L, -1)) /* not found? */
lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
return 1;
}
static const int sentinel_ = 0;
#define sentinel ((void *)&sentinel_)
static int ll_require (lua_State *L) {
const char *name = luaL_checkstring(L, 1);
int i;
lua_settop(L, 1); /* _LOADED table will be at index 2 */
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, 2, name);
if (lua_toboolean(L, -1)) { /* is it there? */
if (lua_touserdata(L, -1) == sentinel) /* check loops */
luaL_error(L, "loop or previous error loading module " LUA_QS, name);
return 1; /* package is already loaded */
}
/* else must load it; iterate over available loaders */
lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
if (!lua_istable(L, -1))
luaL_error(L, LUA_QL("package.loaders") " must be a table");
lua_pushliteral(L, ""); /* error message accumulator */
for (i=1; ; i++) {
lua_rawgeti(L, -2, i); /* get a loader */
if (lua_isnil(L, -1))
luaL_error(L, "module " LUA_QS " not found:%s",
name, lua_tostring(L, -2));
lua_pushstring(L, name);
lua_call(L, 1, 1); /* call it */
if (lua_isfunction(L, -1)) /* did it find module? */
break; /* module loaded successfully */
else if (lua_isstring(L, -1)) /* loader returned error message? */
lua_concat(L, 2); /* accumulate it */
else
lua_pop(L, 1);
}
lua_pushlightuserdata(L, sentinel);
lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */
lua_pushstring(L, name); /* pass name as argument to module */
lua_call(L, 1, 1); /* run loaded module */
if (!lua_isnil(L, -1)) /* non-nil return? */
lua_setfield(L, 2, name); /* _LOADED[name] = returned value */
lua_getfield(L, 2, name);
if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */
lua_pushboolean(L, 1); /* use true as result */
lua_pushvalue(L, -1); /* extra copy to be returned */
lua_setfield(L, 2, name); /* _LOADED[name] = true */
}
return 1;
}
/* }====================================================== */
/*
** {======================================================
** 'module' function
** =======================================================
*/
static void setfenv (lua_State *L) {
lua_Debug ar;
if (lua_getstack(L, 1, &ar) == 0 ||
lua_getinfo(L, "f", &ar) == 0 || /* get calling function */
lua_iscfunction(L, -1))
luaL_error(L, LUA_QL("module") " not called from a Lua function");
lua_pushvalue(L, -2);
lua_setfenv(L, -2);
lua_pop(L, 1);
}
static void dooptions (lua_State *L, int n) {
int i;
for (i = 2; i <= n; i++) {
lua_pushvalue(L, i); /* get option (a function) */
lua_pushvalue(L, -2); /* module */
lua_call(L, 1, 0);
}
}
static void modinit (lua_State *L, const char *modname) {
const char *dot;
lua_pushvalue(L, -1);
lua_setfield(L, -2, "_M"); /* module._M = module */
lua_pushstring(L, modname);
lua_setfield(L, -2, "_NAME");
dot = strrchr(modname, '.'); /* look for last dot in module name */
if (dot == NULL) dot = modname;
else dot++;
/* set _PACKAGE as package name (full module name minus last part) */
lua_pushlstring(L, modname, dot - modname);
lua_setfield(L, -2, "_PACKAGE");
}
static int ll_module (lua_State *L) {
const char *modname = luaL_checkstring(L, 1);
int loaded = lua_gettop(L) + 1; /* index of _LOADED table */
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, loaded, modname); /* get _LOADED[modname] */
if (!lua_istable(L, -1)) { /* not found? */
lua_pop(L, 1); /* remove previous result */
/* try global variable (and create one if it does not exist) */
if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL)
return luaL_error(L, "name conflict for module " LUA_QS, modname);
lua_pushvalue(L, -1);
lua_setfield(L, loaded, modname); /* _LOADED[modname] = new table */
}
/* check whether table already has a _NAME field */
lua_getfield(L, -1, "_NAME");
if (!lua_isnil(L, -1)) /* is table an initialized module? */
lua_pop(L, 1);
else { /* no; initialize it */
lua_pop(L, 1);
modinit(L, modname);
}
lua_pushvalue(L, -1);
setfenv(L);
dooptions(L, loaded - 1);
return 0;
}
static int ll_seeall (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
if (!lua_getmetatable(L, 1)) {
lua_createtable(L, 0, 1); /* create new metatable */
lua_pushvalue(L, -1);
lua_setmetatable(L, 1);
}
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setfield(L, -2, "__index"); /* mt.__index = _G */
return 0;
}
/* }====================================================== */
/* auxiliary mark (for internal use) */
#define AUXMARK "\1"
static void setpath (lua_State *L, const char *fieldname, const char *envname,
const char *def) {
const char *path = getenv(envname);
if (path == NULL) /* no environment variable? */
lua_pushstring(L, def); /* use default */
else {
/* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */
path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP,
LUA_PATHSEP AUXMARK LUA_PATHSEP);
luaL_gsub(L, path, AUXMARK, def);
lua_remove(L, -2);
}
setprogdir(L);
lua_setfield(L, -2, fieldname);
}
static const luaL_Reg pk_funcs[] = {
{"loadlib", ll_loadlib},
{"seeall", ll_seeall},
{NULL, NULL}
};
static const luaL_Reg ll_funcs[] = {
{"module", ll_module},
{"require", ll_require},
{NULL, NULL}
};
static const lua_CFunction loaders[] =
{loader_preload, loader_Lua, loader_C, loader_Croot, NULL};
LUALIB_API int luaopen_package (lua_State *L) {
int i;
/* create new type _LOADLIB */
luaL_newmetatable(L, "_LOADLIB");
lua_pushcfunction(L, gctm);
lua_setfield(L, -2, "__gc");
/* create `package' table */
luaL_register(L, LUA_LOADLIBNAME, pk_funcs);
#if defined(LUA_COMPAT_LOADLIB)
lua_getfield(L, -1, "loadlib");
lua_setfield(L, LUA_GLOBALSINDEX, "loadlib");
#endif
lua_pushvalue(L, -1);
lua_replace(L, LUA_ENVIRONINDEX);
/* create `loaders' table */
lua_createtable(L, sizeof(loaders)/sizeof(loaders[0]) - 1, 0);
/* fill it with pre-defined loaders */
for (i=0; loaders[i] != NULL; i++) {
lua_pushcfunction(L, loaders[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "loaders"); /* put it in field `loaders' */
setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT); /* set field `path' */
setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */
/* store config information */
lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n"
LUA_EXECDIR "\n" LUA_IGMARK);
lua_setfield(L, -2, "config");
/* set field `loaded' */
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2);
lua_setfield(L, -2, "loaded");
/* set field `preload' */
lua_newtable(L);
lua_setfield(L, -2, "preload");
lua_pushvalue(L, LUA_GLOBALSINDEX);
luaL_register(L, NULL, ll_funcs); /* open lib into global table */
lua_pop(L, 1);
return 1; /* return 'package' table */
}
/*
** $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
*/
#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define lstrlib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
/* macro to `unsign' a character */
#define uchar(c) ((unsigned char)(c))
static int str_len (lua_State *L) {
size_t l;
luaL_checklstring(L, 1, &l);
lua_pushinteger(L, l);
return 1;
}
static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) {
/* relative string position: negative means back from end */
if (pos < 0) pos += (ptrdiff_t)len + 1;
return (pos >= 0) ? pos : 0;
}
static int str_sub (lua_State *L) {
size_t l;
const char *s = luaL_checklstring(L, 1, &l);
ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l);
ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l);
if (start < 1) start = 1;
if (end > (ptrdiff_t)l) end = (ptrdiff_t)l;
if (start <= end)
lua_pushlstring(L, s+start-1, end-start+1);
else lua_pushliteral(L, "");
return 1;
}
static int str_reverse (lua_State *L) {
size_t l;
luaL_Buffer b;
const char *s = luaL_checklstring(L, 1, &l);
luaL_buffinit(L, &b);
while (l--) luaL_addchar(&b, s[l]);
luaL_pushresult(&b);
return 1;
}
static int str_lower (lua_State *L) {
size_t l;
size_t i;
luaL_Buffer b;
const char *s = luaL_checklstring(L, 1, &l);
luaL_buffinit(L, &b);
for (i=0; i<l; i++)
luaL_addchar(&b, tolower(uchar(s[i])));
luaL_pushresult(&b);
return 1;
}
static int str_upper (lua_State *L) {
size_t l;
size_t i;
luaL_Buffer b;
const char *s = luaL_checklstring(L, 1, &l);
luaL_buffinit(L, &b);
for (i=0; i<l; i++)
luaL_addchar(&b, toupper(uchar(s[i])));
luaL_pushresult(&b);
return 1;
}
static int str_rep (lua_State *L) {
size_t l;
luaL_Buffer b;
const char *s = luaL_checklstring(L, 1, &l);
int n = luaL_checkint(L, 2);
luaL_buffinit(L, &b);
while (n-- > 0)
luaL_addlstring(&b, s, l);
luaL_pushresult(&b);
return 1;
}
static int str_byte (lua_State *L) {
size_t l;
const char *s = luaL_checklstring(L, 1, &l);
ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l);
ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l);
int n, i;
if (posi <= 0) posi = 1;
if ((size_t)pose > l) pose = l;
if (posi > pose) return 0; /* empty interval; return no values */
n = (int)(pose - posi + 1);
if (posi + n <= pose) /* overflow? */
luaL_error(L, "string slice too long");
luaL_checkstack(L, n, "string slice too long");
for (i=0; i<n; i++)
lua_pushinteger(L, uchar(s[posi+i-1]));
return n;
}
static int str_char (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
int i;
luaL_Buffer b;
luaL_buffinit(L, &b);
for (i=1; i<=n; i++) {
int c = luaL_checkint(L, i);
luaL_argcheck(L, uchar(c) == c, i, "invalid value");
luaL_addchar(&b, uchar(c));
}
luaL_pushresult(&b);
return 1;
}
static int writer (lua_State *L, const void* b, size_t size, void* B) {
(void)L;
luaL_addlstring((luaL_Buffer*) B, (const char *)b, size);
return 0;
}
static int str_dump (lua_State *L) {
luaL_Buffer b;
luaL_checktype(L, 1, LUA_TFUNCTION);
lua_settop(L, 1);
luaL_buffinit(L,&b);
if (lua_dump(L, writer, &b) != 0)
luaL_error(L, "unable to dump given function");
luaL_pushresult(&b);
return 1;
}
/*
** {======================================================
** PATTERN MATCHING
** =======================================================
*/
#define CAP_UNFINISHED (-1)
#define CAP_POSITION (-2)
typedef struct MatchState {
const char *src_init; /* init of source string */
const char *src_end; /* end (`\0') of source string */
lua_State *L;
int level; /* total number of captures (finished or unfinished) */
struct {
const char *init;
ptrdiff_t len;
} capture[LUA_MAXCAPTURES];
} MatchState;
#define L_ESC '%'
#define SPECIALS "^$*+?.([%-"
static int check_capture (MatchState *ms, int l) {
l -= '1';
if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
return luaL_error(ms->L, "invalid capture index");
return l;
}
static int capture_to_close (MatchState *ms) {
int level = ms->level;
for (level--; level>=0; level--)
if (ms->capture[level].len == CAP_UNFINISHED) return level;
return luaL_error(ms->L, "invalid pattern capture");
}
static const char *classend (MatchState *ms, const char *p) {
switch (*p++) {
case L_ESC: {
if (*p == '\0')
luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")");
return p+1;
}
case '[': {
if (*p == '^') p++;
do { /* look for a `]' */
if (*p == '\0')
luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")");
if (*(p++) == L_ESC && *p != '\0')
p++; /* skip escapes (e.g. `%]') */
} while (*p != ']');
return p+1;
}
default: {
return p;
}
}
}
static int match_class (int c, int cl) {
int res;
switch (tolower(cl)) {
case 'a' : res = isalpha(c); break;
case 'c' : res = iscntrl(c); break;
case 'd' : res = isdigit(c); break;
case 'l' : res = islower(c); break;
case 'p' : res = ispunct(c); break;
case 's' : res = isspace(c); break;
case 'u' : res = isupper(c); break;
case 'w' : res = isalnum(c); break;
case 'x' : res = isxdigit(c); break;
case 'z' : res = (c == 0); break;
default: return (cl == c);
}
return (islower(cl) ? res : !res);
}
static int matchbracketclass (int c, const char *p, const char *ec) {
int sig = 1;
if (*(p+1) == '^') {
sig = 0;
p++; /* skip the `^' */
}
while (++p < ec) {
if (*p == L_ESC) {
p++;
if (match_class(c, uchar(*p)))
return sig;
}
else if ((*(p+1) == '-') && (p+2 < ec)) {
p+=2;
if (uchar(*(p-2)) <= c && c <= uchar(*p))
return sig;
}
else if (uchar(*p) == c) return sig;
}
return !sig;
}
static int singlematch (int c, const char *p, const char *ep) {
switch (*p) {
case '.': return 1; /* matches any char */
case L_ESC: return match_class(c, uchar(*(p+1)));
case '[': return matchbracketclass(c, p, ep-1);
default: return (uchar(*p) == c);
}
}
static const char *match (MatchState *ms, const char *s, const char *p);
static const char *matchbalance (MatchState *ms, const char *s,
const char *p) {
if (*p == 0 || *(p+1) == 0)
luaL_error(ms->L, "unbalanced pattern");
if (*s != *p) return NULL;
else {
int b = *p;
int e = *(p+1);
int cont = 1;
while (++s < ms->src_end) {
if (*s == e) {
if (--cont == 0) return s+1;
}
else if (*s == b) cont++;
}
}
return NULL; /* string ends out of balance */
}
static const char *max_expand (MatchState *ms, const char *s,
const char *p, const char *ep) {
ptrdiff_t i = 0; /* counts maximum expand for item */
while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))
i++;
/* keeps trying to match with the maximum repetitions */
while (i>=0) {
const char *res = match(ms, (s+i), ep+1);
if (res) return res;
i--; /* else didn't match; reduce 1 repetition to try again */
}
return NULL;
}
static const char *min_expand (MatchState *ms, const char *s,
const char *p, const char *ep) {
for (;;) {
const char *res = match(ms, s, ep+1);
if (res != NULL)
return res;
else if (s<ms->src_end && singlematch(uchar(*s), p, ep))
s++; /* try with one more repetition */
else return NULL;
}
}
static const char *start_capture (MatchState *ms, const char *s,
const char *p, int what) {
const char *res;
int level = ms->level;
if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures");
ms->capture[level].init = s;
ms->capture[level].len = what;
ms->level = level+1;
if ((res=match(ms, s, p)) == NULL) /* match failed? */
ms->level--; /* undo capture */
return res;
}
static const char *end_capture (MatchState *ms, const char *s,
const char *p) {
int l = capture_to_close(ms);
const char *res;
ms->capture[l].len = s - ms->capture[l].init; /* close capture */
if ((res = match(ms, s, p)) == NULL) /* match failed? */
ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
return res;
}
static const char *match_capture (MatchState *ms, const char *s, int l) {
size_t len;
l = check_capture(ms, l);
len = ms->capture[l].len;
if ((size_t)(ms->src_end-s) >= len &&
memcmp(ms->capture[l].init, s, len) == 0)
return s+len;
else return NULL;
}
static const char *match (MatchState *ms, const char *s, const char *p) {
init: /* using goto's to optimize tail recursion */
switch (*p) {
case '(': { /* start capture */
if (*(p+1) == ')') /* position capture? */
return start_capture(ms, s, p+2, CAP_POSITION);
else
return start_capture(ms, s, p+1, CAP_UNFINISHED);
}
case ')': { /* end capture */
return end_capture(ms, s, p+1);
}
case L_ESC: {
switch (*(p+1)) {
case 'b': { /* balanced string? */
s = matchbalance(ms, s, p+2);
if (s == NULL) return NULL;
p+=4; goto init; /* else return match(ms, s, p+4); */
}
case 'f': { /* frontier? */
const char *ep; char previous;
p += 2;
if (*p != '[')
luaL_error(ms->L, "missing " LUA_QL("[") " after "
LUA_QL("%%f") " in pattern");
ep = classend(ms, p); /* points to what is next */
previous = (s == ms->src_init) ? '\0' : *(s-1);
if (matchbracketclass(uchar(previous), p, ep-1) ||
!matchbracketclass(uchar(*s), p, ep-1)) return NULL;
p=ep; goto init; /* else return match(ms, s, ep); */
}
default: {
if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */
s = match_capture(ms, s, uchar(*(p+1)));
if (s == NULL) return NULL;
p+=2; goto init; /* else return match(ms, s, p+2) */
}
goto dflt; /* case default */
}
}
}
case '\0': { /* end of pattern */
return s; /* match succeeded */
}
case '$': {
if (*(p+1) == '\0') /* is the `$' the last char in pattern? */
return (s == ms->src_end) ? s : NULL; /* check end of string */
else goto dflt;
}
default: dflt: { /* it is a pattern item */
const char *ep = classend(ms, p); /* points to what is next */
int m = s<ms->src_end && singlematch(uchar(*s), p, ep);
switch (*ep) {
case '?': { /* optional */
const char *res;
if (m && ((res=match(ms, s+1, ep+1)) != NULL))
return res;
p=ep+1; goto init; /* else return match(ms, s, ep+1); */
}
case '*': { /* 0 or more repetitions */
return max_expand(ms, s, p, ep);
}
case '+': { /* 1 or more repetitions */
return (m ? max_expand(ms, s+1, p, ep) : NULL);
}
case '-': { /* 0 or more repetitions (minimum) */
return min_expand(ms, s, p, ep);
}
default: {
if (!m) return NULL;
s++; p=ep; goto init; /* else return match(ms, s+1, ep); */
}
}
}
}
}
static const char *lmemfind (const char *s1, size_t l1,
const char *s2, size_t l2) {
if (l2 == 0) return s1; /* empty strings are everywhere */
else if (l2 > l1) return NULL; /* avoids a negative `l1' */
else {
const char *init; /* to search for a `*s2' inside `s1' */
l2--; /* 1st char will be checked by `memchr' */
l1 = l1-l2; /* `s2' cannot be found after that */
while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
init++; /* 1st char is already checked */
if (memcmp(init, s2+1, l2) == 0)
return init-1;
else { /* correct `l1' and `s1' to try again */
l1 -= init-s1;
s1 = init;
}
}
return NULL; /* not found */
}
}
static void push_onecapture (MatchState *ms, int i, const char *s,
const char *e) {
if (i >= ms->level) {
if (i == 0) /* ms->level == 0, too */
lua_pushlstring(ms->L, s, e - s); /* add whole match */
else
luaL_error(ms->L, "invalid capture index");
}
else {
ptrdiff_t l = ms->capture[i].len;
if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture");
if (l == CAP_POSITION)
lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);
else
lua_pushlstring(ms->L, ms->capture[i].init, l);
}
}
static int push_captures (MatchState *ms, const char *s, const char *e) {
int i;
int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
luaL_checkstack(ms->L, nlevels, "too many captures");
for (i = 0; i < nlevels; i++)
push_onecapture(ms, i, s, e);
return nlevels; /* number of strings pushed */
}
static int str_find_aux (lua_State *L, int find) {
size_t l1, l2;
const char *s = luaL_checklstring(L, 1, &l1);
const char *p = luaL_checklstring(L, 2, &l2);
ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1;
if (init < 0) init = 0;
else if ((size_t)(init) > l1) init = (ptrdiff_t)l1;
if (find && (lua_toboolean(L, 4) || /* explicit request? */
strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */
/* do a plain search */
const char *s2 = lmemfind(s+init, l1-init, p, l2);
if (s2) {
lua_pushinteger(L, s2-s+1);
lua_pushinteger(L, s2-s+l2);
return 2;
}
}
else {
MatchState ms;
int anchor = (*p == '^') ? (p++, 1) : 0;
const char *s1=s+init;
ms.L = L;
ms.src_init = s;
ms.src_end = s+l1;
do {
const char *res;
ms.level = 0;
if ((res=match(&ms, s1, p)) != NULL) {
if (find) {
lua_pushinteger(L, s1-s+1); /* start */
lua_pushinteger(L, res-s); /* end */
return push_captures(&ms, NULL, 0) + 2;
}
else
return push_captures(&ms, s1, res);
}
} while (s1++ < ms.src_end && !anchor);
}
lua_pushnil(L); /* not found */
return 1;
}
static int str_find (lua_State *L) {
return str_find_aux(L, 1);
}
static int str_match (lua_State *L) {
return str_find_aux(L, 0);
}
static int gmatch_aux (lua_State *L) {
MatchState ms;
size_t ls;
const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
const char *p = lua_tostring(L, lua_upvalueindex(2));
const char *src;
ms.L = L;
ms.src_init = s;
ms.src_end = s+ls;
for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3));
src <= ms.src_end;
src++) {
const char *e;
ms.level = 0;
if ((e = match(&ms, src, p)) != NULL) {
lua_Integer newstart = e-s;
if (e == src) newstart++; /* empty match? go at least one position */
lua_pushinteger(L, newstart);
lua_replace(L, lua_upvalueindex(3));
return push_captures(&ms, src, e);
}
}
return 0; /* not found */
}
static int gmatch (lua_State *L) {
luaL_checkstring(L, 1);
luaL_checkstring(L, 2);
lua_settop(L, 2);
lua_pushinteger(L, 0);
lua_pushcclosure(L, gmatch_aux, 3);
return 1;
}
static int gfind_nodef (lua_State *L) {
return luaL_error(L, LUA_QL("string.gfind") " was renamed to "
LUA_QL("string.gmatch"));
}
static void add_s (MatchState *ms, luaL_Buffer *b, const char *s,
const char *e) {
size_t l, i;
const char *news = lua_tolstring(ms->L, 3, &l);
for (i = 0; i < l; i++) {
if (news[i] != L_ESC)
luaL_addchar(b, news[i]);
else {
i++; /* skip ESC */
if (!isdigit(uchar(news[i])))
luaL_addchar(b, news[i]);
else if (news[i] == '0')
luaL_addlstring(b, s, e - s);
else {
push_onecapture(ms, news[i] - '1', s, e);
luaL_addvalue(b); /* add capture to accumulated result */
}
}
}
}
static void add_value (MatchState *ms, luaL_Buffer *b, const char *s,
const char *e) {
lua_State *L = ms->L;
switch (lua_type(L, 3)) {
case LUA_TNUMBER:
case LUA_TSTRING: {
add_s(ms, b, s, e);
return;
}
case LUA_TFUNCTION: {
int n;
lua_pushvalue(L, 3);
n = push_captures(ms, s, e);
lua_call(L, n, 1);
break;
}
case LUA_TTABLE: {
push_onecapture(ms, 0, s, e);
lua_gettable(L, 3);
break;
}
}
if (!lua_toboolean(L, -1)) { /* nil or false? */
lua_pop(L, 1);
lua_pushlstring(L, s, e - s); /* keep original text */
}
else if (!lua_isstring(L, -1))
luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1));
luaL_addvalue(b); /* add result to accumulator */
}
static int str_gsub (lua_State *L) {
size_t srcl;
const char *src = luaL_checklstring(L, 1, &srcl);
const char *p = luaL_checkstring(L, 2);
int tr = lua_type(L, 3);
int max_s = luaL_optint(L, 4, srcl+1);
int anchor = (*p == '^') ? (p++, 1) : 0;
int n = 0;
MatchState ms;
luaL_Buffer b;
luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,
"string/function/table expected");
luaL_buffinit(L, &b);
ms.L = L;
ms.src_init = src;
ms.src_end = src+srcl;
while (n < max_s) {
const char *e;
ms.level = 0;
e = match(&ms, src, p);
if (e) {
n++;
add_value(&ms, &b, src, e);
}
if (e && e>src) /* non empty match? */
src = e; /* skip it */
else if (src < ms.src_end)
luaL_addchar(&b, *src++);
else break;
if (anchor) break;
}
luaL_addlstring(&b, src, ms.src_end-src);
luaL_pushresult(&b);
lua_pushinteger(L, n); /* number of substitutions */
return 2;
}
/* }====================================================== */
/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
#define MAX_ITEM 512
/* valid flags in a format specification */
#define FLAGS "-+ #0"
/*
** maximum size of each format specification (such as '%-099.99d')
** (+10 accounts for %99.99x plus margin of error)
*/
#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
static void addquoted (lua_State *L, luaL_Buffer *b, int arg) {
size_t l;
const char *s = luaL_checklstring(L, arg, &l);
luaL_addchar(b, '"');
while (l--) {
switch (*s) {
case '"': case '\\': case '\n': {
luaL_addchar(b, '\\');
luaL_addchar(b, *s);
break;
}
case '\r': {
luaL_addlstring(b, "\\r", 2);
break;
}
case '\0': {
luaL_addlstring(b, "\\000", 4);
break;
}
default: {
luaL_addchar(b, *s);
break;
}
}
s++;
}
luaL_addchar(b, '"');
}
static const char *scanformat (lua_State *L, const char *strfrmt, char *form) {
const char *p = strfrmt;
while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */
if ((size_t)(p - strfrmt) >= sizeof(FLAGS))
luaL_error(L, "invalid format (repeated flags)");
if (isdigit(uchar(*p))) p++; /* skip width */
if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
if (*p == '.') {
p++;
if (isdigit(uchar(*p))) p++; /* skip precision */
if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
}
if (isdigit(uchar(*p)))
luaL_error(L, "invalid format (width or precision too long)");
*(form++) = '%';
strncpy(form, strfrmt, p - strfrmt + 1);
form += p - strfrmt + 1;
*form = '\0';
return p;
}
static void addintlen (char *form) {
size_t l = strlen(form);
char spec = form[l - 1];
strcpy(form + l - 1, LUA_INTFRMLEN);
form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;
form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0';
}
static int str_format (lua_State *L) {
int arg = 1;
size_t sfl;
const char *strfrmt = luaL_checklstring(L, arg, &sfl);
const char *strfrmt_end = strfrmt+sfl;
luaL_Buffer b;
luaL_buffinit(L, &b);
while (strfrmt < strfrmt_end) {
if (*strfrmt != L_ESC)
luaL_addchar(&b, *strfrmt++);
else if (*++strfrmt == L_ESC)
luaL_addchar(&b, *strfrmt++); /* %% */
else { /* format item */
char form[MAX_FORMAT]; /* to store the format (`%...') */
char buff[MAX_ITEM]; /* to store the formatted item */
arg++;
strfrmt = scanformat(L, strfrmt, form);
switch (*strfrmt++) {
case 'c': {
sprintf(buff, form, (int)luaL_checknumber(L, arg));
break;
}
case 'd': case 'i': {
addintlen(form);
sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg));
break;
}
case 'o': case 'u': case 'x': case 'X': {
addintlen(form);
sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg));
break;
}
case 'e': case 'E': case 'f':
case 'g': case 'G': {
sprintf(buff, form, (double)luaL_checknumber(L, arg));
break;
}
case 'q': {
addquoted(L, &b, arg);
continue; /* skip the 'addsize' at the end */
}
case 's': {
size_t l;
const char *s = luaL_checklstring(L, arg, &l);
if (!strchr(form, '.') && l >= 100) {
/* no precision and string is too long to be formatted;
keep original string */
lua_pushvalue(L, arg);
luaL_addvalue(&b);
continue; /* skip the `addsize' at the end */
}
else {
sprintf(buff, form, s);
break;
}
}
default: { /* also treat cases `pnLlh' */
return luaL_error(L, "invalid option " LUA_QL("%%%c") " to "
LUA_QL("format"), *(strfrmt - 1));
}
}
luaL_addlstring(&b, buff, strlen(buff));
}
}
luaL_pushresult(&b);
return 1;
}
static const luaL_Reg strlib[] = {
{"byte", str_byte},
{"char", str_char},
{"dump", str_dump},
{"find", str_find},
{"format", str_format},
{"gfind", gfind_nodef},
{"gmatch", gmatch},
{"gsub", str_gsub},
{"len", str_len},
{"lower", str_lower},
{"match", str_match},
{"rep", str_rep},
{"reverse", str_reverse},
{"sub", str_sub},
{"upper", str_upper},
{NULL, NULL}
};
static void createmetatable (lua_State *L) {
lua_createtable(L, 0, 1); /* create metatable for strings */
lua_pushliteral(L, ""); /* dummy string */
lua_pushvalue(L, -2);
lua_setmetatable(L, -2); /* set string metatable */
lua_pop(L, 1); /* pop dummy string */
lua_pushvalue(L, -2); /* string library... */
lua_setfield(L, -2, "__index"); /* ...is the __index metamethod */
lua_pop(L, 1); /* pop metatable */
}
/*
** Open string library
*/
LUALIB_API int luaopen_string (lua_State *L) {
luaL_register(L, LUA_STRLIBNAME, strlib);
#if defined(LUA_COMPAT_GFIND)
lua_getfield(L, -1, "gmatch");
lua_setfield(L, -2, "gfind");
#endif
createmetatable(L);
return 1;
}
/*
** $Id: lstrlib.c,v 1.132.1.5 2010/05/14 15:34:19 roberto Exp $
** Standard library for string operations and pattern-matching
** See Copyright Notice in lua.h
*/
#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define lstrlib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
/* macro to `unsign' a character */
#define uchar(c) ((unsigned char)(c))
static int str_len (lua_State *L) {
size_t l;
luaL_checklstring(L, 1, &l);
lua_pushinteger(L, l);
return 1;
}
static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) {
/* relative string position: negative means back from end */
if (pos < 0) pos += (ptrdiff_t)len + 1;
return (pos >= 0) ? pos : 0;
}
static int str_sub (lua_State *L) {
size_t l;
const char *s = luaL_checklstring(L, 1, &l);
ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l);
ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l);
if (start < 1) start = 1;
if (end > (ptrdiff_t)l) end = (ptrdiff_t)l;
if (start <= end)
lua_pushlstring(L, s+start-1, end-start+1);
else lua_pushliteral(L, "");
return 1;
}
static int str_reverse (lua_State *L) {
size_t l;
luaL_Buffer b;
const char *s = luaL_checklstring(L, 1, &l);
luaL_buffinit(L, &b);
while (l--) luaL_addchar(&b, s[l]);
luaL_pushresult(&b);
return 1;
}
static int str_lower (lua_State *L) {
size_t l;
size_t i;
luaL_Buffer b;
const char *s = luaL_checklstring(L, 1, &l);
luaL_buffinit(L, &b);
for (i=0; i<l; i++)
luaL_addchar(&b, tolower(uchar(s[i])));
luaL_pushresult(&b);
return 1;
}
static int str_upper (lua_State *L) {
size_t l;
size_t i;
luaL_Buffer b;
const char *s = luaL_checklstring(L, 1, &l);
luaL_buffinit(L, &b);
for (i=0; i<l; i++)
luaL_addchar(&b, toupper(uchar(s[i])));
luaL_pushresult(&b);
return 1;
}
static int str_rep (lua_State *L) {
size_t l;
luaL_Buffer b;
const char *s = luaL_checklstring(L, 1, &l);
int n = luaL_checkint(L, 2);
luaL_buffinit(L, &b);
while (n-- > 0)
luaL_addlstring(&b, s, l);
luaL_pushresult(&b);
return 1;
}
static int str_byte (lua_State *L) {
size_t l;
const char *s = luaL_checklstring(L, 1, &l);
ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l);
ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l);
int n, i;
if (posi <= 0) posi = 1;
if ((size_t)pose > l) pose = l;
if (posi > pose) return 0; /* empty interval; return no values */
n = (int)(pose - posi + 1);
if (posi + n <= pose) /* overflow? */
luaL_error(L, "string slice too long");
luaL_checkstack(L, n, "string slice too long");
for (i=0; i<n; i++)
lua_pushinteger(L, uchar(s[posi+i-1]));
return n;
}
static int str_char (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
int i;
luaL_Buffer b;
luaL_buffinit(L, &b);
for (i=1; i<=n; i++) {
int c = luaL_checkint(L, i);
luaL_argcheck(L, uchar(c) == c, i, "invalid value");
luaL_addchar(&b, uchar(c));
}
luaL_pushresult(&b);
return 1;
}
static int writer (lua_State *L, const void* b, size_t size, void* B) {
(void)L;
luaL_addlstring((luaL_Buffer*) B, (const char *)b, size);
return 0;
}
static int str_dump (lua_State *L) {
luaL_Buffer b;
luaL_checktype(L, 1, LUA_TFUNCTION);
lua_settop(L, 1);
luaL_buffinit(L,&b);
if (lua_dump(L, writer, &b) != 0)
luaL_error(L, "unable to dump given function");
luaL_pushresult(&b);
return 1;
}
/*
** {======================================================
** PATTERN MATCHING
** =======================================================
*/
#define CAP_UNFINISHED (-1)
#define CAP_POSITION (-2)
typedef struct MatchState {
const char *src_init; /* init of source string */
const char *src_end; /* end (`\0') of source string */
lua_State *L;
int level; /* total number of captures (finished or unfinished) */
struct {
const char *init;
ptrdiff_t len;
} capture[LUA_MAXCAPTURES];
} MatchState;
#define L_ESC '%'
#define SPECIALS "^$*+?.([%-"
static int check_capture (MatchState *ms, int l) {
l -= '1';
if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
return luaL_error(ms->L, "invalid capture index");
return l;
}
static int capture_to_close (MatchState *ms) {
int level = ms->level;
for (level--; level>=0; level--)
if (ms->capture[level].len == CAP_UNFINISHED) return level;
return luaL_error(ms->L, "invalid pattern capture");
}
static const char *classend (MatchState *ms, const char *p) {
switch (*p++) {
case L_ESC: {
if (*p == '\0')
luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")");
return p+1;
}
case '[': {
if (*p == '^') p++;
do { /* look for a `]' */
if (*p == '\0')
luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")");
if (*(p++) == L_ESC && *p != '\0')
p++; /* skip escapes (e.g. `%]') */
} while (*p != ']');
return p+1;
}
default: {
return p;
}
}
}
static int match_class (int c, int cl) {
int res;
switch (tolower(cl)) {
case 'a' : res = isalpha(c); break;
case 'c' : res = iscntrl(c); break;
case 'd' : res = isdigit(c); break;
case 'l' : res = islower(c); break;
case 'p' : res = ispunct(c); break;
case 's' : res = isspace(c); break;
case 'u' : res = isupper(c); break;
case 'w' : res = isalnum(c); break;
case 'x' : res = isxdigit(c); break;
case 'z' : res = (c == 0); break;
default: return (cl == c);
}
return (islower(cl) ? res : !res);
}
static int matchbracketclass (int c, const char *p, const char *ec) {
int sig = 1;
if (*(p+1) == '^') {
sig = 0;
p++; /* skip the `^' */
}
while (++p < ec) {
if (*p == L_ESC) {
p++;
if (match_class(c, uchar(*p)))
return sig;
}
else if ((*(p+1) == '-') && (p+2 < ec)) {
p+=2;
if (uchar(*(p-2)) <= c && c <= uchar(*p))
return sig;
}
else if (uchar(*p) == c) return sig;
}
return !sig;
}
static int singlematch (int c, const char *p, const char *ep) {
switch (*p) {
case '.': return 1; /* matches any char */
case L_ESC: return match_class(c, uchar(*(p+1)));
case '[': return matchbracketclass(c, p, ep-1);
default: return (uchar(*p) == c);
}
}
static const char *match (MatchState *ms, const char *s, const char *p);
static const char *matchbalance (MatchState *ms, const char *s,
const char *p) {
if (*p == 0 || *(p+1) == 0)
luaL_error(ms->L, "unbalanced pattern");
if (*s != *p) return NULL;
else {
int b = *p;
int e = *(p+1);
int cont = 1;
while (++s < ms->src_end) {
if (*s == e) {
if (--cont == 0) return s+1;
}
else if (*s == b) cont++;
}
}
return NULL; /* string ends out of balance */
}
static const char *max_expand (MatchState *ms, const char *s,
const char *p, const char *ep) {
ptrdiff_t i = 0; /* counts maximum expand for item */
while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))
i++;
/* keeps trying to match with the maximum repetitions */
while (i>=0) {
const char *res = match(ms, (s+i), ep+1);
if (res) return res;
i--; /* else didn't match; reduce 1 repetition to try again */
}
return NULL;
}
static const char *min_expand (MatchState *ms, const char *s,
const char *p, const char *ep) {
for (;;) {
const char *res = match(ms, s, ep+1);
if (res != NULL)
return res;
else if (s<ms->src_end && singlematch(uchar(*s), p, ep))
s++; /* try with one more repetition */
else return NULL;
}
}
static const char *start_capture (MatchState *ms, const char *s,
const char *p, int what) {
const char *res;
int level = ms->level;
if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures");
ms->capture[level].init = s;
ms->capture[level].len = what;
ms->level = level+1;
if ((res=match(ms, s, p)) == NULL) /* match failed? */
ms->level--; /* undo capture */
return res;
}
static const char *end_capture (MatchState *ms, const char *s,
const char *p) {
int l = capture_to_close(ms);
const char *res;
ms->capture[l].len = s - ms->capture[l].init; /* close capture */
if ((res = match(ms, s, p)) == NULL) /* match failed? */
ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
return res;
}
static const char *match_capture (MatchState *ms, const char *s, int l) {
size_t len;
l = check_capture(ms, l);
len = ms->capture[l].len;
if ((size_t)(ms->src_end-s) >= len &&
memcmp(ms->capture[l].init, s, len) == 0)
return s+len;
else return NULL;
}
static const char *match (MatchState *ms, const char *s, const char *p) {
init: /* using goto's to optimize tail recursion */
switch (*p) {
case '(': { /* start capture */
if (*(p+1) == ')') /* position capture? */
return start_capture(ms, s, p+2, CAP_POSITION);
else
return start_capture(ms, s, p+1, CAP_UNFINISHED);
}
case ')': { /* end capture */
return end_capture(ms, s, p+1);
}
case L_ESC: {
switch (*(p+1)) {
case 'b': { /* balanced string? */
s = matchbalance(ms, s, p+2);
if (s == NULL) return NULL;
p+=4; goto init; /* else return match(ms, s, p+4); */
}
case 'f': { /* frontier? */
const char *ep; char previous;
p += 2;
if (*p != '[')
luaL_error(ms->L, "missing " LUA_QL("[") " after "
LUA_QL("%%f") " in pattern");
ep = classend(ms, p); /* points to what is next */
previous = (s == ms->src_init) ? '\0' : *(s-1);
if (matchbracketclass(uchar(previous), p, ep-1) ||
!matchbracketclass(uchar(*s), p, ep-1)) return NULL;
p=ep; goto init; /* else return match(ms, s, ep); */
}
default: {
if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */
s = match_capture(ms, s, uchar(*(p+1)));
if (s == NULL) return NULL;
p+=2; goto init; /* else return match(ms, s, p+2) */
}
goto dflt; /* case default */
}
}
}
case '\0': { /* end of pattern */
return s; /* match succeeded */
}
case '$': {
if (*(p+1) == '\0') /* is the `$' the last char in pattern? */
return (s == ms->src_end) ? s : NULL; /* check end of string */
else goto dflt;
}
default: dflt: { /* it is a pattern item */
const char *ep = classend(ms, p); /* points to what is next */
int m = s<ms->src_end && singlematch(uchar(*s), p, ep);
switch (*ep) {
case '?': { /* optional */
const char *res;
if (m && ((res=match(ms, s+1, ep+1)) != NULL))
return res;
p=ep+1; goto init; /* else return match(ms, s, ep+1); */
}
case '*': { /* 0 or more repetitions */
return max_expand(ms, s, p, ep);
}
case '+': { /* 1 or more repetitions */
return (m ? max_expand(ms, s+1, p, ep) : NULL);
}
case '-': { /* 0 or more repetitions (minimum) */
return min_expand(ms, s, p, ep);
}
default: {
if (!m) return NULL;
s++; p=ep; goto init; /* else return match(ms, s+1, ep); */
}
}
}
}
}
static const char *lmemfind (const char *s1, size_t l1,
const char *s2, size_t l2) {
if (l2 == 0) return s1; /* empty strings are everywhere */
else if (l2 > l1) return NULL; /* avoids a negative `l1' */
else {
const char *init; /* to search for a `*s2' inside `s1' */
l2--; /* 1st char will be checked by `memchr' */
l1 = l1-l2; /* `s2' cannot be found after that */
while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
init++; /* 1st char is already checked */
if (memcmp(init, s2+1, l2) == 0)
return init-1;
else { /* correct `l1' and `s1' to try again */
l1 -= init-s1;
s1 = init;
}
}
return NULL; /* not found */
}
}
static void push_onecapture (MatchState *ms, int i, const char *s,
const char *e) {
if (i >= ms->level) {
if (i == 0) /* ms->level == 0, too */
lua_pushlstring(ms->L, s, e - s); /* add whole match */
else
luaL_error(ms->L, "invalid capture index");
}
else {
ptrdiff_t l = ms->capture[i].len;
if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture");
if (l == CAP_POSITION)
lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);
else
lua_pushlstring(ms->L, ms->capture[i].init, l);
}
}
static int push_captures (MatchState *ms, const char *s, const char *e) {
int i;
int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
luaL_checkstack(ms->L, nlevels, "too many captures");
for (i = 0; i < nlevels; i++)
push_onecapture(ms, i, s, e);
return nlevels; /* number of strings pushed */
}
static int str_find_aux (lua_State *L, int find) {
size_t l1, l2;
const char *s = luaL_checklstring(L, 1, &l1);
const char *p = luaL_checklstring(L, 2, &l2);
ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1;
if (init < 0) init = 0;
else if ((size_t)(init) > l1) init = (ptrdiff_t)l1;
if (find && (lua_toboolean(L, 4) || /* explicit request? */
strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */
/* do a plain search */
const char *s2 = lmemfind(s+init, l1-init, p, l2);
if (s2) {
lua_pushinteger(L, s2-s+1);
lua_pushinteger(L, s2-s+l2);
return 2;
}
}
else {
MatchState ms;
int anchor = (*p == '^') ? (p++, 1) : 0;
const char *s1=s+init;
ms.L = L;
ms.src_init = s;
ms.src_end = s+l1;
do {
const char *res;
ms.level = 0;
if ((res=match(&ms, s1, p)) != NULL) {
if (find) {
lua_pushinteger(L, s1-s+1); /* start */
lua_pushinteger(L, res-s); /* end */
return push_captures(&ms, NULL, 0) + 2;
}
else
return push_captures(&ms, s1, res);
}
} while (s1++ < ms.src_end && !anchor);
}
lua_pushnil(L); /* not found */
return 1;
}
static int str_find (lua_State *L) {
return str_find_aux(L, 1);
}
static int str_match (lua_State *L) {
return str_find_aux(L, 0);
}
static int gmatch_aux (lua_State *L) {
MatchState ms;
size_t ls;
const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
const char *p = lua_tostring(L, lua_upvalueindex(2));
const char *src;
ms.L = L;
ms.src_init = s;
ms.src_end = s+ls;
for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3));
src <= ms.src_end;
src++) {
const char *e;
ms.level = 0;
if ((e = match(&ms, src, p)) != NULL) {
lua_Integer newstart = e-s;
if (e == src) newstart++; /* empty match? go at least one position */
lua_pushinteger(L, newstart);
lua_replace(L, lua_upvalueindex(3));
return push_captures(&ms, src, e);
}
}
return 0; /* not found */
}
static int gmatch (lua_State *L) {
luaL_checkstring(L, 1);
luaL_checkstring(L, 2);
lua_settop(L, 2);
lua_pushinteger(L, 0);
lua_pushcclosure(L, gmatch_aux, 3);
return 1;
}
static int gfind_nodef (lua_State *L) {
return luaL_error(L, LUA_QL("string.gfind") " was renamed to "
LUA_QL("string.gmatch"));
}
static void add_s (MatchState *ms, luaL_Buffer *b, const char *s,
const char *e) {
size_t l, i;
const char *news = lua_tolstring(ms->L, 3, &l);
for (i = 0; i < l; i++) {
if (news[i] != L_ESC)
luaL_addchar(b, news[i]);
else {
i++; /* skip ESC */
if (!isdigit(uchar(news[i])))
luaL_addchar(b, news[i]);
else if (news[i] == '0')
luaL_addlstring(b, s, e - s);
else {
push_onecapture(ms, news[i] - '1', s, e);
luaL_addvalue(b); /* add capture to accumulated result */
}
}
}
}
static void add_value (MatchState *ms, luaL_Buffer *b, const char *s,
const char *e) {
lua_State *L = ms->L;
switch (lua_type(L, 3)) {
case LUA_TNUMBER:
case LUA_TSTRING: {
add_s(ms, b, s, e);
return;
}
case LUA_TFUNCTION: {
int n;
lua_pushvalue(L, 3);
n = push_captures(ms, s, e);
lua_call(L, n, 1);
break;
}
case LUA_TTABLE: {
push_onecapture(ms, 0, s, e);
lua_gettable(L, 3);
break;
}
}
if (!lua_toboolean(L, -1)) { /* nil or false? */
lua_pop(L, 1);
lua_pushlstring(L, s, e - s); /* keep original text */
}
else if (!lua_isstring(L, -1))
luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1));
luaL_addvalue(b); /* add result to accumulator */
}
static int str_gsub (lua_State *L) {
size_t srcl;
const char *src = luaL_checklstring(L, 1, &srcl);
const char *p = luaL_checkstring(L, 2);
int tr = lua_type(L, 3);
int max_s = luaL_optint(L, 4, srcl+1);
int anchor = (*p == '^') ? (p++, 1) : 0;
int n = 0;
MatchState ms;
luaL_Buffer b;
luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,
"string/function/table expected");
luaL_buffinit(L, &b);
ms.L = L;
ms.src_init = src;
ms.src_end = src+srcl;
while (n < max_s) {
const char *e;
ms.level = 0;
e = match(&ms, src, p);
if (e) {
n++;
add_value(&ms, &b, src, e);
}
if (e && e>src) /* non empty match? */
src = e; /* skip it */
else if (src < ms.src_end)
luaL_addchar(&b, *src++);
else break;
if (anchor) break;
}
luaL_addlstring(&b, src, ms.src_end-src);
luaL_pushresult(&b);
lua_pushinteger(L, n); /* number of substitutions */
return 2;
}
/* }====================================================== */
/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
#define MAX_ITEM 512
/* valid flags in a format specification */
#define FLAGS "-+ #0"
/*
** maximum size of each format specification (such as '%-099.99d')
** (+10 accounts for %99.99x plus margin of error)
*/
#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
static void addquoted (lua_State *L, luaL_Buffer *b, int arg) {
size_t l;
const char *s = luaL_checklstring(L, arg, &l);
luaL_addchar(b, '"');
while (l--) {
switch (*s) {
case '"': case '\\': case '\n': {
luaL_addchar(b, '\\');
luaL_addchar(b, *s);
break;
}
case '\r': {
luaL_addlstring(b, "\\r", 2);
break;
}
case '\0': {
luaL_addlstring(b, "\\000", 4);
break;
}
default: {
luaL_addchar(b, *s);
break;
}
}
s++;
}
luaL_addchar(b, '"');
}
static const char *scanformat (lua_State *L, const char *strfrmt, char *form) {
const char *p = strfrmt;
while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */
if ((size_t)(p - strfrmt) >= sizeof(FLAGS))
luaL_error(L, "invalid format (repeated flags)");
if (isdigit(uchar(*p))) p++; /* skip width */
if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
if (*p == '.') {
p++;
if (isdigit(uchar(*p))) p++; /* skip precision */
if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
}
if (isdigit(uchar(*p)))
luaL_error(L, "invalid format (width or precision too long)");
*(form++) = '%';
strncpy(form, strfrmt, p - strfrmt + 1);
form += p - strfrmt + 1;
*form = '\0';
return p;
}
static void addintlen (char *form) {
size_t l = strlen(form);
char spec = form[l - 1];
strcpy(form + l - 1, LUA_INTFRMLEN);
form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;
form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0';
}
static int str_format (lua_State *L) {
int top = lua_gettop(L);
int arg = 1;
size_t sfl;
const char *strfrmt = luaL_checklstring(L, arg, &sfl);
const char *strfrmt_end = strfrmt+sfl;
luaL_Buffer b;
luaL_buffinit(L, &b);
while (strfrmt < strfrmt_end) {
if (*strfrmt != L_ESC)
luaL_addchar(&b, *strfrmt++);
else if (*++strfrmt == L_ESC)
luaL_addchar(&b, *strfrmt++); /* %% */
else { /* format item */
char form[MAX_FORMAT]; /* to store the format (`%...') */
char buff[MAX_ITEM]; /* to store the formatted item */
if (++arg > top)
luaL_argerror(L, arg, "no value");
strfrmt = scanformat(L, strfrmt, form);
switch (*strfrmt++) {
case 'c': {
sprintf(buff, form, (int)luaL_checknumber(L, arg));
break;
}
case 'd': case 'i': {
addintlen(form);
sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg));
break;
}
case 'o': case 'u': case 'x': case 'X': {
addintlen(form);
sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg));
break;
}
case 'e': case 'E': case 'f':
case 'g': case 'G': {
sprintf(buff, form, (double)luaL_checknumber(L, arg));
break;
}
case 'q': {
addquoted(L, &b, arg);
continue; /* skip the 'addsize' at the end */
}
case 's': {
size_t l;
const char *s = luaL_checklstring(L, arg, &l);
if (!strchr(form, '.') && l >= 100) {
/* no precision and string is too long to be formatted;
keep original string */
lua_pushvalue(L, arg);
luaL_addvalue(&b);
continue; /* skip the `addsize' at the end */
}
else {
sprintf(buff, form, s);
break;
}
}
default: { /* also treat cases `pnLlh' */
return luaL_error(L, "invalid option " LUA_QL("%%%c") " to "
LUA_QL("format"), *(strfrmt - 1));
}
}
luaL_addlstring(&b, buff, strlen(buff));
}
}
luaL_pushresult(&b);
return 1;
}
static const luaL_Reg strlib[] = {
{"byte", str_byte},
{"char", str_char},
{"dump", str_dump},
{"find", str_find},
{"format", str_format},
{"gfind", gfind_nodef},
{"gmatch", gmatch},
{"gsub", str_gsub},
{"len", str_len},
{"lower", str_lower},
{"match", str_match},
{"rep", str_rep},
{"reverse", str_reverse},
{"sub", str_sub},
{"upper", str_upper},
{NULL, NULL}
};
static void createmetatable (lua_State *L) {
lua_createtable(L, 0, 1); /* create metatable for strings */
lua_pushliteral(L, ""); /* dummy string */
lua_pushvalue(L, -2);
lua_setmetatable(L, -2); /* set string metatable */
lua_pop(L, 1); /* pop dummy string */
lua_pushvalue(L, -2); /* string library... */
lua_setfield(L, -2, "__index"); /* ...is the __index metamethod */
lua_pop(L, 1); /* pop metatable */
}
/*
** Open string library
*/
LUALIB_API int luaopen_string (lua_State *L) {
luaL_register(L, LUA_STRLIBNAME, strlib);
#if defined(LUA_COMPAT_GFIND)
lua_getfield(L, -1, "gmatch");
lua_setfield(L, -2, "gfind");
#endif
createmetatable(L);
return 1;
}
/*
** $Id: lvm.c,v 2.63.1.3 2007/12/28 15:32:23 roberto Exp $
** Lua virtual machine
** See Copyright Notice in lua.h
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define lvm_c
#define LUA_CORE
#include "lua.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lgc.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lvm.h"
/* limit for table tag-method chains (to avoid loops) */
#define MAXTAGLOOP 100
const TValue *luaV_tonumber (const TValue *obj, TValue *n) {
lua_Number num;
if (ttisnumber(obj)) return obj;
if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) {
setnvalue(n, num);
return n;
}
else
return NULL;
}
int luaV_tostring (lua_State *L, StkId obj) {
if (!ttisnumber(obj))
return 0;
else {
char s[LUAI_MAXNUMBER2STR];
lua_Number n = nvalue(obj);
lua_number2str(s, n);
setsvalue2s(L, obj, luaS_new(L, s));
return 1;
}
}
static void traceexec (lua_State *L, const Instruction *pc) {
lu_byte mask = L->hookmask;
const Instruction *oldpc = L->savedpc;
L->savedpc = pc;
if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) {
resethookcount(L);
luaD_callhook(L, LUA_HOOKCOUNT, -1);
}
if (mask & LUA_MASKLINE) {
Proto *p = ci_func(L->ci)->l.p;
int npc = pcRel(pc, p);
int newline = getline(p, npc);
/* call linehook when enter a new function, when jump back (loop),
or when enter a new line */
if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p)))
luaD_callhook(L, LUA_HOOKLINE, newline);
}
}
static void callTMres (lua_State *L, StkId res, const TValue *f,
const TValue *p1, const TValue *p2) {
ptrdiff_t result = savestack(L, res);
setobj2s(L, L->top, f); /* push function */
setobj2s(L, L->top+1, p1); /* 1st argument */
setobj2s(L, L->top+2, p2); /* 2nd argument */
luaD_checkstack(L, 3);
L->top += 3;
luaD_call(L, L->top - 3, 1);
res = restorestack(L, result);
L->top--;
setobjs2s(L, res, L->top);
}
static void callTM (lua_State *L, const TValue *f, const TValue *p1,
const TValue *p2, const TValue *p3) {
setobj2s(L, L->top, f); /* push function */
setobj2s(L, L->top+1, p1); /* 1st argument */
setobj2s(L, L->top+2, p2); /* 2nd argument */
setobj2s(L, L->top+3, p3); /* 3th argument */
luaD_checkstack(L, 4);
L->top += 4;
luaD_call(L, L->top - 4, 0);
}
void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) {
int loop;
for (loop = 0; loop < MAXTAGLOOP; loop++) {
const TValue *tm;
if (ttistable(t)) { /* `t' is a table? */
Table *h = hvalue(t);
const TValue *res = luaH_get(h, key); /* do a primitive get */
if (!ttisnil(res) || /* result is no nil? */
(tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */
setobj2s(L, val, res);
return;
}
/* else will try the tag method */
}
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))
luaG_typeerror(L, t, "index");
if (ttisfunction(tm)) {
callTMres(L, val, tm, t, key);
return;
}
t = tm; /* else repeat with `tm' */
}
luaG_runerror(L, "loop in gettable");
}
void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
int loop;
for (loop = 0; loop < MAXTAGLOOP; loop++) {
const TValue *tm;
if (ttistable(t)) { /* `t' is a table? */
Table *h = hvalue(t);
TValue *oldval = luaH_set(L, h, key); /* do a primitive set */
if (!ttisnil(oldval) || /* result is no nil? */
(tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
setobj2t(L, oldval, val);
luaC_barriert(L, h, val);
return;
}
/* else will try the tag method */
}
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
luaG_typeerror(L, t, "index");
if (ttisfunction(tm)) {
callTM(L, tm, t, key, val);
return;
}
t = tm; /* else repeat with `tm' */
}
luaG_runerror(L, "loop in settable");
}
static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2,
StkId res, TMS event) {
const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */
if (ttisnil(tm))
tm = luaT_gettmbyobj(L, p2, event); /* try second operand */
if (ttisnil(tm)) return 0;
callTMres(L, res, tm, p1, p2);
return 1;
}
static const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2,
TMS event) {
const TValue *tm1 = fasttm(L, mt1, event);
const TValue *tm2;
if (tm1 == NULL) return NULL; /* no metamethod */
if (mt1 == mt2) return tm1; /* same metatables => same metamethods */
tm2 = fasttm(L, mt2, event);
if (tm2 == NULL) return NULL; /* no metamethod */
if (luaO_rawequalObj(tm1, tm2)) /* same metamethods? */
return tm1;
return NULL;
}
static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2,
TMS event) {
const TValue *tm1 = luaT_gettmbyobj(L, p1, event);
const TValue *tm2;
if (ttisnil(tm1)) return -1; /* no metamethod? */
tm2 = luaT_gettmbyobj(L, p2, event);
if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */
return -1;
callTMres(L, L->top, tm1, p1, p2);
return !l_isfalse(L->top);
}
static int l_strcmp (const TString *ls, const TString *rs) {
const char *l = getstr(ls);
size_t ll = ls->tsv.len;
const char *r = getstr(rs);
size_t lr = rs->tsv.len;
for (;;) {
int temp = strcoll(l, r);
if (temp != 0) return temp;
else { /* strings are equal up to a `\0' */
size_t len = strlen(l); /* index of first `\0' in both strings */
if (len == lr) /* r is finished? */
return (len == ll) ? 0 : 1;
else if (len == ll) /* l is finished? */
return -1; /* l is smaller than r (because r is not finished) */
/* both strings longer than `len'; go on comparing (after the `\0') */
len++;
l += len; ll -= len; r += len; lr -= len;
}
}
}
int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
int res;
if (ttype(l) != ttype(r))
return luaG_ordererror(L, l, r);
else if (ttisnumber(l))
return luai_numlt(nvalue(l), nvalue(r));
else if (ttisstring(l))
return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0;
else if ((res = call_orderTM(L, l, r, TM_LT)) != -1)
return res;
return luaG_ordererror(L, l, r);
}
static int lessequal (lua_State *L, const TValue *l, const TValue *r) {
int res;
if (ttype(l) != ttype(r))
return luaG_ordererror(L, l, r);
else if (ttisnumber(l))
return luai_numle(nvalue(l), nvalue(r));
else if (ttisstring(l))
return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0;
else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) /* first try `le' */
return res;
else if ((res = call_orderTM(L, r, l, TM_LT)) != -1) /* else try `lt' */
return !res;
return luaG_ordererror(L, l, r);
}
int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) {
const TValue *tm;
lua_assert(ttype(t1) == ttype(t2));
switch (ttype(t1)) {
case LUA_TNIL: return 1;
case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2));
case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */
case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
case LUA_TUSERDATA: {
if (uvalue(t1) == uvalue(t2)) return 1;
tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable,
TM_EQ);
break; /* will try TM */
}
case LUA_TTABLE: {
if (hvalue(t1) == hvalue(t2)) return 1;
tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ);
break; /* will try TM */
}
default: return gcvalue(t1) == gcvalue(t2);
}
if (tm == NULL) return 0; /* no TM? */
callTMres(L, L->top, tm, t1, t2); /* call TM */
return !l_isfalse(L->top);
}
void luaV_concat (lua_State *L, int total, int last) {
do {
StkId top = L->base + last + 1;
int n = 2; /* number of elements handled in this pass (at least 2) */
if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) {
if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT))
luaG_concaterror(L, top-2, top-1);
} else if (tsvalue(top-1)->len == 0) /* second op is empty? */
(void)tostring(L, top - 2); /* result is first op (as string) */
else {
/* at least two string values; get as many as possible */
size_t tl = tsvalue(top-1)->len;
char *buffer;
int i;
/* collect total length */
for (n = 1; n < total && tostring(L, top-n-1); n++) {
size_t l = tsvalue(top-n-1)->len;
if (l >= MAX_SIZET - tl) luaG_runerror(L, "string length overflow");
tl += l;
}
buffer = luaZ_openspace(L, &G(L)->buff, tl);
tl = 0;
for (i=n; i>0; i--) { /* concat all strings */
size_t l = tsvalue(top-i)->len;
memcpy(buffer+tl, svalue(top-i), l);
tl += l;
}
setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl));
}
total -= n-1; /* got `n' strings to create 1 new */
last -= n-1;
} while (total > 1); /* repeat until only 1 result left */
}
static void Arith (lua_State *L, StkId ra, const TValue *rb,
const TValue *rc, TMS op) {
TValue tempb, tempc;
const TValue *b, *c;
if ((b = luaV_tonumber(rb, &tempb)) != NULL &&
(c = luaV_tonumber(rc, &tempc)) != NULL) {
lua_Number nb = nvalue(b), nc = nvalue(c);
switch (op) {
case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break;
case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break;
case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break;
case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break;
case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break;
case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break;
case TM_UNM: setnvalue(ra, luai_numunm(nb)); break;
default: lua_assert(0); break;
}
}
else if (!call_binTM(L, rb, rc, ra, op))
luaG_aritherror(L, rb, rc);
}
/*
** some macros for common tasks in `luaV_execute'
*/
#define runtime_check(L, c) { if (!(c)) break; }
#define RA(i) (base+GETARG_A(i))
/* to be used after possible stack reallocation */
#define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i))
#define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i))
#define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \
ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i))
#define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \
ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i))
#define KBx(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i))
#define dojump(L,pc,i) {(pc) += (i); luai_threadyield(L);}
#define Protect(x) { L->savedpc = pc; {x;}; base = L->base; }
#define arith_op(op,tm) { \
TValue *rb = RKB(i); \
TValue *rc = RKC(i); \
if (ttisnumber(rb) && ttisnumber(rc)) { \
lua_Number nb = nvalue(rb), nc = nvalue(rc); \
setnvalue(ra, op(nb, nc)); \
} \
else \
Protect(Arith(L, ra, rb, rc, tm)); \
}
void luaV_execute (lua_State *L, int nexeccalls) {
LClosure *cl;
StkId base;
TValue *k;
const Instruction *pc;
reentry: /* entry point */
lua_assert(isLua(L->ci));
pc = L->savedpc;
cl = &clvalue(L->ci->func)->l;
base = L->base;
k = cl->p->k;
/* main loop of interpreter */
for (;;) {
const Instruction i = *pc++;
StkId ra;
if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) &&
(--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) {
traceexec(L, pc);
if (L->status == LUA_YIELD) { /* did hook yield? */
L->savedpc = pc - 1;
return;
}
base = L->base;
}
/* warning!! several calls may realloc the stack and invalidate `ra' */
ra = RA(i);
lua_assert(base == L->base && L->base == L->ci->base);
lua_assert(base <= L->top && L->top <= L->stack + L->stacksize);
lua_assert(L->top == L->ci->top || luaG_checkopenop(i));
switch (GET_OPCODE(i)) {
case OP_MOVE: {
setobjs2s(L, ra, RB(i));
continue;
}
case OP_LOADK: {
setobj2s(L, ra, KBx(i));
continue;
}
case OP_LOADBOOL: {
setbvalue(ra, GETARG_B(i));
if (GETARG_C(i)) pc++; /* skip next instruction (if C) */
continue;
}
case OP_LOADNIL: {
TValue *rb = RB(i);
do {
setnilvalue(rb--);
} while (rb >= ra);
continue;
}
case OP_GETUPVAL: {
int b = GETARG_B(i);
setobj2s(L, ra, cl->upvals[b]->v);
continue;
}
case OP_GETGLOBAL: {
TValue g;
TValue *rb = KBx(i);
sethvalue(L, &g, cl->env);
lua_assert(ttisstring(rb));
Protect(luaV_gettable(L, &g, rb, ra));
continue;
}
case OP_GETTABLE: {
Protect(luaV_gettable(L, RB(i), RKC(i), ra));
continue;
}
case OP_SETGLOBAL: {
TValue g;
sethvalue(L, &g, cl->env);
lua_assert(ttisstring(KBx(i)));
Protect(luaV_settable(L, &g, KBx(i), ra));
continue;
}
case OP_SETUPVAL: {
UpVal *uv = cl->upvals[GETARG_B(i)];
setobj(L, uv->v, ra);
luaC_barrier(L, uv, ra);
continue;
}
case OP_SETTABLE: {
Protect(luaV_settable(L, ra, RKB(i), RKC(i)));
continue;
}
case OP_NEWTABLE: {
int b = GETARG_B(i);
int c = GETARG_C(i);
sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c)));
Protect(luaC_checkGC(L));
continue;
}
case OP_SELF: {
StkId rb = RB(i);
setobjs2s(L, ra+1, rb);
Protect(luaV_gettable(L, rb, RKC(i), ra));
continue;
}
case OP_ADD: {
arith_op(luai_numadd, TM_ADD);
continue;
}
case OP_SUB: {
arith_op(luai_numsub, TM_SUB);
continue;
}
case OP_MUL: {
arith_op(luai_nummul, TM_MUL);
continue;
}
case OP_DIV: {
arith_op(luai_numdiv, TM_DIV);
continue;
}
case OP_MOD: {
arith_op(luai_nummod, TM_MOD);
continue;
}
case OP_POW: {
arith_op(luai_numpow, TM_POW);
continue;
}
case OP_UNM: {
TValue *rb = RB(i);
if (ttisnumber(rb)) {
lua_Number nb = nvalue(rb);
setnvalue(ra, luai_numunm(nb));
}
else {
Protect(Arith(L, ra, rb, rb, TM_UNM));
}
continue;
}
case OP_NOT: {
int res = l_isfalse(RB(i)); /* next assignment may change this value */
setbvalue(ra, res);
continue;
}
case OP_LEN: {
const TValue *rb = RB(i);
switch (ttype(rb)) {
case LUA_TTABLE: {
setnvalue(ra, cast_num(luaH_getn(hvalue(rb))));
break;
}
case LUA_TSTRING: {
setnvalue(ra, cast_num(tsvalue(rb)->len));
break;
}
default: { /* try metamethod */
Protect(
if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN))
luaG_typeerror(L, rb, "get length of");
)
}
}
continue;
}
case OP_CONCAT: {
int b = GETARG_B(i);
int c = GETARG_C(i);
Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L));
setobjs2s(L, RA(i), base+b);
continue;
}
case OP_JMP: {
dojump(L, pc, GETARG_sBx(i));
continue;
}
case OP_EQ: {
TValue *rb = RKB(i);
TValue *rc = RKC(i);
Protect(
if (equalobj(L, rb, rc) == GETARG_A(i))
dojump(L, pc, GETARG_sBx(*pc));
)
pc++;
continue;
}
case OP_LT: {
Protect(
if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i))
dojump(L, pc, GETARG_sBx(*pc));
)
pc++;
continue;
}
case OP_LE: {
Protect(
if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i))
dojump(L, pc, GETARG_sBx(*pc));
)
pc++;
continue;
}
case OP_TEST: {
if (l_isfalse(ra) != GETARG_C(i))
dojump(L, pc, GETARG_sBx(*pc));
pc++;
continue;
}
case OP_TESTSET: {
TValue *rb = RB(i);
if (l_isfalse(rb) != GETARG_C(i)) {
setobjs2s(L, ra, rb);
dojump(L, pc, GETARG_sBx(*pc));
}
pc++;
continue;
}
case OP_CALL: {
int b = GETARG_B(i);
int nresults = GETARG_C(i) - 1;
if (b != 0) L->top = ra+b; /* else previous instruction set top */
L->savedpc = pc;
switch (luaD_precall(L, ra, nresults)) {
case PCRLUA: {
nexeccalls++;
goto reentry; /* restart luaV_execute over new Lua function */
}
case PCRC: {
/* it was a C function (`precall' called it); adjust results */
if (nresults >= 0) L->top = L->ci->top;
base = L->base;
continue;
}
default: {
return; /* yield */
}
}
}
case OP_TAILCALL: {
int b = GETARG_B(i);
if (b != 0) L->top = ra+b; /* else previous instruction set top */
L->savedpc = pc;
lua_assert(GETARG_C(i) - 1 == LUA_MULTRET);
switch (luaD_precall(L, ra, LUA_MULTRET)) {
case PCRLUA: {
/* tail call: put new frame in place of previous one */
CallInfo *ci = L->ci - 1; /* previous frame */
int aux;
StkId func = ci->func;
StkId pfunc = (ci+1)->func; /* previous function index */
if (L->openupval) luaF_close(L, ci->base);
L->base = ci->base = ci->func + ((ci+1)->base - pfunc);
for (aux = 0; pfunc+aux < L->top; aux++) /* move frame down */
setobjs2s(L, func+aux, pfunc+aux);
ci->top = L->top = func+aux; /* correct top */
lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize);
ci->savedpc = L->savedpc;
ci->tailcalls++; /* one more call lost */
L->ci--; /* remove new frame */
goto reentry;
}
case PCRC: { /* it was a C function (`precall' called it) */
base = L->base;
continue;
}
default: {
return; /* yield */
}
}
}
case OP_RETURN: {
int b = GETARG_B(i);
if (b != 0) L->top = ra+b-1;
if (L->openupval) luaF_close(L, base);
L->savedpc = pc;
b = luaD_poscall(L, ra);
if (--nexeccalls == 0) /* was previous function running `here'? */
return; /* no: return */
else { /* yes: continue its execution */
if (b) L->top = L->ci->top;
lua_assert(isLua(L->ci));
lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL);
goto reentry;
}
}
case OP_FORLOOP: {
lua_Number step = nvalue(ra+2);
lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */
lua_Number limit = nvalue(ra+1);
if (luai_numlt(0, step) ? luai_numle(idx, limit)
: luai_numle(limit, idx)) {
dojump(L, pc, GETARG_sBx(i)); /* jump back */
setnvalue(ra, idx); /* update internal index... */
setnvalue(ra+3, idx); /* ...and external index */
}
continue;
}
case OP_FORPREP: {
const TValue *init = ra;
const TValue *plimit = ra+1;
const TValue *pstep = ra+2;
L->savedpc = pc; /* next steps may throw errors */
if (!tonumber(init, ra))
luaG_runerror(L, LUA_QL("for") " initial value must be a number");
else if (!tonumber(plimit, ra+1))
luaG_runerror(L, LUA_QL("for") " limit must be a number");
else if (!tonumber(pstep, ra+2))
luaG_runerror(L, LUA_QL("for") " step must be a number");
setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep)));
dojump(L, pc, GETARG_sBx(i));
continue;
}
case OP_TFORLOOP: {
StkId cb = ra + 3; /* call base */
setobjs2s(L, cb+2, ra+2);
setobjs2s(L, cb+1, ra+1);
setobjs2s(L, cb, ra);
L->top = cb+3; /* func. + 2 args (state and index) */
Protect(luaD_call(L, cb, GETARG_C(i)));
L->top = L->ci->top;
cb = RA(i) + 3; /* previous call may change the stack */
if (!ttisnil(cb)) { /* continue loop? */
setobjs2s(L, cb-1, cb); /* save control variable */
dojump(L, pc, GETARG_sBx(*pc)); /* jump back */
}
pc++;
continue;
}
case OP_SETLIST: {
int n = GETARG_B(i);
int c = GETARG_C(i);
int last;
Table *h;
if (n == 0) {
n = cast_int(L->top - ra) - 1;
L->top = L->ci->top;
}
if (c == 0) c = cast_int(*pc++);
runtime_check(L, ttistable(ra));
h = hvalue(ra);
last = ((c-1)*LFIELDS_PER_FLUSH) + n;
if (last > h->sizearray) /* needs more space? */
luaH_resizearray(L, h, last); /* pre-alloc it at once */
for (; n > 0; n--) {
TValue *val = ra+n;
setobj2t(L, luaH_setnum(L, h, last--), val);
luaC_barriert(L, h, val);
}
continue;
}
case OP_CLOSE: {
luaF_close(L, ra);
continue;
}
case OP_CLOSURE: {
Proto *p;
Closure *ncl;
int nup, j;
p = cl->p->p[GETARG_Bx(i)];
nup = p->nups;
ncl = luaF_newLclosure(L, nup, cl->env);
ncl->l.p = p;
for (j=0; j<nup; j++, pc++) {
if (GET_OPCODE(*pc) == OP_GETUPVAL)
ncl->l.upvals[j] = cl->upvals[GETARG_B(*pc)];
else {
lua_assert(GET_OPCODE(*pc) == OP_MOVE);
ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc));
}
}
setclvalue(L, ra, ncl);
Protect(luaC_checkGC(L));
continue;
}
case OP_VARARG: {
int b = GETARG_B(i) - 1;
int j;
CallInfo *ci = L->ci;
int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1;
if (b == LUA_MULTRET) {
Protect(luaD_checkstack(L, n));
ra = RA(i); /* previous call may change the stack */
b = n;
L->top = ra + n;
}
for (j = 0; j < b; j++) {
if (j < n) {
setobjs2s(L, ra + j, ci->base - n + j);
}
else {
setnilvalue(ra + j);
}
}
continue;
}
}
}
}
/*
** $Id: lvm.c,v 2.63.1.4 2009/07/01 21:10:33 roberto Exp $
** Lua virtual machine
** See Copyright Notice in lua.h
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define lvm_c
#define LUA_CORE
#include "lua.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lgc.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lvm.h"
/* limit for table tag-method chains (to avoid loops) */
#define MAXTAGLOOP 100
const TValue *luaV_tonumber (const TValue *obj, TValue *n) {
lua_Number num;
if (ttisnumber(obj)) return obj;
if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) {
setnvalue(n, num);
return n;
}
else
return NULL;
}
int luaV_tostring (lua_State *L, StkId obj) {
if (!ttisnumber(obj))
return 0;
else {
char s[LUAI_MAXNUMBER2STR];
lua_Number n = nvalue(obj);
lua_number2str(s, n);
setsvalue2s(L, obj, luaS_new(L, s));
return 1;
}
}
static void traceexec (lua_State *L, const Instruction *pc) {
lu_byte mask = L->hookmask;
const Instruction *oldpc = L->savedpc;
L->savedpc = pc;
if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) {
resethookcount(L);
luaD_callhook(L, LUA_HOOKCOUNT, -1);
}
if (mask & LUA_MASKLINE) {
Proto *p = ci_func(L->ci)->l.p;
int npc = pcRel(pc, p);
int newline = getline(p, npc);
/* call linehook when enter a new function, when jump back (loop),
or when enter a new line */
if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p)))
luaD_callhook(L, LUA_HOOKLINE, newline);
}
}
static void callTMres (lua_State *L, StkId res, const TValue *f,
const TValue *p1, const TValue *p2) {
ptrdiff_t result = savestack(L, res);
setobj2s(L, L->top, f); /* push function */
setobj2s(L, L->top+1, p1); /* 1st argument */
setobj2s(L, L->top+2, p2); /* 2nd argument */
luaD_checkstack(L, 3);
L->top += 3;
luaD_call(L, L->top - 3, 1);
res = restorestack(L, result);
L->top--;
setobjs2s(L, res, L->top);
}
static void callTM (lua_State *L, const TValue *f, const TValue *p1,
const TValue *p2, const TValue *p3) {
setobj2s(L, L->top, f); /* push function */
setobj2s(L, L->top+1, p1); /* 1st argument */
setobj2s(L, L->top+2, p2); /* 2nd argument */
setobj2s(L, L->top+3, p3); /* 3th argument */
luaD_checkstack(L, 4);
L->top += 4;
luaD_call(L, L->top - 4, 0);
}
void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) {
int loop;
for (loop = 0; loop < MAXTAGLOOP; loop++) {
const TValue *tm;
if (ttistable(t)) { /* `t' is a table? */
Table *h = hvalue(t);
const TValue *res = luaH_get(h, key); /* do a primitive get */
if (!ttisnil(res) || /* result is no nil? */
(tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */
setobj2s(L, val, res);
return;
}
/* else will try the tag method */
}
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))
luaG_typeerror(L, t, "index");
if (ttisfunction(tm)) {
callTMres(L, val, tm, t, key);
return;
}
t = tm; /* else repeat with `tm' */
}
luaG_runerror(L, "loop in gettable");
}
void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
int loop;
TValue temp;
for (loop = 0; loop < MAXTAGLOOP; loop++) {
const TValue *tm;
if (ttistable(t)) { /* `t' is a table? */
Table *h = hvalue(t);
TValue *oldval = luaH_set(L, h, key); /* do a primitive set */
if (!ttisnil(oldval) || /* result is no nil? */
(tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
setobj2t(L, oldval, val);
luaC_barriert(L, h, val);
return;
}
/* else will try the tag method */
}
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
luaG_typeerror(L, t, "index");
if (ttisfunction(tm)) {
callTM(L, tm, t, key, val);
return;
}
/* else repeat with `tm' */
setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */
t = &temp;
}
luaG_runerror(L, "loop in settable");
}
static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2,
StkId res, TMS event) {
const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */
if (ttisnil(tm))
tm = luaT_gettmbyobj(L, p2, event); /* try second operand */
if (ttisnil(tm)) return 0;
callTMres(L, res, tm, p1, p2);
return 1;
}
static const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2,
TMS event) {
const TValue *tm1 = fasttm(L, mt1, event);
const TValue *tm2;
if (tm1 == NULL) return NULL; /* no metamethod */
if (mt1 == mt2) return tm1; /* same metatables => same metamethods */
tm2 = fasttm(L, mt2, event);
if (tm2 == NULL) return NULL; /* no metamethod */
if (luaO_rawequalObj(tm1, tm2)) /* same metamethods? */
return tm1;
return NULL;
}
static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2,
TMS event) {
const TValue *tm1 = luaT_gettmbyobj(L, p1, event);
const TValue *tm2;
if (ttisnil(tm1)) return -1; /* no metamethod? */
tm2 = luaT_gettmbyobj(L, p2, event);
if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */
return -1;
callTMres(L, L->top, tm1, p1, p2);
return !l_isfalse(L->top);
}
static int l_strcmp (const TString *ls, const TString *rs) {
const char *l = getstr(ls);
size_t ll = ls->tsv.len;
const char *r = getstr(rs);
size_t lr = rs->tsv.len;
for (;;) {
int temp = strcoll(l, r);
if (temp != 0) return temp;
else { /* strings are equal up to a `\0' */
size_t len = strlen(l); /* index of first `\0' in both strings */
if (len == lr) /* r is finished? */
return (len == ll) ? 0 : 1;
else if (len == ll) /* l is finished? */
return -1; /* l is smaller than r (because r is not finished) */
/* both strings longer than `len'; go on comparing (after the `\0') */
len++;
l += len; ll -= len; r += len; lr -= len;
}
}
}
int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
int res;
if (ttype(l) != ttype(r))
return luaG_ordererror(L, l, r);
else if (ttisnumber(l))
return luai_numlt(nvalue(l), nvalue(r));
else if (ttisstring(l))
return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0;
else if ((res = call_orderTM(L, l, r, TM_LT)) != -1)
return res;
return luaG_ordererror(L, l, r);
}
static int lessequal (lua_State *L, const TValue *l, const TValue *r) {
int res;
if (ttype(l) != ttype(r))
return luaG_ordererror(L, l, r);
else if (ttisnumber(l))
return luai_numle(nvalue(l), nvalue(r));
else if (ttisstring(l))
return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0;
else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) /* first try `le' */
return res;
else if ((res = call_orderTM(L, r, l, TM_LT)) != -1) /* else try `lt' */
return !res;
return luaG_ordererror(L, l, r);
}
int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) {
const TValue *tm;
lua_assert(ttype(t1) == ttype(t2));
switch (ttype(t1)) {
case LUA_TNIL: return 1;
case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2));
case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */
case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
case LUA_TUSERDATA: {
if (uvalue(t1) == uvalue(t2)) return 1;
tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable,
TM_EQ);
break; /* will try TM */
}
case LUA_TTABLE: {
if (hvalue(t1) == hvalue(t2)) return 1;
tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ);
break; /* will try TM */
}
default: return gcvalue(t1) == gcvalue(t2);
}
if (tm == NULL) return 0; /* no TM? */
callTMres(L, L->top, tm, t1, t2); /* call TM */
return !l_isfalse(L->top);
}
void luaV_concat (lua_State *L, int total, int last) {
do {
StkId top = L->base + last + 1;
int n = 2; /* number of elements handled in this pass (at least 2) */
if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) {
if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT))
luaG_concaterror(L, top-2, top-1);
} else if (tsvalue(top-1)->len == 0) /* second op is empty? */
(void)tostring(L, top - 2); /* result is first op (as string) */
else {
/* at least two string values; get as many as possible */
size_t tl = tsvalue(top-1)->len;
char *buffer;
int i;
/* collect total length */
for (n = 1; n < total && tostring(L, top-n-1); n++) {
size_t l = tsvalue(top-n-1)->len;
if (l >= MAX_SIZET - tl) luaG_runerror(L, "string length overflow");
tl += l;
}
buffer = luaZ_openspace(L, &G(L)->buff, tl);
tl = 0;
for (i=n; i>0; i--) { /* concat all strings */
size_t l = tsvalue(top-i)->len;
memcpy(buffer+tl, svalue(top-i), l);
tl += l;
}
setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl));
}
total -= n-1; /* got `n' strings to create 1 new */
last -= n-1;
} while (total > 1); /* repeat until only 1 result left */
}
static void Arith (lua_State *L, StkId ra, const TValue *rb,
const TValue *rc, TMS op) {
TValue tempb, tempc;
const TValue *b, *c;
if ((b = luaV_tonumber(rb, &tempb)) != NULL &&
(c = luaV_tonumber(rc, &tempc)) != NULL) {
lua_Number nb = nvalue(b), nc = nvalue(c);
switch (op) {
case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break;
case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break;
case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break;
case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break;
case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break;
case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break;
case TM_UNM: setnvalue(ra, luai_numunm(nb)); break;
default: lua_assert(0); break;
}
}
else if (!call_binTM(L, rb, rc, ra, op))
luaG_aritherror(L, rb, rc);
}
/*
** some macros for common tasks in `luaV_execute'
*/
#define runtime_check(L, c) { if (!(c)) break; }
#define RA(i) (base+GETARG_A(i))
/* to be used after possible stack reallocation */
#define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i))
#define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i))
#define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \
ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i))
#define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \
ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i))
#define KBx(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i))
#define dojump(L,pc,i) {(pc) += (i); luai_threadyield(L);}
#define Protect(x) { L->savedpc = pc; {x;}; base = L->base; }
#define arith_op(op,tm) { \
TValue *rb = RKB(i); \
TValue *rc = RKC(i); \
if (ttisnumber(rb) && ttisnumber(rc)) { \
lua_Number nb = nvalue(rb), nc = nvalue(rc); \
setnvalue(ra, op(nb, nc)); \
} \
else \
Protect(Arith(L, ra, rb, rc, tm)); \
}
void luaV_execute (lua_State *L, int nexeccalls) {
LClosure *cl;
StkId base;
TValue *k;
const Instruction *pc;
reentry: /* entry point */
lua_assert(isLua(L->ci));
pc = L->savedpc;
cl = &clvalue(L->ci->func)->l;
base = L->base;
k = cl->p->k;
/* main loop of interpreter */
for (;;) {
const Instruction i = *pc++;
StkId ra;
if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) &&
(--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) {
traceexec(L, pc);
if (L->status == LUA_YIELD) { /* did hook yield? */
L->savedpc = pc - 1;
return;
}
base = L->base;
}
/* warning!! several calls may realloc the stack and invalidate `ra' */
ra = RA(i);
lua_assert(base == L->base && L->base == L->ci->base);
lua_assert(base <= L->top && L->top <= L->stack + L->stacksize);
lua_assert(L->top == L->ci->top || luaG_checkopenop(i));
switch (GET_OPCODE(i)) {
case OP_MOVE: {
setobjs2s(L, ra, RB(i));
continue;
}
case OP_LOADK: {
setobj2s(L, ra, KBx(i));
continue;
}
case OP_LOADBOOL: {
setbvalue(ra, GETARG_B(i));
if (GETARG_C(i)) pc++; /* skip next instruction (if C) */
continue;
}
case OP_LOADNIL: {
TValue *rb = RB(i);
do {
setnilvalue(rb--);
} while (rb >= ra);
continue;
}
case OP_GETUPVAL: {
int b = GETARG_B(i);
setobj2s(L, ra, cl->upvals[b]->v);
continue;
}
case OP_GETGLOBAL: {
TValue g;
TValue *rb = KBx(i);
sethvalue(L, &g, cl->env);
lua_assert(ttisstring(rb));
Protect(luaV_gettable(L, &g, rb, ra));
continue;
}
case OP_GETTABLE: {
Protect(luaV_gettable(L, RB(i), RKC(i), ra));
continue;
}
case OP_SETGLOBAL: {
TValue g;
sethvalue(L, &g, cl->env);
lua_assert(ttisstring(KBx(i)));
Protect(luaV_settable(L, &g, KBx(i), ra));
continue;
}
case OP_SETUPVAL: {
UpVal *uv = cl->upvals[GETARG_B(i)];
setobj(L, uv->v, ra);
luaC_barrier(L, uv, ra);
continue;
}
case OP_SETTABLE: {
Protect(luaV_settable(L, ra, RKB(i), RKC(i)));
continue;
}
case OP_NEWTABLE: {
int b = GETARG_B(i);
int c = GETARG_C(i);
sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c)));
Protect(luaC_checkGC(L));
continue;
}
case OP_SELF: {
StkId rb = RB(i);
setobjs2s(L, ra+1, rb);
Protect(luaV_gettable(L, rb, RKC(i), ra));
continue;
}
case OP_ADD: {
arith_op(luai_numadd, TM_ADD);
continue;
}
case OP_SUB: {
arith_op(luai_numsub, TM_SUB);
continue;
}
case OP_MUL: {
arith_op(luai_nummul, TM_MUL);
continue;
}
case OP_DIV: {
arith_op(luai_numdiv, TM_DIV);
continue;
}
case OP_MOD: {
arith_op(luai_nummod, TM_MOD);
continue;
}
case OP_POW: {
arith_op(luai_numpow, TM_POW);
continue;
}
case OP_UNM: {
TValue *rb = RB(i);
if (ttisnumber(rb)) {
lua_Number nb = nvalue(rb);
setnvalue(ra, luai_numunm(nb));
}
else {
Protect(Arith(L, ra, rb, rb, TM_UNM));
}
continue;
}
case OP_NOT: {
int res = l_isfalse(RB(i)); /* next assignment may change this value */
setbvalue(ra, res);
continue;
}
case OP_LEN: {
const TValue *rb = RB(i);
switch (ttype(rb)) {
case LUA_TTABLE: {
setnvalue(ra, cast_num(luaH_getn(hvalue(rb))));
break;
}
case LUA_TSTRING: {
setnvalue(ra, cast_num(tsvalue(rb)->len));
break;
}
default: { /* try metamethod */
Protect(
if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN))
luaG_typeerror(L, rb, "get length of");
)
}
}
continue;
}
case OP_CONCAT: {
int b = GETARG_B(i);
int c = GETARG_C(i);
Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L));
setobjs2s(L, RA(i), base+b);
continue;
}
case OP_JMP: {
dojump(L, pc, GETARG_sBx(i));
continue;
}
case OP_EQ: {
TValue *rb = RKB(i);
TValue *rc = RKC(i);
Protect(
if (equalobj(L, rb, rc) == GETARG_A(i))
dojump(L, pc, GETARG_sBx(*pc));
)
pc++;
continue;
}
case OP_LT: {
Protect(
if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i))
dojump(L, pc, GETARG_sBx(*pc));
)
pc++;
continue;
}
case OP_LE: {
Protect(
if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i))
dojump(L, pc, GETARG_sBx(*pc));
)
pc++;
continue;
}
case OP_TEST: {
if (l_isfalse(ra) != GETARG_C(i))
dojump(L, pc, GETARG_sBx(*pc));
pc++;
continue;
}
case OP_TESTSET: {
TValue *rb = RB(i);
if (l_isfalse(rb) != GETARG_C(i)) {
setobjs2s(L, ra, rb);
dojump(L, pc, GETARG_sBx(*pc));
}
pc++;
continue;
}
case OP_CALL: {
int b = GETARG_B(i);
int nresults = GETARG_C(i) - 1;
if (b != 0) L->top = ra+b; /* else previous instruction set top */
L->savedpc = pc;
switch (luaD_precall(L, ra, nresults)) {
case PCRLUA: {
nexeccalls++;
goto reentry; /* restart luaV_execute over new Lua function */
}
case PCRC: {
/* it was a C function (`precall' called it); adjust results */
if (nresults >= 0) L->top = L->ci->top;
base = L->base;
continue;
}
default: {
return; /* yield */
}
}
}
case OP_TAILCALL: {
int b = GETARG_B(i);
if (b != 0) L->top = ra+b; /* else previous instruction set top */
L->savedpc = pc;
lua_assert(GETARG_C(i) - 1 == LUA_MULTRET);
switch (luaD_precall(L, ra, LUA_MULTRET)) {
case PCRLUA: {
/* tail call: put new frame in place of previous one */
CallInfo *ci = L->ci - 1; /* previous frame */
int aux;
StkId func = ci->func;
StkId pfunc = (ci+1)->func; /* previous function index */
if (L->openupval) luaF_close(L, ci->base);
L->base = ci->base = ci->func + ((ci+1)->base - pfunc);
for (aux = 0; pfunc+aux < L->top; aux++) /* move frame down */
setobjs2s(L, func+aux, pfunc+aux);
ci->top = L->top = func+aux; /* correct top */
lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize);
ci->savedpc = L->savedpc;
ci->tailcalls++; /* one more call lost */
L->ci--; /* remove new frame */
goto reentry;
}
case PCRC: { /* it was a C function (`precall' called it) */
base = L->base;
continue;
}
default: {
return; /* yield */
}
}
}
case OP_RETURN: {
int b = GETARG_B(i);
if (b != 0) L->top = ra+b-1;
if (L->openupval) luaF_close(L, base);
L->savedpc = pc;
b = luaD_poscall(L, ra);
if (--nexeccalls == 0) /* was previous function running `here'? */
return; /* no: return */
else { /* yes: continue its execution */
if (b) L->top = L->ci->top;
lua_assert(isLua(L->ci));
lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL);
goto reentry;
}
}
case OP_FORLOOP: {
lua_Number step = nvalue(ra+2);
lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */
lua_Number limit = nvalue(ra+1);
if (luai_numlt(0, step) ? luai_numle(idx, limit)
: luai_numle(limit, idx)) {
dojump(L, pc, GETARG_sBx(i)); /* jump back */
setnvalue(ra, idx); /* update internal index... */
setnvalue(ra+3, idx); /* ...and external index */
}
continue;
}
case OP_FORPREP: {
const TValue *init = ra;
const TValue *plimit = ra+1;
const TValue *pstep = ra+2;
L->savedpc = pc; /* next steps may throw errors */
if (!tonumber(init, ra))
luaG_runerror(L, LUA_QL("for") " initial value must be a number");
else if (!tonumber(plimit, ra+1))
luaG_runerror(L, LUA_QL("for") " limit must be a number");
else if (!tonumber(pstep, ra+2))
luaG_runerror(L, LUA_QL("for") " step must be a number");
setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep)));
dojump(L, pc, GETARG_sBx(i));
continue;
}
case OP_TFORLOOP: {
StkId cb = ra + 3; /* call base */
setobjs2s(L, cb+2, ra+2);
setobjs2s(L, cb+1, ra+1);
setobjs2s(L, cb, ra);
L->top = cb+3; /* func. + 2 args (state and index) */
Protect(luaD_call(L, cb, GETARG_C(i)));
L->top = L->ci->top;
cb = RA(i) + 3; /* previous call may change the stack */
if (!ttisnil(cb)) { /* continue loop? */
setobjs2s(L, cb-1, cb); /* save control variable */
dojump(L, pc, GETARG_sBx(*pc)); /* jump back */
}
pc++;
continue;
}
case OP_SETLIST: {
int n = GETARG_B(i);
int c = GETARG_C(i);
int last;
Table *h;
if (n == 0) {
n = cast_int(L->top - ra) - 1;
L->top = L->ci->top;
}
if (c == 0) c = cast_int(*pc++);
runtime_check(L, ttistable(ra));
h = hvalue(ra);
last = ((c-1)*LFIELDS_PER_FLUSH) + n;
if (last > h->sizearray) /* needs more space? */
luaH_resizearray(L, h, last); /* pre-alloc it at once */
for (; n > 0; n--) {
TValue *val = ra+n;
setobj2t(L, luaH_setnum(L, h, last--), val);
luaC_barriert(L, h, val);
}
continue;
}
case OP_CLOSE: {
luaF_close(L, ra);
continue;
}
case OP_CLOSURE: {
Proto *p;
Closure *ncl;
int nup, j;
p = cl->p->p[GETARG_Bx(i)];
nup = p->nups;
ncl = luaF_newLclosure(L, nup, cl->env);
ncl->l.p = p;
for (j=0; j<nup; j++, pc++) {
if (GET_OPCODE(*pc) == OP_GETUPVAL)
ncl->l.upvals[j] = cl->upvals[GETARG_B(*pc)];
else {
lua_assert(GET_OPCODE(*pc) == OP_MOVE);
ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc));
}
}
setclvalue(L, ra, ncl);
Protect(luaC_checkGC(L));
continue;
}
case OP_VARARG: {
int b = GETARG_B(i) - 1;
int j;
CallInfo *ci = L->ci;
int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1;
if (b == LUA_MULTRET) {
Protect(luaD_checkstack(L, n));
ra = RA(i); /* previous call may change the stack */
b = n;
L->top = ra + n;
}
for (j = 0; j < b; j++) {
if (j < n) {
setobjs2s(L, ra + j, ci->base - n + j);
}
else {
setnilvalue(ra + j);
}
}
continue;
}
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment