当前位置:   article > 正文

词法分析器--C实现_词法分析器c语言编写

词法分析器c语言编写

实验目的:

编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类(可自主添加类别)。并依次输出各个单词的内部编码及单词符号自身值。

 程序及其子程序:

1、文件输入(待分析文本拖入控制台或屏幕输入)

2、划分字符集成7个大类

3、预处理(去除空格、回车换行和非法字符)

4、词法分析

5、打印输出识别的终结符及其类别

  1. //程序无法识别出double,因为它的前缀子串与do重复(如有需要自主修改)
  2. /*
  3. 编译原理实验一:词法分析器
  4. 要求:编制一个读单词过程,从输入的C语言源程序中,识别出各个具有独立意义的单词,
  5. 即基本保留字、标识符、常数、运算符、分隔符、特殊字符、控制命令七大类。
  6. 并依次输出各个单词的内部编码及单词符号自身值的二元组。
  7. */
  8. /*
  9. 一、复习c语言字符串数组相关处理,文件相关处理
  10. //用字符数组存放一个字符串
  11. char str[ ]="";
  12. //用字符指针指向一个字符串
  13. char * s="I love China";
  14. //二维数组存放字符串数组
  15. const int N = 5;
  16. char frd[N][10]={"Hello","World","My","Dear","Friends"};
  17. //字符指针的数组存放字符串数组
  18. char *p[]={"This","is","a","char*","array"};
  19. //文件读写函数原型:FILE *fopen(const char *filename, const char *mode);`头文件:#include <stdio.h>`
  20. 二、词法分析器
  21. */
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <math.h>
  25. #include <string.h>
  26. #include <sys/types.h>
  27. #define MAX_LINE 1024*10
  28. //编码,保留字:1、标识符:2、常数:3、运算符:4、分隔符: 5
  29. //控制命令表
  30. static char *contCommands[]={"#include","#define","#undef","#asm","#endasm","#ifdef","#ifndef","#else","#endif"};
  31. //关键字表
  32. static char *keyWords[]={"main","int","double","struct","if","else","char","return","const","float",
  33. "short","void","while","for","break","then","long","switch","case","do","static","typedef","continue",
  34. "default","sizeof","do","extern","static","auto","register","sizeof"};
  35. //运算符表
  36. static char *operators[]={"+","-","*","\/","=","%",">","<","^","&","|","!"};
  37. //分隔符表
  38. static char *delimiters[]={",","(",")","{","}","[","]",";","\""};
  39. //特殊符号表
  40. static char *Spesymbols[]={".","$","?","~","^","%","\\","#","&",":","`","@"};
  41. //科学计数法
  42. // 数字
  43. //const char* number_rule="^([+-]?\\d+\\.\\d+)|([+-]?\\d+)|([+-]?\\.\\d+)$";
  44. //const std::regex pattern_number(number_rule, regex::icase);
  45. //
  46. 科学计数
  47. //const char* scientific_rule="^[+-]?((\\d+\\.?\\d*)|(\\.\\d+))[Ee][+-]?\\d+$";
  48. //const regex pattern_scientific(scientific_rule, regex::icase);
  49. //
  50. 十六进制
  51. //const char* hex_rule="^[+-]?0[xX]([A-Fa-f0-9])+$";
  52. //const regex pattern_hex(hex_rule, regex::icase);
  53. //
  54. 八进制
  55. //const char* oct_rule="^0([0-7])+$";
  56. //const regex pattern_oct(oct_rule, regex::icase);
  57. //输入的源程序存放处,最大可以存放MAX_LINE个字符
  58. static char *resourceProject = (char*)malloc(MAX_LINE * sizeof(char));
  59. //p = (int*)realloc(p, sizeof(int)* 20);扩容
  60. //存放注释
  61. static char *commentStr = (char*)malloc(MAX_LINE * sizeof(char));
  62. static char *commentStr2 = (char*)malloc(MAX_LINE * sizeof(char));
  63. //从str中删除目标字符
  64. void delete_char(char str[],char target){
  65. int i,j;
  66. for(i=j=0;str[i]!='\0';i++){
  67. if(str[i]!=target){
  68. str[j++]=str[i];
  69. }
  70. }
  71. str[j]='\0';
  72. //i-j即为串中存在的目标字符个数
  73. }
  74. //处理"//,/* */"注释
  75. void proComment(char str[]){
  76. int j=0;
  77. int p=0;
  78. //遍历resourceProject,记录注释
  79. for(int i=0; i<strlen(str); i++) {
  80. if(str[i]=='/'&&str[i+1]=='/') {
  81. int k=i-1;
  82. while(str[++k]!='\n') {
  83. commentStr[p++]=str[k];
  84. }
  85. i=k;
  86. }
  87. }
  88. printf("\n单行注释为:%s\n",commentStr);
  89. //遍历resourceProject,原地删除单行注释
  90. j=0;
  91. p=0;
  92. int i;
  93. for(i=0; i<strlen(str); i++){
  94. if(str[i]=='/'&&str[i+1]=='/') {
  95. p=i;
  96. while(str[++p]!='\n');
  97. i=p;
  98. }else{
  99. str[j++]=str[i];
  100. }
  101. }
  102. while(j<=i)
  103. {
  104. str[j++]='\0';
  105. }
  106. printf("\n单行注释处理后的源程序为:\n%s",str);
  107. //处理多行注释“/* 。。。*/”则去除该内容
  108. int count = 0;
  109. p=0;
  110. int k=0;
  111. for (int i=0;i<strlen(str);i++){
  112. if(str[i]=='/'&&str[i+1]=='*')
  113. {
  114. k=i;
  115. while(str[k]!='*'||str[k+1]!='/')
  116. {
  117. commentStr2[p++]=str[k++];
  118. }
  119. commentStr2[p++]='*';
  120. commentStr2[p++]='/';
  121. i=k+2;
  122. }
  123. // printf("\n多行注释处理后的源程序为:%s\n",str);
  124. }
  125. printf("\n多行注释为:%s\n",commentStr2);
  126. //遍历resourceProject,原地删除多行注释
  127. j=0;
  128. p=0;
  129. for(i=0;str[i]!='\0';i++){
  130. if(str[i]=='/'&&str[i+1]=='*'){
  131. i=i+2;
  132. p=i;
  133. while(str[p++]!='*'&&str[p+1]!='/');
  134. i=p+2;
  135. }else{
  136. str[j++]=str[i];
  137. }
  138. }
  139. printf("end\n");
  140. while(j<=i)
  141. {
  142. str[j++]='\0';
  143. }
  144. printf("\n多行注释处理后的源程序为:\n%s",str);
  145. }//endproComment
  146. //预处理函数(可以写个target数组传进来)
  147. void preProcessing(){
  148. //先处理注释,再处理宏定义等预处理,最后处理空格、换行、制表符
  149. proComment(resourceProject);
  150. char target1=' ';
  151. char target2='\t';
  152. char target3='\n';
  153. delete_char(resourceProject,target1);
  154. delete_char(resourceProject,target2);
  155. delete_char(resourceProject,target3);
  156. }
  157. //判断是否为字母
  158. bool isChar(char ch){
  159. if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
  160. return true;
  161. return false;
  162. }
  163. //判断是否为数字
  164. bool isDigit(char ch){
  165. if(ch>='0'&&ch<='9')
  166. return true;
  167. return false;
  168. }
  169. //判断是否为定界符等
  170. bool isDelimiters(char ch){
  171. for(int i=0;i<sizeof(delimiters)/sizeof(delimiters[0]);i++)
  172. if(ch==*delimiters[i])
  173. return true;
  174. return false;
  175. }
  176. //判断是否为控制命令
  177. int isContCommands(char *str){
  178. for(int i=0;i<sizeof(contCommands)/sizeof(contCommands[0]);i++) {
  179. if(strcmp(str,contCommands[i])==0){//匹配
  180. return i;
  181. }
  182. }
  183. //不是关键字即为404
  184. return 404;
  185. }
  186. //判断是否为关键字
  187. int isKeyword(char *str){
  188. for(int i=0;i<sizeof(keyWords)/sizeof(keyWords[0]);i++) {
  189. if(strcmp(str,keyWords[i])==0){//匹配
  190. return i;
  191. }
  192. }
  193. //不是关键字即为404
  194. return 404;
  195. }
  196. //判断是否为运算符
  197. int isOperators(char str){
  198. for(int i=0; i<sizeof(operators)/sizeof(operators[0]); i++) {
  199. if(str==*operators[i]){
  200. return i;
  201. }
  202. }
  203. //不是关键字即为404
  204. return 404;
  205. }
  206. //判断是否为特殊字符
  207. int isSpesymbols(char str){
  208. for(int i=0; i<sizeof(Spesymbols)/sizeof(Spesymbols[0]); i++) {
  209. if(str==*Spesymbols[i]){
  210. return i;
  211. }
  212. }
  213. //不是关键字即为404
  214. return 404;
  215. }
  216. FILE * readTxt(){
  217. FILE *fp;
  218. char filename[100]; //文件名
  219. char tempstr[1024]; //读文件的缓冲
  220. bool flag = true; //文件读取成功标志
  221. printf("请输入或拖入想要打开的文本文件名及其路径,如c:\\temp.txt\n");
  222. while(flag){
  223. gets(filename); //这句要用户输入文件名
  224. char target='"';
  225. delete_char(filename,target);
  226. //getchar();
  227. if ((fp=fopen(filename,"r"))==NULL){//打开文件,并判断是否有打开错误
  228. printf("打开文件%s出现错误\n",filename);
  229. memset(filename,0, sizeof filename); //清空数组
  230. fclose(fp); //关闭文件
  231. printf("请在检查文件路径后再次输入: \n");
  232. }
  233. else
  234. {
  235. flag = false;
  236. //文件读入resourceProject
  237. int cnt=0;
  238. // while(!feof(fp)) //读文件,直到文件末尾
  239. // {
  240. // resourceProject[cnt++] = fgetc(fp); //将文件内容读入resourceProject
  241. // }
  242. printf("文件%s已读取,请检查以下txt文件内容:\n",filename);
  243. }
  244. }
  245. if (fp == NULL )
  246. return 0;
  247. //以下显示文件内容
  248. memset(resourceProject,0,sizeof resourceProject);
  249. while(fgets(tempstr,1024,fp)!=NULL) //读文件一行内容,最多1024字符到缓冲区,并判断是否文件已经结束
  250. {
  251. printf("%s",tempstr); //显示缓冲区内容
  252. strcat(resourceProject,tempstr);//统一读入resourceProject
  253. }
  254. // fclose(fp); //关闭文件
  255. return fp;
  256. }//endRead
  257. //读入下一个字符
  258. char getChar(int p,char str[])
  259. {
  260. return str[p];
  261. }
  262. //分析函数
  263. char textAnalyze(char str[]){
  264. char* elements[strlen(str)]; //文本元素拆分
  265. int tag[strlen(str)];//1:关键字 2:标识符 3:常量 4:运算符 5:分隔符 6:特殊字符 7:控制命令
  266. memset(tag,'\0',sizeof(tag)/sizeof(tag[0]));
  267. int indTag=0;//tag的index
  268. int Tag=0;
  269. memset(elements,'\0',sizeof elements);
  270. memset(elements,'\0',(sizeof tag)/sizeof tag[0]);
  271. char ch;//读入的字符
  272. int p=0;//str的指针
  273. char token[128]={};//记录变量名
  274. int m=0;
  275. while((ch=getChar(p++,str))!='\0'){
  276. Tag=0;
  277. //printf("\ngetFirstChar:%c\n",ch);
  278. memset(token,'\0',sizeof(token)/sizeof(token[0]));
  279. if(isChar(ch)||ch=='_') //可能是标示符或者关键字
  280. {
  281. m=0;
  282. token[m++]=ch;
  283. token[m]='\0';
  284. bool isKey=false;
  285. int tmp=p;
  286. bool While=false;
  287. while(isChar((ch=getChar(tmp++,str)))||isDigit((ch=getChar(--tmp,str))))
  288. {
  289. While=true;
  290. //printf("while:%c\n",ch);
  291. if(isDigit(ch))
  292. tmp++;
  293. token[m++]=ch;
  294. token[m]='\0';
  295. p++;
  296. if(isKeyword(token)!=404)
  297. {
  298. tag[indTag++]=1;//关键字
  299. Tag=1;
  300. isKey=true;
  301. // p--;
  302. break;
  303. }
  304. }
  305. // token[m++]='\0';
  306. if(!isKey)
  307. {
  308. //p++;
  309. tag[indTag++]=2;//标识符
  310. Tag=2;
  311. }
  312. //else
  313. //p++;
  314. //printf("(%d,\"%s\")\n",tag[indTag-1],token);
  315. printf("(%d,\"%s\")\n",Tag,token);
  316. }
  317. else if((ch=='-')||isDigit(ch)||(ch=='.'))//数字
  318. {
  319. bool sym=false;
  320. int m=0;
  321. char digit[128]={};
  322. memset(digit,'\0',sizeof(digit)/sizeof(digit[0]));
  323. digit[m++]=ch;
  324. digit[m]='\0';
  325. int tmp=p;
  326. if((ch=='-')&&isDigit((ch=getChar(tmp+1,str))))
  327. {
  328. sym=true;
  329. }
  330. long long constNum=0;
  331. while(isDigit(ch=getChar(tmp,str))||(ch=getChar(tmp,str))=='.'||(ch=getChar(tmp,str))=='e'||(ch=getChar(tmp,str))=='E')
  332. {
  333. digit[m++]=ch;
  334. digit[m]='\0';
  335. constNum=constNum*10+ch-'0';
  336. tmp++;
  337. p++;
  338. }
  339. digit[m++] = '\0';//结束符
  340. if(!sym)
  341. {
  342. Tag=3;
  343. tag[indTag++]=3;//正数常量
  344. }
  345. else
  346. {
  347. tag[indTag++]=13;//负数常量
  348. Tag=13;
  349. }
  350. if(constNum>9223372036854775807)
  351. {
  352. tag[indTag++]=23;//大数常量
  353. Tag=23;
  354. }
  355. printf("(%d,\"%s\")\n",tag[indTag-1],digit);
  356. }
  357. else if(isDelimiters(ch))
  358. {
  359. // printf("%c\n",ch);
  360. token[0]=ch;
  361. token[1]='\0';
  362. tag[indTag++]=5;
  363. printf("(%d,\"%s\")\n",tag[indTag-1],token);
  364. }
  365. else if(isOperators(ch)!=404)//操作符
  366. {
  367. switch(ch) //其他字符
  368. {
  369. case'<':
  370. m=0;
  371. token[m++]=ch;
  372. ch=getChar(p++,str);
  373. if(ch=='='){
  374. Tag=4;
  375. tag[indTag++]=4;
  376. token[m++]=ch;
  377. token[m++]='\0';
  378. printf("(%d,\"%s\")\n",tag[indTag-1],token);
  379. }
  380. else{
  381. Tag=4;
  382. tag[indTag++]=4;
  383. token[m++]='\0';
  384. p--;
  385. printf("(%d,\"%s\")\n",tag[indTag-1],token);
  386. }
  387. break;
  388. case'>':
  389. m=0;
  390. token[m++]=ch;
  391. ch=getChar(p++,str);//取字符
  392. if(ch=='=')
  393. {
  394. Tag=4;
  395. tag[indTag++]=4;
  396. token[m++]=ch;
  397. token[m++]='\0';
  398. printf("(%d,\"%s\")\n",tag[indTag-1],token);
  399. }
  400. else
  401. {
  402. p--;
  403. Tag=4;
  404. tag[indTag++]=4;
  405. token[m++]='\0';
  406. printf("(%d,\"%s\")\n",tag[indTag-1],token);
  407. }
  408. break;
  409. case'|':
  410. m=0;
  411. token[m++]=ch;
  412. ch=getChar(p++,str);//取字符
  413. if(ch=='|')
  414. {
  415. Tag=4;
  416. tag[indTag++]=4;
  417. token[m++]=ch;
  418. token[m++]='\0';
  419. printf("(%d,\"%s\")\n",tag[indTag-1],token);
  420. }
  421. else
  422. {
  423. p--;
  424. Tag=4;
  425. tag[indTag++]=4;
  426. token[m++]='\0';
  427. printf("(%d,\"%s\")\n",tag[indTag-1],token);
  428. }
  429. break;
  430. case'&':
  431. m=0;
  432. token[m++]=ch;
  433. ch=getChar(p++,str);//取字符
  434. if(ch=='&')
  435. {
  436. Tag=4;
  437. tag[indTag++]=4;
  438. token[m++]=ch;
  439. token[m++]='\0';
  440. printf("(%d,\"%s\")\n",tag[indTag-1],token);
  441. }
  442. else
  443. {
  444. p--;
  445. Tag=4;
  446. tag[indTag++]=4;
  447. token[m++]='\0';
  448. printf("(%d,\"%s\")\n",tag[indTag-1],token);
  449. }
  450. break;
  451. case'!':
  452. m=0;
  453. token[m++]=ch;
  454. ch=getChar(p++,str);//取字符
  455. if(ch=='=')
  456. {
  457. tag[indTag++]=4;
  458. token[m++]=ch;
  459. token[m++]='\0';
  460. printf("(%d,\"%s\")\n",tag[indTag-1],token);
  461. }
  462. else
  463. {
  464. p--;
  465. tag[indTag++]=4;
  466. token[m++]='\0';
  467. printf("(%d,\"%s\")\n",tag[indTag-1],token);
  468. }
  469. break;
  470. default:
  471. {
  472. m=0;
  473. token[m++]=ch;
  474. token[m++]='\0';
  475. tag[indTag++]=4;
  476. printf("(%d,\"%s\")\n",tag[indTag-1],token);
  477. break;
  478. }
  479. }//endSwitch
  480. }//endIfEls
  481. else//特殊字符或控制命令
  482. {
  483. // printf("%c\n",ch);
  484. if(isSpesymbols(ch)!=404){
  485. //printf("%c\n",ch);
  486. bool isControl=false;
  487. if(ch=='#')
  488. {
  489. m=0;
  490. token[m++]=ch;
  491. token[m]='\0';
  492. int t=p;
  493. while(isChar((ch=getChar(t++,str))))
  494. {
  495. p++;
  496. token[m++]=ch;
  497. token[m]='\0';
  498. if(isContCommands(token)!=404)
  499. {
  500. Tag=7;
  501. tag[indTag++]=7;//控制命令
  502. isControl=true;
  503. break;
  504. }
  505. }
  506. // p++;
  507. if(isControl)
  508. {
  509. token[m++]='\0';
  510. // printf("(%d,\"%s\")\n",tag[indTag-1],token);
  511. printf("(%d,\"%s\")\n",Tag,token);
  512. }
  513. }
  514. else
  515. {
  516. m=0;
  517. token[m++]=ch;
  518. token[m++]='\0';
  519. tag[indTag++]=6;//特殊符号符
  520. printf("(%d,\"%s\")\n",tag[indTag-1],token);
  521. }
  522. }
  523. }//endElse
  524. }//endWhile
  525. }//endfunc
  526. //主函数
  527. int main(){
  528. char buf[MAX_LINE]; /*缓冲区*/
  529. char ch,sh;
  530. FILE* fp; /*文件指针*/
  531. //下面是写数据,将数字0~9写入到data.txt文件中
  532. printf("***请选择读取待编译程序的方式***\n");
  533. printf("***输入 1:屏幕输入;输入 2:已放入记事本***\n");
  534. int tmp;
  535. scanf("%d",&tmp);
  536. getchar();//吞掉空格
  537. if (tmp==1)
  538. {
  539. FILE *fpWrite=fopen("data.txt","a");
  540. if(fpWrite==NULL)
  541. {
  542. perror("fail to read");
  543. exit(1);
  544. return 0;
  545. }
  546. printf("您输入的是 1,请继续输入待编译程序,并以@加回车结束!\n");
  547. printf("输入内容将同步显示..\n");
  548. ch = getchar();
  549. while (ch != '@') {
  550. fputc(ch,fpWrite); //写入文件
  551. putchar(ch); //输出到屏幕
  552. ch = getchar();
  553. }
  554. fclose(fpWrite);//关闭文件
  555. printf("程序已读入文件data.txt..");
  556. //已经获取文件处理程序
  557. //fpWrite=fopen("data.txt","a");//重新打开文件,重置文件指针
  558. //fclose(fpWrite);//关闭文件
  559. getchar();
  560. fp=readTxt();//获取文件指针
  561. printf("文件读取完毕..\n");
  562. printf("resourceProject中的程序为:\n");
  563. printf("%s",resourceProject);
  564. fclose(fp);//关闭文件
  565. }
  566. else if(tmp==2)
  567. {
  568. printf("您输入的是 2,接下来将进入文件读取程序..\n");
  569. fp=readTxt();//获取文件指针
  570. printf("文件读取完毕..\n");
  571. printf("resourceProject中的程序为:\n");
  572. printf("%s",resourceProject);
  573. fclose(fp);//关闭文件
  574. }
  575. //开始处理读入的程序
  576. preProcessing();//预处理
  577. printf("\n预处处理完成后的程序为:\n");
  578. printf("%s",resourceProject);
  579. printf("\n下面开始词法分析:\n");
  580. textAnalyze(resourceProject);
  581. //下面是读数据,将读到的数据存到数组a[10]中,并且打印到控制台上
  582. // int a[10]={0};
  583. // FILE *fpRead=fopen("data.txt","r");
  584. // if(fpRead==NULL)
  585. // {
  586. // return 0;
  587. // }
  588. // for(int i=0;i<10;i++)
  589. // {
  590. // fscanf(fpRead,"%d ",&a[i]);
  591. // printf("%d ",a[i]);
  592. // }
  593. // getchar();//等待
  594. //
  595. // fclose(fpRead);
  596. free(commentStr);
  597. free(resourceProject);
  598. return 0;
  599. }

运行结果:

 

        对编译原理中的词法分析实验进行了记录,但需要说明的是,本次实验面向测试集编程,处理方式分为两遍,并非一遍扫描处理。

        编写代码时需十分小心指针的位置,符号的提前读入、回溯等。

        程序逻辑划分较为明显,有需要的可主要对进行词法分析的 char textAnalyze(char str[]) 函数进行改写。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/294235
推荐阅读
相关标签
  

闽ICP备14008679号