编译原理3——词法分析

3.1词法分析器的作用

词法分析是编译的第一阶段。词法分析器的主要任务是读入源程序的输入字符、将它们组成词素,生成并输出一个词法单元序列,每个词法单元对应于一个词素。

但在这个过程中,词法分析器还要和语法分析器进行交互。交互:语法分析器通过一个指令调用词法分析器,让词法分析器从它的输入中不断读取字符,直到识别出下一个词素为止,词法分析器根据这个词素生成下一个词法单元并返回给语法分析器。

词法分析器还会完成一些额外的任务:

  • 过滤掉源程序中的注释和空白(空格、换行符、制表符以及在输入中用于分隔词法单元的其他字符)
  • 将编译器生成的错误信息与源程序的位置联系起来。

总结:词法分析器可以分为两个联级阶段

  • 扫描阶段主要负责完成一些不需要生成词法单元的简单处理,比如删除注释和将多个连续的空白字符压缩成一个字符。
  • 词法分析阶段是较为复杂的部分,它处理扫描阶段的输出并生成词法单元。

3.1.1词法分析及语法分析

把编译过程的分析部分划分成词法分析语法分析两个阶段的原因:

  • 简化编译器的设计。比如:如果在语法分析中还要处理关于一些注释或者空白字符的问题,那么将会更加复杂。
  • 提高编译器的效率。二者独立实现一些功能提高对某个环节处理的专业性。
  • 增强编译器的可移植性。输入设备相关的特殊性可以被限制在词法分析过程中。

3.1.2词法单元、模式和词素

  • 词法单元:词法单元名+可选择的属性。
  • 模式:是一种解释,描述了一个词法单元的词素可能具有的形式。
  • 词素:就是可以被词法分析器识别为该词法单元的一个实例。

大部分的词法单元:

  • 每个关键字有一个词法单元。一个关键字的模式就是该关键字本身。
  • 表示运算符的词法单元。它可以表示单个运算符,也可以表示一类运算符。
  • 一个表示所有标识符的词法单元。
  • 一个或多个表示常量的词法单元。
  • 每个标点符号有一个词法单元,比如左括号、逗号和分号。

3.1.3词法单元的属性

词法分析器不仅仅向语法分析器返回一个词法单元名字,还会返回一个描述该词法单元的词素的属性值。这个属性则会影响语法分析之后对这个词法单元的翻译。

3.2词法单元的规约

正则表达式是一种用来描述词素模式的重要表示方法。

3.2.1串和语言

串s的长度,通常记作|s|,是指s中符号出现的次数。

语言是某个给定字母表上一个任意的可数的串集合。

串的各部分术语:

  • 串s的前缀(prefix):从s的尾部删除0个或多个符号后得到的串。
  • 串s的后缀(suffix):从s的开始处删除0个或多个符号后得到的串。
  • 串s的子串(substring):删除s的某个前缀和某个后缀之后得到的串。
  • 串s的真前缀、真后缀、真子串:既不等于空,也不等于s本身的前缀、后缀、子串。
  • 串s的子序列(subsequence):从s中删除0个或多个符号后得到的串,这些被删除的符号可能不相邻。

如果x和y是串,那么x和y的连接(concatenation)是把y附加到x后面而形成的串。例如:x = handsome 且 y = you 。那么xy = handsomeyou 。

3.2.2语言上的运算

  • L和M的并:就是简单的合并成一个集合
  • L和M的连接:以各种可能的方式,从第一个语言中任取一个串,再从第二个语言中任取一个串,然后将它们连接后得到的串集。
  • L的Kleene闭包(L*):就是将L连接0次或多次后得到的串集。
  • L的正闭包:只不过是去掉空串。

3.2.3正则表达式

正则表达式可以由较小的正则表达式按照如下规则递归地构建。

归纳基础:e是一个正则表达式,L(e)={e},即该语言只包含空串。

归纳步骤:由小的正则表达式构造较大的正则表达式的步骤有四个部分。假定r和s都是正则表达式,分别表示语言L(r)和L(s)。

  • (r)|(s)是一个正则表达式,表示语言L(r) U L(s)。
  • (r)(s)是一个正则表达式,表示语言L(r)L(s)。
  • (r)*是一个正则表达式,表示语言(L(r))*。
  • (r)是一个正则表达式,表示语言L(r)。最后这个规则是说在表达式的两边加上括号并不影响表达式所表示的语言。

当然,有时候是可以去掉括号的,*具有最高优先级且是左结合的,连接具有次高优先级且是左结合的,|的优先级最低且是左结合的。

正则表达式的代数定律:只记录特殊的一条,r** = r* 。 具有幂等性。

取自某学习视频:

限定符:

  • a*:a出现次或多次
  • a+:a出现1次或多次
  • a?:a出现0次或1次
  • a{6}:a出现6次
  • a{2,6}:a出现2-6次
  • a{2,}:a出现两次以上

运算符:

  • (a|b):匹配a或者b
  • (ab)|(cd):匹配ab或者cd

字符类:

  • [abc]:匹配a或者b或者c
  • [a-c]:同上
  • [a-fA-F0-9]:匹配小写+大写英文字符以及数字
  • [^0-9]:匹配非数字字符

元字符:

  • \d:匹配数字字符
  • \D:匹配非数字字符
  • \w:匹配单词字符(字母数字下划线)
  • \W:匹配非单词字符
  • \s:匹配空白符
  • \S:匹配非空白字符
  • . :匹配任意字符(换行符除外)
  • \b:标注字符的边界
  • ^:匹配行首
  • $:匹配行尾

3.3词法单元的识别

3.3.1状态转换图

接下来将通过一张图来解释

有一组被称为“状态”的结点或圆圈。状态图中的边从图的一个状态指向另一个状态(定向搜索,可能性也就只有1)。图中的双层的圈就是“接受状态”或“最终状态”。由一条没有出发结点的箭头指向的是“开始状态”或“初始状态”。如果需要将指针回退到上一个位置,则需要在接受状态的附近加上一个*,若是多个位置,就加多个*。

3.3.2保留字和标识符的识别

主要目的就是防止一些关键字被识别成标识符。

解决方法通常有两种:

  • 将所有可能用到的关键字一一列举在符号表中
  • 为每个关键字设定一个状态转换图,但是在最后的接受状态要添加一个“非字母或数字”的测试,来确保这个状态转换图确实不会成为一个标识符。

3.3.3基于状态转换图的词法分析器的体系结构

用一个函数来模拟状态转换图的实现。不同的词法单元分析有不同的状态转换图,但是为了实现对某个未知的词法单元进行词法分析,你首先要做的就是选定某个状态转换图。

  • 依次调用所有的状态转换图,直到分析出。
  • 同时调用所有的状态转换图,选择最长匹配。
  • 将所有状态转换图归成一个状态转换图。

3.4词法分析器生成工具Lex

Lex最近也叫Flex,这个F就是Fast的意思。它的核心功能就是将输入的模式转换成一个状态转换图,并生成相应的实现代码。

3.4.1 Lex的使用

使用者本身首先要使用Lex语言写一个.l文件,然后运用配置好的Lex编译器,在终端输入相关指令,将.l文件转换成lex.yy.c文件。

3.4.2 Lex程序的结构

%{
....     //声明部分%}//给一些正则表达式typedef一下%%
//转换规则
//正则表达式      {实现的操作}[a-zA-Z]+				{ words++; chars += strlen(yytext); }
%%//辅助函数部分main()
{yylex();return 0;
}
  • 声明部分、辅助函数部分都被直接拷贝到.c文件中
  • yytext 是一个指向词素开头的指针
  • yyleng 存放刚找到的词素的长度

3.4.3 Lex中的冲突解决

当输入的多个前缀与一个或多个模式匹配时,Lex用如下规则选择正确的词素:

  • 总是选择最长的浅醉。
  • 如果最长的可能前缀与多个模式匹配,总是选择在Lex程序中先被列出的模式。(将关键字的定义靠前列出)

3.4.4 向前看运算符

在一些语言中,存在 IF 是一个数组的名字,而不是关键字,这样的使用就给词法分析带来了很大的困扰。

所以要采用一种新的方法,用斜号来指明该模式中和词素实际匹配的部分的结尾,斜号 / 之后的内容表示一个附加的模式,只有附加模式也匹配成功了,最后才能进行返回自己要找的词法单元并进行输出(不包含附加内容)。

举例:这里的IF就变成了:IF / \( . * \) {letter}

3.5 有穷自动机

自动机在本质上是与状态转换图类似的图,但存在几点不同:

  • 有穷自动机是识别器,它们只能对每个可能得输入串简单地回答“是”或“否”。
  • 不确定的有穷自动机(NFA):堆其边上的标号没有任何限制,一个标号可以标记离开同一状态的多条边,并且空串ε也可以作为标记。
  • 确定的有穷自动机(DFA):就只有一条离开该状态的边,且这个边上的标记只能用一次。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis: 特点,优势,与其他产品的区别以及高并发原理

入门Redis概述 1 )选择Redis是因为其高性能 因为 Redis 它数据存储的机制是存在内存中的,减少了传统关系数据库的磁盘IO它是单线程的保证了原子性,它还提供了事务,锁等相关的机制 2 )Redis 环境安装配置 linux 或 d…

学习制作第一个LC带通滤波器的初步认识与总结

作为业余爱好,之前没有接触过射频方面的知识。 收音机,fm调频话筒等等想必是很多人都想制作的一个入门制作。但是这个里面的振荡,谐振,滤波,虽然在电子报上面频频看见,对于普通爱好者,如果没有…

在视频上绘制区域:使用Vue和JavaScript实现交互式画布

在数字时代,交互式媒体内容的创建和消费变得越来越普遍。特别是视频内容,它不仅提供了视觉信息,还允许用户与之互动,从而增强了用户体验。本文将介绍如何使用Vue.js框架和JavaScript创建一个交互式组件,该组件允许用户…

【Docker】Docker快速入门

Docker学习笔记 一、Docker概述 为什么会出现Docker? 安卓开发流程:apk(java开发的)发布到应用商店,用户安装apk即可使用。 后端开发流程: jar(java开发的)带上环境发布到Docker仓库,用户从Docker仓库拉取镜像并部署。 总结…

Android 如何实现搜索功能:本地搜索?数据模型如何设计?数据如何展示和保存?

目录 效果图为什么需要搜索功能如何设计搜索本地的功能,如何维护呢?总结 一、效果图 二、为什么需要搜索功能 找一个选项,需要花非常多的时间,并且每次都需要指导客户在哪里,现在只要让他们搜索一下就可以。这也是模…

低代码平台后端搭建-阶段完结

前言 最近又要开始为跳槽做准备了,发现还是写博客学的效率高点,在总结其他技术栈之前准备先把这个专题小完结一波。在这一篇中我又试着添加了一些实际项目中可能会用到的功能点,用来验证这个平台的扩展性,以及总结一些学过的知识。…

数据库数据恢复—Oracle报错“需要更多的恢复来保持一致性”的数据恢复案例

Oracle数据库故障&检测: 打开oracle数据库报错“system01.dbf需要更多的恢复来保持一致性,数据库无法打开”。 数据库没有备份,无法通过备份去恢复数据库。用户方联系北亚企安数据恢复中心并提供Oracle_Home目录中的所有文件,…

大小端字节序 和 内存高低地址顺序

目录 1. 大小端字节序 1.1 什么是大小端字节序? 1.2 为什么有大小端字节序? 1.3 习题:用程序结果判断大端小端 2. 各种易混淆的高低地址顺序 2.1 监视窗口的地址表示【计算机标准展示方式】 2.2 横向地址表示 2.3 一个字节 与 多个字节 的地址…

C语言 | Leetcode C语言题解之第416题分割等和子集

题目&#xff1a; 题解&#xff1a; bool canPartition(int* nums, int numsSize) {if (numsSize < 2) {return false;}int sum 0, maxNum 0;for (int i 0; i < numsSize; i) {sum nums[i];maxNum fmax(maxNum, nums[i]);}if (sum & 1) {return false;}int tar…

《程序猿之设计模式实战 · 适配器模式》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

springboot文件上传+拦截器

springboot文件上传拦截器 文章目录 springboot文件上传拦截器1.静态资源访问静态目录&#xff1a; 2.文件上传文件上传配置文件书写文件上传代码 3.拦截器1.初始化拦截器2.初始化配置文件 1.静态资源访问 静态目录&#xff1a; 这里的static就是spring boot默认存放静态资源的…

ZYNQ FPGA自学笔记~操作PLL

一 时钟缓冲器、管理和路由 垂直时钟中心&#xff08;clock backbone&#xff09;将设备分为相邻的左侧和右侧区域&#xff0c;水平中心线将设备分为顶部和底部两侧。clock backbone中的资源镜像到水平相邻区域的两侧&#xff0c;从而将某些时钟资源扩展到水平相邻区域。BUFG不…

考研数据结构——C语言实现小顶堆

数组初始化&#xff1a; 首先&#xff0c;我们有一个整数数组arr&#xff0c;里面包含了一系列需要排序的数字。数组的长度n是通过对数组arr的总字节大小除以单个元素的字节大小得到的。 小顶堆调整函数&#xff1a; adjustHeapMin函数的作用是将数组中的元素从某个节点向下调整…

[001-02-001].第2节:java开发环境搭建

4.1.书籍推荐&#xff1a; 4.2.人机交互方式 1.图形化界面(Graphical User Interface GUI)这种方式简单直观&#xff0c;使用者易于接受&#xff0c;容易上手操作2.命令行方式(Command Line Interface CLI)&#xff1a;需要有一个控制台&#xff0c;输入特定的指令&#xff0c…

[数据结构]无头单向非循环链表的实现与应用

文章目录 一、引言二、线性表的基本概念1、线性表是什么2、链表与顺序表的区别3、无头单向非循环链表 三、无头单向非循环链表的实现1、结构体定义2、初始化3、销毁4、显示5、增删查改 四、分析无头单向非循环链表1、存储方式2、优点3、缺点 五、总结1、练习题2、源代码 一、引…

Frontiers出版社系列SCISSCI合集

【SciencePub学术】本期&#xff0c;小编根据WOS数据库&#xff0c;整理了一下Frontiers出版社系列的SCI&SSCI合集&#xff0c;以供各位学者投稿参考&#xff01; 来源&#xff1a;WOS数据库 Frontiers系列期刊中&#xff0c;Frontiers in Immunology以其5.7分的影响因子位…

第十四届蓝桥杯嵌入式国赛

一. 前言 本篇博客主要讲述十四届蓝桥杯嵌入式的国赛题目&#xff0c;包括STM32CubeMx的相关配置以及相关功能实现代码以及我在做题过程中所遇到的一些问题和总结收获。如果有兴趣的伙伴还可以去做做其它届的真题&#xff0c;可去 蓝桥云课 上搜索历届真题即可。 二. 题目概述 …

每日一练:二叉树的层序遍历

102. 二叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff09; 一、题目要求 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,n…

合宙LuatOS应用,与时间相关那些事

合宙嵌入式操作系统LuatOS——在蜂窝物联网模组上推出开源二次开发框架&#xff0c;功能齐全性能稳定&#xff0c;可大幅度降低用户的研发成本和研发周期。 在LuatOS中&#xff0c;获取时间函数用得最多的就是os.time()函数了。接下来&#xff0c;我会讲一些与这个函数以及其他…

c++924

2 #include <iostream> #include <cstring>using namespace std;class MyString { private:char *str; // 记录C风格的字符串int size; // 记录字符串的实际长度public:// 定义无参构造MyString() : size(0) {str new char[1];str[0] \0;cou…