一步步调试,在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源码学习篇二:语法分析的更多相关文章

  1. lua源码学习篇一:环境部署

    研究生即将毕业,答辩完成后,这几天有些时间.开始写一些自己的东西,记录自己的学习历程. --前言 本着学习和交流的原则,本文的内容仅供参考,而不是权威版本,如有任何问题,欢迎指出. --声明 跨专业考 ...

  2. lua源码学习篇三:赋值表达式解析的流程

    上节说到表达式的解析问题,exprstate函数用于解析普通的赋值表达式.lua语言支持多变量赋值.本文先从单变量赋值表达式讲起. a = b = c = a + b 对于简单的两个数的求和过程,lu ...

  3. lua源码学习篇四:字节码指令

    在llimits.h文件中定义了指令的类型.其实就是32个字节. typedef lu_int32 Instruction; 上节说到变量最终会存入proto的数组k中,返回的索引放在expdesc ...

  4. java源码学习(二)Integer

    Integer类包含了一个原始基本类型int.Integer属性中就一个属性,它的类型就是int. 此外,这个类还提供了几个把int转成String和把String转成int的方法,同样也提供了其它跟 ...

  5. Java集合源码学习(二)ArrayList分析

    >>关于ArrayList ArrayList直接继承AbstractList,实现了List. RandomAccess.Cloneable.Serializable接口,为什么叫&qu ...

  6. Spring源码学习(二)AOP

    ----ProxyFactoryBean这个类,这是AOP使用的入口---- AOP有些特有的概念,如:advisor.advice和pointcut等等,使用或配置起来有点绕,让人感觉有些距离感,其 ...

  7. Java集合源码学习(二)ArrayList

    1.关于ArrayList ArrayList直接继承AbstractList,实现了List. RandomAccess.Cloneable.Serializable接口,为什么叫"Arr ...

  8. jQuery源码学习笔记二

    //添加实例属性和方法 jQuery.fn = jQuery.prototype = { // 版本,使用方式:$().jquery弹出当前引入的jquery的版本 jquery: core_vers ...

  9. Guava源码学习(二)Ordering

    基于版本:Guava 22.0 Wiki:Ordering 0. Ordering简介 Guava的Ordering提供了链式风格的比较器的实现,我们可以用Ordering轻松构建复杂的比较器. 1. ...

随机推荐

  1. 上载和下载CSV文件

    sap中把txt .excel .文件上载到内表中,txt和csv速度最快. excel文件导出的csv是用,分隔符分隔的,如果单元格的文本中就有逗号,这样会和分隔符逗号混淆,最好abap产生csv文 ...

  2. iOS-NSLog发布时取消打印日志

    1 选择工程的Target -> Build Settings -> Preprocessor Macros. 如图,默认 Debug项,是“DEBUG=1”. 2 在程序中设置全局宏定义 ...

  3. byteArray转换为double,int

    /*将int转为低字节在前,高字节在后的byte数组   b[0] = 11111111(0xff) & 01100001   b[1] = 11111111(0xff) & (n & ...

  4. Linux系统账户管理指令

    sudo passwd -l weblogic 锁定账户 sudo passwd -u weblogic 解锁账户 useradd weblogic -p xxxxx 添加账户指定密码 sudo us ...

  5. C#实现Base64处理加解密

    using System;using System.Text; namespace Common{    /// <summary>    /// 实现Base64加密解密    ///  ...

  6. 【转载】VMware下的Ubuntu用ifconfig不能显示ip地址的解决方案

    转载于 http://blog.163.com/wjn_mcu/blog/static/23801601620146161062704/ 背景 在虚拟机下运行操作系统,尤其是Linux系统已经是非常常 ...

  7. Maven工程构建

    Maven 相关术语: 本地资源库:用来存储项目的依赖库,默认的文件夹是 “.m2” 目录 中央存储库: 用来下载所有项目的依赖库的默认位置 Maven pom.xml: 项目对象模型(Project ...

  8. jquery 判断文字是否超出div出现三个点的省略号

    现在有个需求,就是一个div宽度固定,但是文字可能会超出,超出出现三个点省略,然后鼠标划入的时候显示全部,不超出鼠标划入就不显示,这就意味着要判断文字是否超出了 参考代码 <html lang= ...

  9. Error: pgraster_wkb_reader: grayscale band type 10 unsupported

    错误原因:Float32 is not supported for rendering.即栅格数据类型Float32不支持. cmd中用GDAL查看.在波段中的数据类型是Float32的不支持渲染. ...

  10. OI实用网址

    LaTeX公式编辑器 https://www.codecogs.com/latex/eqneditor.php http://latex.91maths.com/ 图.坐标系绘制 https://cs ...