450 likes | 727 Views
语法分析 —— 上下文无关文法. 授课:胡静. 目录. 复习内容 上下文无关文法( CFGs ) 推导 语法分析树和抽象语法 二义性文法 属性文法 YACC. 语法分析器所处的位置. 语法分析的例子. 语法分析总述. 达到的目标:确定输入的 token 流是否符合程序的语法,如果符合的话,要识别出其结构。 语法分析需要如下条件: 对语法进行描述的方法 一个接受器的机制,来确定输入的 token 流是否符合语法描述。 能够还原语法结构的方法。. 为什么不能用正则表达式. 正则表达式可以对 token 进行表示
E N D
语法分析——上下文无关文法 授课:胡静
目录 • 复习内容 • 上下文无关文法(CFGs) • 推导 • 语法分析树和抽象语法 • 二义性文法 • 属性文法 • YACC 编译原理
语法分析器所处的位置 编译原理
语法分析的例子 编译原理
语法分析总述 • 达到的目标:确定输入的token流是否符合程序的语法,如果符合的话,要识别出其结构。 • 语法分析需要如下条件: • 对语法进行描述的方法 • 一个接受器的机制,来确定输入的token流是否符合语法描述。 • 能够还原语法结构的方法。 编译原理
为什么不能用正则表达式 • 正则表达式可以对token进行表示 • 用正则表达式对token进行描述,而后可以很方便有效的执行(使用DFA) • 不能够使用正则表达式来描述程序语言的语法: • 正则表达式没有足够的描述能力来描述程序语言的语法。 • 例如:嵌套的等价结构 • 比如括号嵌套 • { (), ()(), (()), (())(), ()(()), (())(()), ((())(())), etc. } 编译原理
上下文无关文法 • 上下文无关文法是一个四元组表示形式 <V,Σ,S,→> • V是非终结符的有限集合 • Σ是终结符的有限集合 • S ∈ V 是一个特殊的非终结符,叫做开始符号 • →⊆ V × (V ∪ Σ)*是一个有限关系,叫做产生式。 • 上下文无关文法简写为 CFG 编译原理
推导 • 设G = <V,Σ,S,→>,是一个上下文无关文法,“直接的推导”关系 (⇒) 定义为 { <αAγ, αβγ> | A→β }. • 例如: • 设G的文法的产生式是 S → aSbS | ε • 那么 • S ⇒ aSbS • aSbS ⇒ aaSbSbS • aaSbSbS ⇒ aabSbS • aabSbS ⇒ aabbS • aabbS ⇒ aabbaSbS • aabbaSbS ⇒ aabbabS • aabbabS ⇒ aabbab 编译原理
上下文无关语言 • 设G = <V,Σ,S,→>是CFG,由G所产生的语言表示为L(G), L(G) = { x | S ⇒* x } • L(G) = 从开始符号开始,将产生式作为规则改写的手段,重复的使用,最后得到的终结符号串的集合,叫做L(G)。 • 上下文无关语言(CFLs)是由上下文无关文法产生的语言。 • 如果x ∈ L(G),那么x的推导是一个字符串序列 S=α0 , α1 , ... , αn=x, 其中每一个αi ⇒ αi+1当0≤i<n。我们写作α0⇒α1 ... ⇒ αn。 编译原理
举例 • 设有文法G,其产生式是S → aSbS | ε • 那么 • S ⇒ aSbS ⇒ aaSbSbS ⇒ aabSbS ⇒ aabbS ⇒ aabbaSbS ⇒aabbabS ⇒ aabbab • 也就是说,属于L(G) 编译原理
文法和接受器 • 上下文无关文法的接受器如下: • 语法分析器 = CFG接受器,当token流被接受时,输出相应的推导 • 例子: LL(k), LR(k), SLR, LALR 编译原理
每个正则语言都是CFL • 为每个RE归纳的建立一个CFG • ε S → ε • a S → a • R1R2 S → S1S2 • R1 | R2 S → S1 | S2 • R1* S → S1 S | ε 编译原理
求和的文法 • 文法: • S → E + S | E • E → number | ( S ) • Expanded: • 可接受的输入的例子: • (1+2+(3+4))+5 编译原理
推导的例子 编译原理
推导和分析树 • 语法树:描述推导过程的树形结构 • 树的叶子是终结符 • 中间结点是非终结符 • 不能够描述推导步骤的顺序。 编译原理
分析树和抽象语法树(AST) • 分析树也叫做具体语法树 编译原理
推导顺序 • 可以选择任意顺序来使用产生式;选择任意一个非终结符A,比如:αAγ ⇒ αβγ • 两个标准的顺序:最左推导和最右推导——使用在不同类的自动语法分析当中。 • 最左推导:总是替换最左边的非终结符 • E + S ⇒ 1 + S • 最右推导:总是替换最右边的非终结符 • E + S ⇒ E + E + S 编译原理
例子 • S → E + S | E • E → number | ( S ) • 最左推导 • 最右推导 • 相同的分析树:选择的相同的产生式,只是顺序不同。 编译原理
二义性文法 • 考虑另一个文法 • S → S + S | S * S | number • 二义性文法:不同的推导产生不同的分析树。 • 考虑表达式1 + 2 * 3 • 推导1: S ⇒ S + S ⇒1 + S ⇒1 + S * S ⇒1 + 2 * S ⇒ 1 + 2 * 3 • 推导2: S ⇒S * S ⇒ S * 3 ⇒S + S * 3 ⇒S + 2 * 3 ⇒1 + 2 * 3 编译原理
二义性的影响 • 不同的分析树对应不同的赋值。 • 对程序来讲,其含义会产生二义性。 编译原理
消除二义性 • 通常可以通过增加非终结符,或者只允许左递归/右递归的方式来消除二义性。 • T非终结符强迫了优先级 • 左递归:左结合 编译原理
目录 • 复习内容 • 上下文无关文法(CFGs) • 推导 • 语法分析树和抽象语法 • 二义性文法 • 属性文法 • YACC 编译原理
属性文法举例 编译原理
翻译模式 • 翻译模式给出了使用语义规则进行计算的次序,这样就可以把某些细节表示出来。 • 在翻译模式中,和文法符号相关的属性和语义规则(这里也称语义动作),用“{}”括起来,插入到产生式右部合适的位置上。 • 翻译模式给出了使用语义规则进行计算的顺序。 编译原理
翻译模式举例 • E→TR • R→addop T {print(addop.lexeme)} R1 | ε • T→num {print(num.val)} 9-5+2 按深度优先遍历之后 95-2+ E R T - {print(‘-’)} 9 T R {print(‘9’)} 5 {print(‘5’)} + {print(‘+’)} T R 2 {print(‘2’)} ε 编译原理
翻译模式举例 S→{B.ps := 10} B {S.ht := B.ht} B→{B1.ps := B.ps} B1 {B2.ps := B.ps} B2 {B.ht := max(B1.ht, B2.ht)} B→ {B1.ps := B.ps} B1 sub {B2.ps := shrink(B.ps)} B2 {B.ht := disp(B1.ht, B2.ht)} B→text {B.ht := text.h ×B.ps} 编译原理
目录 • 复习内容 • 上下文无关文法(CFGs) • 推导 • 语法分析树和抽象语法 • 二义性文法 • 属性文法 • YACC 编译原理
YACC简介 • YACC:Yet Another Compiler Compiler 程序语言的YACC规范说明 程序语言语法分析器 YACC程序 输入串 语法树 程序语言语法分析器 编译原理
YACC规格说明 • YACC规格说明——YACC源程序 • 说明部分 • 翻译规则 • 辅助过程 说明部分 %% 翻译规则 %% 辅助过程 编译原理
YACC规格说明 用%{和%}括起来的部分是C语言程序的正规说明 说明翻译规则和辅助过程里使用的变量和函数的类型 编译原理
YACC规格说明 形如 左部→候选1|候选2|…|候选n| 的产生式,在YACC规格说明里写成 左部:候选1{语义动作1} |候选2{语义动作2} …… |候选n{语义动作n} ; 编译原理
YACC规格说明 E→E+T|T T→T*F|F F→(E)|digit 编译原理
LR语法分析器的结构 • 一个LR分析器实质上是一个带先进后出存储器(栈)的确定有限状态自动机。 编译原理
LR语法分析器分析过程举例 文法G:(1)E E+T (2)E T(3)T T*F (4)T F (5)F (E) (6)F i 编译原理
YACC中的冲突 • YACC可以指出移进-归约冲突和归约-归约冲突默认情况下: • 移移进-归约冲突中选择移进 • 归约-归约冲突中选择先出现的规则来归约 优先级升高 编译原理
语法分析过程 • 语法分析开始于词法分析阶段,其输入为词法分析输出的token序列,输出为语法树 • 语法树和源程序中各个语句的语法结构一一对应,并且作为后续语义分析的重要数据结构 • 词法分析输入的token序列中包括诸如变量、数字、操作符等,对应于产生式中的终结符 • 在token序列中,各个token按照上下文无关文法的规则连接在一起 • 语法分析的任务是将token序列中是否采用正确的语法规则分析出来,并且以树形的数据结构表示出来,最后整个源程序将转化为一棵以程序节点为根,非终结符为中间节点,终结符为叶子节点的语法树。 编译原理
抽象语法树的树形结构 程序 程序头 子程序 代码头 代码体 标签 常数 变量 代码段 复合语句 函数声明 过程声明 赋值语句 过程语句 复合语句 If语句 Case语句 … 函数头 子程序 过程头 子程序 编译原理
YACC的例子 • YACC中文件的说明: • 生成语法分析器文件为spl.y。其中进行SPL语法的描述,在语法检查和分析过程中,加入语法树节点生成的代码。 • Spl.y包含了语法规则的描述和语法树节点生成的程序,YACC程序根据这些自动生成LALR(1)分析表,并用一系列矩阵存储分析表中的内容 • Yacc的输出文件rule.h/rule.c中的yytable及相关部分代码 • YACC程序初始化接收token序列的数组数据结构(包含方法),用于语法分析过程的栈数据结构(包含压入和弹出操作),以及查找LALR分析表和进行语法分析动作。 • 按照语法分析过程组织上述数据结构,完成语法分析 编译原理
YACC的例子 • 存储输入的token序列需要的字符串数组数据结构——rule.c中的函数yylex就实现了token序列生成和返回token的操作(01850) • 分析过程的中间状态需要栈数据结构。Rule.c中存储分析过程的中间状态的栈为(01710) • 文法规则需要存储在表数据结构中,这个数据结构是yytable,其使用见01870行。 • 当状态值为正时,移进token • 状态值为负时,采用正数对应的规则进行规约 • 如果是0,则依照YYDEFACT中定义的情况动作 • 如果是YYTABLE_NINF即-169为语法错误 • Yytable的定义在0999行,yyreduce在01913行 编译原理
语法分析过程中的数据结构 • 语法树需要树数据结构来存储。Tree.h中定义的_tree结构体(046) • Op记录节点或者树的操作参见ops.h中的节点和树操作的枚举结构定义 • Result_tye记录节点的类型 • Kids[2]记录该节点或树的两个孩子节点或树 • U为当前树节点的值部分 tree op Resule_type u 编译原理
具体实现 • A程序 • Spl.y中的00226开始。创建树为00235,对应rule.c为01940 • 树节点的操作类型为TAIL,表示该树节点实现的是程序结束的操作。见ops.h中TAIL类型的定义0068 • New_tree函数的实现在tree.c中0008 • 子程序sub_program的创建:00285树节点创建:00306和00309,对应于rule.c中也有相应表示。 • HEADER表示该树节点定义的为程序的开始;ROUTINE表示该节点定义了程序主体部分并将程序头节点作为函数节点的左子树 编译原理
具体实现 • B子代码(子程序) • 函数声明00718,树生成00726 • 函数头00744,树生成00778和00782 编译原理
Thanks for your time! Questions & Answers 编译原理