编译技术-词法理论

一、文法的种类

1.1 分类定义

Chomsky 文法定义:

  • G = ( V , V t , P , Z ) G = (V, V_t, P, Z) G=(V,Vt,P,Z)
  • V V V:符号集合
  • V t V_t Vt:终结符号集合
  • P P P :有穷规则集合
  • Z Z Z:是被符号,不能是终结符

关于不同文法的区别

类型定义接受重点
0 型文法 α : : = β \alpha::=\beta α::=β图灵机由字符串推导出另一个字符串,对于左部和右部都没有限制
1 型文法 α U β : : = α u β \alpha U \beta ::= \alpha u \beta αUβ::=αuβ线性界限的图灵机要求左部必须有一个非终结符,而且一次只能改变一个非终结符,同时具有上下文敏感的特性
2 型文法 U : : = α U::= \alpha U::=α不确定的下推自动机左部只能有一个非终结符(也就是上下文不敏感的),但是对于右部没有限制
3 型文法 U : : = V a , U : : = a U::=Va, U:: = a U::=Va,U::=a有穷自动机右部一定会推出终结符,而且右部很规则

在判断文法的时候,因为不同文法呈现包含关系,所以应当按照 3 − > 2 − > 1 − > 0 3 -> 2 -> 1 -> 0 3>2>1>0 的顺序去判断。

当一个文法的右部呈现正则形式的时候,那么为 3 型文法,否则判断左部,如果左部都是一个非终结符,那么就是 2 型。然后判断是否每条规则只改变一个非终结符,不是则为 0 型。

1.2 三型文法直观感受

因为只有两种推导形式,所以其语法树会长得很“偏瘫”,比如说对于左线性文法,就是长成这个样子的:

在这里插入图片描述

正是因为这种“偏瘫”的性质,才使得有限状态机成为接受三型文法的工具,可以一个个字符的读入,而且没有“回溯”过程。其推导的过程就好像我们平时书写一样,成一个线性的趋势。

1.3 上下文无关特性

​ 我们说大部分高级程序设计语言都是二型文法,具有上下文无关的特性,但是其实对于复杂特性的语言,比如说 CPP(对,又是它),他是具有上下文敏感的特性的,比如说

a<b<c>> d

​ 可以被理解成一个

vector<vector<int>> matrix;

​ 也可以被理解成位运算

int x = 4 < 3 < 2 >> 1

​ 所以在“不引入符号表”(可以看做符号表就是记录上下文信息的工具)的前提下,是没有办法进行语法分析的,所以 CPP 会进行一种“试错式”的语法分析,就好像我们的错误处理一样。

​ 递归下降法更适合分析二型文法(也不是完全都可以分析出来),而不适合分析一型文法,所以在实验课中,一旦引入错误处理(也就是相当于引入了一些一型文法)就会导致语法分析没有办法是朴素的递归下降,而是必须进行尝试性回溯,这是递归下降的局限性。

​ 有一个观点很有意思,就是词法分析,语法分析,语义分析其实完成的是同一个任务的不同部分,所以我们在设计的时候,可以考虑将总任务分给这三个部分,当一个部分被设计的简单的时候,另一个部分就会被设计的复杂,比如说 python 如果按照普通理解,其实是一型文法,因为缩进的原因,语法变得上下文敏感了,但是实际上是二型文法,这是因为缩进在词法分析的时候被处理成了“缩进增”和“缩进减”,这与“左大括号”和“右大括号”是等价的。另一个例子,肯定程序本身是上下文相关的(又不是计算器),那么相当于符号表这类语义分析中上下文敏感的实现,代替了复杂的语法分析,使得语法分析可以很简单。

​ 另外文法不仅和表达能力相关,它同时也与计算能力相关,所以只有 0 型文法具有图灵机的全部能力,严格讲高级语言并不能够发挥出其全部实力。据说 lisp 是基于 0 型文法的。

1.4 文法的等价性

​ 文法的等价性指的是如果两个文法描述的语言是相等的,那么就称这个两个文法是等价的,也就是说,这并不涉及文法的本身性质,一个三型文法可以与一个一型文法等价,并无大碍。


二、正则转换

2.1 等价转换框架

在这里插入图片描述

如图所示,图上的这五种东西是等价的,并且是可以相互转化的(虽然有一些是没有意义的),下面将分别介绍。

2.2 正则文法与 DFA 转换

正则文法又称为 3 型文法。正则文法有两种,左线性和右线性,分别长这样

  • 左线性:A ::= Bt
  • 右线性:A ::= tB

之前觉得正则文法就已经可以描述很多东西了(我以为 SysY 是可以用正则文法表示的),但是其实正则文法连基本的表达式都没有办法表达。所以正则一般都用于词法分析,语法分析其就不适用了。

首先讨论正则文法转换为 DFA

对于左线性文法,有如下方法

  • 对于 A : : = B t A ::= Bt A::=Bt ,有 δ ( B , t ) = A \delta(B, t) = A δ(B,t)=A
  • 对于 A : : = t A ::= t A::=t,有 δ ( S , t ) = A \delta(S, t) = A δ(S,t)=A ,其中 S S S 为状态机的起始状态
  • 状态机只有一个终止状态 Z Z Z ,同时 Z Z Z 是正则文法的起始符号(识别符号)。

举个例子

在这里插入图片描述

不可否认,左线性有些不自然,因为明明是 A → B A \rightarrow B AB 这种规则,但是最后的边却是 B → A B \rightarrow A BA ,恰恰反过来,但是并非毫无道理,相反,当前状态代表着已经规约的非终结符,是一种自底向下的过程,进行最左规约,如下所示

在这里插入图片描述

对于右线性文法:

  • 对于 A : : = t B A ::= tB A::=tB ,有 δ ( A , t ) = B \delta(A, t) = B δ(A,t)=B
  • 对于 A : : = t A ::= t A::=t,有 δ ( A , t ) = Z \delta(A, t) = Z δ(A,t)=Z ,其中 Z Z Z 为状态机的终止状态
  • 状态机只有一个起始状态 S S S ,同时 S S S 是正则文法的起始符号(识别符号)。

这就很好理解,这是一个自顶向下的过程,当前状态是要解析的非终结符。这么看,正则文法真的有很多很好的性质,他们的 F I R S T FIRST FIRST F O L L O W FOLLOW FOLLOW 都是直白的,这就导致其对于语法规则的选择很简单。

对于 DFA 转换成正则文法,一般转换为左线性文法,并不可以直接看成逆过程,有如下规则

  • 对转换函数 δ ( A , t ) = B \delta(A, t) = B δ(A,t)=B ,可写成一个产生式: A : : = t B A ::= tB A::=tB
  • 对于终止状态,可写成一个产生式: Z : : = ε Z::=\varepsilon Z::=ε
  • DFA 的初态对应于文法的开始符号(识别符号)。

如下示例:

在这里插入图片描述

2.3 正则文法与正则表达式

比较简单,需要注意的是,当正则文法向正则表达式转换的时候,应用的不只是转换法则,还有正则表达式的运算。

由正则文法转正则表达式:

在这里插入图片描述

由正则表达式转正则文法:

在这里插入图片描述

2.4 正则表达式与 NFA

​ 第一条规则描述的是很“显然”的事情,他说的是,对于一个正则表达式,它天然拥有“开始”和“终止”两个状态,他们中间会有一条边或者一堆状态和一堆边。

在这里插入图片描述

​ 在这里其实可以反思一个事情,就是在 DFA 或者在 NFA 中,绝对不会有从一个状态中出现两条相同的边,也就是

在这里插入图片描述

这是显然的,因为这样在 A A A 状态输入 x x x ,就并不知道是应当转移到 B B B 还是应当转移到 C C C 。但是对于 ϵ \epsilon ϵ 却没有这个约束,如下所示

在这里插入图片描述

下面的规则都是这种现象的体现,他们会给 NFA 引入大量的 ϵ \epsilon ϵ 。这显然是不利于 NFA 到 DFA 的化简的(后面的例题也有说明),为了规避这种现象,我们考虑将一些的 ϵ \epsilon ϵ 化简掉。在化简中,这样形式的正则表达式可以十分有用
X = a Y b X = aYb X=aYb
其中 Y Y Y 可以是任何正则表达式, a , b a, b a,b 是终止符,这个表达式意味着, X X X 一定是由 a a a 开头,由 b b b 结尾,这个是一个很好的性质,我们可以将开头的 ϵ \epsilon ϵ 和结尾的 ϵ \epsilon ϵ 可以分别被替换成 a , b a, b a,b 。这种替换只要不会导致 NFA 的定义被违反(也就是上面提到的情况),那么就是可行的。

在这里插入图片描述

在这里插入图片描述

反之:

在这里插入图片描述

2.5 NFA 转 DFA

首先是确定 I ( S ) I(S) I(S) 这是一种 I − ε I-\varepsilon Iε 闭包,说的是开始状态和可以通过 ε \varepsilon ε 到达的状态组成的集合。

然后需要通过边访问,比如说 I a ( S ) I_a(S) Ia(S) ,表示的是通过 a a a 这条边, I ( S ) I(S) I(S) 可以到达的状态的集合,而且这个集合中还有通过 ε \varepsilon ε 到达的状态,换句话说,也是闭包。

然后就这么周而复始的运动即可。

实际操作的时候是真的难,首先是这种题一般是给出正则表达式,然后求最小 DFA。那么如果不幸构造出一个极其复杂的 NFA,那就真的完蛋了,比如说下面的例子,如果按照规则构造的话,是这样的
a ( ( a ∣ b ) ∗ ∣ a b ∗ a ) ∗ b \tt{a((a|b)*|ab*a)*b } a((ab)aba)b
正常构造是 7 个状态

在这里插入图片描述

然后就会陷入无限的求解闭包的过程(甚至 DFA 的状态比 NFA 还多)中,所以应当用前面介绍的方法,先手动化简成这样

在这里插入图片描述

具体的过程是这样的,考虑题目
a ( ( a ∣ b ) ∗ ∣ a b ∗ a ) ∗ b \tt{a((a|b)*|ab*a)*b } a((ab)aba)b
可以知道,大概会翻译成这样

在这里插入图片描述

然后进行一个显然的化简

在这里插入图片描述

然后观察子正则表达式,发现 a b ∗ a \tt{ab*a} aba 发现他是以 a a a 开头结尾的,所以可以进行化简

在这里插入图片描述

其核心在于不能让一个状态有两个同为 ε \varepsilon ε 的出边或者入边,显然是可以化简的,而且这种太难搞了。

然后进行化简的时候,可以考虑先对于每个状态求一遍闭包,这样对于多个状态的合并,就不需要多次重复计算了,如下所示

状态ab
12, 3
23, 42, 3, 5
32, 3, 43, 5
42, 34
5

这里一定要小心谨慎,因为一不小心就错了。

然后就可以进行查表求解过程了,因为 DFA 的状态也需要标识,所以建议用英文字母与原来 NFA 的阿拉伯数字区分

DFANFAab
A12, 3
B2, 32, 3, 42, 3, 5
C2, 3, 42, 3, 42, 3, 4, 5
D2, 3, 52, 3, 42, 3, 5
E2, 3, 4, 52, 3, 42, 3, 4, 5

2.6 最小化 DFA

采用分割法进行计算,不过必须要承认,这个方法实际上的如果按照严谨的计算,那么运算量是极其复杂的,是没有办法很容易的得出答案,在得出答案的过程中,还需要不断的依靠直觉,而且似乎具有某些不动点算法的性质。

最小化 DFA 干的事情就是将 DFA 中“等价”的状态合并到一起,主要考虑两个条件:

  • 一致性条件:状态 s s s 和状态 t t t 必须同时为接受状态或者非接受状态。这个很显然,也很好判断。
  • 蔓延性条件:对于所有的输入符号,状态 s s s 和状态 t t t 必须转换到等价状态里。这个就是很符合直观认知的,但是很难应用,因为等价状态是一个变化的东西。

在教材上,只是用例子演示了一下,并没有完整的算法。我凑活写了一个:

  • 首先利用一致性条件,将状态划分为两个集合。
  • “认为“此时每个集合里的每个状态都是等价的。
  • 基于第二点,遍历集合中的每个状态,利用蔓延性条件检验是否真的与该状态所在集合的其他状态等价,当出现不等价情况,分割集合,回到第二点。如果所有状态均检查完毕,则算法结束。

以一道题举例:这是状态转移表

ab
163
273
315
446
573
641
742

其中接受状态是 { 5 , 6 , 7 } \{5, 6, 7\} {5,6,7}

原题在求解的时候,在这张表的右侧拓展了一列,用于分区,但是这是不严谨的,因为我们并不保证等价状态一定在表上挨在一起。所以考虑手写集合。

首先根据一致性条件划分出两个集合

{ 1 , 2 , 3 , 4 } , { 5 , 6 , 7 } \{1, 2, 3, 4\}, \{5, 6, 7\} {1,2,3,4},{5,6,7}

然后遍历 1,发现 2 和 1 等价,但是和 3 不等价,所以分割出 1,2

{ 1 , 2 } , { 3 , 4 } , { 5 , 6 , 7 } \{1, 2\}, \{3, 4\}, \{5, 6, 7\} {1,2},{3,4},{5,6,7}

此时遍历 1,2 没有问题,遍历 3,发现和 4 不等价,所以分割数 3

{ 1 , 2 } , { 3 } , { 4 } , { 5 , 6 , 7 } \{1, 2\}, \{3\}, \{4\}, \{5, 6, 7\} {1,2},{3},{4},{5,6,7}

遍历 1,2,3,4 没有问题,遍历 5,发现与 6 不等价,所以分割 5

{ 1 , 2 } , { 3 } , { 4 } , { 5 } , { 6 , 7 } \{1, 2\}, \{3\}, \{4\}, \{5\}, \{6, 7\} {1,2},{3},{4},{5},{6,7}

检查完毕,算法结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/8145.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【多线程与高并发(锁)】1、锁的概念、分类和状态

1、锁的概念 java当中的锁、是在多线程环境下为保证共享资源健康、线程安全的一种手段。 线程操作某个共享资源之前&#xff0c;先对资源加一层锁&#xff0c;保证操作期间没有其他线程访问资源&#xff0c;当操作完成后&#xff0c;再释放锁。 2、锁的分类 Java中的锁按照…

ChatGPT对我们的影响-ChatGPT能给我们带来什么

ChatGPT日常应用 ChatGPT是一种应用广泛的自然语言处理算法&#xff0c;其可以应用于多种日常场景。以下是一些ChatGPT的日常应用&#xff1a; 聊天机器人&#xff1a;ChatGPT可用于构建聊天机器人&#xff0c;通过与用户进行自然语言交互来提供个性化的服务和支持。 新闻稿和…

MyBatis零基础入门实践

MyBatis一、MyBatis简介1、MyBatis历史2、MyBatis特性3、MyBatis下载4、和其它持久化层技术对比二、搭建MyBatis1、开发环境2、创建maven工程3、创建MyBatis的核心配置文件4、创建mapper接口5、创建MyBatis的映射文件6、通过junit测试功能7、加入log4j日志功能三、核心配置文件…

代码随想录算法训练营第五十天 | 123. 买卖股票的最佳时机 III、188. 买卖股票的最佳时机 IV

123. 买卖股票的最佳时机 III 关键在于至多买卖两次&#xff0c;这意味着可以买卖一次&#xff0c;可以买卖两次&#xff0c;也可以不买卖。 动规五部曲&#xff1a; 1、确定dp数组以及下标的含义 一天一共就有五个状态&#xff0c; 没有股票第一次持有股票第一次不持有股…

SPI协议

SPI协议 物理层 信号线 SCK(Serial Clock)&#xff1a;时钟线 MOSI(Master Output&#xff0c; Slave Input )&#xff1a;主设备输出&#xff0c;从设备输入 MISO(Master Input,&#xff0c; Slave Output)&#xff1a;主设备输入&#xff0c;从设备输出 SSN&#xff08;…

API 测试 | 了解 API 接口测试 | API 接口测试指南

什么是 API&#xff1f; API 是一个缩写&#xff0c;它代表了一个 pplication P AGC 软件覆盖整个房间。API 是用于构建软件应用程序的一组例程&#xff0c;协议和工具。API 指定一个软件程序应如何与其他软件程序进行交互。 例行程序&#xff1a;执行特定任务的程序。例程也…

Redux的基本使用详解(从入门到入土)

Redux的基本使用过程详解 学习文档 中文文档: http://www.redux.org.cn/ 英文文档: https://redux.js.org/ Github: https://github.com/reactjs/redux 一&#xff0c;redux是什么 1&#xff0c;介紹&#xff1a; redux是一个专门用于做状态管理的JS库(不是react插件库)。它…

程序员讨厌的“笔试题”,还有存在的必要性吗?

面试&#xff0c;是我们拿到offer的必经之地&#xff0c;在面试中我们会遇到各种“刁难”&#xff0c;而让程序员最为排斥的&#xff0c;非“笔试题”莫属。 △ 截图来源脉脉&#xff0c;如侵删 为什么程序员越来越排斥做面试题呢&#xff1f;我们先来看看网友们的说法&#x…

【Vue2从入门到精通】深入浅出,带你彻底搞懂Vue2组件通信的9种方式

文章目录Vue组件间通信分类1.props / $emit1.1 父组件向子组件传值1.2 子组件向父组件传值2.$parent / $children3.ref / $refs3.1 ref作用于组件3.2 ref作用于Html标签3.3 $nextTick()4.EventBus &#xff08;$emit / $on&#xff09;4.1 初始化4.2 发送事件4.3 接收事件4.4 移…

博客首页效果

学习来自风宇blog的博客首页效果 全部用的基本上都是原生的html&#xff0c;css&#xff0c;js特别是flex布局的使用&#xff0c;主轴方向可以是横轴&#xff0c;也可以是纵轴&#xff0c;弹性项还可可以使用百分比sticky粘性布局&#xff0c;作为侧边栏&#xff0c;它不会超出…

分享一个国内可用的免费ChatGPT网站

背景 ChatGPT作为一种基于人工智能技术的自然语言处理工具&#xff0c;近期的热度直接沸腾&#x1f30b;。 作为一个程序员&#xff0c;我也忍不住做了一个基于ChatGPT的网站&#xff0c;免费&#xff01;免登陆&#xff01;&#xff01;国内可直接对话ChatGPT&#xff0c;也…

c++类和对象

&#x1f646;&#x1f3fc;关注作者&#xff1a;玺子写代码 ✍️gitee&#xff1a;玺子写代码 目录&#x1f449;&#x1f3fb;类的定义&#x1f449;&#x1f3fd;类的两种定义方式&#x1f449;&#x1f3fc;类的访问限定符及封装&#x1f449;&#x1f3fd;访问限定符&…

ML@sklearn@ML流程Part3@AutomaticParameterSearches

文章目录Automatic parameter searchesdemomodel_selection::Hyper-parameter optimizersGridSearchCVegRandomizedSearchCVegNoteRandomForestRegressorMSEpipeline交叉验证&#x1f388;egL1L2正则Next stepsUser Guide vs TutorialAutomatic parameter searches Automatic p…

6 计时器(一)

计时器 6.1 TIM TIM简介 TIM&#xff08;Timer&#xff09;定时器 定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元&#xff0c;在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中…

如何在现实场景中随心放置AR虚拟对象?

随着AR的发展和电子设备的普及&#xff0c;人们在生活中使用AR技术的门槛降低&#xff0c;比如对于不方便测量的物体使用AR测量&#xff0c;方便又准确&#xff1b;遇到陌生的路段使用AR导航&#xff0c;清楚又便捷&#xff1b;网购时拿不准的物品使用AR购物&#xff0c;体验更…

Spring-aop面向切面

1、理解必要的专业术语 先看看上面图&#xff0c;这是我的个人理解。(画的丑&#xff0c;主打真实) 1&#xff09;Advice&#xff0c;通知/增强&#xff1a;类方法中提出来的共性功能(大白话就是提出来的重复代码) 2&#xff09;Pointcut&#xff0c;切入点/切点&#…

centos7修改ip

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

uniapp国际化配置

1、创建资源文件 创建一个locale文件夹&#xff0c;新增index.js,en.json,zh-hans.json 2.配置locale文件夹中的index.js文件 import Vue from vue import VueI18n from vue-i18n// v8.x import en from ./en.json import zhHans from ./zh-Hans.json import zhHant from .…

Redis大key问题

Redis大key问题 什么是big key&#xff1f; bigKey的危害&#xff1a; 大key不仅仅是占用内存而已&#xff0c;如果是仅仅内存的问题 那么扩大内存就好了。禁止大key是主要是因为你操作redis&#xff0c;比如说读/写等操作redis的时候 会有io操作&#xff0c;大key会导致io操作…

【K8S】k8s中secret使用方法

secret可以加密用户名和密码文件&#xff0c;将其打包成一个secret并在API服务器上创建对象 echo -n admin > ./username.txt echo -n xvagaxx > ./password.txt将username.txt和password.txt打包成secret kubectl create secret generic db-user-pass \--from-file./u…