diff options
Diffstat (limited to 'lparser.c')
| -rw-r--r-- | lparser.c | 188 |
1 files changed, 174 insertions, 14 deletions
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lparser.c,v 2.95 2011/01/26 16:30:02 roberto Exp roberto $ | 2 | ** $Id: lparser.c,v 2.96 2011/02/01 18:03:10 roberto Exp roberto $ |
| 3 | ** Lua Parser | 3 | ** Lua Parser |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -42,7 +42,9 @@ | |||
| 42 | typedef struct BlockCnt { | 42 | typedef struct BlockCnt { |
| 43 | struct BlockCnt *previous; /* chain */ | 43 | struct BlockCnt *previous; /* chain */ |
| 44 | int breaklist; /* list of jumps out of this loop */ | 44 | int breaklist; /* list of jumps out of this loop */ |
| 45 | lu_byte nactvar; /* # active locals outside the breakable structure */ | 45 | int firstlabel; /* index (in Labellist) of first label in this block */ |
| 46 | int firstgoto; /* index (in Gotolist) of first pending goto in this block */ | ||
| 47 | lu_byte nactvar; /* # active locals outside the block */ | ||
| 46 | lu_byte upval; /* true if some variable in the block is an upvalue */ | 48 | lu_byte upval; /* true if some variable in the block is an upvalue */ |
| 47 | lu_byte isbreakable; /* true if `block' is a loop */ | 49 | lu_byte isbreakable; /* true if `block' is a loop */ |
| 48 | } BlockCnt; | 50 | } BlockCnt; |
| @@ -52,7 +54,7 @@ typedef struct BlockCnt { | |||
| 52 | /* | 54 | /* |
| 53 | ** prototypes for recursive non-terminal functions | 55 | ** prototypes for recursive non-terminal functions |
| 54 | */ | 56 | */ |
| 55 | static void chunk (LexState *ls); | 57 | static void statlist (LexState *ls); |
| 56 | static void expr (LexState *ls, expdesc *v); | 58 | static void expr (LexState *ls, expdesc *v); |
| 57 | 59 | ||
| 58 | 60 | ||
| @@ -172,7 +174,7 @@ static void new_localvar (LexState *ls, TString *name) { | |||
| 172 | checklimit(fs, vl->nactvar + 1 - fs->firstlocal, | 174 | checklimit(fs, vl->nactvar + 1 - fs->firstlocal, |
| 173 | MAXVARS, "local variables"); | 175 | MAXVARS, "local variables"); |
| 174 | luaM_growvector(ls->L, vl->actvar, vl->nactvar + 1, | 176 | luaM_growvector(ls->L, vl->actvar, vl->nactvar + 1, |
| 175 | vl->actvarsize, vardesc, MAX_INT, "local variables"); | 177 | vl->actvarsize, Vardesc, MAX_INT, "local variables"); |
| 176 | vl->actvar[vl->nactvar++].idx = cast(unsigned short, reg); | 178 | vl->actvar[vl->nactvar++].idx = cast(unsigned short, reg); |
| 177 | } | 179 | } |
| 178 | 180 | ||
| @@ -327,10 +329,93 @@ static void enterlevel (LexState *ls) { | |||
| 327 | #define leavelevel(ls) (G((ls)->L)->nCcalls--) | 329 | #define leavelevel(ls) (G((ls)->L)->nCcalls--) |
| 328 | 330 | ||
| 329 | 331 | ||
| 332 | static void closegoto (LexState *ls, int g, Labeldesc *label) { | ||
| 333 | int i; | ||
| 334 | FuncState *fs = ls->fs; | ||
| 335 | Gotodesc *gt = &ls->gtl->gt[g]; | ||
| 336 | lua_assert(gt->name == label->name); | ||
| 337 | if (gt->currlevel < label->nactvar) { | ||
| 338 | const char *msg = luaO_pushfstring(ls->L, | ||
| 339 | "<goto> at line %d attemps to jump into the scope of local " LUA_QS, | ||
| 340 | gt->line, getstr(getlocvar(fs, gt->currlevel)->varname));; | ||
| 341 | luaX_syntaxerror(ls, msg); | ||
| 342 | } | ||
| 343 | luaK_patchlist(fs, gt->pc, label->pc); | ||
| 344 | /* remove goto from pending list */ | ||
| 345 | for (i = g; i < ls->gtl->ngt - 1; i++) | ||
| 346 | ls->gtl->gt[i] = ls->gtl->gt[i + 1]; | ||
| 347 | ls->gtl->ngt--; | ||
| 348 | } | ||
| 349 | |||
| 350 | |||
| 351 | /* | ||
| 352 | ** try to close a goto with existing labels; this solves backward jumps | ||
| 353 | */ | ||
| 354 | static int findlabel (LexState *ls, int g) { | ||
| 355 | int i; | ||
| 356 | BlockCnt *bl = ls->fs->bl; | ||
| 357 | Labellist *labell = ls->labell; | ||
| 358 | Gotodesc *gt = &ls->gtl->gt[g]; | ||
| 359 | /* check labels in current block for a match */ | ||
| 360 | for (i = bl->firstlabel; i < labell->nlabel; i++) { | ||
| 361 | Labeldesc *lb = &labell->label[i]; | ||
| 362 | if (lb->name == gt->name) { | ||
| 363 | lua_assert(labell->label[i].pc <= gt->pc); | ||
| 364 | if (gt->currlevel > lb->nactvar && | ||
| 365 | (bl->upval || ls->labell->nlabel > bl->firstlabel)) | ||
| 366 | luaK_patchclose(ls->fs, gt->pc, lb->nactvar); | ||
| 367 | closegoto(ls, g, lb); /* close it */ | ||
| 368 | return 1; | ||
| 369 | } | ||
| 370 | } | ||
| 371 | return 0; /* label not found; cannot close goto */ | ||
| 372 | } | ||
| 373 | |||
| 374 | |||
| 375 | /* | ||
| 376 | ** check whether new label 'lb' matches any pending goto in current | ||
| 377 | ** block; solves forward jumps | ||
| 378 | */ | ||
| 379 | static void findgotos (LexState *ls, Labeldesc *lb) { | ||
| 380 | int i; | ||
| 381 | Gotolist *gtl = ls->gtl; | ||
| 382 | for (i = ls->fs->bl->firstgoto; i < gtl->ngt; i++) { | ||
| 383 | if (gtl->gt[i].name == lb->name) | ||
| 384 | closegoto(ls, i, lb); | ||
| 385 | } | ||
| 386 | } | ||
| 387 | |||
| 388 | |||
| 389 | /* | ||
| 390 | ** "export" pending gotos to outer level, to check them against | ||
| 391 | ** outer labels; if the block being exited has upvalues, and | ||
| 392 | ** the goto exists the scope of any variable (which can be the | ||
| 393 | ** upvalue), close those variables being exited. | ||
| 394 | */ | ||
| 395 | static void movegotosout (FuncState *fs, BlockCnt *bl) { | ||
| 396 | int i = bl->firstgoto; | ||
| 397 | LexState *ls = fs->ls; | ||
| 398 | /* correct pending gotos to current block and try to close it | ||
| 399 | with visible labels */ | ||
| 400 | while (i < ls->gtl->ngt) { | ||
| 401 | Gotodesc *gt = &ls->gtl->gt[i]; | ||
| 402 | if (gt->currlevel > bl->nactvar) { | ||
| 403 | if (bl->upval) | ||
| 404 | luaK_patchclose(fs, gt->pc, bl->nactvar); | ||
| 405 | gt->currlevel = bl->nactvar; | ||
| 406 | } | ||
| 407 | if (!findlabel(ls, i)) | ||
| 408 | i++; /* move to next one */ | ||
| 409 | } | ||
| 410 | } | ||
| 411 | |||
| 412 | |||
| 330 | static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) { | 413 | static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) { |
| 331 | bl->breaklist = NO_JUMP; | 414 | bl->breaklist = NO_JUMP; |
| 332 | bl->isbreakable = isbreakable; | 415 | bl->isbreakable = isbreakable; |
| 333 | bl->nactvar = fs->nactvar; | 416 | bl->nactvar = fs->nactvar; |
| 417 | bl->firstlabel = fs->ls->labell->nlabel; | ||
| 418 | bl->firstgoto = fs->ls->gtl->ngt; | ||
| 334 | bl->upval = 0; | 419 | bl->upval = 0; |
| 335 | bl->previous = fs->bl; | 420 | bl->previous = fs->bl; |
| 336 | fs->bl = bl; | 421 | fs->bl = bl; |
| @@ -342,6 +427,8 @@ static void leaveblock (FuncState *fs) { | |||
| 342 | BlockCnt *bl = fs->bl; | 427 | BlockCnt *bl = fs->bl; |
| 343 | fs->bl = bl->previous; | 428 | fs->bl = bl->previous; |
| 344 | removevars(fs, bl->nactvar); | 429 | removevars(fs, bl->nactvar); |
| 430 | fs->ls->labell->nlabel = bl->firstlabel; /* remove local labels */ | ||
| 431 | movegotosout(fs, bl); | ||
| 345 | if (bl->upval) | 432 | if (bl->upval) |
| 346 | luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); | 433 | luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); |
| 347 | /* a block either controls scope or breaks (never both) */ | 434 | /* a block either controls scope or breaks (never both) */ |
| @@ -445,8 +532,24 @@ static void open_mainfunc (LexState *ls, FuncState *fs) { | |||
| 445 | } | 532 | } |
| 446 | 533 | ||
| 447 | 534 | ||
| 535 | static void mainblock (LexState *ls, FuncState *fs) { | ||
| 536 | BlockCnt bl; | ||
| 537 | enterblock(fs, &bl, 0); | ||
| 538 | statlist(ls); /* read main block */ | ||
| 539 | if (bl.firstgoto < ls->gtl->ngt) { /* check pending gotos */ | ||
| 540 | Gotodesc *gt = &ls->gtl->gt[bl.firstgoto]; | ||
| 541 | const char *msg = luaO_pushfstring(ls->L, | ||
| 542 | "label " LUA_QS " (<goto> at line %d) undefined", | ||
| 543 | getstr(gt->name), gt->line); | ||
| 544 | luaX_syntaxerror(ls, msg); | ||
| 545 | } | ||
| 546 | bl.upval = 0; /* RETURN will close any pending upvalue */ | ||
| 547 | leaveblock(fs); | ||
| 548 | } | ||
| 549 | |||
| 550 | |||
| 448 | Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Varlist *varl, | 551 | Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Varlist *varl, |
| 449 | const char *name) { | 552 | Gotolist *gtl, Labellist *labell, const char *name) { |
| 450 | LexState lexstate; | 553 | LexState lexstate; |
| 451 | FuncState funcstate; | 554 | FuncState funcstate; |
| 452 | TString *tname = luaS_new(L, name); | 555 | TString *tname = luaS_new(L, name); |
| @@ -454,10 +557,12 @@ Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Varlist *varl, | |||
| 454 | incr_top(L); | 557 | incr_top(L); |
| 455 | lexstate.buff = buff; | 558 | lexstate.buff = buff; |
| 456 | lexstate.varl = varl; | 559 | lexstate.varl = varl; |
| 560 | lexstate.gtl = gtl; | ||
| 561 | lexstate.labell = labell; | ||
| 457 | luaX_setinput(L, &lexstate, z, tname); | 562 | luaX_setinput(L, &lexstate, z, tname); |
| 458 | open_mainfunc(&lexstate, &funcstate); | 563 | open_mainfunc(&lexstate, &funcstate); |
| 459 | luaX_next(&lexstate); /* read first token */ | 564 | luaX_next(&lexstate); /* read first token */ |
| 460 | chunk(&lexstate); /* read main chunk */ | 565 | mainblock(&lexstate, &funcstate); |
| 461 | check(&lexstate, TK_EOS); | 566 | check(&lexstate, TK_EOS); |
| 462 | close_func(&lexstate); | 567 | close_func(&lexstate); |
| 463 | L->top--; /* pop name */ | 568 | L->top--; /* pop name */ |
| @@ -645,7 +750,7 @@ static void parlist (LexState *ls) { | |||
| 645 | 750 | ||
| 646 | 751 | ||
| 647 | static void body (LexState *ls, expdesc *e, int needself, int line) { | 752 | static void body (LexState *ls, expdesc *e, int needself, int line) { |
| 648 | /* body -> `(' parlist `)' chunk END */ | 753 | /* body -> `(' parlist `)' block END */ |
| 649 | FuncState new_fs; | 754 | FuncState new_fs; |
| 650 | open_func(ls, &new_fs); | 755 | open_func(ls, &new_fs); |
| 651 | new_fs.f->linedefined = line; | 756 | new_fs.f->linedefined = line; |
| @@ -656,7 +761,7 @@ static void body (LexState *ls, expdesc *e, int needself, int line) { | |||
| 656 | } | 761 | } |
| 657 | parlist(ls); | 762 | parlist(ls); |
| 658 | checknext(ls, ')'); | 763 | checknext(ls, ')'); |
| 659 | chunk(ls); | 764 | mainblock(ls, &new_fs); |
| 660 | new_fs.f->lastlinedefined = ls->linenumber; | 765 | new_fs.f->lastlinedefined = ls->linenumber; |
| 661 | check_match(ls, TK_END, TK_FUNCTION, line); | 766 | check_match(ls, TK_END, TK_FUNCTION, line); |
| 662 | codeclosure(ls, new_fs.f, e); | 767 | codeclosure(ls, new_fs.f, e); |
| @@ -949,11 +1054,11 @@ static int block_follow (int token) { | |||
| 949 | 1054 | ||
| 950 | 1055 | ||
| 951 | static void block (LexState *ls) { | 1056 | static void block (LexState *ls) { |
| 952 | /* block -> chunk */ | 1057 | /* block -> statlist */ |
| 953 | FuncState *fs = ls->fs; | 1058 | FuncState *fs = ls->fs; |
| 954 | BlockCnt bl; | 1059 | BlockCnt bl; |
| 955 | enterblock(fs, &bl, 0); | 1060 | enterblock(fs, &bl, 0); |
| 956 | chunk(ls); | 1061 | statlist(ls); |
| 957 | lua_assert(bl.breaklist == NO_JUMP); | 1062 | lua_assert(bl.breaklist == NO_JUMP); |
| 958 | leaveblock(fs); | 1063 | leaveblock(fs); |
| 959 | } | 1064 | } |
| @@ -1043,6 +1148,13 @@ static int cond (LexState *ls) { | |||
| 1043 | } | 1148 | } |
| 1044 | 1149 | ||
| 1045 | 1150 | ||
| 1151 | /* code a break statement. The last 'if' decides the need to close | ||
| 1152 | upvalues when leaving the block. If the block has upvalues, it | ||
| 1153 | must be closed. If it has local variables and any label | ||
| 1154 | before the break, those variables must be closed too, as they | ||
| 1155 | may be used as upvalues after the break and through a goto | ||
| 1156 | be exited through this break. | ||
| 1157 | */ | ||
| 1046 | static void breakstat (LexState *ls) { | 1158 | static void breakstat (LexState *ls) { |
| 1047 | FuncState *fs = ls->fs; | 1159 | FuncState *fs = ls->fs; |
| 1048 | BlockCnt *bl = fs->bl; | 1160 | BlockCnt *bl = fs->bl; |
| @@ -1054,11 +1166,49 @@ static void breakstat (LexState *ls) { | |||
| 1054 | if (!bl) | 1166 | if (!bl) |
| 1055 | luaX_syntaxerror(ls, "no loop to break"); | 1167 | luaX_syntaxerror(ls, "no loop to break"); |
| 1056 | luaK_concat(fs, &bl->breaklist, luaK_jump(fs)); | 1168 | luaK_concat(fs, &bl->breaklist, luaK_jump(fs)); |
| 1057 | if (upval) | 1169 | if (upval || |
| 1170 | (fs->nactvar > bl->nactvar && | ||
| 1171 | ls->labell->nlabel > bl->firstlabel)) | ||
| 1058 | luaK_patchclose(fs, bl->breaklist, bl->nactvar); | 1172 | luaK_patchclose(fs, bl->breaklist, bl->nactvar); |
| 1059 | } | 1173 | } |
| 1060 | 1174 | ||
| 1061 | 1175 | ||
| 1176 | static void gotostat (LexState *ls, TString *label, int line) { | ||
| 1177 | Gotolist *gtl = ls->gtl; | ||
| 1178 | int g = gtl->ngt; /* index of new goto being created */ | ||
| 1179 | /* create new entry for this goto */ | ||
| 1180 | luaM_growvector(ls->L, gtl->gt, gtl->ngt, gtl->gtsize, | ||
| 1181 | Gotodesc, MAX_INT, "labels"); | ||
| 1182 | gtl->gt[g].name = label; | ||
| 1183 | gtl->gt[g].line = line; | ||
| 1184 | gtl->gt[g].currlevel = ls->fs->nactvar; | ||
| 1185 | gtl->gt[g].pc = luaK_jump(ls->fs); /* create jump instruction */ | ||
| 1186 | gtl->ngt++; | ||
| 1187 | findlabel(ls, g); | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | |||
| 1191 | static void labelstat (LexState *ls, TString *label) { | ||
| 1192 | /* label -> '@' NAME ':' */ | ||
| 1193 | FuncState *fs = ls->fs; | ||
| 1194 | Labellist *labell = ls->labell; | ||
| 1195 | int l = labell->nlabel; /* index of new label being created */ | ||
| 1196 | checknext(ls, ':'); | ||
| 1197 | /* create new entry for this label */ | ||
| 1198 | luaM_growvector(ls->L, labell->label, labell->nlabel, labell->labelsize, | ||
| 1199 | Labeldesc, MAX_INT, "labels"); | ||
| 1200 | labell->label[l].name = label; | ||
| 1201 | labell->label[l].pc = fs->pc; | ||
| 1202 | /* if label is last statement in the block, | ||
| 1203 | assume that local variables are already out of scope */ | ||
| 1204 | labell->label[l].nactvar = (ls->t.token == TK_END) | ||
| 1205 | ? fs->bl->nactvar | ||
| 1206 | : fs->nactvar; | ||
| 1207 | labell->nlabel++; | ||
| 1208 | findgotos(ls, &labell->label[l]); | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | |||
| 1062 | static void whilestat (LexState *ls, int line) { | 1212 | static void whilestat (LexState *ls, int line) { |
| 1063 | /* whilestat -> WHILE cond DO block END */ | 1213 | /* whilestat -> WHILE cond DO block END */ |
| 1064 | FuncState *fs = ls->fs; | 1214 | FuncState *fs = ls->fs; |
| @@ -1087,7 +1237,7 @@ static void repeatstat (LexState *ls, int line) { | |||
| 1087 | enterblock(fs, &bl1, 1); /* loop block */ | 1237 | enterblock(fs, &bl1, 1); /* loop block */ |
| 1088 | enterblock(fs, &bl2, 0); /* scope block */ | 1238 | enterblock(fs, &bl2, 0); /* scope block */ |
| 1089 | luaX_next(ls); /* skip REPEAT */ | 1239 | luaX_next(ls); /* skip REPEAT */ |
| 1090 | chunk(ls); | 1240 | statlist(ls); |
| 1091 | check_match(ls, TK_UNTIL, TK_REPEAT, line); | 1241 | check_match(ls, TK_UNTIL, TK_REPEAT, line); |
| 1092 | condexit = cond(ls); /* read condition (inside scope block) */ | 1242 | condexit = cond(ls); /* read condition (inside scope block) */ |
| 1093 | if (!bl2.upval) { /* no upvalues? */ | 1243 | if (!bl2.upval) { /* no upvalues? */ |
| @@ -1382,6 +1532,11 @@ static int statement (LexState *ls) { | |||
| 1382 | localstat(ls); | 1532 | localstat(ls); |
| 1383 | return 0; | 1533 | return 0; |
| 1384 | } | 1534 | } |
| 1535 | case '@': { /* stat -> label */ | ||
| 1536 | luaX_next(ls); /* skip '@' */ | ||
| 1537 | labelstat(ls, str_checkname(ls)); | ||
| 1538 | return 0; | ||
| 1539 | } | ||
| 1385 | case TK_RETURN: { /* stat -> retstat */ | 1540 | case TK_RETURN: { /* stat -> retstat */ |
| 1386 | luaX_next(ls); /* skip RETURN */ | 1541 | luaX_next(ls); /* skip RETURN */ |
| 1387 | retstat(ls); | 1542 | retstat(ls); |
| @@ -1392,6 +1547,11 @@ static int statement (LexState *ls) { | |||
| 1392 | breakstat(ls); | 1547 | breakstat(ls); |
| 1393 | return 1; /* must be last statement */ | 1548 | return 1; /* must be last statement */ |
| 1394 | } | 1549 | } |
| 1550 | case TK_GOTO: { /* stat -> 'goto' NAME */ | ||
| 1551 | luaX_next(ls); /* skip GOTO */ | ||
| 1552 | gotostat(ls, str_checkname(ls), line); | ||
| 1553 | return 0; | ||
| 1554 | } | ||
| 1395 | default: { /* stat -> func | assignment */ | 1555 | default: { /* stat -> func | assignment */ |
| 1396 | exprstat(ls); | 1556 | exprstat(ls); |
| 1397 | return 0; | 1557 | return 0; |
| @@ -1400,8 +1560,8 @@ static int statement (LexState *ls) { | |||
| 1400 | } | 1560 | } |
| 1401 | 1561 | ||
| 1402 | 1562 | ||
| 1403 | static void chunk (LexState *ls) { | 1563 | static void statlist (LexState *ls) { |
| 1404 | /* chunk -> { stat [`;'] } */ | 1564 | /* statlist -> { stat [`;'] } */ |
| 1405 | int islast = 0; | 1565 | int islast = 0; |
| 1406 | enterlevel(ls); | 1566 | enterlevel(ls); |
| 1407 | while (!islast && !block_follow(ls->t.token)) { | 1567 | while (!islast && !block_follow(ls->t.token)) { |
