词法分析器

词法分析器

词法分析器又称扫描器。词法分析是指将我们编写的文本代码流解析为一个一个的记号,分析得到的记号(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;
     }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容