实验二:CMM语言词法分析

时间:2022-02-10 07:00:15

笔记
(一)、扫描处理
最主要的是正则表达式( regular expression)和有穷自动机( finite automata)。
扫描程序的任务是从源代码中读取字符并形成由编译器的以后部分(通常是分析程序)处
理的逻辑单元。
由记号表示的字符串有时称作它的串值( string value)或它的词义( l e x e m e)。
任何与记号相关的值都是记号的属性( a t t r i b u t e),而串值就是属性的示例。
由于扫描程序必须计算每一个记号的若干属性,所以将所有的属性收集到一个单独构造的
数据类型中是很有用的,这种数据类型称作记号记录(token record)。可在C中将这样的记录
说明为:
typedef struct
{ TokenType tokenval;
char * stringval;
int numval;
} TokenRecord;
(以上假设只有标识符需要串值属性,只有数字需要数值属性)。
实际上,扫描程序是在分析程序的控制下进行操作的,它通过函数从输入中返回有关命令的下一个记号,该函数有与C说明相似的说明:
TokenType getToken( void );
这个方式中声明的g e t T o k e n函数在调用时从输入中返回下一个记号,并计算诸如记号串值这
样的附加属性。输入字符串通常并不给这个函数提供参数,但参数却被保存在缓冲区中或由系
统输入设备提供。

(二)、正则表达式
1、基本正则表达式
它们是字母表中的单个字符且自身匹配。假设 a是字母表å中的任一字符,则指定正则表达式a 通过书写L (a) = {a}来匹配a字符。而特殊情况还要用到另外两个字符。有时需要指出空串( empty string)的匹配,空串就是不包含任何字符的串。空串用 ( e p s i l o n )来表示,元符号 (黑体 )是通过设定L( ) = { }来定义的。偶尔还需要写出一个与任何串都不匹配的符号,它的语言为空集( empty set),写作{ }。我们用符号 来表示,并写作L ( ) = {}。
请注意{ }和{ }的区别: { }集不包括任何串,而{ }则包含一个没有任何字符的串。
2、正则表达式运算
在正则表达式中有3种基本运算:① 从各选择对象中选择,用元字符|(竖线)表示。②连结,由并置表示(不用元字符)。③重复或“闭包”,由元字符*表示。④一个或多个重复。⑤任意字符。⑥字符范围。⑦不在给定集合中的任意字符。⑧可选的子表达式
(1)、从各选择对象中选择
如果r 和s 是正则表达式,那么正则表达式r | s 可匹配被r 或s 匹配的任意串。
(2)、连结
正则表达式r 和正则表达式s 的连结可写作r s,它匹配两串连结的任何一个串,其中第1个匹配r,第2个匹配s。
(3)、重复
正则表达式r * 匹配串的任意有穷连结,每个连结均匹配r。例如a *匹配串 、 a、a a、 a aa …(它与 匹配是因为 是与a匹配的非串连结)。
(4)、一个或多个重复
r +表明r 的一个或多个重复。
(5)、任意字符
句号“.”表示任意字符匹配的典型元字符,它不要求真正将字母表写出来。
(6)、字符范围
[ a - z ]是指所有小写字母, [ 0 -9 ]则指数字。这种表示法还可用作表示单个的解,因此 a | b | c可写成[ a b c ],它还可用于多个范围,如[ a - z A - Z ]代表所有的大小写字母。这种普遍表示法称为字符类(character class)。
(7)、不在给定集合中的任意字符
任何非a 的字符可写作[ ^ a ],任何非a、 b 及c 的字符则写作:[ ^ a b c ]
(8)、可选的子表达式
问号元字符 r?来表示由r 匹配的串是可选的(或显示 r 的0个或1个拷贝)。

优先级:*优先权最高,连结其次, | 最末。
注:不同的正则表达式可生成相同的语言。并非用简单术语描述的所有串都可由正则表达式产生。

(三)、有穷自动机(详情可参考《编译原理》)
有穷自动机可用作描述在输入串中识别模式的过程,因此也能用作构造扫描程序。
正则表达式 -> NFA -> DFA
在扫描程序中,效率是很重要的,如果可能的话,在某种意义上构造的 D FA应最小。实际上,自动机理论中有一个很重要的结果,即:对于任何给定的 D FA,都有一个含有最少量状态的等价的 D FA,而且这个最小状态的 D FA是唯一的(重命名的状态除外)。

——————————分割线—————————————————
代码实现过程
一、Token如下:
实验二:CMM语言词法分析

二、状态自动机如下:
实验二:CMM语言词法分析
对应的数据结构为:
实验二:CMM语言词法分析
三、程序结构如下:
实验二:CMM语言词法分析
说明如下:
(一)本程序由cmake组织
(二)头文件Global.h声明的是全局变量,有TokenType和lineNo
(三)头文件LexAnalysis.h声明了静态全局string变量tokenString、静态全局函数,该函数实现词法分析的主体工作,分析出每个token。

static TokenType getTokenPrivate(std::istream&,std::ostream&,int);

还声明了全局函数,作为外部的调用接口。

//外部接口,用来从源文件中解析出下个token
//传递参数istream和ostream,实现多态流操作
//最后一个参数是用来分辨控制台输入还是文件输入,以实现不同的控制
TokenType getToken(std::istream&,std::ostream&,int);

(四)源文件LexAnalysis.cpp是词法分析的主体源文件,定义了数据结构如下:

//每一行输入缓存的最大长度
#define BUFLEN 256
string tokenString;
static char lineBuf[BUFLEN]; //存放每一行
static int bufSize = 0; //目前行的长度
static int linepos = 0; //目前行中的字符当前位置
static int EOF_flag = FALSE;
static map<const string,int> keyMap; //存放保留字的map,用于检索
static map<const string,int> specialMap; //存入特殊token的map,用于检索。特殊token指的是双字符的token
static char check[] = {'+','-','*','/','%','=','<','>','(',')','[',']','{','}',',',';','\'','\"'};
map<const string,int>::iterator iter;

定义了方法如下:

//初始化keyMap
void initKeyMap();
//初始化specialMap
void initSpecialMap() ;
//从lineBuf中获取下个字符
//flag为0表示是控制台输入,设定#是程序结束标志。
//flag为1表示是文件输入,设定到达文件结尾表示程序结束标志
static char getNextChar(istream &source_file,ostream &lex_file,int flag);
//取消获取下一个字符,也就是撤消上一次的取字符操作
static void unGetNextChar() ;
//判断字符是否为空白
bool isBlank(char c);
//判断字符是否为运算符、限界符、注释的第一个字符
static bool isFirst(char c) ;
//外部接口
TokenType getToken(istream &source_file,ostream &lex_file,int flag);
//内部实现,用来从源文件中解析出下个token
TokenType getTokenPrivate(istream &source_file,ostream &lex_file,int flag);

(五)头文件Util.h和源文件Util.cpp是作为程序的辅助工具,定义了函数

//打印token,通过istream和ostream实现多态,达到控制台输入输出或者文件输入输出
void printToken(std::istream &,std::ostream &,TokenType,const std::string);

(六)Main.cpp定义程序的主函数,通过参数的个数来确定词法分析是控制台输入输出还是文件输入输出。
四、程序的算法
将cmm程序的每一行进行词法分析,调用函数getNextChar()逐个读入字符并且调用函数getTokenPrivate()通过状态自动机识别出token,调用printToken()打印token的相关信息,返回token。如果token是ENDFILE,表示cmm程序词法分析结束。

五、结果
(1)采用文件输入输出格式:
终端输入如下命令,其中test.cmm是事先写好的cmm文件,程序运行成功之后会生成test.lex文件

实验二:CMM语言词法分析
实验二:CMM语言词法分析
实验二:CMM语言词法分析
(2)采用控制台输入输出格式:
终端操作如下:
实验二:CMM语言词法分析

————————————分割线——————————————
具体代码先不贴。

——————————2016.11.13分割线——————————————
把代码放到github上了:
https://github.com/loveEason/CMM_Interpreter.git