编译原理--C-Minus词法分析器C++实现
2016-04-25 14:57
344 查看
词法分析器的主要功能是把源代码整理成一个个记号(token),记号的类型主要有系统保留字(if,return等)、特殊字符(+,*,/等)、字符串记号(数字和标志符)。
如:str[i] = 45 + 6;
将上面代码整理:
标识符:str,i
特殊符:[ , ],+
数字 : 45,6
DFA如下:
c-测试源代码:
代码实现:
实现小结:其实写词法分析器的DFA并不难,使用ifelse嵌套或者switch选择嵌套都可以,我觉得容易出现问题是对文本的读取,以上代码基本使用的是c的文件库函数实现。
需要注意的是:计算机从ASCII文件(文本文件)读入字符时,遇到回车换行符(两个字符‘\r,‘\n’)时,系统把它转化为一个字符’\n’。所以用户只能收到’\n’,不能收到’\r’。但是使用函数fseek()时要格外小心。
看如下代码:
可以看到 ‘h’属于第五个字符,从0开始的h看似应该是第4个字符啊!
原来ftell()函数 并没有把’\r’忽略掉!这样编程就会遇到问题。
如文件指针回退 ,假如当前字符为’e’,回退到’c’,应该是fseek(fp,-4L,1)而不是fseek(fp,-3L,1)。这个问题让我找了两个小时的错误!
都到了崩溃的边缘了。~.~!
《完》
如:str[i] = 45 + 6;
将上面代码整理:
标识符:str,i
特殊符:[ , ],+
数字 : 45,6
DFA如下:
c-测试源代码:
/* A program to perform Euclid's Algorithm to compute gcd. */ int gcd (int u, int v) { if (v == 0) return u ; else return gcd(v,u-u/v*v); /* u-u/v*v == u mod v */ } void main(void) { int x; int y; x = input(); y = input(); output(gcd(x,y)); }
代码实现:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<string> #include<iostream> #define sfName "source.c" #define tfName "target.c" typedef int sInt; using namespace std; FILE *source; FILE *target; long beginp[5]; //记录文件指针的开始位置 long endp[5]; //记录文件指针的结束位置 char idStr[80]=""; //保存标识符中间值 int state[5]={0}; //不同记号类型的状态 char unaryOP[16]={'+','-','*','/','<','>','=',';',',','(',')','[',']','{','}','!'}; //保存一元运算符 char *p[6]={"if","else","int","return","void","while"}; //系统保留字 char *strToken[7]={"ID","NUM","OP","FUCN","RESW","ERROR","COM"}; typedef enum {ID,NUM,OP,FUCN,RESW,ERROR,COM}tokenType; //记号类型 void clearState() { memset(state,0,sizeof(state)); memset(idStr,'\0',sizeof(idStr)); memset(beginp,0,sizeof(beginp)); memset(endp,0,sizeof(endp)); } void strPrintf(long begin,long end,tokenType t) { bool isComment = false; int k; char s[200]=""; long len=end-begin; //printf("%d %d\n",begin,end); fseek(source,-len,1);//文件指针回退 for(int i=0;i<len;i++) { s[i]=fgetc(source); } //fseek(source,len,1); //文件指针归位 switch(t) { case ID: k=0;break; case NUM: k=1;break; case OP: k=2;break; case FUCN: k=3;break; case RESW: k=4;break; case ERROR: k=5;break; case COM: k=6; isComment=true;break; default:cout<<"输入有误!\n"; } //printf("%s\n",s); if(isComment) { fprintf(target,"%s\n",s); printf("%s\n",s); } else { fprintf(target,"<%s,%s>\n",strToken[k],s); printf("<%s,%s>\n",strToken[k],s); } } void unaryPrintf(char s,tokenType t) { int k; switch(t) { case ID: k=0;break; case NUM: k=1;break; case OP: k=2;break; case FUCN: k=3;break; case RESW: k=4;break; case ERROR: k=5;break; case COM: k=6;break; default:cout<<"输入有误!\n"; } //cout<<strToken[k]<<endl; fprintf(target,"<%s,%c>\n",strToken[k],s); printf("<%s,%c>\n",strToken[k],s); } bool isOperator(char ch) { for(int i=0;i<16;i++) { if( ch==unaryOP[i] ) return true; } return false; } void comment(char ch,int i) { bool isexit = false; //当不是注释时用于跳出循环 //tokenType t=COM; while(!feof(source)) { switch(state[i]) { case 0: if(ch=='/') { state[i]=1; beginp[i]=ftell(source)-1; } break; case 1: if(ch=='*') { state[i]=2; } else { isexit = true; //说明不是注释,请求退出 unaryPrintf('/',OP); fseek(source,-1L,1); //回退一个字节,因为向后判断移了一位 } break; case 2: if(ch=='*') { state[i]=3; }else { state[i]=2; } break; case 3: if(ch=='/') { state[i]=4; endp[i]= ftell(source); strPrintf( beginp[i],endp[i],COM); isexit = true; //back }else state[i]=2; break; } if(isexit) return;//back; ch = fgetc(source); } } void number(char ch,int i) { beginp[i]=ftell(source)-1; while(!feof(source)) { if(ch>='0' && ch<='9') { state[i]=1; endp[i]=ftell(source); }else { fseek(source,-1L,1); //回退,读到了下一个字符 strPrintf(beginp[i],endp[i],NUM); return; } ch = fgetc(source); } } void myOperator(char ch,int i) { bool isReturn = false; while(!feof(source)) { if(ch=='+' ||ch=='-'||ch=='*'||ch==';'||ch==','||ch=='('||ch==')'||ch=='['||ch==']'||ch=='{'||ch=='}') { state[i]=1; unaryPrintf(ch,OP); isReturn = true; }else { switch(state[i]) { case 0: beginp[i]=ftell(source)-1; switch(ch) { case '<': state[i]=2; break; case '>': state[i]=4; break; case '=': state[i]=6; break; case '!': state[i]=8; break; default:cout<<"data error!\n"; } break; case 2: if(ch=='=') { state[i]=3; endp[i]=ftell(source); strPrintf(beginp[i],endp[i],OP); isReturn = true; }else { //属于一元操作符 state[i]=2; fseek(source,-1L,1);//回退一个字符 unaryPrintf('<',OP); isReturn = true; } break; case 4: if(ch=='=') { state[i]=5; endp[i]=ftell(source); strPrintf(beginp[i],endp[i],OP); isReturn = true; }else { //属于一元操作符 state[i]=4; fseek(source,-1L,1);//回退一个字符 unaryPrintf('>',OP); isReturn = true; } break; case 6: if(ch=='=') { state[i]=7; endp[i]=ftell(source); strPrintf(beginp[i],endp[i],OP); isReturn = true; }else { //属于一元操作符 state[i]=6; fseek(source,-1L,1);//回退一个字符 unaryPrintf('=',OP); isReturn = true; } break; case 8: if(ch=='=') { state[i]=9; endp[i]=ftell(source); strPrintf(beginp[i],endp[i],OP); isReturn = true; }else { //属于一元操作符 state[i]=8; fseek(source,-1L,1);//回退一个字符 unaryPrintf('!',OP); isReturn = true; } break; default:cout<<"data error!\n"; } } if(isReturn) return; ch = fgetc(source); } } bool isLiter(char ch) { if((ch>='A' && ch<='Z')||( ch>= 'a' && ch<='z') || ch=='_') { return true; }else return false; } bool isResw(char *s) { for(int i=0;i<6;i++) { if( strcmp(s,p[i])==0 ) return true; } return false; } void identifier(char ch,int i) { beginp[i] = ftell(source)-1; long len =0; bool isQuit = false; bool isFucn = false; while(!feof(source)) { if( isLiter(ch) ) { state[i]=1; endp[i]=ftell(source); } else { long enter = 1; // isQuit = true; if(ch=='\n') enter = 2; fseek(source,-enter,1); //回退一或2个字符 //cout<<"pos="<<ftell(source)<<endl; //printf("%d %d\n",beginp[i],endp[i]); len = endp[i]-beginp[i]; fseek(source,-len,1); //cout<<"pos="<<ftell(source)<<endl; for(int j=0;j<len;j++) { idStr[j] = fgetc(source); } if( isResw(idStr) ) //如果是保留字,就保存退出 { strPrintf(beginp[i],endp[i],RESW); }else { char temps; long cout=1; temps = fgetc(source); while(!feof(source)) { if(temps==' ' ||temps=='\n' || temps=='\t') { ; //jump it; }else { if(temps=='(') { isFucn = true; //表明是函数名 break; } else { isFucn = false; //不是函数名 break; } } temps = fgetc(source); cout++; } fseek(source,-cout,1); //回退文件指针 //printf("back=%ld\n",ftell(source)); if(isFucn) { strPrintf(beginp[i],endp[i],FUCN); }else { strPrintf(beginp[i],endp[i],ID); } } } if(isQuit) return; ch = fgetc(source); } } void startScanner() { char ch=fgetc(source); while( !feof(source) ) { if(ch==' ' ||ch=='\n' || ch=='\t') { ;//nothing jump it! }else if(ch=='/') { comment(ch,0); clearState(); //清楚状态信息 }else if(ch>='0'&&ch<='9') { number(ch,1); //处理数字 clearState(); }else if( isOperator(ch) ) { myOperator(ch,2); //处理操作符 clearState(); }else if( isLiter(ch) ) { identifier(ch,3); //处理标志符 clearState(); } else { unaryPrintf(ch,ERROR); } ch = fgetc(source); } } int main() { if((source=fopen(sfName,"r"))==NULL) { printf("文件打开失败!\n"); exit(0); } if((target=fopen(tfName,"w"))==NULL) { fprintf(stderr,"文件写入失败!\n"); exit(0); } startScanner();//开始扫描文件 entrance fclose(source); fclose(target); return 0; }
实现小结:其实写词法分析器的DFA并不难,使用ifelse嵌套或者switch选择嵌套都可以,我觉得容易出现问题是对文本的读取,以上代码基本使用的是c的文件库函数实现。
需要注意的是:计算机从ASCII文件(文本文件)读入字符时,遇到回车换行符(两个字符‘\r,‘\n’)时,系统把它转化为一个字符’\n’。所以用户只能收到’\n’,不能收到’\r’。但是使用函数fseek()时要格外小心。
看如下代码:
#include <iostream> #include <stdio.h> #include <stdlib.h> #define fname "target.c" #define filename "hiluo.txt" using namespace std; FILE *fp; void solution() { char ch; long pos; if((fp=fopen(filename,"r"))==NULL) { printf("open file error\n"); exit(0); } ch = fgetc(fp); //接受第一个字符 while(!feof(fp)) { printf("%c",ch); //打印文本文件 ch=fgetc(fp); } printf("\n"); printf("\n"); rewind(fp);//将文件指针指向文件头 printf("文件的起始位置 = %ld\n",ftell(fp)); ch =fgetc(fp); while(ch!='\n') { printf("%c",ch); //输出换行符之前的字符 ch = fgetc(fp); } putchar(10); pos = ftell(fp); //文件指针当前位置 ch = fgetc(fp); printf("position = %ld\n",pos); printf("当前字符 = '%c'\n",ch); fclose(fp); putchar(10); } int main() { solution(); //cout << "Hello world!" << endl; return 0; }
可以看到 ‘h’属于第五个字符,从0开始的h看似应该是第4个字符啊!
原来ftell()函数 并没有把’\r’忽略掉!这样编程就会遇到问题。
如文件指针回退 ,假如当前字符为’e’,回退到’c’,应该是fseek(fp,-4L,1)而不是fseek(fp,-3L,1)。这个问题让我找了两个小时的错误!
都到了崩溃的边缘了。~.~!
《完》
相关文章推荐
- C/C++复习:不等长字符串排序(2)
- C++程序员面试总结
- C++第四次实验-图形输出与穷举法
- 九大排序算法-c语言实现及详解
- C++ 类的前置声明
- C/C++复习:指针引出奇数因子
- Java调用C++类库--JNI
- 删除指定文件夹里的文件(1)
- 一个简单的MD5加密算法C语言示例程序
- C/C++复习:整数排序(2)
- (转)C/C++ 中使用正则表达式 v
- (转)C/C++ 中使用正则表达式
- 图解VC++开发ActiveX控件C#调用
- C/C++复习:逆向输出数字
- C/C++复习:数组作实参,指针作形参排序
- c++不常用功能之——内联函数
- C语言do while语句
- C语言while循环
- C语言for循环
- C++(学习笔记)——VS2015静态编译libcurl