Skip to content

Commit e6d146a

Browse files
committed
refactor: implement foreach loop
1 parent 6d37428 commit e6d146a

2 files changed

Lines changed: 132 additions & 24 deletions

File tree

compiler/compiler.c

Lines changed: 81 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -998,13 +998,48 @@ static void forStmt(Compiler* compiler){
998998
| |
999999
+---------------------------------------------------------------------------------+
10001000
*/
1001+
bool isForeach = false;
1002+
int iterReg = -1;
1003+
int stateReg = -1;
1004+
10011005
beginScope(compiler);
10021006
consume(compiler, TOKEN_LEFT_PAREN, "Expect '(' after 'for'.");
10031007

10041008
if(match(compiler, TOKEN_SEMICOLON)){
10051009
// No initializer
10061010
}else if(match(compiler, TOKEN_VAR)){
1007-
varDecl(compiler);
1011+
Token varName = compiler->parser.cur;
1012+
consume(compiler, TOKEN_IDENTIFIER, "Expect variable name.");
1013+
1014+
if(match(compiler, TOKEN_COLON)){
1015+
isForeach = true;
1016+
iterReg = getFreeReg(compiler);
1017+
reserveReg(compiler, 2);
1018+
stateReg = iterReg + 1;
1019+
1020+
ExprDesc iterExpr;
1021+
expression(compiler, &iterExpr);
1022+
expr2Reg(compiler, &iterExpr, iterReg);
1023+
freeExpr(compiler, &iterExpr);
1024+
1025+
emitABC(compiler, OP_LOADNULL, stateReg, 0, 0);
1026+
1027+
addLocal(compiler, varName);
1028+
defineVar(compiler, 0);
1029+
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after foreach variable.");
1030+
}else{
1031+
addLocal(compiler, varName);
1032+
int reg = compiler->locals[compiler->localCnt - 1].reg;
1033+
if(match(compiler, TOKEN_ASSIGN)){
1034+
ExprDesc initExpr;
1035+
expression(compiler, &initExpr);
1036+
expr2Reg(compiler, &initExpr, reg);
1037+
}else{
1038+
emitABC(compiler, OP_LOADNULL, reg, 0, 0);
1039+
}
1040+
defineVar(compiler, 0);
1041+
consume(compiler, TOKEN_SEMICOLON, "Expect ';' after loop initializer.");
1042+
}
10081043
}else{
10091044
ExprDesc initExpr;
10101045
expression(compiler, &initExpr);
@@ -1024,36 +1059,57 @@ static void forStmt(Compiler* compiler){
10241059
loop->breakCnt = 0;
10251060

10261061
int exitJmp = -1;
1027-
if(!match(compiler, TOKEN_SEMICOLON)){
1028-
ExprDesc condition;
1029-
expression(compiler, &condition);
1030-
consume(compiler, TOKEN_SEMICOLON, "Expect ';' after loop condition.");
1031-
1032-
expr2NextReg(compiler, &condition);
1033-
exitJmp = emitJmpIfFalse(compiler, condition.data.loc.index);
1034-
freeExpr(compiler, &condition);
1062+
if(isForeach){
1063+
reserveReg(compiler, 1);
1064+
int condReg = getFreeReg(compiler) - 1;
1065+
emitABC(compiler, OP_FOREACH, condReg, iterReg, 0);
1066+
exitJmp = emitJmpIfFalse(compiler, condReg);
1067+
freeRegs(compiler, 1);
1068+
}else{
1069+
if(!match(compiler, TOKEN_SEMICOLON)){
1070+
ExprDesc condition;
1071+
expression(compiler, &condition);
1072+
consume(compiler, TOKEN_SEMICOLON, "Expect ';' after loop condition.");
1073+
1074+
expr2NextReg(compiler, &condition);
1075+
exitJmp = emitJmpIfFalse(compiler, condition.data.loc.index);
1076+
freeExpr(compiler, &condition);
1077+
}
10351078
}
1079+
1080+
int bodyJmp = -1;
1081+
int incStart = -1;
10361082

1037-
int bodyJmp = emitJmp(compiler); //
1038-
int incStart = compiler->func->chunk.count;
1039-
loop->start = incStart;
1083+
if(!isForeach){
1084+
int bodyJmp = emitJmp(compiler);
1085+
int incStart = compiler->func->chunk.count;
1086+
loop->start = incStart;
10401087

1041-
if(!match(compiler, TOKEN_RIGHT_PAREN)){
1042-
ExprDesc incExpr;
1043-
expression(compiler, &incExpr);
1044-
freeExpr(compiler, &incExpr);
1045-
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after loop increment.");
1088+
if(!match(compiler, TOKEN_RIGHT_PAREN)){
1089+
ExprDesc incExpr;
1090+
expression(compiler, &incExpr);
1091+
freeExpr(compiler, &incExpr);
1092+
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after loop increment.");
1093+
}
1094+
1095+
int loopJmpIndex = emitJmp(compiler);
1096+
int loopOffset = loopStart - loopJmpIndex - 1;
1097+
compiler->func->chunk.code[loopJmpIndex] = CREATE_AsBx(OP_JMP, 0, loopOffset);
1098+
patchJump(compiler, bodyJmp);
10461099
}
10471100

1048-
int loopJmpIndex = emitJmp(compiler);
1049-
int loopOffset = loopStart - loopJmpIndex - 1;
1050-
compiler->func->chunk.code[loopJmpIndex] = CREATE_AsBx(OP_JMP, 0, loopOffset);
1051-
patchJump(compiler, bodyJmp);
1101+
10521102
stmt(compiler);
10531103

1054-
int incJmp = emitJmp(compiler);
1055-
int incOffset = incStart - incJmp - 1;
1056-
compiler->func->chunk.code[incJmp] = CREATE_AsBx(OP_JMP, 0, incOffset);
1104+
if(isForeach){
1105+
emitLoop(compiler, loopStart);
1106+
}else{
1107+
int incJmp = emitJmp(compiler);
1108+
int incOffset = incStart - incJmp - 1;
1109+
compiler->func->chunk.code[incJmp] = CREATE_AsBx(OP_JMP, 0, incOffset);
1110+
}
1111+
1112+
10571113
if(exitJmp != -1){
10581114
patchJump(compiler, exitJmp);
10591115
}
@@ -1757,6 +1813,7 @@ static void handleString(Compiler* compiler, ExprDesc* expr, bool canAssign){
17571813
emitABC(compiler, OP_ADD, resReg, resReg, tmpExpr.data.loc.index);
17581814
freeRegs(compiler, 1);
17591815
}
1816+
advance(compiler);
17601817
}else{
17611818
consume(compiler, TOKEN_INTERPOLATION_START, "Expect string or interpolation.");
17621819
expression(compiler, &tmpExpr);

vm/vm.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,8 @@ static InterpreterStatus run(VM* vm){
377377
[OP_FILL_LIST] = &&DO_OP_FILL_LIST,
378378
[OP_BUILD_MAP] = &&DO_OP_BUILD_MAP,
379379
[OP_SLICE] = &&DO_OP_SLICE,
380+
381+
[OP_FOREACH] = &&DO_OP_FOREACH,
380382
};
381383

382384
#ifdef DEBUG_TRACE
@@ -1276,6 +1278,55 @@ static InterpreterStatus run(VM* vm){
12761278

12771279
} DISPATCH();
12781280

1281+
DO_OP_FOREACH:
1282+
{
1283+
int a = GET_ARG_A(instruction);
1284+
int b = GET_ARG_B(instruction);
1285+
1286+
Value iter = R(b);
1287+
Value state = R(b + 1);
1288+
1289+
bool hasNext = false;
1290+
1291+
if(IS_LIST(iter)){
1292+
ObjectList* list = AS_LIST(iter);
1293+
int index = IS_NUM(state) ? 0 : (int)AS_NUM(state);
1294+
if(index < list->count){
1295+
R(b + 2) = list->items[index];
1296+
R(b + 1) = NUM_VAL(index + 1);
1297+
hasNext = true;
1298+
}
1299+
}else if(IS_MAP(iter)){
1300+
ObjectMap* map = AS_MAP(iter);
1301+
int index = IS_NUM(state) ? 0 : (int)AS_NUM(state);
1302+
while(index < map->table.capacity){
1303+
if(!IS_NULL(map->table.entries[index].key)){
1304+
R(b + 2) = map->table.entries[index].key;
1305+
R(b + 1) = NUM_VAL(index + 1);
1306+
hasNext = true;
1307+
break;
1308+
}
1309+
index++;
1310+
}
1311+
if(!hasNext){
1312+
R(b + 1) = NUM_VAL(index);
1313+
}
1314+
}else if(IS_STRING(iter)){
1315+
ObjectString* str = AS_STRING(iter);
1316+
int index = IS_NUM(state) ? 0 : (int)AS_NUM(state);
1317+
if(index < str->length){
1318+
char chars[2] = {str->chars[index], '\0'};
1319+
R(b + 2) = OBJECT_VAL(copyString(vm, chars, 1));
1320+
R(b + 1) = NUM_VAL(index + 1);
1321+
hasNext = true;
1322+
}
1323+
}else{
1324+
runtimeError(vm, "Object is not iterable.");
1325+
return VM_RUNTIME_ERROR;
1326+
}
1327+
R(a) = BOOL_VAL(hasNext);
1328+
} DISPATCH();
1329+
12791330
DO_OP_BUILD_MAP:
12801331
{
12811332
int a = GET_ARG_A(instruction);

0 commit comments

Comments
 (0)