- C++ 开发 Web 服务框架
- 1 小时入门增强现实技术
- C++ 实现高性能内存池
- GDB 简明教程
- C++ 实现太阳系行星系统
- C++11/14 高速上手教程
- C 语言实现 Linux Shell 命令解释器
- C++ 打造 Markdown 解析器
- C 语言实现文件类型统计程序
- C 语言实现 Linux touch 命令
- C 语言入门教程
- C 语言实现多线程排序
- 多线程生产者消费者模型仿真停车场
- C++实现运动目标的追踪
- C 语言实现 Linux 网络嗅探器
- 100 行 C++ 代码实现线程池
- C 语言实现聊天室软件
- C 语言实现 Linux who 命令
- C 语言实现 Linux cp 命令
- C++实现第一人称射击游戏
- C++ 实现银行排队服务模拟
- 数据结构(新版)
- 软件工程(C 编码实践篇)
- C 语言制作简单计算器
- C 语言版 flappy_bird
- C 语言编写万年历
- C 语言版扫雷游戏
- C 语言实现一个支持 PHP 的简易 WEB 服务器
- C 语言制作 2048
- C 语言模拟 ATM 自动取款机系统
- Linux 系统编程
- C 语言利用 epoll 实现高并发聊天室
- C 语言快速实现五子棋
- C 语言实现 ping 程序
- 简单词法分析器(C++语言)
第 1 节 c 语言词法分析器
一、项目说明
1. 项目使用
本实验环境采用带桌面的 Ubuntu Linux 环境,实验中会用到桌面上的程序:
- LX 终端(LXTerminal): Linux 命令行终端,打开后会进入 Bash 环境,可以使用 Linux 命令
- GVim:非常好用的编辑器,最简单的用法可以参考课程 Vim 编辑器 。
2. 环境使用
使用 GVim 编辑器输入实验所需的代码及文件,使用 LX 终端(LXTerminal)运行所需命令进行操作。
实验报告可以在个人主页中查看,其中含有每次实验的截图及笔记,以及每次实验的有效学习时间(指的是在实验桌面内操作的时间,如果没有操作,系统会记录为发呆时间)。这些都是您学习的真实性证明。
4. 项目简介
项目目的:设计并实现一个包含预处理功能的词法分析程序,加深对编译中词法分析过程的理解。
项目要求:
1、实现预处理功能 源程序中可能包含有对程序执行无意义的符号,要求将其剔除。 首先编制一个源程序的输入过程,从键盘、文件或文本框输入若干行语句,依次存入输入缓冲区(字符型数据);然后编制一个预处理子程序,去掉输入串中的回车符、换行符和跳格符等编辑性文字;把多个空白符合并为一个;去掉注释。
2、实现词法分析功能 输入:所给文法的源程序字符串。 输出:二元组构成的序列。 具体实现时,可以将单词的二元组用结构进行处理。
3、待分析的 C 语言子集的词法 1)关键字 main if then while do static int double struct break else long switch case typedef char return const float short continue for void default sizeof do
所有的关键字都是小写。
2)运算符和界符 “ + - * / : := < <> <= > >= = ; ( ) #”
3)其他标记 ID 和 NUM 通过以下正规式定义其他标记: ID→letter(letter|digit)* NUM→digit digit* letter→a|…|z|A|…|Z digit→0|…|9…
4)空格由空白、制表符和换行符组成 空格一般用来分隔 ID、NUM、专用符号和关键字,词法分析阶段通常被忽略。
4、各种单词符号对应的种别码
功能流程图(代码实现思路基本根据流程图来的):
二、项目实现
实践出真知,只有在实践过程中才能发现不足,现在让我们打开命令行,键入命令新建一个 .cpp 的文本:
从流程图可以知道,我们需要判断字符是否为数字、字母、定界符、关键字 我们通过 IsDigit()、IsLetter()、IsSymbol()、IsKeyword() 去实现这个四个功能
第一个函数 IsDigit()
//判断是否为数字
bool IsDigit(char ch)
{
if(ch>='0'&&ch<='9')
return true;
return false;
}
第二个函数 IsLetter()
//判断是否为字母
bool IsLetter(char ch)
{
if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
return true;
return false;
}
第三个函数 IsSymbol()
//判断是否为定界符等
int IsSymbol(char ch)
{
for(int i=0; i<9; i++)
{
if(ch==symbol[i])
return i;
}
return -1;
}
第四个函数 IsKeyword()
//判断是否为关键字
int IsKeyword(string str)
{
for(int i=0; i<26; i++)
{
if(str==keyword[i])
{
return i;
}
}
return 25;
}
读者仔细阅读上面项目要求会发现,还有一个预处理的要求,需要合并空格,去掉注释的功能,下面我们就来完成合并空格的功能。
//空格处理
void HandleSpace(char a[])
{
int j=0;
memset(word,0,255);
temp=false;
for(int i=0; i<strlen(a); i++)
{
if(a[i]!=' ' && a[i]!='\t') //'\t'是 table 键
{
word[j++]=a[i];
temp=false;
}
else
{
if(!temp&&a[i]!='\t')
{
word[j++]=a[i];
temp=true;
}
}
}
}
然后是处理注释,这里我是将 //
注释进行了预处理, /* */
注释是在主程序中处理的>
//处理"//"注释
void prePro()
{
int j=0;
memset(tempstr,0,255);
for(int i=0; i<strlen(word); i++)
{
if(word[i]=='/'&&word[i+1]=='/')
{
while(i<strlen(word))
{
i++;
}
}
else {
tempstr[j++]=word[i];
}
}
}
这样整个程序的核心大部分就完成了,思路就是判断读入的第一个单词是否为字母,若为字母,则为关键字或者标识符,若为数字则为 NUM。
三、完整源码
整个程序的源代码如下:
/*
*author:leetao
*contact:leetao94cn@gmail.com
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
using namespace std;
//存放处理后的字符串
char tempstr[255]={};
//空格标志
bool temp=false;
//临时数组
char word[255]={};
//keyword 关键字
string keyword[26]={
"main","if","then","while","do","static","defualt","do","int","double","struct","break","else","long","swtich","case","typedf","char","return","const","float","short","continue","for","void","sizeof"};
int keyword_num[26]={1,2,3,4,5,6,39,40,7,8,9,10,11,
12,13,14,15,16,17,18,19,20,21,22,23,24};
//部分运算符,定界符等
char symbol[9]={'+','-','*','/','=',';','(',')','#'};
//对应的种码值
int symbol_num[9]={27,28,29,30,38,41,42,43,0};
//判断是否为字母
bool IsLetter(char ch)
{
if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
return true;
return false;
}
//判断是否为数字
bool IsDigit(char ch)
{
if(ch>='0'&&ch<='9')
return true;
return false;
}
//判断是否为定界符等
int IsSymbol(char ch)
{
for(int i=0; i<9; i++)
{
if(ch==symbol[i])
return i;
}
return -1;
}
//判断是否为关键字
int IsKeyword(string str)
{
for(int i=0; i<26; i++)
{
if(str==keyword[i])
{
return i;
}
}
//不是关键字即为 ID
return 25;
}
//空格处理
void HandleSpace(char a[])
{
int j=0;
memset(word,0,255);//需要清空,不然可能残留上次的字符串
temp=false;
for(int i=0; i<strlen(a); i++)
{
if(a[i]!=' ' && a[i]!='\t')
{
word[j++]=a[i];
temp=false;
}
else
{
if(!temp&&a[i]!='\t')
{
word[j++]=a[i];
temp=true;
}
}
}
}
//处理"//"注释
void prePro()
{
int j=0;
memset(tempstr,0,255);
for(int i=0; i<strlen(word); i++)
{
if(word[i]=='/'&&word[i+1]=='/')
{
while(i<strlen(word))
{
i++;
}
}
else {
tempstr[j++]=word[i];
}
}
}
int main()
{
char instr[255]={}; //接收输入字符串
bool flag=false; //多行注释标志,false 为未处于注释区域
string Token;//存放字符串
char *str=NULL;//存放每行的字符串
char delims[]=" ";//分割标志
freopen("test.cpp","r",stdin);
freopen("result.txt","w",stdout); //此行注释后,控制台输出,
//否则文本输出
while((gets(instr))!=NULL)
{
HandleSpace(instr);
prePro();
str=strtok(tempstr,delims);//分割字符串
while(str!=NULL)
{
//头文件,宏定义
if(*(str)=='#')
{
printf("#\n");
break;
}
for(int i=0; i<strlen(str);i++)
{
if(*(str+i)=='/')
{
if(*(str+i+1)=='*')
{
flag=true;
break;
}
}
//注释处理: */,注释区域结束
if(*(str+i)=='*'&&flag)
{
if(*(str+i+1)=='/')
{
flag=false;
i++;
break;
}
}
//标识符,关键词
if(IsLetter(*(str+i))&&(!flag))
{
// printf("进入标识符判断\n");
while(IsLetter(*(str+i))||IsDigit(*(str+i))
||*(str+i)=='_')
{
Token+=*(str+i);
i++;
}
if(IsKeyword(Token)!=25)
{
printf("%s---->%d\n",Token.c_str(),
keyword_num[IsKeyword(Token)]);
}
else printf("%s---->25\n",Token.c_str());
Token="";
// printf("退出标识符判断\n");
}
if(IsDigit(*(str+i))&&(!flag))
{
// printf("进入数字判断\n");
while(IsDigit(*(str+i)))
{
Token+=*(str+i);
i++;
}
printf("%s------>26\n",Token.c_str());
Token="";
}
//<,<=,<>
if(*(str+i)=='<'&&(!flag))
{
if(*(str+i)=='=') {printf("<=------>35\n");i++;}
if(*(str+i)=='>') {printf("<>------>34\n");i++;}
else printf("<------>33\n");
}
//>,>=
else if(*(str+i)=='>'&&(!flag))
{
if(*(str+i+1)=='=') {printf(">------>37\n");}
else printf(">-------36\n");
}
//:,:=
else if(*(str+i)==':'&&(!flag))
{
if(*(str+i+1)=='=') {printf(":=------->32\n");}
else printf(":-------->31\n");
}
//余下定界符等
else if(IsSymbol(*(str+i))!=-1&&(!flag))
{
printf("%c------->%d\n",*(str+i),
symbol_num[IsSymbol(*(str+i))]);
}
}
str=strtok(NULL,delims);
}
}
return 0;
}
这个代码完成了我们还需要一个程序,在当前目录下使用命令行:gvim test.cpp 新建一个测试程序 test.cpp 文件代码如下(读者也可以自行发挥):
#include<stdio.h>
int main()
{
//test
/* test */
for(int i=1;i<0;i++)
printf("%d",i);
return 0;
}
四、编译运行
自此准备工作都完成了,现在开始编译了:
g++ testword.cpp -o testword
注意是使用 g++ 而不是 gcc 编译,会出现 waring,不用管,gets() 函数在输入时没有限定字符串的长度,而 linux 是很严谨的,所以这里给出一 warning。 这个时候输入 ls,发现目录下已经出现编译成功的 testword 可运行程序,然后运行,成功运行结果如图:
有个 result.txt 的文件,打开它,内容如下:
本课程到此结束,谢谢学习,如有问题请留言,我会定期回复。
五、作业思考
实验楼环境中暂时无法输入中文字符,但是在实际生活应用中,中文是很常见的。考虑一下,如果遇到中文字符,词法分析器该怎么解决。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论