1. 实验题目:词法分析
实验目的
- 根据PL/0语言的文法规范,编写PL/0语言的词法分析程序;或者调研词法分析程序的自动生成工具LEX或FLEX,设计并实现一个能够输出单词序列的词法分析器。
- 通过设计调试词法分析程序,实现从源程序中分出各种单词的方法;加深对课堂教学的理解;提高词法分析方法的实践能力。
- 掌握从源程序文件中读取有效字符的方法和产生源程序的内部表示文件的法。
- 掌握词法分析的实现方法。上机调试编出的词法分析程序。
实验内容
已给PL/0语言文法,输出单词符号(关键字、专用符号以及其它标记)。
实验要求
- 把词法分析器设计成一个独立一遍的过程。
- 词法分析器的输出形式采用二元式序列,即:(单词种类,单词的值)
输入输出
输入:
const a=10;
var b,c;
begin
read(b);
c:=a+b;
write( c);
end.
输出:
(constsym, const)
(ident , a)
(eql, =)
(number, 10)
(semicolon, ; )
(varsym, var)
(ident,b)
(comma, ,)
(ident, c)
(semicolon, ; )
(beginsym, begin)
(readsym, read )
(lparen,( )
(ident, b)
(rparen, ))
(semicolon, ; )
(ident, c)
(becomes, := )
(ident, a)
(plus, +)
(ident,b )
(semicolon, ; )
(writesym,write)
(lparen, ( )
(ident, c)
(rparen,) )
(endsym, end )
(period, .)
2. 设计思想
基本字:
单词(编码) | 正规式r |
---|---|
begin(beginsym) | begin |
call(callsym) | call |
const(constsym) | const |
do(dosys) | do |
end(endsym) | end |
if(ifsym) | if |
odd(oddsym) | odd |
procedure(proceduresym) | procedure |
read(readsym) | read |
var(varsym) | var |
while(whilesym) | while |
write(writesym) | write |
then(thensym) | then |
标识符: | |
单词(编码) | 正规式r |
----- | ----- |
<标识符>(ident) | (字母)(字母 |数字)* |
常数: | |
单词(编码) | 正规式r |
----- | ----- |
<常数>(ident) | (数字)(数字)* |
运算符: | |
单词(编码) | 正规式r |
----- | ----- |
+(plus) | + |
-(minus) | - |
*(times) | * |
/(slash) | / |
=(eql) | = |
<>(neq) | <> |
<(lss) | < |
<=(leq) | <= |
(gtr)|>
=(geq)|>=
:=(becomes)|:=
界符:
单词(编码)| 正规式r
-----|-----
( (lparen)| (
) (rparen)| )
, (comma)| ,
; (semicolon)| ;
. (period)| .
3.算法流程
- 词法分析程序打开源文件,读取文件内容,直至遇上文件结束符,然后读取结束。
- 接下来就要对源文件从头到尾进行扫描了,从头开始扫描,这个时候扫描程序首先要询问当前的字符是不是空格,若是空格,则继续扫描下一个字符,直至不是空格。然后询问这个字符是不是字母,若是则进行标识符和保留字的识别;若这个字符为数字,则进行数字的判断。否则,依次对这个字符可能的情况进行判断(界符和运算符),若将所有可能都走了一遍还是没有知道它是谁,则认定为错误符号,输出该无法识别error,程序结束。每次成功识别了一个单词后,单词都会存在word1[]数组中,然后字符指针往后移,进行下一个单词的识别。
- 主控程序需要负责对每次识别的种别码进行判断,对于不同的单词种别做出不同的反应,直至文件结束。
- 本次实验我采用了map这个STL关联容器,主要是考虑到词法分析中的数据映射的关系,因此采用这种结构。map提供一对一的数据处理能力,其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值。这个容器是非常方便使用的,对于查找可以直接使用迭代器进行,利用find()函数,若一直到末尾都未找到,则是不能识别或为标识符。
4. 源程序
#include<bits/stdc++.h>
using namespace std;
map<string,string> word;//应用map数据结构形成一个string->string的对应
std::map<string,string>::iterator it;//用来遍历整个对应关系的迭代器
void map_init(){//对应关系进行初始化
word["begin"]="beginsym";
word["call"]="callsym";
word["const"]="constsym";
word["do"]="dosym";
word["end"]="endsym";
word["if"]="ifsym";
word["odd"]="oddsym";
word["procedure"]="proceduresym";
word["read"]="readsym";
word["then"]="thensym";
word["var"]="varsym";
word["while"]="whilesym";
word["write"]="writesym";
word["+"]="plus";
word["-"]="minus";
word["*"]="times";
word["/"]="slash";
word["="]="eql";
word["<>"]="neq";
word["<"]="lss";
word["<="]="leq";
word[">"]="gtr";
word[">="]="geq";
word[":="]="becomes";
word["("]="lparen";
word[")"]="rparen";
word[","]="comma";
word[";"]="semicolon";
word["."]="period";
}
int main(){
map_init();//初始化
char ch;
char a;
string word1;//string变量识别单词
string str;//string变量进行字符识别
ifstream infile("F:\\编译原理\\第一次实验\\analysis.txt");//文件输入流
ofstream outfile("F:\\编译原理\\第一次实验\\result.txt");//文件输出流
ostringstream buf;
while(buf&&infile.get(ch)) buf.put(ch);//将文件中的字符读出来
str= buf.str();//将得到的字符储存到string类型变量中
int csize=str.length();
for(int i=0;i<csize;i++){//对整个字符串进行遍历
while(str[i]==' '||str[i]=='\n') i++;//若最开始为空格或换行符,则将指针的位置往后移
if(isalpha(str[i])){//对标识符和基本字进行识别,调用库函数isalpha()
word1=str[i++];
while(isalpha(str[i])||isdigit(str[i])){
word1+=str[i++];
}
it=word.find(word1);
if(it!=word.end()){//判断是不是基本字,若为基本字则进行输出
cout<<"("<<word[word1]<<","<<word1<<")"<<endl;
}
else{//否则直接输出
cout<<"(ident"<<","<<word1<<")"<<endl;
}
i--;
}
else if(isdigit(str[i])){//判断是不是常数,调用库函数isdigit()
word1=str[i++];
while(isdigit(str[i])){
word1+=str[i++];
}
if(isalpha(str[i])){
cout<<"error!"<<endl;
break;
}
else{
cout<<"(number"<<","<<word1<<")"<<endl;
}
i--;
}else if(str[i]=='<'){//对<,<=分别进行判断
word1=str[i++];
if(str[i]=='>'){
word1+=str[i];
cout<<"("<<word[word1]<<","<<word1<<")"<<endl;
}else if(str[i]=='='){
word1+=str[i];
cout<<"("<<word[word1]<<","<<word1<<")"<<endl;
}else if(str[i]!=' '||!isdigit(str[i])||!isalpha(str[i])){
cout<<"("<<word[word1]<<","<<word1<<")"<<endl;
}else{
cout<<"error!"<<endl;
break;
}
i--;
}else if(str[i]=='>'){//对>,>=分别进行判断
word1=str[i++];
if(str[i]=='='){
word1+=str[i];
cout<<"("<<word[word1]<<","<<word1<<")"<<endl;
}else if(str[i]!=' '||!isdigit(str[i])||!isalpha(str[i])){
cout<<"("<<word[word1]<<","<<word1<<")"<<endl;
}else{
cout<<"error!"<<endl;
break;
}
i--;
}else if(str[i]==':'){//对:=进行判断
word1=str[i++];
if(str[i]=='='){
word1+=str[i];
cout<<"("<<word[word1]<<","<<word1<<")"<<endl;
}else{
cout<<"error!"<<endl;
break;
}
i--;
}else{//对其他的基本字依次进行判断
word1=str[i];
it=word.find(word1);
if(it!=word.end()){
cout<<"("<<word[word1]<<","<<word1<<")"<<endl;
}else{
cout<<"error!"<<endl;
break;
}
}
}
infile.close();
return 0;
}
5. 调试数据
待输入的文件流:
输出数据:
说明:如上实验仅符合当时实验要求的相关条件,其他的需求略微进行更改就行,思想是一样的,还是很简单的。输入输出自己按自己需求更改即可。