编写Lex和Yacc

大学课程设计中,有一次是编写Lex(词法分析器的生成器)和Yacc(语法分析器的生成器),编写这类工具软件不是一件容易的事情。这篇文章记录了当时编程时候的主要思想,主要还是编译原理的思想。

准备

Lex

根据输入文件生成RE—>NFA—>DFA—>简化的DFA—>根据DFA生成文件。

RE处理:

对正规表达式进行处理使其只有|、*、(、)等特殊符号,代换{}[]-等
将RE转化为后缀表达式   

生成NFA:

把下列类型的string转换:

M+----->M.M*

M?------>M|e

最后一共有四种连接:

普通字符(除了.,|,*):

a*:增加三条epsilon边,并且修改终点和起点。

a|b:

新建两个节点,并把其中一个指向原来的两个nfa的起点,另一个被原来的两个终点指向。这四个边均是epsilon。修改终点和起点。

a.b:连接,合并2号和3号节点。(红色代表nfa的起点,黑色代表nfa的终点)

NFA合并:

直接添加一个起点,指向所有nfa的起点。修改起点值,并把原来的终点(每个nfa只有一个)都加入到最后的终点集合。

NFA--->DFA

求闭包

通过epsilon到达的边。迭代直到T’=T,每次

repeat:
    T1 = T;
    T=T1∪T1所有点能通过epsilon到达的边
until T1==T
求通过某个字母到达的子集
for each(Node* node in d)//对d的每一个节点
{
	vector<Node*> eout = node->findNext(c);//求出每个节点的出边集合
	d1.insert(eout.begin(), eout.end());//将后继的每一个节点不重复的插入d1
}
最后返回这个d1的闭包
 
生成DFA

用j标记当前遍历的节点,用p标记已经存在的节点数量

对当前遍历的节点求每个字符的出边集合,如果有的话,就求该集合的闭包,并判断是否已经存在,做相应的处理:

    如果已经存在,则加边

    如果不存在,新建节点,再加边,p++

DFA最小化

参考维基百科中关于Hopcroft的算法。

但是对于DFA来说,刚开始并不能简单地分为两个非终结符和终结符的集合,因为每个终结符最后应该在单独的一个集合中。

Yacc

读取文件设置符号和产生式的值—>计算FIRST和FOLLOW—>构造LR(1)预测分析器和PPT(预测分析表)—>LR(1)—>LALR—>打印到输出文件。

计算FIRST和FOLLOW

还是参考的“现代编译原理”这本书。

对于LR(1)可以不计算FOLLOW,所以只计算FIRST。

初始化:每个终结符的FIRST是自己

设置一个是否修改的bool变量,检测本次循环是否修改过

遍历每个产生式

    每个产生式的右部符号,如果当前符号前面都是可为空,则将这个产生式左部的FIRST增加当前符号。

    如果每个符号都是可为空的,则把这个产生式置为可为空。

 

构造预测分析器(FA)和预测分析表(PPT)

计算闭包和计算GOTO

类似Lex的计算闭包和子集

构造LR状态图和分析表

类似构造DFA

 

反思

感觉这次做的比较辛苦,可能这是因为是工具软件的原因吧。工具软件要能针对不同的输入,生成不同的代码,然后生成出来的代码可以去分析一个文件。

Technorati 标记: 编译原理,Lex,Yacc,课程设计

编写Lex和Yacc

时间: 2024-10-02 19:38:49

编写Lex和Yacc的相关文章

Lex与Yacc学习(一)之环境配置篇

Abstract 在开发程序的过程中经常会遇到文本解析的问题,例如:解析 C 语言源程序,编写 脚本引擎等等,解决这种文本解析的方法有很多,一种方法就是自己手动用 C 或者 C++直接编写解析程序,这对于简单格式的文本信息来说,不会是什么问题,但是 对于稍微复杂一点的文本信息的解析来说,手工编写解析器将会是一件漫长痛苦 而容易出错的事情.本系列文档就是专门用来由浅入深的介绍两个有名的 Unix 工 具 Lex 和 Yacc,并会一步一步的详细解释如何用这两个工具来实现我们想要的任何 功能的解析程

初步学习lex和yacc

因为是非计算机本科,所以没有学编译原理,进来想补补课,于是买了本<自制编程语言>,里面介绍了lex和yacc工具,于是装起来试了下. 原来用工具来解析字符串还是挺方便的,以前知道正则以后,就觉得这东西很好,现在有了lex和yacc,把正则能做的事情又放大了,能够做更丰富的事情. 例如,写一个简单的把字符串里的数字相加,其他忽略的程序(说是简单是指功能,其实调通很不简单,哈哈,特别是把%type写成了%token的笔误后,纠结了很久) 下面贴上代码 test.l %{ #include <

lex 和 yacc 的区别与联系

lex负责词法解析,而yacc负责语法解析,其实说白了就是lex负责根据指定的正则表达式,将输入的字符串匹配成一个一个的token,同时允许用户将当前匹配到的字符串进行处理,并且允许返回一个标识当前token的标识码.而yacc则负责进行语法解析,将一个个的token最终形成一个完整的语法. lex和yacc类似的,分为三个部分 %{ 这里可以写任何的c代码 比如一些初始化的状态 %} 这里是一些的lex或者yacc的定义 比如lex里的%s yacc里的%type %token %left %

Lex与Yacc学习(九)之Yacc语法

Yacc语法 本文讨论yacc语法的格式并描述可用的各种特征和选项 yacc语法结构 yacc语法包括三部分:定义段.规则段和用户子例程段 ...定义段... %% ...规则段... %% ...用户子例程段... 各部分由以两个百分号开头的行分开,尽管某一个部分可以为空,但是前两部分是必须的,第三部分和前面的百分号可以省略. 符号 yacc 语法由符号组成,即语法的"词".符号是一串不以数字开头的字母.数字.句点和下划线.符号error专用于错误恢复,另外,yacc对任何符号都不会

Lex与Yacc学习(三)之符号表

符号表 列举单词表的方式虽然简单但是不全面,如果在词法分析程序运行时可以构建一个单词表,那么就可以在添加新的单词时不用修改词法分析程序. 下面示例便利用符号表实现,即在词法分析程序运行时从输入文件中读取声明的单词时允许动态的声明单词.声明以词性的名字开始,后面跟着要声明的单词. 添加符号表可以完全的改变词法分析程序,不必在词法分析程为每个要匹配的单词放置独立的模式,只要有一个匹配任意单词的模式,再查阅符号表就能决定所找到的词性. lex程序ch1-04.l %{ /* *带符号表的词法分析程序

Lex与Yacc学习(四)之Lex规范

Lex规范的结构 lex程序由三部分组成:定义段.规则段和用户子例程序段 ...定义段... %% ...规则段... %% ...用户子例程序段... 这些部分由以两个百分号组成的行分隔开.尽管某一部分可以为空,但前两部分是必须的,第三部分和前面的%%行可以忽略. 定义段 定义段包括文字块.定义.内部表声明.起始条件和转换. 以空白开头的行被逐字拷贝到C文件中,通常,这用于包含包围在/*和*/中的注释,一般前面有空白. 规则段 规则段包含模式行和C代码,以空白开始的行或者包围在%{和%}中的内

Lex与Yacc学习(五)之正则表达式篇

正则表达式语法 lex模式是由编辑程序和实用程序使用的正则表达式的扩展版本.正则表达式由常规字符(代表它们本身)和元字符(在一种模式中具有特殊含义)组成. 元字符 . . 匹配除了换行符 \n 之外的任意单个字符 [] [] 匹配括号中字符的任意一个.用"-"(短划线)指示字符的范围,例如[0-9]指10个数字中的任意一个.如果开括号之后的第一个字符是短划线或者闭括号,那么它就不能被解释为元字符.如果第一个字符是抑扬字符" ^ ",那么它的含义就变为匹配括号内字符以

Lex与Yacc学习(八)之变量和有类型的标记(扩展计算器)

变量和有类型的标记 下一步扩展计算器来处理具有单个字母名字的变量,因为只有26个字母 (目前只关心小写字母),所以我们能在26个条目的数组(称它为vbltable)中存储变量. 为了使得计算器更加有用,也可以扩展它来处理多个表达式(每行一个)和使用浮点值. 具有变量和实值的计算器词法ch3-03.l %{ #include "ch3-03.tab.h" #include <math.h> extern double vbltable[26]; %} %% ([0-9]+|(

Lex与Yacc学习(七)之环境配置另一种方式

必备工具 flex-2.5.4a-1.exe   和  bison-2.4.1-setup.exe   以及 cygwin2.738 的安装文件,下载地址 http://download.csdn.net/detail/fly_yr/8385245 flex与bison安装 运行flex-2.5.4a-1.exe  和  bison-2.4.1-setup.exe 文件安装至D:\Software Files\GnuWin32下,然后按配置环境变量: 将路径 D:\Software Files\