-
---------------------------------------------------
-
- 第一组输入
-
---------------------------------------------------
-
var a,b;
-
begin
-
a := 10;
-
b := a + 20;
-
write(b);
-
end.
-
---------------------------------------------------
-
- 第一组输出
-
---------------------------------------------------
-
varsys, "var"
-
ident, "a"
-
comma, ","
-
ident, "b"
-
semicolon, ";"
-
beginsys, "begin"
-
ident, "a"
-
becomes, ":="
-
number, "10"
-
semicolon, ";"
-
ident, "b"
-
becomes, ":="
-
ident, "a"
-
plus, "+"
-
number, "20"
-
semicolon, ";"
-
writesys, "write"
-
lparen, "("
-
ident, "b"
-
rparen, ")"
-
semicolon, ";"
-
endsys, "end"
-
period, "."
-
---------------------------------------------------
-
- 第二组输入
-
---------------------------------------------------
-
const a = 10;
-
var b,c;
-
procedure p;
-
begin
-
c:=b+a;
-
end;
-
begin
-
read(b);
-
while b#0 do
-
begin
-
call p; write(2*c); read(b);
-
end;
-
end.
-
---------------------------------------------------
-
- 第二组输出
-
---------------------------------------------------
-
constsys, "const"
-
ident, "a"
-
equal, "="
-
number, "10"
-
semicolon, ";"
-
varsys, "var"
-
ident, "b"
-
comma, ","
-
ident, "c"
-
semicolon, ";"
-
proceduresys, "procedure"
-
ident, "p"
-
semicolon, ";"
-
beginsys, "begin"
-
ident, "c"
-
becomes, ":="
-
ident, "b"
-
plus, "+"
-
ident, "a"
-
semicolon, ";"
-
endsys, "end"
-
semicolon, ";"
-
beginsys, "begin"
-
readsys, "read"
-
lparen, "("
-
ident, "b"
-
rparen, ")"
-
semicolon, ";"
-
ident, "while"
-
ident, "b"
-
#, "#"
-
number, "0"
-
dosys, "do"
-
beginsys, "begin"
-
callsys, "call"
-
ident, "p"
-
semicolon, ";"
-
writesys, "write"
-
lparen, "("
-
number, "2"
-
multiply, "*"
-
ident, "c"
-
rparen, ")"
-
semicolon, ";"
-
readsys, "read"
-
lparen, "("
-
ident, "b"
-
rparen, ")"
-
semicolon, ";"
-
endsys, "end"
-
semicolon, ";"
-
endsys, "end"
-
period, "."
-
---------------------------------------------------
-
- 第三组输入
-
---------------------------------------------------
-
var x,y;
-
procedure p;
-
var a;
-
procedure q;
-
var b;
-
begin
-
b:=10;
-
end;
-
procedure s;
-
var c,d;
-
procedure r;
-
var e,f;
-
begin
-
call q;
-
end;
-
begin
-
call r;
-
end;
-
begin
-
call s;
-
end;
-
begin
-
call p;
-
end.
-
---------------------------------------------------
-
- 第三组输出
-
---------------------------------------------------
-
varsys, "var"
-
ident, "x"
-
comma, ","
-
ident, "y"
-
semicolon, ";"
-
proceduresys, "procedure"
-
ident, "p"
-
semicolon, ";"
-
varsys, "var"
-
ident, "a"
-
semicolon, ";"
-
proceduresys, "procedure"
-
ident, "q"
-
semicolon, ";"
-
varsys, "var"
-
ident, "b"
-
semicolon, ";"
-
beginsys, "begin"
-
ident, "b"
-
becomes, ":="
-
number, "10"
-
semicolon, ";"
-
endsys, "end"
-
semicolon, ";"
-
proceduresys, "procedure"
-
ident, "s"
-
semicolon, ";"
-
varsys, "var"
-
ident, "c"
-
comma, ","
-
ident, "d"
-
semicolon, ";"
-
proceduresys, "procedure"
-
ident, "r"
-
semicolon, ";"
-
varsys, "var"
-
ident, "e"
-
comma, ","
-
ident, "f"
-
semicolon, ";"
-
beginsys, "begin"
-
callsys, "call"
-
ident, "q"
-
semicolon, ";"
-
endsys, "end"
-
semicolon, ";"
-
beginsys, "begin"
-
callsys, "call"
-
ident, "r"
-
semicolon, ";"
-
endsys, "end"
-
semicolon, ";"
-
beginsys, "begin"
-
callsys, "call"
-
ident, "s"
-
semicolon, ";"
-
endsys, "end"
-
semicolon, ";"
-
beginsys, "begin"
-
callsys, "call"
-
ident, "p"
-
semicolon, ";"
-
endsys, "end"
-
period, "."
简单PL/0词法分析
一、功能描述:
编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的内部编码及单词符号。(遇到错误时可显示“Error”,然后跳过错误部分继续显示)。
二、定义:
识别保留字:var、const、begin、end、while、do、read、
write、if、then、procedure、call
其他的都识别为标识符;
常数为无符号整形数;
运算符包括:+ - * / = > < >= <= #
分隔符包括:, ; ( )
Pl/0源程序由“.”结束。
三、程序结构描述:
全局变量:
set<string> sys; —— 存储系统保留字集合
set<char> opChar1; —— 存储运算符的第一个字符集合
map<char,string> split; —— 存储分隔符
map<string,string> op; —— 存储运算符
程序分成如下几个函数:
void init() ——初始化函数:
初始化各个类型中可能出现的值,即初始化识别保留字、运算符和分隔符的集合。
void println(string s, char ch)、void println(string s1, string s2) 打印函数
void printlnError()——打印错误信息
bool isNumber(char ch) ——判定一个字符是否是数字
bool isNumber(string s) ——判定一个字符串是否是整数
bool isLetter(char ch) ——判定一个字符是否是字母
bool isIdent(string s) ——判定一个字符串是否是标识符
void splitWord(string &str, char ch = 0) ——划分单词
传入一个字符ch,如果它是运算符或分隔符的第一个字符,或者是0(表示到了行尾),是则解析存储于公共变量str的字符串输出,同时判定该字符是否是一个分隔符。如果不是则将该字符添加到公共变量str的末尾,再次判定字符串是否是运算符,如果不是则留待下次解析。
void readFile(string filePath) ——读取文件内容
传入一个字符串filePath,用来指定要打开的文件名。打开文件读入字符串直到文件结束或读到字符串为“.”。对每个字符串,从第一个字符开始调用splitWord函数解析单词,直到字符串都拆解完毕。
main ——主函数
处理输入文件名的部分,若输入为“q”则退出循环,否则对指定文件进行词法分析。
程序总体执行流程图
拆分单词流程图
四、实验总结:
完成总使用四课时:
其中纸上时间 一课时,编程一课时,调试两课时。
问题:
- 刚开始时考虑欠缺,只考虑到了各个单词间会有空格符分隔开来的情况,导致当输入形如“a=b”这类将单词连接起来的语句时就会出错。
在每次输入字符串时,细化到对字符串中的各个字符逐步进行解析。
- 未考虑到“:=”、“>=”、“<=”这种不止一个字符的运算符的情况。
多写了一层判定。
- 未考虑到代码是否违法。
增加了对是否是合法数字和合法标识符的判定,
另外在”.”后面若还有内容也判定为错误(即使是空行 = =)。
评价:
程序中的判定过多,目的不够明确,需要重构。
由于没有做好纸上设计,导致中途发现较多错误,调试时间过长。
收获:
了解到一些词法分析的知识,但还需要理解书后的代码以提高水平。
* 附件1:程序源码
-
#include<iostream>
-
#include<fstream>
-
#include<string>
-
#include<map>
-
#include<set>
-
using namespace std;
-
-
set<string> sys; // 存储系统保留字集合
-
set<char> opChar1; // 存储运算符首字符
-
-
map<char,string> split; // 存储分隔符集合
-
map<string,string> op; // 存储运算符集合
-
-
/* 初始化集合 */
-
void init(){
-
// 初始化系统保留字集合
-
sys.insert("var");
-
sys.insert("begin");
-
sys.insert("end");
-
sys.insert("read");
-
sys.insert("write");
-
sys.insert("const");
-
sys.insert("do");
-
sys.insert("if");
-
sys.insert("then");
-
sys.insert("procedure");
-
sys.insert("call");
-
-
// 存储运算符首字符
-
opChar1.insert('+');
-
opChar1.insert('-');
-
opChar1.insert('*');
-
opChar1.insert('/');
-
opChar1.insert('#');
-
opChar1.insert('=');
-
opChar1.insert(':');
-
opChar1.insert('>');
-
opChar1.insert('<');
-
-
// 初始化分隔符集合
-
split[',']="comma";
-
split[';']="semicolon";
-
split['(']="lparen";
-
split[')']="rparen";
-
split['.']="period";
-
-
// 初始化运算符集合
-
op["+"]="plus";
-
op["-"]="minus";
-
op["*"]="multiply";
-
op["/"]="divide";
-
op["="]="equal";
-
op[">"]="greater";
-
op["<"]="less";
-
op[":="]="becomes";
-
op[">="]="ge";
-
op["<="]="le";
-
op["#"]="#";
-
}
-
-
/* 打印错误信息 */
-
void printError(){
-
cout<<"Error"<<endl;
-
}
-
-
/* 打印字符串类型 */
-
void println(string s1, string s2){
-
cout<<s1<<", \""<<s2<<"\""<<endl;
-
}
-
-
/* 打印字符类型 */
-
void println(string s, char ch){
-
cout<<s<<", \""<<ch<<"\""<<endl;
-
}
-
-
/* 判断是否是一个数字字符 */
-
bool isNumber(char ch){
-
return ch>='0'&&ch<='9';
-
}
-
-
/* 判断是否是一个数字字符串 */
-
bool isNumber(string str){
-
for(int i=0; i<str.size(); i++){
-
if(!isNumber(str[i])){
-
return false;
-
}
-
}
-
return true;
-
}
-
-
/* 判断是否是一个字母 */
-
bool isLetter(char ch){
-
if(ch>='A'&&ch<='Z')return true;
-
if(ch>='a'&&ch<='z')return true;
-
return false;
-
}
-
-
/* 判断是否是一个标识符字符串 */
-
bool isIdent(string str){
-
if(!isLetter(str[0]))return false; // 标识符首字符应是字母
-
for(int i=1; i<str.size(); i++){ // 标识符其他字符应是字母或数字
-
if(!isLetter(str[i])&&!isNumber(str[i])){
-
return false;
-
}
-
}
-
return true;
-
}
-
-
/* 词法分析 */
-
void splitWord(string &str, char ch = 0){
-
if(ch==0||opChar1.find(ch)!=opChar1.end()||split.find(ch)!=split.end()){
-
// 若输入字符是行尾/运算符首字符/分隔符 则
-
if(str.size()>0){ // 若保存的字符串不为空 则
-
if(sys.find(str)!=sys.end()){ // 若字符串是系统保留字 输出
-
println(str+"sys", str);
-
}else{ // 否则判断 字符是否是“=”,而保留的串是“:><”中的一种(运算符的首字符)
-
if(ch=='='&&str.size()==1&&(string(":><").find(str))>=0){
-
str = str.append(1,ch); // 是则输出该运算符类型
-
println(op[str],str);
-
str=""; // 清空保留的串并返回
-
return;
-
}
-
if(isNumber(str)){ // 判断该字符串是否是数字 是则输出
-
println("number",str);
-
}else{
-
if(isIdent(str)){ // 否则判断该字符串是否是合法的标识符,是则输出
-
println("ident",str);
-
}else{
-
printError(); // 否则报错
-
}
-
}
-
}
-
str = ""; // 清空字符串
-
}
-
if(split.find(ch)!=split.end()){ // 该字符是否是分隔符
-
println(split[ch],ch); // 打印分隔符并返回
-
str="";
-
return;
-
}
-
}
-
str = str.append(1,ch); // 都不是则将字符存到字符串末尾
-
if(op.find(str)!=op.end()){ // 判定该字符是否是运算符 是则输出
-
if(str.size()==1&&(str[0]=='>'||str[0]=='<'))return;
-
println(op[str],str);
-
str="";
-
}
-
}
-
-
/* 读取指定的文件内容并对其进行词法分析 */
-
void readFile(string filePath){
-
fstream file;
-
file.open(filePath.c_str(),ios_base::in);
-
-
if(!file){ // 如果文件不存在则报错并返回
-
printError();
-
return;
-
}
-
-
char ch;
-
string s, str;
-
while(file>>s){ // 从文件中读取字符串
-
for(int i=0; i<s.size(); i++){ // 对每个字符进行分析
-
ch = s[i];
-
splitWord(str, ch);
-
}
-
splitWord(str); // 最后再进行一次词法分析
-
if(str==".")break; // 当最后为“.”时退出循环
-
if(str[0])printError(); // 若一行未解析完毕 表示有错误不能解析 报错
-
str=""; // 清空该行字符串
-
}
-
-
if(file>>s){ // 若遇到“.”后还可以读入 报错
-
cout<<"Error"<<endl;
-
}
-
}
-
-
/***主函数***/
-
int main(){
-
init();
-
string filePath;
-
-
while(true){
-
cout<<"please input filePath(enter q to quit): ";
-
cin>>filePath;
-
-
if(filePath=="q")break; // 如果输入q则退出
-
-
readFile(filePath); // 读取文件内容并分析词法
-
-
cout<<endl;
-
}
-
-
return 0;
-
}
*附件2:样例输入输出