diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2011-02-04 15:34:43 -0200 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2011-02-04 15:34:43 -0200 |
commit | 7cc0e63d8a5bd45eabd328c398f02a933e07746d (patch) | |
tree | 94a00b4e19c3c1ea0c6e3ee2e3dbb036d960f5c0 /lparser.c | |
parent | a4a8914c2097bdcaaa4e82c5fd1c9b0c7371e432 (diff) | |
download | lua-7cc0e63d8a5bd45eabd328c398f02a933e07746d.tar.gz lua-7cc0e63d8a5bd45eabd328c398f02a933e07746d.tar.bz2 lua-7cc0e63d8a5bd45eabd328c398f02a933e07746d.zip |
first implementation of 'goto'
Diffstat (limited to 'lparser.c')
-rw-r--r-- | lparser.c | 188 |
1 files changed, 174 insertions, 14 deletions
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lparser.c,v 2.95 2011/01/26 16:30:02 roberto Exp roberto $ | 2 | ** $Id: lparser.c,v 2.96 2011/02/01 18:03:10 roberto Exp roberto $ |
3 | ** Lua Parser | 3 | ** Lua Parser |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -42,7 +42,9 @@ | |||
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 */ | 44 | int breaklist; /* list of jumps out of this loop */ |
45 | lu_byte nactvar; /* # active locals outside the breakable structure */ | 45 | int firstlabel; /* index (in Labellist) of first label in this block */ |
46 | int firstgoto; /* index (in Gotolist) of first pending goto in this block */ | ||
47 | lu_byte nactvar; /* # active locals outside the block */ | ||
46 | lu_byte upval; /* true if some variable in the block is an upvalue */ | 48 | lu_byte upval; /* true if some variable in the block is an upvalue */ |
47 | lu_byte isbreakable; /* true if `block' is a loop */ | 49 | lu_byte isbreakable; /* true if `block' is a loop */ |
48 | } BlockCnt; | 50 | } BlockCnt; |
@@ -52,7 +54,7 @@ typedef struct BlockCnt { | |||
52 | /* | 54 | /* |
53 | ** prototypes for recursive non-terminal functions | 55 | ** prototypes for recursive non-terminal functions |
54 | */ | 56 | */ |
55 | static void chunk (LexState *ls); | 57 | static void statlist (LexState *ls); |
56 | static void expr (LexState *ls, expdesc *v); | 58 | static void expr (LexState *ls, expdesc *v); |
57 | 59 | ||
58 | 60 | ||
@@ -172,7 +174,7 @@ static void new_localvar (LexState *ls, TString *name) { | |||
172 | checklimit(fs, vl->nactvar + 1 - fs->firstlocal, | 174 | checklimit(fs, vl->nactvar + 1 - fs->firstlocal, |
173 | MAXVARS, "local variables"); | 175 | MAXVARS, "local variables"); |
174 | luaM_growvector(ls->L, vl->actvar, vl->nactvar + 1, | 176 | luaM_growvector(ls->L, vl->actvar, vl->nactvar + 1, |
175 | vl->actvarsize, vardesc, MAX_INT, "local variables"); | 177 | vl->actvarsize, Vardesc, MAX_INT, "local variables"); |
176 | vl->actvar[vl->nactvar++].idx = cast(unsigned short, reg); | 178 | vl->actvar[vl->nactvar++].idx = cast(unsigned short, reg); |
177 | } | 179 | } |
178 | 180 | ||
@@ -327,10 +329,93 @@ static void enterlevel (LexState *ls) { | |||
327 | #define leavelevel(ls) (G((ls)->L)->nCcalls--) | 329 | #define leavelevel(ls) (G((ls)->L)->nCcalls--) |
328 | 330 | ||
329 | 331 | ||
332 | static void closegoto (LexState *ls, int g, Labeldesc *label) { | ||
333 | int i; | ||
334 | FuncState *fs = ls->fs; | ||
335 | Gotodesc *gt = &ls->gtl->gt[g]; | ||
336 | lua_assert(gt->name == label->name); | ||
337 | if (gt->currlevel < label->nactvar) { | ||
338 | const char *msg = luaO_pushfstring(ls->L, | ||
339 | "<goto> at line %d attemps to jump into the scope of local " LUA_QS, | ||
340 | gt->line, getstr(getlocvar(fs, gt->currlevel)->varname));; | ||
341 | luaX_syntaxerror(ls, msg); | ||
342 | } | ||
343 | luaK_patchlist(fs, gt->pc, label->pc); | ||
344 | /* remove goto from pending list */ | ||
345 | for (i = g; i < ls->gtl->ngt - 1; i++) | ||
346 | ls->gtl->gt[i] = ls->gtl->gt[i + 1]; | ||
347 | ls->gtl->ngt--; | ||
348 | } | ||
349 | |||
350 | |||
351 | /* | ||
352 | ** try to close a goto with existing labels; this solves backward jumps | ||
353 | */ | ||
354 | static int findlabel (LexState *ls, int g) { | ||
355 | int i; | ||
356 | BlockCnt *bl = ls->fs->bl; | ||
357 | Labellist *labell = ls->labell; | ||
358 | Gotodesc *gt = &ls->gtl->gt[g]; | ||
359 | /* check labels in current block for a match */ | ||
360 | for (i = bl->firstlabel; i < labell->nlabel; i++) { | ||
361 | Labeldesc *lb = &labell->label[i]; | ||
362 | if (lb->name == gt->name) { | ||
363 | lua_assert(labell->label[i].pc <= gt->pc); | ||
364 | if (gt->currlevel > lb->nactvar && | ||
365 | (bl->upval || ls->labell->nlabel > bl->firstlabel)) | ||
366 | luaK_patchclose(ls->fs, gt->pc, lb->nactvar); | ||
367 | closegoto(ls, g, lb); /* close it */ | ||
368 | return 1; | ||
369 | } | ||
370 | } | ||
371 | return 0; /* label not found; cannot close goto */ | ||
372 | } | ||
373 | |||
374 | |||
375 | /* | ||
376 | ** check whether new label 'lb' matches any pending goto in current | ||
377 | ** block; solves forward jumps | ||
378 | */ | ||
379 | static void findgotos (LexState *ls, Labeldesc *lb) { | ||
380 | int i; | ||
381 | Gotolist *gtl = ls->gtl; | ||
382 | for (i = ls->fs->bl->firstgoto; i < gtl->ngt; i++) { | ||
383 | if (gtl->gt[i].name == lb->name) | ||
384 | closegoto(ls, i, lb); | ||
385 | } | ||
386 | } | ||
387 | |||
388 | |||
389 | /* | ||
390 | ** "export" pending gotos to outer level, to check them against | ||
391 | ** outer labels; if the block being exited has upvalues, and | ||
392 | ** the goto exists the scope of any variable (which can be the | ||
393 | ** upvalue), close those variables being exited. | ||
394 | */ | ||
395 | static void movegotosout (FuncState *fs, BlockCnt *bl) { | ||
396 | int i = bl->firstgoto; | ||
397 | LexState *ls = fs->ls; | ||
398 | /* correct pending gotos to current block and try to close it | ||
399 | with visible labels */ | ||
400 | while (i < ls->gtl->ngt) { | ||
401 | Gotodesc *gt = &ls->gtl->gt[i]; | ||
402 | if (gt->currlevel > bl->nactvar) { | ||
403 | if (bl->upval) | ||
404 | luaK_patchclose(fs, gt->pc, bl->nactvar); | ||
405 | gt->currlevel = bl->nactvar; | ||
406 | } | ||
407 | if (!findlabel(ls, i)) | ||
408 | i++; /* move to next one */ | ||
409 | } | ||
410 | } | ||
411 | |||
412 | |||
330 | static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) { | 413 | static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) { |
331 | bl->breaklist = NO_JUMP; | 414 | bl->breaklist = NO_JUMP; |
332 | bl->isbreakable = isbreakable; | 415 | bl->isbreakable = isbreakable; |
333 | bl->nactvar = fs->nactvar; | 416 | bl->nactvar = fs->nactvar; |
417 | bl->firstlabel = fs->ls->labell->nlabel; | ||
418 | bl->firstgoto = fs->ls->gtl->ngt; | ||
334 | bl->upval = 0; | 419 | bl->upval = 0; |
335 | bl->previous = fs->bl; | 420 | bl->previous = fs->bl; |
336 | fs->bl = bl; | 421 | fs->bl = bl; |
@@ -342,6 +427,8 @@ static void leaveblock (FuncState *fs) { | |||
342 | BlockCnt *bl = fs->bl; | 427 | BlockCnt *bl = fs->bl; |
343 | fs->bl = bl->previous; | 428 | fs->bl = bl->previous; |
344 | removevars(fs, bl->nactvar); | 429 | removevars(fs, bl->nactvar); |
430 | fs->ls->labell->nlabel = bl->firstlabel; /* remove local labels */ | ||
431 | movegotosout(fs, bl); | ||
345 | if (bl->upval) | 432 | if (bl->upval) |
346 | luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); | 433 | luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); |
347 | /* a block either controls scope or breaks (never both) */ | 434 | /* a block either controls scope or breaks (never both) */ |
@@ -445,8 +532,24 @@ static void open_mainfunc (LexState *ls, FuncState *fs) { | |||
445 | } | 532 | } |
446 | 533 | ||
447 | 534 | ||
535 | static void mainblock (LexState *ls, FuncState *fs) { | ||
536 | BlockCnt bl; | ||
537 | enterblock(fs, &bl, 0); | ||
538 | statlist(ls); /* read main block */ | ||
539 | if (bl.firstgoto < ls->gtl->ngt) { /* check pending gotos */ | ||
540 | Gotodesc *gt = &ls->gtl->gt[bl.firstgoto]; | ||
541 | const char *msg = luaO_pushfstring(ls->L, | ||
542 | "label " LUA_QS " (<goto> at line %d) undefined", | ||
543 | getstr(gt->name), gt->line); | ||
544 | luaX_syntaxerror(ls, msg); | ||
545 | } | ||
546 | bl.upval = 0; /* RETURN will close any pending upvalue */ | ||
547 | leaveblock(fs); | ||
548 | } | ||
549 | |||
550 | |||
448 | Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Varlist *varl, | 551 | Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Varlist *varl, |
449 | const char *name) { | 552 | Gotolist *gtl, Labellist *labell, const char *name) { |
450 | LexState lexstate; | 553 | LexState lexstate; |
451 | FuncState funcstate; | 554 | FuncState funcstate; |
452 | TString *tname = luaS_new(L, name); | 555 | TString *tname = luaS_new(L, name); |
@@ -454,10 +557,12 @@ Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Varlist *varl, | |||
454 | incr_top(L); | 557 | incr_top(L); |
455 | lexstate.buff = buff; | 558 | lexstate.buff = buff; |
456 | lexstate.varl = varl; | 559 | lexstate.varl = varl; |
560 | lexstate.gtl = gtl; | ||
561 | lexstate.labell = labell; | ||
457 | luaX_setinput(L, &lexstate, z, tname); | 562 | luaX_setinput(L, &lexstate, z, tname); |
458 | open_mainfunc(&lexstate, &funcstate); | 563 | open_mainfunc(&lexstate, &funcstate); |
459 | luaX_next(&lexstate); /* read first token */ | 564 | luaX_next(&lexstate); /* read first token */ |
460 | chunk(&lexstate); /* read main chunk */ | 565 | mainblock(&lexstate, &funcstate); |
461 | check(&lexstate, TK_EOS); | 566 | check(&lexstate, TK_EOS); |
462 | close_func(&lexstate); | 567 | close_func(&lexstate); |
463 | L->top--; /* pop name */ | 568 | L->top--; /* pop name */ |
@@ -645,7 +750,7 @@ static void parlist (LexState *ls) { | |||
645 | 750 | ||
646 | 751 | ||
647 | static void body (LexState *ls, expdesc *e, int needself, int line) { | 752 | static void body (LexState *ls, expdesc *e, int needself, int line) { |
648 | /* body -> `(' parlist `)' chunk END */ | 753 | /* body -> `(' parlist `)' block END */ |
649 | FuncState new_fs; | 754 | FuncState new_fs; |
650 | open_func(ls, &new_fs); | 755 | open_func(ls, &new_fs); |
651 | new_fs.f->linedefined = line; | 756 | new_fs.f->linedefined = line; |
@@ -656,7 +761,7 @@ static void body (LexState *ls, expdesc *e, int needself, int line) { | |||
656 | } | 761 | } |
657 | parlist(ls); | 762 | parlist(ls); |
658 | checknext(ls, ')'); | 763 | checknext(ls, ')'); |
659 | chunk(ls); | 764 | mainblock(ls, &new_fs); |
660 | new_fs.f->lastlinedefined = ls->linenumber; | 765 | new_fs.f->lastlinedefined = ls->linenumber; |
661 | check_match(ls, TK_END, TK_FUNCTION, line); | 766 | check_match(ls, TK_END, TK_FUNCTION, line); |
662 | codeclosure(ls, new_fs.f, e); | 767 | codeclosure(ls, new_fs.f, e); |
@@ -949,11 +1054,11 @@ static int block_follow (int token) { | |||
949 | 1054 | ||
950 | 1055 | ||
951 | static void block (LexState *ls) { | 1056 | static void block (LexState *ls) { |
952 | /* block -> chunk */ | 1057 | /* block -> statlist */ |
953 | FuncState *fs = ls->fs; | 1058 | FuncState *fs = ls->fs; |
954 | BlockCnt bl; | 1059 | BlockCnt bl; |
955 | enterblock(fs, &bl, 0); | 1060 | enterblock(fs, &bl, 0); |
956 | chunk(ls); | 1061 | statlist(ls); |
957 | lua_assert(bl.breaklist == NO_JUMP); | 1062 | lua_assert(bl.breaklist == NO_JUMP); |
958 | leaveblock(fs); | 1063 | leaveblock(fs); |
959 | } | 1064 | } |
@@ -1043,6 +1148,13 @@ static int cond (LexState *ls) { | |||
1043 | } | 1148 | } |
1044 | 1149 | ||
1045 | 1150 | ||
1151 | /* code a break statement. The last 'if' decides the need to close | ||
1152 | upvalues when leaving the block. If the block has upvalues, it | ||
1153 | must be closed. If it has local variables and any label | ||
1154 | before the break, those variables must be closed too, as they | ||
1155 | may be used as upvalues after the break and through a goto | ||
1156 | be exited through this break. | ||
1157 | */ | ||
1046 | static void breakstat (LexState *ls) { | 1158 | static void breakstat (LexState *ls) { |
1047 | FuncState *fs = ls->fs; | 1159 | FuncState *fs = ls->fs; |
1048 | BlockCnt *bl = fs->bl; | 1160 | BlockCnt *bl = fs->bl; |
@@ -1054,11 +1166,49 @@ static void breakstat (LexState *ls) { | |||
1054 | if (!bl) | 1166 | if (!bl) |
1055 | luaX_syntaxerror(ls, "no loop to break"); | 1167 | luaX_syntaxerror(ls, "no loop to break"); |
1056 | luaK_concat(fs, &bl->breaklist, luaK_jump(fs)); | 1168 | luaK_concat(fs, &bl->breaklist, luaK_jump(fs)); |
1057 | if (upval) | 1169 | if (upval || |
1170 | (fs->nactvar > bl->nactvar && | ||
1171 | ls->labell->nlabel > bl->firstlabel)) | ||
1058 | luaK_patchclose(fs, bl->breaklist, bl->nactvar); | 1172 | luaK_patchclose(fs, bl->breaklist, bl->nactvar); |
1059 | } | 1173 | } |
1060 | 1174 | ||
1061 | 1175 | ||
1176 | static void gotostat (LexState *ls, TString *label, int line) { | ||
1177 | Gotolist *gtl = ls->gtl; | ||
1178 | int g = gtl->ngt; /* index of new goto being created */ | ||
1179 | /* create new entry for this goto */ | ||
1180 | luaM_growvector(ls->L, gtl->gt, gtl->ngt, gtl->gtsize, | ||
1181 | Gotodesc, MAX_INT, "labels"); | ||
1182 | gtl->gt[g].name = label; | ||
1183 | gtl->gt[g].line = line; | ||
1184 | gtl->gt[g].currlevel = ls->fs->nactvar; | ||
1185 | gtl->gt[g].pc = luaK_jump(ls->fs); /* create jump instruction */ | ||
1186 | gtl->ngt++; | ||
1187 | findlabel(ls, g); | ||
1188 | } | ||
1189 | |||
1190 | |||
1191 | static void labelstat (LexState *ls, TString *label) { | ||
1192 | /* label -> '@' NAME ':' */ | ||
1193 | FuncState *fs = ls->fs; | ||
1194 | Labellist *labell = ls->labell; | ||
1195 | int l = labell->nlabel; /* index of new label being created */ | ||
1196 | checknext(ls, ':'); | ||
1197 | /* create new entry for this label */ | ||
1198 | luaM_growvector(ls->L, labell->label, labell->nlabel, labell->labelsize, | ||
1199 | Labeldesc, MAX_INT, "labels"); | ||
1200 | labell->label[l].name = label; | ||
1201 | labell->label[l].pc = fs->pc; | ||
1202 | /* if label is last statement in the block, | ||
1203 | assume that local variables are already out of scope */ | ||
1204 | labell->label[l].nactvar = (ls->t.token == TK_END) | ||
1205 | ? fs->bl->nactvar | ||
1206 | : fs->nactvar; | ||
1207 | labell->nlabel++; | ||
1208 | findgotos(ls, &labell->label[l]); | ||
1209 | } | ||
1210 | |||
1211 | |||
1062 | static void whilestat (LexState *ls, int line) { | 1212 | static void whilestat (LexState *ls, int line) { |
1063 | /* whilestat -> WHILE cond DO block END */ | 1213 | /* whilestat -> WHILE cond DO block END */ |
1064 | FuncState *fs = ls->fs; | 1214 | FuncState *fs = ls->fs; |
@@ -1087,7 +1237,7 @@ static void repeatstat (LexState *ls, int line) { | |||
1087 | enterblock(fs, &bl1, 1); /* loop block */ | 1237 | enterblock(fs, &bl1, 1); /* loop block */ |
1088 | enterblock(fs, &bl2, 0); /* scope block */ | 1238 | enterblock(fs, &bl2, 0); /* scope block */ |
1089 | luaX_next(ls); /* skip REPEAT */ | 1239 | luaX_next(ls); /* skip REPEAT */ |
1090 | chunk(ls); | 1240 | statlist(ls); |
1091 | check_match(ls, TK_UNTIL, TK_REPEAT, line); | 1241 | check_match(ls, TK_UNTIL, TK_REPEAT, line); |
1092 | condexit = cond(ls); /* read condition (inside scope block) */ | 1242 | condexit = cond(ls); /* read condition (inside scope block) */ |
1093 | if (!bl2.upval) { /* no upvalues? */ | 1243 | if (!bl2.upval) { /* no upvalues? */ |
@@ -1382,6 +1532,11 @@ static int statement (LexState *ls) { | |||
1382 | localstat(ls); | 1532 | localstat(ls); |
1383 | return 0; | 1533 | return 0; |
1384 | } | 1534 | } |
1535 | case '@': { /* stat -> label */ | ||
1536 | luaX_next(ls); /* skip '@' */ | ||
1537 | labelstat(ls, str_checkname(ls)); | ||
1538 | return 0; | ||
1539 | } | ||
1385 | case TK_RETURN: { /* stat -> retstat */ | 1540 | case TK_RETURN: { /* stat -> retstat */ |
1386 | luaX_next(ls); /* skip RETURN */ | 1541 | luaX_next(ls); /* skip RETURN */ |
1387 | retstat(ls); | 1542 | retstat(ls); |
@@ -1392,6 +1547,11 @@ static int statement (LexState *ls) { | |||
1392 | breakstat(ls); | 1547 | breakstat(ls); |
1393 | return 1; /* must be last statement */ | 1548 | return 1; /* must be last statement */ |
1394 | } | 1549 | } |
1550 | case TK_GOTO: { /* stat -> 'goto' NAME */ | ||
1551 | luaX_next(ls); /* skip GOTO */ | ||
1552 | gotostat(ls, str_checkname(ls), line); | ||
1553 | return 0; | ||
1554 | } | ||
1395 | default: { /* stat -> func | assignment */ | 1555 | default: { /* stat -> func | assignment */ |
1396 | exprstat(ls); | 1556 | exprstat(ls); |
1397 | return 0; | 1557 | return 0; |
@@ -1400,8 +1560,8 @@ static int statement (LexState *ls) { | |||
1400 | } | 1560 | } |
1401 | 1561 | ||
1402 | 1562 | ||
1403 | static void chunk (LexState *ls) { | 1563 | static void statlist (LexState *ls) { |
1404 | /* chunk -> { stat [`;'] } */ | 1564 | /* statlist -> { stat [`;'] } */ |
1405 | int islast = 0; | 1565 | int islast = 0; |
1406 | enterlevel(ls); | 1566 | enterlevel(ls); |
1407 | while (!islast && !block_follow(ls->t.token)) { | 1567 | while (!islast && !block_follow(ls->t.token)) { |