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


如果要将Lua的编译器和虚拟机通过一张图串联起来 , 那么图4则展示了这一点 。
图4
Lua的的主要工作 , 就是进行语法分析和语义分析 , 最后直接生成 , 将作为IR交由vm去执行 。
本章要实现的部分
在继续讨论之前 , 我们首先要理清楚两个概念 , 即是和 。首先我们来看看 , 在一门语言中什么是?是一个表达式 , 表达式最终能够演变成一个值 , 并且这个值可以被赋值到一个变量中 , 符合这个条件的就是否则就是[4] 。而则是一系列操作的集合 , 它其他包含一系列的或者 。是一门语言中单独的一个单元 , 一门语言中 , 有不同的类别 , 并且这些有限类别的组成的序列 , 构成一门语言的逻辑代码 。
我们现在来看一个例子 , 如下所示:
a = 1b = 1+2*(3-4)c = trued = nil
左边所示的a、b、c、d是变量 , 右边的1、1+2*(3-4)、true和nil , 最终能够演变成一个值 , 并且可以赋值给左边的变量 , 因此他们是表达式 , 而每一行的赋值语句 , 则是单独的 。
此外 , 我们还有以下几种 , 并且这些都不能演变成一个值 , 并赋值到一个变量中:
// do end statementdo block end // while statementwhile exp do block end// repeat statementrepeat block until exp// if statementif exp then block {elseif exp then block} [else block] end// for statementfor Name `=′ exp `,′ exp [`,′ exp] do block endfor namelist in explist1 do block end// function statementfunction funcname funcbody// local statementlocal function Name funcbodylocal namelist [`=′ explist1]// expr statementvarlist1 `=′ explist1functioncall
这里需要注意的是 , 在lua中 , 是个单独的类型 , 因此 是可以直接被赋值到某个变量的 , 因此从某种意义上来说 , 它也是 , 但是传统定义上 , 比如我们的c语言 , 这些都是作为存在的 , 我们现在需要关注这个特例 。此外 , 是在expr 分支里调用的 , 但是它的实现是包含在的函数里的 , 在lua中其本质上也被归类为 。
我们现在来复习一下 , 构成lua语言的EBNF范式表达 , 从最顶层的层面来看 , 我们可以看到如下的结果:
chunk ::= {stat [`;′]} [laststat[`;′]]
其中stat表示 , 表示last。一个lua脚本表示为一个chunk , 一段被输入解释器的代码字符串也是一个chunk , 上述的范式很好地表达了 , 是构成lua代码的最基础的单元 。虽然Part5中有对EBNF范式进行说明 , 这里我们再来复习一下它的用法:
如果你忘记和的概念 , 可以回到Part5的EBNF章节复习一下 。
现在我们再来看一下 , 这个stat和的EBNF表达:
stat ::= ifstat | whilestat | dostat | forstat | repeatstat | functionstat | localstat | labelstat | returnstat | gotostat | exprstatlaststat ::= return [explist]|break
现在我们通过一张图来展示它的类别:
图5
我们已知 , lua代码是由一系列的组合而成 , 因此在在编译过程中 , 就是要将脚本代码中的识别出来 , 并且进入到对应的编译分支 , 将其编译为虚拟机指令 , 由于篇幅所限 , 我无法在本章展示所有的编译分支逻辑 , 因此本章集中力量 , 对expr 的设计和实现进行详细的分析 。这也是本章要实现的部分 。