河北工业大学编译原理实验
c++/c
词法分析
```cpp
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
#include <math.h>
#include<ctype.h>
#define BEGIN 1
#define END 2
#define IF 3
#define THEN 4
#define ELSE 5
#define FOR 6
# define ID 7 //标识符
# define LT 8 //小于
# define LE 9 //小于等于
# define EQ 10 //等于
# define NE 11 //不等于
# define GT 12 //大于
# define GE 13 //大于等于
# define IS 14 //赋值
# define PL 15 //加
# define MI 16 //减
# define MU 17 //乘
# define DI 18 // 除
char TOKEN[20];//依次存放一个单词英文中的各个字符
char to[20]; //存放下一个要分析的单词
int lookup (char*);//以TOKEN中的字符串查保留字表
void out(int, char*);//输出函数
void report_error ();//报告出错
void scanner_example (FILE *);//遍历文件查询
int HandleOtherWord (void);//处理出错
#define chushi 0
#define DIGIT 1
#define POINT 2
#define OTHER 3
#define POWER 4
#define PLUS 5
#define MINUS 6
#define UCON 7 //无符号数常量的类号是7
#define ClassOther 200
#define EndState -1
int w,n,p,e,d,j;
int ICON;
int row=1;
int Class;//用来表示单词的等级
float FCON;
static int CurrentState;//用来表示当前状态,初始状态值为0
int GetChar (int c);
int EXCUTE (int,int);
int LEX (void);
//建立保留字表
#define MAX_KEY_NUMBER 20 //关键字的数量
#define KEY_WORD_END "waiting for your expanding" //关键字结束标记
char *KeyWordTable[MAX_KEY_NUMBER]={"begin","end","if","then","else","for",KEY_WORD_END};//保留字表
//查保留字表,判断是否为关键字
int lookup(char* token)
{
int n=0;
while(strcmp(KeyWordTable[n],KEY_WORD_END))//strcmp 比较两串是否相同,若相同返回0 保留字与关键字结束标记不一样,接着执行
{
if(!strcmp(KeyWordTable[n],token))//比较token所指向的关键字和保留字表中哪个关键字相符
{
return n+1; //返回类别码的值
break;
}
n++;
}
return 0;//单词不是关键字,而是标识符
}
int HandleOtherWord (void)
{
return ClassOther;
}
//报错函数
int HandleError (void)
{
FILE *fpt;
fpt = fopen("输出1.txt","a");//打开文本文档进行写入
fprintf(fpt,"识别失败!\n");
return 0;
}
//主函数,打开并读文件
void main(void)
{
FILE *fp;
if((fp=fopen("编译1.txt","r"))==NULL)//打开文件,打不开则报错
{
printf("\nfile open error!!\n");
exit(1);
}
do{
scanner_example(fp);//查询文件, 直到文件结束
}while(fgetc(fp)!=EOF);
fclose(fp);//关闭文件
}
void scanner_example (FILE *fp)
{
char ch;
int i, c;
ch=fgetc(fp); //fgetc函数 在文件中读取一个字符,赋值给ch
while(ch!=EOF) //文件不结束时一直执行
{
//判断是否为关键字或者标识符
if (isalpha (ch)) //判断字符ch是否为英文字母,若为英文字母,返回非0(小写字母为2,大写字母为1)。若不是字母,返回0。
{
TOKEN[0]=ch; //将字符存到token中
to[0]=ch;
ch=fgetc(fp); //再往下读一个字符
//if(ch=='\n') row++;
i=1;
while (isalnum (ch))//检查所传的字符是否是字母和数字,如果ch是一个数字或一个字母,则该函数返回非零值,否则返回 0
{//当ch不是数字或者字母时结束while循环
TOKEN[i]=ch; //存入字符
to[i]=ch;
i++;
ch=fgetc(fp);//再往下读一个字符
// if(ch=='\n') row++;
}
TOKEN[i]= '\0';//结束token
to[i]='\0';
fseek(fp,-1,1); // retract fseek函数 每调用一次,就把扫描指示器回退一个字符位置(即退回多读的那个字符)
//fp为文件指针,-1:为偏移量,正数表示正向偏移,负数表示负向偏移;1:设定从文件的哪里开始偏移,可能取值为:SEEK_CUR、 SEEK_END 或 SEEK_SET
//SEEK_SET: 文件开头SEEK_CUR: 当前位置 SEEK_END: 文件结尾其中SEEK_SET, SEEK_CUR和SEEK_END依次为0,1和2.
//指针退回到离文件当前位置1字节处
//保留字
c=lookup(TOKEN);//检查TOKEN中的字符串是否为保留字,若有则将相应的关键字的类别码赋给c,否则c=0
if (c==0)
{//输出为标识符
FILE *fpt;
fpt = fopen("输出1.txt","a");//打开文本文档进行写入
fprintf(fpt,"(ID,'%s')\n",TOKEN);
fclose(fpt);
}
else
out (c," "); //说明为关键字
if(ch==EOF)//文件读完,跳出循环
{
break;
}
}
//判断是否为“无符号数”
else if (isdigit(ch)||ch=='.') //当第一个字符为数字时 当ch为数字0-9时,返回非零值,否则返回零
{//第一个为数字或者点
to[0]=ch;
int CurrentState=0;
char y=GetChar(ch);//每调用一次,就把扫描指示器当前所指示的源程序字符送入字符变量ch,
//然后把扫描指示器前推一个字符位置。
CurrentState=EXCUTE(CurrentState,y);//状态
//循环判断过程
i=1;
while (CurrentState!=EndState)//当前状态不是结束状态时,到结束时为止
{
ch=fgetc(fp); //再读入一个字符
to[i]=ch;
i++;
y=GetChar(ch);
CurrentState=EXCUTE(CurrentState,y);
}
if(j==0)
{
FILE *fpt;
fpt = fopen("输出1.txt","a");//打开文本文档进行写入
fprintf(fpt,"(INT,%d)\n",ICON);//无符号数整形常数
fclose(fpt);
TOKEN[8]= '\0';///
to[i]='\0';
fseek(fp,-1,1);
}
else if(j==3)
{
FILE *fpt;
fpt = fopen("输出1.txt","a");//打开文本文档进行写入
fprintf(fpt,"包含多个小数点,识别失败!!\n");//无符号数 实型常数
fclose(fpt);
TOKEN[8]= '\0';
to[i]='\0';
fseek(fp,1,1);//识别错误之后还可以继续识别
}
else if(j==4)
{
to[i]='\0';
}
else
{
FILE *fpt;
fpt = fopen("输出1.txt","a");//打开文本文档进行写入
fprintf(fpt,"(REAL,%g)\n",FCON);//无符号数 实型常数
fclose(fpt);
TOKEN[8]='\0';//结束token
to[i]='\0';
fseek(fp,-1,1);//返回一个字符
}
if(ch==EOF)//结束
{
break;
}
}
//判断第一个字符是否为运算符
else
switch(ch)
{
to[0]=ch;
i=1;
case'<':
ch=fgetc(fp); //再读入一个字符
if(ch=='=')
{
to[i]=ch;
i++;
out(LE," "); // <=
to[i]='\0';
}
else if(ch=='>')
{
to[i]=ch;
i++;
out(NE," "); // <>
to[i]='\0';
}
else
{
//to[i]=ch;
out (LT," "); // <
// i++;
to[i]='\0';
if(ch==EOF) //如果结束了,则跳出循环
{
break;
}
else
fseek (fp,-1,1); //返回上一个字符
}
break;
case '=':
out(EQ, " "); // =
to[i]='\0';
break;
case ':':
ch=fgetc(fp);
if(ch=='=')
{
to[i]=ch;
i++;
out(IS," ");// :=
to[i]='\0';
}
else if(ch==EOF)
{
break;
}
else
{
if(ch==EOF)//结束
{
break;
}
else {
fseek (fp,-1,1);//返回一个字符
//report_error();//报告出错位置
}
}
break;
case'>':
ch=fgetc(fp);
if(ch=='=')
{
to[i]=ch;
i++;
out(GE," "); //>=
to[i]='\0';
}
else
{
to[i]=ch;
i++;
out(GT," "); //>
to[i]='\0';
if(ch==EOF)
{break;}
else fseek (fp,-1,1);//返回一个字符
}
break;
case'+':
to[i]=ch;
i++;
to[i]='\0';
out(PL," ");break;
case'-':
to[i]=ch;
i++;
to[i]='\0';
out(MI," ");break;
case'*':
to[i]=ch;
i++;
to[i]='\0';
out(MU," ");break;
case'/':
to[i]=ch;
i++;
to[i]='\0';
out(DI," ");break;
case' ':
break;
case'\n':
row++;
break;
default: report_error(); //报告出错位置
break;
}
ch=fgetc(fp);
}
return;
}
/*输出函数*/
void out(int c,char*v)
{
FILE *fpt;
fpt = fopen("输出1.txt","a");//打开文本文档进行写入
char* cl="";//定义一个字符
switch(c)
{
case BEGIN:
cl = "BEGIN"; break;
case END:
cl = "END"; break;
case IF:
cl = "IF"; break;
case THEN:
cl = "THEN"; break;
case ELSE:
cl = "ELSE"; break;
case ID:
cl = "ID"; break;
case LE:
cl = "LE"; break;
case LT:
cl = "LT"; break;
case EQ:
cl = "EQ"; break;
case NE:
cl = "NE"; break;
case GT:
cl = "GT"; break;
case GE:
cl = "GE"; break;
case IS:
cl = "IS"; break;
case PL:
cl = "PL"; break;
case MI:
cl = "MI"; break;
case MU:
cl = "MU"; break;
case DI:
cl = "DI"; break;
case FOR:
cl = "FOR"; break;
}
fprintf(fpt,"(%s,%s)\n",cl,v);
fclose(fpt);
}
void report_error()
{
printf("error");
}
//每调用一次,就把扫描指示器当前所指示的源程序字符送入字符变量ch,然后把扫描指示器前推一个字符位置。
int GetChar (int c)
{
if(isdigit(c))//若参数ch为阿拉伯数字0~9,则返回非0值,否则返回0
{//数字
d=c-'0';
return DIGIT;
}
if (c=='.')
return POINT;//返回点
if (c=='E'||c=='e')
return POWER;//返回给定数字的乘幂
if (c=='+')
return PLUS;
if (c=='-')
return MINUS;
return OTHER;
}
//识别无符号数
int EXCUTE (int state, int symbol)//状态和DIGIT这种标志
{
j=0;
switch (state)
{
case 0:
switch (symbol)
{
case DIGIT: n=0;p=0;e=1;w=d;CurrentState=1;Class=UCON;break;
case POINT: w=0;n=0;p=0;e=1;CurrentState=3;Class=UCON;break;
default: HandleOtherWord();Class=ClassOther;
CurrentState=EndState;
}break;
case 1:
switch (symbol)
{
case DIGIT: w=w*10+d;CurrentState=1;break; //CurrentState=1
case POINT: CurrentState=2;break;
case POWER: CurrentState=4;break;
default: ICON=w;CurrentState=EndState;j=0;//整型常数
}break;
case 2:
switch (symbol)
{
case DIGIT: n++;w=w*10+d;CurrentState=2;break;
case POWER: CurrentState=4;break;
case POINT: CurrentState=EndState;j=3;break;
default: FCON=float(w*pow(10,e*p-n));CurrentState=EndState;j=1;//实型常数
}break;
case 3:
switch (symbol)
{
case DIGIT: n++;w=w*10+d;CurrentState=2;break;
default: HandleError();CurrentState=EndState;j=4;
}break;
case 4:
switch (symbol)
{
case DIGIT: p=p*10+d;CurrentState=6;break;
case MINUS:e=-1; CurrentState=5;break;
case PLUS: CurrentState=5;break;
default: HandleError();CurrentState=EndState;
}break;
case 5:
switch (symbol)
{
case DIGIT: p=p*10+d;CurrentState=6;break;
default: HandleError();CurrentState=EndState;
}break;
case 6:
switch (symbol)
{
case DIGIT:p=p*10+d;CurrentState=6;break;
default: FCON=w*pow(10,e*p-n);CurrentState=EndState;j=1;
}break;
}
return CurrentState;
}