美文网首页
词法分析器

词法分析器

作者: 435fa00b72e7 | 来源:发表于2016-12-29 20:19 被阅读0次

词法分析器

词法分析器又称扫描器。词法分析是指将我们编写的文本代码流解析为一个一个的记号,分析得到的记号(Token)以供后续语法分析使用。

  • 输出分析

     关键字表:program begin end var while do repeat until for to if then else ; : ( ) , := + - * / > >= == < <=   
     Token序列:(4,0)(13,0)(1,0)(26,0)(1,1)(1,2)(0,0)(2,0)(5,0)
     符号表:a b c 
     常数表:33 
    
    • 关键字表是固定的,是你设置的关键字
    • Token是输出的记号
    • 符号表是读取的输入中有哪些英文字母
    • 常数表是读取的输入中有哪些数字
  • 变量分析

    • char keywords[30][12]:所存储的所有界符,就是特殊标识符和符号
    • int aut[11][8]:有限状态机转移形成的矩阵
    • char ID[50][12]:存储字符的数组,用来避免重复记录
    • int C[20]:存储数字的数组,用来避免重复记录
    • struct token:生成的记号
    • int n,p,m,e,t:尾数值,指数值,小数位数,指数符号,类型
    • char w[50]:读入缓冲区
    • struct map:用来识别你的输入字符
  • 方法分析

    • void act(int s):读取一个状态,选择case执行
    • find(int s,char ch):输入一个状态和一个读取的字符,返回对应有限状态自动机对应表中的值
    • int InsertConst(double num):将一个数字添加到C[]集合中,返回该值在C集合中的位置
    • int Reserve(char *str):读入一个字符判断是不是界符,如果是界符,则返回符合界符数组的下标+3,否则返回0<不知道这里的+3是不是错误,我原来以为是为了区分code=1和2的情况,后来发现添加数字的时候又不加3了>
    • int InsertID(char *str):将一个字符串插入到ID[]数组中
  • 简易分析图

词法分析器.png
  • 代码分析
    /*
    code表:
    0 :
    1 : 标识符
    2 : 常数
    3-33 : 对应的是界符中元素的位置
    */

     #include "string.h"
     #include "math.h"
     #include "stdio.h"
    
     char keywords[30][12]={
         "program","begin","end","var","while","do","repeat",
         "until","for","to","if","then","else",";", ":", "(", ")", ",",
         ":=", "+", "-", "*", "/", ">", ">=", "==", "<", "<="};
     int num_key=28;
     /*
       d    ·  E|e +|-  l   b  -1
     1 2                8   9  15
     2 2   3   5   11          11
     3 4
     4 4       5   11          11
     5 7           6
     6 7
     7 7           11          11
     8 8                8      12
     9                      10 14
     10 13
    
      d为数字,l为字母,b为界符,-1代表其它符号(如在状态8处遇到了非字母或数字的其它符号,会变换到状态12)。
      */
     int aut[11][8]={
         0, 0, 0, 0, 0, 0, 0, 0,
         0, 2, 0, 0, 0, 8, 9,15,
         0, 2, 3, 5,11, 0, 0,11,
         0, 4, 0, 0, 0, 0, 0, 0,
         0, 4, 0, 5,11, 0, 0,11,
         0, 7, 0, 0, 6, 0, 0, 0,
         0, 7, 0, 0, 0, 0, 0, 0,
         0, 7, 0, 0,11, 0, 0,11,
         0, 8, 0, 0, 0, 8, 0,12,
         0, 0, 0, 0, 0, 0,10,14,
         0, 0, 0, 0, 0, 0, 0,13
     };
     char ID[50][12];
     int C[20];
     int num_ID=0,num_C=0;
    
     struct token{
         int code;
         int value;
     };                                    //Token结构
    
     struct token tok[100];                //Token数组
     int i_token=0,num_token=0;            //Token计数器和Token个数
    
     char strTOKEN[15];                    //当前单词
     int i_str;                            //当前单词指针
    
     int n,p,m,e,t;                        //尾数值,指数值,小数位数,指数符号,类型
     double num;//常数值
     char w[50];                           //源程序缓冲区
     int i;                                //源程序缓冲区指针,当前字符为w[i]
    
     struct map{                            //当前字符到状态转换矩阵列标记的映射
         char str[50];
         int col;
     };
     struct map col1[4]={{"0123456789",1},{".",2},{"Ee",3},{"+-",4}};        //数字
     struct map col2[2]={{"abcdefghijklmnopqrstuvwxyz",5},{"0123456789",1}}; //关键字或标识符
     struct map col3[1]={{";:(),+-*/=><",6}};                                //界符
     struct map *ptr;
     int num_map;
    
     void act(int s);
     int find(int s,char ch);
     int InsertConst(double num);
     int Reserve(char *str);
     int InsertID(char *str);
    
     int main(int argc, char* argv[]){
         FILE *fp;
         int s;//当前状态
         fp=fopen("/Users/za/Desktop/doc/编译原理/code/Compliers/Compliers/exa.txt","r");
         while (fgets(w,50,fp)!=NULL){
             //读取文件中的50个字符
             printf("1");
             i=0;
             do{
                 //w[50] = begin /n end
                 while (w[i]==' '){//滤空格
                     i++;
                 }if (w[i]>='a' && w[i]<='z'){//读取的首个字符是字母
                     ptr=col2;
                     num_map=2;
                 }else if (w[i]>='0' && w[i]<='9'){//读取的首个字符是数字
                     ptr=col1;
                     num_map=4;
                 }else if (strchr(col3[0].str,w[i])==NULL){
                     printf("非法字符%c\n",w[i]);
                     i++;
                     continue;
                 }else{
                     ptr=col3;  num_map=1;
                 }
                 i--;
                 s=1;//开始处理一个单词
                 //当s不为0时执行循环,s代表的是有限状态自动机中的状态集
                 
                 while (s!=0){
                     act(s);//s=1时,strTOKEN[0]='\0'
                     if (s>=11 && s<=14){
                         break;
                     }
                     /*
                      getchar()
                      i= (-1)=>{0}
             */
                     i++;
                     /*
                      s=1 w[0]='b'
                     */
                     s=find(s,w[i]);
                 }
                 
                 //如果s是等于0跳出的循环
                 if (s==0){
                     strTOKEN[i_str]='\0';//代表字符串的结尾
                     printf("词法错误:%s\n",strTOKEN);
                 }
             }while (w[i]!=10);
         }
         
         printf("关键字表:");//输出结果
         for (i=0;i<30;i++){
             printf("%s ",keywords[i]);
         }
         printf("\n");
         printf("Token序列:");
         for (i=0;i<num_token;i++){
             printf("(%d,%d)",tok[i].code,tok[i].value);
         }
         printf("\n");
         printf("符号表:");
         for (i=0;i<num_ID;i++){
             printf("%s ",ID[i]);
         }
         printf("\n");
         printf("常数表:");
         for (i=0;i<num_C;i++){
             printf("%d ",C[i]);
         }
         printf("\n");
         
         fclose(fp);
         return 0;
     }
    
     void act(int s){
         int code;
         switch (s){
             case 1:n=0;m=0;p=0;t=0;e=1;num=0;i_str=0;
                 strTOKEN[i_str]='\0';                   //其它变量初始化
                 break;
                 /*
                  n,p,m,e,t;
                  尾数值,指数值,小数位数,指数符号,类型
                  */
             case 2:n=10*n+w[i]-48;
                 break;
             case 3:t=1;
                 break;
             case 4:n=10*n+w[i]-48; m++;
                 break;
             case 5:t=1;
                 break;
             case 6:if (w[i]=='-') e=-1;
                 break;
             case 7:p=10*p+w[i]-48;
                 break;
             case 8:strTOKEN[i_str++]=w[i];  //将ch中的符号拼接到strTOKEN的尾部;
                 break;
             case 9:strTOKEN[i_str++]=w[i];  //将ch中的符号拼接到strTOKEN的尾部;
                 break;
             case 10:strTOKEN[i_str++]=w[i]; //将ch中的符号拼接到strTOKEN的尾部;
                 break;
             case 11:num=n*pow(10,e*p-m);           //计算常数值
                 tok[i_token].code=2;  tok[i_token++].value=InsertConst(num);  //生成常数Token
                 num_token++;
                 break;
             //case12是当你的首个输入字符是字母,而后的情况中遇到了输入非字母和数字会执行的情况
             case 12:strTOKEN[i_str]='\0';
                 code=Reserve(strTOKEN);//查界符表
                 if (code){//如果匹配到界符表中的元素,生成关键字Token
                     tok[i_token].code=code;//将之前减去的3加回来
                     tok[i_token++].value=0;
                 }else{
                     tok[i_token].code=1;
                     tok[i_token++].value=InsertID(strTOKEN);
                 }//生成标识符Token
                 num_token++;//token的个数
                 break;
             case 13:strTOKEN[i_str]='\0';
                 code=Reserve(strTOKEN);//查界符表
                 if (code){
                     tok[i_token].code=code;  tok[i_token++].value=0; }//生成界符Token
                 else{
                     strTOKEN[strlen(strTOKEN)-1]='\0';//单界符
                     i--;
                     code=Reserve(strTOKEN);//查界符表
                     tok[i_token].code=code;
                     tok[i_token++].value=0;//生成界符Token
                 }
                 num_token++;
                 break;
             case 14:strTOKEN[i_str]='\0';
                 code=Reserve(strTOKEN);//查界符表
                 tok[i_token].code=code;
                 tok[i_token++].value=0;//生成界符Token
                 num_token++;
                 break;
         }
     }
     //find方法:输入一个int和一个char
     //s=1,ch='b'
     //s代表的是当前状态集,ch代表的是当前读取到的字符
     //find方法,接收一个状态集和一个字符集,返回下一步的状态集
     int find(int s,char ch){
         int i;
         int col=7;//默认了i的值是-1,如果之后查找不到的话直接认作是其他符号输入,再进行判断是不是界符
         struct map *p = ptr;
         for (i=0;i<num_map;i++){
             /*
              ch='b' 返回'b'在str数组中的位置,即判断是否存在这个元素
             */
             if (strchr((p+i)->str,ch)){
                 col=(p+i)->col;//如果是'd',则col=5
                 break;
             }
         }
         //返回当前状态和字符所对应的有限状态自动机中的位置
         return aut[s][col];
     }
     //插入数字
     int InsertConst(double num){
         int i;
         for (i=0;i<num_C;i++)
             if (num==C[i])
                 return i;
         C[i]=num;
         num_C++;
         return i;
     }
     /*
      Reserve方法,读入一个字符
      判断是不是界符,如果是界符,则返回符合界符数组的下标+3,否则返回0
      */
     int Reserve(char *str){
         int i;
         for (i=0;i<num_key;i++){
             //判断str的元素是不是keywords里的
             if (!strcmp(keywords[i],str)){
                 return (i+3);
             }
         }
         return 0;
     }
     /*
      InsertId方法,输入一个字符
      返回当前的字符串在ID字符串数组中的下标
      */
     int InsertID(char *str){
         //查看在ID数组中有没有和输入字符串相同的元素
         //第一个字符串没有,i=num_ID=0
         int i;
         for (i=0;i<num_ID;i++){
             if (!strcmp(ID[i],str)){
                 return i;
             }
         }
         //将当前输入的str copy到ID[i]中
         strcpy(ID[i],str);
         num_ID++;
         return i;
     }

相关文章

  • 一个编译器最简前端的python实现

    一个编译器的前端通常包括词法分析器和语法分析器。在分析过程中,文本输入词法分析器,根据词法规则解析出词法单元。词法...

  • PHP核心理解-flex和bison入门

    一般词法分析器和语法分析器会一起使用,语法分析器会调用词法分析器来读取输入,词法分析器匹配到特定的模式后,就向语法...

  • 三. Flex进阶:需要了解的一些知识

    参考:词法分析器生成工具flex词法分析器总结--flex&bison词法分析生成器flex的选项 1. Flex...

  • 自己动手制作C 语言编译器(3):词法分析器

    本章我们要讲解如何构建词法分析器。 什么是词法分析器 简而言之,词法分析器用于对源码字符串做预处理,以减少语法分析...

  • 龙书 第三章

    词法单元:词法分析器扫描源程序并输出一个由词法单元组成的序列。这些词法单元通常会逐个传送给语法分析器。有些词法单元...

  • 编译原理->词法分析

    词法分析器的作用 词法分析器的主要任务是读入源程序的输入字符、将它们组成词素,生成并输出一个词法单元序列,每个词法...

  • 词法分析

    词法分析 词法分析器:字符流->记号流 词法分析器的手工构造比较符号的转移图转移图.png标识符的转移图转移图标识...

  • 语法分析器 Parser

    词法分析器-->output: 记号 --> 语法分析器 -->output: AST 抽象语法树 context...

  • 编译原理(一) Lexical analysis

    词法分析器的作用 词素(Lexeme)词法单元(Token): Token-name + Attribute-va...

  • 从正则表达式到词法分析器代码

    词法分析器 从声明式的规范到词法分析器代码通常会经历一些步骤: RE(通过Thompson算法)>RFA(子集构造...

网友评论

      本文标题:词法分析器

      本文链接:https://www.haomeiwen.com/subject/buupvttx.html