Browse Source

first implementation of FOR

Roberto Ierusalimschy 25 years ago
parent
commit
f9cf402fbd
7 changed files with 87 additions and 15 deletions
  1. 5 3
      lcode.c
  2. 2 2
      llex.c
  3. 2 2
      llex.h
  4. 4 1
      lopcodes.h
  5. 42 5
      lparser.c
  6. 3 1
      ltests.c
  7. 29 1
      lvm.c

+ 5 - 3
lcode.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lcode.c,v 1.22 2000/04/07 13:13:11 roberto Exp roberto $
+** $Id: lcode.c,v 1.23 2000/04/07 19:35:20 roberto Exp roberto $
 ** Code generator for Lua
 ** See Copyright Notice in lua.h
 */
@@ -53,7 +53,6 @@ static void luaK_fixjump (FuncState *fs, int pc, int dest) {
     SETARG_S(*jmp, NO_JUMP);  /* point to itself to represent end of list */
   else {  /* jump is relative to position following jump instruction */
     int offset = dest-(pc+1);
-    LUA_ASSERT(L, offset != NO_JUMP, "cannot link to itself");
     if (abs(offset) > MAXARG_S)
       luaK_error(fs->ls, "control structure too long");
     SETARG_S(*jmp, offset);
@@ -434,7 +433,6 @@ int luaK_code2 (FuncState *fs, OpCode o, int arg1, int arg2) {
   mode = iP;
   switch (o) {
 
-    case OP_JMP: delta = 0; mode = iS; break;
     case OP_CLOSURE: delta = -arg2+1; mode = iAB; break;
     case OP_SETLINE: mode = iU; break;
     case OP_CALL: mode = iAB; break;
@@ -443,6 +441,10 @@ int luaK_code2 (FuncState *fs, OpCode o, int arg1, int arg2) {
     case OP_SETTABLE: delta = -arg2; mode = iAB; break;
     case OP_SETLIST: delta = -(arg2+1); mode = iAB; break;
     case OP_SETMAP: delta = -2*(arg1+1); mode = iU; break;
+    case OP_FORLOOP: delta = -3; arg1 = NO_JUMP; mode = iS; break;
+
+    case OP_FORPREP: arg1 = NO_JUMP;  /* go through */
+    case OP_JMP: mode = iS; break;
 
     case OP_END: case OP_PUSHNILJMP: case OP_NOT:
       mode = iO; break;

+ 2 - 2
llex.c

@@ -1,5 +1,5 @@
 /*
-** $Id: llex.c,v 1.55 2000/04/05 17:51:58 roberto Exp roberto $
+** $Id: llex.c,v 1.56 2000/04/07 13:11:49 roberto Exp roberto $
 ** Lexical Analyzer
 ** See Copyright Notice in lua.h
 */
@@ -32,7 +32,7 @@
 
 /* ORDER RESERVED */
 static const char *const token2string [] = {
-    "and", "break", "do", "else", "elseif", "end",
+    "and", "break", "do", "else", "elseif", "end", "for",
     "function", "if", "local", "nil", "not", "or", "repeat", "return", "then",
     "until", "while", "", "..", "...", "==", ">=", "<=", "~=", "", "", "<eof>"};
 

+ 2 - 2
llex.h

@@ -1,5 +1,5 @@
 /*
-** $Id: llex.h,v 1.22 2000/04/05 17:51:58 roberto Exp roberto $
+** $Id: llex.h,v 1.23 2000/04/07 13:11:49 roberto Exp roberto $
 ** Lexical Analyzer
 ** See Copyright Notice in lua.h
 */
@@ -24,7 +24,7 @@
 enum RESERVED {
   /* terminal symbols denoted by reserved words */
   TK_AND = FIRST_RESERVED, TK_BREAK,
-  TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FUNCTION, TK_IF, TK_LOCAL,
+  TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FOR, TK_FUNCTION, TK_IF, TK_LOCAL,
   TK_NIL, TK_NOT, TK_OR, TK_REPEAT, TK_RETURN, TK_THEN, TK_UNTIL, TK_WHILE,
   /* other terminal symbols */
   TK_NAME, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,

+ 4 - 1
lopcodes.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lopcodes.h,v 1.55 2000/04/07 13:12:50 roberto Exp roberto $
+** $Id: lopcodes.h,v 1.56 2000/04/07 19:35:31 roberto Exp roberto $
 ** Opcodes for Lua virtual machine
 ** See Copyright Notice in lua.h
 */
@@ -148,6 +148,9 @@ OP_JMP,/*	J	-		-		PC+=s		*/
 
 OP_PUSHNILJMP,/* -	-		nil		PC++;		*/
 
+OP_FORPREP,/*	J							*/
+OP_FORLOOP,/*	J							*/
+
 OP_CLOSURE,/*	A B	v_b-v_1		closure(KPROTO[a], v_1-v_b)	*/
 
 OP_SETLINE/*	U	-		-		LINE=u		*/

+ 42 - 5
lparser.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lparser.c,v 1.80 2000/04/10 19:21:14 roberto Exp roberto $
+** $Id: lparser.c,v 1.81 2000/04/11 18:37:18 roberto Exp roberto $
 ** LL(1) Parser and code generator for Lua
 ** See Copyright Notice in lua.h
 */
@@ -884,7 +884,7 @@ static void whilestat (LexState *ls, int line) {
   block(ls);
   luaK_patchlist(fs, luaK_jump(fs), while_init);
   luaK_patchlist(fs, v.u.l.f, luaK_getlabel(fs));
-  check_END(ls, TK_WHILE, line);
+  check_END(ls, TK_WHILE, line);  /* trace END when loop ends */
   leavebreak(fs, &bl);
 }
 
@@ -906,7 +906,40 @@ static void repeatstat (LexState *ls, int line) {
 }
 
 
-static void test_and_bock (LexState *ls, expdesc *v) {
+static void forstat (LexState *ls, int line) {
+  /* forstat -> FOR NAME '=' expr1 ',' expr1 [',' expr1] DO block END */
+  FuncState *fs = ls->fs;
+  int prep;
+  int blockinit;
+  Breaklabel bl;
+  enterbreak(fs, &bl);
+  setline_and_next(ls);  /* skip for */
+  store_localvar(ls, str_checkname(ls), 0);  /* control variable */
+  check(ls, '=');
+  exp1(ls);  /* initial value */
+  check(ls, ',');
+  exp1(ls);  /* limit */
+  if (optional(ls, ','))
+    exp1(ls);  /* optional step */
+  else
+    luaK_code1(fs, OP_PUSHINT, 1);  /* default step */
+  adjustlocalvars(ls, 1, 0);  /* init scope for control variable */
+  add_localvar(ls, " limit ");
+  add_localvar(ls, " step ");
+  prep = luaK_code0(fs, OP_FORPREP);
+  blockinit = luaK_getlabel(fs);
+  check(ls, TK_DO);
+  block(ls);
+  luaK_patchlist(fs, prep, luaK_getlabel(fs));
+  luaK_patchlist(fs, luaK_code0(fs, OP_FORLOOP), blockinit);
+  check_END(ls, TK_WHILE, line);
+  leavebreak(fs, &bl);
+  removelocalvars(ls, 3, fs->lastsetline);
+}
+
+
+static void test_and_block (LexState *ls, expdesc *v) {
+  /* test_and_block -> [IF | ELSEIF] cond THEN block */
   setline_and_next(ls);  /* skip IF or ELSEIF */
   expr(ls, v);  /* cond */
   luaK_goiftrue(ls->fs, v, 0);
@@ -921,11 +954,11 @@ static void ifstat (LexState *ls, int line) {
   FuncState *fs = ls->fs;
   expdesc v;
   int escapelist = NO_JUMP;
-  test_and_bock(ls, &v);  /* IF cond THEN block */
+  test_and_block(ls, &v);  /* IF cond THEN block */
   while (ls->token == TK_ELSEIF) {
     luaK_concat(fs, &escapelist, luaK_jump(fs));
     luaK_patchlist(fs, v.u.l.f, luaK_getlabel(fs));
-    test_and_bock(ls, &v);  /* ELSEIF cond THEN block */
+    test_and_block(ls, &v);  /* ELSEIF cond THEN block */
   }
   if (ls->token == TK_ELSE) {
     luaK_concat(fs, &escapelist, luaK_jump(fs));
@@ -1062,6 +1095,10 @@ static int stat (LexState *ls) {
       check_END(ls, TK_DO, line);
       return 1;
 
+    case TK_FOR:  /* stat -> forstat */
+      forstat(ls, line);
+      return 1;
+
     case TK_REPEAT:  /* stat -> repeatstat */
       repeatstat(ls, line);
       return 1;

+ 3 - 1
ltests.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltests.c,v 1.11 2000/04/06 17:35:23 roberto Exp roberto $
+** $Id: ltests.c,v 1.12 2000/04/07 13:12:50 roberto Exp roberto $
 ** Internal Module for Debugging of the Lua Implementation
 ** See Copyright Notice in lua.h
 */
@@ -104,6 +104,8 @@ static int printop (lua_State *L, Instruction i) {
     case OP_PUSHNILJMP: O("PUSHNILJMP"); break;
     case OP_JMPT: S("JMPT"); break;
     case OP_JMPF: S("JMPF"); break;
+    case OP_FORPREP: S("OP_FORPREP"); break;
+    case OP_FORLOOP: S("OP_FORLOOP"); break;
     case OP_CLOSURE: AB("CLOSURE"); break;
     case OP_SETLINE: U("SETLINE"); break;
   }

+ 29 - 1
lvm.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lvm.c,v 1.99 2000/04/04 20:48:44 roberto Exp roberto $
+** $Id: lvm.c,v 1.100 2000/04/07 13:13:11 roberto Exp roberto $
 ** Lua virtual machine
 ** See Copyright Notice in lua.h
 */
@@ -630,6 +630,34 @@ StkId luaV_execute (lua_State *L, const Closure *cl, register StkId base) {
         pc++;
         break;
 
+      case OP_FORPREP:
+        if (tonumber(top-1))
+          lua_error(L, "`for' step must be a number");
+        if (tonumber(top-2))
+          lua_error(L, "`for' limit must be a number");
+        if (tonumber(top-3))
+          lua_error(L, "`for' initial value must be a number");
+        nvalue(top-3) -= nvalue(top-1);  /* to be undone by first FORLOOP */
+        pc += GETARG_S(i);
+        break;
+
+      case OP_FORLOOP: {
+        Number step = nvalue(top-1);
+        Number limit = nvalue(top-2);
+        Number index;
+        LUA_ASSERT(L, ttype(top-1) == TAG_NUMBER, "invalid step");
+        LUA_ASSERT(L, ttype(top-2) == TAG_NUMBER, "invalid limit");
+        if (tonumber(top-3)) lua_error(L, "`for' index must be a number");
+        index = nvalue(top-3)+step;
+        if ((step>0) ? index<=limit : index>=limit) {
+          nvalue(top-3) = index;
+          pc += GETARG_S(i);
+        }
+        else  /* end of `for': remove control variables */
+          top -= 3;
+        break;
+      }
+
       case OP_CLOSURE:
         L->top = top;
         luaV_Lclosure(L, tf->kproto[GETARG_A(i)], GETARG_B(i));