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 */ |