aboutsummaryrefslogtreecommitdiff
path: root/lparser.c
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-29 14:26:48 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-29 14:26:48 -0300
commita006514ea138a29b6031058d9002b48a572b5dd6 (patch)
treeb289a8af0c0497f2555784a0cf666659ceab0236 /lparser.c
parent6e9b719694bffb8de711f182d405ec37d32ae0b1 (diff)
downloadlua-a006514ea138a29b6031058d9002b48a572b5dd6.tar.gz
lua-a006514ea138a29b6031058d9002b48a572b5dd6.tar.bz2
lua-a006514ea138a29b6031058d9002b48a572b5dd6.zip
Big revamp in the implmentation of labels/gotos
Added restriction that, when a label is created, there cannot be another label with the same name visible. That allows backward goto's to be resolved when they are read. Backward goto's get a close if they jump out of the scope of some variable; labels get a close only if previous goto to it jumps out of the scope of some upvalue.
Diffstat (limited to 'lparser.c')
-rw-r--r--lparser.c241
1 files changed, 117 insertions, 124 deletions
diff --git a/lparser.c b/lparser.c
index c3dc6362..499e9531 100644
--- a/lparser.c
+++ b/lparser.c
@@ -49,8 +49,6 @@ typedef struct BlockCnt {
49 struct BlockCnt *previous; /* chain */ 49 struct BlockCnt *previous; /* chain */
50 int firstlabel; /* index of first label in this block */ 50 int firstlabel; /* index of first label in this block */
51 int firstgoto; /* index of first pending goto in this block */ 51 int firstgoto; /* index of first pending goto in this block */
52 int brks; /* list of break jumps in this block */
53 lu_byte brkcls; /* true if some 'break' needs to close upvalues */
54 lu_byte nactvar; /* # active locals outside the block */ 52 lu_byte nactvar; /* # active locals outside the block */
55 lu_byte upval; /* true if some variable in the block is an upvalue */ 53 lu_byte upval; /* true if some variable in the block is an upvalue */
56 lu_byte isloop; /* true if 'block' is a loop */ 54 lu_byte isloop; /* true if 'block' is a loop */
@@ -330,50 +328,56 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
330#define leavelevel(ls) ((ls)->L->nCcalls--) 328#define leavelevel(ls) ((ls)->L->nCcalls--)
331 329
332 330
333static void closegoto (LexState *ls, int g, Labeldesc *label) { 331/*
332** Generates an error that a goto jumps into the scope of some
333** local variable.
334*/
335static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
336 const char *varname = getstr(getlocvar(ls->fs, gt->nactvar)->varname);
337 const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'";
338 msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname);
339 luaK_semerror(ls, msg); /* raise the error */
340}
341
342
343/*
344** Solves the goto at index 'g' to given 'label' and removes it
345** from the list of pending goto's.
346** If it jumps into the scope of some variable, raises an error.
347*/
348static void solvegoto (LexState *ls, int g, Labeldesc *label) {
334 int i; 349 int i;
335 FuncState *fs = ls->fs; 350 Labellist *gl = &ls->dyd->gt; /* list of goto's */
336 Labellist *gl = &ls->dyd->gt; 351 Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
337 Labeldesc *gt = &gl->arr[g];
338 lua_assert(eqstr(gt->name, label->name)); 352 lua_assert(eqstr(gt->name, label->name));
339 if (gt->nactvar < label->nactvar) { 353 if (gt->nactvar < label->nactvar) /* enter some scope? */
340 TString *vname = getlocvar(fs, gt->nactvar)->varname; 354 jumpscopeerror(ls, gt);
341 const char *msg = luaO_pushfstring(ls->L, 355 luaK_patchlist(ls->fs, gt->pc, label->pc);
342 "<goto %s> at line %d jumps into the scope of local '%s'", 356 for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */
343 getstr(gt->name), gt->line, getstr(vname));
344 luaK_semerror(ls, msg);
345 }
346 luaK_patchgoto(fs, gt->pc, label->pc, 1);
347 /* remove goto from pending list */
348 for (i = g; i < gl->n - 1; i++)
349 gl->arr[i] = gl->arr[i + 1]; 357 gl->arr[i] = gl->arr[i + 1];
350 gl->n--; 358 gl->n--;
351} 359}
352 360
353 361
354/* 362/*
355** try to close a goto with existing labels; this solves backward jumps 363** Search for an active label with the given name.
356*/ 364*/
357static int solvelabel (LexState *ls, int g) { 365static Labeldesc *findlabel (LexState *ls, TString *name) {
358 int i; 366 int i;
359 BlockCnt *bl = ls->fs->bl;
360 Dyndata *dyd = ls->dyd; 367 Dyndata *dyd = ls->dyd;
361 Labeldesc *gt = &dyd->gt.arr[g]; 368 /* check labels in current function for a match */
362 /* check labels in current block for a match */ 369 for (i = ls->fs->firstlabel; i < dyd->label.n; i++) {
363 for (i = bl->firstlabel; i < dyd->label.n; i++) {
364 Labeldesc *lb = &dyd->label.arr[i]; 370 Labeldesc *lb = &dyd->label.arr[i];
365 if (eqstr(lb->name, gt->name)) { /* correct label? */ 371 if (eqstr(lb->name, name)) /* correct label? */
366 if (gt->nactvar > lb->nactvar && 372 return lb;
367 (bl->upval || dyd->label.n > bl->firstlabel))
368 luaK_patchclose(ls->fs, gt->pc);
369 closegoto(ls, g, lb); /* close it */
370 return 1;
371 }
372 } 373 }
373 return 0; /* label not found; cannot close goto */ 374 return NULL; /* label not found */
374} 375}
375 376
376 377
378/*
379** Adds a new label/goto in the corresponding list.
380*/
377static int newlabelentry (LexState *ls, Labellist *l, TString *name, 381static int newlabelentry (LexState *ls, Labellist *l, TString *name,
378 int line, int pc) { 382 int line, int pc) {
379 int n = l->n; 383 int n = l->n;
@@ -382,6 +386,7 @@ static int newlabelentry (LexState *ls, Labellist *l, TString *name,
382 l->arr[n].name = name; 386 l->arr[n].name = name;
383 l->arr[n].line = line; 387 l->arr[n].line = line;
384 l->arr[n].nactvar = ls->fs->nactvar; 388 l->arr[n].nactvar = ls->fs->nactvar;
389 l->arr[n].close = 0;
385 l->arr[n].pc = pc; 390 l->arr[n].pc = pc;
386 l->n = n + 1; 391 l->n = n + 1;
387 return n; 392 return n;
@@ -389,51 +394,64 @@ static int newlabelentry (LexState *ls, Labellist *l, TString *name,
389 394
390 395
391/* 396/*
392** check whether new label 'lb' matches any pending gotos in current 397** Solves forward jumps. Check whether new label 'lb' matches any
393** block; solves forward jumps 398** pending gotos in current block and solves them. Return true
399** if any of the goto's need to close upvalues.
394*/ 400*/
395static void solvegotos (LexState *ls, Labeldesc *lb) { 401static int solvegotos (LexState *ls, Labeldesc *lb) {
396 Labellist *gl = &ls->dyd->gt; 402 Labellist *gl = &ls->dyd->gt;
397 int i = ls->fs->bl->firstgoto; 403 int i = ls->fs->bl->firstgoto;
404 int needsclose = 0;
398 while (i < gl->n) { 405 while (i < gl->n) {
399 if (eqstr(gl->arr[i].name, lb->name)) 406 if (eqstr(gl->arr[i].name, lb->name)) {
400 closegoto(ls, i, lb); /* will remove 'i' from the list */ 407 needsclose |= gl->arr[i].close;
408 solvegoto(ls, i, lb); /* will remove 'i' from the list */
409 }
401 else 410 else
402 i++; 411 i++;
403 } 412 }
413 return needsclose;
414}
415
416
417/*
418** Create a new label with the given 'name' at the given 'line'.
419** 'last' tells whether label is the last non-op statement in its
420** block. Solves all pending goto's to this new label and adds
421** a close instruction if necessary.
422** Returns true iff it added a close instruction.
423*/
424static int createlabel (LexState *ls, TString *name, int line,
425 int last) {
426 FuncState *fs = ls->fs;
427 Labellist *ll = &ls->dyd->label;
428 int l = newlabelentry(ls, ll, name, line, luaK_getlabel(fs));
429 if (last) { /* label is last no-op statement in the block? */
430 /* assume that locals are already out of scope */
431 ll->arr[l].nactvar = fs->bl->nactvar;
432 }
433 if (solvegotos(ls, &ll->arr[l])) { /* need close? */
434 luaK_codeABC(fs, OP_CLOSE, fs->nactvar, 0, 0);
435 return 1;
436 }
437 return 0;
404} 438}
405 439
406 440
407/* 441/*
408** export pending gotos to outer level, to check them against 442** Adjust pending gotos to outer level of a block.
409** outer labels; if the block being exited has upvalues, and
410** the goto exits the scope of any variable (which can be the
411** upvalue), close those variables being exited. Also export
412** break list.
413*/ 443*/
414static void movegotosout (FuncState *fs, BlockCnt *bl) { 444static void movegotosout (FuncState *fs, BlockCnt *bl) {
415 int i = bl->firstgoto; 445 int i;
416 Labellist *gl = &fs->ls->dyd->gt; 446 Labellist *gl = &fs->ls->dyd->gt;
417 /* correct pending gotos to current block and try to close it 447 /* correct pending gotos to current block */
418 with visible labels */ 448 for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */
419 while (i < gl->n) { /* for each pending goto */
420 Labeldesc *gt = &gl->arr[i]; 449 Labeldesc *gt = &gl->arr[i];
421 if (gt->nactvar > bl->nactvar) { /* leaving a variable scope? */ 450 if (gt->nactvar > bl->nactvar) { /* leaving a variable scope? */
422 if (bl->upval) /* variable may be an upvalue? */
423 luaK_patchclose(fs, gt->pc); /* jump will need a close */
424 gt->nactvar = bl->nactvar; /* update goto level */ 451 gt->nactvar = bl->nactvar; /* update goto level */
452 gt->close |= bl->upval; /* jump may need a close */
425 } 453 }
426 if (!solvelabel(fs->ls, i))
427 i++; /* move to next one */
428 /* else, 'solvelabel' removed current goto from the list
429 and 'i' now points to next one */
430 } 454 }
431 /* handles break list */
432 if (bl->upval) /* exiting the scope of an upvalue? */
433 luaK_patchclose(fs, bl->brks); /* breaks will need OP_CLOSE */
434 /* move breaks to outer block */
435 luaK_concat(fs, &bl->previous->brks, bl->brks);
436 bl->previous->brkcls |= bl->brkcls;
437} 455}
438 456
439 457
@@ -442,8 +460,6 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
442 bl->nactvar = fs->nactvar; 460 bl->nactvar = fs->nactvar;
443 bl->firstlabel = fs->ls->dyd->label.n; 461 bl->firstlabel = fs->ls->dyd->label.n;
444 bl->firstgoto = fs->ls->dyd->gt.n; 462 bl->firstgoto = fs->ls->dyd->gt.n;
445 bl->brks = NO_JUMP;
446 bl->brkcls = 0;
447 bl->upval = 0; 463 bl->upval = 0;
448 bl->previous = fs->bl; 464 bl->previous = fs->bl;
449 fs->bl = bl; 465 fs->bl = bl;
@@ -452,20 +468,6 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
452 468
453 469
454/* 470/*
455** Fix all breaks in block 'bl' to jump to the end of the block.
456*/
457static void fixbreaks (FuncState *fs, BlockCnt *bl) {
458 int target = fs->pc;
459 if (bl->brkcls) /* does the block need to close upvalues? */
460 luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
461 luaK_patchgoto(fs, bl->brks, target, bl->brkcls);
462 bl->brks = NO_JUMP; /* no more breaks to fix */
463 bl->brkcls = 0; /* no more need to close upvalues */
464 lua_assert(!bl->upval); /* loop body cannot have local variables */
465}
466
467
468/*
469** generates an error for an undefined 'goto'. 471** generates an error for an undefined 'goto'.
470*/ 472*/
471static l_noret undefgoto (LexState *ls, Labeldesc *gt) { 473static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
@@ -478,11 +480,10 @@ static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
478static void leaveblock (FuncState *fs) { 480static void leaveblock (FuncState *fs) {
479 BlockCnt *bl = fs->bl; 481 BlockCnt *bl = fs->bl;
480 LexState *ls = fs->ls; 482 LexState *ls = fs->ls;
481 if (bl->upval && bl->brks != NO_JUMP) /* breaks in upvalue scopes? */ 483 int hasclose = 0;
482 bl->brkcls = 1; /* these breaks must close the upvalues */ 484 if (bl->isloop) /* fix pending breaks? */
483 if (bl->isloop) 485 hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
484 fixbreaks(fs, bl); /* fix pending breaks */ 486 if (!hasclose && bl->previous && bl->upval)
485 if (bl->previous && bl->upval)
486 luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); 487 luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
487 fs->bl = bl->previous; 488 fs->bl = bl->previous;
488 removevars(fs, bl->nactvar); 489 removevars(fs, bl->nactvar);
@@ -492,7 +493,6 @@ static void leaveblock (FuncState *fs) {
492 if (bl->previous) /* inner block? */ 493 if (bl->previous) /* inner block? */
493 movegotosout(fs, bl); /* update pending gotos to outer block */ 494 movegotosout(fs, bl); /* update pending gotos to outer block */
494 else { 495 else {
495 lua_assert(bl->brks == NO_JUMP); /* no pending breaks */
496 if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ 496 if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */
497 undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ 497 undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */
498 } 498 }
@@ -550,6 +550,7 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
550 fs->nactvar = 0; 550 fs->nactvar = 0;
551 fs->needclose = 0; 551 fs->needclose = 0;
552 fs->firstlocal = ls->dyd->actvar.n; 552 fs->firstlocal = ls->dyd->actvar.n;
553 fs->firstlabel = ls->dyd->label.n;
553 fs->bl = NULL; 554 fs->bl = NULL;
554 f->source = ls->source; 555 f->source = ls->source;
555 f->maxstacksize = 2; /* registers 0/1 are always valid */ 556 f->maxstacksize = 2; /* registers 0/1 are always valid */
@@ -1204,63 +1205,59 @@ static int cond (LexState *ls) {
1204} 1205}
1205 1206
1206 1207
1207static void gotostat (LexState *ls, int pc) { 1208static void gotostat (LexState *ls) {
1209 FuncState *fs = ls->fs;
1208 int line = ls->linenumber; 1210 int line = ls->linenumber;
1209 int g; 1211 TString *name = str_checkname(ls); /* label's name */
1210 luaX_next(ls); /* skip 'goto' */ 1212 Labeldesc *lb = findlabel(ls, name);
1211 g = newlabelentry(ls, &ls->dyd->gt, str_checkname(ls), line, pc); 1213 if (lb == NULL) /* no label? */
1212 solvelabel(ls, g); /* close it if label already defined */ 1214 /* forward jump; will be resolved when the label is declared */
1215 newlabelentry(ls, &ls->dyd->gt, name, line, luaK_jump(fs));
1216 else { /* found a label */
1217 /* backward jump; will be resolved here */
1218 if (fs->nactvar > lb->nactvar) /* leaving the scope of some variable? */
1219 luaK_codeABC(fs, OP_CLOSE, lb->nactvar, 0, 0);
1220 /* create jump and link it to the label */
1221 luaK_patchlist(fs, luaK_jump(fs), lb->pc);
1222 }
1213} 1223}
1214 1224
1215 1225
1226/*
1227** Break statement. Semantically equivalent to "goto break".
1228*/
1216static void breakstat (LexState *ls, int pc) { 1229static void breakstat (LexState *ls, int pc) {
1217 FuncState *fs = ls->fs; 1230 FuncState *fs = ls->fs;
1231 int line = ls->linenumber;
1218 BlockCnt *bl = fs->bl; 1232 BlockCnt *bl = fs->bl;
1219 luaX_next(ls); /* skip break */ 1233 luaX_next(ls); /* skip break */
1234 newlabelentry(ls, &ls->dyd->gt, luaS_newliteral(ls->L, "break"), line, pc);
1220 while (bl && !bl->isloop) { bl = bl->previous; } 1235 while (bl && !bl->isloop) { bl = bl->previous; }
1221 if (!bl) 1236 if (!bl)
1222 luaX_syntaxerror(ls, "no loop to break"); 1237 luaX_syntaxerror(ls, "no loop to break");
1223 luaK_concat(fs, &fs->bl->brks, pc);
1224} 1238}
1225 1239
1226 1240
1227/* check for repeated labels on the same block */ 1241/*
1228static void checkrepeated (FuncState *fs, Labellist *ll, TString *label) { 1242** Check whether there is already a label with the given 'name'.
1229 int i; 1243*/
1230 for (i = fs->bl->firstlabel; i < ll->n; i++) { 1244static void checkrepeated (LexState *ls, TString *name) {
1231 if (eqstr(label, ll->arr[i].name)) { 1245 Labeldesc *lb = findlabel(ls, name);
1232 const char *msg = luaO_pushfstring(fs->ls->L, 1246 if (lb != NULL) { /* already defined? */
1233 "label '%s' already defined on line %d", 1247 const char *msg = "label '%s' already defined on line %d";
1234 getstr(label), ll->arr[i].line); 1248 msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line);
1235 luaK_semerror(fs->ls, msg); 1249 luaK_semerror(ls, msg); /* error */
1236 }
1237 } 1250 }
1238} 1251}
1239 1252
1240 1253
1241/* skip no-op statements */ 1254static void labelstat (LexState *ls, TString *name, int line) {
1242static void skipnoopstat (LexState *ls) {
1243 while (ls->t.token == ';' || ls->t.token == TK_DBCOLON)
1244 statement(ls);
1245}
1246
1247
1248static void labelstat (LexState *ls, TString *label, int line) {
1249 /* label -> '::' NAME '::' */ 1255 /* label -> '::' NAME '::' */
1250 FuncState *fs = ls->fs;
1251 Labellist *ll = &ls->dyd->label;
1252 int l; /* index of new label being created */
1253 checkrepeated(fs, ll, label); /* check for repeated labels */
1254 checknext(ls, TK_DBCOLON); /* skip double colon */ 1256 checknext(ls, TK_DBCOLON); /* skip double colon */
1255 /* create new entry for this label */ 1257 while (ls->t.token == ';' || ls->t.token == TK_DBCOLON)
1256 l = newlabelentry(ls, ll, label, line, luaK_getlabel(fs)); 1258 statement(ls); /* skip other no-op statements */
1257 luaK_codeABC(fs, OP_CLOSE, fs->nactvar, 0, 0); 1259 checkrepeated(ls, name); /* check for repeated labels */
1258 skipnoopstat(ls); /* skip other no-op statements */ 1260 createlabel(ls, name, line, block_follow(ls, 0));
1259 if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */
1260 /* assume that locals are already out of scope */
1261 ll->arr[l].nactvar = fs->bl->nactvar;
1262 }
1263 solvegotos(ls, &ll->arr[l]);
1264} 1261}
1265 1262
1266 1263
@@ -1295,8 +1292,6 @@ static void repeatstat (LexState *ls, int line) {
1295 statlist(ls); 1292 statlist(ls);
1296 check_match(ls, TK_UNTIL, TK_REPEAT, line); 1293 check_match(ls, TK_UNTIL, TK_REPEAT, line);
1297 condexit = cond(ls); /* read condition (inside scope block) */ 1294 condexit = cond(ls); /* read condition (inside scope block) */
1298 if (bl2.upval) /* upvalues? */
1299 luaK_patchclose(fs, condexit);
1300 leaveblock(fs); /* finish scope */ 1295 leaveblock(fs); /* finish scope */
1301 if (bl2.upval) { /* upvalues? */ 1296 if (bl2.upval) { /* upvalues? */
1302 int exit = luaK_jump(fs); /* normal exit must jump over fix */ 1297 int exit = luaK_jump(fs); /* normal exit must jump over fix */
@@ -1453,15 +1448,12 @@ static void test_then_block (LexState *ls, int *escapelist) {
1453 luaX_next(ls); /* skip IF or ELSEIF */ 1448 luaX_next(ls); /* skip IF or ELSEIF */
1454 expr(ls, &v); /* read condition */ 1449 expr(ls, &v); /* read condition */
1455 checknext(ls, TK_THEN); 1450 checknext(ls, TK_THEN);
1456 if (ls->t.token == TK_GOTO || ls->t.token == TK_BREAK) { 1451 if (ls->t.token == TK_BREAK) {
1457 luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ 1452 luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */
1458 enterblock(fs, &bl, 0); /* must enter block before 'goto' */ 1453 enterblock(fs, &bl, 0); /* must enter block before 'goto' */
1459 if (ls->t.token == TK_GOTO) 1454 breakstat(ls, v.t); /* handle break */
1460 gotostat(ls, v.t); /* handle goto */
1461 else
1462 breakstat(ls, v.t); /* handle break */
1463 while (testnext(ls, ';')) {} /* skip semicolons */ 1455 while (testnext(ls, ';')) {} /* skip semicolons */
1464 if (block_follow(ls, 0)) { /* 'goto'/'break' is the entire block? */ 1456 if (block_follow(ls, 0)) { /* 'break' is the entire block? */
1465 leaveblock(fs); 1457 leaveblock(fs);
1466 return; /* and that is it */ 1458 return; /* and that is it */
1467 } 1459 }
@@ -1683,7 +1675,8 @@ static void statement (LexState *ls) {
1683 break; 1675 break;
1684 } 1676 }
1685 case TK_GOTO: { /* stat -> 'goto' NAME */ 1677 case TK_GOTO: { /* stat -> 'goto' NAME */
1686 gotostat(ls, luaK_jump(ls->fs)); 1678 luaX_next(ls); /* skip 'goto' */
1679 gotostat(ls);
1687 break; 1680 break;
1688 } 1681 }
1689 default: { /* stat -> func | assignment */ 1682 default: { /* stat -> func | assignment */