终于港到实现。。


LL(1)文法与LL(1)分析

当一个文法满足以下条件时,称这个语法为LL(1)语法,LL(1)语法可以通过LL(1)的分析方法进行分析

  • 文法不含有左递归
  • 同一非终结符的FIRST集合两两不相交
  • 非终结符的FIRST集若包含 ε,则不能与FOLLOW集相交

LL(1)分析:

  • 若当前待匹配符号属于当前非终结符的某个产生式的候选首符集中,则将该非终结符按此产生式推导
  • 若当前待匹配符号 a 不属于当前非终结符 A 的任何一个候选首符集,
    • 若存在产生式 A → ε 且 a 属于FOLLOW(A),则将 A 推导为 ε;
    • 否则 a 在此处为语法错误

LL(1)语法分析是一种自上而下分析的方法,从文法开始符号出发匹配输入符号,语法树自上而下生成
第一个L指从左向右扫描输入串,第二个L指最左推导,(1)指每次向前看1个输入符号进行语法分析


LL(1)语法分析的实现

递归下降分析程序

顾名思义,采用递归的方式向下构建语法树
每个非终结符的推导都作为一个函数过程

例,有LL(1)文法G(E)如下,
E → T EE
EE → + T EE | ε
T → F TT
TT → * F TT | ε
F → (E) | i

void parser(){token = lexer();//词法分析获取下一tokenE();
}void E(){//用E匹配当前token   E → T EET();EE();
}void T(){//用T匹配当前token   T → F TTF();TT();
}void EE(){//EE → + T EE | εif(token == '+'){token = lexer();//当前token ‘+’ 得到匹配,继续分析T();EE();}//无操作即可匹配 ε//也可以显式匹配 ε//else if(FOLLOW(EE).contains(token))return;//else printfError();//或者else if(!FOLLOW(EE).contains(token)){ printfError(); }
}void F(){//F → (E) | iif(token == '('){token = lexer();E();if(token == ')'){ token = lexer(); }else printfError();}else if(token == 'i'){ token = lexer(); }else printError();
}void TT(){//TT → * F TT | εif(token == '*'){token = lexer();F();TT();}//无操作即将TT推导为 ε,是否可以推导为 ε 交给在后面的语法分析中验证
}

递归下降由此得名


预测分析程序

有一个问题,如果我们用于编写parser的语言不支持递归,该如何实现LL(1)的语法分析呢

递归程序一般可改写为栈+数据表的形式

下面是预测分析程序的构成

  • 控制程序。最早的压栈处理、根据现行栈顶符号和当前输入符号执行动作
  • 分析表M[A,a]矩阵,A ∈ VN,a ∈ VT 或 a == #(假设#是输入串结束标志),当要用非终结符 A,
    匹配输入符号 a 时,查表M[A,a],根据M[A,a]的情况,进行响应的操作
  • 分析栈,存放当前推导过程中的文法符号,用于当前匹配的符号均在栈顶

设栈顶元素为X,当前输入符号为a,分析过程如下:

  • 假设输入串结束标志为 #,开始时在栈中压入 # 与文法开始符号 S
  • 若 X == a == #,则分析结束且成功
  • 若 X == a ≠ #,则 X 与 a 匹配,X 出栈,将下一输入符号保存至 a
  • 若 X 是一个非终结符,则查表M[A,a]
    • 若M[A,a]为一个产生式,则将 X 出栈,将产生式的右部反序入栈(若产生式右部为 ε 则不用入栈)
    • 若M[A,a]中为出错标志,则调用出错诊察程序

与词法分析的表驱动法类似,预测分析程序也是使用 查表 + 流程控制 实现

LL(1)文法保证了语法分析的每一步都是确定的,因此我们就可以“预测”文法符号的推导方向
这大概是预测分析程序的“预测分析”由来

void parser(){int unfinished = 1;Stack stack = new Stack();stack.push("#");stack.push("S");//文法开始符号Swhile(unfinished){a = lexer();//获得待匹配输入符号if(X ∈ 终结符){if(X == a){ stack.pop(); }else printError();}else {if(X == "#"){ if(a == "#"){ unfinished = 0; }else printError();}else if(M[X,a] == {X → X1X2X3…Xn}){if(X1 == ε)continue;stack.push(Xn);......stack.push(X3);stack.push(X2);stack.push(X1);}else printError();}}
}

那么这个分析表是什么亚子呢?

预测分析表的构造
以文法G(E)为例
E → TE/
E/ → + TE/ | ε
T → FT/
T/ → * FT/ | ε
F → (E) | i

i + * ( ) #
E E → TE/ E → TE/
E/ E/ → + TE/ E/ → ε E/ → ε
T T → FT/ T → FT/
T/ T/ → ε T/ → * FT/ T/ → ε T/ → ε
F F → i F → (E)

相信很容易就可以发现规律,如果输入符号 a 包含在非终结符 A 的某个FIRST集中,
那么M[A,a] = 该FIRST集对应的产生式
如果 A 没有任何一个FIRST集 包含 a,
- 如果存在产生式 A → ε,且 a ∈ FOLLOW(A),则M[A,a] = A → ε
- 否则,a 不能够被分析表接受,出现语法错误,M[A,a] = 出错标志


预测分析程序的匹配过程
以输入串 i1 * i2 + i3 为例,预测分析程序的匹配过程如下


扩展的巴科斯范式

在元符号 → 或 ::= 和 | 的基础上,扩充几个元语言符号:

  • 用花括号{α}表示闭包运算α*
  • 用 {α}n0 表示可以任意重复[0,n]次
  • 用方括号 [α] 表示{α}10,即 α | ε,α 可有可无,或者说是可选的

扩充的巴克斯范式,语义更强,便于表示左递归的消去和因子的提取
如我们一直用来举例的文法
E → T | E + T
T → F| T*F
F → i | (E)
用扩展的巴科斯范式可以表示成
E → T { +T }
T → F { *F }
F → i | (E)

然后有什么用呢?

可以画出下面的语法图奥

很清楚了吧,懂我的意思吧

void parser(){token = lexer();//词法分析得到下一单词符号E();
}void E(){T();while(token == "+"){token = lexer();T();}
}void T(){F();while(token == "*"){token = lexer();F();}
}void F(){if(token == "i"){token = lexer();}else if(token == "("){token = lexer();E();if(token == ")"){ token = lexer(); }else printError();}else printError();
}

扩展的巴科斯范式引入了循环,使得语法描述中的重复可以直观地使用 循环 来描述,进而使用循环实现,而不一定是递归
显然,扩展的巴克斯范式给出的递归下降分析程序更加直观,没有更多引入的非终结符

斯巴拉西


LL(1)文法与二义性

奈斯,我们现在晓得如何构造分析表、实现预测分析程序了
然而某些语法,消除左递归、提取左公共因子后仍然不满足LL(1)文法的条件,因为它有二义性

比如描述选择结构的文法
S → if Condition S | if Condition S else S | statement
Condition → bool

提取左公因式后
S → if Condition SS/ | statement
S/ → else S | ε
Condition → bool

斯巴拉西
看哈这个文法的FIRST集和FOLLOW集

FIRST(S) → { { if },{ statement } }   FOLLOW(S) → { #,else }
FIRST(S/) → { else,ε }       FOLLOW(S/) → { #,else }
FIRST(Condition) → { bool }     FOLLOW(Condition) → { if,statement }

明显可以看到,FIRST(S/)∩FOLLOW(S/) = { else },不符合LL(1)文法的条件
因为在用非终结符 S/ 匹配输入符号 else 时,可以选择把 S/ 推导为 else S,进一步即使 else 得到匹配,
也可以选择把 S/ 推导为 ε,让后面的符号来匹配这个 else,
也就是说,在非终结符 S/ 匹配输入符号 else 时,对应着两种不同的语法树,会产生歧义

这个二义性文法的分析表如下,可以看到分析表中有多重定义入口(一个格子里有多个产生式)

if bool else statement #
S S → if Condition SS/ S → statement
S/ S/ → else S
S/ → ε
S/ → ε
Condition Condition → bool

上面这个问题是编程语言普遍存在的问题,它是 else 与 if 的结合次序的问题

if()
if(){}
else{}

那么下面的 else 是与第一个 if 匹配,还是与第二个 if 匹配呢

即在上图情况中,token⑥是由内层S/匹配与 if③ 结合,还是将内层S/推导为 ε 使⑥与外层的 if① 结合?

也就是说,
如果使用产生式 S/ → else S,那么 else 将与最近的未匹配 if 匹配
如果使用产生式 S/ → ε,那么 else 将与最远的未匹配 if 匹配

上面的例子表明,存在多重入口的分析表会产生不同的语法树,对应着不同的语义(二义性)

如果语言规范指明了,else 会与最近/远的 if 匹配,那么就可以通过人为方式解决冲突(删除不符合语义的产生式)

实际上大部分程序设计语言都是这么做的
一般的,程序设计语言都选择就近匹配,即只保留产生式 S/ → else S

if bool else statement #
S S → if Condition SS/ S → statement
S/ S/ → else S S/ → ε
Condition Condition → bool

如下图

onemore thing,

G为LL(1)文法 ⇔ G的预测分析表不含有多重定义入口
LL(1)文法不是二义的
证明略


所以说,即使语言是二义的,文法是二义的,构造出来的分析表也有冲突,也可以根据合理的语言规范消除冲突


2019/8/13

语法分析:LL(1)语法分析的实现及扩展的巴科斯范式相关推荐

  1. 什么是BNF EBNF 巴科斯范式及其扩展 BNF Augmented BNF

    什么是BNF范式,什么又是EBNF范式? 巴科斯范式及其扩展 BNF & Augmented BNF                 什么是巴科斯范式?      巴科斯范式(BNF: Bac ...

  2. java 范式 问号_巴科斯范式和扩展巴科斯范式

    巴科斯范式(也称为巴科斯-瑙尔范式.巴克斯-诺尔范式)即 BNF 是一种用于表示上下文无关文法的语言,上下文无关文法描述了一类形式语言.尽管巴科斯范式也能表示一部分自然语言的语法,它还是更广泛地使用于 ...

  3. 【编译器学习】EBNF扩展巴科斯范式学习总结

    EBNF扩展巴科斯范式学习总结 是一种描述计算机编程语言且与上下文无关(没有前置条件)的语法的一种语法表达式.简而言之,就是描述语言语法的规范. EBNF的基本语法形式如下: 左手边(LeftHand ...

  4. 扩展巴科斯范式(EBNF)简介

    介绍 扩展巴科斯-瑙尔范式(Extended Backus–Naur Form,EBNF)是一种用于描述计算机编程语言等正式语言的与上下文无关语法的元语法(metasyntax)符号表示法.简而言之, ...

  5. ebnf范式_(4条消息)扩展巴科斯范式(EBNF)简介

    介绍 扩展巴科斯-瑙尔范式(Extended Backus–Naur Form,EBNF)是一种用于描述计算机编程语言等正式语言的与上下文无关语法的元语法(metasyntax)符号表示法.简而言之, ...

  6. 扩展巴科斯范式 EBNF

    EBNF Extended Backus–Naur Form 定义: 代码,如由终结符即可视字符.数字.标点符号.空白字符等组成的计算机程序的源代码. | 竖杠表示可供选择, "-" ...

  7. iOS字符串处理笔记(正则表达式、NSScanner扫描、CoreParse解析器)

    搜索 在一个字符串中搜索子字符串 最灵活的方法 1 - (NSRange)rangeOfString:(NSString *)aString options:(NSStringCompareOptio ...

  8. 语法格式描述规范BNF、EBNF、ABNF

    1. 简介 BNF 是最原始,最简单的方法,主要用于理论背景的学术论文中,以与人类进行交流.(与在编译器/解析器中使用相反).BNF 没有确切的规范. EBNF 是 Extended BNF (扩展的 ...

  9. 基于Websocket草案10协议的升级及基于Netty的握手实现

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 最近发现 ...

最新文章

  1. NopCommerce架构分析-依赖注入
  2. inter® management engine interface黄色感叹号解决方法
  3. 红帽技术开放日:参与开源社区不只有贡献代码这一种方式
  4. php 实现 pacs 系统,影像管理系统(PACS)
  5. android gridview 间隔线,Android开发之RecyclerView的间隔线处理
  6. 创意夜晚行驶迷路网站404页面源码
  7. 前端开发-编辑器安装-HbuilderX安装过程与基本使用 0226
  8. LINQ语法类似于SQL的语法
  9. 图像增强处理之:同态滤波与Retinex算法(三)Retinex邻域算法:SSR,MSR,MSRCR
  10. pic单片机c语言程序设计实例精粹 pdf,PIC单片机C语言程序设计.pdf
  11. 用微信公众号做淘宝优惠券查券搜券和返利机器人的详细配置教程
  12. Spring 事务传播机制
  13. 今日头条 java笔试题_今日头条笔试第一题
  14. 英语学习之‘加减乘除’
  15. 如何使用图灵机器人实现自动回复?
  16. Robot Framework Selenium UI自动化测试 --- 进阶篇
  17. 1.5Go语言的基本数据类型
  18. 20154312 曾林 ExpFinal CTF Writeup
  19. 使用mpx开发外卖小程序完整教程(附源码)
  20. 索骥馆-网络营销之《锦囊妙计 网站推广101招 第7版》扫描版[PDF]

热门文章

  1. 码分多址cdma通信_码分多址(CDMA)| 计算机网络
  2. 【前端学习】前端学习第十五天:JavaScript中的事件模型
  3. 实战智能推荐系统(14)-- 推荐系统架构
  4. 语法转换_【语法专题】句型转换(上)
  5. Java Gui 简介
  6. python利用server酱推送IP地址
  7. Java——超市会员管理系统(JDBC+MySQL+Apache DBUtils)
  8. 基于Abaqus的边坡可靠度计算
  9. Google面试之快乐智力题(Cracking the Coding Interview)
  10. 360android文件恢复,删除的文件怎么恢复?360删除的文件怎么恢复?