编译原理 词法分析实验
实验一:词法分析
一、实验目的
通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。
编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的内部编码及单词符号自身值。
二、实验预习提示
1、 词法分析器的功能和输出格式
词法分析器的功能是输入源程序,输出单词符号。词法分析器的单词符号常常表示成以下的二元式(单词种别码,单词符号的属性值)。本实验中,采用的是一类符号一种别码的方式。
2、 单词的BNF表示
<标识符>----> <字母><字母数字串>
<无符号整数>----> <数字><数字串>
<加法运算符>----> +
<减法运算符>----> -
等等
3、 模块结构(见课本P95-96)(可根据自己的理解适当修改)
三、实验过程和指导:
(一) 准备:
- 阅读课本有关章节,明确语言的语法,写出基本保留字、标识符、常数、运算符、分隔符和程序例。
- 初步编制好程序。
- 准备好多组测试数据。
(二) 上机:
(三) 程序要求:
1. 要求用C++Builder或者Dephi或者VB或者VC或者JAVA等可视化编程工具编写;要求有界面(即一般windows下应用程序界面)。
2. 输入为某语言源代码。
程序输入/输出示例:
如源程序为C语言。输入如下一段:
main()
{
int a,b;
a=10;
b=a+20;
}
要求输出如下(也可以以文件形式输出)。
(2,”main”)
(5,”(“)
(5,”)“)
(5,”{“}
(1,”int”)
(2,”a”)
(5,”,”)
(2,”b”)
(5,”;”)
(2,”a”)
(4,”=”)
(3,”10”)
(5,”;”)
(2,”b”)
(4,”=”)
(2,”a”)
(4,”+”)
(3,”20”)
(5,”;”)
(5,”)“) 注:为右大括号
要求(可根据实际情况加以扩充和修改):
识别保留字:if、int、for、while、do、return、break、continue等等;单词种别码为1。
其他的都识别为标识符;单词种别码为2。
常数为无符号数;单词种别码为3。
运算符包括:+、-、*、/、=、>、<等;可以考虑更复杂情况>=、<=、!= ;单词种别码为4。
分隔符包括: “,”“;”“(”“)”“{”“}”等; 单词种别码为5。
(四) 程序思路(仅供参考):
- 定义部分:定义常量、变量、数据结构。
- 初始化:从文件将源程序输入到字符缓冲区中。
- 取单词前:去掉多余空白。调用过程GETNB();
- 提取字符组成单词,利用课本转换图构造单词扫描过程SCAN(),需要根据实际情况加以修改。
- 判断单词的种别码,调用过程LOOKUP();
- 显示(导出)结果。
#include <iostream> #include <fstream> #include <cstdlib> #include <vector> #include <cstring> using namespace std; char *key[]={"auto","break","case","char","const","continue","default","do","double","else", "enum","extern","float","for","goto","if","int","long","register","return", "short","signed","sizeof","static","struct","switch","typedef","union","unsigned", "void","volatile","while"}; //保留字表(c语言32个保留字) vector<char> Token(0);//存放构成单词串 vector<char> Num(0); //存放构成数字串 vector<char> Yunsuan(0); //存放构成运算符串,如 ==,!=,<= 等。 vector<char> v(0);//存放源程序串 void openfile(void)//从文件里扫描源程序,放入向量v中。 { ifstream in_stream; in_stream.open("code.txt"); if(in_stream.fail()) { cout << "Input file opening error!" << endl; exit(1); } char next; while(! in_stream.eof()) { in_stream.get(next); v.push_back(next); } v.pop_back(); } int is_class(char ch)//判断一个字符是属于哪一类的。 { if(ch==' ' || ch=='\n' || ch=='\t') return 0;//空格符、换行符等 else if(ch>='0' && ch<='9') return 1;//数字类 else if((ch>='A' && ch<='Z') || (ch>='a' && ch<='z')) return 2;//字母类 else if(ch=='+' || ch=='-' || ch=='*' || ch=='/' || ch=='=' || ch=='>' || ch=='<' || ch=='!') return 3;//运算符类 else if(ch==',' || ch==';' || ch=='(' || ch==')' || ch=='{' || ch=='}') return 4;//分界符类 //else return -1;//其他非法字符 } void Getnum(void)//获取当前的第一个数字串,第一个符号必定是数字类 { Num.clear();//首先清空向量 int i=0; if(is_class(v[0])==1) while(is_class(v[i])==1) { Num.push_back(v[i]); i++; } } void Getnb(void)//获取当前的第一个单词串,第一个符号必定是字母类 { Token.clear(); int i=0; if(is_class(v[0])==2) while(is_class(v[i])==2 || is_class(v[i])==1) { Token.push_back(v[i]); i++; } } void Getyunsuan(void) { Yunsuan.clear(); int i=0; while(is_class(v[i])==3) { Yunsuan.push_back(v[i]); i++; } } int is_key(vector<char> &p)//判断字符串是否是保留字,是则返回1,否则返回0. { const int key_len=32; int i,flag=0; char str[100]; int p_len=p.size(); for(i=0; i<p_len; i++) { str[i]=p[i]; } str[i]='\0'; for(i=0; i<key_len; i++) { if(strcmp(str,key[i])==0) { flag=1; return 1; } } if(flag==0) return 0; } void print(vector<char> &q) { int i,len=q.size(); for(i=0; i<len; i++) { cout << q[i]; } cout << endl; } int main() { openfile(); vector<char>::iterator n=v.begin(); int i=0; while(! v.empty()) { if(is_class(v[i])==0) { v.erase(n); } else if(is_class(v[i])==1) { Getnum(); int len=Num.size(); cout << 3 << " " ; print(Num); while(len--) { v.erase(n); } } else if(is_class(v[i])==2) { Getnb(); int len=Token.size(); if(is_key(Token)==1) { cout << 1 << " " ; print(Token); } else { cout << 2 << " " ; print(Token); } while(len--) { v.erase(n); } } else if(is_class(v[i])==3) { Getyunsuan(); cout << 4 << " " ; print(Yunsuan); int len=Yunsuan.size(); while(len--) { v.erase(n); } } else if(is_class(v[i])==4) { cout << 5 << " " << v[i] << endl; v.erase(n); } else { cout << "待扫描程序含有非法字符!" << endl; break; } } return 0; }