📄 合作.cpp
字号:
/*
* 程序名称:编译原理之词法分析器
* 输 入:Test.c C语言源程序文件
* 输 出:Const.txt 常量表 Sign.txt 标识符表 Result.txt 二元式结果
* 编写时间:2008-6-26
* 作 者:张文明 杨倩
* 编译环境:VC6.0_CN
* 操作系统:WinXP_SP2
* 程序版本:V1.0
* 备 注:此程序采用一缓冲方式读入程序源码,首先进行预处理去掉注释和无效空格
* 然后再进行详细的词法分析,为了便于后续处理,程序设置了几个表:符号
* 表和常数表。
* 注 意:此分析器只对C语言子集进行处理
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <conio.h>
#include <stdlib.h>
#include <iostream.h>
#define KEYWORD_LEN 32 //保留字个数
#define STR_MAX_LEN 300 //标识符最大长度
#define PRO_MAX_LEN 20480 //源程序最大长度
#define STB_MAX_LEN 1000 //符号表最大容量
#define CTB_MAX_LEN 1000 //常数表最大容量
#define ERROR 0 //错误
#define ID (KEYWORD_LEN+1) //标识符
#define CONST (KEYWORD_LEN+2) //常量
#define OPERAT (KEYWORD_LEN+3) //运算符
#define DIVIDE (KEYWORD_LEN+4) //界符
int errorLine=0;
char proBuffer[PRO_MAX_LEN] = ""; //存储程序代码的全局缓冲区
char ch; //读出来的当前字符
char wordget[STR_MAX_LEN]; //标识符 或 常量
int point = 0; //源程序当前位置指针
char signTab[STB_MAX_LEN][STR_MAX_LEN]; //符号表
int pointSTB = 0; //符号表指针
char constTab[CTB_MAX_LEN][STR_MAX_LEN]; //常量表
int pointCTB = 0; //常数表指针
char kwTab[KEYWORD_LEN][60]= //保留字表 ??字符指针数组??
{"PROGRAM","VAR","BEGIN","DIV","END","INTEGER",
"AND","ARRAY","CASE","CONST","DO","DOWNTO","ELSE",
"FILE","FOR","FUNTION","GOTO","IF","IN","LABEL",
"MOD","NIL","NOT","OF","OR","PACKED","PROCEDURE"};
char errorTab[][50]={ //错误代码表
/*0*/"未知错误", /*1*/"非法的字符", /*2*/"不正确的字符常量表达",
/*3*/"不正确的字符串表达", /*4*/"不正确的数字表达", /*5*/"注释丢失'*/'"};
typedef struct signDuality //二元表的定义
{
int kind;
int value;
}*pDualistic, Dualistic;
//函数声明
void pretreatment(); //预处理
void ProcError(int id); //错误
bool GetChar(); //获得一个字符不包括结束标记
bool GetBC(); //获得一个非空白字符
void Concat(char *str); //将ch连接到str后
int Reserve(char *str); //对str字符串查找保留字表 若是一个保留字-返回其编码 否则返回0
void Retract(); //将搜索指示器回调一个字符位置
int InsertId(char *str);//将str串以标识符插入符号表,并返回符号表指针
int InsertConst(char *str); //将str串以常数插入符号表,并返回常数表指针
bool wordAnalyse(pDualistic pDu); //词法分析 true正常
//end
//预处理 将缓冲区内的源代码去掉注释和无效空格【所谓的无效空格是指出现在非字符串
// 常量中的空格】
void pretreatment() //预处理
{int lines=0;
char tmp[PRO_MAX_LEN]; //先将处理结果保存到临时空间,这样虽然浪费空间但是节约
//时间
int tmpp = 0; //这个临时空间的末尾指针
bool flg;
char tmpc;
//去掉注释先
//注释有两种 一种是C++风格的// 另一种是C风格的/**/
point = 0;
do
{
flg = GetChar();
if(ch == '/')
{
flg = GetChar();
switch(ch) // case "/"and "*"
{
case '/':
do
{
flg = GetChar();
}while(!(ch == '\n' || flg == false));// || ch == '\0'));//注释一直到行尾或文件结束
if(ch == '\n')
Retract(); //归还换行函数
break;
case '*':
do
{
flg = GetChar();
tmpc = ch;
//为了保证出错处理程序能正确定位出错位置 保留注释中的换行
if(tmpc == '\n')
tmp[tmpp++] = tmpc;
flg = GetChar();
Retract(); //归还一个字符函数
}while(flg && !(flg && tmpc == '*' && ch == '/')); //不以*/
flg = GetChar();
if (!flg)
{
ProcError(5); //错误调用函数 5"注释丢失'
}
break;
default:
//不是任何一种注释
Retract(); //将搜索指示器回调一个字符位置
Retract(); //将搜索指示器回调一个字符位置 “//”或“*/”
GetChar(); //获得一个字符不包括结束标记
tmp[tmpp++] = ch;
flg = GetChar();
tmp[tmpp++] = ch; //tmpp临时空间的末尾指针
}
}
else
{
tmp[tmpp++] = ch;
}
}while(flg); //flg布尔变量,判断是否取得字符
tmp[tmpp] = '\0';
strcpy(proBuffer,tmp); // 新取得的字符送入缓冲区
}
void ProcError(int id) //错误
{
printf("\nError:第%d行,%s\n",errorLine, errorTab[id]);//errorTab[id]错误列表
}
bool GetChar()//获得一个字符不包括结束标记
{
if(point < PRO_MAX_LEN && proBuffer[point] != '\0')
{//如果当前下标合法且当前字符为结束标记则取字符增游标
ch = proBuffer[point++];
if (ch == '\n')
errorLine ++;
return true;
}
ch = '\0';
return false;
}
bool GetBC()//获得一个非空白字符
{
do
{
if(!GetChar()) //获取字符失败
{
ch = '\0';
return false;
}
}while(isspace(ch)); //直到获得一个非空白字符 //isspace(ch)判断字符ch是否为空白符
return true;
}
void Concat(char *str) //将ch连接到str后
{
int i;
for(i=0; str[i]; ++i);
str[i] = ch;
str[i+1] = '\0';
}
int Reserve(char *str)//对str字符串查找保留字表 若是一个保留字-返回其编码 否则返回0
{
int i;
for(i=0; i<KEYWORD_LEN; ++i) //从保留字表中查找str串
{
if(0 == strcmp(kwTab[i], str))
return i+1; //注意,这里加一原因是0值被错误标记占用
}
return 0;
}
void Retract()///char *ch//将搜索指示器回调一个字符位置
{
if(proBuffer[point] == '\n' && errorLine > 0)
errorLine --;
point --;
}
int InsertId(char *str)//将str串以标识符插入符号表,并返回符号表指针
{
int i;
for(i=0; i < pointSTB; ++i) // pointSTB符号表指针
if(0 == strcmp(signTab[i], str))
return i;
strcpy(signTab[pointSTB++], str);
return (pointSTB-1);
}
int InsertConst(char *str)//将str串以常数插入常量表,并返回常数表指针
{
int i;
for(i=0; i < pointCTB; ++i) //pointCTB常数表指针
if(0 == strcmp(constTab[i], str))
return i;
strcpy(constTab[pointCTB++], str);
return (pointCTB-1);
}
//词法分析 false--分析结束
bool wordAnalyse(pDualistic pDu) //词法分析 true正常
{
int code, value;
char judge; //这里有个技巧 借用此变量巧妙的运用SWITCH结构
int i = 0; //辅助
GetBC(); //获得一个非空白字符
judge = ch;
if (isalpha(ch) || ch == '_') judge='L';
//isalpha(ch)如果ch的内容为字母表中的字母,本函数返回非零值,否则返回零值。
if (isdigit(ch)) judge='D';
//(isdigit(ch)判断一个字符是否为数字(0-9),是数字返回1,不是返回0
switch(judge)
{
case 'L':
while(isalnum(ch) || ch == '_')
//isalnum(ch)如果本函数的变元为字母或数字,它将返回非零值,否则返回零值。
{ //标识符
wordget[i++] = ch;
GetChar(); //获得一个字符不包括结束标记
}
wordget[i] = '\0';
Retract(); //回退一个字符
code = Reserve(wordget); //对str字符串查找保留字表 若是一个保留字-返回其编码 否则返回0
if(code == 0)
{
value = InsertId(wordget);//将str串以标识符插入符号表,并返回符号表指针
pDu->kind = ID;
pDu->value = value;
}
else
{
pDu->kind = code;
pDu->value = -1;
}
return true;
case 'D':
while(isdigit(ch)) //isdigit(ch)判断字符ch是否为数字(0-9),是数字返回1,不是返回0;
{
wordget[i++] = ch;
GetChar(); //获得一个字符不包括结束标记
}
wordget[i] = '\0';
Retract(); //回退一个字符
value = InsertConst(wordget);//将str串以常数插入符号表,并返回常数表指针
pDu->kind = CONST;
pDu->value= value;
return true;
//( ) [ ] . , ! != ~ sizeof < << <= > >> >= = == & && &= | || |= ?: + ++ +=
// - -> -- -= * *= / /= % %= >>= <<= ^ ^=
case '"':
//字符串常量
do
{
wordget[i++] = ch;
GetChar();
}while(ch != '"' && ch != '\0');
wordget[i++] = ch; wordget[i] = '\0';
if(ch == '\0')
{
printf("%s",wordget);
ProcError(3);
pDu->kind = ERROR;
pDu->value = 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -