Lua编译实践1-词法分析:
前言:
编译实践系列将以lua的词法,语法和语意为标准,用C++实现lua的解释器,这其中也会参考lua解释器官方实现,但重点在于理清编译系统的基本框架和每个环节最基本的实现。
一般的编译系统结构:
1 从源码文件读入字符,词法分析阶段即按给定的标准(标识符,分隔符,数字)组装成token,进而形成token流;
2 语法分析阶段,会将token按照编程语言给定的语法模式,组合成节点,最后形成抽象语法树AST;
3 语义分析,说的抽象点就是语言背后的含义,那么谁赋予语言以含义?是解释它的“人”,从来不是语法它自己。俗话说,一千个读者就有一千个哈姆雷特,但在程序语言的世界里,这肯定是行不通的。所以,人们规定一个语法模式只能有一种解释,所有的这些设定都在语言规范(language specification)里。
说了这么多,那么语义到底是什么呢?是变量查找顺序(C++里是先找最近的block,依次向外延伸),是结构体内存对齐的规则,是类型转换规则(C++容许int转double,但不容许int转string,然而lua是可以的)等等,所有这些规则就形成了语义分析。语义分析阶段便是靠这些规则来解释语法的含义的。其实,CPU和内存才是最终的解释器。
4.代码优化可以分为与机器无关的优化和机器相关的优化,说的简单点就是整合,删除不必要的代码。这部分可以单独拿出来,不影响后续流程,所以lua解释器暂时不考虑做优化。
5.代码生成可以是虚拟机的字节码,也可以是x86汇编码,甚至可以是c语言。当然,我会先生成字节码,然后配套做一个虚拟机,解释执行。
本系列相应的源码在:https://github.com/pzhp/lua_interpretor
****************************************************************************
词法分析:
基本篇
1 先看一道常见的面试题:把字符串转换为整数(即实现atoi),如果考虑到实现基本功能(不含溢出处理),这并不难:依次读入字符取得对应的数字再乘以权重累加即可,正负数也只是加个前置判断而已。
2 再给道题,判断一段字符串是否满足一下要求:只能以字母或下划线开始,其余部分数字、字母、下划线均可。我相信这个也不难,从头开始扫描字符串,if判断即可。
3 如果再告诉你一条规则,以 ‘“’ 开头直到遇到 ‘”’结尾,或者以 单引号开头和结尾可判定为字符串常量。
4 最后,增加判定是运算符(+ - * 、 % <= ...)和分隔符(; , ...)的规则,也就可以识别程序语言中的运算符和分割符了。只不过这里会用到一个小技巧,lua里存在 ".", "..", "..." 当字符判定为"."时,会继续预读入下一个字符再判定, 如果第二个字符是"." 则拿到该字符,结果为"..",继续这么处理第三个字符。可以将这个判断转化为查表。
当完成这几个函数,那么需要考虑什么时候选择解析成数字,什么时候解析成字符串...,其核心在于预先看一两个字符做预判,决定走那条路,这里的预测可以采用标准流的peek, get, putback等函数完成,其基本框架如下:
while (true)
{ // init a single token recognise statechar ch = GetNextChar();if (isdigit(ch)){tk = ProcessNumberState(ch);}else if (std::isalpha(ch) || ch == '_'){tk = ProcessIndentifyState(ch);}else if (ch == '\'' || ch == '"'){tk = ProcessStringState(ch);}else if (ch == '-' && PeekNextChar() == '-'){HandleComemnt(ch);continue;}else if (m_dict.haveToken(std::string(1,ch))){tk = ProcessOperatorAndDelim(ch);}else{m_currState = S_Fatal;m_strErrReason = "cannot find dispatch method";}
}
为了存放词法分析的结果,定义Token类,主要含有一下属性:token所在位置(文件,行,列),token类型(数字常量,字符串常量,标识符等),token的值(可以用解析出来字符串代表)。
具体源码请参考scanner.cpp
高级篇
如果你要写针对特定语言的词法分析模块,上面描述就够了,那么为什么编译原理类的书会讲正则文法,有限状态自动机(DFA,NFA),搞的那么复杂呢?一方面为了形式化表述,另外方面也是为了自动化生成,满足可配置性。以lex generator:flex为例:
/* 规定DIGIT为0-9的数,如果规定[0-8],那么9将无法被判定到数字里面 */
DIGIT ([0-9])
HEX_DIGIT ([0-9a-fA-F])
HEX_INTEGER (0[Xx]{HEX_DIGIT}+)
INTEGER ({DIGIT}+)
EXPONENT ([Ee][-+]?{INTEGER})
DOUBLE ({INTEGER}"."{DIGIT}*{EXPONENT}?)
BEG_STRING (\"[^"\n]*)
STRING ({BEG_STRING}\")
IDENTIFIER ([a-zA-Z][a-zA-Z_0-9]*)
OPERATOR ([-+/*%=.,;!<>()[\]{}])
BEG_COMMENT ("/*")
END_COMMENT ("*/")
SINGLE_COMMENT ("//"[^\n]*)/* -------------------- Constants ------------------------------ */
"true"|"false" { yylval.boolConstant = (yytext[0] == 't');return T_BoolConstant; }
{INTEGER} { yylval.integerConstant = strtol(yytext, NULL, 10);return T_IntConstant; }
{HEX_INTEGER} { yylval.integerConstant = strtol(yytext, NULL, 16);return T_IntConstant; }
{DOUBLE} { yylval.doubleConstant = atof(yytext);return T_DoubleConstant; }
{STRING} { yylval.stringConstant = _strdup(yytext); return T_StringConstant; }
{BEG_STRING} { ReportError::UntermString(&yylloc, yytext); }/* -------------------- Identifiers --------------------------- */
{IDENTIFIER} { if (strlen(yytext) > MaxIdentLen)ReportError::LongIdentifier(&yylloc, yytext);strncpy(yylval.identifier, yytext, MaxIdentLen);yylval.identifier[MaxIdentLen] = '\0';return T_Identifier; }
具体源码请参考scanner.l
我们提供正则表达式,generator其内部完成以下转换,从正则式转换到NFA再到DFA阶段,DFA还可以做优化精简,生成一个词法分析模块。每一步的转化都有相应的算法完成。比如NFA到DFA算法:采用子集构造算法,类似于广度优先搜索,将下一阶段有可能的状态集中到一个集合,遍历操作增加下一步,只有最终集合中存在可接受的状态,则视为成功。可以参考engineering a compiler:chapter2 和装配脑袋博客
****************************************************************************
小问题:
如果数字超过其类型而溢出,gcc会怎么处理?
关于"1++2",“x+++1”,gcc会怎么分割成token?1+(+2) or error?
数字在词法分析阶段需要判断正负?
转载于:https://www.cnblogs.com/espzest/p/4095914.html
Lua编译实践1-词法分析:相关推荐
- 编译原理中词法分析的递归下降分析法实例--能被5整除的二进制数---c语言实现
一.前言 又到了一周一度的编译原理实验课,一次实验课上完了,又是大学生必备技能-写实验报告.行了,废话不多说,我直接展现,如何实现编译原理中词法分析的递归下降分析法实例–能被5整除的二进制数的思路.作 ...
- 编译原理 C-Minus词法分析(FLEX)
C–源代码词法分析 文章目录 C--源代码词法分析 一.实现目标 二.C-Minus语法 三.Flex Flex简介 Flex正则表达式 Flex安装与使用 Flex文件编写 定义 规则 用户代码 四 ...
- 编译原理画出c语言中注释的转化图,编译原理节词法分析DFANFA及其转换.ppt
编译原理节词法分析DFANFA及其转换 Step4 寻找可合并状态 ε ε 0 1 2 0 5 0 1 6 1 3 4 7 ε ε 0 8 9 1 1 0 10 11 0 0 1 1 1 0 12 1 ...
- Cocos2d-x lua 编译到Android设备
需要完成Android下ant,ndk,sdk(adt)的环境配置.Java环境配置. 也可以搭建VS下的Lua开发环境. 首先新建项目 cocos new -l lua -d (...address ...
- 【编译原理】词法分析和语法分析两万字全总结(这知识它不进脑子啊~!)
文章目录 1 引论-一些名词解释 1.1 解释器 1.2 翻译器和编译器 1.3 编译型的程序设计语言和解释型的程序设计语言各有哪些优缺点? 1.4 编译过程六个阶段的任务 1.5 遍的概念 2 词法 ...
- 编译实验(一)词法分析
编译原理课程即将结束,开始了在校中最麻烦的实验,编译实验......同在一个系,其他班的编译实验分成好几块,简短的文法, 完成一些小功能,就我们班的老师,撂下一句话:参考书本,把编译器实现了,可以一组 ...
- 编译原理之词法分析、语法分析、语义分析,【精炼总结】
词法分析和词法分析程序 词法分析阶段是编译过程的第一个阶段.这个阶段的任务是从左到右一个字符一个字符地读入源程序,即对构成源程序的字符流进行扫描然后根据构词规则识别单词(也称单词符号或符号).词法分析 ...
- 【编译原理】词法分析程序设计
概述 词法分析即对程序源码进行分词处理,分词处理就是把文本流分割成一个又一个符号.分词处理的输入输出是什么呢? 输入是源码字符串流 输出是: 整型的类型枚举值,表示符号类型,如字符串: 符号内容信息, ...
- Android 6.0 源代码编译实践
前阵子去上海参加 Android 开发面试,被问及了 Android 的基本原理.常用组件背后的实现机制.设计模式等问题,我都回答地不好.面试时,老司机们常常问我对知识点"背后的实现代码有没 ...
最新文章
- 清华人工智能研究院成立,张钹姚期智分别任院长和主任
- java request get json数据_Java中,获取request中json数据
- 【DIY】自己动手更换热水器镁棒,保养电加热热水器注意事项,电热水器镁棒多久更换一次实际数据参考...
- cmake使用教程(一)-起步
- Semaphore同步
- 【Linux系统编程】Linux 可执行文件结构与进程结构
- php cap,PHP ImagickDraw setStrokeLineCap()用法及代码示例
- C语言怎么合并两个有序链表
- 为Mac安装homebrew
- 阿里云物联网边缘计算加载MQTT驱动
- 英语总结系列(十五):别样的三月
- php常用的日期时间操作
- FPGA 实现 RGB 图像转 Gray
- Google基本语法二,指令
- 安装了java环境后,双击.jar包无法运行的解决方案
- 2018年终总结--修身篇
- Backdoor.Zegost木马病毒分析(一)
- k8s——通过暴漏端口实现外部访问服务
- mysql数据库导出数据乱码问题_Mysql数据库导出来的是乱码如何解决
- 开源问答社区软件Answer
热门文章
- PHP服务器时间差8小时解决方案
- LSA 安装及管理应用程序
- hadoop 023.0与hadoop 1.0 io.serializable分析
- 主瓣,旁瓣,栅瓣概念解析
- php mysql什么意思_php MySQLi是什么意思?
- mac邮件过滤器SpamSieve,支持任意类型的任意数量的电子邮件帐户
- JavaScript夯实基础系列(一):词法作用域
- blast | diamond 输出结果选择和解析 | 比对
- CentOS 7.2 搭建内网ntp时间服务器
- 【计算几何】【分类讨论】Gym - 101173C - Convex Contour