diff options
-rw-r--r-- | lparser.c | 84 | ||||
-rw-r--r-- | testes/code.lua | 20 | ||||
-rw-r--r-- | testes/goto.lua | 16 |
3 files changed, 100 insertions, 20 deletions
@@ -113,7 +113,7 @@ static void checknext (LexState *ls, int c) { | |||
113 | 113 | ||
114 | 114 | ||
115 | static void check_match (LexState *ls, int what, int who, int where) { | 115 | static 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 | ||
396 | static 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 | */ |
473 | static l_noret undefgoto (LexState *ls, Labeldesc *gt) { | 478 | static 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 | */ |
1229 | static void breakstat (LexState *ls, int pc) { | 1241 | static 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 | */ |
1244 | static void checkrepeated (LexState *ls, TString *name) { | 1251 | static 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 | */ | ||
1457 | static 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 | |||
1442 | static void test_then_block (LexState *ls, int *escapelist) { | 1481 | static 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 | ||
340 | checkequal( | 340 | checkequal( |
341 | function (a) while a < 10 do a = a + 1 end end, | 341 | function (a) while a < 10 do a = a + 1 end end, |
342 | function (a) while true do if not(a < 10) then break end; a = a + 1; end end | 342 | function (a) |
343 | ::loop:: | ||
344 | if not (a < 10) then goto exit end | ||
345 | a = a + 1 | ||
346 | goto loop | ||
347 | ::exit:: | ||
348 | end | ||
343 | ) | 349 | ) |
344 | 350 | ||
351 | checkequal( | ||
352 | function (a) repeat local x = a + 1; a = x until a > 0 end, | ||
353 | function (a) | ||
354 | ::loop:: do | ||
355 | local x = a + 1 | ||
356 | a = x | ||
357 | end | ||
358 | if not (a > 0) then goto loop end | ||
359 | end | ||
360 | ) | ||
361 | |||
362 | |||
345 | print 'OK' | 363 | print '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") | |||
249 | assert(testG(3) == "3") | 249 | assert(testG(3) == "3") |
250 | assert(testG(4) == 5) | 250 | assert(testG(4) == 5) |
251 | assert(testG(5) == 10) | 251 | assert(testG(5) == 10) |
252 | |||
253 | do | ||
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 | ||
267 | end | ||
252 | -------------------------------------------------------------------------------- | 268 | -------------------------------------------------------------------------------- |
253 | 269 | ||
254 | 270 | ||