GCC编译器原理(三)------编译原理三:编译过程(2-2)---编译之语法分析

发布者:Huanle最新更新时间:2024-08-22 来源: cnblogs关键字:GCC编译器  编译过程  语法分析 手机看文章 扫描二维码
随时随地手机看文章

2.2 语法分析

语法分析器(Grammar Parser)将对由扫描器产生的记号进行语法分析,从而产生语法树(Syntax Tree)。整个分析过程采用了上下文无关语法(Context-free Grammar)的分析手段。


由语法分析器生成的语法树就是以表达式(Expression)为节点的树。如下所示:


从图中可以知道,整个语句就是一个赋值表达式:赋值表达式的左边是一个数组表达式,右边是一个乘法表达式;数组表达式又由两个符号表达式组成,等等。符号和数字是最小的表达式,它们不是由其他表达式来组成,所以它们通常作为整个语法树的叶节点。


在语法分析的同时,很多运算符号的优先级和含义也被确定下来了。比如乘法表达式比加法表达式的优先级高。


另外有些符号具有多重含义,比如 * 在C语言中可以表示乘法表达式,也可以表示指针取内容的表达式,所以语法分析阶段必须对这些内容进行区分。如果出现了表达式不合法,比如各种括号不匹配、表达式中缺少操作符等,编译器就会报告语法分析阶段的错误。


语法分析工具使用 yacc(Yet Another Compiler Compiler),它像 lex 一样,可以根据用户给定的语法规则对输入的记号序列进行解析,从而构建出一棵语法树。


2.2.1 yacc 介绍

yacc(Yet Another Compiler Compiler),是Unix/Linux上一个用来生成编译器的编译器(编译器代码生成器).


使用巴克斯范式(BNF)定义语法,能处理上下文无关文法(context-free)。出现在每个产生式左边(left-hand side:lhs)的符号是非终端符号,出现在产生式右边(right-hand side:rhs)的符号有非终端符号和终端符号,但终端符号只出现在右端。


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


LR(k)分析方法是1965年Knuth提出的,括号中的k(k >=0)表示向右查看输入串符号的个数。LR分析法正视给出一种能根据当前分析栈中的符号串和向右顺序查看输入串的k个符号就可唯一确定分析器的动作是移进还是规约和用哪个产生式规约。


这种方法具有分析速度快,能准确,即使地指出出错的位置,它的主要缺点是对于一个使用语言文法的分析器的构造工作量相当大,k愈大构造愈复杂,实现比较困难。


一个LR分析器有3个部分组成:

总控程序,也可以称为驱动程序。

对所有的LR分析器总控程序都是相同的。

分析表或分析函数。

不同的文法分析表将不同,同一个文法采用的LR分析器不同时,分析表也不同,分析表又可分为动作(ACTION)表和状态转换(GOTO)表两个部分,它们都可用二维数组表示。

分析栈,包括文法符号栈和相应的状态栈。

它们均是先进后出栈。 分析器的动作由栈顶状态和当前输入符号所决定(LR(0)分析器不需要向前查看输入符号)。

LR分析器工作过程如下 :

其中SP为栈指针,S[i]为状态栈,X[i]为文法符号栈。状态转换表内容按关系GOTO[Si,X] = Sj确定,该关系式是指当栈顶状态为Si遇到当前文法符号为X时应转向状态Sj。X为终结符或非终结符。 ACTION[Si,a]规定了栈顶状态为Si是遇到输入符号a应执行的动作。

执行的动作的分类:

移进:当Sj = GOTO[Si,a]成立,则把Sj移入到状态栈,把a移入到文法符号栈。其中i,j表示状态号。

规约:当在栈顶形成句柄为β时,则用β归约为相应的非终结符A,即当文法中有 A-->β的产生式,而β的长度为r(即|β| = r),则从状态栈和文法符号栈中自栈顶向下去掉r个符号,即栈指针SP减去r。并把A移入文法符号栈内,再把满足Sj = GOTO[Si,A]的状态移进状态栈,其中Si为修改指针后的栈顶状态。

接受acc:当规约到文法符号栈只剩文法的开始符号S时,并且输入符号串已结束即当前输入符是'#',则为分析成功。

报错:当遇到状态栈顶为某一状态下出现不该遇到的文法符号时,则报错,说明输入串不是该文法能接受的句子。

2.2.2 YACC 的文件格式

YACC 的文件格式分为三个部分:


...definitions...( % {} % )  

% % 

...rules...

 % % 

...subroutines...  

定义部分:定义部分包括标志(token)定义和C代码(用'%{'和'%}'括起来)。

规则部分:规则中目标或非终端符放在左边,后跟一个冒号(:),然后是产生式的右边,之后是对应的动作(用{}包含)

第三部分:该部分是函数部分。当yacc解析出错时,会调用函数yyerror(),用户可自定义函数的实现。

递归的处理:递归处理有左递归和右递归。

If-else 的冲突:当有两个IF一个ELSE时,该ELSE和哪个IF匹配是一个问题。有两种匹配方法:与第一个匹配和与第二匹配。现代程序语言都让ELSE与最近的IF匹配,这也是yacc的缺省行为。

出错处理:当yacc解析出错时,缺省的行为是调用函数yyerror(),然后从yylex返回一个值。一个更友好的方法是忽略一段错误输入流,继续开始扫描。这里要涉及到YACC中错误保留字error的应用。


关键字:GCC编译器  编译过程  语法分析 引用地址:GCC编译器原理(三)------编译原理三:编译过程(2-2)---编译之语法分析

上一篇:GCC编译器原理(三)------编译原理三:编译过程(3)---编译之汇编以及静态链接【1】
下一篇:GCC编译器原理(三)------编译原理三:编译过程(2-1)---编译之词法分析

推荐阅读最新更新时间:2024-11-06 10:58

Exynos4412 内核移植(二)—— 内核编译过程分析
内核的编译同样是从Makefile 来分析: 一、内核源码结构 Linux内核文件数目近2万,出去其他架构CPU的相关文件,他们分别位于顶层目录下的17个子目录,各个目录功能独立,下面是常用目录: arch:体系结构相关代码 ipc:进程调度相关代码 mm:内存管理 Documentation:帮助文档 net:网络协议 lib:库 scripts:编译相关脚本工具 tools:编译相关工具 drivers:设备驱动 fs:文件系统实现 对于ARM 架构的Exynos4412,其体系相关的代码在arch/arm/目录下,在后面进行的Linux移植时,开始的工作正式修改这个目录下的文件。 二、Linux Make
[单片机]
Exynos4412 内核移植(二)—— 内核<font color='red'>编译</font><font color='red'>过程</font>分析
编译通过的U-boot和使用的arm-linux-gcc编译
说实话编译U-boot挺累人的,要做的修改不是很多,但是在编译器上花的功夫却很多,经常遇到各种奇怪的问题。 下面是编译通过的U-boot和对应的gcc编译器 GCC下载地址:http://download.csdn.net/detail/king_mcu/9002001 U-Boot下载地址:http://download.csdn.net/detail/king_mcu/9002011 说明: 1、arm-linux-gcc解压到linux电脑上后,添加环境变量,这点不用多说,下面是我电脑上的安装路径: 2、解压U-boot到工作目录,修改Makefile中的编译器路径,改为你电脑上arm-linux-g
[单片机]
<font color='red'>编译</font>通过的U-boot和使用的arm-linux-<font color='red'>gcc编译</font>器
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved