词法分析器在llex.h, llex.c两个文件中实现。

1 /* maximum length of a reserved word */
2 #define TOKEN_LEN       (sizeof("function")/sizeof(char))

这一句说的意思是,function是Lua的保留字中最长的保留字,其它所有的保留字单词长度不应该超过它。

01 enum RESERVED {
02   /* terminal symbols denoted by reserved words */
03   TK_AND = FIRST_RESERVED, TK_BREAK,
04   TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
05   TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
06   TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
07   /* other terminal symbols */
08   TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,
09   TK_NAME, TK_STRING, TK_EOS
10 };

这一句定义了保留字的内部符号,叫做“终止符”,意思就是词法分析器一找到这些单词,就自动记录并断开。
前21个是基础的保留字的程序中对应的宏名称,后10个是一些运算符对应的宏名称。定义这样的符号集,是为了后面写解析器时方便返回值。

01 /* ORDER RESERVED */
02 const char *const luaX_tokens [] = {
03     "and""break""do""else""elseif",
04     "end""false""for""function""if",
05     "in""local""nil""not""or""repeat",
06     "return""then""true""until""while",
07     "..""...""=="">=""<=""~=",
08     "<number>""<name>""<string>""<eof>",
09     NULL
10 };

这些是真实的保留字,与上面那个与前一个完全是一一对应的,所以要变位置的话,要两个同时变。这里写明了真正的保留字符号,存在数组luaX_tokens 里面。

1 /* number of reserved words */
2 #define NUM_RESERVED    (cast(int, TK_WHILE-FIRST_RESERVED+1))

这一句是计算所有的保留字的个数。FIRST_RESERVED在前面被定义为257,不知道为什么要这么定义。设计这样一个单数值的方式很巧妙。使得后面编程时,只需返回一个单变量索引值就可以知道扫描到一个什么记号了。给后面编程带来很大简化工作。注意:TK_WHILE是保留字中的最后一个保留字。

01 typedef struct LexState {
02   int current;  /* current character (charint) */
03   int linenumber;  /* input line counter */
04   int lastline;  /* line of last token `consumed' */
05   Token t;  /* current token */
06   Token lookahead;  /* look ahead token */
07   struct FuncState *fs;  /* `FuncState' is private to the parser */
08   struct lua_State *L;
09   ZIO *z;  /* input stream */
10   Mbuffer *buff;  /* buffer for tokens */
11   TString *source;  /* current source name */
12   char decpoint;  /* locale decimal point */
13 } LexState;

LexState是词法解析器在解析过程中的一个状态结构。这里面这些结构都非常重要。

成员变量 意义
current 指示当前的字符 (相对于文件开头的偏移位置),可以看做一个指针,实际是一个索引;
linenumber 指示当前解析器的current指针的行位置;
lastline 指示当前文件里面的最后一个有作用的记号所在的行;
t 当前的记号;
lookahead 下一个 (待分析的) 记号;
fs 函数状态指针,暂时不明何用;
L Lua的状态机指针;
z 输入流;
buff 用于存储所有记号的一个缓存;
source 当前源码的的名字,是不是文件名还不太清楚;
decpoint 当前十进制小数点?不太清楚。后面给其赋值为’.'。看后面的代码,估计是认为在有些地区或行业里面,十进制小数点不一定是’.'。所以当一个变量先存起来了。

下面继续往下看:

01 /*
02 ** String headers for string table
03 */
04 typedef union TString {
05   L_Umaxalign dummy;  /* ensures maximum alignment for strings */
06   struct {
07     CommonHeader;
08     lu_byte reserved;
09     unsigned int hash;
10     size_t len;
11   } tsv;
12 } TString;

Lua中字符串表的一个头结构,在整个解析过程字符串的存储中起到很重要的作用。

1 void luaX_init (lua_State *L) {
2   int i;
3   for (i=0; i<NUM_RESERVED; i++) {
4     TString *ts = luaS_new(L, luaX_tokens[i]);
5     luaS_fix(ts);  /* reserved words are never collected */
6     lua_assert(strlen(luaX_tokens[i])+1 <= TOKEN_LEN);
7     ts->tsv.reserved = cast_byte(i+1);  /* reserved word */
8   }
9 }

luaX_init 函数的功能是将所所的保留字设置标志量,以便在搜集的时候,会自动略过这些保留字。luaS_fix功能是给保留字变量设置防搜集标志。ts->tsv.reserved = cast_byte(i+1) 给各个保留字编序号,以1起始开始编号。

1 const char *luaX_token2str (LexState *ls, int token) {
2   if (token < FIRST_RESERVED) {
3     lua_assert(token == cast(unsigned char, token));
4     return (iscntrl(token)) ? luaO_pushfstring(ls->L,"char(%d)", token) :
5                               luaO_pushfstring(ls->L,"%c", token);
6   }
7   else
8     return luaX_tokens[token-FIRST_RESERVED];
9 }

luaX_token2str将以数值形式表示的记号序号转换成相应的字符,如果是大于256的值,会直接转换为保留字(多个字符),而不是一个字符。只有小于等于256的,才转换为一个字符,并且这其中还要区分普通字符还是控制字符。 这里就体现出它设计FIRST_RESERVED等于257的用意了,统一化,一次性处理。

Lua 5.1.3源代码分析之词法分析[1]相关推荐

  1. php输出源代码,PHP源代码分析-echo实现详解

    PHP源代码分析-echo实现详解 echo,这个是PHP运用得最多的标记之一,算不上是函数,PHP手册里这么写的,因为它没有返回值.今天好奇就去看看PHP的源代码,因为echo不是一般的函数,所以找 ...

  2. Android系统默认Home应用程序(Launcher)的启动过程源代码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...

  3. 《LINUX3.0内核源代码分析》第一章:内存寻址

    https://blog.csdn.net/ekenlinbing/article/details/7613334 摘要:本章主要介绍了LINUX3.0内存寻址方面的内容,重点对follow_page ...

  4. Scrapy源代码分析-经常使用的爬虫类-CrawlSpider(三)

    CrawlSpider classscrapy.contrib.spiders.CrawlSpider 爬取一般站点经常使用的spider.其定义了一些规则(rule)来提供跟进link的方便的机制. ...

  5. Android 中View的绘制机制源代码分析 三

    到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...

  6. Android应用程序进程启动过程的源代码分析(1)

    Android应用程序框架层创建的应用程序进程具有两个特点,一是进程的入口函数是ActivityThread.main,二是进程天然支持Binder进程间通信机制:这两个特点都是在进程的初始化过程中实 ...

  7. AFNetworking 源代码分析

    关于其他 AFNetworking 源代码分析的其他文章: AFNetworking 概述(一) AFNetworking 的核心 AFURLSessionManager(二) 处理请求和响应 AFU ...

  8. Hadoop源代码分析 - MapReduce(转载)

    1. Hadoop源代码分析(MapReduce概论) http://caibinbupt.javaeye.com/blog/336467

  9. RTMPdump(libRTMP) 源代码分析 3: AMF编码

    2019独角兽企业重金招聘Python工程师标准>>> 注:此前写了一些列的分析RTMPdump(libRTMP)源代码的文章,在此列一个列表: RTMPdump 源代码分析 1: ...

最新文章

  1. 目标检测--Object Detection via Aspect Ratio and Context Aware
  2. Android fill_parent、wrap_content和match_parent的区别
  3. js deep clone 深克隆
  4. python下载手机-QPython下载
  5. 【干货】H3C如何配置VLAN-trunk 二层隔离技术?
  6. 【收藏】CentOS 7 安装NFS
  7. bzoj 1221: [HNOI2001] 软件开发
  8. MFC学习之路之多媒体 --(1) DirectShow
  9. C++STL与泛型编程 侯捷 (1)
  10. 如何执行字符串的PHP代码
  11. CentOS查看分区的方式
  12. eclipse cdt无法调试_eclipse导入Web项目并且部署到tomcat(超详细)
  13. c#.net利用RNGCryptoServiceProvider产生任意范围强随机数的办法
  14. 交易系统开发(七)——交易延迟分析
  15. app录制回放 jmeter_Jmeter使用之脚本录制回放
  16. CentOS安装Nacos后,输入默认用户名和密码nacos/nacos,提示“用户名或密码错误”
  17. 基于BP神经网络的车牌识别问题研究附Matlab代码
  18. Centos 7分区合并
  19. 04 【CSS选择器 】
  20. 算法分析:时间和空间复杂度

热门文章

  1. 计算机与人脑的出版社,计算机与人脑
  2. 考取RHCE认证的历程,总结的经验
  3. C# 一键获取本机的详细IP信息
  4. Cache简介(一)
  5. vue 父传子props
  6. 如何对CAD图纸上的图形进行单独保存起来
  7. 信息系统安全防范策略
  8. JS阻止默认行为和Vue阻止默认行为
  9. AT-GAN: A Generative Attack Model for Adversarial Transferring on Generative Adversarial Nets
  10. Attention-GAN