使用C/C++语言编写PL/0编译程序的词法分析程序。
需要注意的点:
(1)识别非法字符:如 @ 、 & 和 ! 等;
(2)识别非法单词:数字开头的数字字母组合;
(3)标识符和无符号整数的长度不超过8位;
(4)能自动识别并忽略/* */及//格式的注释信息;
(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 。
<无符号整数> ::=<数字>{<数字>}
<标识符> ::=<字母>{<字母>|<数字>}
<字母> ::= 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
#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; }