lua-users home
lua-l archive

Added `continue` keyword to Lua 5.4 [code review requested].

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


Hi all,
I have patched Lua 5.4.5 to add the `continue` keyword to the for loop, while loop, and repeat loop, and I've attached the patch file and some test code.  Use it like so:

for i = 1, 10 do
  if i % 2 == 0 then continue end
  print( i )
end

This should really help to reduce unnecessary if-statement nesting inside loops.

Since this is my first time patching Lua, can someone help to review my patch?

It was surprisingly easy since the `continue` keyword can be implemented entirely at the parser level as an implicit `goto`, similar to `break`.  The only part I wasn't sure of was the part in the test_then_block function -- not sure what the purpose of that is, as my tests seem to work whether I update it or not.

Note that the patch is applied to the current Lua master from github (https://github.com/lua/lua).

Thanks in advance,
David
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..773dc070 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,16 @@ 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)' ? */
 int line = ls->linenumber;
 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);
+ if (ls->t.token == TK_BREAK)
+ newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, v.t);
+ else /* ls->t.token == TK_CONTINUE */
+ newgotoentry(ls, luaS_newliteral(ls->L, "continue"), line, v.t);
 while (testnext(ls, ';')) {} /* skip semicolons */
 if (block_follow(ls, 0)) { /* jump is the entire block? */
 leaveblock(fs);
@@ -1658,7 +1685,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 +1925,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);
lst = { 'one', 'two', 'three', 'four', 'five', 'six' }
local function closable()
 return setmetatable( {}, {
 __close = function()
 print( '', '==> I was closed.' )
 end
 } )
end
print( '-------- repeat loop -----------' )
local idx = 1
repeat
 local word = lst[idx]
 idx = idx + 1
 local _<close> = closable()
 if word:sub( 1, 1 ) == 't' then continue end
 print( idx-1, word )
until idx > 6
print( '-------- while loop -----------' )
local idx = 1
while idx <= 6 do
 local word = lst[idx]
 idx = idx + 1
 local _<close> = closable()
 if word:sub( 1, 1 ) == 't' then continue end
 print( idx-1, word )
end
print( '-------- for loop -----------' )
for idx, word in ipairs( lst ) do
 local _<close> = closable()
 if word:sub( 1, 1 ) == 't' then continue end
 print( idx, word )
end
print( '-------- multiple statements -----------' )
for idx, word in ipairs( lst ) do
 local _<close> = closable()
 if word:sub( 1, 1 ) == 't' then
 print( '', '==> I am continuing.' )
 continue
 end
 print( idx, word )
end

AltStyle によって変換されたページ (->オリジナル) /