上 构建Lua解释器Part7:构建完整的语法分析器(22)


第二个例子是:
foo:bar(a, b, c)
其生成的虚拟机指令如下所示:
1 lua.dumpfile_name = ../test.lua.out+proto+p|+linedefine [0]|+maxstacksize [5]|+locvars|+k+1 [foo]|| +2 [bar]|| +3 [a]|| +4 [b]|| +5 [c]|+code+1 [iABC:OP_GETTABUPA:0B:0C:256 ; R(A) := UpValue[B][RK(C)]]||+2 [iABC:OP_SELFA:0B:0C:257 ; R(A+1) := R(B); R(A) := R(B)[RK(C)]]||+3 [iABC:OP_GETTABUPA:2B:0C:258 ; R(A) := UpValue[B][RK(C)]]||+4 [iABC:OP_GETTABUPA:3B:0C:259 ; R(A) := UpValue[B][RK(C)]]||+5 [iABC:OP_GETTABUPA:4B:0C:260 ; R(A) := UpValue[B][RK(C)]]||+6 [iABC:OP_CALLA:0B:5C:1; R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1))]||+7 [iABC:OP_RETURNA:0B:1; return R(A), ... ,R(A+B-2)(see note)]|+type_name [Proto]|+lineinfo+1 [1]||+2 [1]||+3 [1]||+4 [1]||+5 [1]||+6 [1]||+7 [1]|+is_vararg [1]|+lastlinedefine [0]|+numparams [0]|+source [@../test.lua]|+upvalues+1+instack [1]|+idx [0]|+name [_ENV]+upvals+1+name [_ENV]+type_name [LClosure]
构造和编译
我们现在回顾一下 , 的EBNF范式 , 如下所示:
assignment ::= suffixedexp {, suffixedexp} ['=' explist]
这样的 , 一定要有一个等于号 , 位于等于号左边的是 , 但是不能是类型的 , 而右边的则是exp列表 , 这个的工作 , 就是将右边的exp赋值到左边的 , 其函数实现如下所示:
// luaparser.c546 static void assignment(FuncState* fs, LH_assign* v, int nvars) {547expdesc* var = &v->v;548check_condition(fs->ls, vkisvar(var), "not var");549 550LH_assign lh;551lh.prev = v;552 553expdesc e;554init_exp(&e, VVOID, 0);555if (fs->ls->t.token == ',') {556luaX_next(fs->ls->L, fs->ls);557suffixedexp(fs->ls->L, fs->ls, fs, &lh.v);558 559assignment(fs, &lh, nvars + 1);560}561else if (fs->ls->t.token == '=') {562luaX_next(fs->ls->L, fs->ls);563int nexps = explist(fs, &e);564adjust_assign(fs, nvars, nexps, &e);565}566else {567luaX_syntaxerror(fs->ls->L, fs->ls, "syntax error");568}569 570init_exp(&e, VNONRELOC, fs->freereg - 1);571luaK_storevar(fs, &v->v, &e);572 }
代码片段9
我们可以看到是递归调用的 , 我现在将通过一个例子 , 来阐述赋值语句的编译流程:
a, b = c, d
我们通过图88来说明这个流程:
图88
从图88 , 我现在梳理出如下的信息:
完成整个编译以后 , 得到图89的结果:
图89
赋值顺序 , 是根据列表 , 从右到左的顺序 , 赋值的内容 , 则是从-1开始 , 朝R(0)方向的顺序 。到现在为止 , 我就完成了编译流程的论述了 。
虚拟机部分
我们前面已经阐述过 , 虚拟机指令的操作流程 , 这里不再赘述 , 虚拟机指令实现全部在luavm.h|c文件内 , 读者可以到这些文件中去查阅相关的操作 , 这里不再进行说明 , 因为通过前面的指令说明 , 再结合源码 , 相信读者可以很容易地就消化理解 。
结束语
到现在为止 , 我就完成了【构建Lua解释器:构建完整的语法分析器(上)】的论述 。本篇我花费了巨量的精力在的编译流程的论述之上 。我们首先探讨了编译器的概念 , 编译器的组成 , 然后探讨了lua编译器的工作 , 以及本篇要实现编译器的哪些部分 , 后来我们又探索了的编译流程 , 包括expr、和的构造和编译流程 。本文并未展示过多的代码 , 而是通过EBNF范式描述lua的语法 , 然后仅仅只是展现了主要流程的代码 , 最后将一些关键函数 , 通过一系列构图的方式 , 展现其编译流程 。抽象这些操作能够让我们更好地理解lua 的运作流程 , 下篇 , 我将完整实现lua的编译器 。