摘要使用过现代计算机的人都知道,多数用户是应用高级语言来实现他们所需要的计算的。
现在计算机系统一般都含有不只一个的高级语言的编译程序,对有些高级语言甚至配置了几个不同性能的编译程序,供用户按不同需要进行选择。
高级语言编译程序是计算机系统软件最主要的组成部分之一,也是用户最直接关系的工具之一。
计算机上执行一个高级语言程序一般分为两步:第一,用一个编译程序把高级语言翻译成机器语言程序;第二,运行所得的机器语言程序求得计算结果。
通常说的翻译程序是指能够把某一种语言程序转换成另一种语言程序(目标语言程序)。
如果源语言诸如Fortran,Pascal,C,Ada或java这样的高级语言,而目标程序是诸如汇编语言或者机器语言这类的低级语言,这样的一个翻译程序就是称为编译程序。
一个编译程序的工作过程一般可以划分为五个阶段:词法分析、语法分析、语义分析与中间代码生成、优化、目标代码生成。
每个阶段都是从上一个阶段得到结果,对他进行分析,并且根据一些外部环境(例如符号表等)得到最终的输出结果。
要构造一个编译程序,可以按照这样的阶段来分别构造,最后来连调。
现在人们已经建立了多种编制部分编译程序或整个编译程序的有效工具。
有些能用于自动生成扫描器(如LEX),有些可以用于自动产生语法分析器(如YACC),有些甚至可以用来自动产生整个的编译程序。
这些构造编译程序的工具成为编译程序-编译程序、编译程序产生器或翻译程序书写系统,他们是按照编译程序和目标语言的形式描述而自动产生编译程序的。
编译程序是一极其庞大而又复杂的系统,掌握它比较苦难。
但是一旦对其掌握,对以后的程序语言设计,系统软件分析,系统软件设计,形式语言研究等方面都是非常有好处的。
关键字:C语言、、编译、扫描器、语法分析一、需求分析给出类C语言(C语言的子集)的词法和语法定义,并根据对应的语法定义写出一些属性文法和语法制导。
根据词法和语法的定义,构造一个编译程序,它主要可以完成如下功能:1、读入某个已经编辑好的类C源程序文件,通过词法分析器,生成二元组,同时检查词法错误;2、语法分析器将产生的二元组作为输入,进行语法分析,同时检查语法错误;3、在语法分析同时,利用属性文法和语法制导技术,产生具体的语意动作,并对符号表进行操作;4、根据语义动作产生整个源程序的四元式序列;5、将产生的四元式序列连同符号表一起输出,作为编译程序的最终输出结果;6、对最后的代码优化和目标代码生成要有所考虑,必须留有一定的接口供以后扩展;7、增大程序的可移植性,努力做到整个系统方便移植。
二、详细算法设计词法分析程序→语法分析程序→语义分析程序→编译器。
三、流程图图I 主函数示意图图II 递归下降分析程序示意图图III 语句块分析示意图图IV 语句串分析示意图图V 语句分析示意图四、相关函数说明void scanner(); //扫描void lrparser();void staBlock(int *nChain); //语句块void staString(int *nChain); //语句串void sta(int *nChain); //语句void fuzhi(); //赋值语句void tiaojian(int *nChain); //条件语句void xunhuan(); //循环语句char* E(); //Expresiion表达式char* T(); //Term项char* F(); //Factor因子char *newTemp(); //自动生成临时变量void backpatch(int p,int t); //回填int merge(int p1,int p2); //合并p1和p2void emit(char *res,char *num1,char *op,char *num2); //生成四元式五.运行结果图VI 赋值语句的分析图VII 条件语句的分析图VIII 循环语句的分析图IX 综合六.编译器使用说明程序提示用户输入字符串“Please input your source string:”,用户输入字符串并以“#”号结束。
回车后,程序显示运行结果。
七.程序清单#include<stdio.h>#include<string.h>#include<math.h>#include<stdlib.h>char prog[80]; /* 存放所有输入字符 */char token[8]; /* 存放词组 */char ch; /* 单个字符 */int syn,p,m,n,i; /* syn:种别编码 */double sum;int count;int isSignal; /* 是否带正负号(0不带,1负号,2正号) */int isError;int isDecimal; /* 是否是小数 */double decimal; /* 小数 */int isExp; /* 是否是指数 */int index; /* 指数幂 */int isNegative; /* 是否带负号 */double temp;int temp2;int repeat; /* 是否连续出现+,- */int nextq;int kk; /* 临时变量的标号 */int ntc,nfc,nnc,nnb,nna;char *rwtab[9]={"main","int","float","double","char","if","else","do","while"}; struct{char result[10]; /* 字符串(字符数组) */char arg1[10];char opera[10];char arg2[10];}fourCom[20]; /* 结构体数组 */void scanner(); /* 扫描 */void lrparser();void staBlock(int *nChain); /* 语句块 */void staString(int *nChain); /* 语句串 */void sta(int *nChain); /* 语句 */void fuzhi(); /* 赋值语句 */void tiaojian(int *nChain); /* 条件语句 */void xunhuan(); /* 循环语句 */char* E(); /* Expresiion表达式 */char* T(); /* Term项 */char* F(); /* Factor因子 */char *newTemp(); /* 自动生成临时变量 */void backpatch(int p,int t); /* 回填 */int merge(int p1,int p2); /* 合并p1和p2 */void emit(char *res,char *num1,char *op,char *num2); /* 生成四元式 */void main(){ p=0;count=0;isDecimal=0;index=0;repeat=0;kk=0;printf("\nPlease input your source string:\n");do{ch=getchar();prog[p++]=ch;}while(ch!='#');p=0;isError=0;scanner();lrparser();for(i=1;i<nextq;i++) /* 循环输出四元式 */{printf("\n%d\t",i);printf("(%5s %5s %5s \t%5s )\n",fourCom[i].arg1,fourCom[i].opera,fourCom[i].arg2,fourCom[i].result);}}void lrparser(){int nChain;nfc=ntc=1;nextq=1;if(syn==1) /* main */{scanner();if(syn==26) /* ( */{ scanner();if(syn==27) /* ) */{ scanner();staBlock(&nChain);}elseprintf("缺少右括号\n");}elseprintf("缺少左括号\n");}elseprintf("缺少main\n");}/* <语句块> ::= '{'<语句串>'}' */void staBlock(int *nChain) /* 语句块 */{ if(syn==28) /* { */{scanner();staString(nChain);/* backpatch(*nChain,nextq); */if(syn==29) /* } */scanner(); /* 读下一个 */elseprintf("缺少}号\n");}elseprintf("缺少{号\n");}/* <语句串>::=<语句>{;<语句>}; */void staString(int *nChain) /* 语句串 */{sta(nChain);backpatch(*nChain,nextq);while(syn==31) /* ; */{scanner();sta(nChain);}/* backpatch(*nChain,nextq-1); */}void sta(int *nChain) /* 语句 */{if(syn==10){fuzhi();/* *nChain=0; */}else if(syn==6) /* if */{tiaojian(nChain);}else if(syn==8) /* do */xunhuan();}/* <条件语句>->if(<条件>)<语句块> */void tiaojian(int *nChain){char res[10],num1[10],num2[10],op[10]; int nChainTemp;/* <条件>-><表达式><关系运算符><表达式> */ if(syn==6) /* if */{scanner();/* strcpy(num1,E()); */if(syn==26) /* ( */{scanner();strcpy(num1,E());if((syn<=37)&&(syn>=32)){switch(syn){case 32:strcpy(op,">");break;case 33:strcpy(op,">=");break;case 34:strcpy(op,"<");break;case 35:strcpy(op,"<=");break;case 36:strcpy(op,"==");break;case 37:strcpy(op,"!=");break;default:printf("error");}}scanner();strcpy(num2,E());strcat(num1,op);strcat(num1,num2);/* nfc=nextq+1; */ntc=nextq; /* 记住if语句位置 */emit("0","if",num1,"goto");nfc=nextq; /* if中表达式为假 */emit("0","","","goto");/* 第一个0已回填 */backpatch(ntc,nextq); /* ntc链接的所有四元式都回填nextq */ }if(syn==27) /* ) */scanner();staBlock(&nChainTemp); /* 语句块 */*nChain=merge(nChainTemp,nfc);}}/* <循环语句>::=do <语句块>while <条件> */void xunhuan(){char res[10],num1[10],num2[10],op[10];int nChainTemp;if(syn==8) /* do */{nnc=nextq; /* 记住if语句位置,emit之后nextq就变了 */ /* emit("0","if",num1,"goto"); */scanner();staBlock(&nChainTemp); /* 语句块 */if(syn==9) /* while */{scanner();if(syn==26) /* ( */{scanner();strcpy(num1,E());if((syn<=37)&&(syn>=32)){switch(syn){case 32:strcpy(op,">");break;case 33:strcpy(op,">=");break;case 34:strcpy(op,"<");break;case 35:strcpy(op,"<=");break;case 36:strcpy(op,"==");break;case 37:strcpy(op,"!=");break;default:printf("error");}}scanner();strcpy(num2,E());strcat(num1,op);strcat(num1,num2);nnb=nextq;backpatch(nnb,nnc);nna=nextq;emit("0","","","goto");backpatch(nna,nextq);}if(syn==27) /* ) */scanner();}}}void fuzhi() /* 赋值语句只有1个操作数 */ {char res[10],num[10]; /* num操作数 */ if(syn==10) /* 字符串 */{strcpy(res,token); /* 结果 */scanner();if(syn==21) /* = */{scanner();strcpy(num,E());emit(res,num,"=","");}else{printf("缺少=号\n");}}}char* E() /* Expression表达式 */{char *res,*num1,*op,*num2;res=(char *)malloc(10);num1=(char *)malloc(10);op=(char *)malloc(10);num2=(char *)malloc(10);strcpy(num1,T());while((syn==22)||(syn==23)) /* + - */ {if(syn==22) /* + */strcpy(op,"+");elsestrcpy(op,"-");strcpy(num2,T());strcpy(res,newTemp());emit(res,num1,op,num2);strcpy(num1,res);}return num1;}char* T() /* Term项 */{char *res,*num1,*op,*num2;res=(char *)malloc(10);num1=(char *)malloc(10);op=(char *)malloc(10);num2=(char *)malloc(10);strcpy(num1,F());while((syn==24)||(syn==25)) /* * / */{if(syn==24)strcpy(op,"*");elsestrcpy(op,"/");scanner();strcpy(num2,F());strcpy(res,newTemp());emit(res,num1,op,num2);strcpy(num1,res);}return num1;}char* F() /* Factor因子 */{char *res;res=(char *)malloc(10);if(syn==10) /* 字符串 */{strcpy(res,token);scanner();}else if(syn==20) /* 二进制数 */{itoa((int)sum,res,10); /* 整数转换为字符串 */ scanner();}else if(syn==26) /* ( */{res=E();if(syn==27) /* ) */{scanner();}else isError=1;}elseisError=1;return res;}char *newTemp(){char *p;char varTemp[10];p=(char *)malloc(10);kk++;itoa(kk,varTemp,10);strcpy(p+1,varTemp);p[0]='T';return p;}/* 将p所链接的每个四元式的第四个分量都回填t */void backpatch(int p,int t){int w,circle=p;while(circle) /* circle不为0的时候 */{w=atoi(fourCom[circle].result); /* 四元式circle第四分量内容 *//* strcpy(fourCom[circle].result,t); //把t填进四元式circle的第四分量 */ sprintf(fourCom[circle].result,"%d",t);circle=w; /* w记录的是链条上下一个四元式,移动! */} return;}int merge(int p1,int p2) /* 合并p1和p2 */{char circle,nResult;if(p2==0)nResult=p1;else{nResult=circle=p2;while(atoi(fourCom[circle].result)) /* 四元式第四个分量不为0 */{circle=atoi(fourCom[circle].result);sprintf(fourCom[circle].result,"%s",p1);} /* 目的是用p1的值覆盖0 */}return nResult; /* p2是头,p1覆盖0,接在p2后边 */}void emit(char *res,char *num1,char *op,char *num2){strcpy(fourCom[nextq].result,res);strcpy(fourCom[nextq].arg1,num1);strcpy(fourCom[nextq].opera,op);strcpy(fourCom[nextq].arg2,num2);nextq++;}void scanner(){sum=0;decimal=0;m=0;for(n=0;n<8;n++)token[n]=NULL;ch=prog[p++]; /* 从prog中读出一个字符到ch中 */while(ch==' '||ch=='\n') /* 跳过空字符(无效输入) */ch=prog[p++];if(((ch>='a')&&(ch<='z'))||((ch>='A')&&(ch<='Z'))) /* ch是字母字符 */{while(((ch>='a')&&(ch<='z'))||((ch>='A')&&(ch<='Z'))||((ch>='0')&&(ch<='9'))) {token[m++]=ch; /* ch=>token */ch=prog[p++]; /* 读下一个字符 */}token[m++]='\0';p--; /* 回退一格 */syn=10; /* 标识符 *//* 如果是"begin","if","then","while","do","end"标识符中的一个 */for(n=0;n<9;n++)if(strcmp(token,rwtab[n])==0){syn=n+1;break;}}else if((ch>='0')&&(ch<='9')){if(isSignal==1){/* token[m++]='-'; */}while((ch>='0')&&(ch<='9')){sum=sum*10+ch-'0'; /* ch中数字本身是当做字符存放的 */ch=prog[p++];}if(ch=='.'){isDecimal=1;ch=prog[p++];count=0; /* 之前忘了清零,123.123+123.123#两个浮点数就无法识别 */ while((ch>='0')&&(ch<='9')){/* pow(x,y)计算x的y次幂 */temp=(ch-'0')*pow(0.1,++count);decimal=decimal+temp;/* AddToDec(); */ch=prog[p++];}sum=sum+decimal;}if(ch=='e'||ch=='E'){isExp=1;ch=prog[p++];if(ch=='-'){isNegative=1;ch=prog[p++];}while((ch>='0')&&(ch<='9')){/* 指数 */index=index*10+ch-'0';ch=prog[p++];}/* 10的幂 *//* 123e3代表123*10(3) *//* sum=sum*pow(10,index);是错误的 */if(isNegative)sum=sum*pow(0.1,index);elseif(isSignal==1){sum=-sum;isSignal=0;}p--;syn=20;}else switch(ch){case '<':m=0;token[m++]=ch;ch=prog[p++];if(ch=='='){syn=35;token[m++]=ch; }else{syn=34;p--;}break;case '>':m=0;token[m++]=ch;ch=prog[p++];if(ch=='='){syn=33;token[m++]=ch; }else{syn=32;p--;}break;case '=':m=0;token[m++]=ch;ch=prog[p++];syn=36;token[m++]=ch;}else{syn=21;p--;}break;case '+':temp2=prog[p];token[m++]=ch;if((temp2>='0')&&(temp2<='9')&&(repeat==1)){isSignal=2;ch=prog[p++];repeat=0;goto IsNum;}if(((temp2=='+')||(temp2=='-'))&&(repeat==0)) /* 如果重复出现符号,才将后边的+,-视为正负号 */{repeat=1;/* ch=prog[p++]; */}syn=22;break;case '-':temp2=prog[p];token[m++]=ch;if((temp2>='0')&&(temp2<='9')&&(repeat==1)){isSignal=1;ch=prog[p++]; /* 读"-"下一个字符 */repeat=0;goto IsNum; /* 转到数字的识别 */}if(((temp2=='+')||(temp2=='-'))&&(repeat==0)) /* 如果重复出现符号,才将后边的+,-视为正负号 */{repeat=1; /* 预言会重复 *//* ch=prog[p++]; //读下一个字符 */syn=23;break;case '*':temp2=prog[p];token[m++]=ch;if(temp2=='+'){isSignal=2;repeat=1;}else if(temp2=='-') {isSignal=1;repeat=1;}syn=24;break;case '/':syn=25;token[m++]=ch; break;case '(':temp2=prog[p];token[m++]=ch;if(temp2=='+'){isSignal=2;repeat=1;}else if(temp2=='-') {isSignal=1;repeat=1;}syn=26;break;case ')':syn=27;token[m++]=ch; break;case '{':syn=28;token[m++]=ch; break;syn=29;token[m++]=ch;break;case ',':syn=30;token[m++]=ch;break;case ';':syn=31;token[m++]=ch;break;case'#':syn=0;token[m++]=ch;break;default:syn=-1;}getch();}八、总结本次设计的C语言的子集(便于叙述,以后称之为Cxx),是本着简单、易懂、易操作,而且能够完成一定功能的原则,尽量实现最基本的,人们最常用的部分,而抛弃那些不常使用的,或者难以实现的部分。