0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

手写词法分析器

冬至子 来源:赵同学的代码时间 作者:Jun. 2023-05-23 11:20 次阅读

在开始手写词法分析器之前呢,我们得先准备好一些零件,规划好将要使用哪些函数,如果函数没有现成的,那还得自己写。

输入

由于我们需要从流中读出,有时还需要放回流,词法分析器显然每次读入都是按字符读入,所以使用getchar函数一般没有问题,然后对于放回流,C中提供了一个ungetc的后悔函数,首先先来尝试一下这个函数:

int main()
{
    char t;
    t = getchar();
    cout << t;
    ungetc(t, stdin);
    t = 'a';
    t = getchar();
    cout << t;
    // input : 12, output : 11
}

其实也就是把这个字符放回到标准输入流的最前面。

判断

我们需要判断一个字符它是字符还是数字,参考实验指导中的代码,有一个头文件ctype.h可以帮上忙 。

isalnum() 判断一个字符是否是字母或数字

isalpha() 判断一个字符是否是字母

isdigit() 判断一个字符是否是十进制数字

islower() 判断一个字符是否是小写字母

isspace() 判断一个字符是否是空白符(包含\\n等空白效果的控制字符)

返回值:如果满足,返回非0,不满足,返回0;

子问题1:识别关键字、标识符、数据类型

图片

这一步要完成上面这一个分支,计划使用Trie树保存关键字,然后按照以下步骤进行,完成一个函数就可以:

图片

建树过程:

// 预设的keywords和basic
string keyword[] = {"if", "while", "do", "break", "float", "true", "false"};
string basic[] = {"int", "char", "bool", "float"};
// 宏定义,使代码更可读
#define KEYWORD 1
#define BASIC 2
#define IDENTITY 3
// 定义全局变量
const int N = 128;
int son[N][N], idx;
int token[N * N]; // 定义这个idx对应的token
// insert函数,详见Trie树
void insert(string s, int mode)
{
    int p = 0; // 从根节点开始
    for(int i = 0; i < s.size(); i ++)
    {
        if(!son[p][s[i]]) son[p][s[i]] = ++ idx;
        p = son[p][s[i]];
    }
    token[idx] = mode;
}
// 查询函数,返回token类型
int query(string s)
{
    int p = 0;
    for(int i = 0; i < s.size(); i ++)
    {
        if(!son[p][s[i]]) return IDENTITY;
        p = son[p][s[i]];
    }
    return token[p];
}
// 初始化函数
void initialize()
{
    for(int i = 0; i < 7; i ++) insert(keyword[i], KEYWORD);
    for(int i = 0; i < 4; i ++) insert(basic[i], BASIC);
}

查询测试:

图片

已经成功的可以使用了。

我们可以发现,不光是keyword和basic和identity,其他的字符也可以存到Trie树里面去,比如以下专用符号:

= + - * / < <= > >= == != ; , ( ) [ ] { }

甚至是注释符号:

/* */

子问题2:识别数字

首先,为了识别数字,我们先给出数字的正则表达式:

图片

尝试给出NFA

图片

转化为DFA

图片

分析状态转移表:

1.jpg

上标转化为代码表示:

int st[11][5] = {{0, 0, 0, 0, 0},
                 {2, 3, 4, 0, 0},
                 {0, 0, 4, 0, 0},
                 {0, 0, 4, 0, 0},
                 {0, 0, 4, 5, 7},
                 {0, 0, 6, 0, 0},
                 {0, 0, 6, 0, 7},
                 {8, 9, 10, 0, 0},
                 {0, 0, 10, 0, 0},
                 {0, 0, 10, 0, 0},
                 {0, 0, 10, 0, 0}};

根据状态转移表完成编程

string num() // 输入一个数字字符串
{
    string s;
    char t;

    int status = 1;
    int last_status;
    while(1){
        t = getchar();
        switch (t)
        {
        case '+':
            last_status = status;
            status = st[status][0];
            break;
        case '-':
            last_status = status;
            status = st[status][1];
            break;
        case '.':
            last_status = status;
            status = st[status][3];
            break;
        case 'E':
            last_status = status;
            status = st[status][4];
            break;
        case 'e':
            last_status = status;
            status = st[status][4];
            break;

        default:
            if(isdigit(t))
            {
                last_status = status;
                status = st[status][2];
            }
            else {
                last_status = status;
                status = 0;
            }
            break;
        }
        if(!status)
        {
            ungetc(t, stdin);
            break;
        }
        s += t;
    }
    status = last_status;
    if(status != 4 && status != 6 && status != 10) return "";
    return s;
}

结果测试:

图片

于是乎我们又实现了一个有限状态自动机,从而实现了数字字符串的输入。

子问题3:将输入分割为小字符串

要实现将输入分割为小字符串,且向前看的步长仅为1步。首先我们需要分析出究竟在什么时候,才应该将他们分开。

1) 以下划线、字母开头,使用Trie树搜索;

2) 以+ -开头,再看一步为数字,放回加减后使用DFA搜索;

3) 除+ -专用符号开头,使用Trie树搜索;

4) 注释符号开头,一直接收到遇到注释尾。

子问题4:处理注释

此处做一个简化,不考虑注释中的转义字符,匹配到 */ 则结束。

string comments()
{
    string s;
    char last_c, c;

    while(last_c != '*' || c != '/')
    {
        last_c = c;
        c = getchar();
        s += c;
    }


    return s;
}

验证:

图片

可以看到,它自动截取了注释的前面部分。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 分析器
    +关注

    关注

    0

    文章

    92

    浏览量

    12492
  • 状态机
    +关注

    关注

    2

    文章

    492

    浏览量

    27529
  • NFA
    NFA
    +关注

    关注

    0

    文章

    4

    浏览量

    7130
收藏 人收藏

    评论

    相关推荐

    #硬声创作季 4.2.1 词法分析器的结构

    编译原理词法分析
    Mr_haohao
    发布于 :2022年09月01日 06:50:51

    关于antlr词法分析器的使用

    刚刚接触antlr词法分析器只略看了些基本理论知识,关于做实例就完全不懂了,我想知道他需要什么样的环境和软件,以及软件的下载地址.多谢各位了
    发表于 11-12 16:29

    Hanlp分词之CRF中文词法分析详解

    这是另一套基于CRF的词法分析系统,类似感知机词法分析器,提供了完善的训练与分析接口。CRF的效果比感知机稍好一些,然而训练速度较慢,也不支
    发表于 02-18 15:28

    postgreSQL命令的词法分析和语法分析

    PostgreSQL查询SQL的语法分析(1)——词法分析
    发表于 05-16 16:33

    手写SQL编译词法分析

    精读《手写 SQL 编译 - 词法分析
    发表于 05-26 16:27

    一种实用的脉冲幅度分析器

    摘要:通过对高精度脉冲幅度分析器的电路分析,得出了在使用过程中,采用高精度和低温系数的电压基准集成块,可保证该脉冲幅度分析器比传统脉冲幅度分析器灵敏度高、稳定
    发表于 05-25 08:39 29次下载

    C语言词法分析器的代码

    C语言词法分析器的代码#include #include #include #include <
    发表于 10-10 15:32 85次下载

    遥控分析器

    遥控分析器
    发表于 09-18 14:14 602次阅读
    遥控<b class='flag-5'>分析器</b>

    谐波失真分析器

    谐波失真分析器 电路包括一个1KHZ的低失真
    发表于 09-23 14:34 826次阅读
    谐波失真<b class='flag-5'>分析器</b>

    网络分析器,网络分析器原理是什么?

    网络分析器,网络分析器原理是什么? 网络分析器   具有发现并解决各种故障特性的硬件或软件设备
    发表于 03-22 11:25 1054次阅读

    协议分析器在WLAN中的应用

    协议分析器在WLAN中的应用 协议分析器广泛应用于有线网络,成为一类极有用的测试和维护工具。然而,在WLAN领域,这个问题很有可
    发表于 03-29 17:11 516次阅读

    静态代码分析器

    Fortify的静态代码分析器(Static Code Analyzer,SCA)是组成Fortify 360的三个分析器之一。SCA工作在开发阶段,以用于分析应用程序的源代码是否存在安全漏洞。这种类型的
    发表于 04-07 20:32 22次下载

    交换机端口分析器

    本文将重点介绍“交换端口分析器(SPAN)”的工作原理及配置方法。
    发表于 02-03 14:09 999次阅读

    Python-写个可转债分析器

    用Python写个可转债分析器
    的头像 发表于 02-16 15:13 1308次阅读
    Python-写个可转债<b class='flag-5'>分析器</b>

    自顶向下的语法分析器—采用递归下降方法

    在之前已经通过手写的方式实现了一个词法分析器,现在,我将利用之前手写词法分析器,使用递归下降的
    的头像 发表于 05-23 11:24 2076次阅读
    自顶向下的语法<b class='flag-5'>分析器</b>—采用递归下降方法