此程序是针对Pascal语言文集所做的一个词法分析器,也是兰州大学编译原理实验课所要求的。
由于Pascal语言结构严谨,层次清晰,语法与C语言接近,也便于理解,因此本实验抽取Pascal语言的一个子集,稍加改造,作为源语言,姑且命名为LittleP。
1. LittleP的文法:
〈程序〉→〈程序首部〉〈程序体〉.
〈程序首部〉→ program〈程序名〉;
〈程序体〉→〈变量声明〉〈复合语句〉
〈变量声明〉→ var〈变量定义列表〉|〈空〉
<变量定义列表> → 〈变量定义〉〈变量定义列表〉|〈变量定义〉;
〈变量定义〉→〈变量名列表〉: <类型> ;
<变量名列表> → 〈变量名〉|〈变量名〉,〈变量名列表〉
<类型> → integer
〈复合语句〉→ begin〈语句块〉end
〈语句块〉→〈语句〉|〈语句〉 ;〈语句块〉
〈语句〉→〈赋值语句〉|〈条件语句〉|〈循环语句〉|〈复合语句〉|〈空〉
〈赋值语句〉→〈左部〉:= 〈右部〉
〈左部〉→〈变量名〉
〈右部〉→〈算术表达式〉
〈条件语句〉→ if〈关系表达式〉then〈语句〉else〈语句〉
〈循环语句〉→ while〈关系表达式〉do〈语句〉
<关系表达式> →〈算术表达式〉〈关系运算符〉〈算术表达式〉
<算术表达式> → 〈项〉| 〈算术表达式〉〈加运算符〉〈项〉
<项> → 〈因子〉| 〈项〉〈乘运算符〉〈因子〉
〈因子〉→〈变量名〉|(〈算术表达式〉) |〈整数〉
〈程序名〉→〈标识符〉
〈变量名〉→〈标识符〉
〈标识符〉→〈字母〉|〈标识符〉〈字母〉|〈标识符〉〈数字〉
〈整数〉→〈数字〉|〈整数〉〈数字〉
〈关系运算符〉→ < | <= | = | >= | > | <>
〈加运算符〉→ + | -
〈乘运算符〉→ * | /
〈字母〉→ a|b|…|x|y|z
〈数字〉→ 1|2|3|4|5|6|7|8|9|0
2、程序代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_WD_LEN 255//word长度
#define MAX_INT 32767
#define MAX_SRC_LEN 1000//源文件source长度
#define MAX_WD_CNT 100
#define KWD_CNT 6
/************************************************/
union value_type{
int d;
char c;
char s[MAX_WD_LEN];
};
typedef struct{
int syn;
union value_type value;
}word_type;
/********************关键字 source 栈数组和指针等****************************/
char *keywords[20]={"begin","if","then","while","do","end"};
char source[MAX_SRC_LEN];
word_type word_queue[MAX_WD_CNT];
int line=1,wip=0,ip=0;
/**********************打印队列**************************/
void p_word_queue(){
int i;word_type w;
printf("(syn,value)\n");
printf("________________\n");
for(i=0;i<wip;i++){
w=word_queue[i];
if( (w.syn>=1 && w.syn<=10) || w.syn==18 || w.syn==21 || w.syn==22
|| w.syn==24)
printf("(%d,%s)\n",w.syn,w.value.s);//开头是字母(关键字或标识符)
else if(w.syn==11)
printf("(%d,%d)\n",w.syn,w.value.d);//数字
else
printf("(%d,%c)\n",w.syn,w.value.c);//符号
}
return ;
}
/*************报告错误******************/
void tell_err(){
printf("error in line %d\n",line);
exit(1);
return ;
}
/**************扫描器**********************/
void scan(){
word_type w;
char c;
int j=0;
/**********对数字进行处理***********/
if(isdigit(c=source[ip])){
w.syn=11; /* dd* */
w.value.d=c-'0';//变成int型
while(isdigit(c=source[++ip]))//下一位也是数字时候的处理方法
w.value.d=w.value.d*10+c-'0';
if(!isalpha(c))
word_queue[wip++]=w;
else
tell_err();
return;
}
/*********开头是字母(关键字或者标识符)**********/
if(isalpha(c=source[ip])){
w.syn=10; /* (ll|d) */
w.value.s[0]=c;//存入缓冲区数组之中
while(isalpha(c=source[++ip]) || isdigit(c))//下一位是数字或者字母
w.value.s[++j]=c;
w.value.s[j+1]='\0';//以此符号作为最终态
for(j=0;j<KWD_CNT;j++){//判断是否是关键字
if(strcmp(keywords[j],w.value.s)==0)
w.syn=j+1;
}
word_queue[wip++]=w;
return ;
}
/*************对符号进行处理******************/
switch(c=source[ip]){
case '+' :
w.syn=14; /* '+' */
w.value.c='+';
word_queue[wip++]=w;
ip++;
break;
case '-' :
w.syn=15; /* '-' */
w.value.c='-';
word_queue[wip++]=w;
ip++;
break;
case '*' :
w.syn=16; /* '*' */
w.value.c='*';
word_queue[wip++]=w;
ip++;
break;
case '/' :
w.syn=17;
w.value.c='/';
word_queue[wip++]=w;
ip++;
break;
case ':' :
w.syn=19;
w.value.c=':';
if( (c=source[++ip]) !='='){
word_queue[wip++]=w;
}
else if(c=='='){
strcpy(w.value.s,":=");
w.syn=18;
word_queue[wip++]=w;
ip++;
}
break;
case '<' :
w.syn=20;
w.value.c='<';
if( (c=source[++ip]) !='>' && c!='='){
word_queue[wip++]=w;
}
else if(c=='>'){
w.syn=21;
strcpy(w.value.s,"<>");
word_queue[wip++]=w;
ip++;
}
else if(c=='='){
w.syn=22;
strcpy(w.value.s,"<=");
word_queue[wip++]=w;
ip++;
}
break;
case '>' :
w.syn=23;
w.value.c='>';
if( (c=source[++ip]) !='='){
word_queue[wip++]=w;
}
else if(c=='='){
w.syn=24;
strcpy(w.value.s,">=");
word_queue[wip++]=w;
ip++;
}
break;
case '=' :
w.syn=25;
w.value.c='=';
word_queue[wip++]=w;
ip++;
break;
case ';' :
w.syn=26;
w.value.c=';';
word_queue[wip++]=w;
ip++;
break;
case '(' :
w.syn=27;
w.value.c='(';
word_queue[wip++]=w;
ip++;
break;
case ')' :
w.syn=28;
w.value.c=')';
word_queue[wip++]=w;
ip++;
break;
case ' ' :
while(source[++ip]==' ');
break;
case '\n' :
line++;
while(source[++ip]=='\n')line++;
break;
case '\t' :
while(source[++ip]=='\t');
break;
case '\r' :
while(source[++ip]=='\r');
break;
default:
tell_err();
}
return;
}
int main(){
FILE* fp;
int i=0;
word_type w;
fp=fopen("input.txt","r");
/****读取所有字符到source数组中*****/
while(!feof(fp))
source[i++]=getc(fp);
fclose(fp);
/****检测是否已经达到源文件末尾*****/
while(source[ip]!='#')
scan(ip);//对单个字符进行扫描、处理
w.syn=0;//此时已经扫描完毕
w.value.c='#';//对#单独赋值
word_queue[wip++]=w;//加入到打印队列里
p_word_queue();//调用打印函数
}
3、词法分析器要分析的littleP对象,即input文件
begin
ab2a:=9;
if x>=0 then x:=x+1;
while a=0 do
b:=a*x/33455;
end
#
4、运行效果