我们都知道,要执行一个C语言的代码,要编译链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件、操作系统的启动代码和用到的库文件进行组织形成最终生成可执行代码的过程。我们用到的编写C语言的编辑器,你将写好的代码点击了一下编译,那么这些代码会被送到一个编译程序里面,将它转换为机器代码。那么编译器又是如何运作的呢?编译程序的工作过程可以划分为五个阶段:词法分析,语法分析,语义分析,中间代码生成和优化,目标代码生成。
首先来一段心情记录~~
写词法分析器是昨天下午的事,从两点多开始写,居然一直没有看手机(都有点出乎我的意料。。)边写边调,写一段测试一段,写了好多个小demo(比如十进制转二进制,将一个字符连接到字符串后面等)当然也犯了好多超级蠢的错误(比如直接用==来比较字符串,已被自己蠢哭)。当然也有写不下去的时候因为一个小问题一直有错,然后就去跑步了(自行想象一个摊手的表情)结果当然是跑完步问题就那样轻松被解决掉啦~~~开心。从此以后决定每天跑步。。。
OK!言归正传,直接上代码!!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//保留字表1-10
char keywords[15][10]={"int","break","else","char","return","continue","for","if","while","void"}; //int是1
//分隔符表11-23
char delimiters[20][5]={"=","+","*","**",";","(",")","{","}","<",">","#","."}; //=是11
//标识符表24
char id[30][10]={""};
//常数表25
int digitals[50]={};
//int j=0;
//int n=0;
int i;
int p=0,q=0;
int main(void){
FILE *file,*file1;
file1=fopen("词法分析器(结果).txt","w");
char string[100];
int j0,k0=0;
file=fopen("词法分析器.txt","r");//打开TXST.TxT文件
char ch0;
while((ch0=fgetc(file))!=EOF){
string[k0++]=ch0;
}
string[k0]='\0';
// printf("%s\n",string);
char str[100]={};
strcpy(str,string);
str[k0]='\0';
// printf("%s\n",str);
// int w;
// for(w=0;w<100;w++){
// str[w]=getchar();
// if(str[w]=='~'){
// str[w]='\0';
// break;
// }
// }
char ch;
int length=getLength(str);
printf("成功,请前往文件:词法分析器(结果).txt中查看!");
fprintf(file1,"class value\n");
for(i=0;i<length;i++){
char strToken[]={""};
ch=str[i];
if(ch==' '|| ch=='\n' ||ch=='\t'){
}
else if(IsLetter(ch)){ //第一位为字母
while((IsLetter(ch) || IsDigit(ch)) && i<length){
Concat(getLength(strToken),strToken,ch); //将ch连接在strToken后面
// printf("%d %s\n",i,strToken);
i=i+1; //指针向后移一位
ch=str[i]; //ch变为下一个ch
}
i=i-1; //指针向前移一位
int keyword=IsKeyword(strToken); //判断是否为保留字
// printf("%d",keyword);
if(keyword==0){ //为标识符
InsertId(strToken);
// printf("%s",id[0]);
// printf("24 %s\n",strToken);
fprintf(file1,"24 %s\n",strToken);
}
else{ //为保留字
// printf("%d %s\n",keyword,strToken);
fprintf(file1,"%d %s\n",keyword,strToken);
}
}
else if(IsDigit(ch)){ //为数字
while(IsDigit(ch)){
Concat(getLength(strToken),strToken,ch);
i=i+1;
ch=str[i];
}
i=i-1;
// printf("%s",strToken);
InsertDigit(Binary(atoi(strToken))); //将strToken字符串转化为数字插入到常数表中
// printf("%d\n",digitals[0]);
// printf("25 %d\n",Binary(atoi(strToken)));
fprintf(file1,"25 %d\n",Binary(atoi(strToken)));
}
else if(ch=='='){
// printf("%d =\n",IsDelimiter(ch));
fprintf(file1,"%d =\n",IsDelimiter(ch));
}
else if(ch=='+'){
// printf("%d +\n",IsDelimiter(ch));
fprintf(file1,"%d +\n",IsDelimiter(ch));
}
else if(ch=='*'){
i=i+1;
ch=str[i];
if(ch=='*'){
// printf("14 **\n");
fprintf(file1,"14 **\n");
}else{
// printf("13 *\n");
fprintf(file1,"13 *\n");
i=i-1;
}
// i=i-1;
// printf("%d *\n",IsDelimiter(str[i]));
}
else if(ch==';'){
fprintf(file1,"%d ;\n",IsDelimiter(ch));
}
else if(ch=='('){
fprintf(file1,"%d (\n",IsDelimiter(ch));
}
else if(ch==')'){
fprintf(file1,"%d )\n",IsDelimiter(ch));
}
else if(ch=='{'){
fprintf(file1,"%d {\n",IsDelimiter(ch));
}
else if(ch=='}'){
fprintf(file1,"%d }\n",IsDelimiter(ch));
}
else if(ch=='>'){
fprintf(file1,"%d >\n",IsDelimiter(ch));
}
else if(ch=='<'){
fprintf(file1,"%d <\n",IsDelimiter(ch));
}
else if(ch=='#'){
fprintf(file1,"%d #\n",IsDelimiter(ch));
}
else if(ch=='.'){
fprintf(file1,"%d .\n",IsDelimiter(ch));
}
else{
fprintf(file1,"错误!\n");
}
}
// while(j<100){
// if(ch[j]){
// strToken[j]=ch[j];
// j++;
// }else{
// strToken[j]='\0';
// break;
// }
// }
// int key=IsKeyword(strToken);
// printf("%d %s",key,strToken);
return 0;
}
//输入的字符串的长度的函数
int getLength(char *str){
int len=0,k;
for(k=0;k<100;k++){
if(str[k]!='\0'){
len++;
}else{
break;
}
}
return len;
}
//判断读入的字符是否为字母
int IsLetter(char *ch){
if(ch>='A'&&ch<='Z' || ch>='a'&&ch<='z'){
return 1;
}
return 0;
}
//判断读入的字符是否为数字
int IsDigit(char *ch){
if(ch>=48&&ch<=57){
return 1;
}
return 0;
}
//将字符连接到字符串后面
void Concat(int len,char *str,char ch){
str+=len;
*str=ch;
*(++str)='\0';
return;
}
//判断是否为保留字
int IsKeyword(char *ch){
int s;
for(s=0;s<15;s++){
if(strcmp(keywords[s],ch)==0){
return s+1; //若是保留字,则返回它的类别码
}
}
return 0;
}
//将标识符插入到标识符表中
InsertId(char *ch){
int t,j=0;
for(t=0;t<getLength(ch);t++){
id[p][j]=ch[j];
j++;
}
p++;
}
//将常数插入到常数表中
InsertDigit(int d){
digitals[q]=d;
q++;
}
//将一个数字转化为二进制
int Binary(int d){
int sum=0;int r=1;
while(d){
sum+=(d%2*r);
r*=10;
d=d/2;
}
return sum;
}
//判断是否为分隔符
int IsDelimiter(char *ch){
int h;
for(h=0;h<20;h++){
if(delimiters[h][0]==ch){
return h+11; //若是保留字,则返回它的类别码
}
}
return 0;
}
输入和输出说明:
1.输入:在同一目录下面放一个名为:词法分析器.txt的文件,里面放入源程序。
2.输出:在同一目录下面放一个名为:词法分析器(结果).txt的文件,里面存放形如class value的键值对。(保留字和分隔符一词一类,标识符为一类,常数为一类)
(1)保留字:class为该保留字的类别码(在此为1-9),value为该保留字。
(2)分隔符:class为该分隔符的类别码(在此为10-20),value为该保留字。
(3)标识符:class为标识符的类别码(在此为21),value为该标识符。
(4)常数:class为常数的类别码(在此为22),value为该常数的二进制。
代码简单说明:
(1)先建立四个表,其中保留字表和分隔符表初始化好值,以便到时候查询;标识符表和常数表分别初始化为空。
(2)判断是否为标识符,首先是要以字母开头,然后由字母数字组成。每读入一个,ch向后移一位。跳出循环意味着读入的ch既不是数字也不是字母,此时要将ch向前移一位,以便进行下一次的判断。
(3)判断乘号和幂。若输入的第一个字符为*
,接下来判断第二个字符是否也为*
,若是则为幂,否则为乘号。
(4)保留字表和分隔符表可能不太全面,到时候需要再添加即可。
(5)代码中都有注释和调试过程,看着会比较乱,谅解。