(C++)带你手肝词法分析器,容易理解,跟着思路有手就行

article/2025/10/31 15:31:49

词法分析器

  • 一.前言
  • 二.什么是“词法分析器”?
  • 三.正式设计
    • 1.设计种别码表
    • 2.设置判断为字母或数字的函数
    • 3.设置全局参数
    • 4.核心:scan()函数
    • 5.主函数里结合scan函数进行循环扫描
    • 6.结果截图
    • 7.需要注意(比较难)的地方
  • 四.心得体会
  • 五.源代码

一.前言

编译原理的第一个实验:设计、编制并调试一个词法分析程序,加深对词法分析原理的理解。

如果你有点思路但码力不足,可以跟着我一步步来手肝!

二.什么是“词法分析器”?

词法分析程序简单来说就是“读单词程序”,
该程序扫描高级语言编写的源程序,将源程序中由“单词符号”组成的字符串分解出一个个单词来。

单词符号分为5种:
1.保留字:例如C语言中的if、else、while、do等等
2.标识符:通常由用户自己定义。用来标记常量、数组、变量、类型等等。a,b,c等
3.常数:整型常数369、布尔常数TRUE等
4.运算符:如“+、-、*、/、<、>”
5.界符:在语言中是作为语法上的分界符号使用的,如“,”“;”“(”“)”等

注意:保留字、运算符、界符的个数是确定的; 而常数、标识符则是不限定个数的。

词法分析器例子:
输入内容:源程序字符串
例如“int a=1;a++;”

输出内容:输出的是与源程序等价的单词符号序列,并且所输出的单词符号通常表示成如下的二元式:
(单词种别,单词自身的词)

单词种别:我们可以类比成,每个种类都有相对应的“种别码”,即给对应的种别进行编号以加以区分。
就像自然界中,我们可以将植物编号为0,动物编号为1等等

单词自身的词:就是说在同一种类的不同单词区别开来。
比如,动物中可以再细分:两栖动物内码编号为1,哺乳动物内码编号为2等

所以当我们输入“小狗”,则输出二元式(1,2)
当然这只是形象的比喻,在计算机中我们往往需要识别什么“int、a、+、-”等等符号,其实原理和刚刚举的例子有相同之处的。

  • [1] 流程图:

在这里插入图片描述

  • [2] 状态转化图:

在这里插入图片描述

三.正式设计

1.设计种别码表

由于实验里已经给出了,我们就按照它纯cout出来就行了。

用一个函数showAll()来展示
​​
在这里插入图片描述

void showAll()    //展示部分单词符号所对应的种别码(可自行扩展)
{cout << "---------- 符号表---------------------- " << endl;cout << "符号\t种别码" << "\t" << "符号\t种别码" << endl;cout << "main" << '\t' << '1' << '\t' << "/" << '\t' << "25" << endl;cout << "int" << '\t' << '2' << '\t' << "(" << '\t' << "26" << endl;cout << "char" << '\t' << '3' << '\t' << ")" << '\t' << "27" << endl;cout << "if" << '\t' << '4' << '\t' << "[" << '\t' << "28" << endl;cout << "else" << '\t' << '5' << '\t' << "]" << '\t' << "29" << endl;cout << "for" << '\t' << '6' << '\t' << "{" << '\t' << "30" << endl;cout << "while" << '\t' << '7' << '\t' << "}" << '\t' << "31" << endl;cout << "return" << '\t' << '8' << '\t' << "," << '\t' << "32" << endl;cout << "void" << '\t' << '9' << '\t' << ":" << '\t' << "33" << endl;cout << "STRING" << '\t' << "50" << '\t' << ";" << '\t' << "34" << endl;cout << "ID" << '\t' << "10" << '\t' << ">" << '\t' << "35" << endl;cout << "INT" << '\t' << "20" << '\t' << "<" << '\t' << "36" << endl;cout << "=" << '\t' << "21" << '\t' << ">=" << '\t' << "37" << endl;cout << "+" << '\t' << "22" << '\t' << "<=" << '\t' << "38" << endl;cout << "-" << '\t' << "23" << '\t' << "==" << '\t' << "39" << endl;cout << "*" << '\t' << "24" << '\t' << "!=" << '\t' << "40" << endl;cout << "---------------------------------------" << endl;cout << "@author(Gassing)" << endl;
}

2.设置判断为字母或数字的函数

		为什么要做这一步?因为在状态转化图中,我们可以看到当我们要判断是保留字还是标识符时,首先要判断这个输入的字符是字母还是数字以方便后续的判断。

因此我们设置两个函数:

  • bool IsLetter(char ch); //判断是否为字母
  • bool IsDigit(char ch); //判断是否为数字

具体代码:

bool IsLetter(char ch)  //判断是否为字母
{if ((ch >= 'a'&&ch <= 'z') || (ch >= 'A'&&ch <= 'Z'))return true;elsereturn false;}bool IsDigit(char ch)  //判断是否为数字
{if (ch >= '0'&&ch <= '9')return true;elsereturn false;}

3.设置全局参数

//保留字
const string KeyWord[12] = { "main","int","char","if","else","for","while","return","void","STRING","ID","INT" };   //这个还可以有很多东西加,这里只是简单模拟而已,感兴趣的可以自己多加int syn;  //单词种别码
string  token;       //单词自身字符串int sum;  //INT整型里的码内值int i = 0;  int tag = 1;     //后面判断STRING字符的 string a="";    //你所输入的字符串

4.核心:scan()函数

我们自己设置一个函数scan(),此函数就是整个词法分析器的核心。
因为我们输入字符串进去,众所周知,字符串可以当作由多个字符组成的,即string a = “int”,也可以分解为a[0]=‘i’,a[1]=‘n’,a[2]=‘t’,a[3]=’\0’。

所以其实计算机是在字符串里一个个的单个字符进行分析的,而这个scan函数的作用就是对输入的这个字符进行判断,是属于哪一种种类。

token:用来记录扫描到的成型的标识符或关键字
syn:种别码

可以看到有空格就会跳过,若没有空格,则清空token,然后进行判断是字母、数字还是符号。然后若是字母或者数字则用个while循环来将s[i]加入到token这个字符串里,最后当扫描到不是字母或数字时就会跳出循环。此时判断并给出相对应的syn,然后保存token。

举个栗子,我现在输入字符串s=“int”,int i=0 然后扫描第i个字符,s[i] (即s[0])为 ‘i’ ,然后进到“拼字符串”环节,用while实现(每次都要i++),
token += s[i] ;
然后得到token = “int” ;
然后得到syn = 2 ;// int关键字对应种别码2

~ 代码有点长,放在最后了,可自行拉到最后查看。

  • scan函数的流程图,非常清晰!

在这里插入图片描述

5.主函数里结合scan函数进行循环扫描

我们可以把上面的scan函数当作是一个扫描机器,每次只能扫描一个字符。

所以我们若要扫描完所有字符,则可以在主函数里设置一个do-while函数,使得输入的字符串可以循环扫描完。

do{scan(a);switch (syn)   //最后判断一波syn{case -1:cout << "error:无结束符或存在非法字符" << endl;syn = 0;break;case -2:      //遇到空格跳过break;default:if(syn!=0)cout << "( " << syn << "," << token << " )" << endl;}} while (syn!=0);

可以看出,我们跳出的条件是syn = 0

那么syn =0 的情况:

  • 正常情况:我们可以要求我们输入的字符串在最后必须加入一个结束符“#”,来表示结束,当scan扫描到最后一个字符是“#”的时候就知道结束了,然后将syn = 0,跳出循环。
  • 非正常情况:如果遇到非法字符的话就将syn=0;然后告诉我们error,存在非法字符,结束循环。

此时有人会问:刚刚在scan里面得到的token和syn在哪里展示出来呢?
---- 我们在do-while 循环里用switch(syn)来选择,在default那里,在每次scan结束后进行输出二元组(syn,token)。

case-1: 代表error。
case-2:代表刚刚scan那里遇到空格,则直接break,不进行选择。

~@经过一系列的分析,整个词法分析器就完成啦!

6.结果截图

在这里插入图片描述
在这里插入图片描述

  • 还有含字符串的,看下图
    在这里插入图片描述

7.需要注意(比较难)的地方

  • 判断字符串这里
	case '"':syn = -1;token += s[i];i++;while (s[i] != '"'){if (s[i] == '#'){tag = 0;break;}else{token += s[i];i++;}}if (tag){token += s[i];i++;syn = 50;break;}else{syn = -1;cout << "双引号只存在一个,非法输入 " << endl;break;}

遇到单引号的时候我们先说它是非法字符,然后继续扫描,在遇到另一个单引号之前用token记录,若遇到另一个单引号,则说明两个双引号之间是字符串STRING,则让syn=50;

若一直遇不到,直到扫描到最后的字符是‘#’结束符,则说明只存在一个单引号,说明是非法的,则令syn=-1;


小技巧
在开始的时候string a;然后我输入int main,中间有个空格,但输出的却是只有int,空格后面的却不见了。

若想要保留空格的话,要用getline(cin,a);

在这里插入图片描述

四.心得体会

其实看起来简单,但还是调试了两三天,思路其实不难,感觉还是细节的问题。其实大家有余力的话可以在种别码表里加多点种类,然后就是CV的苦力活了,很简单的,照葫芦画瓢,来充实你的词法分析器。

其实这个输入字符串,我们也可以创建一个txt文件,里面就输入些字符串,然后在main函数里open就行了。这只是一个形式,最主要还是思想吧~

最后希望大家都能看得懂,如果有什么不懂或评价都可以在评论区,或者私信我都可以,大家讨论交流!


@如果这篇文章能帮到你的话,可以点个赞给作者一个支持哦!也可以关注我~

五.源代码

#include "pch.h"
#include <iostream>
#include<string>
#include <stdio.h>
using namespace std;//保留字
const string KeyWord[12] = { "main","int","char","if","else","for","while","return","void","STRING","ID","INT" };   //这个还可以有很多东西加,这里只是简单模拟而已,感兴趣的可以自己多加int syn;  //单词种别码
string  token;       //单词自身字符串int sum;  //INT整型里的码内值int i = 0;  int tag = 1;     //后面判断STRING字符的 void showAll();     //输入部分单词符号所对应的种别码(可自行扩展)bool IsLetter(char ch);   //判断是否为字母bool IsDigit(char ch);   //判断是否为数字void scan(string s);     //扫描int main()
{showAll();string a="";cout << "\n欢迎来到Gassing的词法分析器,请输入字符串(以‘#’作为结束标志):" << endl;getline(cin,a);cout<<"\n输出二元组(种别码,token或sum):"<<endl;do{scan(a);switch (syn)   //最后判断一波syn{case -1:cout << "error:无结束符或存在非法字符" << endl;syn = 0;break;case -2:      //遇到空格跳过break;default:if(syn!=0)cout << "( " << syn << "," << token << " )" << endl;}} while (syn!=0);}void showAll()    //展示部分单词符号所对应的种别码(可自行扩展)
{cout << "---------- 符号表---------------------- " << endl;cout << "符号\t种别码" << "\t" << "符号\t种别码" << endl;cout << "main" << '\t' << '1' << '\t' << "/" << '\t' << "25" << endl;cout << "int" << '\t' << '2' << '\t' << "(" << '\t' << "26" << endl;cout << "char" << '\t' << '3' << '\t' << ")" << '\t' << "27" << endl;cout << "if" << '\t' << '4' << '\t' << "[" << '\t' << "28" << endl;cout << "else" << '\t' << '5' << '\t' << "]" << '\t' << "29" << endl;cout << "for" << '\t' << '6' << '\t' << "{" << '\t' << "30" << endl;cout << "while" << '\t' << '7' << '\t' << "}" << '\t' << "31" << endl;cout << "return" << '\t' << '8' << '\t' << "," << '\t' << "32" << endl;cout << "void" << '\t' << '9' << '\t' << ":" << '\t' << "33" << endl;cout << "STRING" << '\t' << "50" << '\t' << ";" << '\t' << "34" << endl;cout << "ID" << '\t' << "10" << '\t' << ">" << '\t' << "35" << endl;cout << "INT" << '\t' << "20" << '\t' << "<" << '\t' << "36" << endl;cout << "=" << '\t' << "21" << '\t' << ">=" << '\t' << "37" << endl;cout << "+" << '\t' << "22" << '\t' << "<=" << '\t' << "38" << endl;cout << "-" << '\t' << "23" << '\t' << "==" << '\t' << "39" << endl;cout << "*" << '\t' << "24" << '\t' << "!=" << '\t' << "40" << endl;cout << "---------------------------------------" << endl;cout << "@author(Gassing)" << endl;
}bool IsLetter(char ch)  //判断是否为字母
{if ((ch >= 'a'&&ch <= 'z') || (ch >= 'A'&&ch <= 'Z'))return true;elsereturn false;}bool IsDigit(char ch)  //判断是否为数字
{if (ch >= '0'&&ch <= '9')return true;elsereturn false;}void scan(string s)    //扫描
{if (s[i] == ' '){syn = -2;i++;}else{token = "";   //清空当前字符串//  1.判断字符是否为数字if (IsDigit(s[i])){token = ""; //清空当前字符串sum = 0;while (IsDigit(s[i])) {sum = sum * 10 + (s[i] - '0');i++;  //字符位置++syn = 20;   //INT种别码为20}token += to_string(sum);     //骚操作,直接转化字符串}// 2.字符为字符串,表现为字母开头衔接任意个数字或字母else if (IsLetter(s[i])){token = ""; //清空当前字符串while (IsDigit(s[i]) || IsLetter(s[i])) {token += s[i];   //加入token字符串i++;}//s[i] = '\0';  //刚刚上面最后i++了所以补充syn = 10;  // 如果是标识符,种别码为10//如果是关键字,则用for循环将token与keyword比较找对应的种别码for (int j = 0; j < 12; j++){if (token == KeyWord[j])    //如果都是string类型,可以直接=相比较,若相等则返回1,否则为0{syn = j + 1;   //种别码从1开始所以要加1break;}}}//3. 判断为符号else {token = ""; //清空当前字符串switch (s[i]) {case'=':syn = 21;i++;token = "=";if (s[i] == '=') {syn = 39;i++;token = "==";}break;case'+':syn = 22;i++;token = "+";break;case'-':syn = 23;i++;token = "-";break;case'*':syn = 24;i++;token = "*";break;case'/':syn = 25;i++;token = "/";break;case'(':syn = 26;i++;token = "(";break;case')':syn = 27;i++;token = ")";break;case'[':syn = 28;i++;token = "[";break;case']':syn = 29;i++;token = "]";break;case'{':syn = 30;i++;token = "{";break;case'}':syn = 31;i++;token = "}";break;case',':syn = 32;i++;token = ",";break;case':':syn = 33;i++;token = ":";break;case';':syn = 34;i++;token = ";";break;case'>':syn = 35;i++;token = ">";if (s[i] == '='){syn = 37;i++;token = ">=";}break;case'<':syn = 36;i++;token = "<";if (s[i] == '='){syn = 38;i++;token = "<=";}break;case'!':syn = -1;i++;if (s[i] == '='){syn = 40;i++;token = "!=";}break;case '"':syn = -1;token += s[i];i++;while (s[i] != '"'){if (s[i] == '#'){tag = 0;break;}else{token += s[i];i++;}}if (tag){token += s[i];i++;syn = 50;break;}else{syn = -1;cout << "双引号只存在一个,非法输入 " << endl;break;}case '#': //结束syn = 0;cout << "\n#结束" << endl;break;default:syn = -1;break;}}}}

@如果这篇文章能帮到你的话,可以点个赞给作者一个支持哦!也可以关注我~

希望大家少CV,多思考!


http://chatgpt.dhexx.cn/article/sYrg7bDr.shtml

相关文章

词法分析器实现

点击打开链接 词法分析器实现 一、写在前面 编译原理是软件工程的一项基础的课程&#xff0c;是研究软件是什么&#xff0c;为什么可以运行&#xff0c;以及怎么运行的学科&#xff0c;编译系统的改进将会直接对其上层的应用程序的执行效率&#xff0c;执行原理产生深刻的影响…

词法分析器(一)

词法分析器 &#xff08;基本符号表&#xff0c;状态转换图&#xff09; 引言&#xff1a;编译原理的实验部分是关于编译器的&#xff0c;我决定将这部分的学习和实践过程记录下来&#xff0c;也希望看到这篇文章的有缘人来指正和提出宝贵的意见。 基本符号表 本次实验我通过…

词法分析器 Java完整代码版

想了解更多内容&#xff0c;移步至编译原理专栏 2021.12.22 更新 整理了一下代码&#xff0c;同步到了gitee https://gitee.com/godelgnis/lrparserhttps://gitee.com/godelgnis/lrparser --------------------------------------------------分割线-----------------------…

编译原理——词法分析器(C/C++代码实现)

目录 0 实验目的&#xff1a; 1 实验要求&#xff1a; 2 实验内容&#xff1a; 3 实验思路&#xff1a; 4 实验代码&#xff1a; 5 实验结果&#xff1a; 6 实验总结&#xff1a; 7 实验程序以及实验报告下载链接&#xff1a; 0 实验目的&#xff1a; 设计、编制、实现…

词法分析器的构成(含源代码)

标题&#xff1a;词法分析器 本人最近在学习编译原理&#xff0c;刚刚学到词法分析器&#xff0c;心想着挺好玩&#xff0c;就想着自己写一个&#xff0c;奈何一没有系统的学过c语言&#xff0c;只是粗略的看过一遍K&R的c语言书&#xff0c;所以水平尚浅&#xff0c;代码有…

java实现词法分析器

实现词法分析器 实验内容要求 一、实验目的 加深对词法分析器的工作过程的理解&#xff1b;加强对词法分析方法的掌握&#xff1b;能够采用一种编程 语言实现简单的词法分析程序&#xff1b;能够使用自己编写的分析程序对简单的程序段进行词法分 析。 二、实验内容 自定义一…

python实现词法分析器

基于python3 实现一个简单的词法分析器。 主要使用的库&#xff1a;正则表达式、tkinter 识别关键字&#xff0c;标识符&#xff0c;运算符&#xff0c;分界符&#xff0c;数字&#xff08;整数和浮点数&#xff09; 当以数字开头时报错&#xff0c;标识符超过8个字符长度时报…

词法分析器设计与实现

开篇 编译&#xff0c;简单的说&#xff0c;就是把源程序转换为可执行程序。从hello world 说程序运行机制 里面简单的说明了程序运行的过程&#xff0c;以及一个程序是如何一步步变成可执行文件的。在这个过程中&#xff0c;编译器做了很多重要的工作。对底层该兴趣的我&…

[编译原理]词法分析器的分析与实现

词法分析概述&#xff1a; 编译程序要对高级语言编写的源程序进行分析和合成&#xff0c;生成目标程序。词法分析是对源程序进行的首次分析&#xff0c;实现词法分析的程序成为词法分析程序(或词法分析器)&#xff0c;也称扫描器。像用自然语言书写的文章一样&#xff0c;源程…

词法分析器【编译原理】

实验内容&#xff1a; 基于TEST语言设计相应的词法输入器并且输出二元组 实验目的&#xff1a; 1、理解词法分析器的基本功能 2、理解简单的词法规则的描述方法 3、理解状态转化图及其实现 4、能够编写简单的词法分析器 实验原理&#xff1a; 根据DFA构造词法分析程序 1、…

词法分析器的实现

原文地址为&#xff1a; 词法分析器的实现 开篇 编译&#xff0c;简单的说&#xff0c;就是把源程序转换为可执行程序。从hello world 说程序运行机制 里面简单的说明了程序运行的过程&#xff0c;以及一个程序是如何一步步变成可执行文件的。在这个过程中&#xff0c;编译器…

词法分析器原理简介

词法分析器原理简介 词法分析器读取有字符串组成的输入流&#xff0c;并产生包含单词的输出流&#xff0c;每个单词都标记了其语法范畴&#xff08;syntactic category&#xff09;或类型&#xff0c;等效于英文单词的词类。为了完成这种聚集和分类操作&#xff0c;词法分析器…

编译原理——词法分析器 C++实现

词法分析器 实验目的单词分类表单词结构描述单词状态转换图算法描述程序结构源代码实验结果 实验目的 对C语言的一个子集设计并实现一个简单的词法分析器&#xff0c;掌握利用状态转换图设计词法分析器的基本方法。利用该词法分析器完成对源程序字符串的词法分析。培养团队合作…

词法分析器(纯c语言)

一、原文章&#xff1a;词法分析器&#xff08;分析C语言&#xff09; 二、该词法分析器种别码表 三、词法分析器实现思路描述&#xff1a; 1.首先用一个数组来存储txt文本中非空白字符&#xff0c;并将存储字符的个数记录下来。 2.用scan()函数扫描数组中的字符&#xff0c…

编译原理--词法分析器(python语言实现)

词法分析器 最近在学习编译原理。由于实验要求有词法分析器&#xff0c;这里我就先记录一下词法分析器实现过程以及具体思路。 目标语言 此处我选择的目标语言是c语言的子集来进行词法分析。 实现语言 此处我选用的语言是python&#xff0c;主要还是考虑到python的数据结构…

词法分析器--C实现

实验目的&#xff1a; 编制一个读单词过程&#xff0c;从输入的源程序中&#xff0c;识别出各个具有独立意义的单词&#xff0c;即基本保留字、标识符、常数、运算符、分隔符五大类(可自主添加类别)。并依次输出各个单词的内部编码及单词符号自身值。 程序及其子程序&#xff1…

c语言实现词法分析器

词法分析器的功能:输入源程序&#xff0c;输出单词字符。单词字符一般可以分为下面五种。 &#xff08;1&#xff09;关键字 是由程序语言定义的具有固定意义的标识符。有时称这些标识符为保留字或者基本字。例如c语言中的int,char,define,strcut,double,if,else.等等 &#xf…

词法分析器(分析C语言)

问题描述&#xff1a; 用C或C语言编写一个简单的词法分析程序&#xff0c;扫描C语言小子集的源程序&#xff0c;根据给定的词法规则&#xff0c;识别单词&#xff0c;填写相应的表。如果产生词法错误&#xff0c;则显示错误信息、位置&#xff0c;并试图从错误中恢复。简单的恢…

词法分析器(c++)

前景提示&#xff1a; 个人觉得单纯是用来完成实验报告的话还行&#xff0c;但仅做参考&#xff0c;因为本人的编程水平有限&#xff0c;怕误人子弟。 本次代码支持以下操作&#xff1a; 单行注释 多行注释 文件形式输入 种别码可以在文件中自由修改 单词字符串识别支持…

词法分析——词法分析器的作用

目录 综述 正文 1 词法分析与语法分析 2 词法单元、模式和词素 3 词法单元的属性 4 词法错误 综述 词法分析是编译的第一阶段。词法分析器的主要作用是读入源程序的输入字符、将它们组成词素&#xff0c;生成并输出一个词法单元序列&#xff0c;每个词法单元对应一个词素。…