实验一:词法分析器
词法分析器的思路:
1.首先是对关键词的识别
可以做一个数组存放关键词的所有单词,例如下列程序的
char *rwtab[6]={"begin","if","then","while","do","end"};
2.然后是主程序的逻辑:
* 循环输入缓存中的字母放进输入数组中
* 调用scaner函数对每一个词进行分析
* 为数字则拼在一起作为一个数输出
* 为关键字符或者变量时,拼成字符串输出
3.scaner函数的思路:
* token临时数组存放完成后的数字或字符串
* prog数组存放输入的所有内容
* p用来保存识别的所有进度,m为token的偏移量,n用来判断是哪个关键词,num用来保存最后的数字
* 首先初始化token数组
* 跳过所有空格和回车
* 先判定字母,然后把数字判断也包含进来,组成了含数字的变量名或者关键词,再判断是不是关键词
* 再判定数字,最后组成数字
* 最后是各种符号
#include <stdio.h>
#include <string.h>
char prog[80],token[8],ch; //prog数组用来存放输入的字符串 ,token数组用来存放字符,ch用来表示输入缓存的移动标志
int syn,p,m,n,num;//标识码
char *rwtab[6]={"begin","if","then","while","do","end"};//关键字数组,由1到6
void scaner(void);//扫描输入后的字符串,识别关键字、字母和数字
int main()
{
p=0; //用p来表示输入字符串数组的偏移量
printf("\nplease input a string(end with '#'):\n");//提示字符
do{ //用循环来读取输入缓冲区的字符串
scanf("%c",&ch); //读取单个字符
prog[p++]=ch;
}while(ch!='#');//用#来表示结束
p=0;
do{
scaner();
switch(syn)//用syn来分辨不同种情况
{
case 11://如果syn是11就是数字
printf("< %-5d%8d >\n",syn,num);
break;
case -1://如果是-1则是错的
printf("you have input a wrong string\n");
//getch();
return 0;
break;
default://默认就是字符
printf("< %-5d%8s >\n",syn,token);
break;
}
}while(syn!=0);
//getch();
}
void scaner(void)
{
num=0;
for(m=0;m<8;)
token[m++]= NULL; //初始化token数组
ch=prog[p++]; //p从0开始判断
m=0;
while((ch==' ')||(ch=='\n')) //跳过空格和回车
ch=prog[p++];
if(((ch<='z')&&(ch>='a'))||((ch<='Z')&&(ch>='A'))) //识别字母
{
while(((ch<='z')&&(ch>='a'))||((ch<='Z')&&(ch>='A'))||((ch>='0')&&(ch<='9')))//变量名可以含有数字
{
token[m++]=ch;
ch=prog[p++];
}
p--; //p减一回到上一个字符进行判断
syn=10;
for(n=0;n<6;n++) //循环识别关键字
if(strcmp(token,rwtab[n])==0)
{
syn=n+1; //由1到6
break;
}
}
else if((ch>='0')&&(ch<='9')) //识别数字
{
while((ch>='0')&&(ch<='9'))
{
num=num*10+ch-'0'; //将多个单个数字组合成一个数字
ch=prog[p++];
}
p--;
syn=11;
}
else //剩下就是各种运算符
{
switch(ch)
{
case '<':
token[m++]=ch;
ch=prog[p++];
if(ch=='=') //识别为小于号
{
syn=22;
token[m++]=ch;
}
else //识别为小于等于号
{
syn=20;
p--;
}
break;
case '>':
token[m++]=ch;
ch=prog[p++];
if(ch=='=') //识别为大于等于号
{
syn=24;
token[m++]=ch;
}
else //识别为大于号
{
syn=23;
p--;
}
break;
case '+':
token[m++]=ch;
ch=prog[p++];
if(ch=='+') //识别为++号
{
syn=17;
token[m++]=ch;
}
else //识别为+号
{
syn=13;
p--;
}
break;
case '-':
token[m++]=ch;
ch=prog[p++];
if(ch=='-') //识别为--号
{
syn=29;
token[m++]=ch;
}
else //识别为-号
{
syn=14;
p--;
}
break;
case '!':
ch=prog[p++];
if(ch=='=') //识别为!=号
{
syn=21;
token[m++]=ch;
}
else //识别为错误
{
syn=31;
p--;
}
break;
case ':':
token[m++]=ch;
ch=prog[p++];
if(ch=='=') //识别为==号
{
syn=18;
token[m++]=ch;
}
else //识别为=号
{
syn=17;
p--;
}
break;
case '*': //识别为*
syn=15;
token[m++]=ch;
break;
case '/': //识别为/
syn=16;
token[m++]=ch;
break;
case '(': //识别为(
syn=27;
token[m++]=ch;
break;
case ')': //识别为)
syn=28;
token[m++]=ch;
break;
case '{': //识别为{
syn=5;
token[m++]=ch;
break;
case '}': //识别为}
syn=6;
token[m++]=ch;
break;
case ';': //识别为;
syn=26;
token[m++]=ch;
break;
case '\"': //识别为”
syn=30;
token[m++]=ch;
break;
case '#': //识别为#
syn=0;
token[m++]=ch;
break;
default: //识别为错误
syn=-1;
break;
}
}
token[m++]='\0';
}
重要的点
- 循环的问题
for(m=0;m<8;)
token[m++]= NULL;
这个位置for循环的第三个参数不要写,有些人习惯就直接写m++。或者可以:
for(m=0;m<8;m++)
token[m]= NULL;
- 识别字母
if(ch>='A'&&ch<='z')//当识别字母时可以直接使用这个
{
}
因为ascii码中字母的顺便是从A~Z a~z变大的,可以一次性整合在一块。