类 Pascal语言词法分析实验(简易版)
一、 实验目的
设计、编制并调试一个词法分析程序,加深对词法分析原理的理解。
二、 实验要求
2.1 待分析的简单的词法
(1)关键字:
- begin if then while do end
- 所有的关键字都是小写。
(2)运算符和界符
- : = + - * / < <= <> > >= = ; ( ) #
(3)其他单词是标识符(ID)和整型常数(SUM),通过以下正规式定义:
- ID = letter (letter | digit)*
- NUM = digit digit*
(4)空格有空白、制表符和换行符组成。空格一般用来分隔ID、SUM、运算符、界符和关键字,词法分析阶段通常被忽略。
2.2 各种单词符号对应的种别码:
表2.1 各种单词符号对应的种别码
单词符号 | 种别码 | 单词符号 | 种别码 |
---|---|---|---|
begin | 1 | : | 17 |
if | 2 | := | 18 |
then | 3 | < | 20 |
while | 4 | <> | 21 |
do | 5 | <= | 22 |
end | 6 | > | 23 |
lettet(letter / digit)* | 10 | >= | 24 |
dight dight* | 11 | = | 25 |
+ | 13 | ; | 26 |
— | 14 | ( | 27 |
* | 15 | ) | 28 |
/ | 16 | # | 0 |
2.3 词法分析程序的功能:
输入:所给文法的源程序字符串。
输出:二元组(syn,token或sum)构成的序列。
其中:
- syn为单词种别码;
- token为存放的单词自身字符串;
- sum为整型常数;
- 例如:对源程序begin x:=9: if x>9 then x:=2*x+1/3; end #的源文件,经过词法分析后输出如下序列:
(1,begin)(10,x)(18,:=)(11,9)(26, ; )(2,if)……
设计思路:
Judge函数:
1.对所给的单词(字符数组)做出判断:
- 关键字,运算符和界符,标识符(ID),整型常数(SUM):
2.读到的字符串处理方法:
首先判断传入参数的第一个字符,分以下几种情况:
- 字母开头:判断是否为关键字,是则返回,不是则为标识符
- 数字开头的判断是否全为数字 是则处理后返回十进制数字大小,不是则返回错误
- 其他情况直接和运算符表做对比 是则返回运算符,不是则返回错误
3.判断后返回种别码
main()函数
- 读取文件(待分析的源代码)
- 从文件中取出一个个独立的单词(以分隔符为界限)
- 把单词作为参数调用Judge() 返回值对应的种别码
- 用上述得到的种别码和单词为参数调用print函数,输出结果到文件
Print函数:参数为单词和种别码
- 写文件模式为追加写(每次在文件末尾写,不覆原有内容)(ios::app)
代码实现:
#include<iostream>
#include <fstream>
using namespace std;
const int symbolNum =29;
char syn[symbolNum+1][10] {
"#","begin","if","then","while","do","end","","","",
"token","sum","","+","-","*","/" ,":" ,":=","" ,
"<" ,"<>" ,"<=" ,">" ,">=" ,"=" ,";" ,"(" ,")" ,"Error!!!"
};
char* GetToken() {
ifstream In("输入.txt");
char* A = new char[10];
for (int i = 0; i < 10; i++)
A[i] = '\0';
In >> A;
//cout << "\n读入" << A << endl;
In.close();
return A;
}
bool IsLetter(char a) {
if ((a >= 'a' && a <= 'z')|| (a >= 'A' && a <= 'Z'))
return 1;
else return 0;
}
bool IsDigit(char a) {
if (a >= '0' && a <= '9')
return 1;
else return 0;
}
int Judge(char Token[]) {
char beg = Token[0];
if (IsLetter(beg)) {//字母开头
for (int i = 0; Token[i] != '\0'; i++)
if (!IsDigit(Token[i]) && !IsLetter(Token[i]))
return symbolNum;//返回错误
for (int i = 0; i < 7; i++) {
if (beg == syn[i][0]) {
for (int j = 0; Token[j] != '\0'&& syn[i][j] != '\0'; j++) {
//for (int j = 0; ; j++) {
if (Token[j + 1] != syn[i][j + 1])
return 10;//是标识符
}
return i;//是关键字
}
}
return 10;//是标识符
}
else if (IsDigit(beg)) {//数字开头
for (int i = 0; Token[i] != '\0'; i++) {
if (!IsDigit(Token[i]))
return symbolNum;//返回错误(数字开头不全为数字)
}
return 11;//是数字
}
else {//符号开头
if (beg == '#' && Token[1] == '\0')
return 0;
for (int i = 13; i < symbolNum; i++) {
if (beg == syn[i][0]) {
bool flag = 1;
for (int j = 0; Token[j] != '\0' && syn[i][j] != '\0'; j++) {
//for (int j = 0; ; j++) {
if (Token[j + 1] != syn[i][j + 1]) {
flag = 0;
break;
}
}
if(flag)
return i;//是运算符
}
}
return symbolNum;//返回错误
}
}
void C_Print(int id, char* Token) {
//输出到文件
ofstream Out;
Out.open("输出.txt", ios::app);
if (id == symbolNum) Out << "Error ";
Out << "(" << id << ",";
for (int i = 0; Token[i] != '\0'; i++)
Out << Token[i];
Out << ")" << endl;
Out.close();
//以下为输出到控制台
if (id == symbolNum)cout << "Error ";
cout << "(" << id << ",";
for (int i = 0; Token[i] != '\0'; i++)
cout << Token[i];
cout << ")" << endl;
}
int main() {
ifstream In("输入.txt");
ofstream Out("输出.txt", ios::trunc);//清除之前的内容
while (!In.eof()) {
char* A = new char[10];
for (int i = 0; i < 10; i++)
A[i] = '\0';
In >> A;
int id = Judge(A);
C_Print(id, A);
}
In.close();
//char* T = GetToken();
// int id = Judge(T);
// C_Print(id, T);
}
文件:输出.txt 的内容(随便写的,测试用)
if ( a > 0 )
while ( a >= 9 ) :
a = a - 1 ;
#
else {
if ( b < 987 ) :
b ++ ;
} end
输出结果:
//from 输出.txt
(2,if)
(27,()
(10,a)
(23,>)
(11,0)
(28,))
(4,while)
(27,()
(10,a)
(24,>=)
(11,9)
(28,))
(17,:)
(10,a)
(25,=)
(10,a)
(14,-)
(11,1)
(26,;)
(0,#)
(10,else)
Error (29,{)
(2,if)
(27,()
(10,b)
(20,<)
(11,987)
(28,))
(17,:)
(10,b)
Error (29,++)
(26,;)
Error (29,})
(6,end)
(19,)