0.PL/0文法
〈程序〉→〈分程序〉.
〈分程序〉→ [<常量说明部分>][<变量说明部分>][<过程说明部分>]〈语句〉
<常量说明部分> → CONST<常量定义>{ ,<常量定义>};
<常量定义> → <标识符>=<无符号整数>
<无符号整数> → <数字>{<数字>}
<变量说明部分> → VAR<标识符>{ ,<标识符>};
<标识符> → <字母>{<字母>|<数字>}
<过程说明部分> → <过程首部><分程序>;{<过程说明部分>}
<过程首部> → procedure<标识符>;
<语句> → <赋值语句>|<条件语句>|<当型循环语句>|<过程调用语句>|<读语句>|<写语句>|<复合语句>|<空>
<赋值语句> → <标识符>:=<表达式>
<复合语句> → begin<语句>{ ;<语句>}<end>
<条件> → <表达式><关系运算符><表达式>|odd<表达式>
<表达式> → [+|-]<项>{<加减运算符><项>}
<项> → <因子>{<乘除运算符><因子>}
<因子> → <标识符>|<无符号整数>|(<表达式>)
<加减运符> → +|-
<乘除运算符> → *|/
<关系运算符> → =|#|<|<=|>|>=
<条件语句> → if<条件>then<语句>
<过程调用语句> → call<标识符>
<当型循环语句> → while<条件>do<语句>
<读语句> → read(<标识符>{ ,<标识符>})
<写语句> → write(<表达式>{,<表达式>})
<字母> → a|b|c…x|y|z
<数字> → 0|1|2…7|8|9
1.PL/0语言建立一个词法分程序GETSYM(函数)
要求:
把关键字、算符、界符称为语言固有的单词,标识符、常量称为用户自定义的单词。为此设置三个全程量:SYM,ID,NUM 。
SYM:存放每个单词的类别,为内部编码的表示形式。
ID:存放用户所定义的标识符的值,即标识符字符串的机内表示。
NUM:存放用户定义的数。
GETSYM要完成的任务:
- 滤掉单词间的空格。
- 识别关键字,用查关键字表的方法识别。当单词是关键字时,将对应的类别放在SYM中。如IF的类别为IFSYM,THEN的类别为THENSYM。
- 识别标识符,标识符的类别为IDENT,IDENT放在SYM中,标识符本身的值放在ID中。关键字或标识符的最大长度是10。
- 拼数,将数的类别NUMBER放在SYM中,数本身的值放在NUM中。
- 拼由两个字符组成的运算符,如:>=、<=等等,识别后将类别存放在SYM中。
- 打印源程序,边读入字符边打印。
由于一个单词是由一个或多个字符组成的,所以在词法分析程序GETSYM中定义一个读字符过程GETCH。
2.自己的实现
将源代码放入文件“ex1.txt”,大小写转换及去掉注释后的代码放入“ex1_1.txt”,分析之后的结果放入“lex.txt”并且在命令行中打印出来。对于关键字、算符、界符,放在了一个表中,在表中的位置即为各自的类别。对于标识符和数字,其类别固定。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <iomanip>
using namespace std;
//SYM:存放每个单词的类别,为内部编码的表示形式。
//ID:存放用户所定义的标识符的值,即标识符字符串的机内表示。
//NUM:存放用户定义的数。
//#define size 120
char symTB[29][15] = {
"const","var","procedure","begin","end","odd","if","then","call","while","do","read","write",//0-12
"+","-","*","/","=","#","<","<=",">",">=",":=",";",",",".","(",")"//13-28
};//关键字与算符界符表
//int sym = 0;//指向symTB的指针
#define NUMBER 29//数字的种类
#define IDENT 30//标识符的种类
#define idnum 50//词法分析构造的标识符表的大小
#define numnum 50//词法分析构造的数字表的大小
char idTB[idnum][10] = { "" };//标识符表
int id = 0;//标识符表指针
char numTB[numnum][10] = { "" };//数字表
int num = 0;//数字表指针
char ch;//当前读入的字符
char strToken[15] = "";//当前读入的字符串
int p = 0;//当前读入的字符串的指针
int code, value;//返回信息
char other[] = "--";//用于输出
char x[50];//用于输出
//******************************************
//判断是否为字母
bool IsLetter(char letter)
{
if ((letter >= 'a'&&letter <= 'z') || (letter >= 'A'&&letter <= 'Z'))
return true;
else
return false;
}
//******************************************
//判断是否为数字
bool IsDigit(char digit)
{
if (digit >= '0'&&digit <= '9')
return true;
else
return false;
}
//******************************************
//查找关键字表,找到返回类别(位置+1)
int Retract(char*te)
{
for (int i = 0; i < 13; i++)
{
if (strcmp(te, symTB[i]) == 0)
{
return i + 1;
}
}
return -1;
}
//******************************************
//判断是否为算符或界符,找到返回位置+1,否则返回-1
int IsSoj(char soj)
{
for (int i = 13; i < 29; i++)
{
if (soj == symTB[i][0])
{
return i + 1;
}
}
return -1;
}
//******************************************
//判断是否为算符或界符,找到返回位置+1,否则返回-1
int IsSoj(char *soj)
{
for (int i = 13; i < 29; i++)
{
if (strcmp(soj, symTB[i]) == 0)
{
return i + 1;
}
}
return -1;
}
//******************************************
//从标识符表中查找标识符,如果没有则插入,最后返回指针(位置)
int InsertId(char*te)
{
for (int i = 0; i < id; i++)
{
if (strcmp(te, idTB[i]) == 0)
{
return i;
}
}
strcpy_s(idTB[id++], strlen(te) + 1, te);
return id - 1;
}
//******************************************
//从数字表表中查找数字,如果没有则插入,最后返回指针(位置)
int InsertNum(char*te)
{
for (int i = 0; i < id; i++)
{
if (strcmp(te, numTB[i]) == 0)
{
return i;
}
}
strcpy_s(numTB[num++], strlen(te) + 1, te);
return num - 1;
}
//******************************************
//将一个大写字符转换为小写字符
char tran(char te)
{
if (te >= 'A'&&te <= 'Z')
{
te = te + 32;
return te;
}
else
return te;
}
//******************************************
//过滤注释,并且统一将大写转换为小写
void deal(ifstream *sourfile, ofstream *destfile)
{
char tempx;
do
{
tempx = sourfile->get();
if (tempx == '/')
{
char tempy = sourfile->get();
if (tempy == '/')
{
while (sourfile->get() != 10);
tempx = sourfile->get();
}
else if (tempy == '*')
{
char tempz1 = sourfile->get();
char tempz2 = sourfile->get();
while (!(tempz1 == '*'&&tempz2 == '/'))
{
tempz1 = tempz2;
tempz2 = sourfile->get();
if (tempz1 == EOF || tempz2 == EOF)
{
tempx = EOF;
break;
}
}
}
else
{
*destfile << tran(tempx) << tran(tempy);
}
}
else
{
//if (tempx != 10 && tempx != 13 && tempx != 9 && tempx != EOF )
if (tempx != EOF)
*destfile << tran(tempx);
}
} while (tempx != EOF);
return;
}
//******************************************
//根据文件,每次处理一个字符串,并输出到文件以及终端
int getsym(ifstream *sourfile, ofstream *destfile)
{
ch = ' ';
while (ch != EOF)
{
p = 0;
sourfile->get(ch);
while (ch == ' ' || ch == 10)//过滤空格与换行
ch = sourfile->get();
if (ch == EOF)
{
return 1;
}
if (IsLetter(ch))//读到的是字母,则要么是关键字,要么是标识符
{
strToken[p++] = ch;
sourfile->get(ch);
while (IsDigit(ch) || IsLetter(ch))//如果是数字或字母,则继续读取
{
strToken[p++] = ch;
sourfile->get(ch);
}
sourfile->seekg(-1, ios::cur);//回退一格
code = Retract(strToken);//查找关键字表
if (code == -1)
{//没找到,插入标识符表
value = InsertId(strToken);
cout << left << setw(15) << idTB[value] << left << setw(15) << IDENT << left << setw(15) << value << "\n";
*destfile << left << setw(15) << idTB[value] << left << setw(15) << IDENT << left << setw(15) << value << "\n";
/*sprintf_s(x, 100, "%-15s%-15d%-15d\n", idTB[value], IDENT, value);
printf(x);
*destfile << x;*/
}
else
{
cout << left << setw(15) << symTB[code - 1] << left << setw(15) << code << left << setw(15) << other << "\n";
*destfile << left << setw(15) << symTB[code - 1] << left << setw(15) << code << left << setw(15) << other << "\n";
/*sprintf_s(x, 100, "%-15s%-15d%-15s\n", symTB[code - 1], code, other);
printf(x);
*destfile << x;*///使用sprintf或者printf输出产生了内存读写错误,待研究!!
}
}
else if (IsDigit(ch))
{
strToken[p++] = ch;
sourfile->get(ch);
while (IsDigit(ch))//如果是数字,则继续读取
{
strToken[p++] = ch;
sourfile->get(ch);
}
sourfile->seekg(-1, ios::cur);//回退一格
value = InsertNum(strToken);
string temp(strToken);
int num = atoi(temp.c_str());
cout << left << setw(15) << numTB[value] << left << setw(15) << NUMBER << left << setw(15) << value << "\n";
*destfile << left << setw(15) << numTB[value] << left << setw(15) << NUMBER << left << setw(15) << value << "\n";
/*sprintf_s(x, 100, "%-15s%-15d%-15d\n", numTB[value], NUMBER, value);
printf(x);
*destfile << x;*/
}
else if ((code = IsSoj(ch)) != -1)
{
strToken[p++] = ch;
if (ch == ':')
{
sourfile->get(ch);
if (ch == '=')
{
strToken[p++] = ch;
cout << left << setw(15) << strToken << left << setw(15) << code << left << setw(15) << other << "\n";
*destfile << left << setw(15) << strToken << left << setw(15) << code << left << setw(15) << other << "\n";
/*sprintf_s(x, 100, "%-15s%-15d%-15s\n", strToken, code, other);
printf(x);
*destfile << x;*/
}
else
{
sourfile->seekg(-1, ios::cur);//回退一格
printf("error\n");
return -1;
}
}
else if (ch == '>' || ch == '<')
{
sourfile->get(ch);
if (ch == '=')
{
strToken[p++] = ch;
code = IsSoj(strToken);
cout << left << setw(15) << strToken << left << setw(15) << code << left << setw(15) << other << "\n";
*destfile << left << setw(15) << strToken << left << setw(15) << code << left << setw(15) << other << "\n";
/*sprintf_s(x, 100, "%-15s-15d%-15s\n", strToken, code, other);
printf(x);
*destfile << x;*/
}
else
{
sourfile->seekg(-1, ios::cur);//回退一格
cout << left << setw(15) << strToken << left << setw(15) << code << left << setw(15) << other << "\n";
*destfile << left << setw(15) << strToken << left << setw(15) << code << left << setw(15) << other << "\n";
/*sprintf_s(x, 100, "%-15s%-15d%-15s\n", strToken, code, other);
printf(x);
*destfile << x;*/
}
}
else
{
cout << left << setw(15) << strToken << left << setw(15) << code << left << setw(15) << other << "\n";
*destfile << left << setw(15) << strToken << left << setw(15) << code << left << setw(15) << other << "\n";
/*sprintf_s(x, 100, "%-15s%-15d%-15s\n", strToken, code, other);
printf(x);
*destfile << x;*/
}
}
else
{
printf("error\n");
return -1;
}
ch = ' ';
memset(strToken, 0, sizeof(strToken));
memset(x, 0, sizeof(x));
}
return 1;
}
//******************************************
//词法分析入口,通过文件名对文件进行词法分析
bool lexanalysis(string filename) {
ifstream openfile;//读文件用
ofstream outfile;//写文件用
bool flag = false;
string pfilename = "../" + filename;
string tempfilename = pfilename;
//string tempfilename = pfilename.substr(0, pfilename.length() - 4)+ "_1.txt";
//ifstream openfile(pfilename);//以输入方式打开(读操作)
//ofstream outfile(tempfilename, ios::out);//以输出方式打开(写操作)
//if (!openfile.is_open())
//{
// cout << "未成功打开文件" << filename << endl;
// return flag;
//}
//deal(&openfile, &outfile);//
//openfile.close();
//outfile.close();
openfile.open(pfilename);//打开源文件进行读
tempfilename = pfilename.substr(0, pfilename.length() - 4) + "_lex.txt";
outfile.open(tempfilename, ios::out);//新建文件进行写
if (!openfile.is_open())
{
cout << "未成功打开文件" << filename << endl;
return flag;
}
int flagint = getsym(&openfile, &outfile);
openfile.close();
outfile.close();
if (flagint == 1)
flag = true;
return flag;
//printf("\n\n\n\n");
//printf("id table\n");
//for (int i = 0; i < id; i++)
//{
// printf("%-15s%-15d\n", idTB[i], i);
//}
//printf("\n\n\n\n");
//printf("num table\n");
//for (int i = 0; i < num; i++)
//{
// printf("%-15s%-15d\n", numTB[i], i);
//}
}