lua源码学习篇二:语法分析
一步步调试,在lparser.c文件中luaY_parser函数是语法分析的重点函数,词法分析也是在这个过程中调用的。在这个过程中,用到一些数据结构,下面会详细说。
Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
struct LexState lexstate;
struct FuncState funcstate;
lexstate.buff = buff;
luaX_setinput(L, &lexstate, z, luaS_new(L, name));
open_func(&lexstate, &funcstate);//初始化funcstate
funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */
luaX_next(&lexstate); //Luax_next用于获取下一个字符
chunk(&lexstate);//代码块分析
check(&lexstate, TK_EOS);//判断lua程序文件是否到达末尾
close_func(&lexstate);//关闭程序
lua_assert(funcstate.prev == NULL);
lua_assert(funcstate.f->nups == );
lua_assert(lexstate.fs == NULL);
return funcstate.f;
}
好,不着急,一步一步来看。lua_State ,LexState ,FuncState 是啥玩意呢?
lua_state是lua程序运行过程中一直存在的,并且一个运行程序只有一个lua_State实例。
struct lua_State {
CommonHeader;
lu_byte status;
StkId top; /* first free slot in the stack */
StkId base; /* base of current function */
global_State *l_G;//全局状态的指针
CallInfo *ci; /* call info for current function */当前函数的调用信息
const Instruction *savedpc; /* `savedpc' of current function */记录上一个函数的pc位置
StkId stack_last; /* last free slot in the stack */
StkId stack; /* stack base */
CallInfo *end_ci; /* points after end of ci array*/函数调用栈的栈顶
CallInfo *base_ci; /* array of CallInfo's */函数调用栈的栈底
int stacksize;
int size_ci; /* size of array `base_ci' */
unsigned short nCcalls; /* number of nested C calls */
lu_byte hookmask;
lu_byte allowhook;
int basehookcount;
int hookcount;
lua_Hook hook;
TValue l_gt; /* table of globals */
TValue env; /* temporary place for environments */
GCObject *openupval; /* list of open upvalues in this stack */
GCObject *gclist;
struct lua_longjmp *errorJmp; /* current error recover point */
ptrdiff_t errfunc; /* current error handling function (stack index) */
};
LexState是用于存储词法分析时的上下文数据。
typedef struct LexState {
int current; /* current character (charint) */指向下一个要读取的字符
int linenumber; /* input line counter */行号
int lastline; /* line of last token `consumed' */
Token t; /* current token */
Token lookahead; /* look ahead token */ 预读的下一个token
struct FuncState *fs; /* `FuncState' is private to the parser */函数状态的数据结构
struct lua_State *L;
ZIO *z; /* input stream */ 输入流
Mbuffer *buff; /* buffer for tokens */ 临时缓冲区
TString *source; /* current source name */ 源文件名
char decpoint; /* locale decimal point */
} LexState;
FuncState是用于存储函数状态的数据结构。
typedef struct FuncState {
Proto *f; /* current function header */函数头信息
Table *h; /* table to find (and reuse) elements in `k' */
struct FuncState *prev; /* enclosing function */指向函数链表的上一个函数
struct LexState *ls; /* lexical state */
struct lua_State *L; /* copy of the Lua state */
struct BlockCnt *bl; /* chain of current blocks */
int pc; /* next position to code (equivalent to `ncode') */
int lasttarget; /* `pc' of last `jump target' */
int jpc; /* list of pending jumps to `pc' */
int freereg; /* first free register */
int nk; /* number of elements in `k' */
int np; /* number of elements in `p' */
short nlocvars; /* number of elements in `locvars' */local变量个数
lu_byte nactvar; /* number of active local variables */
upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */
unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */
} FuncState;
初始化完成后,就要进行词法分析,即读取下一个token,调用luaX_next(&lexstate); 下面进入llex.c文件的源代码中
void luaX_next (LexState *ls) {
ls->lastline = ls->linenumber;
if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
ls->t = ls->lookahead; /* use this one */
ls->lookahead.token = TK_EOS; /* and discharge it */
}
else
ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */调用llex函数读取下一个token
}
llex函数:里面是一大串的switch...case...语句,对各种可能的情况进行处理,正常的变量名或者保留字会进入default语句,分别处理空格,数字或者变量名。
for (;;) {
switch (ls->current) {
case '\n':
case '\r':
case '-':
case '[':
case '=':
case '<':
case '>':
case '~':
case '"':
case '\'':
case '.':
case EOZ:
default: {
if (isspace(ls->current)) {
lua_assert(!currIsNewline(ls));
next(ls);
continue;
}
else if (isdigit(ls->current)) {
read_numeral(ls, seminfo);
return TK_NUMBER;
}
else if (isalpha(ls->current) || ls->current == '_') {
/* identifier or reserved word */
TString *ts;
do {
save_and_next(ls);
} while (isalnum(ls->current) || ls->current == '_');
ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
luaZ_bufflen(ls->buff));
if (ts->tsv.reserved > ) /* reserved word? */
return ts->tsv.reserved - + FIRST_RESERVED;
else {
seminfo->ts = ts;
return TK_NAME;
}
}
else {
int c = ls->current;
next(ls);
return c; /* single-char tokens (+ - / ...) */
}
}
}
}
luaX_newstring用于生成变量名,如果全局变量表中没有该变量的字符串,则会创建新的变量字符串。对每个token,如果是保留字段,都会预先加载在全局变量表中,因此,如果不是保留字段,就会生成TK_NAME。保留字段的判定来自于if (ts->tsv.reserved > 0),关于Token的种类,定义在llex.h头文件中。
获取token字符串后,进入chunk代码:
static void chunk (LexState *ls) {
/* chunk -> { stat [`;'] } */
int islast = ;
enterlevel(ls);//内嵌调用层数
while (!islast && !block_follow(ls->t.token)) {//当前token既不是block的开始也不是结束
islast = statement(ls);//代码语句分析
testnext(ls, ';');
lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&
ls->fs->freereg >= ls->fs->nactvar);
ls->fs->freereg = ls->fs->nactvar; /* free registers */
}
leavelevel(ls);
}
statement函数用于分析语义,里面是也是大大的switch...case...语句。如果是if, while, do, for, function等等关键字,都会进入相应的处理函数中,在default语句中处理赋值和函数调用的分析。
static int statement (LexState *ls) {
int line = ls->linenumber; /* may be needed for error messages */
switch (ls->t.token) {
case TK_IF: { /* stat -> ifstat */
ifstat(ls, line);
return ;
}
case TK_WHILE: { /* stat -> whilestat */
whilestat(ls, line);
return ;
}
case TK_DO: { /* stat -> DO block END */
luaX_next(ls); /* skip DO */
block(ls);
check_match(ls, TK_END, TK_DO, line);
return ;
}
case TK_FOR: { /* stat -> forstat */
forstat(ls, line);
return ;
}
case TK_REPEAT: { /* stat -> repeatstat */
repeatstat(ls, line);
return ;
}
case TK_FUNCTION: {
funcstat(ls, line); /* stat -> funcstat */
return ;
}
case TK_LOCAL: { /* stat -> localstat */
luaX_next(ls); /* skip LOCAL */
if (testnext(ls, TK_FUNCTION)) /* local function? */
localfunc(ls);
else
localstat(ls);
return ;
}
case TK_RETURN: { /* stat -> retstat */
retstat(ls);
return ; /* must be last statement */
}
case TK_BREAK: { /* stat -> breakstat */
luaX_next(ls); /* skip BREAK */
breakstat(ls);
return ; /* must be last statement */
}
default: {
exprstat(ls);
return ; /* to avoid warnings */
}
}
}
语句中的表达式通过exprstat(ls)函数处理,还有lua代码指令的生成,有时间再写。
lua源码学习篇二:语法分析的更多相关文章
- lua源码学习篇一:环境部署
研究生即将毕业,答辩完成后,这几天有些时间.开始写一些自己的东西,记录自己的学习历程. --前言 本着学习和交流的原则,本文的内容仅供参考,而不是权威版本,如有任何问题,欢迎指出. --声明 跨专业考 ...
- lua源码学习篇三:赋值表达式解析的流程
上节说到表达式的解析问题,exprstate函数用于解析普通的赋值表达式.lua语言支持多变量赋值.本文先从单变量赋值表达式讲起. a = b = c = a + b 对于简单的两个数的求和过程,lu ...
- lua源码学习篇四:字节码指令
在llimits.h文件中定义了指令的类型.其实就是32个字节. typedef lu_int32 Instruction; 上节说到变量最终会存入proto的数组k中,返回的索引放在expdesc ...
- java源码学习(二)Integer
Integer类包含了一个原始基本类型int.Integer属性中就一个属性,它的类型就是int. 此外,这个类还提供了几个把int转成String和把String转成int的方法,同样也提供了其它跟 ...
- Java集合源码学习(二)ArrayList分析
>>关于ArrayList ArrayList直接继承AbstractList,实现了List. RandomAccess.Cloneable.Serializable接口,为什么叫&qu ...
- Spring源码学习(二)AOP
----ProxyFactoryBean这个类,这是AOP使用的入口---- AOP有些特有的概念,如:advisor.advice和pointcut等等,使用或配置起来有点绕,让人感觉有些距离感,其 ...
- Java集合源码学习(二)ArrayList
1.关于ArrayList ArrayList直接继承AbstractList,实现了List. RandomAccess.Cloneable.Serializable接口,为什么叫"Arr ...
- jQuery源码学习笔记二
//添加实例属性和方法 jQuery.fn = jQuery.prototype = { // 版本,使用方式:$().jquery弹出当前引入的jquery的版本 jquery: core_vers ...
- Guava源码学习(二)Ordering
基于版本:Guava 22.0 Wiki:Ordering 0. Ordering简介 Guava的Ordering提供了链式风格的比较器的实现,我们可以用Ordering轻松构建复杂的比较器. 1. ...
随机推荐
- keepalived和lvs实现mysql读负载均衡
keepalived+lvs实现对mysql读负载均衡 环境:redhat 6.5 real server: 172.25.254.189 172.25.254.224 lvs: 172.25.254 ...
- (转) Linux权限管理(基本权限、默认权限)
一.文件基本权限 1-1.基本权限的修改 -rw-r--r-- - 第一个"-"表示文件类型(- 文件,d 目录,l 软链接文件) - rw- r-- ...
- 绕过CDN找到真实IP
现在很多大型企业都会使用CDN内容分发网络,因为CDN存在多个缓存服务点,而且会根据用户IP地址,将用户请求导向到最近的服务点上进行相应,所以得不到主服务站点的ip地址,总结学习一下绕过CDN找到真实 ...
- 【洛谷P2894】Hotel 线段树+二分查询
题目大意:给定一个长度为 N 的序列,每个点有两种状态 1/0,表示占有和空闲,现支持 first-fit 查询是否有一段连续的长度为 X 的空闲子序列和区间赋值操作. 题解:get到了线段树新技能. ...
- Pythoy修炼之路
路漫漫其修远兮,吾将上下而求索... 编程语言众多,徘徊在C.C#.Java.PHP等十字路口,看到哪个领域大牛的成功,便想打开那扇窗,反反复复,犹豫不决,终无所获. 个人认为选择一门语言,要根据自身 ...
- Java 11必掌握的8大特性,完美代码信手拈来
[MyEclipse CI 2019.4.0安装包下载] 美国时间 09 月 25 日,Oralce正式发布了Java 11,这是据Java 8以后支持的首个长期版本.从官方发布的支持路线图表看出,J ...
- ZROI 19.07.31 AB班ACM
写在前面:非常感谢cjc和djh两位神仙带我,非常感谢他们给了我一次躺赢的机会. 虽然我被硬点成了代码手,但我写的基本每次都有一堆罚时.然而djh爷全部1A,tql. 题目按照一血时间升序,大致符合难 ...
- python selenium 相关操作
selenium : 是一个用于Web应用程序测试的工具.Selenium测试直接运行在浏览器中,就像真正的用户在操作一样.支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Fi ...
- SpringBoot框架(3)--条件装配
场景:需要根据系统的编码格式有选择装配类. 分析:最直接的实现方式,定义各种编码格式对应的处理类,可以通过System.getProperty("file.encoding")获得 ...
- 【GDOI2017模拟12.9】最近公共祖先
题目 分析 首先,将这些节点按dfs序建一棵线段树. 因为按dfs序,所以在同一子树上的节点会放在线段树相邻的位置. 发现,对于一个位置x,它的权值只会对以x为根的子树造成影响. 当修改x时,用w[x ...