lua-users home
lua-l archive

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


Programming would be sometimes easier if it was a possibility to break
or continue any of a few nested loops. For example:

   a = 0
   while a < 10 do
         a = a + 1
         if a == 5 then continue end
         b = 0
         while b < 10 do
               b = b + 1
               if b == 5 then continue end

               if a == 9 and b == 9 then
                  break 2 -- break the `while a < 10' loop
               end

               if a == b then
                  continue 2 -- next iteration of the `while a < 10' loop
               end

               print(a, b)
         end
   end

Here is a patch to lparser.c:

/* put this at the beginning of the file */

#include <math.h>
#define LUA_MAXBREAKLEVEL 100

/* ... */

/* replace `breakstat' (and `continuestat' if you have it) functions with the following
code */

static void breakstat (LexState *ls) {
  /* stat -> BREAK [loop number] */
  FuncState *fs = ls->fs;
  BlockCnt *bl = fs->bl;
  int upval = 0;
  int levels = 1;
  next(ls);  /* skip BREAK */
  if (testnext(ls, TK_NUMBER)) {
    if (ls->t.seminfo.r != floor(ls->t.seminfo.r))
      luaX_syntaxerror(ls, "loop block number must be integer");
    levels = (int) ls->t.seminfo.r;
    if (levels < 1 || levels > LUA_MAXBREAKLEVEL)
      luaX_syntaxerror(ls, "loop block number out of range");
  }
  while (levels-- > 0) {
    while (bl && !bl->isbreakable) {
      upval |= bl->upval;
      bl = bl->previous;
    }
    if (bl && levels > 0)
      bl = bl->previous;
  }
  if (!bl)
    luaX_syntaxerror(ls, "no loop to break");
  if (upval)
    luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
  luaK_concat(fs, &bl->breaklist, luaK_jump(fs));
}

static void continuestat (LexState *ls) {
  /* stat -> CONTINUE [loop number] */
  FuncState *fs = ls->fs;
  BlockCnt *bl = fs->bl;
  int levels = 1;
  next(ls);  /* skip CONTINUE */
  if (testnext(ls, TK_NUMBER)) {
    if (ls->t.seminfo.r != floor(ls->t.seminfo.r))
      luaX_syntaxerror(ls, "loop block number must be integer");
    levels = (int) ls->t.seminfo.r;
    if (levels < 1 || levels > LUA_MAXBREAKLEVEL)
      luaX_syntaxerror(ls, "loop block number out of range");
  }
  while (levels-- > 0) {
    while (bl && !bl->isbreakable)
      bl = bl->previous;
    if (bl && levels > 0)
      bl = bl->previous;
  }
  if (!bl)
    luaX_syntaxerror(ls, "no loop to continue");
  luaK_concat(fs, &bl->continuelist, luaK_jump(fs));
}

And here is test code:

--
-- `break N' and `continue N' statement test
--

for i = 1, 5 do
    for j = 1, 6 do
        if j == 1 then continue end
        if j == 5 then continue 2 end
        if i == 4 and j == 4 then break 2 end
        print(i, j)
    end
end

print(loadstring("for i = 1, 2 do for j = 1, 2 do break 2.00001 end end"))
print(loadstring("for i = 1, 2 do break 2 end"))

--
-- the result is:
--
-- 1    2
-- 1    3
-- 1    4
-- 2    2
-- 2    3
-- 2    4
-- 3    2
-- 3    3
-- 3    4
-- 4    2
-- 4    3
-- nil  [string "for i = 1, 2 do for j = 1, 2 do break 2.00001 end end"]:1: loop block number must be integer near `end'
-- nil  [string "for i = 1, 2 do break 2 end"]:1: no loop to break near `end'