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

article/2025/11/1 10:13:07

词法分析器

    • 实验目的
    • 单词分类表
    • 单词结构描述
    • 单词状态转换图
    • 算法描述
    • 程序结构
    • 源代码
    • 实验结果

实验目的

  • 对C语言的一个子集设计并实现一个简单的词法分析器,掌握利用状态转换图设计词法分析器的基本方法。利用该词法分析器完成对源程序字符串的词法分析。
  • 培养团队合作精神,体会协同工作在解决问题中的作用。

单词分类表

单词符号种类种别码单词符号种类种别码
未定义符号未定义符号0==运算符17
void关键字1<运算符18
main关键字2<=运算符19
int关键字3>运算符20
double关键字4>=运算符21
for关键字5(分界符22
while关键字6)分界符23
switch关键字7[分界符24
case关键字8]分界符25
if关键字9{分界符26
else关键字10}分界符27
return关键字11,分界符28
+运算符12;分界符29
-运算符13整型常量整型常量30
*运算符14浮点型常量浮点型常量31
/运算符15标识符标识符32
=运算符16

单词结构描述

正规文法G[S]:

  1. S→关键字|运算符|分界符|整型常量|浮点型常量|标识符
  2. 关键字→ void|main|int|float|for|while|switch|case|if|else|return
  3. 运算符 → +|-|*|/|=|==|<|<=|>|>=
  4. 分界符 → (|)|[|]|{|}|,|;
  5. 整型常量 → digit(digit)*
  6. 浮点型常量 → digit(digit).digit(digit)
  7. digit→ 0|1|2|3|4|5|6|7|8|9
  8. letter→ a|b|…|z|A|B|…|Z
  9. 标识符 → letter(letter|digit)*

单词状态转换图

状态转换图

算法描述

  • 读取文件到内存,逐个字符分析,若是空白符则跳过,为字母时将连续的字母使用超前搜索组合成为变量或关键字;若是数字,则要判断是否为浮点数,即使用超前搜索的时候,判断扫描到的字符是否为小数点;若是分隔符或者操作符,利用switch语句判断并输出,若是其他字符,输出为未定义的字符。
    算法描述

程序结构

TokenCode code = TK_UNDEF; //记录单词的种别码
int row = 1; //记录字符所在的行数
string token = “”; //用于存储单词符号构成的字符串
TokenCode枚举类型中定义了单词的种别码,keyWord中存储了关键字,分隔符、操作符利用switch语句判断。lexicalAnalysis函数用于词法分析,print函数用于打印输出结果。
程序流程图

源代码

  • 目前在VS2017上测试通过,其他编译器可能不成功,代码是次要的,只要理解了思想,代码很容易写出来的
/***********************************************
* 词法分析器
* 编译环境:Visual Studio 2017
***********************************************/
#include <iostream>
#include <string>
#include <Windows.h>
using namespace std;/* 单词编码 */
enum TokenCode
{/*未定义*/TK_UNDEF = 0,/* 关键字 */KW_VOID,	//void关键字KW_MAIN,	//main关键字KW_INT,		//int关键字KW_DOUBLE,	//double关键字KW_FOR,		//for关键字KW_WHILE,	//while关键字KW_SWITCH,	//switch关键字KW_CASE,	//case关键字KW_IF,		//if关键字KW_ELSE,	//else关键字KW_RETURN,	//return关键字/* 运算符 */TK_PLUS,	//+加号TK_MINUS,	//-减号TK_STAR,	//*乘号TK_DIVIDE,	///除号TK_ASSIGN,	//=赋值运算符TK_EQ,		//==等于号TK_LT,		//<小于号TK_LEQ,		//<=小于等于号TK_GT,		//>大于号TK_GEQ,		//>=大于等于号/* 分隔符 */TK_OPENPA,	//(左圆括号TK_CLOSEPA,	//)右圆括号TK_OPENBR,	//[左中括号TK_CLOSEBR,	//]右中括号TK_BEGIN,	//{左大括号TK_END,		//}右大括号TK_COMMA,	//,逗号TK_SEMOCOLOM,	//;分号/* 常量 */TK_INT,		//整型常量TK_DOUBLE,	//浮点型常量/* 标识符 */TK_IDENT
};/******************************************全局变量*****************************************************/
TokenCode code = TK_UNDEF;		//记录单词的种别码
const int MAX = 11;				//关键字数量
int row = 1;					//记录字符所在的行数
string token = "";				//用于存储单词
char  keyWord[][10] = { "void","main","int","double","for","while","switch","case","if","else","return" };	//存储关键词/**********************************************函数*****************************************************//********************************************
* 功能:打印词法分析的结果
* code:单词对应的种别码
* token:用于存储单词
* row:单词所在的行数
*********************************************/
void print(TokenCode code)
{switch (code){/*未识别的符号*/case TK_UNDEF:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED);	//未识别的符号为红色cout << '(' << code << ',' << token << ")" << "未识别的符号在第" << row << "行。" << endl;return;break;/*关键字*/case KW_VOID:		//void关键字case KW_MAIN:	//main关键字case KW_INT:		//int关键字case KW_DOUBLE:	//double关键字case KW_FOR:		//for关键字case KW_WHILE:	//while关键字case KW_SWITCH:	//switch关键字case KW_CASE:	//case关键字case KW_IF:		//if关键字case KW_ELSE:	//else关键字case KW_RETURN:	//return关键字SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_BLUE);	//关键字为蓝色break;/* 运算符 */case TK_PLUS:	//+加号case TK_MINUS:	//-减号case TK_STAR:	//*乘号case TK_DIVIDE:	///除号case TK_ASSIGN:	//=赋值运算符case TK_EQ:		//==等于号case TK_LT:		//<小于号case TK_LEQ:	//<=小于等于号case TK_GT:		//>大于号case TK_GEQ:		//>=大于等于号/* 分隔符 */case TK_OPENPA:	//(左圆括号case TK_CLOSEPA:	//)右圆括号case TK_OPENBR:	//[左中括号case TK_CLOSEBR:	//]右中括号case TK_BEGIN:	//{左大括号case TK_END:	//}右大括号case TK_COMMA:	//,逗号case TK_SEMOCOLOM:	//;分号SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);	//运算符和分隔符为绿色break;/* 常量 */case TK_INT:	//整型常量case TK_DOUBLE:	//浮点型常量SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);	//常量为黄色if(token.find('.')==token.npos)cout << '(' << code << ',' << atoi(token.c_str()) << ")" << endl;						//单词为整型elsecout << '(' << code << ',' << atof(token.c_str()) << ")" << endl;							//单词为浮点型return;break;/* 标识符 */case TK_IDENT:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);	//关键字为灰色break;default:break;}cout << '(' << code << ',' << token << ")" << endl;
}/********************************************
* 功能:判断是否是关键字
* MAX:关键字数量
* token:用于存储单词
*********************************************/
bool isKey(string token)
{for (int i = 0; i < MAX; i++){if (token.compare(keyWord[i]) == 0)return true;}return false;
}/********************************************
* 功能:返回关键字的内码值
* MAX:关键字数量
* token:用于存储单词
*********************************************/
int  getKeyID(string token)
{for (int i = 0; i < MAX; i++){	//关键字的内码值为keyWord数组中对应的下标加1if (token.compare(keyWord[i]) == 0)	return i+1;}return -1;
}/********************************************
* 功能:判断一个字符是否是字母
* letter:被判断的字符
*********************************************/
bool isLetter(char letter)
{if ((letter >= 'a'&&letter <= 'z') || (letter >= 'A' &&letter <= 'Z'))return true;return false;}/********************************************
* 功能:判断一个字符是否是数字
* digit:被判断的字符
*********************************************/
bool isDigit(char digit)
{if (digit >= '0'&&digit <= '9')return true;return false;
}/********************************************
* 功能:词法分析
* fp:文件指针
* code:单词对应的种别码
* token:用于存储单词
* row:单词所在的行数
*********************************************/
void lexicalAnalysis(FILE *fp)
{char ch;			//用于存储从文件中获取的单个字符while ((ch = fgetc(fp)) != EOF)	//未读取到文件尾,从文件中获取一个字符{token = ch;									//将获取的字符存入token中if (ch == ' ' || ch == '\t' || ch == '\n')	//忽略空格、Tab和回车{if (ch == '\n')							//遇到换行符,记录行数的row加1row++;continue;								//继续执行循环}else if (isLetter(ch))			//以字母开头,关键字或标识符{token = "";					//token初始化while (isLetter(ch) || isDigit(ch))	//非字母或数字时退出,将单词存储在token中{token.push_back(ch);	//将读取的字符ch存入token中ch = fgetc(fp);			//获取下一个字符}//文件指针后退一个字节,即重新读取上述单词后的第一个字符fseek(fp, -1L, SEEK_CUR);if (isKey(token))	//关键字code = TokenCode(getKeyID(token));else	//标识符code = TK_IDENT;	//单词为标识符}else if (isDigit(ch))	//无符号常数以数字开头{int isdouble = 0;	//标记是否为浮点数token = "";			//token初始化while (isDigit(ch))	//当前获取到的字符为数字{token.push_back(ch);		//读取数字,将其存入token中ch = fgetc(fp);				//从文件中获取下一个字符//该单词中第一次出现小数点if (ch == '.'&& isdouble == 0){//小数点下一位是数字if (isDigit(fgetc(fp))){isdouble = 1;		//标记该常数中已经出现过小数点fseek(fp, -1L, SEEK_CUR);		//将超前读取的小数点后一位重新读取	token.push_back(ch);			//将小数点入token中ch = fgetc(fp);				//读取小数点后的下一位数字}}}if (isdouble == 1)code = TK_DOUBLE;	//单词为浮点型elsecode = TK_INT;				//单词为整型//文件指针后退一个字节,即重新读取常数后的第一个字符fseek(fp, -1L, SEEK_CUR);}else switch (ch){	/*运算符*/case '+': code = TK_PLUS;		//+加号			break;case '-': code = TK_MINUS;		//-减号break;case '*': code = TK_STAR;		//*乘号		break;case '/': code = TK_DIVIDE;		//除号break;case '=':{ch = fgetc(fp);				//超前读取'='后面的字符if (ch == '=')				//==等于号{token.push_back(ch);	//将'='后面的'='存入token中code = TK_EQ;			//单词为"=="}		else {						//=赋值运算符code = TK_ASSIGN;		//单词为"="fseek(fp, -1L, SEEK_CUR);	//将超前读取的字符重新读取}}break;case '<':		{ch = fgetc(fp);				//超前读取'<'后面的字符if (ch == '=')				//<=小于等于号{token.push_back(ch);	//将'<'后面的'='存入token中code = TK_LEQ;			//单词为"<="}		else {						//<小于号code = TK_LT;			//单词为"<"fseek(fp, -1L, SEEK_CUR);	//将超前读取的字符重新读取}}break;case '>':{ch = fgetc(fp);				//超前读取'>'后面的字符if (ch == '=')				//>=大于等于号{token.push_back(ch);	//将'>'后面的'='存入token中code = TK_GEQ;			//单词为">="}	else {						//>大于号code = TK_GT;			//单词为">"fseek(fp, -1L, SEEK_CUR);	//将超前读取的字符重新读取}}break;/*分界符*/case '(': code = TK_OPENPA;		//(左圆括号break;case ')': code = TK_CLOSEPA;	//)右圆括号break;case '[': code = TK_OPENBR;		//[左中括号break;case ']': code = TK_CLOSEBR;	//]右中括号break;case '{': code = TK_BEGIN;		//{左大括号break;case '}': code = TK_END;		//}右大括号break;case ',': code = TK_COMMA;		//,逗号break;case ';': code = TK_SEMOCOLOM;	//;分号break;//未识别符号default: code = TK_UNDEF;}print(code);				//打印词法分析结果}
}int main()
{string filename;		//文件路径FILE* fp;				//文件指针cout << "请输入源文件名:" << endl;while (true) {cin >> filename;		//读取文件路径if ((fopen_s(&fp,filename.c_str(), "r"))==0)		//打开文件break;elsecout << "路径输入错误!" << endl;	//读取失败}cout << "/=***************************词法分析结果***************************=/" << endl;lexicalAnalysis(fp);		//词法分析fclose(fp);					//关闭文件SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);	//字体恢复原来的颜色return 0;
}
  • 测试数据
double add(double x, double y)
{double a = 3.456;return x + y;
}
$
int sum = 0;
for(int i = 1; i< 10; i = i + 1)
{sum = sum + i;
}

实验结果

在这里插入图片描述


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

相关文章

词法分析器(纯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;每个词法单元对应一个词素。…

词法分析器

词法分析&#xff08;Lexical Analysis&#xff09; 词法分析器在英文中一般叫做 Tokenizer。 有一个计算模型&#xff0c;叫做有限自动机&#xff08;Finite-state Automaton&#xff0c;FSA&#xff09;&#xff0c;或者叫做有限状态自动机&#xff08;Finite-state Machin…

编译原理——词法分析器

1 概述 设计、编制并调试一个简单的C语言词法分析程序&#xff0c;掌握利用状态转换图设计词法分析器的基本方法&#xff0c;利用该词法分析器完成对源程序字符串的词法分析。通过对该词法分析器的设计&#xff0c;加深对词法分析原理、状态转换图等编译原理知识的理解。 2 使…

编译原理词法分析器(C/C++)

前言&思路 词法分析器不用多说&#xff0c;一开始我还不知道是什么样的&#xff0c;看了下别人的博客&#xff0c;再看看书&#xff0c;原来是输出二元组&#xff0c;这不就是字符串操作嘛。然后细看几篇博客&#xff0c;发现大都是用暴力判断来写的。我对代码重复性比较高…

【编译原理】词法分析(C/C++源代码+实验报告)

文章目录 1 实验目的和内容1.1实验目的1.2实验内容 2 设计思想2.1单词种类及其正规式2.2 根据正规式构造NFA2.3根据NFA构造DFA2.3.1根据替换规则构造未化简的DFA2.3.2最小化DFA 3算法流程4源程序5调试数据5.1 测试样例一5.2 测试样例二5.3 测试样例三 6实验调试情况及体会6.1 实…

session 每次请求都会产生新的sessionID

问题描述&#xff1a; 最近在写一个项目时&#xff0c;在运行项目后每刷新一次都会产生一个新的Session ID&#xff0c;导致无法取值。 原因分析&#xff1a; 搞了很久发现是URL路径的问题&#xff0c;把http://localhost:8080//的双斜杠该为单斜杠就行了 解决方案&#xf…

JavaWeb - Cookie、Session、SessionId 详解

一、概述 会话&#xff08;Session&#xff09;跟踪是Web程序中常用的技术&#xff0c;用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份&#xff0c;Session通过在服务器端记录信息确定用户身份。 本章将系统地讲述Co…

JSESSIONID和sessionid的区别

要保持登陆状态&#xff0c;但是sessionid 和 JSESSIONID的值不一致&#xff0c; 情况一&#xff1a;部署到测试机上&#xff0c;利用本机登陆网页&#xff0c;sessionid和jsessionid不一样。 情况二&#xff1a;部署在本机&#xff0c;本机登陆页面&#xff0c;sessionid和js…

关于两次访问接口的sessionid不一致问题

在测试验证邮箱、注册逻辑时&#xff0c;出现验证码错误的问题。验证码是存放在session内的&#xff0c;在排除了逻辑代码的问题后&#xff0c;检查出这两次访问接口的sessionid并不一致&#xff0c;而在swagger测试接口时是一致的。因此我比较了swagger与ajax请求/响应头的区别…

cookie、session、sessionid 与jsessionid

cookie、session、sessionid 与jsessionid&#xff0c;要想明白他们之间的关系&#xff0c;下面来看个有趣的场景来帮你理解。 我们都知道银行&#xff0c;银行的收柜台每天要接待客户存款/取款业务&#xff0c;可以有几种方案&#xff1a; 凭借柜台职员的记忆&#xff0c;由收…

如何根据sessionID获取session解决方案

点个赞&#xff0c;看一看&#xff0c;好习惯&#xff01;本文 GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录&#xff0c;这是我花了3个月总结的一线大厂Java面试总结&#xff0c;本人已拿腾讯等大厂offer。 另外&#xff0c;原创文章首发在我的个人博客&#…

sessionId的生成过程和过期时间

支持作者 最便宜的卫生纸 浏览器第一次请求服务器时&#xff0c;服务器会生成一个sessionId&#xff0c;并返回给浏览器&#xff0c;这个sessionId会被保存在浏览器的会话cookie中。如下图 在浏览器不关闭的情况下&#xff0c;之后的每次请求请求头都会携带这个sessionId到服务…

session,sessionid,cookie之间的关系解析

session&#xff0c;sessionid&#xff0c;cookie之间的关系解析 文章目录 session&#xff0c;sessionid&#xff0c;cookie之间的关系解析1.简介2.session和cookie定义&#xff0c;创建&#xff0c;周期和联系2.1cookie2.2session 3.如果禁用cookie后&#xff0c;如何解决账号…

dubbox拦截器配置

dubbo是一个被国内很多互联网公司广泛使用的开源分布式服务框架&#xff0c;即使从国际视野来看应该也是一个非常全面的SOA基础框架。作为一个重要的技术研究课题&#xff0c;在当当网我们根据自身的需求&#xff0c;为Dubbo实现了一些新的功能&#xff0c;并将其命名为Dubbox&…