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

词法分析器(Lexer)的实现

作者头像
h1J4cker
发布2022-12-01 15:53:05
1.6K0
发布2022-12-01 15:53:05
举报
文章被收录于专栏:二进制安全
  • 写在前面

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

  • Lexer是什么

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

  • 词法分析的任务

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

  • Lexer的实现

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

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

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

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

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

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

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

代码语言:javascript
复制
enum Tokenizer
{
    tok_eof = -1,
    tok_def = -2,
    tok_extern = -3,
    tok_identifier = -4,
    tok_number = -5
};

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

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

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

代码语言:javascript
复制
static int __Get_Tok()
{
    static int LastChar = " ";

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

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

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

首先我们来识别关键字:

代码语言:javascript
复制
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
复制
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
复制
if (LastChar == "#")
{
    do
    {
        LastChar = getchar();
    } while (LastChar != EOF && LastChar != '\n' && LastChar != '\r');

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

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

1.读到了运算符

2.读到了EOF

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

代码语言:javascript
复制
if (LastChar == EOF)
{
    return tok_eof;
}

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

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

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档