编译原理这门课在计算机基础课程中绝对是金牌杀手,无论是大学还是研究生,挂科率都挺高。当初现学现卖的编译原理老师对我们说:“好歹你们也是学过编译原理的人,给我写个词法分析器看看!”下面马上传来唉声叹气的声音。老师又安慰我们说:“没让你们写语法分析器算好的了,词法分析器比语法分析器简单多了,你们试着做一下。”
上大学那会儿,我还是个乖宝宝,特听话。回去就写了一个简易版的词法分析器,然后发给老师。下次上课的时候,老师就说:“怎么只有一个人给我交作业,还是个女生,真是‘巾帼不让须眉’。”接着,老师引出重点:“你们这些男生,要加油啊!”结果一周后,大部分男生都提交了词法分析器。
简易版的词法分析器
#include"stdio.h"
#include"string.h"
#include"stdlib.h"
typedef struct{
char wordSymbol[20]; //单词符号
//int code; //种别编码
char mnemonic[20]; //助记符
}Token;
Token token[15];
char c[1000];
void initTable();
int findCode(char c[],int len);
int isIdentifier(char c[],int len);
int isInteger(char c[],int len);
void initTable(){
strcpy(token[1].wordSymbol,"DIM");
strcpy(token[1].mnemonic,"$DIM");
strcpy(token[2].wordSymbol,"IF");
strcpy(token[2].mnemonic,"$IF");
strcpy(token[3].wordSymbol,"DO");
strcpy(token[3].mnemonic,"$DO");
strcpy(token[4].wordSymbol,"STOP");
strcpy(token[4].mnemonic,"$STOP");
strcpy(token[5].wordSymbol,"END");
strcpy(token[5].mnemonic,"$END");
strcpy(token[6].wordSymbol,"标识符");
strcpy(token[6].mnemonic,"$IDN");
strcpy(token[7].wordSymbol,"整数");
strcpy(token[7].mnemonic,"$INT");
strcpy(token[8].wordSymbol,"=");
strcpy(token[8].mnemonic,"$ASG");
strcpy(token[9].wordSymbol,"+");
strcpy(token[9].mnemonic,"$PLUS");
strcpy(token[10].wordSymbol,"*");
strcpy(token[10].mnemonic,"$STAR");
strcpy(token[11].wordSymbol,"**");
strcpy(token[11].mnemonic,"$POWER");
strcpy(token[12].wordSymbol,",");
strcpy(token[12].mnemonic,"$COMMA");
strcpy(token[13].wordSymbol,"(");
strcpy(token[13].mnemonic,"$SLP");
strcpy(token[14].wordSymbol,")");
strcpy(token[14].mnemonic,"$SRP");
}
//找到种别编码
int findCode(char c[],int len){
int i;
for(i=1;i<6;i++)
if(!stricmp(c,token[i].wordSymbol) )
return i;
if( isIdentifier(c,len) )
return 6;
if( isInteger(c,len) )
return 7;
for(i=8;i<15;i++)
if(!stricmp(c,token[i].wordSymbol) )
return i;
return 0;
}
//根据种别码返回单词符号信息
void wordInfor(char c[],int len,int code){
if(code==0)
printf("ERROR WORD !");
else {
if(code==6 || code==7)
printf("%s%s\t",token[code].wordSymbol,c);
else
printf("%s\t\t",token[code].wordSymbol);
printf("%d %s",code,token[code].mnemonic);
}
}
//判断是否是标识符
int isLetter(char c){
if(c<='z'&&c>='a' || c<='Z'&&c>='A')
return 1;
return 0;
}
int isNum(char c){
if(c<='9'&&c>='0')
return 1;
return 0;
}
int isIdentifier(char c[],int len){
int i;
if( !isLetter(c[0]) )
return 0;
for(i=1;i<len;i++){
if( !isLetter(c[i]) && !isNum(c[i]) )
return 0;
}
return 1;
}
//判断是否是整数
int isDecimalDigit(char c[],int len){
int i=0;
if( c[0]=='0' && len!=1)
return 0;
for(i=0;i<len;i++)
if( !isNum(c[i]) )
return 0;
return 1;
}
int isOctalDigit(char c[],int len){
int i=0;
if(len==1 || c[0]!='0' )
return 0;
for(i=0;i<len;i++)
if( c[i]>'7' || c[i]<'0' )
return 0;
return 1;
}
int isHexDigit(char c[],int len){
int i=0;
if(len<=2 || c[0]!='0' )
return 0;
if( !(c[1]=='x')||(c[1]=='X') )
return 0;
for(i=2;i<len;i++)
if( !isNum(c[i]) && !(c[i]<='f'&&c[i]>='a' || c[i]<='F'&&c[i]>='A'))
return 0;
return 1;
}
int isInteger(char c[],int len){
return isDecimalDigit(c,len) || isOctalDigit(c,len) || isHexDigit(c,len) ;
}
void output(int code,FILE *fp){
fputs(token[code].wordSymbol,fp);
fputs("\t\t",fp);
if(code<10)
putc(code+48,fp);
else{
putc('1',fp);
putc(code+38,fp);
}
fputs("\t\t",fp);
fputs(token[code].mnemonic,fp);
fputs("\t\t",fp);
fputs(c,fp);
fputc('\n',fp);
}
main(){
FILE *fs,*fd;
char ch;
char str[50]="WordSymbol\tCode\t\tMnemonic\t\tString\n";
initTable();
//open file
if((fs=fopen("source.txt","r"))==NULL){
printf("ERROR OPEN FILE!");
exit(1);
}
if((fd=fopen("destination.txt","w"))==NULL){
printf("ERROR OPEN FILE!");
exit(1);
}
fputs(str,fd);
//扫描单词序列
ch=fgetc(fs);
while(!ch)
ch=fgetc(fs);
while(ch!=EOF){
if(isLetter(ch)||isNum(ch)){
int i = 0,code=0;
while(ch!=EOF && ( isLetter(ch)||isNum(ch) ) ){
//putchar(ch);
c[i]=ch;
i++;
ch=fgetc(fs);
//fputc(ch,fd);
}
c[i]='\0';
code=findCode(c,strlen(c));
if(!strcmp(c," ")||!strcmp(c,"\n")||!strcmp(c,"\t")){
ch=fgetc(fs);
continue;
}
if(!code){
fputs(" \tERROR WORD\t\t",fd);
fputs("\t\t",fd);
fputs(c,fd);
fputc('\n',fd);
}
else
output(code,fd);
}
else{
int code=0;
if(ch==' '){
ch=fgetc(fs);
continue;
}
c[0]=ch;
c[1]='\0';
code=findCode(c,strlen(c));
if(8<=code&&code<=14){
if(code!=10){
output(code,fd);
ch=fgetc(fs);
}
else{
ch=fgetc(fs);
if(ch=='*'){
c[1]=ch;
c[2]='\0';
output(code+1,fd);
ch=fgetc(fs);
}
else
output(code,fd);
}
}
else if(!strcmp(c," ")||!strcmp(c,"\n")||!strcmp(c,"\t")){
ch=fgetc(fs);
continue;
}
if(!code){
fputs(" \tERROR WORD\t\t",fd);
fputs("\t\t",fd);
fputs(c,fd);
fputc('\n',fd);
ch=fgetc(fs);
}
}
}
fflush(fd);
fclose(fs);
fclose(fd);
}
几年后,回看这份作业,心情略为复杂。一方面,觉得自己的代码风格和模块划分还有待改进。另一方面,也为过去自己的那份认真而感动。