一步步调试,在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. JVM内存分配和垃圾回收以及性能调优

    JVM内存分配策略 一:堆中优先分配Eden 大多数情况下,对象都在新生代的Eden区中分配内存.而新生代会频繁进行垃圾回收. 二:大对象直接进入老年代 需要大量连续空间的对象,如:长字符串.数组等, ...

  2. 32-第3章 数据链路层--抓包分析数据帧格式-ISO一图了然-小结

    OSI理论模型 层级 名称 事物举例 功能 数据单位 别名 数据组成 协议举例 7 应用层 QQ.OA 网络通信 上层数据 上层数据 HTTP/FTP/DNS 6 表示层 web数据压缩.https加 ...

  3. 一道有关#define的题

    题目是:查看以下代码,问结果是什么? 结果是打印出“array:16345678910”吗? #include "stdafx.h" #include <iostream&g ...

  4. keymaps - 对键盘映射文件的描述

    描述 (DESCRIPTION) loadkeys(1) 能够 通过 调入 指定的 文件 修改 键盘翻译表, 键盘翻译表 通常 用于 内核的 键盘驱动程序; 另外 dumpkeys(1) 可以 根据 ...

  5. Centos 7 SSh--端口号的更改

    前言:开启某服务或软件的端口,要从该服务或软件监听的端口(多以修改配置文件为主),SeLinux和防火墙(FireWall)的安全策略下手.如果使用阿里云,腾讯等第三方服务器还需要对管理控制台的安全组 ...

  6. Big Data(一)分治思想

    按照课程安排,接下来半年,我将会去上一个为期半年的大数据课程.第一课是马士兵老师机构的周老师所讲,这里单纯记录讲课的内容. 问题1: 我有一万个元素(比如数字或单词)需要存储? 如果查找某一个元素,最 ...

  7. oracle pl/sql 程序设计 历史笔记整理

    20131016 周三 oracle pl/sql 程序设计 第2章 创建并运行pl/sql代码 sqlplus yjkhecc/yjkhecc@10.85.23.92:1521/orcl 在java ...

  8. 费用流 Dijkstra 原始对偶方法(primal-dual method)

    简单叙述用Dijkstra求费用流 Dijkstra不能求有负权边的最短路. 类似于Johnson算法,我们也可以设计一个势函数,以满足在与原图等价的新图中的边权非负. 但是这个算法并不能处理有负圈的 ...

  9. DevExpress v19.1新版亮点——WinForms篇(五)

    行业领先的.NET界面控件DevExpress v19.1终于正式发布,本站将以连载的形式介绍各版本新增内容.在本系列文章中将为大家介绍DevExpress WinForms v19.1中新增的一些控 ...

  10. 使用纯注解方式实现账户的CRUD

    1 需求和技术要求 1.1 需求 实现账户的CRUD. 1.2 技术要求 使用Spring的IOC实现对象的管理. 使用QueryRunner作为持久层的解决方案. 使用C3p0作为数据源. 2 搭建 ...