最近实在是忙,项目就快要发布了,加班加点在所难免。继上一篇关于lua基本数据类型的简单分析后,我将继续写阅读lua虚拟机的源代码笔记,这是第一次接触虚拟机工作原理,lua虚拟机代码量不大,是个很好的学习的例子,应当坚持下去。关于语法分析,我觉得只需要弄清楚从哪分析,怎么分析以及最终的生成结果就差不多了。
当通过调用lstate.c:lua_newstate()方法生成一个新的lua_State时,其内部又会调用llex.c:luaX_init方法,该方法的作用是生成lua的关键字(保留字)到global_State的字符串表中(stringtable,以哈希表的形式保存),关于lua的保留字,在llex.c中可看到下面这段:
/* ORDER RESERVED */
static const char *const luaX_tokens [] = {
"and", "break", "do", "else", "elseif",
"end", "false", "for", "function", "goto", "if",
"in", "local", "nil", "not", "or", "repeat",
"return", "then", "true", "until", "while",
"..", "...", "==", ">=", "<=", "~=", "::", "<eof>",
"<number>", "<name>", "<string>"
};
而真正让虚拟机开始分析源代码,则是通过lapi.c:lua_load函数,lauxlib.c中则封装了对lua_load的调用,其中包括从文件加载源码的分析lauxlib.c:luaL_loadfilex,以及从内存加载lauxlib.c:luaL_loadbufferex。在lua_load中,除了必要的初始化,比如生成一个ZIO对象(流对象)负责处理文件或字符串的输入,还调用了ldo.c:luaD_protectedparser函数,而在这个函数中,又最终调用了lparser.c:luaY_parser函数,好了,虚拟机开始进入源码的分析阶段了,代码如下:
Closure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
Dyndata *dyd, const char *name, int firstchar) {
LexState lexstate; // 扫描状态机,单词读取、步进等操作
FuncState funcstate; // 函数状态机,每输入一个源代码文件或者是一段源代码字符串,都将有一个状态机对应
Closure *cl = luaF_newLclosure(L, 1); /* create main closure */
/* anchor closure (to avoid being collected) */
setclLvalue(L, L->top, cl);
incr_top(L);
funcstate.f = cl->l.p = luaF_newproto(L);
funcstate.f->source = luaS_new(L, name); /* c