0x0. 原理
控制结构包括条件控制,循环控制。不管是哪一种都可以简化为两部分:
- 语句块:顺序执行的语句组合。
- 跳转:包括循环的条件控制,
if/else
控制,break
。 ```lua while cond do —do something end
— 简化后:
test cond jump after-end block
after-end
```lua
if cond
then
--block1
else
--block2
end
--
简化后
test cond
jump block2
block1
jump after-end
block2
jump test
after-end
语句块
static void block (LexState *ls) {
/* block -> chunk */
FuncState *fs = ls->fs;
BlockCnt bl;
enterblock(fs, &bl, 0);
chunk(ls);
lua_assert(bl.breaklist == NO_JUMP);
leaveblock(fs);
}
跳转
从上面简化后的跳转可以看出有个问题,就是我们在生成跳转语句的时候,其实我们是不知道要跳转到哪里的。因为要跳转的地方是在跳转语句之后。一种很容易想到的做法就是我们把跳转语句都保存下来,等待block都解析完成再去填充跳转语句的值。lua的实现思路也是类似,不过这里的实现比较巧妙,它把各个跳转语句链起来了。如下图所示。
//将一个跳转语句加入到跳转链表中
void luaK_concat (FuncState *fs, int *l1, int l2) {
if (l2 == NO_JUMP) return;
else if (*l1 == NO_JUMP)
*l1 = l2;
else {
int list = *l1;
int next;
while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */
list = next;
fixjump(fs, list, l2);
}
}
//将跳转链表加入到fs->jpc中,生成下一条指令的时候就会去解析jpc链表,将跳转指令修改为正确的值。
//在一个block结束的时候就会调用这个
void luaK_patchtohere (FuncState *fs, int list) {
luaK_getlabel(fs);
luaK_concat(fs, &fs->jpc, list);
}
0x2. while语句
static void whilestat (LexState *ls, int line) {
/* whilestat -> WHILE cond DO block END */
FuncState *fs = ls->fs;
int whileinit;
int condexit;
BlockCnt bl;
luaX_next(ls); /* skip WHILE */
whileinit = luaK_getlabel(fs);//缓存while的第一条语句
condexit = cond(ls);
enterblock(fs, &bl, 1);
checknext(ls, TK_DO);
block(ls);
luaK_patchlist(fs, luaK_jump(fs), whileinit);//生成跳转语句到while之前的语句
check_match(ls, TK_END, TK_WHILE, line);
leaveblock(fs); //这里面会处理breaklist,也就是通过break跳出的jump语句
luaK_patchtohere(fs, condexit); //将条件判断语句的jump重置为下一条语句
}
0x3. ifelse
static void ifstat (LexState *ls, int line) {
/* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */
FuncState *fs = ls->fs;
int flist;
int escapelist = NO_JUMP;
flist = test_then_block(ls); /* flist即为if的跳转语句*/
while (ls->t.token == TK_ELSEIF) {
luaK_concat(fs, &escapelist, luaK_jump(fs));
luaK_patchtohere(fs, flist);/*循环将flist更新为elseif语句*/
flist = test_then_block(ls); /* ELSEIF cond THEN block */
}
if (ls->t.token == TK_ELSE) {
luaK_concat(fs, &escapelist, luaK_jump(fs));
luaK_patchtohere(fs, flist);
luaX_next(ls); /* skip ELSE (after patch, for correct line info) */
block(ls); /* `else' part */
}
else
luaK_concat(fs, &escapelist, flist);
luaK_patchtohere(fs, escapelist);/*escapelist即为各个block执行完要跳转的链表*/
check_match(ls, TK_END, TK_IF, line);
}