lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Makes sense, thanks for the explanation.  In that case, I will add a corresponding optimization for "if cond then continue ...".  And now looking at my patch, I wrote a bug in that function which actually causes a test failure; I've attached the corrected patch.

David

On Mon, Mar 20, 2023 at 5:00 PM Roberto Ierusalimschy <roberto@inf.puc-rio.br> wrote:
> In the absence of a code review, would anyone know the purpose of the
> `break` keyword handling in the `test_then_block` method?  I am curious to
> know what that does so I know whether to modify it or not.  From what I can
> see, the `continue` keyword seems to work properly whether I update that
> method or not, so I'm wondering if it is simply some kind of optimization
> for special cases.

Yes, it is an optimization for the case "if cond then break ...". Without
the optimization, the "if" would jump around 'break' when the condition
is false. With the optimization, the "if" jumps straight to the end of
the loop when the condition is true.

-- Roberto
diff --git a/llex.c b/llex.c
index b0dc0acc..f96a0239 100644
--- a/llex.c
+++ b/llex.c
@@ -38,7 +38,7 @@
 
 /* ORDER RESERVED */
 static const char *const luaX_tokens [] = {
-    "and", "break", "do", "else", "elseif",
+    "and", "break", "continue", "do", "else", "elseif",
     "end", "false", "for", "function", "goto", "if",
     "in", "local", "nil", "not", "or", "repeat",
     "return", "then", "true", "until", "while",
diff --git a/llex.h b/llex.h
index 389d2f86..bb55dafe 100644
--- a/llex.h
+++ b/llex.h
@@ -31,7 +31,7 @@
 */
 enum RESERVED {
   /* terminal symbols denoted by reserved words */
-  TK_AND = FIRST_RESERVED, TK_BREAK,
+  TK_AND = FIRST_RESERVED, TK_BREAK, TK_CONTINUE,
   TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
   TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
   TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
diff --git a/lparser.c b/lparser.c
index 24668c24..34c1555b 100644
--- a/lparser.c
+++ b/lparser.c
@@ -661,6 +661,10 @@ static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
     msg = "break outside loop at line %d";
     msg = luaO_pushfstring(ls->L, msg, gt->line);
   }
+  else if (eqstr(gt->name, luaS_newliteral(ls->L, "continue"))) {
+    msg = "continue outside loop at line %d";
+    msg = luaO_pushfstring(ls->L, msg, gt->line);
+  }
   else {
     msg = "no visible label '%s' for <goto> at line %d";
     msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line);
@@ -669,6 +673,12 @@ static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
 }
 
 
+static void endloopblock (FuncState *fs) {
+  LexState *ls = fs->ls;
+  createlabel(ls, luaS_newliteral(ls->L, "continue"), 0, 0);
+}
+
+
 static void leaveblock (FuncState *fs) {
   BlockCnt *bl = fs->bl;
   LexState *ls = fs->ls;
@@ -1442,6 +1452,16 @@ static void breakstat (LexState *ls) {
 }
 
 
+/*
+** Continue statement. Semantically equivalent to "goto continue".
+*/
+static void contstat (LexState *ls) {
+  int line = ls->linenumber;
+  luaX_next(ls);  /* skip continue */
+  newgotoentry(ls, luaS_newliteral(ls->L, "continue"), line, luaK_jump(ls->fs));
+}
+
+
 /*
 ** Check whether there is already a label with the given 'name'.
 */
@@ -1477,6 +1497,7 @@ static void whilestat (LexState *ls, int line) {
   enterblock(fs, &bl, 1);
   checknext(ls, TK_DO);
   block(ls);
+  endloopblock(fs);  /* inside loop scope ('continue' jumps to this point) */
   luaK_jumpto(fs, whileinit);
   check_match(ls, TK_END, TK_WHILE, line);
   leaveblock(fs);
@@ -1494,6 +1515,7 @@ static void repeatstat (LexState *ls, int line) {
   enterblock(fs, &bl2, 0);  /* scope block */
   luaX_next(ls);  /* skip REPEAT */
   statlist(ls);
+  endloopblock(fs);  /* inside loop scope ('continue' jumps to this point) */
   check_match(ls, TK_UNTIL, TK_REPEAT, line);
   condexit = cond(ls);  /* read condition (inside scope block) */
   leaveblock(fs);  /* finish scope */
@@ -1554,6 +1576,7 @@ static void forbody (LexState *ls, int base, int line, int nvars, int isgen) {
   adjustlocalvars(ls, nvars);
   luaK_reserveregs(fs, nvars);
   block(ls);
+  endloopblock(fs);  /* inside loop scope ('continue' jumps to this point) */
   leaveblock(fs);  /* end of scope for declared variables */
   fixforjump(fs, prep, luaK_getlabel(fs), 0);
   if (isgen) {  /* generic for? */
@@ -1644,12 +1667,18 @@ static void test_then_block (LexState *ls, int *escapelist) {
   luaX_next(ls);  /* skip IF or ELSEIF */
   expr(ls, &v);  /* read condition */
   checknext(ls, TK_THEN);
-  if (ls->t.token == TK_BREAK) {  /* 'if x then break' ? */
+  if (ls->t.token == TK_BREAK ||
+      ls->t.token == TK_CONTINUE) {  /* 'if x then (break|continue)' ? */
+    const char* jmplit;
     int line = ls->linenumber;
+    switch (ls->t.token) {
+      case TK_BREAK: jmplit = "break"; break;
+      case TK_CONTINUE: jmplit = "continue"; break;
+    }
     luaK_goiffalse(ls->fs, &v);  /* will jump if condition is true */
     luaX_next(ls);  /* skip 'break' */
     enterblock(fs, &bl, 0);  /* must enter block before 'goto' */
-    newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, v.t);
+    newgotoentry(ls, luaS_newlstr(ls->L, jmplit, strlen(jmplit)), line, v.t);
     while (testnext(ls, ';')) {}  /* skip semicolons */
     if (block_follow(ls, 0)) {  /* jump is the entire block? */
       leaveblock(fs);
@@ -1658,7 +1687,7 @@ static void test_then_block (LexState *ls, int *escapelist) {
     else  /* must skip over 'then' part if condition is false */
       jf = luaK_jump(fs);
   }
-  else {  /* regular case (not a break) */
+  else {  /* regular case (not a break or continue) */
     luaK_goiftrue(ls->fs, &v);  /* skip over block if condition is false */
     enterblock(fs, &bl, 0);
     jf = v.f;
@@ -1898,6 +1927,10 @@ static void statement (LexState *ls) {
       breakstat(ls);
       break;
     }
+    case TK_CONTINUE: {  /* stat -> contstat */
+      contstat(ls);
+      break;
+    }
     case TK_GOTO: {  /* stat -> 'goto' NAME */
       luaX_next(ls);  /* skip 'goto' */
       gotostat(ls);