LR1语法分析C语言代码,LR1语法分析
LR1语法分析
Last Updated 11.28 by Nie Zili
一、数据结构简介
该项目采用面向对象的思想设计数据结构,共设有6个类,其递进关系如下:
pstring,产生式类。定义了LR1所需的产生式结构,包含“点位置”与“展望字符”
Grammar,文法类。主要用来保存产生式集合,用于最开始用户输入的文法
Node,节点类。继承自Grammar,除了一个产生式集合以外,定义了“节点标号”与“分界下标”,“分界下标”的意义是区分原产生式与求闭包后加入的产生式,这样在检查是否需要添加新节点时只需要检查[0,分界处)的部分即可。
Edge,边类。包含Node类型的开始与结束节点,还有一个string类型的转换字符
DFA,项目集类。包含一个Node集合与一个Edge集合。
Table,预测分析表类。包含一个DFA和一个Map集合,表示状态转换。需要用DFA来构造
二、 产生式类pstring
数据成员结构如下:
class pstring
{
protected:
string left;// 产生式左部
string right;// 产生式右部
int dot;// ·的位置
int No;// 产生式编号
bool valid;// 产生式是否有效(仅用于初始化)
string prospection;// 展望字符
}
产生式由用户输入,形如“S-Ab”。对于LR1语法分析过程中需要用到的·与展望字符做了定义:
“·”不显式的插入产生式中,仅用下标表示其所在位置。例如,S-·Ab,则int dot=0,·后字符即,’A’ right[dot]。点的移动使用函数shift_dot()
展望字符使用string类型存储(该项目中几乎都使用string,方便做可能的扩展)
pstring类型对==做了重载,两个产生式相同的条件为:左部相同、右部相同、展望字符相同、点位置相同。由于此处的规定较为严格,所以在后面规约时不能够使用==,只需要匹配左部右部相同即可
三、文法类Grammar
数据成员结构如下:
class Grammar
{
protected:
vector productions;// 产生式集合
string s;// 开始字符
set Vn;// 非终结符集
set Vt;// 终结符集
map> first_set;// First集
}
文法即产生式集合,提供了手动初始化函数init()和从文件读入方式init_from_file(),后者将在简单计算器项目中使用。
创建一个文法的正确调用顺序如下:
int main()
{
Grammar g;
g.init();// 初始化
g.extent();// 求扩展文法
g.cal_First();// 计算First集合
}
cal_First()用于求取该文法中非终结符的First集合,存储与一个map中。求First集合的意义在于之后对节点求闭包时确定展望字符,在Node中介绍。
此处的计算First集合采用while循环方式,类似于LL1中求Follow的方法,因此不会因为左递归而无限循环
四、节点类Node
数据成员结构如下:
class Node: public Grammar
{
protected:
int number;// 节点编号
int divide_index;// 原始产生式与闭包的分界处
}
Node继承于Grammar,但事实上我们只需要产生式集合,Grammar的其余结构并不需要。其数据成员中的divide_index表示“原始产生式与闭包的分界”,具体来说就是记录求闭包之前的产生式数量,这样在比较两个节点是否相同时可以只比较divide_index之前的部分,减少运算量。
关键的函数是get_closure()求取闭包,下面用伪代码描述流程:
while(产生式集合发生变化)
{
for p in productions
{
// 需要添加新的产生式
if(产生式p的·后有字符 && 该字符为非终结符)
{
ch = p.right[dot];
// 如果当前字符为产生式末尾,则直接以当前产生式的展望字符作为新产生式的展望字符。
prospections = p.get_prospection();
// 如果当前字符不是产生式末尾,则需要对后面求First,确定展望字符集
if(ch不是产生式末尾)
{
bch = p.right.substr(dot+1) + p.get_prospection();
prospections = get_First(bch);
}
将以ch为左端的产生式,以prospections中的字符作为展望字符,添加到产生式集合中。
}
}
if(产生式集合大小发生变化)
产生式集合发生变化;
}
在具体实现时,对流程中的一些细节做了优化,比如我们不是每次都从头遍历产生式集合,而是从上一次的结束位置开始。
在求闭包时有一步求后部产生式First集合来确定展望字符的过程,下面举例说明:
产生式为S-·ABCd,#,则点后字符为A,A后部为BCd#,我们需要求First(BCd#),来获取将加入的以A为左部的产生式的展望字符。
如果First(B)含有空串,则继续看First(C),直到遇到一个终结符或者First集合不再有空串。
五、边类Edge
数据成员结构如下:
class Edge
{
private:
Node Start;
Node End;
string trans;
}
六、DFA类
数据成员结构如下:
class DFA
{
private:
vector node_set;
vector edge_set;
Grammar G;
}
DFA需要由一个Grammar初始化,之后只需要调用cal_dfa()即可。
cal_dfa()函数的伪代码描述如下:
while(加入新边)
{
for node in 节点集合
{
转移字符集 = 遍历node的产生式集合,找到所有“·”后字符;
}
for ch in 转移字符集
{
新建节点new_node;
for p in node的产生式集合
{
如果p.right[dot] == ch,则向new_node中加入向右移点后的p;
}
new_node.get_closure();
add_node(new_node);
新建边new_edge;
new_edge.Start(node);
new_edge.End(new_node);
new_edge.trans(ch);
if(add_edge(new_edge))
{
加入新边;
}
}
}
这里面add_node和add_edge函数在添加之前都会查重,因此仅当成功加入边之后才再次循环。
七、预测表类Table
数据成员结构如下:
class Table
{
private:
vector> table;
DFA graph;
}
预测表的抽象结构如下:
终结符0
终结符1
#
...
非终结符0
非终结符1
状态0
Sx
m
n
状态1
r1
状态2
acc
代码中用vector的下标表示状态n(DFA一个节点对应一个状态),map表示该状态下一个符号对应的动作。
用DFA初始化一个Table之后,遍历Edge集合计算移入,遍历Node集合计算规约即可。
predict(string input)是本项目中用来检查一个输入串是否通过语法分析的函数,在简单计算器项目中将使用另外一个实现语法分析(语义分析),不详述了。
LR1语法分析C语言代码,LR1语法分析相关推荐
- 转:PL/0语言词法及语法分析系统的设计与实现
PL/0语言词法及语法分析系统的设计与实现 作者:陶善文 南京航空航天大学信息与计算机科学专业 下载源代码 摘要:本文介绍了一个PL/0语言的词法及语法分析系统的设计与实现 关键词:循环分支 递归下降 ...
- 遗传算法c语言程序,遗传算法c语言代码.doc
遗传算法c语言代码 遗传算法代码 #include #include #include #include #include struct group //染色体的结构 { int city[citie ...
- 071_html语言代码
1. ISO语言代码 1.1. 国际标准化组织(International Organization for Standardization, ISO)简称ISO, 是一个全球性的非政府组织, 是国际 ...
- 嵌入式C语言代码规范
C语言代码规范 参考安富莱C语言编码规范 1.文件与目录 1.文件及目录的命名规定可用的字符集是[A-Z:a-z:0-9:._-]. 2.源文件名后缀用小写字母 .c 和.h. 3.文件的命名要准确清 ...
- java 与c 运行效率_Java语言与C语言代码运行效率的比较
<Java语言与C语言代码运行效率的比较>由会员分享,可在线阅读,更多相关<Java语言与C语言代码运行效率的比较(2页珍藏版)>请在人人文库网上搜索. 1.Java语言与C语 ...
- Python语言学习:python语言代码调试—异常处理之详细攻略
Python语言学习:python语言代码调试-异常处理之详细攻略 目录 python语言代码调试-异常处理 异常捕捉可以使用 try/except 语句 相关文章 Python3 错误和异常 | 菜 ...
- 编程笔试(解析及代码实现):猴子吃桃。猴子第一天吃了若干个桃子,当即吃了一半,还不解馋,又多吃了一个…的C++、Java、Python、C#等语言代码实现
编程笔试(解析及代码实现):猴子吃桃.猴子第一天吃了若干个桃子,当即吃了一半,还不解馋,又多吃了一个. 第二天早上又将剩下的桃子吃了一半,还是不过瘾,又多吃了一个.以后每天都吃前一天剩下的一半再加一个 ...
- c语言错误 xef代表什么,单片机C语言代码手册 含100多个经典C程序
1 单片机单片机 C 语言代码手册语言代码手册 1 LED 灯灯 点亮一个点亮一个 LED include void main while 1 P0 0 x01 P2 0 x7d 流水灯闪烁流水灯闪烁 ...
- c 语言怎么编译 .dll,将你的 C 语言代码编译成 .NET
介绍 通常情况下,对于那些使用C语言编程并开始用C#/ VB或一些其他的用于.NET的编译语言编程,那么他们希望或者甚至是需要调用我们用C语言编写的函数代码. 每当我在互联网上钻研,或说要在编译器中使 ...
最新文章
- 技术图文:排序技术在求解算法题中的应用
- 深度学习面临天花板,亟需更可信、可靠、安全的第三代AI技术|AI ProCon 2019
- 【Machine learning】余弦相似度
- 21. Merge Two Sorted Lists
- html获取文本框中的文字,JavaScript实现input输入框点击获取文字内容
- 程序员们的三高:高并发、高性能、高可用
- 语义分割江湖的那些事儿——从旷视说起
- UI-Day02--昨日作业代码(二)
- 《『若水新闻』客户端开发教程》——06.设计新闻内容UI
- Unity Invoke 函数调用
- html文件导入奥维,【干货】奥维地图 | 如何导入高程数据
- 5月27日股市趋势追踪策略分析
- 2011黑帽大会:由黑客操控的世界
- STM32F4的基础介绍
- C#秘密武器之多线程——参数与返回值
- python中bytes的用法_Python bytes类型及用法详解
- 计算机组成原理南阳理工学院教务管理系统,南阳理工学院教务管理系统使用暂行办法...
- 跳出while循环的三种方法
- mysql查询95031班人数_MySQL查询练习
- 2021年宣传部第一次ps培训总结
热门文章
- Emily Dickinson 《Not In Vain》
- 【DEBUG】2021-06-27 不定期的自我检讨002
- [再寄小读者之数学篇](2014-06-21 向量公式)
- Linux从入门到秃头
- Richard Stallman称Ubuntu为木马
- Android 开发 入门
- Java学习之路1——安装JDK1.8||安装idea2022||Java项目创建【重拾Java】
- C语言回音消除算法,一种语音识别场景中回音消除的方法
- chrom如何兼容本地file文件
- 天天链n1 与电脑连接Samba win10 教程