diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2011-02-10 12:50:41 -0200 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2011-02-10 12:50:41 -0200 |
| commit | bf8b08295aa2f4b66aeb7d8313d464ccd80c0b0c (patch) | |
| tree | 5965feb819f09902846bcb5af86ed4b5e7af85b9 | |
| parent | 3f5b56c48b51b0e4e2c439f1d0bf717b018c1aee (diff) | |
| download | lua-bf8b08295aa2f4b66aeb7d8313d464ccd80c0b0c.tar.gz lua-bf8b08295aa2f4b66aeb7d8313d464ccd80c0b0c.tar.bz2 lua-bf8b08295aa2f4b66aeb7d8313d464ccd80c0b0c.zip | |
'break' coded as 'goto' + small bug when closing multiple gotos
to the same label
| -rw-r--r-- | lparser.c | 111 |
1 files changed, 52 insertions, 59 deletions
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lparser.c,v 2.102 2011/02/09 16:51:28 roberto Exp roberto $ | 2 | ** $Id: lparser.c,v 2.103 2011/02/09 17:03:18 roberto Exp $ |
| 3 | ** Lua Parser | 3 | ** Lua Parser |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -41,7 +41,6 @@ | |||
| 41 | */ | 41 | */ |
| 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 */ | ||
| 45 | int firstlabel; /* index of first label in this block */ | 44 | int firstlabel; /* index of first label in this block */ |
| 46 | int firstgoto; /* index of first pending goto in this block */ | 45 | int firstgoto; /* index of first pending goto in this block */ |
| 47 | lu_byte nactvar; /* # active locals outside the block */ | 46 | lu_byte nactvar; /* # active locals outside the block */ |
| @@ -380,16 +379,31 @@ static int findlabel (LexState *ls, int g) { | |||
| 380 | } | 379 | } |
| 381 | 380 | ||
| 382 | 381 | ||
| 382 | static int newlabelentry (LexState *ls, Labellist *l, TString *name, | ||
| 383 | int line, int pc) { | ||
| 384 | int n = l->n; | ||
| 385 | luaM_growvector(ls->L, l->arr, n, l->size, Labeldesc, MAX_INT, "labels"); | ||
| 386 | l->arr[n].name = name; | ||
| 387 | l->arr[n].line = line; | ||
| 388 | l->arr[n].nactvar = ls->fs->nactvar; | ||
| 389 | l->arr[n].pc = pc; | ||
| 390 | l->n++; | ||
| 391 | return n; | ||
| 392 | } | ||
| 393 | |||
| 394 | |||
| 383 | /* | 395 | /* |
| 384 | ** check whether new label 'lb' matches any pending goto in current | 396 | ** check whether new label 'lb' matches any pending gotos in current |
| 385 | ** block; solves forward jumps | 397 | ** block; solves forward jumps |
| 386 | */ | 398 | */ |
| 387 | static void findgotos (LexState *ls, Labeldesc *lb) { | 399 | static void findgotos (LexState *ls, Labeldesc *lb) { |
| 388 | int i; | ||
| 389 | Dyndata *dyd = ls->dyd; | 400 | Dyndata *dyd = ls->dyd; |
| 390 | for (i = ls->fs->bl->firstgoto; i < dyd->gt.n; i++) { | 401 | int i = ls->fs->bl->firstgoto; |
| 402 | while (i < dyd->gt.n) { | ||
| 391 | if (eqstr(dyd->gt.arr[i].name, lb->name)) | 403 | if (eqstr(dyd->gt.arr[i].name, lb->name)) |
| 392 | closegoto(ls, i, lb); | 404 | closegoto(ls, i, lb); |
| 405 | else | ||
| 406 | i++; | ||
| 393 | } | 407 | } |
| 394 | } | 408 | } |
| 395 | 409 | ||
| @@ -419,7 +433,6 @@ static void movegotosout (FuncState *fs, BlockCnt *bl) { | |||
| 419 | 433 | ||
| 420 | 434 | ||
| 421 | static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { | 435 | static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { |
| 422 | bl->breaklist = NO_JUMP; | ||
| 423 | bl->isloop = isloop; | 436 | bl->isloop = isloop; |
| 424 | bl->nactvar = fs->nactvar; | 437 | bl->nactvar = fs->nactvar; |
| 425 | bl->firstlabel = fs->ls->dyd->label.n; | 438 | bl->firstlabel = fs->ls->dyd->label.n; |
| @@ -431,30 +444,48 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { | |||
| 431 | } | 444 | } |
| 432 | 445 | ||
| 433 | 446 | ||
| 447 | /* | ||
| 448 | ** create a label named "break" to resolve break statements | ||
| 449 | */ | ||
| 450 | static void breaklabel (LexState *ls) { | ||
| 451 | TString *n = luaS_new(ls->L, "break"); | ||
| 452 | int l = newlabelentry(ls, &ls->dyd->label, n, 0, ls->fs->pc); | ||
| 453 | findgotos(ls, &ls->dyd->label.arr[l]); | ||
| 454 | } | ||
| 455 | |||
| 456 | /* | ||
| 457 | ** generates an error for an undefined 'goto'; choose appropriate | ||
| 458 | ** message when label name is a resserved word (which can only be 'break') | ||
| 459 | */ | ||
| 460 | static void undefgoto (LexState *ls, Labeldesc *gt) { | ||
| 461 | const char *msg = (gt->name->tsv.reserved > 0) | ||
| 462 | ? "<%s> at line %d not inside a loop" | ||
| 463 | : "label " LUA_QS " (<goto> at line %d) undefined"; | ||
| 464 | msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); | ||
| 465 | semerror(ls, msg); | ||
| 466 | } | ||
| 467 | |||
| 468 | |||
| 434 | static void leaveblock (FuncState *fs) { | 469 | static void leaveblock (FuncState *fs) { |
| 435 | BlockCnt *bl = fs->bl; | 470 | BlockCnt *bl = fs->bl; |
| 436 | LexState *ls = fs->ls; | 471 | LexState *ls = fs->ls; |
| 437 | fs->bl = bl->previous; | ||
| 438 | removevars(fs, bl->nactvar); | ||
| 439 | ls->dyd->label.n = bl->firstlabel; /* remove local labels */ | ||
| 440 | if (bl->previous) /* inner block? */ | ||
| 441 | movegotosout(fs, bl); /* update pending gotos to outer block */ | ||
| 442 | else if (bl->firstgoto < ls->dyd->gt.n) { /* pending gotos in outer block? */ | ||
| 443 | Labeldesc *gt = &ls->dyd->gt.arr[bl->firstgoto]; | ||
| 444 | const char *msg = luaO_pushfstring(ls->L, | ||
| 445 | "label " LUA_QS " (<goto> at line %d) undefined", | ||
| 446 | getstr(gt->name), gt->line); | ||
| 447 | semerror(ls, msg); | ||
| 448 | } | ||
| 449 | if (bl->previous && bl->upval) { | 472 | if (bl->previous && bl->upval) { |
| 450 | /* create a 'jump to here' to close upvalues */ | 473 | /* create a 'jump to here' to close upvalues */ |
| 451 | int j = luaK_jump(fs); | 474 | int j = luaK_jump(fs); |
| 452 | luaK_patchclose(fs, j, bl->nactvar); | 475 | luaK_patchclose(fs, j, bl->nactvar); |
| 453 | luaK_patchtohere(fs, j); | 476 | luaK_patchtohere(fs, j); |
| 454 | } | 477 | } |
| 478 | if (bl->isloop) | ||
| 479 | breaklabel(ls); /* close pending breaks */ | ||
| 480 | fs->bl = bl->previous; | ||
| 481 | removevars(fs, bl->nactvar); | ||
| 455 | lua_assert(bl->nactvar == fs->nactvar); | 482 | lua_assert(bl->nactvar == fs->nactvar); |
| 456 | fs->freereg = fs->nactvar; /* free registers */ | 483 | fs->freereg = fs->nactvar; /* free registers */ |
| 457 | luaK_patchtohere(fs, bl->breaklist); | 484 | ls->dyd->label.n = bl->firstlabel; /* remove local labels */ |
| 485 | if (bl->previous) /* inner block? */ | ||
| 486 | movegotosout(fs, bl); /* update pending gotos to outer block */ | ||
| 487 | else if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ | ||
| 488 | undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ | ||
| 458 | } | 489 | } |
| 459 | 490 | ||
| 460 | 491 | ||
| @@ -1070,7 +1101,6 @@ static void block (LexState *ls) { | |||
| 1070 | BlockCnt bl; | 1101 | BlockCnt bl; |
| 1071 | enterblock(fs, &bl, 0); | 1102 | enterblock(fs, &bl, 0); |
| 1072 | statlist(ls); | 1103 | statlist(ls); |
| 1073 | lua_assert(bl.breaklist == NO_JUMP); | ||
| 1074 | leaveblock(fs); | 1104 | leaveblock(fs); |
| 1075 | } | 1105 | } |
| 1076 | 1106 | ||
| @@ -1159,44 +1189,6 @@ static int cond (LexState *ls) { | |||
| 1159 | } | 1189 | } |
| 1160 | 1190 | ||
| 1161 | 1191 | ||
| 1162 | /* code a break statement. The last 'if' decides the need to close | ||
| 1163 | upvalues when leaving the block. If the block has upvalues, it | ||
| 1164 | must be closed. If it has local variables and any label | ||
| 1165 | before the break, those variables must be closed too, as they | ||
| 1166 | may be used as upvalues after the break and through a goto | ||
| 1167 | be exited through this break. | ||
| 1168 | */ | ||
| 1169 | static void breakstat (LexState *ls) { | ||
| 1170 | FuncState *fs = ls->fs; | ||
| 1171 | BlockCnt *bl = fs->bl; | ||
| 1172 | int upval = 0; | ||
| 1173 | while (bl && !bl->isloop) { | ||
| 1174 | upval |= bl->upval; | ||
| 1175 | bl = bl->previous; | ||
| 1176 | } | ||
| 1177 | if (!bl) | ||
| 1178 | semerror(ls, "no loop to break"); | ||
| 1179 | luaK_concat(fs, &bl->breaklist, luaK_jump(fs)); | ||
| 1180 | if (upval || | ||
| 1181 | (fs->nactvar > bl->nactvar && | ||
| 1182 | ls->dyd->label.n > bl->firstlabel)) | ||
| 1183 | luaK_patchclose(fs, bl->breaklist, bl->nactvar); | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | |||
| 1187 | static int newlabelentry (LexState *ls, Labellist *l, TString *name, | ||
| 1188 | int line, int pc) { | ||
| 1189 | int n = l->n; | ||
| 1190 | luaM_growvector(ls->L, l->arr, l->n, l->size, Labeldesc, MAX_INT, "labels"); | ||
| 1191 | l->arr[n].name = name; | ||
| 1192 | l->arr[n].line = line; | ||
| 1193 | l->arr[n].nactvar = ls->fs->nactvar; | ||
| 1194 | l->arr[n].pc = pc; | ||
| 1195 | l->n++; | ||
| 1196 | return n; | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | |||
| 1200 | static void gotostat (LexState *ls, TString *label, int line) { | 1192 | static void gotostat (LexState *ls, TString *label, int line) { |
| 1201 | /* create new entry for this goto */ | 1193 | /* create new entry for this goto */ |
| 1202 | int g = newlabelentry(ls, &ls->dyd->gt, label, line, luaK_jump(ls->fs)); | 1194 | int g = newlabelentry(ls, &ls->dyd->gt, label, line, luaK_jump(ls->fs)); |
| @@ -1551,7 +1543,8 @@ static int statement (LexState *ls) { | |||
| 1551 | } | 1543 | } |
| 1552 | case TK_BREAK: { /* stat -> breakstat */ | 1544 | case TK_BREAK: { /* stat -> breakstat */ |
| 1553 | luaX_next(ls); /* skip BREAK */ | 1545 | luaX_next(ls); /* skip BREAK */ |
| 1554 | breakstat(ls); | 1546 | /* code it as "goto 'break'" */ |
| 1547 | gotostat(ls, luaS_new(ls->L, "break"), line); | ||
| 1555 | return 1; /* must be last statement */ | 1548 | return 1; /* must be last statement */ |
| 1556 | } | 1549 | } |
| 1557 | case TK_GOTO: { /* stat -> 'goto' NAME */ | 1550 | case TK_GOTO: { /* stat -> 'goto' NAME */ |
