aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lparser.c84
-rw-r--r--testes/code.lua20
-rw-r--r--testes/goto.lua16
3 files changed, 100 insertions, 20 deletions
diff --git a/lparser.c b/lparser.c
index 499e9531..9419f880 100644
--- a/lparser.c
+++ b/lparser.c
@@ -113,7 +113,7 @@ static void checknext (LexState *ls, int c) {
113 113
114 114
115static void check_match (LexState *ls, int what, int who, int where) { 115static void check_match (LexState *ls, int what, int who, int where) {
116 if (!testnext(ls, what)) { 116 if (unlikely(!testnext(ls, what))) {
117 if (where == ls->linenumber) 117 if (where == ls->linenumber)
118 error_expected(ls, what); 118 error_expected(ls, what);
119 else { 119 else {
@@ -350,7 +350,7 @@ static void solvegoto (LexState *ls, int g, Labeldesc *label) {
350 Labellist *gl = &ls->dyd->gt; /* list of goto's */ 350 Labellist *gl = &ls->dyd->gt; /* list of goto's */
351 Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */ 351 Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
352 lua_assert(eqstr(gt->name, label->name)); 352 lua_assert(eqstr(gt->name, label->name));
353 if (gt->nactvar < label->nactvar) /* enter some scope? */ 353 if (unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */
354 jumpscopeerror(ls, gt); 354 jumpscopeerror(ls, gt);
355 luaK_patchlist(ls->fs, gt->pc, label->pc); 355 luaK_patchlist(ls->fs, gt->pc, label->pc);
356 for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */ 356 for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */
@@ -393,6 +393,11 @@ static int newlabelentry (LexState *ls, Labellist *l, TString *name,
393} 393}
394 394
395 395
396static int newgotoentry (LexState *ls, TString *name, int line, int pc) {
397 return newlabelentry(ls, &ls->dyd->gt, name, line, pc);
398}
399
400
396/* 401/*
397** Solves forward jumps. Check whether new label 'lb' matches any 402** Solves forward jumps. Check whether new label 'lb' matches any
398** pending gotos in current block and solves them. Return true 403** pending gotos in current block and solves them. Return true
@@ -471,8 +476,15 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
471** generates an error for an undefined 'goto'. 476** generates an error for an undefined 'goto'.
472*/ 477*/
473static l_noret undefgoto (LexState *ls, Labeldesc *gt) { 478static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
474 const char *msg = "no visible label '%s' for <goto> at line %d"; 479 const char *msg;
475 msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); 480 if (eqstr(gt->name, luaS_newliteral(ls->L, "break"))) {
481 msg = "break outside loop at line %d";
482 msg = luaO_pushfstring(ls->L, msg, gt->line);
483 }
484 else {
485 msg = "no visible label '%s' for <goto> at line %d";
486 msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line);
487 }
476 luaK_semerror(ls, msg); 488 luaK_semerror(ls, msg);
477} 489}
478 490
@@ -1212,7 +1224,7 @@ static void gotostat (LexState *ls) {
1212 Labeldesc *lb = findlabel(ls, name); 1224 Labeldesc *lb = findlabel(ls, name);
1213 if (lb == NULL) /* no label? */ 1225 if (lb == NULL) /* no label? */
1214 /* forward jump; will be resolved when the label is declared */ 1226 /* forward jump; will be resolved when the label is declared */
1215 newlabelentry(ls, &ls->dyd->gt, name, line, luaK_jump(fs)); 1227 newgotoentry(ls, name, line, luaK_jump(fs));
1216 else { /* found a label */ 1228 else { /* found a label */
1217 /* backward jump; will be resolved here */ 1229 /* backward jump; will be resolved here */
1218 if (fs->nactvar > lb->nactvar) /* leaving the scope of some variable? */ 1230 if (fs->nactvar > lb->nactvar) /* leaving the scope of some variable? */
@@ -1226,15 +1238,10 @@ static void gotostat (LexState *ls) {
1226/* 1238/*
1227** Break statement. Semantically equivalent to "goto break". 1239** Break statement. Semantically equivalent to "goto break".
1228*/ 1240*/
1229static void breakstat (LexState *ls, int pc) { 1241static void breakstat (LexState *ls) {
1230 FuncState *fs = ls->fs;
1231 int line = ls->linenumber; 1242 int line = ls->linenumber;
1232 BlockCnt *bl = fs->bl;
1233 luaX_next(ls); /* skip break */ 1243 luaX_next(ls); /* skip break */
1234 newlabelentry(ls, &ls->dyd->gt, luaS_newliteral(ls->L, "break"), line, pc); 1244 newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, luaK_jump(ls->fs));
1235 while (bl && !bl->isloop) { bl = bl->previous; }
1236 if (!bl)
1237 luaX_syntaxerror(ls, "no loop to break");
1238} 1245}
1239 1246
1240 1247
@@ -1243,7 +1250,7 @@ static void breakstat (LexState *ls, int pc) {
1243*/ 1250*/
1244static void checkrepeated (LexState *ls, TString *name) { 1251static void checkrepeated (LexState *ls, TString *name) {
1245 Labeldesc *lb = findlabel(ls, name); 1252 Labeldesc *lb = findlabel(ls, name);
1246 if (lb != NULL) { /* already defined? */ 1253 if (unlikely(lb != NULL)) { /* already defined? */
1247 const char *msg = "label '%s' already defined on line %d"; 1254 const char *msg = "label '%s' already defined on line %d";
1248 msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line); 1255 msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line);
1249 luaK_semerror(ls, msg); /* error */ 1256 luaK_semerror(ls, msg); /* error */
@@ -1332,7 +1339,7 @@ static void fixforjump (FuncState *fs, int pc, int dest, int back) {
1332 int offset = dest - (pc + 1); 1339 int offset = dest - (pc + 1);
1333 if (back) 1340 if (back)
1334 offset = -offset; 1341 offset = -offset;
1335 if (offset > MAXARG_Bx) 1342 if (unlikely(offset > MAXARG_Bx))
1336 luaX_syntaxerror(fs->ls, "control structure too long"); 1343 luaX_syntaxerror(fs->ls, "control structure too long");
1337 SETARG_Bx(*jmp, offset); 1344 SETARG_Bx(*jmp, offset);
1338} 1345}
@@ -1439,28 +1446,67 @@ static void forstat (LexState *ls, int line) {
1439} 1446}
1440 1447
1441 1448
1449/*
1450** Check whether next instruction is a single jump (a 'break', a 'goto'
1451** to a forward label, or a 'goto' to a backward label with no variable
1452** to close). If so, set the name of the 'label' it is jumping to
1453** ("break" for a 'break') or to where it is jumping to ('target') and
1454** return true. If not a single jump, leave input unchanged, to be
1455** handled as a regular statement.
1456*/
1457static int issinglejump (LexState *ls, TString **label, int *target) {
1458 if (testnext(ls, TK_BREAK)) { /* a break? */
1459 *label = luaS_newliteral(ls->L, "break");
1460 return 1;
1461 }
1462 else if (ls->t.token != TK_GOTO || luaX_lookahead(ls) != TK_NAME)
1463 return 0; /* not a valid goto */
1464 else {
1465 TString *lname = ls->lookahead.seminfo.ts; /* label's id */
1466 Labeldesc *lb = findlabel(ls, lname);
1467 if (lb) { /* a backward jump? */
1468 if (ls->fs->nactvar > lb->nactvar) /* needs to close variables? */
1469 return 0; /* not a single jump; cannot optimize */
1470 *target = lb->pc;
1471 }
1472 else /* jump forward */
1473 *label = lname;
1474 luaX_next(ls); /* skip goto */
1475 luaX_next(ls); /* skip name */
1476 return 1;
1477 }
1478}
1479
1480
1442static void test_then_block (LexState *ls, int *escapelist) { 1481static void test_then_block (LexState *ls, int *escapelist) {
1443 /* test_then_block -> [IF | ELSEIF] cond THEN block */ 1482 /* test_then_block -> [IF | ELSEIF] cond THEN block */
1444 BlockCnt bl; 1483 BlockCnt bl;
1484 int line;
1445 FuncState *fs = ls->fs; 1485 FuncState *fs = ls->fs;
1486 TString *jlb = NULL;
1487 int target = NO_JUMP;
1446 expdesc v; 1488 expdesc v;
1447 int jf; /* instruction to skip 'then' code (if condition is false) */ 1489 int jf; /* instruction to skip 'then' code (if condition is false) */
1448 luaX_next(ls); /* skip IF or ELSEIF */ 1490 luaX_next(ls); /* skip IF or ELSEIF */
1449 expr(ls, &v); /* read condition */ 1491 expr(ls, &v); /* read condition */
1450 checknext(ls, TK_THEN); 1492 checknext(ls, TK_THEN);
1451 if (ls->t.token == TK_BREAK) { 1493 line = ls->linenumber;
1494 if (issinglejump(ls, &jlb, &target)) { /* 'if x then goto' ? */
1452 luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ 1495 luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */
1453 enterblock(fs, &bl, 0); /* must enter block before 'goto' */ 1496 enterblock(fs, &bl, 0); /* must enter block before 'goto' */
1454 breakstat(ls, v.t); /* handle break */ 1497 if (jlb != NULL) /* forward jump? */
1498 newgotoentry(ls, jlb, line, v.t); /* will be resolved later */
1499 else /* backward jump */
1500 luaK_patchlist(fs, v.t, target); /* jump directly to 'target' */
1455 while (testnext(ls, ';')) {} /* skip semicolons */ 1501 while (testnext(ls, ';')) {} /* skip semicolons */
1456 if (block_follow(ls, 0)) { /* 'break' is the entire block? */ 1502 if (block_follow(ls, 0)) { /* jump is the entire block? */
1457 leaveblock(fs); 1503 leaveblock(fs);
1458 return; /* and that is it */ 1504 return; /* and that is it */
1459 } 1505 }
1460 else /* must skip over 'then' part if condition is false */ 1506 else /* must skip over 'then' part if condition is false */
1461 jf = luaK_jump(fs); 1507 jf = luaK_jump(fs);
1462 } 1508 }
1463 else { /* regular case (not goto/break) */ 1509 else { /* regular case (not a jump) */
1464 luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */ 1510 luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */
1465 enterblock(fs, &bl, 0); 1511 enterblock(fs, &bl, 0);
1466 jf = v.f; 1512 jf = v.f;
@@ -1671,7 +1717,7 @@ static void statement (LexState *ls) {
1671 break; 1717 break;
1672 } 1718 }
1673 case TK_BREAK: { /* stat -> breakstat */ 1719 case TK_BREAK: { /* stat -> breakstat */
1674 breakstat(ls, luaK_jump(ls->fs)); 1720 breakstat(ls);
1675 break; 1721 break;
1676 } 1722 }
1677 case TK_GOTO: { /* stat -> 'goto' NAME */ 1723 case TK_GOTO: { /* stat -> 'goto' NAME */
diff --git a/testes/code.lua b/testes/code.lua
index 9b3f2b68..4d44fa6a 100644
--- a/testes/code.lua
+++ b/testes/code.lua
@@ -339,8 +339,26 @@ check(function (a, b)
339 339
340checkequal( 340checkequal(
341function (a) while a < 10 do a = a + 1 end end, 341function (a) while a < 10 do a = a + 1 end end,
342function (a) while true do if not(a < 10) then break end; a = a + 1; end end 342function (a)
343 ::loop::
344 if not (a < 10) then goto exit end
345 a = a + 1
346 goto loop
347::exit::
348end
343) 349)
344 350
351checkequal(
352function (a) repeat local x = a + 1; a = x until a > 0 end,
353function (a)
354 ::loop:: do
355 local x = a + 1
356 a = x
357 end
358 if not (a > 0) then goto loop end
359end
360)
361
362
345print 'OK' 363print 'OK'
346 364
diff --git a/testes/goto.lua b/testes/goto.lua
index 85038d02..92f048fb 100644
--- a/testes/goto.lua
+++ b/testes/goto.lua
@@ -249,6 +249,22 @@ assert(testG(2) == "2")
249assert(testG(3) == "3") 249assert(testG(3) == "3")
250assert(testG(4) == 5) 250assert(testG(4) == 5)
251assert(testG(5) == 10) 251assert(testG(5) == 10)
252
253do
254 -- if x back goto out of scope of upvalue
255 local X
256 goto L1
257
258 ::L2:: goto L3
259
260 ::L1:: do
261 local scoped a = function () X = true end
262 assert(X == nil)
263 if a then goto L2 end -- jumping back out of scope of 'a'
264 end
265
266 ::L3:: assert(X == true) -- checks that 'a' was correctly closed
267end
252-------------------------------------------------------------------------------- 268--------------------------------------------------------------------------------
253 269
254 270