前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >词法分析器(Lexer)的实现

词法分析器(Lexer)的实现

作者头像
h1J4cker
发布于 2022-12-01 07:53:05
发布于 2022-12-01 07:53:05
1.9K00
代码可运行
举报
文章被收录于专栏:二进制安全二进制安全
运行总次数:0
代码可运行
  • 写在前面

写下Compiler系列的主要目的,是为了记录一下本人在学习编译原理以及做出一个简单的Compiler的历程,为后续向二进制安全的更深领域的学习打下基础。

  • Lexer是什么

Lexer是Lexical analyzer的缩写,中文意思为词法分析器,是进行词法分析的程序或者函数,这也是编译器所做的第一项工作。注意:如果没有编译原理的相关知识,请先学了编译原理之后再来看本文章。

  • 词法分析的任务

词法分析的任务就是让编译器搞懂我们究竟写了什么,编译器会先将我们的程序切片成一个一个的单词,将其作为一个token,每个token都会带有一个编号。

  • Lexer的实现

从这里开始,将会开始进行第一步,也就是实现一个简单的词法分析器,文章中只会讲述思想的思路以及部分代码,完整的代码请看我的github:h1J4cker

我们先思考一下,在我们的代码中,哪些东西虽然表达的方式不同,但其实可以被划分为一类呢?

首先是我们定义变量的时候,用到的int,char等,我们可以认为他们都是标识类型的关键字,所以即便int与char在单词的组成以及含义方面不同,但我们仍然可以把他抽象为同一个类型:关键字

注意:我们可以把宏定义的关键词define,以及外部链接extern单独分出来,因为对于这两个关键词我们需要单独处理。

其次,我们可以想到,在一个程序中,被大量使用的不仅是int,char等关键字,还有由他们所定义的数据,为了简单起见,我们把所有的数据都认为是double类型,那么再次,我们又可以抽象出另一个类型:数值

最后我们需要把文件的结束符,也就是EOF单独作为一个类型。

那么从上面的分析中我们可以定义一个枚举类型Tokenizer:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
enum Tokenizer
{
    tok_eof = -1,
    tok_def = -2,
    tok_extern = -3,
    tok_identifier = -4,
    tok_number = -5
};

此外我们还需要定义两个变量来存储标识符的值,以及数据的数值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static std::string IdentifierStr; //标识符一般是一串字符串,所以将存储它的变量定义为string类
static double NumVal; //数据统一认为是double类型

当我们抽象出一个个token之后,我们需要一段程序来识别这些token,而我们需要做的第一步,就是要去除为了程序的工整性而加入的一个个空格。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static int __Get_Tok()
{
    static int LastChar = " ";

    while (isspace(LastChar))
    {
        LastChar = getchar();
    }

这段程序的作用是判断LastChar是否是空格,如果是就不处理,并接收下一个字符。

然后我们需要识别对应的字符串是否属于我们前面定义中的某一类,如果属于,则返回相应的值,如果不属于,那么他可能是一些运算符如:+-。那么我们就需要返回他的ASCII码值。

首先我们来识别关键字:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (isalpha(LastChar))
  {
      IdentifierStr = LastChar;
      while (isalnum(LastChar = getchar()))
      {
          IdentifierStr += LastChar;
      }

      if (IdentifierStr == "def")
      {
          return tok_def;
      }
      else if (IdentifierStr == "extern")
      {
          return tok_extern;
      }
      else
      {
          return tok_identifier;
      }
  }

这段程序首先判断了是否LastChar是字符,如果是那么就进入下面的阶段,接收下一个字符,判断它是否是数字,如果不是,那么就将LastChar赋值给IdentifierStr进行存储,同时判断是否是三种关键字中的某一个,如果是就返回相应的值。

接下来我们来判断数值类型,只要遇到代表数值的字符串,我们就将其转换为数值,并存入Numval中,并返回相应的值:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (isdigit(LastChar) || LastChar == ".")
{

    std::string NumStr;

    do
    {
        NumStr += LastChar;
        LastChar = getchar();
    } while (isdigit(LastChar) || LastChar == ".");

    NumVal = strtod(NumStr.c_str(), 0);
    return tok_number;
}

当我们遇到注释时,我们只需要跳过当前行即可,也就是说我们遇到注释符时,我们就一直读到\n为止:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (LastChar == "#")
{
    do
    {
        LastChar = getchar();
    } while (LastChar != EOF && LastChar != '\n' && LastChar != '\r');

    if (LastChar != EOF)
    {
        return __Get_Tok();
    }
}

当上面这些步骤做完以后,如果能存在未处理的字符串,那么只可能是两种情况:

1.读到了运算符

2.读到了EOF

那么,此时的处理过程也相当简单,我们只需要判断一下到底是哪种情况,并返回相应的值即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (LastChar == EOF)
{
    return tok_eof;
}

int ThisChar = LastChar;
LastChar = getchar();
return ThisChar;
  • 结尾

到这里,一个简单的词法分析器就基本上完成了,我们已经可以识别数据,关键词,标识符等等识别出来为下一步语法分析做准备了。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
不依赖yacc如何实现表达式按优先级解析
无意发现一个非常有意思的简单语法解析器,不依赖lex/yacc,本文对其中比较难理解的表达式解析(带优先级)部分做一些分析和记录。
mingjie
2023/10/13
2670
如何编写一个 Python 词法分析器
Python 词法分析器是一种可以将 Python 代码分解成一组记号的程序。这些记号是 Python 语法的基本组成单位,包括标识符、关键字、运算符、分隔符等。词法分析器在 Python 解释器中扮演着重要的角色,它负责将源代码转换为计算机可以理解的形式。
用户11021319
2024/03/22
1990
如何编写一个 Python 词法分析器
llvm入门教程-Kaleidoscope前端-2-解析器和AST
llvm是当前编译器领域非常火热的项目,其设计优雅,官方文档也很全面,可惜目前官方中文翻译。笔者在学习过程中也尝试进行一些翻译记录,希望能对自己或者他人的学习有所帮助。
hunterzju
2021/12/09
1.8K0
Java编写的C语言词法分析器
    这是java编写的C语言词法分析器,我也是参考很多代码,然后将核心代码整理起来,准备放在QQ空间和博客上,目的是互相学习借鉴,希望可以得到高手改进。这个词法分析器实现的功能有打开文件、保存文件、打开帮助文档、文本域内容的剪切和复制和黏贴、进行词法分析 程序的项目结构如图,Word类和Unidentifiable类是两个JavaBean类,存放的参数有两个row(整型)、word(String),row用于获取行数,word用于获取标识符,LexerFrame是词法分析器的界面类,Analyze封装了进行词法分析的核心代码 ,doc文件夹放一个帮助文档,当用户点击帮助按钮时可以弹出来以帮助用户使用。 Github项目链接:https://github.com/u014427391/lexer1.1.0,欢迎star //核心程序:
SmileNicky
2019/01/17
1.2K0
【编译原理】词法分析:C/C++实现
编译原理是计算机科学领域的一个重要分支,它研究如何将高级编程语言的源代码转化成计算机能够执行的机器代码或中间代码的过程。编译原理涵盖了编译器的设计和实现,其中编译器是一种将源代码翻译成目标代码的软件工具。编译器的主要任务包括语法分析、词法分析、语义分析、优化和代码生成等环节。
SarPro
2024/02/20
1.7K0
【编译原理】词法分析:C/C++实现
试用GO开发python编译器:实现词法解析
编译器由于涉及到编译原理,了解计算机科学的同学就能感触到,编译原理是较为抽象,无论从原理还是从实践上都是比较难把握的对象。在接触理论性较强,难度较大的问题时,最好的办法是从最简单的情况入手,先从感性上获得认知,为后面的理性认知打下基础,因此我们先从编译原理算法的基础入手,首先掌握词法解析。
望月从良
2021/12/31
5560
试用GO开发python编译器:实现词法解析
llvm入门教程-Kaleidoscope前端-6-用户定义运算符
llvm是当前编译器领域非常火热的项目,其设计优雅,官方文档也很全面,可惜目前缺乏官方中文翻译。笔者在学习过程中也尝试进行一些翻译记录,希望能对自己或者他人的学习有所帮助。
hunterzju
2021/12/09
1.4K0
编译原理实验1词法分析器的设计_编译原理实验一 词法分析
<语句>→<赋值语句> | <条件语句> | <WHILE语句> | <复合语句> | <过程定义>
全栈程序员站长
2022/11/19
3.3K0
编译原理实验1词法分析器的设计_编译原理实验一 词法分析
Go 译文之词法分析与解析 Part Two
本文是关于词法器实现的具体介绍,如果在阅读时遇到困难,建议参考源码阅读,文中的代码片段为了介绍思路。如何解析会在下一篇介绍。
波罗学
2019/07/31
4990
从词法分析角度看 Go 代码的组成
之前的 Go 笔记系列,已经完成到了开发环境搭建,原本接下来的计划就是到语法部分了,但后来一直没有前进。主要是因为当时的工作比较忙,分散了精力,于是就暂时放下了。
波罗学
2019/11/06
5030
antlr4入门篇
ANTLR实际上有两件事:一种将您的语法转换为Java(或其他目标语言)的解析器/词法分析器的工具,以及生成的解析器/词法分析器所需的运行时。即使您使用ANTLR Intellij插件或ANTLRWorks来运行ANTLR工具,生成的代码仍将需要运行时库。
山行AI
2020/08/18
4.5K0
antlr4入门篇
编译原理课程设计词法分析
主要实现对文本中的程序进行词法分析,把程序中的单词分为五大类(基本保留字[1]、标识符[2]、常数[3]、运算符[4]、分隔符[5])并与相应的区域数字来对应输出.
程序员小藕
2020/07/28
1.2K0
词法分析程序
程序分为4个关键方法,用户输入方法,读、写文件方法以及词法分析方法。其中词法分析方法是程序的核心。 词法分析程序主要分为两个部分,第一是取词,第二是分析。 取词阶段: 依次取字符串的每一个字符,遇到空字符时停下,将取到的字符合并成一个字符串,送去进行分析阶段。 分析阶段:程序先构建有关键字数组、分隔符数组和运算符数组,通过将取词阶段送来的字符串与各数组中元素进行比较,将字符串分类到相应的类别数组中保存。 词法分析伪代码如下: While (源码字符串没有取完){ Getchar(获取一个非空字符);
SuperHeroes
2018/05/30
1.1K0
[1] 使用 LLVM 实现一门简单的语言
本文跟着 LLVM Tutorial 教程完成,加上了一些注释。本文中的代码并非工程最佳实践。
谛听
2022/03/06
1.3K0
笔记:写Flink SQL Helper时学到的一些姿势
这块其实是编译原理的一部分,属于前端编译部分,并未涉及后端编译。见:github.com/camilesing/…中的 // 使用生成的词法分析器和解析器进行语法检查 const inputStream = new ANTLRInputStream(event.getText()); //词法解析 const lexer = new FlinkSQLLexer(inputStream); const tokenStream = new CommonTokenStream(lexer); //语法解析 const parser = new FlinkSQLParser(tokenStream); parser.removeErrorListeners(); parser.addErrorListener({ syntaxError: (recognizer: Recognizer<any, any>, offendingSymbol: any, line: number, charPositionInLine: number, msg: string, e: RecognitionException | undefined): void => { vscode.window.showErrorMessage("Parser flink sql error. line: " + line + " position: " + charPositionInLine + " msg: " + msg); }, }) parser.compileParseTreePattern // 解析文件内容并获取语法树 const parseTree = parser.program(); 写这块代码我用到了Antlr4-TS这个库。我根据一些Antlr4的语法规则,生成了对应的代码,并将输入内容丢进这些类,让它们吐出结果。在了解Antlr相关的语法规则时,让我特别震撼——类似于刚毕业一年时接触到DSL时的震撼。通过一系列规则的描述,竟然可以生产如此复杂、繁多的代码,巨幅解放生产力。这些规则是一种很美又具有实际价值的抽象。 那让我们抛开Antlr这个框架的能力,如果去手写一个词法、语法分析的实现,该怎么做呢? 在编程语言里,一般会有保留字和标识符的概念。保留字就是这个语言的关键字,比如SQL中的select,Java中的int等等,标识符就是你用于命名的文字。比如public class Person中的Person,select f1 as f1_v2 from t1 中的f1,f1_v2,t1。 再扩展一下概念,我们以int a=1;这样一段代码为例子,int 是关键字,a是标识符,=是操作符,;是符号(结束符)。搞清楚哪些词属于什么类型,这就是词法解析器要做的事。那怎么做呢?最简单的方法其实就是按照一定规则(比如A-Za-z$)一个个去读取,比如读到i的时候,它要去看后面是不是结束符或者空格,也就上文提到的的peek,如果不为空,就要继续往后读,直到读到空格或者结束符。那么读取出来是个int,就知道这是个关键字。 伪代码如下: 循环读取字符 case 空白字符 处理,并继续循环 case 行结束符 处理,并继续循环 case A-Za-z$_ 调用scanIden()识别标识符和关键字,并结束循环 case 0之后是X或x,或者1-9 调用scanNumber()识别数字,并结束循环 case , ; ( ) [ ]等字符 返回代表这些符号的Token,并结束循环 case isSpectial(),也就是% * + - | 等特殊字符 调用scanOperator()识别操作符 ... 这下我们知道了int a=1;在词法解析器看来其实就是关键字(类型) 标识符 操作符 数字 结束符。这样的写法其实是符合Java的语法规则的。反过来说:int int=1;是能够通过词法分析的,但是无法通过语法分析,因为关键字(类型) 关键字(类型) 操作符 数字 结束符是不符合Java的语法定义的。 这个时候可能会有人问,为啥要有词法分析这一层?都放到语法分析这一层也是可以做的啊。可以做,但会很复杂。而且一般软件工程中会都做分层,避免外面的变动影响到里面的核心逻辑。 举个例子:后续Java新增了一个类型,如果词法分析、语法分析是拆开的,那么只要改词法分析层的一些代码就行了,语法分析不用。但是如果没有词法分析这一层,语法分析的代码会有很多,而且一点点改动就很容易影响到这一层。 在此之后就会生成语法树。后续我打算做一些基于语法树的分析,Antlr提供了两种读语法节点的方式,一种是Vistor,一种是Listeners。前者意
泊浮目
2024/01/09
2310
Go语言编译链接过程
在之前interface、channel的文章中经常会提到,Go在编译时会将interface和channel关键字转换成runtime中的结构和函数调用。所以我觉得很有必要就Go的编译过程理一理做个进行总结,然后结合之前对底层原理总结的文章,那么对整个逻辑会更加清晰。我也是查了各种资料,尽量把整个过程能总起出一些东西来,学习嘛,总是需要不断总结,分享!
小许code
2023/03/07
1.1K0
Go语言编译链接过程
Go 译文之词法分析与解析 - Part One
一直对词法分析与解析的话题比较感兴趣,最近发现了好几篇相关的优秀文章,准备好好翻译和研究下。我的理解,词法分析与解析的应用还是比较广泛的,无论简单的配置文件、各种模板语言、还是我们每天在写编程语言都离不开它。
波罗学
2019/07/31
5110
【死磕Sharding-jdbc】---SQL解析-词法分析
sharding-jdbc对SQL解析的源码主要在下图所示parsing模块中,由下图可知SQL解析主要分为两部分:lexer和parser。lexer就是本文需要分析的词法分析:
用户1655470
2018/08/16
1.1K0
【死磕Sharding-jdbc】---SQL解析-词法分析
Golang 编译原理 计算器(通俗易懂)
本文不需要你掌握任何编译原理的知识。 只需要看懂简单的golang语言即可, 完整的代码示例在GIT
李海彬
2019/03/07
1K0
Golang 编译原理 计算器(通俗易懂)
编译原理实验一词法分析器_编译原理词法错误举例
实验目的:理解词法分析在编译程序中的作用; 加深对有穷自动机模型的理解; 掌握词法分析程序的实现方法和技术。
全栈程序员站长
2022/11/10
7440
编译原理实验一词法分析器_编译原理词法错误举例
相关推荐
不依赖yacc如何实现表达式按优先级解析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档