📄 编译.txt
字号:
程序代码
/*
* 程序名称:编译原理之词法分析器
* 输 入:Test.c C语言源程序文件
* 输 出:Const.txt 常量表 Sign.txt 标识符表 Result.txt 二元式结果
* 操作系统:WinXP_SP2
* 备 注:此程序读入程序源码,首先进行预处理去掉注释和无效空格
* 然后再进行详细的词法分析,为了便于后续处理,程序设置了几个表:符号
* 表和常数表。
* 注 意:此分析器只对C语言子集进行处理
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <conio.h>
#include <stdlib.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][10]={ //保留字表 C语言一共有32个保留字[关键字]
"auto", "break", "case", "char", "const",
"continue", "default", "do", "double", "else",
"enum", "extern", "float", "for", "goto",
"if", "int", "long", "register", "return",
"short", "signed", "sizeof", "static", "struct",
"switch", "typedef", "union", "unsigned", "void",
"volatile", "while"};
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 '/':
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);
}
break;
default:
//不是任何一种注释
Retract();
Retract();
GetChar();
tmp[tmpp++] = ch;
flg = GetChar();
tmp[tmpp++] = ch;
}
}
else
{
tmp[tmpp++] = ch;
}
}while(flg);
tmp[tmpp] = '\0';
strcpy(proBuffer,tmp);
}
//错误
void ProcError(int id)
{
printf("\nError:第%d行,%s\n",errorLine, 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)); //直到获得一个非空白字符
return true;
}
//将ch连接到str后
void Concat(char *str)
{
int i;
for(i=0; str[i]; ++i);
str[i] = ch;
str[i+1] = '\0';
}
//对str字符串查找保留字表 若是一个保留字-返回其编码 否则返回0
int Reserve(char *str)
{
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 --;
}
//将str串以标识符插入符号表,并返回符号表指针
int InsertId(char *str)
{
int i;
for(i=0; i < pointSTB; ++i)
if(0 == strcmp(signTab[i], str))
return i;
strcpy(signTab[pointSTB++], str);
return (pointSTB-1);
}
//将str串以常数插入常量表,并返回常数表指针
int InsertConst(char *str)
{
int i;
for(i=0; i < pointCTB; ++i)
if(0 == strcmp(constTab[i], str))
return i;
strcpy(constTab[pointCTB++], str);
return (pointCTB-1);
}
//词法分析 false--分析结束
bool wordAnalyse(pDualistic pDu)
{
int code, value;
char judge; //这里有个技巧 借用此变量巧妙的运用SWITCH结构
int i = 0; //辅助
GetBC();
judge = ch;
if (isalpha(ch) || ch == '_') judge='L';
if (isdigit(ch)) judge='D';
switch(judge)
{
case 'L':
while(isalnum(ch) || ch == '_')
{ //标识符
wordget[i++] = ch;
GetChar();
}
wordget[i] = '\0';
Retract(); //回退一个字符
code = Reserve(wordget);
if(code == 0)
{
value = InsertId(wordget);
pDu->kind = ID;
pDu->value = value;
}
else
{
pDu->kind = code;
pDu->value = -1;
}
return true;
case 'D':
while(isdigit(ch))
{
wordget[i++] = ch;
GetChar();
}
wordget[i] = '\0';
Retract();
value = InsertConst(wordget);
pDu->kind = CONST;
pDu->value= value;
return true;
//( ) [ ] . , ! != ~ sizeof < << <= > >> >= = == & && &= | || |= ?: + ++ +=
// - -> -- -= * *= / /= % %= >>= <<= ^ ^=
case '"':
//字符串常量
do
{
wordget[i++] = ch;
GetChar();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -