一、实验内容:
【问题描述】
请根据给定的文法设计并实现词法分析程序,从源程序中识别出单词,记录其单词类别和单词值,输入输出及处理要求如下:
(1)数据结构和与语法分析程序的接口请自行定义;类别码需按下表格式统一定义;
(2)为了方便进行自动评测,输入的被编译源文件统一命名为testfile.txt;输出的结果文件统一命名为output.txt,结果文件中每行按如下方式组织:
单词类别码 单词的字符/字符串形式(中间仅用一个空格间隔)
单词的类别码请统一按如下形式定义:
单词名称 | 类别码 | 单词名称 | 类别码 | 单词名称 | 类别码 | 单词名称 | 类别码 |
标识符 | IDENFR | if | IFTK | - | MINU | = | ASSIGN |
整型常量 | INTCON | else | ELSETK | * | MULT | ; | SEMICN |
字符常量 | CHARCON | do | DOTK | / | DIV | , | COMMA |
字符串 | STRCON | while | WHILETK | < | LSS | ( | LPARENT |
const | CONSTTK | for | FORTK | <= | LEQ | ) | RPARENT |
int | INTTK | scanf | SCANFTK | > | GRE | [ | LBRACK |
char | CHARTK | printf | PRINTFTK | >= | GEQ | ] | RBRACK |
void | VOIDTK | return | RETURNTK | == | EQL | { | LBRACE |
main | MAINTK | + | PLUS | != | NEQ | } | RBRACE |
【输入形式】testfile.txt中的符合文法要求的测试程序。
【输出形式】要求将词法分析结果输出至output.txt中。
【样例输入】
const int const1 = 1, const2 = -100;
const char const3 = '_';
int change1;
char change3;
int gets1(int var1,int var2){
change1 = var1 + var2;
return (change1);
}
void main(){
printf("Hello World");
printf(gets1(10, 20));
}
【样例输出】
CONSTTK const
INTTK int
IDENFR const1
ASSIGN =
INTCON 1
COMMA ,
IDENFR const2
ASSIGN =
MINU -
INTCON 100
SEMICN ;
CONSTTK const
CHARTK char
IDENFR const3
ASSIGN =
CHARCON _
SEMICN ;
INTTK int
IDENFR change1
SEMICN ;
CHARTK char
IDENFR change3
SEMICN ;
INTTK int
IDENFR gets1
LPARENT (
INTTK int
IDENFR var1
COMMA ,
INTTK int
IDENFR var2
RPARENT )
LBRACE {
IDENFR change1
ASSIGN =
IDENFR var1
PLUS +
IDENFR var2
SEMICN ;
RETURNTK return
LPARENT (
IDENFR change1
RPARENT )
SEMICN ;
RBRACE }
VOIDTK void
MAINTK main
LPARENT (
RPARENT )
LBRACE {
PRINTFTK printf
LPARENT (
STRCON Hello World
RPARENT )
SEMICN ;
PRINTFTK printf
LPARENT (
IDENFR gets1
LPARENT (
INTCON 10
COMMA ,
INTCON 20
RPARENT )
RPARENT )
SEMICN ;
RBRACE }
【评分标准】 按与预期结果不一致的项数(每一行单词信息算一项)扣分,每项扣5%。
二、实验过程
1、根据DFA构造词法分析程序
直接编程的词法分析程序
(1)、适合词法比较简单的、手工实现、比较精简,分析速度快。
(2)、与要识别的语言单词密切相关,一旦词法规则发生变化,则要重新编写程序。
(3)、通过程序的控制流转移来完成对输入字符的响应,程序中的每一条语句都要与识别的单词符号有关。
2、表驱动的词法分析程序
(1)、一种典型的数据与操作的分离的工作模式,控制程序不变;不同的词法分 析器实质上是构造不同的分析表。
(2)、为词法分析程序的自动生成提供了极大的方便
(3)、程序比较复杂,分析速度慢一些
3、实验步骤
:
(1)、输入:源文件字符序列testfile.txt
任务:识别单词符号;滤过空格、注释等
依据:TEST语言的词法规则
输出:字符流(单词)
(2)、本实验我设计的词法分析器:
①能够识别出保留字、标识符、单分符、双分符、常量
②利用表驱动法识别注释并且滤过注释
三 实验代码
#include<string.h>
#include<string>
#include<stdio.h>
#include<fstream>
#define MAX 22
#define RES_MAX 13
#define MAXBUF 255
char ch =' ';
int Line_NO;
struct keywords
{
char lexptr[MAXBUF];
int token;
};
struct keywords symtable[MAX];
char in[MAX] [10]= { "const","int","main","void","char","if","else","do","while","for","scanf","printf","return" };
char out[MAX] [10] = { "CONSTTK","INTTK","MAINTK","VOIDTK","CHARTK","IFTK","ELSETK","DOTK","WHILETK","FORTK","SCANFTK","PRINTFTK","RETURNTK" };
void init()
{
int j;
for(j=0; j<MAX; j++)
{
strcpy(symtable[j].lexptr,in[j]);
symtable[j].token=j;
}
}
int Iskeyword(char * is_res){
int i;
for(i=0;i<MAX;i++){
if((strcmp(symtable[i].lexptr,is_res))==0) break;
}
if(i<MAX) return symtable[i].token;
else return 0;
}
int IsLetter(char c)
{
if(((c<='z')&&(c>='a'))||((c<='Z')&&(c>='A'))|| (ch == '_')) return 1;
else return 0;
}
int IsDigit(char c){
if(c>='0'&&c<='9') return 1;
else return 0;
}
void analyse(FILE *fpin,FILE *fpout){
char arr[MAXBUF];
int j=0;
while((ch=fgetc(fpin))!=EOF)
{
if(ch==' '||ch=='\t'){}
else if(ch=='\n')
{Line_NO++;}
else if(IsLetter(ch)){
while(IsLetter(ch)||IsDigit(ch)){
arr[j]=ch;
j++;
ch=fgetc(fpin);
}
fseek(fpin,-1L,SEEK_CUR);
arr[j]='\0';
j=0;
if (Iskeyword(arr))
{
int a;
a=Iskeyword(arr);
fprintf(fpout,"%s %s\n",out[a],arr);
}
else if(!strcmp(arr,"const"))
fprintf(fpout,"CONSTTK %s\n",arr);
else
fprintf(fpout,"IDENFR %s\n",arr);
}
else if(int(ch)==34||int(ch)==39)
{
ch=fgetc(fpin);
char ar[MAXBUF];int i,j=0;
while(int(ch)!=34&&int(ch)!=39)
{
ar[j]=ch;
j++;
ch=fgetc(fpin);
}
int length = strlen(ar);
if(length ==1)
{
fprintf(fpout,"CHARCON %s\n",ar);
}
else{
fprintf(fpout,"STRCON %s\n",ar);
}
for(i=0;i<length;i++)
ar[i]='\0';
}
else if(IsDigit(ch)){
int s=0;
while(IsDigit(ch)||IsLetter(ch)){
if(IsLetter(ch)){
arr[j]=ch;
j++;
ch=fgetc(fpin);
s=1;
}
else if(IsDigit(ch)){
arr[j]=ch;
j++;
ch=fgetc(fpin);
}
}
fseek(fpin,-1L,SEEK_CUR);
arr[j]='\0';
j=0;
if(s==0)
fprintf(fpout,"INTCON %s\n",arr) ;
else if(s==1)
fprintf(fpout,"INTTK %s\n",arr) ;
}
else switch(ch)
{
case'+' :fprintf(fpout,"PLUS %s\n","+");break;
case'-' :fprintf(fpout,"MINU %s\n","-");break;
case'*' :fprintf(fpout,"MULT %s\n","*");break;
case'(' :fprintf(fpout,"LPARENT %s\n","(");break;
case')' :fprintf(fpout,"RPARENT %s\n",")");break;
case'[' :fprintf(fpout,"LBRACK %s\n","[");break;
case']' :fprintf(fpout,"RBRACK %s\n","]");break;
case';' :fprintf(fpout,"SEMICN %s\n",";");break;
case'/' :fprintf(fpout,"DIV %s\n","/");break;
case',' :fprintf(fpout,"COMMA %s\n",",");break;
case' ' :fprintf(fpout,"NEQ %s\n"," ");break;
case'{' :fprintf(fpout,"LBRACE %s\n","{");break;
case'}' :fprintf(fpout,"RBRACE %s\n","}");break;
case'=' :{
ch=fgetc(fpin);
if(ch=='=')
fprintf(fpout,"EQL %s\n","==");
else{
fprintf(fpout,"ASSIGN %s\n","=");
fseek(fpin,-1L,SEEK_CUR);
}
}break;
case'!':{
ch=fgetc(fpin);
if(ch=='=')
fprintf(fpout,"NEQ %s\n","!=");
}break;
case'>' :{
ch=fgetc(fpin);
if(ch=='=')
fprintf(fpout,"GEQ %s\n",">=");
else {
fprintf(fpout,"GRE %s\n",">");
fseek(fpin,-1L,SEEK_CUR);
}
}break;
case'<' :{
ch=fgetc(fpin);
if(ch=='=')
fprintf(fpout,"LEQ %s\n","<=");
else
{
fprintf(fpout,"LSS %s\n","<");
fseek(fpin,-1L,SEEK_CUR);}
}break;
default :break;
}
}}
int main()
{
char in_fn[25],out_fn[25];
FILE * fpin,* fpout;
fpin=fopen("testfile.txt","r");
fpout=fopen("output.txt","w");
init();
analyse(fpin,fpout);
fclose(fpin);
fclose(fpout);
printf(".....程序已分析完成分析并保存至目标文件\n");
return 0;
}
四、运行举例
testfile.txt文件:
output.txt文件:(没截全)
说明:这个是我学校实验平台的题目,代码没有写注释,看不懂的地方可以留言,有时间会把注释 加上 (如果是同学看到的话,希望不要借鉴,平台有查重)