任务描述
使用C/C++语言编写PL/0编译程序的词法分析程序。
需要注意的点:
(1)识别非法字符:如 @ 、 & 和 ! 等;
(2)识别非法单词:数字开头的数字字母组合;
(3)标识符和无符号整数的长度不超过8位;
(4)能自动识别并忽略/* */及//格式的注释信息;
(5)词法分析过程中遇到错误后能继续往下识别,并输出错误信息。
PL/0的单词可以划分为5个大类:保留字(关键字)、标识符、运算符、无符号整数和界符。具体如下:
(1)保留字:共有13个,包括 const , var , procedure , begin , end , odd , if , then , call , while , do , read , write 。
(2)运算符:共有11个,包括4个整型算数运算符号 + 、 - 、 * 和 / ,6个比较运算符号 < 、 <= 、 > 、 >= 、 # 和 = ,1个赋值运算符 := 。
(3)界符:共有5个,包括 ( 、 ) 、 , 、 ; 和 . 。
(4)无符号整数:是由一个或多个数字组成的序列,数字为 0 , 1 , 2 , … , 9 。
(5)标识符:是字母开头的字母数字序列,字母包括大小写英文字母: a , b , ..., z , A , B , …, Z 。
PL/0语言中5类单词的EBNF描述如下:
<无符号整数> ::=<数字>{<数字>}
<标识符> ::=<字母>{<字母>|<数字>}
<字母> ::= a | b | ... | X | Y | Z
<数字> ::= 0 | 1 | 2 | ... | 8 | 9
<保留字> ::= const | var | procedure | begin | end | odd | if | then | call | while | do | read | write
<运算符> ::= + | - | * | / | < | <= | > | >= | # | = | :=
<界符> ::= ( | ) | , | ; | .
参考
https://blog.csdn.net/qq_46350148/article/details/112243428
cpp
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void init();
void getsym();
/* 符号 */
enum symbol {
nul, ident, number, plus, minus, times, slash, oddsym, eql, neq,
lss, leq, gtr, geq, lparen, rparen, comma, semicolon, period, becomes,
beginsym, endsym, ifsym, thensym, whilesym, writesym, readsym, dosym, callsym,
constsym, varsym, procsym, programsym,
};
#define norw 14 /*关键字个数*/
#define al 10 //符号的最大的长度
#define nmax 10 //number的最大位数
#define legal 8
char word[norw][al]; //保留字13个
char ch; /* 获取字符的缓冲区 */
enum symbol sym; /* 当前的符号 */
enum symbol wsym[norw]; /* 保留字对应的符号值 */
enum symbol ssym[256]; //单字符的符号值
int line = 1;
int main() {
ch = getc(stdin);
while (ch != EOF) //EOF实际是-1,用来表示文本文件的结束
{
getsym();
}
}
//读取源文件
void getsym() {
char id[al + 10], a[al + 10];
int i, k;
init();
if (ch == ' ' || ch == '\t') /*忽略空格32、换行*/
{
while (ch == ' ' || ch == '\t') {
ch = getc(stdin);
}
}
if (ch == '\n' || ch == '\r') {//count lines num ->TAP
while (ch == '\n' || ch == '\r') {
line++;
ch = getc(stdin);
}
} else {
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) /*名字或保留字以a..z开头*/
{
k = 0;
memset(a, '\0', sizeof(a));
do /*搜索当前符号是否为保留字*/
{
if (k < al) {
a[k++] = ch;
}
ch = getc(stdin);
} while ((ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9') ||
(ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9'));
a[k] = '\0';
strcpy(id, a);
//将识别出来的字符和已定义的标示符作比较, 判断是否是关键字
for (i = 0; i < norw; i++) {
if (strcmp(id, word[i]) == 0) {
sym = wsym[i];
printf("(保留字,%s)\n", id);
break;
}
if (i == (norw - 1)) {// 遍历结束,查无此文
sym = ident;
if (k > legal) {
printf("(标识符长度超长,%s,行号:%d)\n", id, line);
break;
}
printf("(标识符,%s)\n", id);
break;
}
}
} else if (ch >= '0' && ch <= '9') {
/*检测是否为数字:以0..9开头*/
int flag = 0;
k = 0;
memset(a, '\0', sizeof(a));
sym = number;
do {
a[k++] = ch;
ch = getc(stdin);
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
flag = 1;
}
} while ((ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9') ||
(ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9')); /*获取数字的值*/
if (flag == 1) {
printf("(非法字符(串),%s,行号:%d)\n", a, line);
} else if (k >= legal) {
printf("(无符号整数越界,%s,行号:%d)\n", a, line);
} else {
printf("(无符号整数,%s)\n", a);
}
} else if (ch == ':') {
/*检测赋值符号*/
ch = getc(stdin);
if (ch == '=') {
sym = becomes;
printf("(运算符,:=)\n");
ch = getc(stdin);
} else {
sym = nul;
printf("(非法字符(串),:,行号:%d\n", line); /*不能识别的符号*/
}
} else if (ch == '<') {
/*检测小于或小于等于符号*/
ch = getc(stdin);
if (ch == '=') {
sym = leq;
printf("(运算符,<=)\n");
ch = getc(stdin);
} else {
sym = lss;
printf("(运算符,<)\n");
}
} else if (ch == '>') {
/*检测大于或大于等于符号*/
ch = getc(stdin);
if (ch == '=') {
sym = geq;
printf("(运算符,>=)\n");
ch = getc(stdin);
} else {
sym = gtr;
printf("(运算符,>)\n");
}
} else if (ch == '/') {
ch = getc(stdin);
if (ch == '/') {//single note
do {
ch = getc(stdin);
} while (ch != '\n');
}
if ('*' == ch) {//mul line note
int flag_backslash = 1;
while (flag_backslash) {
ch = getc(stdin);
if (ch == '\n' || ch == '\r')line++;
if (ch == '*') {
ch = getc(stdin);
if (ch == '/') {
flag_backslash = 0;
}
}
}
}
} else /*当符号不满足上述条件时,全部按照单字符符号处理*/
{
sym = ssym[ch];
if (ch == '+') {
printf("(运算符,+)\n");
ch = getc(stdin);
} else if (ch == '-') {
printf("(运算符,-)\n");
ch = getc(stdin);
} else if (ch == '*') {
printf("(运算符,*)\n");
ch = getc(stdin);
} else if (ch == '/') {
printf("(运算符,)\n");
ch = getc(stdin);
} else if (ch == '(') {
printf("(界符,()\n");
ch = getc(stdin);
} else if (ch == ')') {
printf("(界符,))\n");
ch = getc(stdin);
} else if (ch == '=') {
printf("(运算符,=)\n");
ch = getc(stdin);
} else if (ch == ',') {
printf("(界符,,)\n");
ch = getc(stdin);
} else if (ch == '#') {
printf("(运算符,#)\n");
ch = getc(stdin);
} else if (ch == '.') {
printf("(界符,.)\n");
ch = getc(stdin);
} else if (ch == ';') {
printf("(界符,;)\n");
ch = getc(stdin);
} else {
printf("(非法字符(串),%c,行号:%d)\n", ch, line);
ch = getc(stdin);
}
}
}
}
//对关键字等实现初始化
void init() {
/*设置单字符符号*/
int i;
for (i = 0; i <= 255; i++) {
ssym[i] = nul;
}
ssym['+'] = plus;
ssym['-'] = minus;
ssym['*'] = times;
ssym['/'] = slash;
ssym['('] = lparen;
ssym[')'] = rparen;
ssym['='] = eql;
ssym[','] = comma;
ssym['.'] = period;
ssym['#'] = neq;
ssym[';'] = semicolon;
/*设置保留字名字,按照字母表顺序,便于折半查找*/
strcpy(&(word[0][0]), "begin");
strcpy(&(word[1][0]), "call");
strcpy(&(word[2][0]), "const");
strcpy(&(word[3][0]), "do");
strcpy(&(word[4][0]), "end");
strcpy(&(word[5][0]), "if");
strcpy(&(word[6][0]), "odd");
strcpy(&(word[7][0]), "procedure");
strcpy(&(word[8][0]), "read");
strcpy(&(word[9][0]), "program");
strcpy(&(word[10][0]), "var");
strcpy(&(word[11][0]), "while");
strcpy(&(word[12][0]), "write");
strcpy(&(word[13][0]), "then");
/*设置保留字符号*/
wsym[0] = beginsym;
wsym[1] = callsym;
wsym[2] = constsym;
wsym[3] = dosym;
wsym[4] = endsym;
wsym[5] = ifsym;
wsym[6] = oddsym;
wsym[7] = procsym;
wsym[8] = readsym;
wsym[9] = programsym;
wsym[10] = varsym;
wsym[11] = whilesym;
wsym[12] = writesym;
wsym[13] = thensym;
}