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


exprstat ::= funccall | assignmentassignment ::= suffixedexp {, suffixedexp} ['=' explist]funccall ::= suffixedexp
这里清晰地展示了 , 主要的两种形态 , 我们的代码 , 则如下所示:
// luaparser.c574 static void exprstat(struct lua_State* L, LexState* ls, FuncState* fs) {575LH_assign lh;576suffixedexp(L, ls, fs, &lh.v);577 578if (ls->t.token == '=' || ls->t.token == ',') {579assignment(fs, &lh, 1);580}581else {582check_condition(ls, lh.v.k == VCALL, "exp type error");583}584 }
代码片段3
上面的代码 , 与前面的EBNF范式基本吻合 , 这里希望读者仔细观察一下 。但是上面的方式有点让人疑惑 , 就是赋值语句中 , 等号左边的必然是变量 , 而不能是函数调用 , 上面的EBNF范式清晰地暗示了 , 既能是一个变量 , 也能是一个函数调用 。这似乎并不符合我们的直观直觉 , 实际上下面的定义 , 似乎更直观:
exprstat ::= funccall | assignmentnameexp::= primaryexp { '.' TK_NAME | '[' expr ']' }funccall ::= nameexp ( ':' TK_NAME funcargs | funcargs )assignment ::= nameexp { ',' nameexp } '=' explist
相对应的 , 它的伪代码如下所示 , 更符合我们的直觉:
void exprstat() {if exp is funccall then funccallelse if exp is assignment thenassignmentelse syntax errorend}
代码片段4
多么nice的逻辑 。但是 , 很快我们就要遇到一个现实的问题 , 那就是 , 我怎么知道当前的这个是还是?如图10所示 , 我们进入逻辑分支的时候 , 当前的token是一个类型的 , 同时 , 也正如图10所示 , 不管是还是 , 他们的首个token都是类型 , 而刚进入这段逻辑时 , 我们只有中首个token的信息 , 单靠这个信息我们无法判定这个是还是 。因此lua采取的方式就是 , 不管怎样 , 先把第一个exp识别出来 , 如果它是个变量 , 那么就必须当做赋值语句处理 , 如果是个 , 那么处理直接结束 。回到代码片段3的逻辑 , 的逻辑是 , 不管三七二十一 , 先把expr识别出来再说 , 而做这件事情的则如576行代码所示 , 通过函数来识别 。但是在开始讨论的编译流程之前 , 我们首先要看看expr的处理 。
expr的构造与编译
1、的识别与处理
expr是的缩写 , 意为表达式 , 在lua的语法分析器中 , 它的作用就是将表达式识别出来 , 并将对应的信息存储到一个中间的结构之中 。为什么要先从它开始讨论呢?因为它是组成的基础 , 我们先来看一下它的EBNF范式:
expr ::= (simpleexp | unop expr) {binop expr}simpleexp ::= FLT|INT|STRING|NIL|TRUE|FALSE|...|constructor|FUNCTION body|suffixedexp
前面我们也介绍过 , expr最终的归宿就是计算成一个确切的值 , 这个确切的值是什么意思呢?就是expr最后可以演变成中 , 除了…和以外的任意一种类型的值 , 这些值实际上就是lua的基本类型的值 。从上面的EBNF范式来看 , 它以或者一个单目运算的exp为开头 , 后面可能跟一堆双目运算操作 , 但不论如何 , 最终的结果都是演变为一个lua基本类型的值 。expr的逻辑如下所示:
// luaparser.c349 static const struct {350lu_byte left;// left priority for each binary operator351lu_byte right;// right priority352 } priority[] = {353{10,10}, {10,10},// '+' and '-'354{11,11}, {11,11}, {11,11}, {11, 11}, // '*', '/', '//' and '%'355{14,13},// '^' right associative356{6,6}, {4,4}, {5,5},// '&', '|' and '~'357{7,7}, {7,7},// '