输入及词法分析详解

2024-04-30 21:38
文章标签 分析 详解 输入 词法

本文主要是介绍输入及词法分析详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

欢迎大家来到coding迪斯尼,我的愿景是:让天下没有难学的知识 

这句话是跟马云学的,就算没马云的命,那就用马云的话,也是不错的。

阅读博客的朋友可以到我的网易云课堂中,通过视频的方式查看代码的调试和执行过程:

 

http://study.163.com/course/courseMain.htm?courseId=1002830012

 

大家好,继上几节我们通过实现一个简易编译器,通过实践获得一定的感性认识后,这里,我们开始对编译器的各个技术点深入分析,逐个击破。从这节开始,以及后续的相应课程,我们注重输入系统的设计和词法解析所用到的各种算法和理论基础。

 

本节的重点,是对词法解析系统和输入系统做一个大概的介绍

 

词法解析是一种相当实用的技术,除了在编译器中,在很多应用场合同样是非常实用的。如果把词法解析看成是一种模式识别引擎,那它可以应用到例如文本编辑器,数据模式识别等方面。稍加扩展,还可以应用到网络协议解析等方面去。

 

2.1 词法解析器在编译器中的作用

 

词法解析器在编译器中的作用,是将输入流解析为一种能够被语法解析器使用和管理的格式。他将输入文本分割,打标签,也就是用一些数值来指代一系列相应的字符串,例如关键字while 可以和一个标签关联起来,一般情况下,一个标签可以和很多字符串关联,例如以前看到的NUM_OR_ID,它指代的是代码中的变量定义和数字常量,显然,他们是无穷的, 词法解析器还可以将源码中的注释剔除,忽略空格,换行符等,将这些琐事的工作从语法解析中剥离出来,简化语法解析器的设计。

 

词法解析是编译过程的一个独立阶段,它通过一组简单的接口与语法解析器相互交互,下面这幅图可以将编译器的骨架给展示出来(描述一下结构图):


从这个结构,我们可以得到几点启示:词法分析器由于与其他各个部分通过接口进行通信,那么词法解析器内部的修改不会影响到其他部分。例如,如果要编译的程序语言从 c++ 换成c#, 那么只要把词法解析的内部实现换掉,其他部分不变,c++编译器就会转换成c#编译器。另一个好处是,一个独立的词法解析器可以优化它的字符读取速度,例如一次读取足够多的内容,而这种局部优化可以带来整体效果的改进,而优化的过程可以局限在词法解析内部。这其实是典型的将面向对象的设计方法运用到架构设计上。

 

有时候,词法解析和语法解析之间需要更复杂的交互流程,例如C语言中的typedef ,该语句可以构造一个新的关键字,当语法解析器处理下面这条语句:

typedef int  Integer;

词法解析器必须将字符串“Integer” 当做一个类型标签而不能是变量标签(NUM_OR_ID).  这种交互流程就需要一种复杂的数据结构,就是图中的符号表。 语法解析器可以将 Integer插入符号表,词法解析器遇到字符串Integer 时,到符号表中检索一下,检索到后就给Integer打上标签TYPE, 而不是NUM_OR_ID.

 

2.2 词法解析中的错误恢复

在词法解析过程中,发现错误是很正常的,例如在C语言中,@num 这样的变量定义是非法的,词法解析器遇到这种错误的时候,有若干种处理方法,最简单的是,词法解析器在解析过程中,忽略符号@, 同时输出错误提示。还有更复杂的处理方法,如果读取到一个关键字是 swatch ,那么词法解析器根据匹配算法,将swatch识别为switch等。

 

2.3 输入系统

在上图中,词法解析器的下方有一个模块叫输入系统。它的作用是将源文件从磁盘或内存中读入,根据模块化设计原理,如果输入系统是一个独立模块,通过固定接口与词法解析器交互的话,那么它的修改和维护将会非常灵活。

 

输入系统的效率,决定着整个编译系统的效率。我们用C语言的时候,经常会用到他提供的输入函数例如scanf 等。C语言的输入系统设计得不是很合理,当C语言的库函数将数据读入程序的过程中,有三次拷贝, 一是从磁盘上将数据拷贝到操作系统中,二是将数据从操作系统拷贝到一个 FILE 结构中,三是将数据从FILE 结构拷贝到程序的内存中。这些拷贝都需要耗费时间和空间。另外,词法解析器在解析时需要预先读入一些字符(look ahead,在前面的简易编译器中使用过这种技巧), 以便对输入的字符串打上合适的标签(想象前面的typeof 语句,一旦读到typeof 那就需要将typeof后面的字符读进来才好解析), 预先读入的字符,使用完后,可能需要重新放回到缓冲区中,这一取一放,如果不加以良好的设计,那很可能会产生I/O性能上的影响

 

2.3 一个输入系统的实现

鉴于以上特点,现有的输入系统无法满足编译系统输入的各种要求,因此,我们需要自己开发一个专用的输入系统。我们的输入系统应当具备以下特点:

1.    输入过程要尽可能的快,尽力减少不必要的拷贝。

2.    支持多个字符的预读取和回放。

3.    当解析当前分割的字符串时,上一个解析过的字符串需要容易的获取。

4.    磁盘读写要足够便捷。

 

下图是我们要实现的输入系统的内存模型:

 

startBuf 指向缓冲区的起点

END 指向缓冲区的物理结束位置

endBuf 指向缓冲区的逻辑结束位置,也就是数据最多存储到endBuf 处

例如我们申请的内容是300k, 那么END就在300k处,而实际我们用到的是256k,那么endBuf就在256k处。

 

MAXLEX 是分割后字符串的最多长度,一般设为128,大家在写代码时没有写过长度达128个字符的变量名吧。我们一次从磁盘读入缓冲区的数据量是MAXLEX 的倍数,也就是说,endBuf – startBuf 一定是MAXLEX的整数倍。

 

Next指向下一个要读取的字符,词法解析器是一个字符一个字符从缓冲区读取数据的。

 

danger  后面解释

 

下图显示的是词法解析器解析若干个字符串后,缓冲区的情况:

 

 

pMark指向上一个被解析的字符串的起始位置

 

sMark 指向当前被解析的字符串的起始位置

 

eMark 指向当前解析的字符串的结束位置

 

sMark – pMark 是上一个字符串的长度

 

eMark – sMark 是当前解析字符串的长度

 

词法解析器在解析时需要读取字符串后的若干字符,所有Next 往后挪动了一段距离。这也就是前面提到的look ahead.

 

我们前面提到过,预先读取和读取后放回缓冲区需要加以处理,在我们给的的内存模型中,当需要把预先读到的字符退回缓存区时,只要把Next 设置成eMark就可以了。

 

look ahead 也就是预读取的字符个数是有限制的,限制的个数用常量MAXLOOK表示,也就是

 

Next – eMark <= MAXLOOK

 

当Next 的值接近danger 时, 表明缓冲区内的有效数据快被读取完了,当Next指针越过danger 时,就会触发一次flush操作

 

flush 操作的效果是,将pMark 到endBuf间的数据整体平移到startBuf处,

平移的距离是:

pMark – startBuf

 

从磁盘中读入数据,把平移后空出来的可用空间给填满,如下图所示(字有点难看):

 

从磁盘读入数据:这一段空间的长度就是pMark – startBuf

 

 

下一节我们将以代码的形式讲解输入系统的实现

 

这篇关于输入及词法分析详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/949982

相关文章

Linux线程同步/互斥过程详解

《Linux线程同步/互斥过程详解》文章讲解多线程并发访问导致竞态条件,需通过互斥锁、原子操作和条件变量实现线程安全与同步,分析死锁条件及避免方法,并介绍RAII封装技术提升资源管理效率... 目录01. 资源共享问题1.1 多线程并发访问1.2 临界区与临界资源1.3 锁的引入02. 多线程案例2.1 为

python panda库从基础到高级操作分析

《pythonpanda库从基础到高级操作分析》本文介绍了Pandas库的核心功能,包括处理结构化数据的Series和DataFrame数据结构,数据读取、清洗、分组聚合、合并、时间序列分析及大数据... 目录1. Pandas 概述2. 基本操作:数据读取与查看3. 索引操作:精准定位数据4. Group

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

Python标准库之数据压缩和存档的应用详解

《Python标准库之数据压缩和存档的应用详解》在数据处理与存储领域,压缩和存档是提升效率的关键技术,Python标准库提供了一套完整的工具链,下面小编就来和大家简单介绍一下吧... 目录一、核心模块架构与设计哲学二、关键模块深度解析1.tarfile:专业级归档工具2.zipfile:跨平台归档首选3.

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

idea的终端(Terminal)cmd的命令换成linux的命令详解

《idea的终端(Terminal)cmd的命令换成linux的命令详解》本文介绍IDEA配置Git的步骤:安装Git、修改终端设置并重启IDEA,强调顺序,作为个人经验分享,希望提供参考并支持脚本之... 目录一编程、设置前二、前置条件三、android设置四、设置后总结一、php设置前二、前置条件

python中列表应用和扩展性实用详解

《python中列表应用和扩展性实用详解》文章介绍了Python列表的核心特性:有序数据集合,用[]定义,元素类型可不同,支持迭代、循环、切片,可执行增删改查、排序、推导式及嵌套操作,是常用的数据处理... 目录1、列表定义2、格式3、列表是可迭代对象4、列表的常见操作总结1、列表定义是处理一组有序项目的

python使用try函数详解

《python使用try函数详解》Pythontry语句用于异常处理,支持捕获特定/多种异常、else/final子句确保资源释放,结合with语句自动清理,可自定义异常及嵌套结构,灵活应对错误场景... 目录try 函数的基本语法捕获特定异常捕获多个异常使用 else 子句使用 finally 子句捕获所

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3