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语法分析相关推荐

  1. 转:PL/0语言词法及语法分析系统的设计与实现

    PL/0语言词法及语法分析系统的设计与实现 作者:陶善文 南京航空航天大学信息与计算机科学专业 下载源代码 摘要:本文介绍了一个PL/0语言的词法及语法分析系统的设计与实现 关键词:循环分支 递归下降 ...

  2. 遗传算法c语言程序,遗传算法c语言代码.doc

    遗传算法c语言代码 遗传算法代码 #include #include #include #include #include struct group //染色体的结构 { int city[citie ...

  3. 071_html语言代码

    1. ISO语言代码 1.1. 国际标准化组织(International Organization for Standardization, ISO)简称ISO, 是一个全球性的非政府组织, 是国际 ...

  4. 嵌入式C语言代码规范

    C语言代码规范 参考安富莱C语言编码规范 1.文件与目录 1.文件及目录的命名规定可用的字符集是[A-Z:a-z:0-9:._-]. 2.源文件名后缀用小写字母 .c 和.h. 3.文件的命名要准确清 ...

  5. java 与c 运行效率_Java语言与C语言代码运行效率的比较

    <Java语言与C语言代码运行效率的比较>由会员分享,可在线阅读,更多相关<Java语言与C语言代码运行效率的比较(2页珍藏版)>请在人人文库网上搜索. 1.Java语言与C语 ...

  6. Python语言学习:python语言代码调试—异常处理之详细攻略

    Python语言学习:python语言代码调试-异常处理之详细攻略 目录 python语言代码调试-异常处理 异常捕捉可以使用 try/except 语句 相关文章 Python3 错误和异常 | 菜 ...

  7. 编程笔试(解析及代码实现):猴子吃桃。猴子第一天吃了若干个桃子,当即吃了一半,还不解馋,又多吃了一个…的C++、Java、Python、C#等语言代码实现

    编程笔试(解析及代码实现):猴子吃桃.猴子第一天吃了若干个桃子,当即吃了一半,还不解馋,又多吃了一个. 第二天早上又将剩下的桃子吃了一半,还是不过瘾,又多吃了一个.以后每天都吃前一天剩下的一半再加一个 ...

  8. c语言错误 xef代表什么,单片机C语言代码手册 含100多个经典C程序

    1 单片机单片机 C 语言代码手册语言代码手册 1 LED 灯灯 点亮一个点亮一个 LED include void main while 1 P0 0 x01 P2 0 x7d 流水灯闪烁流水灯闪烁 ...

  9. c 语言怎么编译 .dll,将你的 C 语言代码编译成 .NET

    介绍 通常情况下,对于那些使用C语言编程并开始用C#/ VB或一些其他的用于.NET的编译语言编程,那么他们希望或者甚至是需要调用我们用C语言编写的函数代码. 每当我在互联网上钻研,或说要在编译器中使 ...

最新文章

  1. 技术图文:排序技术在求解算法题中的应用
  2. 深度学习面临天花板,亟需更可信、可靠、安全的第三代AI技术|AI ProCon 2019
  3. 【Machine learning】余弦相似度
  4. 21. Merge Two Sorted Lists
  5. html获取文本框中的文字,JavaScript实现input输入框点击获取文字内容
  6. 程序员们的三高:高并发、高性能、高可用
  7. 语义分割江湖的那些事儿——从旷视说起
  8. UI-Day02--昨日作业代码(二)
  9. 《『若水新闻』客户端开发教程》——06.设计新闻内容UI
  10. Unity Invoke 函数调用
  11. html文件导入奥维,【干货】奥维地图 | 如何导入高程数据
  12. 5月27日股市趋势追踪策略分析
  13. 2011黑帽大会:由黑客操控的世界
  14. STM32F4的基础介绍
  15. C#秘密武器之多线程——参数与返回值
  16. python中bytes的用法_Python bytes类型及用法详解
  17. 计算机组成原理南阳理工学院教务管理系统,南阳理工学院教务管理系统使用暂行办法...
  18. 跳出while循环的三种方法
  19. mysql查询95031班人数_MySQL查询练习
  20. 2021年宣传部第一次ps培训总结

热门文章

  1. Emily Dickinson 《Not In Vain》
  2. 【DEBUG】2021-06-27 不定期的自我检讨002
  3. [再寄小读者之数学篇](2014-06-21 向量公式)
  4. Linux从入门到秃头
  5. Richard Stallman称Ubuntu为木马
  6. Android 开发 入门
  7. Java学习之路1——安装JDK1.8||安装idea2022||Java项目创建【重拾Java】
  8. C语言回音消除算法,一种语音识别场景中回音消除的方法
  9. chrom如何兼容本地file文件
  10. 天天链n1 与电脑连接Samba win10 教程