最近看了人家的C词法分析程序,就抽了一个C非常小的子集来做练习
/**
简单语言词法分析程序
关键字:if else while for KEY 1
标识符:(字母|_)(字母|数字|_)* IDT 2
运算符:= == + - * / != > < OPT 3
常量数字:数字(数字)* DGT 4
分界符:;( ) { } , LMT 5
空白符:\n \t 空格 (文件结束符EOF作分析完毕的参照点)
2012.8.13 cc
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#define KEY 1
#define IDT 2
#define OPT 3
#define DGT 4
#define LMT 5
char *keywords[5]={"if","else","while","for","break"};
char *opts[7]={"=","==","!=","+","-","*","/"};
char delimiter[7]={';','(',')','{','}',',',' '};
char whitepace[3]={'\n','\t',' '};
int number; //当前常数
char curr[20]; //当前字符串
char file[1000]; //当前文件的内存映射
int curpos; //处理的文件当前字符串位置
char filename[]="d:\\test.c";
void prehandle(char *src);
int isLimiter(char ch);
int isLetter(char ch);
int isDigit(char ch);
int isOperator(char *ch);
int isWhitepace(char *src);//包括: \n \t 连续两个空白符
//处理字母开始的串
int handleLetter(char *src){
assert(isLetter(*src));
int pos=0;
char curch;
//如果是字母可能是两种情况 标识符/关键字
curch=*(src);
while((isLetter(curch))||(curch=='_')||(isDigit(curch))){
curr[pos]=curch;
curch=*(src+pos+1);
pos++;
}
curr[pos]='\0';
curpos+=strlen(curr);
//得到了当前字符串curr,判断curr的属性,可能是关键字或者标识符
for(int i=0;i<5;i++){
if(strcmp(keywords[i],curr)==0){
return KEY;
}
}
return IDT;
}
//处理下划线开始的串
int handleUnderline(char *src){
assert(*src=='_');
int pos=0;
char curch=*(src+curpos);
while((isLetter(curch))||(curch=='_')||(isDigit(curch))){
curr[pos++]=curch;
curpos++;
curch=*(src+curpos);
}
return IDT;
}
//处理数字开始的串
int handleDigit(char *src){
assert(isDigit((*src)));
int pos=0;
char curch=*(src);
while(isDigit(curch)){
curr[pos]=curch;
curch=*(src+pos+1);
pos++;
}
curr[pos]='\0';
curpos+=strlen(curr);
return DGT;
}
//处理分界符
int handleLimitter(char *src){
assert(isLimiter(*src));
curr[0]=*src;
curr[1]='\0';
curpos++;
return LMT;
}
//处理运算符
int handleOperator(char *src){
assert(isOperator(src));
char curch=*src;
if((curch=='+')||(curch=='-')||(curch=='*')||(curch=='/')||(curch=='>')||(curch=='<')){
curr[0]=curch;
curr[1]='\0';
curpos++;
return OPT;
}
// !=
else if((curch=='!')&&((*(src+1))=='=')){
curr[0]='!';
curr[1]='=';
curr[2]='\0';
curpos+=2;
return OPT;
}
// == / =
else if((curch=='=')){
if(((*(src+1))=='=')){
curr[0]='=';
curr[1]='=';
curr[2]='\0';
curpos+=2;
return OPT;
}
else{
curr[0]='=';
curr[1]='\0';
curpos++;
return OPT;
}
}
return OPT;
}
int main(){
FILE *fp;
curpos=0;
if((fp=fopen(filename,"r"))==NULL){
printf("Open file <%s> error .\n",filename);
return 0;
}
//fgets(file,200,fp);
int pos=0;
while(!feof(fp)){
file[pos++]=fgetc(fp);
}
file[pos]=EOF;
//strcpy(file,"while(asdd==2);");
puts(file);
prehandle(file);
puts(file);
char curch;
while((*(file+curpos))!=EOF){
curch=*(file+curpos);
int curType;
while(curch==' '){
curpos++;
curch=*(file+curpos);
}
//字母
if(isLetter(curch)){
curType=1;
}
//下划线
else if(curch=='_'){
curType=2;
}
//数字
else if(isDigit(curch)){
curType=3;
}
//分界符
else if(isLimiter(curch)){
curType=4;
}
//运算符
else if(isOperator((file+curpos))){
curType=5;
}
else if(curch==EOF){
printf("Lex analysis is done.\n");
break;
}
else{
curType=0;
}
switch(curType){
case 1:
printf("<%d,%s>\n",handleLetter(file+curpos),curr);
break;
case 2:
printf("<%d,%s>\n",handleUnderline(file+curpos),curr);
break;
case 3:
printf("<%d,%s>\n",handleDigit(file+curpos),curr);
break;
case 4:
printf("<%d,%s>\n",handleLimitter(file+curpos),curr);
break;
case 5:
printf("<%d,%s>\n",handleOperator(file+curpos),curr);
break;
default:
printf("error.\n");
return 0;
}
}
fclose(fp);
return 0;
}
//预处理掉空白符
void prehandle(char *src){
int pos=0;
char cur;
while((cur=(*(src+pos)))!=EOF){
if(cur=='\n'||cur=='\t'){
*(src+pos)=' ';
}
++pos;
}
}
//判断是否是空白符
int isWhitepace(char *src){
char cur;
cur=*src;
//换行和tab是空白符
if(cur=='\n'&&cur=='\t'){
return 1;
}
//连续两个空白符算,第一个算,连续n个空格符,前面n-1个算,保留最后一个
if(cur==' '){
if(*(src+1)==' '){
return 1;
}
}
return 0;
}
//判断是否是分界符
int isLimiter(char ch){
for(int i=0;i<sizeof(delimiter)/sizeof(char);i++){
if(delimiter[i]==ch){
return 1;
}
}
return 0;
}
//判断是否是数字
int isDigit(char ch){
if(ch>='0'&&ch<='9'){
return 1;
}
else{
return 0;
}
}
//是否是字母
int isLetter(char ch){
if(((ch>='a'&&ch<='z'))||((ch>='A')&&(ch<='Z'))){
return 1;
}
else{
return 0;
}
}
//判断是否是运算符
int isOperator(char *ch){
char curch=*ch;
if(
(curch=='+')||
(curch=='-')||
(curch=='*')||
(curch=='/')||
(curch=='<')||
(curch=='>')
){
return 1;
}
//!=
else if((curch=='!')&&((*(ch+1))=='=')){
return 1;
}
//==
else if((curch=='=')){
return 1;
}
else
return 0;
}
1.代码扩展性。比如当要增加关键字的时候,要修改很大部分代码
可以用文件来处理,把关键字保存在一个配置文件中,提供一个增加关键字接口,一方面用户可以用这个接口来灵活添加关键字,另一方面我们也可以通过修改文件来设置哪些算关键字。
2.效率问题。当关键字很多时,一个源文件会涉及很多关键字,难道每一个查找都用完全遍历方法,这无疑会是编译效率大大降低
可以用hash来解决,有很多字符串哈希算法,包括BKDB,ELF等等,来提高编译效率
当作练习好了