语法分析的一些总结

news/2024/6/29 11:49:57 标签: 算法, , 编译器

ps:本文处理的输入为 字符串(如 “3+4*5”…),不为词法分析器分析后的输出的单词流

|| 语法分析的分析规则:上下文无关文法(定义:CFG(N, T, S, R)

  • N - Nonterminal - 非终结符
  • T - Terminal - 终结符
  • S - Start Character - 开始符号
  • R - Syntax Rule - 语法规则

|| 语法分析的分析方式:语法分析器有不同的算法可以进行语法分析,一般来说,语法分析的算法分为两种,自顶向下的算法和自底向上的算法,而这两类算法又有很多不同的实现方式,我们只谈最主流的方式

  1. 自顶向下:(分析方式)

  2. 自底向上:(分析表方式)

    • LR(0)
    • LR(1)

分析的语法分析方式

给定文法CFG和待匹配句子s,回答s能否从CFG推导出来?

|| 自顶向下算法

算法:从G的开始符号出发,随意推出某个句子t,比较s和t:

  • 若 t == s ,则回答 “是”
  • 若 t != s ,则回答 “否”
tokens[];  // 所有token
i = 0;
stack = [S] // S是开始符号
while (!stack.empty())
	if (stack.top() is a terminal t)
		if (t == tokens[i])
			pop(); //成功
			i++;
		else
			backtrack(); //回溯
	else if (stack.top() is a non-terminal T)
		pop();
		push(next possible choice); // 请注意 possible

当发现匹配错误的时候,分析需要回溯到原来的样子,然后再次遍历,效率很低。

|| 递归下降算法(纯手工编码实现语法分析器时常用的算法

(自顶向下算法+预测功能结合,即是我们的递归下降算法)

算法:修改push(next possible choice); // 请注意 possible,不选择 possible ,而是选择 right ,比如我提前看一个符号,我发现是 g ,那么我就直接选择 push g。

代码

parse_S()
	parse_N()
	parse_V()
	parse_N()

parse_N()
	token = tokens[i++] // 前看
	if (token == s || token = t ...)
		return;  // OK
	error("expect s, t, but given ...")

parse_V()
	token = tokens[i++] // 前看
	if (token == e || token = d ...)
		return; // OK
	error("expect e, d, but given ...")

分治法重新实现递归下降算法

parse_X()
	token = nextToken()
	switch(token)
	case 1: parse_E(); eat('+'); parse_T(); // ...
	case 2: // ...
	...
	default: error("expect ..., but given ...");

|| 递归下降算法的二义性问题
若CFG为以下形式,待匹配句子是 3+4*5

E -> E + T
	-> T
T -> T * F
    -> F
F -> num

代码

parse_E()
	token = tokens[i++]
	if (token == num)
		? // 是调用 E + T 还是调用 T
	else
		error("expect ..., but given ...")

此时若使用递归下降算法,调用 E + T 和调用 T都是可行的。即产生了二义性
而递归中消除二义性文法就是消除左递归和左因子

代码改进

parse_E()
	parse_T()
	token = tokens[i++]
	while (token == '+')
		parse_T()
		token = tokens[i++]

parse_T()
	parse_F()
	token = tokens[i++]
	while (token == '*')
		parse_F()
		token = tokens[i++]

分析表的语法分析方式

ps:(自动生成器里最常用的算法

|| LL(1)算法

第一个L表示从左到右读程序,第二个L表示每次优先选择最左边的非终结符推导,(1)表示前看1个符号

算法:所谓的表驱动就是给你提供一张表,通过查表你就能决定下一步往哪走。而表驱动算法的主要工作就是把这张表给你算出来。

|| 理想的有表的分析算法

tokens[];  // 所有token
i = 0;
stack = [S] // S是开始符号
while (!stack.empty())
	if (stack.top() is a terminal t)
		if (t == tokens[i])
			pop(); //成功
			i++;
		else
			backtrack(); //回溯
	else if (stack.top() is a non-terminal T)
		pop();
		//!!!!!!push(next possible choice); !!!!!!
		/*修改:根据表直接获得正确的值,而不是靠随机选择。避免导致错误而导致的回溯*/
		push(next possible choice);

在这里插入图片描述
即:有的非终结符作为行,所有的终结符作为列,然后为每一条语法规则标上行号,根据语法规则填表就行

|| 构造一张分析表的方法

|| LR(0)算法(自底向上的分析算法

同样L表示从左到右读程序,而第二个R表示每次优先选择最右边的非终结符推导,(0)表示前看0个符号

|| 自底向上算法也被称作移进-规约算法(shitf-reduce),主要是因为算法中涉及了两个常用的核心操作,shift和reduce

|| LR(0)对于原先例子的处理方式:

E -> E + T
	-> T
T -> T * F
   -> F
F -> num

在这里插入图片描述
|| LR算法的实现方式: 同确定性有限自动机DFA,我们要构造的分析表也就是状态转移表
针对以下文法:

0: S -> A$
1: A -> xxB
2: B -> y

在这里插入图片描述
在这里插入图片描述

代码

stack = []
push($) // EOF
push(1) // 初始化状态
while (true)
	token t = nextToken()
	state s = stack[top]
	if (ACTION[s, t] == "s"+i)
		push(t)
		push(i)
	else if (ACTION[s, t] == "r"+j)
		pop(第j条规则的右边全部符号)
		state s = stack[top]
		push(X) // 把第j条规则的左边非终结符入
		push(GOTO[s, X]) // 对应的状态入
	else
		error("...")

http://www.niftyadmin.cn/n/575261.html

相关文章

谈谈final、finally、 finalize有什么不同

一、final:适合用来在语义方面标识当前的方法、变量、类不可以更改,适合封装一些代码,让用的人知道这些不要随意更改。final标识的变量不等于不可变,对于变量而言这个变量只是不能够在赋值,但是可以做任何增删改查操作…

【模板】Kruskal算法求最小生成树

文章目录1.Kruskal算法介绍2.模板实现3.例题1.Kruskal算法介绍 克鲁斯卡尔算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。 把图中的所有边按代价从小到大排序&…

强引用、软引用、弱引用、幻象引用有什么区别

在Java语言中,除了基本数据类型外,其他的都是指向各类对象的对象引用;Java中根据其生命周期的长短,将引用分为4类。 1 强引用 特点:我们平常典型编码Object obj new Object()中的obj就是强引用。通过关键字new创建的对…

Mysql-5-数据表的基本操作

1.创建表:之前需要use database database_name 然后create table 表名(); 例:创建员工表tb_employee1,结构如下表所示 字段名称 数据类型 备注 id int(11) 员工编号 name var…

高斯消元法解线性方程组和矩阵求逆(C++面向对象实现)

实现了两个向量和矩阵的基本类&#xff0c;针对处理对象创建了MatrixVector类和MatrixMatrix类。使用gauss法同时实现了线性方程组的求解和矩阵的求逆 文章目录0. 预处理1. Vector向量类2. Matrix矩阵类3. MatrixVector类4. MatrixMatrix类5. 运行调用0. 预处理 #include <…

强软弱虚引用区别

1 强引用 特点&#xff1a;我们平常典型编码Object obj new Object()中的obj就是强引用。通过关键字new创建的对象所关联的引用就是强引用。 当JVM内存空间不足&#xff0c;JVM宁愿抛出OutOfMemoryError运行时错误&#xff08;OOM&#xff09;&#xff0c;使程序异常终止&…

angular2学习资源汇总

文档博客书籍类 官方网站&#xff1a; https://angular.io中文站点&#xff1a; https://angular.cnVictor的blog&#xff08;Victor是Angular路由模块的作者&#xff09;&#xff1a; https://vsavkin.com/vsavkinTodd Motto的Blog&#xff1a; https://toddmotto.com/Thought…

String、StringBuffer、StringBuilder有什么区别

1 String (1) String的创建机理 由于String在Java世界中使用过于频繁&#xff0c;Java为了避免在一个系统中产生大量的String对象&#xff0c;引入了字符串常量池。其运行机制是&#xff1a;创建一个字符串时&#xff0c;首先检查池中是否有值相同的字符串对象&#xff0c;如果…