编译原理解析
2006-08-23 19:40
357 查看
编译原理是大学的主要课程之一。可是我们在学习时不一定很好的了解。主要的问题可能是课程抽象和没有太多的基础知识和程序结构的了解等。其实通过我前面的CPU设计就可是知道汇编语言是怎么在计算机上运行的了。下面就来介绍一下我们写的高级语言怎么转换为低级语言的。这样我们就可以比较完整的了解程序的运行原理。
编译原理一般分为词法分析,语法分析,语义分析和代码优化及目标代码生成等一系列过程。下面我们结合来了解一下:本文的程序主要使用了孙悦红的编译原理及实行书中的代码。并且她的代码我发邮件要了一份。本来我打了不少可是实在太多。最后的模拟机等部分我就懒得打了。就按照书上的说明要了一份。在此感谢孙老师。同时我修改了部分代码。为了更好的学习我发改了的部分给大家。
本程序可以同时在BC和Visual C++下运行。我测试过了的。下面先将代码贴给大家。其中如果你只想先了解词法分析部分,你可以将其余的注释就可以。我建议大家学习时一步一步来,从词法分析学,然后学语法分析。其他的就类似了。
如果你在DOS下运行,由于使用了汉字,请将其自行换成英文方便您的识别。
程序中的解释就是编译的基本原理。如果还不清楚建议大家看孙悦红的编译原理及实现清华的。
#include <stdio.h>
#include <ctype.h>
#include <conio.h>
#include "stdio.h"
//下面定义保留,为简化程序,使用字符指针数组保存所有保留字。
//如果想增加保留字,可继续添加,并修改保留字数目
#define keywordSum 8
#define maxvartablep 500//定义符号表的容量
char *keyword[keywordSum]={ "if","else","for","while","do","int","read","write"};
//下面定义纯单分界符,如需要可添加
char singleword[50]="+-*(){};,:";
//下面定义双分界符的首字符
char doubleword[10]="><=!";
//函数调用
int TESTparse();
int program();
int compound_stat();
int statement();
int expression_stat();
int expression();
int bool_expr();
int additive_expr();
int term();
int factor();
int if_stat();
int while_stat();
int for_stat();
int write_stat();
int read_stat();
int declaration_stat();
int declaration_list();
int statement_list();
int compound_stat();
int name_def(char *name);
char token[20],token1[40];//token保存单词符号,token1保存单词值
char Scanout[300],Codeout[300]; //保存词法分析输出文件名
FILE *fp,*fout; //用于指向输入输出文件的指针
struct{//定义符号表结构
char name[8];
int address;
}vartable[maxvartablep];//改符号表最多容纳maxvartablep个记录
int vartablep=0,labelp=0,datap=0;
int TESTscan();
char Scanin[300],Errorfile[300]; //用于接收输入输出以及错误文件名
FILE *fin; //用于指向输入输出文件的指针
int TESTscan()//词法分析函数
{
char ch,token[40]; //ch为每次读入的字符,token用于保存识别出的单词
int es=0,j,n; //es错误代码,0表示没有错误。j,n为临时变量,控制组合单词时的下标等
printf("请输入源程序文件名(包括路径):");
scanf("%s",Scanin);
printf("请输入词法分析输出文件名(包括路径):");
scanf("%s",Scanout);
if ((fin=fopen(Scanin,"r"))==NULL) //判断输入文件名是否正确
{
printf("/n打开词法分析输入文件出错!/n");
return(1);//输入文件出错返回错误代码1
}
if ((fout=fopen(Scanout,"w"))==NULL) //判断输出文件名是否正确
{
printf("/n创建词法分析输出文件出错!/n");
return(2); //输出文件出错返回错误代码2
}
ch=getc(fin);
while(ch!=EOF)
{
while (ch==' '||ch=='/n'||ch=='/t') ch=getc(fin);
if (ch==EOF) break;
if (isalpha(ch)) //如果是字母,则进行标识符处理
{
token[0]=ch; j=1;
ch=getc(fin);
while(isalnum(ch)) //如果是字母数字则组合标识符;如果不是则标识符组合结束
{
token[j++]=ch; //组合的标识符保存在token中
ch=getc(fin); //读下一个字符
}
token[j]='/0'; //标识符组合结束
//查保留字
n=0;
while ((n<keywordSum) && strcmp(token,keyword
)) n++;
if (n>=keywordSum) //不是保留字,输出标识符
fprintf(fout,"%s/t%s/n","ID",token); //输出标识符符号
else//是保留字,输出保留字
fprintf(fout,"%s/t%s/n",token,token); //输出保留字符号
} else if (isdigit(ch))//数字处理
{
token[0]=ch; j=1;
ch=getc(fin); //读下一个字符
while (isdigit(ch)) //如果是数字则组合整数;如果不是则整数组合结束
{
token[j++]=ch; //组合整数保存在token中
ch=getc(fin); //读下一个字符
}
token[j]='/0'; //整数组合结束
fprintf(fout,"%s/t%s/n","NUM",token); //输出整数符号
} else if (strchr(singleword,ch)>0) //单分符处理
{
token[0]=ch; token[1]='/0';
ch=getc(fin);//读下一个符号以便识别下一个单词
fprintf(fout,"%s/t%s/n",token,token); //输出单分界符符号
}else if (strchr(doubleword,ch)>0) //双分界符处理
{
token[0]=ch;
ch=getc(fin); //读下一个字符判断是否为双分界符
if (ch=='=') //如果是=,组合双分界符
{
token[1]=ch;token[2]='/0'; //组合双分界符结束
ch=getc(fin); //读下一个符号以便识别下一个单词
} else//不是=则为单分界符
token[1]='/0';
fprintf(fout,"%s/t%s/n",token,token); //输出单或双分界符符号
} else if (ch=='/') //注释处理
{
ch=getc(fin); //读下一个字符
if (ch=='*') //如果是*,则开始处理注释
{ char ch1;
ch1=getc(fin); //读下一个字符
do
{ ch=ch1;ch1=getc(fin);} //删除注释
while ((ch!='*' || ch1!='/')&&ch1!=EOF); //直到遇到注释结束符*/或文件尾
ch=getc(fin);//读下一个符号以便识别下一个单词
} else //不是*则处理单分界符/
{
token[0]='/'; token[1]='/0';
fprintf(fout,"%s/t%s/n",token,token); //输出单分界符/
}
} else//错误处理
{
token[0]=ch;token[1]='/0';
ch=getc(fin); //读下一个符号以便识别下一个单词
es=3; //设置错误代码
fprintf(fout,"%s/t%s/n","ERROR",token); //输出错误符号
}
}
fclose(fin);//关闭输入输出文件
fclose(fout);
return(es); //返回主程序
}
//语法、语义分析及代码生成
//插入符号表动作@name-def↓n, t的程序如下:
int name_def(char *name)
{
int i,es=0;
if (vartablep>=maxvartablep) return(21);
for(i=vartablep-1;i==0;i--)//查符号表
{
if (strcmp(vartable[i].name,name)==0)
{
es=22;//22表示变量重复声明
break;
}
}
if (es>0) return(es);
strcpy(vartable[vartablep].name,name);
vartable[vartablep].address=datap;
datap++;//分配一个单元,数据区指针加1
vartablep++;
return(es);
}
//查询符号表返回地址
int lookup(char *name,int *paddress)
{
int i,es=0;
for(i=0;i<vartablep;i++)
{
if (strcmp(vartable[i].name,name)==0)
{
*paddress=vartable[i].address;
return(es);
}
}
es=23;//变量没有声明
return(es);
}
//语法、语义分析及代码生成程序
int TESTparse()
{
int es=0;
if((fp=fopen(Scanout,"r"))==NULL)
{
printf("/n打开%s错误!/n",Scanout);
es=10;
return(es);
}
printf("请输入目标文件名(包括路径):");
scanf("%s",Codeout);
if((fout=fopen(Codeout,"w"))==NULL)
{
printf("/n创建%s错误!/n",Codeout);
es=10;
return(es);
}
if (es==0) es=program();
printf("==语法、语义分析及代码生成程序结果==/n");
switch(es)
{
case 0: printf("语法、语义分析成功并抽象机汇编生成代码!/n");break;
case 10: printf("打开文件 %s失败!/n",Scanout);break;
case 1: printf("缺少{!/n");break;
case 2: printf("缺少}!/n");break;
case 3: printf("缺少标识符!/n");break;
case 4: printf("少分号!/n");break;
case 5: printf("缺少(!/n");break;
case 6: printf("缺少)!/n");break;
case 7: printf("缺少操作数!/n");break;
case 21: printf("符号表溢出!/n");break;
case 22: printf("变量重复定义!/n");break;
case 23: printf("变量未声明!/n");break;
}
fclose(fp);
fclose(fout);
return(es);
}
//program::={<declaration_list><statement_list>}
int program()
{
int es=0,i;
fscanf(fp,"%s %s/n",token,token1);
printf("%s %s/n",token,token1);
if(strcmp(token,"{"))//判断是否'{'
{
es=1;
return(es);
}
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=declaration_list();
if (es>0) return(es);
printf(" 符号表/n");
printf(" 名字 地址/n");
for(i=0;i<vartablep;i++)
printf(" %s %d/n",vartable[i].name,vartable[i].address);
es=statement_list();
if (es>0) return(es);
if(strcmp(token,"}"))//判断是否'}'
{
es=2;
return(es);
}
fprintf(fout," STOP/n");//产生停止指令
return(es);
}
//<declaration_list>::=
//<declaration_list><declaration_stat>|<declaration_stat>
//改成<declaration_list>::={<declaration_stat>}
int declaration_list()
{
int es=0;
while (strcmp(token,"int")==0)
{
es=declaration_stat();
if (es>0) return(es);
}
return(es);
}
//<declaration_stat>↓vartablep,datap,codep ->int ID↑n@name-def↓n,t;
int declaration_stat()
{
int es=0;
fscanf(fp,"%s %s/n",&token,&token1);printf("%s %s/n",token,token1);
if (strcmp(token,"ID")) return(es=3); //不是标识符
es=name_def(token1);//插入符号表
if (es>0) return(es);
fscanf(fp,"%s %s/n",&token,&token1);printf("%s %s/n",token,token1);
if (strcmp(token,";") ) return(es=4);
fscanf(fp,"%s %s/n",&token,&token1);printf("%s %s/n",token,token1);
return(es);
}
//<statement_list>::=<statement_list><statement>|<statement>
//改成<statement_list>::={<statement>}
int statement_list()
{
int es=0;
while (strcmp(token,"}"))
{
es=statement();
if (es>0) return(es);
}
return(es);
}
//<statement>::= <if_stat>|<while_stat>|<for_stat>
// |<compound_stat> |<expression_stat>
int statement()
{
int es=0;
if (es==0 && strcmp(token,"if")==0) es=if_stat();//<IF语句>
if (es==0 && strcmp(token,"while")==0) es=while_stat();//<while语句>
if (es==0 && strcmp(token,"for")==0) es=for_stat();//<for语句>
//可在此处添加do语句调用
if (es==0 && strcmp(token,"read")==0) es=read_stat();//<read语句>
if (es==0 && strcmp(token,"write")==0) es=write_stat();//<write语句>
if (es==0 && strcmp(token,"{")==0) es=compound_stat();//<复合语句>
if (es==0 && (strcmp(token,"ID")==0||strcmp(token,"NUM")==0||strcmp(token,"(")==0)) es=expression_stat();//<表达式语句>
return(es);
}
//<IF_stat>::= if (<expr>) <statement > [else < statement >]
/*
if (<expression>)@BRF↑label1 <statement > @BR↑label2 @SETlabel↓label1
[ else < statement >] @SETlabel↓label2
其中动作符号的含义如下
@BRF↑label1 :输出 BRF label1,
@BR↑label2:输出 BR label2,
@SETlabel↓label1:设置标号label1
@SETlabel↓label2:设置标号label2
*/
int if_stat(){
int es=0,label1,label2; //if
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
if (strcmp(token,"(")) return(es=5); //少左括号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=expression();
if (es>0) return(es);
if (strcmp(token,")")) return(es=6); //少右括号
label1=labelp++;//用label1记住条件为假时要转向的标号
fprintf(fout," BRF LABEL%d/n",label1);//输出假转移指令
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=statement();
if (es>0) return(es);
label2=labelp++;//用label2记住要转向的标号
fprintf(fout," BR LABEL%d/n",label2);//输出无条件转移指令
fprintf(fout,"LABEL%d:/n",label1);//设置label1记住的标号
if (strcmp(token,"else")==0)//else部分处理
{
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=statement();
if (es>0) return(es);
}
fprintf(fout,"LABEL%d:/n",label2);//设置label2记住的标号
return(es);
}
//<while_stat>::= while (<expr >) < statement >
//<while_stat>::=while @SET↑labellabel1(<expression>) @BRF↑label2
// <statement >@BR↑label1 @SETlabel↓label2
//动作解释如下:
//@SETlabel↑label1:设置标号label1
//@BRF↑label2 :输出 BRF label2,
//@BR↑label1:输出 BR label1,
//@SETlabel↓label2:设置标号label2
int while_stat()
{
int es=0,label1,label2;
label1=labelp++;
fprintf(fout,"LABEL%d:/n",label1);//设置label1标号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
if (strcmp(token,"(")) return(es=5); //少左括号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=expression();
if (es>0) return(es);
if (strcmp(token,")")) return(es=6); //少右括号
label2=labelp++;
fprintf(fout," BRF LABEL%d/n",label2);//输出假转移指令
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=statement();
if (es>0) return(es);
fprintf(fout," BR LABEL%d/n",label1);//输出无条件转移指令
fprintf(fout,"LABEL%d:/n",label2);//设置label2标号
return(es);
}
//<for_stat>::= for(<expr>,<expr>,<expr>)<statement>
/*
<for_stat>::=for (<expression>;
@SETlabel↓label1< expression >@BRF↑label2@BR↑label3;
@SETlabel↓label4 < expression >@BR↑label1)
@SETlabel↓label3 <语句 >@BR↑label4@SETlabel↓label2
动作解释:
1. @SETlabel↓label1:设置标号label1
2. @BRF↑label2 :输出 BRF label2,
3. @BR↑label3:输出 BR label3,
4. @SETlabel↓label4:设置标号label4
5. @BR↑label1:输出 BR label1,
6. @SETlabel↓label3:设置标号label3
7. @BR↑label4:输出 BR label4,
8. @SETlabel↓label2:设置标号label2
*/
int for_stat()
{
int es=0,label1,label2,label3,label4;
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
if (strcmp(token,"(")) return(es=5); //少左括号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=expression();
if (es>0) return(es);
if (strcmp(token,";")) return(es=4); //少分号
label1=labelp++;
fprintf(fout,"LABEL%d:/n",label1);//设置label1标号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=expression();
if (es>0) return(es);
label2=labelp++;
fprintf(fout," BRF LABEL%d/n",label2);//输出假条件转移指令
label3=labelp++;
fprintf(fout," BR LABEL%d/n",label3);//输出无条件转移指令
if (strcmp(token,";")) return(es=4); //少分号
label4=labelp++;
fprintf(fout,"LABEL%d:/n",label4);//设置label4标号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=expression();
if (es>0) return(es);
fprintf(fout," BR LABEL%d/n",label1);//输出无条件转移指令
if (strcmp(token,")")) return(es=6); //少右括号
fprintf(fout,"LABEL%d:/n",label3);//设置label3标号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=statement();
if (es>0) return(es);
fprintf(fout," BR LABEL%d/n",label4);//输出无条件转移指令
fprintf(fout,"LABEL%d:/n",label2);//设置label2标号
return(es);
}
//<write_stat>::=write <expression>;
//<write_stat>::=write <expression>@OUT;
//动作解释:
//@ OUT:输出 OUT
int write_stat()
{
int es=0;
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=expression();
if (es>0)return(es);
if (strcmp(token,";")) return(es=4); //少分号
fprintf(fout," OUT/n");//输出指令
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
return(es);
}
//<read_stat>::=read ID;
//<read_stat>::=read ID↑n LOOK↓n↑d @IN@STI↓d;
//动作解释:
//@LOOK↓n↑d:查符号表n,给出变量地址d; 没有,变量没定义
//@IN:输出IN
//@STI↓d:输出指令代码STI d
int read_stat()
{
int es=0,address;
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
if (strcmp(token,"ID")) return(es=3); //少标识符
es=lookup(token1,&address);
if (es>0) return(es);
fprintf(fout," IN /n");//输入指令
fprintf(fout," STI %d/n",address);//指令
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
if (strcmp(token,";")) return(es=4); //少分号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
return(es);
}
//<compound_stat>::={<statement_list>}
int compound_stat(){ //复合语句函数
int es=0;
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=statement_list();
return(es);
}
//<expression_stat>::=<expression>;|;
int expression_stat()
{
int es=0;
if (strcmp(token,";")==0)
{
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
return(es);
}
es=expression();
if (es>0) return(es);
if (strcmp(token,";")==0)
{
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
return(es);
} else
{
es=4;
return(es);//少分号
}
}
//<expression>::=ID↑n@LOOK↓n↑d@ASSIGN=<bool_expr>@STO↓d |<bool_expr>
int expression()
{
int es=0,fileadd;
char token2[20],token3[40];
if (strcmp(token,"ID")==0)
{
fileadd=ftell(fp); //@ASSIGN记住当前文件位置
fscanf(fp,"%s %s/n", &token2,&token3);
printf("%s %s/n",token2,token3);
if (strcmp(token2,"=")==0) //'='
{
int address;
es=lookup(token1,&address);
if (es>0) return(es);
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=bool_expr();
if (es>0) return(es);
fprintf(fout," STO %d/n",address);
} else
{
fseek(fp,fileadd,0); //若非'='则文件指针回到'='前的标识符
printf("%s %s/n",token,token1);
es=bool_expr();
if (es>0) return(es);
}
} else es=bool_expr();
return(es);
}
//<bool_expr>::=<additive_expr>
// |< additive_expr >(>|<|>=|<=|==|!=)< additive_expr >
/*
<bool_expr>::=<additive_expr>
|< additive_expr >><additive_expr>@GT
|< additive_expr ><<additive_expr>@LES
|< additive_expr >>=<additive_expr >@GE
|< additive_expr ><=< additive_expr >@LE
|< additive_expr >==< additive_expr >@EQ
|< additive_expr >!=< additive_expr >@NOTEQ
*/
int bool_expr()
{
int es=0;
es=additive_expr();
if(es>0) return(es);
if ( strcmp(token,">")==0 || strcmp(token,">=")==0
||strcmp(token,"<")==0 || strcmp(token,"<=")==0
||strcmp(token,"==")==0|| strcmp(token,"!=")==0)
{
char token2[20];
strcpy(token2,token);//保存运算符
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=additive_expr();
if(es>0) return(es);
if ( strcmp(token2,">")==0 ) fprintf(fout," GT/n");
if ( strcmp(token2,">=")==0 ) fprintf(fout," GE/n");
if ( strcmp(token2,"<")==0 ) fprintf(fout," LES/n");
if ( strcmp(token2,"<=")==0 ) fprintf(fout," LE/n");
if ( strcmp(token2,"==")==0 ) fprintf(fout," EQ/n");
if ( strcmp(token2,"!=")==0 ) fprintf(fout," NOTEQ/n");
}
return(es);
}
//<additive_expr>::=<term>{(+|-)< term >}
//< additive_expr>::=<term>{(+< term >@ADD |-<项>@SUB)}
int additive_expr()
{
int es=0;
es=term();
if(es>0) return(es);
while (strcmp(token,"+")==0 || strcmp(token,"-")==0)
{
char token2[20];
strcpy(token2,token);
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=term();
if(es>0) return(es);
if ( strcmp(token2,"+")==0 ) fprintf(fout," ADD/n");
if ( strcmp(token2,"-")==0 ) fprintf(fout," SUB/n");
}
return(es);
}
//< term >::=<factor>{(*| /)< factor >}
//< term >::=<factor>{(*< factor >@MULT | /< factor >@DIV)}
int term()
{
int es=0;
es=factor();
if(es>0) return(es);
while (strcmp(token,"*")==0 || strcmp(token,"/")==0)
{
char token2[20];
strcpy(token2,token);
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=factor();
if(es>0) return(es);
if ( strcmp(token2,"*")==0 ) fprintf(fout," MULT/n");
if ( strcmp(token2,"/")==0 ) fprintf(fout," DIV/n");
}
return(es);
}
//< factor >::=(<additive_expr>)| ID|NUM
//< factor >::=(< expression >)| ID↑n@LOOK↓n↑d@LOAD↓d |NUM↑i@LOADI↓i
int factor()
{
int es=0;
if (strcmp(token,"(")==0)
{
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=expression();
if (es>0) return(es);
if (strcmp(token,")")) return(es=6); //少右括号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
} else
{
if (strcmp(token,"ID")==0)
{
int address;
es=lookup(token1,&address);//查符号表,获取变量地址
if (es>0) return(es);//变量没声明
fprintf(fout," LOAD %d/n",address);
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
return(es);
}
if (strcmp(token,"NUM")==0)
{
fprintf(fout," LOADI %s/n",token1);
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
return(es);
}else
{
es=7;//缺少操作数
return(es);
}
}
return(es);
}
//主程序
void main(){
int es=0;
es=TESTscan();//调词法分析
if (es>0) printf("词法分析有错,编译停止!");
else printf("词法分析成功!/n");
if (es==0)
{
es=TESTparse(); //调语法、语义分析并生成代码
if (es==0) printf("语法、语义分析并生成代码成功!/n");
else printf("语法、语义分析并生成代码错误!/n");
}
}
下面我们可以进行测试:如下我挑了几个典型的。大家可以看看。
下面就是一个从高级语言到低级语言的转变过程:
Lexical.txt:
{
int a;
int bb;
/*this is a test*/
a=20;
bb=10*a;
}
Grammar.txt:
{ {
int int
ID a
; ;
int int
ID bb
; ;
ID a
= =
NUM 20
; ;
ID bb
= =
NUM 10
* *
ID a
; ;
} }
Translation.txt:
LOADI 20
STO 0
LOADI 10
LOAD 0
MULT
STO 1
STOP
从前面的图中我们可以看到a对应的内存地址单元为0,b为1。LOADI 20 读入20这个立即数, STO 0将20存入内存地址为0的 单元,LOADI 10 读入10这个立即数。读出内存地址为0的单元内容,MULT 进行乘法运算,STO 1 存入内存地址为1的单元。STOP 程序运行结束。
下面是程序部分在VC内的内存存储:
- code 0x0012a028
+ [0] 0x0012a028 "LOADI"
+ [1] 0x0012a03c "20"
+ [2] 0x0012a050 "STO"
+ [3] 0x0012a064 "0"
+ [4] 0x0012a078 "LOADI"
+ [5] 0x0012a08c "10"
+ [6] 0x0012a0a0 "LOAD"
+ [7] 0x0012a0b4 "0"
+ [8] 0x0012a0c8 "MULT"
+ [9] 0x0012a0dc "STO"
+ [10] 0x0012a0f0 "1"
+ [11] 0x0012a104 "STOP"
+ [12] 0x0012a118 “*****”
- [0] 0x0012a028 "LOADI"
[0] 0x4c 'L'
[1] 0x4f 'O'
[2] 0x41 'A'
[3] 0x44 'D'
[4] 0x49 'I'
[5] 0x00 ''
[6] 0xcc '?
[7] 0xcc '?
[8] 0xcc '?
[9] 0xcc '?
[10] 0xcc '?
[11] 0xcc '?
[12] 0xcc '?
[13] 0xcc '?
[14] 0xcc '?
[15] 0xcc '?
[16] 0xcc '?
[17] 0xcc '?
[18] 0xcc '?
[19] 0xcc '?
+ [1] 0x0012a03c "20"
//<for_stat>::= for(<expr>,<expr>,<expr>)<statement>
/*
<for_stat>::=for (<expression>;
@SETlabel↓label1< expression >@BRF↑label2@BR↑label3;
@SETlabel↓label4 < expression >@BR↑label1)
@SETlabel↓label3 <语句 >@BR↑label4@SETlabel↓label2
动作解释:
1. @SETlabel↓label1:设置标号label1
2. @BRF↑label2 :输出 BRF label2,
3. @BR↑label3:输出 BR label3,
4. @SETlabel↓label4:设置标号label4
5. @BR↑label1:输出 BR label1,
6. @SETlabel↓label3:设置标号label3
7. @BR↑label4:输出 BR label4,
8. @SETlabel↓label2:设置标号label2
*/
{
int a;
int b;
a=0;
b=0;
for(a=0;a<=10;a=a+1)
{
b=b+a;
}
}
词法分析
{ {
int int
ID a
; ;
int int
ID b
; ;
ID a
= =
NUM 0
; ;
ID b
= =
NUM 0
; ;
for for
( (
ID a
= =
NUM 0
; ;
ID a
<= <=
NUM 10
; ;
ID a
= =
ID a
+ +
NUM 1
) )
{ {
ID b
= =
ID b
+ +
ID a
; ;
} }
} }
语义分析:
LOADI 0
STO 0
LOADI 0
STO 1
LOADI 0
STO 0
LABEL0:
LOAD 0
LOADI 10
LE
BRF LABEL1
BR LABEL2
LABEL3:
LOAD 0
LOADI 1
ADD
STO 0
BR LABEL0
LABEL2:
LOAD 1
LOAD 0
ADD
STO 1
BR LABEL3
LABEL1:
STOP
//<IF_stat>::= if (<expr>) <statement > [else < statement >]
/*
if (<expression>)@BRF↑label1 <statement > @BR↑label2 @SETlabel↓label1
[ else < statement >] @SETlabel↓label2
其中动作符号的含义如下
@BRF↑label1 :输出 BRF label1,
@BR↑label2:输出 BR label2,
@SETlabel↓label1:设置标号label1
@SETlabel↓label2:设置标号label2
*/
IF
If.txt
{
int a;
a=3;
if(a!=1)
{
a=1;
}
}
Ifp.txt
{ {
int int
ID a
; ;
ID a
= =
NUM 3
; ;
if if
( (
ID a
!= !=
NUM 1
) )
{ {
ID a
= =
NUM 1
; ;
} }
} }
Ift.txt
LOADI 3
STO 0
LOAD 0
LOADI 1
NOTEQ
BRF LABEL0
LOADI 1
STO 0
BR LABEL1
LABEL0:
LABEL1:
STOP
While
//<while_stat>::= while (<expr >) < statement >
//<while_stat>::=while @SET↑labellabel1(<expression>) @BRF↑label2
// <statement >@BR↑label1 @SETlabel↓label2
//动作解释如下:
//@SETlabel↑label1:设置标号label1
//@BRF↑label2 :输出 BRF label2,
//@BR↑label1:输出 BR label1,
//@SETlabel↓label2:设置标号label2
While.txt
{
int a;
a=0;
while(a<5)
{
a=a+1;
}
}
Whilep.txt
{ {
int int
ID a
; ;
ID a
= =
NUM 0
; ;
while while
( (
ID a
< <
NUM 5
) )
{ {
ID a
= =
ID a
+ +
NUM 1
; ;
} }
} }
Whilet.txt
LOADI 0
STO 0
LABEL0:
LOAD 0
LOADI 5
LES
BRF LABEL1
LOAD 0
LOADI 1
ADD
STO 0
BR LABEL0
LABEL1:
STOP
Wirte
{
int a;
a=10;
write a;
}
//语义
LOADI 10
STO 0
LOAD 0
OUT
STOP
Read
{
int a;
a=1;
read a;
}
LOADI 1
STO 0
IN
STI 0
STOP
在如上的分析后,我们就看到其已经转换为类似汇编语言格式的语言。结合前面的精简计算机系统,这样我们就可以明白了它是怎么在计算机中运行的了。在孙悦红的书中还有Test语言的模拟机。大家可以看看,其实程序的基本运行原理也是类似。谢谢!
Lexical.txt:
{
int a;
int bb;
/*this is a test*/
a=20;
bb=10*a;
}
Grammar.txt:
{ {
int int
ID a
; ;
int int
ID bb
; ;
ID a
= =
NUM 20
; ;
ID bb
= =
NUM 10
* *
ID a
; ;
} }
Lexical.txt:
{
int a;
int bb;
/*this is a test*/
a=20;
bb=10*a;
}
Grammar.txt:
{ {
int int
ID a
; ;
int int
ID bb
; ;
ID a
= =
NUM 20
; ;
ID bb
= =
NUM 10
* *
ID a
; ;
} }
Lexical.txt:
{
int a;
int bb;
/*this is a test*/
a=20;
bb=10*a;
}
Grammar.txt:
{ {
int int
ID a
; ;
int int
ID bb
; ;
ID a
= =
NUM 20
; ;
ID bb
= =
NUM 10
* *
ID a
; ;
} }
最后,我们来看看前面的for循环在BC下的编译结果如下:
从#1#8开始:
┌─────Module: 1 File: ../../1.C 12────────────────────────────────────1────────┐
│ #include "stdio.h" │
│► void main() │
│ { ╔═[■]═CPU 80486═══════════════════════════════╤═══════3═[↑][↓]═╗
│ int a; ║#1#8: for(a=0;a<=10;a=a+1) ▲ ax 0100 │c=0║
│ int b; ║ cs:02C9 33D2 xor dx,dx ■ bx 028C │z=1║
│ a=0; ║ cs:02CB EB0B jmp 02D8 ▒ cx 0001 │s=0║
│ b=0; ║#1#10: b=b+a; ▒ dx 028C │o=0║
│ for(a=0;a<=10║ cs:02CD 8BC3 mov ax,bx ▒ si 0286 │p=1║
│ { ║ cs:02CF 03C2 add ax,dx ▒ di 028C │a=0║
│ b=b+a; ║ cs:02D1 8BD8 mov bx,ax ▒ bp 0000 │i=1║
│ } ║ cs:02D3 8BC2 mov ax,dx ▒ sp FFF8 │d=0║
│ } ║ cs:02D5 40 inc ax ▒ ds 66A1 │ ║
│ ║ cs:02D6 8BD0 mov dx,ax ▒ es 66A1 │ ║
│ ║ cs:02D8 83FA0A cmp dx,000A ▒ ss 66A1 │ ║
│ ║ cs:02DB 7EF0 jle #1#10 (02CD) ▒ cs 65E1 │ ║
│ ║#1#12: } ▼ ip 02C2 │ ║
│ ╟◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►┤ │ ║
│ ║ ds:0000 00 00 00 00 42 6F 72 6C Borl │ │ ║
└───────────────║ ds:0008 61 6E 64 20 43 2B 2B 20 and C++ ├────────────┴───╢
┌─────Watches───║ ds:0010 2D 20 43 6F 70 79 72 69 - Copyri │ ss:FFFA 0000 ║
│ ║ ds:0018 67 68 74 20 31 39 39 31 ght 1991 │ ss:FFF8►015B ║
└───────────────╚═════════════════════════════════════════════╧═══════════════─┘
F1-Help F2-Bkpt F3-Mod F4-Here F5-Zoom F6-Next F7-Trace F8-Step F9-Run F10-Menu
这样,我们就大概明白和了解了编译的基本原理了。
从上面的翻译看来和我们前面的翻译有点区别,这就是实际的使用了。其中还有很多其他的知识比如堆等及CPU的寄存器等。另外学习。还有其他的翻译大家自己试试看。谢谢!
编译原理一般分为词法分析,语法分析,语义分析和代码优化及目标代码生成等一系列过程。下面我们结合来了解一下:本文的程序主要使用了孙悦红的编译原理及实行书中的代码。并且她的代码我发邮件要了一份。本来我打了不少可是实在太多。最后的模拟机等部分我就懒得打了。就按照书上的说明要了一份。在此感谢孙老师。同时我修改了部分代码。为了更好的学习我发改了的部分给大家。
本程序可以同时在BC和Visual C++下运行。我测试过了的。下面先将代码贴给大家。其中如果你只想先了解词法分析部分,你可以将其余的注释就可以。我建议大家学习时一步一步来,从词法分析学,然后学语法分析。其他的就类似了。
如果你在DOS下运行,由于使用了汉字,请将其自行换成英文方便您的识别。
程序中的解释就是编译的基本原理。如果还不清楚建议大家看孙悦红的编译原理及实现清华的。
#include <stdio.h>
#include <ctype.h>
#include <conio.h>
#include "stdio.h"
//下面定义保留,为简化程序,使用字符指针数组保存所有保留字。
//如果想增加保留字,可继续添加,并修改保留字数目
#define keywordSum 8
#define maxvartablep 500//定义符号表的容量
char *keyword[keywordSum]={ "if","else","for","while","do","int","read","write"};
//下面定义纯单分界符,如需要可添加
char singleword[50]="+-*(){};,:";
//下面定义双分界符的首字符
char doubleword[10]="><=!";
//函数调用
int TESTparse();
int program();
int compound_stat();
int statement();
int expression_stat();
int expression();
int bool_expr();
int additive_expr();
int term();
int factor();
int if_stat();
int while_stat();
int for_stat();
int write_stat();
int read_stat();
int declaration_stat();
int declaration_list();
int statement_list();
int compound_stat();
int name_def(char *name);
char token[20],token1[40];//token保存单词符号,token1保存单词值
char Scanout[300],Codeout[300]; //保存词法分析输出文件名
FILE *fp,*fout; //用于指向输入输出文件的指针
struct{//定义符号表结构
char name[8];
int address;
}vartable[maxvartablep];//改符号表最多容纳maxvartablep个记录
int vartablep=0,labelp=0,datap=0;
int TESTscan();
char Scanin[300],Errorfile[300]; //用于接收输入输出以及错误文件名
FILE *fin; //用于指向输入输出文件的指针
int TESTscan()//词法分析函数
{
char ch,token[40]; //ch为每次读入的字符,token用于保存识别出的单词
int es=0,j,n; //es错误代码,0表示没有错误。j,n为临时变量,控制组合单词时的下标等
printf("请输入源程序文件名(包括路径):");
scanf("%s",Scanin);
printf("请输入词法分析输出文件名(包括路径):");
scanf("%s",Scanout);
if ((fin=fopen(Scanin,"r"))==NULL) //判断输入文件名是否正确
{
printf("/n打开词法分析输入文件出错!/n");
return(1);//输入文件出错返回错误代码1
}
if ((fout=fopen(Scanout,"w"))==NULL) //判断输出文件名是否正确
{
printf("/n创建词法分析输出文件出错!/n");
return(2); //输出文件出错返回错误代码2
}
ch=getc(fin);
while(ch!=EOF)
{
while (ch==' '||ch=='/n'||ch=='/t') ch=getc(fin);
if (ch==EOF) break;
if (isalpha(ch)) //如果是字母,则进行标识符处理
{
token[0]=ch; j=1;
ch=getc(fin);
while(isalnum(ch)) //如果是字母数字则组合标识符;如果不是则标识符组合结束
{
token[j++]=ch; //组合的标识符保存在token中
ch=getc(fin); //读下一个字符
}
token[j]='/0'; //标识符组合结束
//查保留字
n=0;
while ((n<keywordSum) && strcmp(token,keyword
)) n++;
if (n>=keywordSum) //不是保留字,输出标识符
fprintf(fout,"%s/t%s/n","ID",token); //输出标识符符号
else//是保留字,输出保留字
fprintf(fout,"%s/t%s/n",token,token); //输出保留字符号
} else if (isdigit(ch))//数字处理
{
token[0]=ch; j=1;
ch=getc(fin); //读下一个字符
while (isdigit(ch)) //如果是数字则组合整数;如果不是则整数组合结束
{
token[j++]=ch; //组合整数保存在token中
ch=getc(fin); //读下一个字符
}
token[j]='/0'; //整数组合结束
fprintf(fout,"%s/t%s/n","NUM",token); //输出整数符号
} else if (strchr(singleword,ch)>0) //单分符处理
{
token[0]=ch; token[1]='/0';
ch=getc(fin);//读下一个符号以便识别下一个单词
fprintf(fout,"%s/t%s/n",token,token); //输出单分界符符号
}else if (strchr(doubleword,ch)>0) //双分界符处理
{
token[0]=ch;
ch=getc(fin); //读下一个字符判断是否为双分界符
if (ch=='=') //如果是=,组合双分界符
{
token[1]=ch;token[2]='/0'; //组合双分界符结束
ch=getc(fin); //读下一个符号以便识别下一个单词
} else//不是=则为单分界符
token[1]='/0';
fprintf(fout,"%s/t%s/n",token,token); //输出单或双分界符符号
} else if (ch=='/') //注释处理
{
ch=getc(fin); //读下一个字符
if (ch=='*') //如果是*,则开始处理注释
{ char ch1;
ch1=getc(fin); //读下一个字符
do
{ ch=ch1;ch1=getc(fin);} //删除注释
while ((ch!='*' || ch1!='/')&&ch1!=EOF); //直到遇到注释结束符*/或文件尾
ch=getc(fin);//读下一个符号以便识别下一个单词
} else //不是*则处理单分界符/
{
token[0]='/'; token[1]='/0';
fprintf(fout,"%s/t%s/n",token,token); //输出单分界符/
}
} else//错误处理
{
token[0]=ch;token[1]='/0';
ch=getc(fin); //读下一个符号以便识别下一个单词
es=3; //设置错误代码
fprintf(fout,"%s/t%s/n","ERROR",token); //输出错误符号
}
}
fclose(fin);//关闭输入输出文件
fclose(fout);
return(es); //返回主程序
}
//语法、语义分析及代码生成
//插入符号表动作@name-def↓n, t的程序如下:
int name_def(char *name)
{
int i,es=0;
if (vartablep>=maxvartablep) return(21);
for(i=vartablep-1;i==0;i--)//查符号表
{
if (strcmp(vartable[i].name,name)==0)
{
es=22;//22表示变量重复声明
break;
}
}
if (es>0) return(es);
strcpy(vartable[vartablep].name,name);
vartable[vartablep].address=datap;
datap++;//分配一个单元,数据区指针加1
vartablep++;
return(es);
}
//查询符号表返回地址
int lookup(char *name,int *paddress)
{
int i,es=0;
for(i=0;i<vartablep;i++)
{
if (strcmp(vartable[i].name,name)==0)
{
*paddress=vartable[i].address;
return(es);
}
}
es=23;//变量没有声明
return(es);
}
//语法、语义分析及代码生成程序
int TESTparse()
{
int es=0;
if((fp=fopen(Scanout,"r"))==NULL)
{
printf("/n打开%s错误!/n",Scanout);
es=10;
return(es);
}
printf("请输入目标文件名(包括路径):");
scanf("%s",Codeout);
if((fout=fopen(Codeout,"w"))==NULL)
{
printf("/n创建%s错误!/n",Codeout);
es=10;
return(es);
}
if (es==0) es=program();
printf("==语法、语义分析及代码生成程序结果==/n");
switch(es)
{
case 0: printf("语法、语义分析成功并抽象机汇编生成代码!/n");break;
case 10: printf("打开文件 %s失败!/n",Scanout);break;
case 1: printf("缺少{!/n");break;
case 2: printf("缺少}!/n");break;
case 3: printf("缺少标识符!/n");break;
case 4: printf("少分号!/n");break;
case 5: printf("缺少(!/n");break;
case 6: printf("缺少)!/n");break;
case 7: printf("缺少操作数!/n");break;
case 21: printf("符号表溢出!/n");break;
case 22: printf("变量重复定义!/n");break;
case 23: printf("变量未声明!/n");break;
}
fclose(fp);
fclose(fout);
return(es);
}
//program::={<declaration_list><statement_list>}
int program()
{
int es=0,i;
fscanf(fp,"%s %s/n",token,token1);
printf("%s %s/n",token,token1);
if(strcmp(token,"{"))//判断是否'{'
{
es=1;
return(es);
}
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=declaration_list();
if (es>0) return(es);
printf(" 符号表/n");
printf(" 名字 地址/n");
for(i=0;i<vartablep;i++)
printf(" %s %d/n",vartable[i].name,vartable[i].address);
es=statement_list();
if (es>0) return(es);
if(strcmp(token,"}"))//判断是否'}'
{
es=2;
return(es);
}
fprintf(fout," STOP/n");//产生停止指令
return(es);
}
//<declaration_list>::=
//<declaration_list><declaration_stat>|<declaration_stat>
//改成<declaration_list>::={<declaration_stat>}
int declaration_list()
{
int es=0;
while (strcmp(token,"int")==0)
{
es=declaration_stat();
if (es>0) return(es);
}
return(es);
}
//<declaration_stat>↓vartablep,datap,codep ->int ID↑n@name-def↓n,t;
int declaration_stat()
{
int es=0;
fscanf(fp,"%s %s/n",&token,&token1);printf("%s %s/n",token,token1);
if (strcmp(token,"ID")) return(es=3); //不是标识符
es=name_def(token1);//插入符号表
if (es>0) return(es);
fscanf(fp,"%s %s/n",&token,&token1);printf("%s %s/n",token,token1);
if (strcmp(token,";") ) return(es=4);
fscanf(fp,"%s %s/n",&token,&token1);printf("%s %s/n",token,token1);
return(es);
}
//<statement_list>::=<statement_list><statement>|<statement>
//改成<statement_list>::={<statement>}
int statement_list()
{
int es=0;
while (strcmp(token,"}"))
{
es=statement();
if (es>0) return(es);
}
return(es);
}
//<statement>::= <if_stat>|<while_stat>|<for_stat>
// |<compound_stat> |<expression_stat>
int statement()
{
int es=0;
if (es==0 && strcmp(token,"if")==0) es=if_stat();//<IF语句>
if (es==0 && strcmp(token,"while")==0) es=while_stat();//<while语句>
if (es==0 && strcmp(token,"for")==0) es=for_stat();//<for语句>
//可在此处添加do语句调用
if (es==0 && strcmp(token,"read")==0) es=read_stat();//<read语句>
if (es==0 && strcmp(token,"write")==0) es=write_stat();//<write语句>
if (es==0 && strcmp(token,"{")==0) es=compound_stat();//<复合语句>
if (es==0 && (strcmp(token,"ID")==0||strcmp(token,"NUM")==0||strcmp(token,"(")==0)) es=expression_stat();//<表达式语句>
return(es);
}
//<IF_stat>::= if (<expr>) <statement > [else < statement >]
/*
if (<expression>)@BRF↑label1 <statement > @BR↑label2 @SETlabel↓label1
[ else < statement >] @SETlabel↓label2
其中动作符号的含义如下
@BRF↑label1 :输出 BRF label1,
@BR↑label2:输出 BR label2,
@SETlabel↓label1:设置标号label1
@SETlabel↓label2:设置标号label2
*/
int if_stat(){
int es=0,label1,label2; //if
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
if (strcmp(token,"(")) return(es=5); //少左括号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=expression();
if (es>0) return(es);
if (strcmp(token,")")) return(es=6); //少右括号
label1=labelp++;//用label1记住条件为假时要转向的标号
fprintf(fout," BRF LABEL%d/n",label1);//输出假转移指令
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=statement();
if (es>0) return(es);
label2=labelp++;//用label2记住要转向的标号
fprintf(fout," BR LABEL%d/n",label2);//输出无条件转移指令
fprintf(fout,"LABEL%d:/n",label1);//设置label1记住的标号
if (strcmp(token,"else")==0)//else部分处理
{
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=statement();
if (es>0) return(es);
}
fprintf(fout,"LABEL%d:/n",label2);//设置label2记住的标号
return(es);
}
//<while_stat>::= while (<expr >) < statement >
//<while_stat>::=while @SET↑labellabel1(<expression>) @BRF↑label2
// <statement >@BR↑label1 @SETlabel↓label2
//动作解释如下:
//@SETlabel↑label1:设置标号label1
//@BRF↑label2 :输出 BRF label2,
//@BR↑label1:输出 BR label1,
//@SETlabel↓label2:设置标号label2
int while_stat()
{
int es=0,label1,label2;
label1=labelp++;
fprintf(fout,"LABEL%d:/n",label1);//设置label1标号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
if (strcmp(token,"(")) return(es=5); //少左括号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=expression();
if (es>0) return(es);
if (strcmp(token,")")) return(es=6); //少右括号
label2=labelp++;
fprintf(fout," BRF LABEL%d/n",label2);//输出假转移指令
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=statement();
if (es>0) return(es);
fprintf(fout," BR LABEL%d/n",label1);//输出无条件转移指令
fprintf(fout,"LABEL%d:/n",label2);//设置label2标号
return(es);
}
//<for_stat>::= for(<expr>,<expr>,<expr>)<statement>
/*
<for_stat>::=for (<expression>;
@SETlabel↓label1< expression >@BRF↑label2@BR↑label3;
@SETlabel↓label4 < expression >@BR↑label1)
@SETlabel↓label3 <语句 >@BR↑label4@SETlabel↓label2
动作解释:
1. @SETlabel↓label1:设置标号label1
2. @BRF↑label2 :输出 BRF label2,
3. @BR↑label3:输出 BR label3,
4. @SETlabel↓label4:设置标号label4
5. @BR↑label1:输出 BR label1,
6. @SETlabel↓label3:设置标号label3
7. @BR↑label4:输出 BR label4,
8. @SETlabel↓label2:设置标号label2
*/
int for_stat()
{
int es=0,label1,label2,label3,label4;
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
if (strcmp(token,"(")) return(es=5); //少左括号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=expression();
if (es>0) return(es);
if (strcmp(token,";")) return(es=4); //少分号
label1=labelp++;
fprintf(fout,"LABEL%d:/n",label1);//设置label1标号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=expression();
if (es>0) return(es);
label2=labelp++;
fprintf(fout," BRF LABEL%d/n",label2);//输出假条件转移指令
label3=labelp++;
fprintf(fout," BR LABEL%d/n",label3);//输出无条件转移指令
if (strcmp(token,";")) return(es=4); //少分号
label4=labelp++;
fprintf(fout,"LABEL%d:/n",label4);//设置label4标号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=expression();
if (es>0) return(es);
fprintf(fout," BR LABEL%d/n",label1);//输出无条件转移指令
if (strcmp(token,")")) return(es=6); //少右括号
fprintf(fout,"LABEL%d:/n",label3);//设置label3标号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=statement();
if (es>0) return(es);
fprintf(fout," BR LABEL%d/n",label4);//输出无条件转移指令
fprintf(fout,"LABEL%d:/n",label2);//设置label2标号
return(es);
}
//<write_stat>::=write <expression>;
//<write_stat>::=write <expression>@OUT;
//动作解释:
//@ OUT:输出 OUT
int write_stat()
{
int es=0;
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=expression();
if (es>0)return(es);
if (strcmp(token,";")) return(es=4); //少分号
fprintf(fout," OUT/n");//输出指令
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
return(es);
}
//<read_stat>::=read ID;
//<read_stat>::=read ID↑n LOOK↓n↑d @IN@STI↓d;
//动作解释:
//@LOOK↓n↑d:查符号表n,给出变量地址d; 没有,变量没定义
//@IN:输出IN
//@STI↓d:输出指令代码STI d
int read_stat()
{
int es=0,address;
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
if (strcmp(token,"ID")) return(es=3); //少标识符
es=lookup(token1,&address);
if (es>0) return(es);
fprintf(fout," IN /n");//输入指令
fprintf(fout," STI %d/n",address);//指令
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
if (strcmp(token,";")) return(es=4); //少分号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
return(es);
}
//<compound_stat>::={<statement_list>}
int compound_stat(){ //复合语句函数
int es=0;
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=statement_list();
return(es);
}
//<expression_stat>::=<expression>;|;
int expression_stat()
{
int es=0;
if (strcmp(token,";")==0)
{
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
return(es);
}
es=expression();
if (es>0) return(es);
if (strcmp(token,";")==0)
{
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
return(es);
} else
{
es=4;
return(es);//少分号
}
}
//<expression>::=ID↑n@LOOK↓n↑d@ASSIGN=<bool_expr>@STO↓d |<bool_expr>
int expression()
{
int es=0,fileadd;
char token2[20],token3[40];
if (strcmp(token,"ID")==0)
{
fileadd=ftell(fp); //@ASSIGN记住当前文件位置
fscanf(fp,"%s %s/n", &token2,&token3);
printf("%s %s/n",token2,token3);
if (strcmp(token2,"=")==0) //'='
{
int address;
es=lookup(token1,&address);
if (es>0) return(es);
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=bool_expr();
if (es>0) return(es);
fprintf(fout," STO %d/n",address);
} else
{
fseek(fp,fileadd,0); //若非'='则文件指针回到'='前的标识符
printf("%s %s/n",token,token1);
es=bool_expr();
if (es>0) return(es);
}
} else es=bool_expr();
return(es);
}
//<bool_expr>::=<additive_expr>
// |< additive_expr >(>|<|>=|<=|==|!=)< additive_expr >
/*
<bool_expr>::=<additive_expr>
|< additive_expr >><additive_expr>@GT
|< additive_expr ><<additive_expr>@LES
|< additive_expr >>=<additive_expr >@GE
|< additive_expr ><=< additive_expr >@LE
|< additive_expr >==< additive_expr >@EQ
|< additive_expr >!=< additive_expr >@NOTEQ
*/
int bool_expr()
{
int es=0;
es=additive_expr();
if(es>0) return(es);
if ( strcmp(token,">")==0 || strcmp(token,">=")==0
||strcmp(token,"<")==0 || strcmp(token,"<=")==0
||strcmp(token,"==")==0|| strcmp(token,"!=")==0)
{
char token2[20];
strcpy(token2,token);//保存运算符
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=additive_expr();
if(es>0) return(es);
if ( strcmp(token2,">")==0 ) fprintf(fout," GT/n");
if ( strcmp(token2,">=")==0 ) fprintf(fout," GE/n");
if ( strcmp(token2,"<")==0 ) fprintf(fout," LES/n");
if ( strcmp(token2,"<=")==0 ) fprintf(fout," LE/n");
if ( strcmp(token2,"==")==0 ) fprintf(fout," EQ/n");
if ( strcmp(token2,"!=")==0 ) fprintf(fout," NOTEQ/n");
}
return(es);
}
//<additive_expr>::=<term>{(+|-)< term >}
//< additive_expr>::=<term>{(+< term >@ADD |-<项>@SUB)}
int additive_expr()
{
int es=0;
es=term();
if(es>0) return(es);
while (strcmp(token,"+")==0 || strcmp(token,"-")==0)
{
char token2[20];
strcpy(token2,token);
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=term();
if(es>0) return(es);
if ( strcmp(token2,"+")==0 ) fprintf(fout," ADD/n");
if ( strcmp(token2,"-")==0 ) fprintf(fout," SUB/n");
}
return(es);
}
//< term >::=<factor>{(*| /)< factor >}
//< term >::=<factor>{(*< factor >@MULT | /< factor >@DIV)}
int term()
{
int es=0;
es=factor();
if(es>0) return(es);
while (strcmp(token,"*")==0 || strcmp(token,"/")==0)
{
char token2[20];
strcpy(token2,token);
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=factor();
if(es>0) return(es);
if ( strcmp(token2,"*")==0 ) fprintf(fout," MULT/n");
if ( strcmp(token2,"/")==0 ) fprintf(fout," DIV/n");
}
return(es);
}
//< factor >::=(<additive_expr>)| ID|NUM
//< factor >::=(< expression >)| ID↑n@LOOK↓n↑d@LOAD↓d |NUM↑i@LOADI↓i
int factor()
{
int es=0;
if (strcmp(token,"(")==0)
{
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
es=expression();
if (es>0) return(es);
if (strcmp(token,")")) return(es=6); //少右括号
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
} else
{
if (strcmp(token,"ID")==0)
{
int address;
es=lookup(token1,&address);//查符号表,获取变量地址
if (es>0) return(es);//变量没声明
fprintf(fout," LOAD %d/n",address);
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
return(es);
}
if (strcmp(token,"NUM")==0)
{
fprintf(fout," LOADI %s/n",token1);
fscanf(fp,"%s %s/n",&token,&token1);
printf("%s %s/n",token,token1);
return(es);
}else
{
es=7;//缺少操作数
return(es);
}
}
return(es);
}
//主程序
void main(){
int es=0;
es=TESTscan();//调词法分析
if (es>0) printf("词法分析有错,编译停止!");
else printf("词法分析成功!/n");
if (es==0)
{
es=TESTparse(); //调语法、语义分析并生成代码
if (es==0) printf("语法、语义分析并生成代码成功!/n");
else printf("语法、语义分析并生成代码错误!/n");
}
}
下面我们可以进行测试:如下我挑了几个典型的。大家可以看看。
下面就是一个从高级语言到低级语言的转变过程:
Lexical.txt:
{
int a;
int bb;
/*this is a test*/
a=20;
bb=10*a;
}
Grammar.txt:
{ {
int int
ID a
; ;
int int
ID bb
; ;
ID a
= =
NUM 20
; ;
ID bb
= =
NUM 10
* *
ID a
; ;
} }
Translation.txt:
LOADI 20
STO 0
LOADI 10
LOAD 0
MULT
STO 1
STOP
从前面的图中我们可以看到a对应的内存地址单元为0,b为1。LOADI 20 读入20这个立即数, STO 0将20存入内存地址为0的 单元,LOADI 10 读入10这个立即数。读出内存地址为0的单元内容,MULT 进行乘法运算,STO 1 存入内存地址为1的单元。STOP 程序运行结束。
下面是程序部分在VC内的内存存储:
- code 0x0012a028
+ [0] 0x0012a028 "LOADI"
+ [1] 0x0012a03c "20"
+ [2] 0x0012a050 "STO"
+ [3] 0x0012a064 "0"
+ [4] 0x0012a078 "LOADI"
+ [5] 0x0012a08c "10"
+ [6] 0x0012a0a0 "LOAD"
+ [7] 0x0012a0b4 "0"
+ [8] 0x0012a0c8 "MULT"
+ [9] 0x0012a0dc "STO"
+ [10] 0x0012a0f0 "1"
+ [11] 0x0012a104 "STOP"
+ [12] 0x0012a118 “*****”
- [0] 0x0012a028 "LOADI"
[0] 0x4c 'L'
[1] 0x4f 'O'
[2] 0x41 'A'
[3] 0x44 'D'
[4] 0x49 'I'
[5] 0x00 ''
[6] 0xcc '?
[7] 0xcc '?
[8] 0xcc '?
[9] 0xcc '?
[10] 0xcc '?
[11] 0xcc '?
[12] 0xcc '?
[13] 0xcc '?
[14] 0xcc '?
[15] 0xcc '?
[16] 0xcc '?
[17] 0xcc '?
[18] 0xcc '?
[19] 0xcc '?
+ [1] 0x0012a03c "20"
//<for_stat>::= for(<expr>,<expr>,<expr>)<statement>
/*
<for_stat>::=for (<expression>;
@SETlabel↓label1< expression >@BRF↑label2@BR↑label3;
@SETlabel↓label4 < expression >@BR↑label1)
@SETlabel↓label3 <语句 >@BR↑label4@SETlabel↓label2
动作解释:
1. @SETlabel↓label1:设置标号label1
2. @BRF↑label2 :输出 BRF label2,
3. @BR↑label3:输出 BR label3,
4. @SETlabel↓label4:设置标号label4
5. @BR↑label1:输出 BR label1,
6. @SETlabel↓label3:设置标号label3
7. @BR↑label4:输出 BR label4,
8. @SETlabel↓label2:设置标号label2
*/
{
int a;
int b;
a=0;
b=0;
for(a=0;a<=10;a=a+1)
{
b=b+a;
}
}
词法分析
{ {
int int
ID a
; ;
int int
ID b
; ;
ID a
= =
NUM 0
; ;
ID b
= =
NUM 0
; ;
for for
( (
ID a
= =
NUM 0
; ;
ID a
<= <=
NUM 10
; ;
ID a
= =
ID a
+ +
NUM 1
) )
{ {
ID b
= =
ID b
+ +
ID a
; ;
} }
} }
语义分析:
LOADI 0
STO 0
LOADI 0
STO 1
LOADI 0
STO 0
LABEL0:
LOAD 0
LOADI 10
LE
BRF LABEL1
BR LABEL2
LABEL3:
LOAD 0
LOADI 1
ADD
STO 0
BR LABEL0
LABEL2:
LOAD 1
LOAD 0
ADD
STO 1
BR LABEL3
LABEL1:
STOP
//<IF_stat>::= if (<expr>) <statement > [else < statement >]
/*
if (<expression>)@BRF↑label1 <statement > @BR↑label2 @SETlabel↓label1
[ else < statement >] @SETlabel↓label2
其中动作符号的含义如下
@BRF↑label1 :输出 BRF label1,
@BR↑label2:输出 BR label2,
@SETlabel↓label1:设置标号label1
@SETlabel↓label2:设置标号label2
*/
IF
If.txt
{
int a;
a=3;
if(a!=1)
{
a=1;
}
}
Ifp.txt
{ {
int int
ID a
; ;
ID a
= =
NUM 3
; ;
if if
( (
ID a
!= !=
NUM 1
) )
{ {
ID a
= =
NUM 1
; ;
} }
} }
Ift.txt
LOADI 3
STO 0
LOAD 0
LOADI 1
NOTEQ
BRF LABEL0
LOADI 1
STO 0
BR LABEL1
LABEL0:
LABEL1:
STOP
While
//<while_stat>::= while (<expr >) < statement >
//<while_stat>::=while @SET↑labellabel1(<expression>) @BRF↑label2
// <statement >@BR↑label1 @SETlabel↓label2
//动作解释如下:
//@SETlabel↑label1:设置标号label1
//@BRF↑label2 :输出 BRF label2,
//@BR↑label1:输出 BR label1,
//@SETlabel↓label2:设置标号label2
While.txt
{
int a;
a=0;
while(a<5)
{
a=a+1;
}
}
Whilep.txt
{ {
int int
ID a
; ;
ID a
= =
NUM 0
; ;
while while
( (
ID a
< <
NUM 5
) )
{ {
ID a
= =
ID a
+ +
NUM 1
; ;
} }
} }
Whilet.txt
LOADI 0
STO 0
LABEL0:
LOAD 0
LOADI 5
LES
BRF LABEL1
LOAD 0
LOADI 1
ADD
STO 0
BR LABEL0
LABEL1:
STOP
Wirte
{
int a;
a=10;
write a;
}
//语义
LOADI 10
STO 0
LOAD 0
OUT
STOP
Read
{
int a;
a=1;
read a;
}
LOADI 1
STO 0
IN
STI 0
STOP
在如上的分析后,我们就看到其已经转换为类似汇编语言格式的语言。结合前面的精简计算机系统,这样我们就可以明白了它是怎么在计算机中运行的了。在孙悦红的书中还有Test语言的模拟机。大家可以看看,其实程序的基本运行原理也是类似。谢谢!
Lexical.txt:
{
int a;
int bb;
/*this is a test*/
a=20;
bb=10*a;
}
Grammar.txt:
{ {
int int
ID a
; ;
int int
ID bb
; ;
ID a
= =
NUM 20
; ;
ID bb
= =
NUM 10
* *
ID a
; ;
} }
Lexical.txt:
{
int a;
int bb;
/*this is a test*/
a=20;
bb=10*a;
}
Grammar.txt:
{ {
int int
ID a
; ;
int int
ID bb
; ;
ID a
= =
NUM 20
; ;
ID bb
= =
NUM 10
* *
ID a
; ;
} }
Lexical.txt:
{
int a;
int bb;
/*this is a test*/
a=20;
bb=10*a;
}
Grammar.txt:
{ {
int int
ID a
; ;
int int
ID bb
; ;
ID a
= =
NUM 20
; ;
ID bb
= =
NUM 10
* *
ID a
; ;
} }
最后,我们来看看前面的for循环在BC下的编译结果如下:
从#1#8开始:
┌─────Module: 1 File: ../../1.C 12────────────────────────────────────1────────┐
│ #include "stdio.h" │
│► void main() │
│ { ╔═[■]═CPU 80486═══════════════════════════════╤═══════3═[↑][↓]═╗
│ int a; ║#1#8: for(a=0;a<=10;a=a+1) ▲ ax 0100 │c=0║
│ int b; ║ cs:02C9 33D2 xor dx,dx ■ bx 028C │z=1║
│ a=0; ║ cs:02CB EB0B jmp 02D8 ▒ cx 0001 │s=0║
│ b=0; ║#1#10: b=b+a; ▒ dx 028C │o=0║
│ for(a=0;a<=10║ cs:02CD 8BC3 mov ax,bx ▒ si 0286 │p=1║
│ { ║ cs:02CF 03C2 add ax,dx ▒ di 028C │a=0║
│ b=b+a; ║ cs:02D1 8BD8 mov bx,ax ▒ bp 0000 │i=1║
│ } ║ cs:02D3 8BC2 mov ax,dx ▒ sp FFF8 │d=0║
│ } ║ cs:02D5 40 inc ax ▒ ds 66A1 │ ║
│ ║ cs:02D6 8BD0 mov dx,ax ▒ es 66A1 │ ║
│ ║ cs:02D8 83FA0A cmp dx,000A ▒ ss 66A1 │ ║
│ ║ cs:02DB 7EF0 jle #1#10 (02CD) ▒ cs 65E1 │ ║
│ ║#1#12: } ▼ ip 02C2 │ ║
│ ╟◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►┤ │ ║
│ ║ ds:0000 00 00 00 00 42 6F 72 6C Borl │ │ ║
└───────────────║ ds:0008 61 6E 64 20 43 2B 2B 20 and C++ ├────────────┴───╢
┌─────Watches───║ ds:0010 2D 20 43 6F 70 79 72 69 - Copyri │ ss:FFFA 0000 ║
│ ║ ds:0018 67 68 74 20 31 39 39 31 ght 1991 │ ss:FFF8►015B ║
└───────────────╚═════════════════════════════════════════════╧═══════════════─┘
F1-Help F2-Bkpt F3-Mod F4-Here F5-Zoom F6-Next F7-Trace F8-Step F9-Run F10-Menu
这样,我们就大概明白和了解了编译的基本原理了。
从上面的翻译看来和我们前面的翻译有点区别,这就是实际的使用了。其中还有很多其他的知识比如堆等及CPU的寄存器等。另外学习。还有其他的翻译大家自己试试看。谢谢!
相关文章推荐
- Dubbo原理解析-Dubbo内核实现之动态编译
- Unity编译Android的原理解析和apk打包分析
- 编译时类型和运行时类型---关于类型转换的原理解析
- 深度探索Linux操作系统第二章:系统构建和原理解析-工具编译
- 3. Dubbo原理解析-Dubbo内核实现之动态编译
- 「编译原理」“根据LL(1)求FIRST集” 书本算法的解析及改进
- 编译原理动手实操,用java实现一个简易编译器-语法解析
- Unity编译Android的原理解析和apk打包分析
- 编译原理实验,实现一个代码解析程序
- 编译原理动手实操,用java实现一个简易编译器1-词法解析入门
- 解析编译原理主要过程
- 【RocketMQ原理解析1.1】整体介绍&IDE编译并启动RocketMQ的第一个例子
- Unity编译Android的原理解析和apk打包分析
- Unity编译Android的原理解析和apk打包分析
- 编译原理及实践教材TINY编译器代码解析
- 读书笔记--Spring技术内幕深入解析Spring架构与设计原理--Spring源码的编译(一)
- 编译器编译原理详细解析
- LAMP原理架构解析(三):LAMP编译安装
- linux 内核编译:内核配置原理与常见配置问题的解决方法&&内核版本控制解析
- Unity编译Android的原理解析和apk打包分析