简化版Yacc报告

目录

1. 项目描述

yacc(Yet Another Compiler Compiler),是Unix/Linux上一个用来生成编译器的编译器(编译器代码生成器)。yacc生成的编译器主要是用C语言写成的语法解析器(Parser),需要与词法解析器Lex一起使用,再把两部分产生出来的C程序一并编译。yacc本来只在Unix系统上才有,但现时已普遍移植往Windows及其他平台。

yacc的输入是巴科斯范式(BNF)表达的语法规则以及语法规约的处理代码,Yacc输出的是基于表驱动的编译器,包含输入的语法规约的处理代码部分。

yacc是开发编译器的一个有用的工具,采用LALR(1)语法分析方法

(以上内容摘录自维基百科):

因个人能力有限,只实现了一个非常简单的yacc(LR(1))版本

假设:

要在输入文件开始声明要用到的终止符非终止符

输入文件不支持注释

产生式左部要紧跟 ':' ,不能有空格

终止符和非终止声明不支持换行声明,production支持换行

两个字符之间要有空格, ':'之后要有空格

不能自定义处理二义文法

有错误处理机制

例如:

输入中non_terminal代表非终止符, terminal代表终止符, production代表产生式.

non_terminal: E T F

terminal: + * ( ) id

production:

E: E + T

E: T

T: T * F

T: F

F: ( E )

F: id

输出yacc.py程序, 运行yacc.py程序对如下输入处理

python yacc.py yacc.txt yacc.txt

if aaaab asd

输出:

if ex1 ex2

2. 模块设计和中间结果校验

2.0 主要数据结构(YaccDataStructures.py)

产生式 Production

class Production(object):

non_terminal_syms = []

terminal_syms = []

def __init__(self, left, right):

self.left = left

self.right = right

其中right以如下方式存储:

right = [symbol1, symbol2......]

LR(1)项 Item

class Item(object):

"""

LR1 item

"""

@classmethod

def from_production(cls, prd, next_predict):

item = Item(prd, next_predict, 0)

# item.predict_next(firsts)

return item

def __init__(self, production, next_predict, index):

"""

:param production: 产生式

:param next_predict: 预测项

:param index: 当前 . 的位置

"""

self.production = production

self.next_predict = next_predict

self.index = index

其中next_predict通过以下方式存储:

next_predict = set(T1, T2, T3......)

状态 State

class State(object):

"""

状态

"""

def __init__(self, items):

"""

:param items: 该状态内的item

"""

self.items = items

通过自定义__str__方法来展示对应的状态图:

( S' -> .E , {'$'})

( E -> .E + T , {'+', '$'})

( E -> .T , {'+', '$'})

( T -> .T * F , {'+', '$', '*'})

( T -> .F , {'+', '$', '*'})

( F -> .( E ) , {'+', '$', '*'})

( F -> .id , {'+', '$', '*'})

2.1 输入模块(LexInput.py)

流程:

读入输入文件的每一行

如果是空行跳过

如果是非终止符,读入非终止符号集合

如果是终止符, 读入紧随其后的终止符集合

如果是产生式, 读入紧随其后的产生式集合

在非终止符集合中插入S', 产生式集合中插入零号产生式

返回三个集合

注意:

最简单模块,没有什么值得注意的

例子:

non_terminal: E T F

terminal: + * ( ) id

production:

E: E + T

E: T

T: T * F

T: F

F: ( E )

F: id

输出:

[S' -> E , E -> E + T , E -> T , T -> T * F , T -> F , F -> ( E ) , F -> id ]

Non terminal: ["S'", 'E', 'T', 'F']

Terminal: ['+', '*', '(', ')', 'id']

2.2 计算所有非终止符的fisrt(YaccFuncs.py)

流程:

下面通过一段简化版代码来说明, 具体可参照龙书或者具体代码

for nt in non_terminals:

productions_of_nt = 所有左侧含有nt的产生式集合

while is_changed:

for first_prd in productions_of_nt:

left = 当前产生式左侧

right = 当前产生式左侧

if A->epsilon:

FIRST(nt)

elif A->a: //a is terminal symbol

FIRST(nt)

elif A->Bx:

if B not contains epsilon:

FIRST(nt)

elif B contains epsilon

FIRST(nt)

return 所有first

def calc_every_first_of_nt(productions, nts):

"""

计算每一个非终止符的first

:param productions: 产生式集合

:param nts: 非终止符集合

:return: 所有非终止符first集合

"""

例子:

(1) 输入:

[S' -> E , E -> E + T , E -> T , T -> T * F , T -> F , F -> ( E ) , F -> id ]

Non terminal: ["S'", 'E', 'T', 'F']

Terminal: ['+', '*', '(', ')', 'id']

输出:

{"S'": ['(', 'id'], 'E': ['(', 'id'], 'T': ['(', 'id'], 'F': ['(', 'id']}

(2) 输入:

含有循环左递归

S': S

S: A a

S:

A: S d

输出:

# None 代表 epsilon

{"S'": [None, 'd'], 'S': [None, 'd'], 'A': ['d']}

2.3 构建LR1 Table (YaccFuncs.py)

状态内扩展 (State内函数)

def expand(self, productions, firsts):

"""

:param productions: 产生式集合

:param firsts: 所有非终止符first

:return:

"""

输入:

before:

( S' -> .E , {'$'})

产生式:

S': E

E: E + T

E: T

T: T * F

T: F

F: ( E )

F: id

输出:

after:

( S' -> .E , {'$'})

( E -> .E + T , {'+', '$'})

( E -> .T , {'+', '$'})

( T -> .T * F , {'*', '+', '$'})

( T -> .F , {'*', '+', '$'})

( F -> .( E ) , {'*', '+', '$'})

( F -> .id , {'*', '+', '$'})

推导到下一个状态

def through_edge(self, edge):

"""

:param edge: 通过的边

:return:

"""

例子: 因为逻辑简单,只需移动点位置即可,所以不给出具体例子

LR(1) table 构建完成

例子:

输入:

non_terminal: E T F

terminal: + * ( ) id

production:

E: E + T

E: T

T: T * F

T: F

F: ( E )

F: id

输出:

比较复杂,建议跳过,看后面的具体语法分析

[{'E': [1], 'T': [2], 'F': [3], '(': [4], 'id': [5]},

{'+': [6], '$': ['acc']},

{'*': [7], '+': [Production('E',['T'])], '$': [Production('E',['T'])]},

{'*': [Production('T',['F'])], '+': [Production('T',['F'])], '$': [Production('T',['F'])]},

{'E': [8], 'T': [9], 'F': [10], 'id': [11]},

{'*': [Production('F',['id'])], '+': [Production('F',['id'])], '$': [Production('F',['id'])]},

{'T': [12], 'F': [3], '(': [4], 'id': [5]},

{'F': [13], '(': [4], 'id': [5]},

{'+': [14], ')': [15]},

{'*': [16], '+': [Production('E',['T'])], ')': [Production('E',['T'])]},

{'+': [Production('T',['F'])], ')': [Production('T',['F'])], '*': [Production('T',['F'])]},

{'+': [Production('F',['id'])], ')': [Production('F',['id'])], '*': [Production('F',['id'])]},

{'*': [7], '+': [Production('E',['E', '+', 'T'])], '$': [Production('E',['E', '+', 'T'])]},

{'*': [Production('T',['T', '*', 'F'])], '+': [Production('T',['T', '*', 'F'])], '$': [Production('T',['T', '*', 'F'])]},

{'T': [17], 'F': [10], '(': [18], 'id': [11]},

{'*': [Production('F',['(', 'E', ')'])], ')': [Production('F',['(', 'E', ')'])], '+': [Production('F',['(', 'E', ')'])], '$': [Production('F',['(', 'E', ')'])]},

{'F': [19], '(': [18], 'id': [11]},

{'*': [16], '+': [Production('E',['E', '+', 'T'])], ')': [Production('E',['E', '+', 'T'])]},

{'E': [20], 'T': [9], 'F': [10], 'id': [11]},

{'+': [Production('T',['T', '*', 'F'])], ')': [Production('T',['T', '*', 'F'])], '*': [Production('T',['T', '*', 'F'])]},

{'+': [14], ')': [21]},

{'+': [Production('F',['(', 'E', ')'])], ')': [Production('F',['(', 'E', ')'])], '*': [Production('F',['(', 'E', ')'])]}]

2.4 Output部分(YaccOutput.py)

过程:

根据输入的lr1 table 构建输出程序

def yacc_output(lr1_table, path):

"""

输出yacc程序

:param lr1_table: lr1 parsing table

:param path: 要输出的语法分析程序

:return:

"""

例子:

输入:

在TestInput.txt中输入的是龙书上的一个例子

使用命令

python Main.py ./test/TestInput.txt ./test/yacc.py

python ./test/yacc.py ./test/TestExpression.txt

输出:

其中每一列代表 栈, 符号, 输入 和动作

(1) 0 id * id + id $ shift 5

(2) 0 5 id * id + id $ reduce by F -> id

(3) 0 3 F * id + id $ reduce by T -> F

(4) 0 2 T * id + id $ shift 7

(5) 0 2 7 T * id + id $ shift 5

(6) 0 2 7 5 T * id + id $ reduce by F -> id

(7) 0 2 7 13 T * F + id $ reduce by T -> T * F

(8) 0 2 T + id $ reduce by E -> T

(9) 0 1 E + id $ shift 6

(10) 0 1 6 E + id $ shift 5

(11) 0 1 6 5 E + id $ reduce by F -> id

(12) 0 1 6 3 E + F $ reduce by T -> F

(13) 0 1 6 12 E + T $ reduce by E -> E + T

(14) 0 1 E $ accept

complete

与书上的正确答案比较:

因为书上是SLR(1), 本题使用了LR(1),所以在具体的状态编号上不完全相同

3. 使用说明

使用如下命令指定输入,输出文件

python Main.py inputPath output.py

然后

python output.py inputPath

在命令行中查看语法分析过程

例如:

可使用项目中提供的内容进行测试

测试一:

python Main.py ./test/TestInput.txt ./test/yacc.py

# 正常情况1

python ./test/yacc.py ./test/TestExpression.txt

# 正常情况2

python ./test/yacc.py ./test/TestExpression1.txt

因为写的比较简陋,很多功能无法使用,且输入处理也不够友好,望多多包容

4 感受

代码数量:

| 语言 | 文件 | 空行 | 注释| 代码|

| -------- | -----: | :----: | :----: |:----: |:----: |

| Python | 8 | 130 | 154 | 532|

加深了对语法分析流程和算法具体实现的理解.

锻炼了编程能力

yacc c语言语法分析器,GitHub - waxnkw/yacc: 简陋版语法分析器相关推荐

  1. ll1语法分析器c语言E-E T,算术表达式的 LL(1)语法分析器

    算术表达式的 LL(1)语法分析器 张会霞 (辽宁师范大学 计算机与信息技术学院,辽宁 大连 116000) 摘要:语法分析是编译程序的核心部分,对其进行研究有着重要意义.本文介绍了编译过程语法分析阶 ...

  2. 持续排名GITHUB榜首的V语言竟是C++的孪生兄弟-V版毁灭战士值得点赞

    最近V语言持续在GITHUB上霸屏,而笔者在上周介绍了V语言的一些基础,详见:GITHUB排行榜C位出道-手把手教你玩转V语言版的俄罗斯方块,而这周我也持续关注了V语言的动态,这次带大家玩一下V语言版 ...

  3. Go:go语言的认识理解、安装应用、语法使用之详细攻略

    Go:go语言的认识理解.安装应用.语法使用之详细攻略 目录 go语言的认识理解 go语言的安装应用 硬件架构 操作系统 开发工具 go语言的语法使用 go语言的认识理解 Go语言是谷歌2009发布的 ...

  4. c语言高亮字符,C语言必背18个程序+190例--语法高亮

    <C语言必背18个程序+190例--语法高亮>由会员分享,可在线阅读,更多相关<C语言必背18个程序+190例--语法高亮(111页珍藏版)>请在人人文库网上搜索. 1.C语言 ...

  5. 我的Go+语言初体验——(5)Go+ 基本语法之 Switch

    我的Go+语言初体验--(5)Go+ 基本语法之 Switch "我的Go+语言初体验" | 征文活动进行中- Go+ 语言中提供多路分支条件语句 switch, 用于在不同条件下 ...

  6. c语言程序设教材计 乌云高娃,C语言程序设计教学课件作者第3版乌云高娃演示文稿C语言程序设计教学课件作者第3版乌云高娃演示文稿演示文稿第1章C语言程序设计基础课件.ppt...

    C语言程序设计教学课件作者第3版乌云高娃演示文稿C语言程序设计教学课件作者第3版乌云高娃演示文稿演示文稿第1章C语言程序设计基础课件.ppt 主要内容 课程概述 为什么选择C语言作为入门课程? C语言 ...

  7. 《数据结构与抽象:Java语言描述(原书第4版)》一JI2.3 抛出异常

    本节书摘来华章计算机<数据结构与抽象:Java语言描述(原书第4版)>一书中的第2章 ,[美]弗兰克M.卡拉诺(Frank M. Carrano) 蒂莫西M.亨利(Timothy M. H ...

  8. C语言入门书籍推荐:C语言程序设计:现代方法(第二版-作者K.N.King) APP推荐:微信读书

    C语言入门书籍推荐:C语言程序设计:现代方法(第二版-作者K.N.King)&& APP推荐:微信读书 结论 受众 第一种情况 第二种情况 读后感 利用微信读书 写在最后 结论 先说结 ...

  9. 跟涛哥一起学习嵌入式 33:《嵌入式C语言自我修养》书稿V1.0版

    经常有网友要我推荐一些关于嵌入式方面的书,尤其是一些转行学嵌入式的朋友,该看那些书能快速构建嵌入式学习所需要的知识体系呢?嵌入式是一门交叉学科,没有足够的知识储备,上来就学习的话,往往也就成了走过场, ...

  10. 《C语言程序设计与实践(第2版)》——3.2 数据类型

    本节书摘来自华章出版社<C语言程序设计与实践(第2版)>一书中的第3章,第3.2节,作者:凌云等著,更多章节内容可以访问云栖社区"华章计算机"公众号查看 3.2 数据类 ...

最新文章

  1. oracle sys改密码,Oracle修改SYS密码
  2. ACM入门之【线段树】
  3. SAP、ORACLE、用友、金蝶四大ERP软件供应商的区别
  4. java定时器的使用
  5. 浅谈 温故知新——HTML5!
  6. 理解 .NET Platform Standard
  7. java ee me se_java EE ME SE有什么关系
  8. 【Android Developers Training】 8. 定义Action Bar风格
  9. Cisco Packet Tracer7.1 rip协议实验
  10. http 与https 区别浅析
  11. Python+OpenCV:ORB: An efficient alternative to SIFT or SURF
  12. Python Matplotlib add_subplot 和 subplots_adjust详解及代码详细说明 配图片说明
  13. mysqldump关于--set-gtid-purged=OFF的使用(好文章!!)
  14. ADSL上网常见故障解答
  15. 陕西计算机在职研究生院校排名,陕西在职研究生哪个学校好上
  16. hive与es交互bug
  17. 【python掩膜及多子图colorbar】
  18. C语言 键盘输入年月日,计算该年第几天
  19. 国内各大优秀免费源码下载站集合!
  20. Oracle建库建表

热门文章

  1. RNNnoise知识整理
  2. 网页加载出现没有合适的负载均衡器_一篇文章彻底了解清楚什么是负载均衡
  3. 的唯一性_原神:被氪金玩家淹没的角色,输出很高,技能具有唯一性
  4. ubuntu运行c/c++语言
  5. vmware虚拟机上的debian系统通过电脑的无线网卡联网
  6. RHEL6/7 下安装 devtoolset-3/4
  7. python架构师书籍_阿里巴巴高级架构师:学好python这本书必看,堪称python入门宝典...
  8. java queue代码_java代码实现顺序队列
  9. 用C++计算文件的MD5值
  10. TIA protal与SCL从入门到精通(1)——SCL编程入门