词法分析器模拟

模拟词法分析器,把读入的字符串,识别为一个个词法单元并输出。

对应语言关键字 else if int return void while

专用符号  + - * / < <= > >= == != = ; , ( ) [ ] { } /* */

首先理清思路:需要做什么?

1 从输入文件读取字符 。

2 识别字符,这个识别过程应该是循环的。

3 输出识别结果。

------------------------------------------------------

怎么读:词法分析需要我们一个一个的读入字符

这里使用fgetc(FILE*file)函数。

-----------------------------------------------------

怎么识别:对接收的字符进行处理,这里需要识别

的结果有:数字,操作符,标识符,函数名,保留字,

注释,不能识别。这些可以存在一个字符型指针数组中。

如何开始识别数字:读入一个 '0'~‘9’之间的字符

如何开始识别操作符,标识符,函数名,保留字,注释,

读入一个字母,如果这个字母是'/',开始识别注释,如果是

操作符中的字符,那么开始识别操作符,如果是其他,开始识别

标识符与函数名。

------------------------------------------------------

怎么输出,这里使用格式:<RESERVED,INT>

在文件中输出用 fprintf(FILE*file,s)

-----------------------------------------------------

打印部分代码:

void console_print(int i,int start_pos,int end_pos){   
    cout<<"<"<<token[i] <<",";
    fprintf(output,"%c%s%c","<",token[i],",");
    int length=end_pos-start_pos;   //识别的串的长度 

    fseek(input,-length,1);       
    //fseek(input,start_pos,0);
    //移动位置太多   
    /**********************
    要打印识别的串,首先要把
    读取文本的文件指针的位置
    回退到,串的开始位置。 
    source:文件指针
    length:移动偏移,负数为
    向左偏移 
    1:此部分有三个参数可选
    0:从文件的开始位置移动
    1:从文件的当前位置移动
    2:从文件的结束位置移动 
    **********************/
    char temp;
    for(int j=start_pos;j<end_pos;j++){
        temp=char(fgetc(input));
        cout<<temp;
        fprintf(output,"%c",temp); 
    } 
    fprintf(output,"%c%c",">",'/n');
    cout<<">"<<endl;
}
/**********************************************

fucntion:
    file_print(int i,int start_pos,int end_pos)
    
parameter:
     @i:在字符指针数组中的索引,代表识别的类型 
     @start_pos: 要打印的串在文件中的开始位置
     @end_pos:   要打印的串在文件中的结束位置
      
usage:
    在文件中打印识别的串 

format:
    打印样例:<RESERVED,int>
        

分开打印会存在问题,当调用cosole_print后,又要重新调整
文件指针,较为麻烦 写完之后发现,又注释掉,把此部分合道
cosole_print中 
void file_print(int i,int start_pos,int end_pos){
    
    int length=end_pos=start_pos;  //写完代码测试发现出错,应为减号
    
    fseek(input,-length,1);
    fprintf(output,"<%s, ",token[i]);
    for(int j=start_pos;j<end_pos;j++) {
        fprintf(output,"%c",fgetc(input));
    }
    fprintf(output,">\n");
}

在get_number()中调用,打印

console_print(i,start_pos,end_pos);
file_print(i,start_pos,end_pos);

这样存在的问题是,当调用console_print后,文件的指针与最初调用console时原来相比又有变化,

在file_print时,需要重新改动代码,如果是与console_print中一样的计算方法,就会出现问题,

为简单方便,直接把file_print()中的代码中合并到console_print()中。

get_comment()识别注释

void get_comment(char ch){
    
    start_pos=ftell(input)-1;  //记录开始位置
    state=0;
    
    while(!feof(input)){
        //若基于读入的字符分类,则比较麻烦
        //需要回吐字符判断
        switch(state){
            case 0:
                state=1;
                ch=fgetc(input);
            
                break;
            case 1:
                if(ch=='*'){                
                state=2;
                ch=fgetc(input);
                }
                else    //紧跟着'/'的不是'*'说明不是注释 ,根据定义只能是操作符 
                {    
                    if(ch=='\n')
                    fseek(input,-2,1);
                    else
                    fseek(input,-1,1);  //回退一个字符到结束位置 ,打印操作符 / 
                    
                    end_pos=ftell(input);
                    console_print(1,start_pos,end_pos);
                    file_print(1,start_pos,end_pos); 
                    return;
                }
                
                break;
            case 2:
                if(ch=='`'){
                    end_pos=ftell(input);
                    console_print(3,start_pos,end_pos);
                    file_print(3,start_pos,end_pos);
                }  //程序读取时候遇到一点问题,这里加一个"`"作为技术符号
                 
                else if(ch=='/'){             //如果是'/'检查是否为注释的结束 
                    fseek(input,-2,1);  //回退到'/'前面的位置,检查是否为* 
                    char temp=fgetc(input);
                
                    //system("pause");
                    if(temp=='*'){      //如果是则结束对注释的识别,打印注释部分 
                        fseek(input,1,1);
                        end_pos=ftell(input); 
                        console_print(3,start_pos,end_pos);
                        file_print(3,start_pos,end_pos);
                        
                        return; 
                    }
                    else{              //否则继续识别 
                        fseek(input,1,1);
                        ch=fgetc(input);
                        
                    }
                }
                
                else{
                    ch=fgetc(input);
                }
            
                break;
                    
        } 
    } 

}

这里起初用转化输入字符的方式,但是太麻烦,需要频繁的回吐字符,判断是否到达条件

因此用转换状态的方法。其次是在代码中有很多的选择结构,要注意文件指针的位置。

 

 get_id识别标识符(保留字,函数名,变量名)

void get_id(char ch){
    start_pos=ftell(input)-1;
    state=0;
    int end=0; 
    while(!feof(input)){
        if(!is_letter(ch)&&!is_number(ch)){
            if(state==0){  //状态为0时,判断是否为保留字 
             
            
            if(ch=='\n')
            fseek(input,-2,1);
            else
            fseek(input,-1,1);
            
            end_pos=ftell(input);
            char s[100]="";
            fseek(input,-(end_pos-start_pos),1);
            
            for(int i=0;i<end_pos-start_pos;i++){
                s[i]=fgetc(input);
        
            }
        
            if(is_reserved(s)){   //保留字 
        
                console_print(2,start_pos,end_pos);
                file_print(2,start_pos,end_pos);
                return;
            }
            ch=fgetc(input);  //不是保留字,继续读到下一个符号,进行下一轮的判断 
            state=1;
            //ch=fgetc(input);
        }
        else{
            
            if(ch==' '||ch=='\t'||ch=='\n'){
                ch=fgetc(input);
            } 
            else if(ch=='('){  //函数名 
                fseek(input,-1,1);
                
                end=ftell(input);
                //cout<<end-end_pos<<endl;
                fseek(input,end_pos-end,1);
                console_print(4,start_pos,end_pos);//在状态为0时,一把end_pos调到正好的位置 
                file_print(4,start_pos,end_pos);
                return;
            }
            else{
                if(ch=='\n')
                fseek(input,-2,1);
                else
                fseek(input,-1,1);
                end=ftell(input);
                fseek(input,end_pos-end,1); //文件指针回退到标识符结束的地方 
                console_print(5,start_pos,end_pos);
                file_print(5,start_pos,end_pos);
                return;
            }
        }
     }
        else{
            ch=fgetc(input);
        //    cout<<ch<<endl;
            //system("pause");
        }
    }
    
}

 

遇到的问题:

(1)

while(!is_recognized(ch)){//读取到可以识别的字符的地方 
                fgetc(input);
            //    system("pause");  检查代码时用到 
            }

最初的部分代码是这样的,这里存在一个问题是,

识别一个不能识别的字符后,就不能结束while循环了。

 (2)

注意换行符\n的特殊性,在使用fgetc时,会把\n一块读

入,但文件指针向前移动两份位置。

posted @ 2017-12-08 17:20  qynx  阅读(238)  评论(0编辑  收藏  举报