2.2 语法分析

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

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

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

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

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

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

2.2.1 yacc 介绍

引用:https://blog.csdn.net/zdy0_2004/article/details/54918450

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 的文件格式分为三个部分:

  1. ...definitions...( % {} % )  
  2. % % 
  3. ...rules...
  4.  % % 
  5. ...subroutines...  
  • 定义部分:定义部分包括标志(token)定义和C代码(用"%{"和"%}"括起来)。
  • 规则部分:规则中目标或非终端符放在左边,后跟一个冒号(:),然后是产生式的右边,之后是对应的动作(用{}包含)
  • 第三部分:该部分是函数部分。当yacc解析出错时,会调用函数yyerror(),用户可自定义函数的实现。
    • 递归的处理:递归处理有左递归和右递归。
    • If-else 的冲突:当有两个IF一个ELSE时,该ELSE和哪个IF匹配是一个问题。有两种匹配方法:与第一个匹配和与第二匹配。现代程序语言都让ELSE与最近的IF匹配,这也是yacc的缺省行为。
    • 出错处理:当yacc解析出错时,缺省的行为是调用函数yyerror(),然后从yylex返回一个值。一个更友好的方法是忽略一段错误输入流,继续开始扫描。这里要涉及到YACC中错误保留字error的应用。

GCC编译器原理(三)------编译原理三:编译过程(2-2)---编译之语法分析的更多相关文章

  1. gcc编译器参数使用及解决

    gcc -c CStringAndPointer.c -o CStringAndPointer.o 执行时出现问题: ./CStringAndPointer.o bash: ./CStringAndP ...

  2. GCC编译器原理(三)------编译原理三:编译过程---预处理

    Gcc的编译流程分为了四个步骤: 预处理,生成预编译文件(.文件):gcc –E hello.c –o hello.i 编译,生成汇编代码(.s文件):gcc –S hello.i –o hello. ...

  3. 跟vczh看实例学编译原理——三:Tinymoe与无歧义语法分析

    文章中引用的代码均来自https://github.com/vczh/tinymoe.   看了前面的三篇文章,大家应该基本对Tinymoe的代码有一个初步的感觉了.在正确分析"print ...

  4. jQuery-1.9.1源码分析系列(三) Sizzle选择器引擎——编译原理

    这一节要分析的东东比较复杂,篇幅会比较大,也不知道我描述后能不能让人看明白.这部分的源码我第一次看的时候也比较吃力,现在重头看一遍,再分析一遍,看能否查缺补漏. 看这一部分的源码需要有一个完整的概念后 ...

  5. gcc/g++等编译器 编译原理: 预处理,编译,汇编,链接各步骤详解

    摘自http://blog.csdn.net/elfprincexu/article/details/45043971 gcc/g++等编译器 编译原理: 预处理,编译,汇编,链接各步骤详解 C和C+ ...

  6. 正则表达式引擎的构建——基于编译原理DFA(龙书第三章)——3 计算4个函数

    整个引擎代码在github上,地址为:https://github.com/sun2043430/RegularExpression_Engine.git nullable, firstpos, la ...

  7. GCC编译器原理(二)------编译原理一:ELF文件(2)

    四. ELF 文件格式分析 ELF文件(目标文件)格式主要四种: 可重定向文件: 文件保存着代码和适当的数据,用来和其他的目标文件一起来创建一个可执行文件或者是一个共享目标文件.(目标文件或者静态库文 ...

  8. 学了编译原理能否用 Java 写一个编译器或解释器?

    16 个回答 默认排序​ RednaxelaFX JavaScript.编译原理.编程 等 7 个话题的优秀回答者 282 人赞同了该回答 能.我一开始学编译原理的时候就是用Java写了好多小编译器和 ...

  9. 浅谈C++编译原理 ------ C++编译器与链接器工作原理

    原文:https://blog.csdn.net/zyh821351004/article/details/46425823 第一篇:      首先是预编译,这一步可以粗略的认为只做了一件事情,那就 ...

  10. Knowledge Point 20180303 对比编译器、解释器与Javac编译原理

    编译器与Javac编译原理 在前文我们知道了Java是一种编译语言和解释语言,它的源代码经过编译器Javac编译为能够被JVM识别的二进制语言,然后JVM将其解释为能够被平台识别的机器语言.那么什么是 ...

随机推荐

  1. 「ZJOI2016」旅行者 解题报告

    「ZJOI2016」旅行者 对网格图进行分治. 每次从中间选一列,然后枚举每个这一列的格子作为起点跑最短路,进入子矩形时把询问划分一下,有点类似整体二分 至于复杂度么,我不会阿 Code: #incl ...

  2. A/D和D/A的学习

    从我们学到的知识了解到,我们的单片机是一个典型的数字系统.数字系统只能对输入的数字信号进行处理,其输出信号也是数字信号.但是在工业检测系统和日常生活中的许多物理量都是模拟量,比如温度.长度.压力.速度 ...

  3. Spring注解方式实现任务调度

    原文:http://docs.spring.io/spring/docs/4.0.1.BUILD-SNAPSHOT/javadoc-api/ 注解类型:EnableScheduling @Target ...

  4. git init github

    Command line instructions 执行这些命令是在windows 右菜单里面的git bash运行. Git global setup git config --global use ...

  5. 解决MySQL5.7密码重置问题

    前言:最近活动,买了台服务器,环境什么的都弄完了,MySQL是安装的5.7的版本,连接进入的时候出现了下面的错误 这其实是MySQL5.7的一个安全机制,需要你重新设置密码. set password ...

  6. struts2 contextMap

    一.contextMap中的数据操作 root根:List 元素1 元素2 元素3 元素4 元素5 contextMap:Map key value application Map key value ...

  7. ES6优缺点

    看了一篇ES6语法示例,觉得还可以 here ES6是新一版的标准,对语言有扩展,添加了新的属性与方法.这一标准虽然现在还要用babel来做浏览器支持(将ES6编译成ES5才能被浏览器支持),但是未来 ...

  8. POJ 3687 Labeling Balls (top 排序)

    Labeling Balls Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 15792   Accepted: 4630 D ...

  9. (叉乘求面积) nyoj1011-So Easy[II]

    1011-So Easy[II] 内存限制:64MB 时间限制:1000ms 特判: No通过数:2 提交数:4 难度:2 题目描述: 这是一道基础的计算几何问题(其实这不提示大家也都看的出).问题描 ...

  10. linux c 编程 ------ 通过设备节点调用驱动

    驱动程序如下,加载驱动后,会在/dev文件夹下生成一个文件hello_device_node,是此驱动的设备节点 #include <linux/init.h> #include < ...