编译原理:词法分析实验

发布时间 2023-06-10 20:20:00作者: 丘丘王

实验二 词法分析

实验目的

  • 根据 PL/0 语言的文法规范,编写 PL/0 语言的词法分析程序。
  • 通过设计调试词法分析程序,实现从源程序中分出各种单词的方法; 加深对课堂教学的理解;提高词法分析方法的实践能力。
  • 掌握从源程序文件中读取有效字符的方法和产生源程序的内部表示 文件的法。
  • 掌握词法分析的实现方法。 上机调试编出的词法分析程序。

主要思想:

  • IO:实际的词法分析应当是对输入的文本文件进行检测,因此将IO重定向至文件。读入文件后,首先在main函数中逐行分割文本,再将每一行的文本分别送入检测器。

  • 词法检测:我们使用的方法是设计一个状态机进行检测。我们设置了以下几种状态:

    public enum WordState {
        START,WORD,INT,FLOAT,OPERATE,OPERATE2,SYMBOL,WORDEND,INTEND,INVALID,
        FLOATEND,OPERATEEND,SYMBOLEND;
    }
    

    其中前七种状态分别代表初态、检测到单词、整数、浮点数、操作符1(除了=)和=(这是因为当识别到除了=之外的操作符时匹配已经完成,但是匹配到=后,需要再检测其后是否为=,若是则组成==,若不是则结束,因此需要额外设置一个状态),以及非操作符。

    之后的状态(XXEND)则分别代表在前七种状态下识别到其他符号,所进入的终态。一旦我们检测到状态机进入了终态,就可以根据对应的终态获得该单词属于什么类别,从而输出结果了。

    从上述可知,该状态机中最重要的问题是如何根据输入改变状态。我们编写下面的函数用于改变状态机的状态:

    static WordState transfer(WordState state,char c)
    

    该函数定义较长且多为分支选择,因此不再赘述而简要说明其原理。该函数中传入的参数分别为当前状态机的状态,以及下一个输入字符,函数将根据这两个参数进行状态转移。

    例如,当前状态为初态(START)时,若接收到的字符c是字母,则可以断定正常情况下这是用户输入了单词,因此状态转移至单词(WORD);若接收到的是数字,则有两种可能,该词为整数或浮点数,这将由之后状态机是否接受到"."决定,因此我们现在直接状态转移至INT;若接收到的是< > = ! 则为操作数,转移至OPERATE;若收到的是空格(' '),则说明下个单词即将开始,状态仍为START

    再比如,当前状态为单词(WORD)时,若接收到的是字母、数字或下划线,则符合文法中对单词的定义,因此其状态仍是WORD;若其接收到了除此之外的符号,则说明单词已经输完,应当转而判别和输出了,因此进入WORDEND状态。

    其余状态转移与此原理相同,不再赘述。

  • 全句判别与结果输出:当状态机进入终态后要输出,其中函数void outPut()完成格式化输出工作,函数void checkString(String s)完成重新设置初态、清空缓存的工作。

重要代码定义

public enum WordState {
    START,WORD,INT,FLOAT,OPERATE,OPERATE2,SYMBOL,WORDEND,INTEND,INVALID,
    FLOATEND,OPERATEEND,SYMBOLEND;

    static WordState transfer(WordState state,char c){
        switch (state){
            case WORD:
                if(Character.isLetter(c)||Character.isDigit(c)||c=='_') //回到本状态
                    return WORD;
                else
                    return WORDEND;
            case INT:
                if(Character.isDigit(c))
                    return INT;
                else if(Character.isLetter(c))
                    return INVALID;
                else if(c=='.')
                    return FLOAT;
                else return INTEND;
            case FLOAT:
                if (Character.isLetter(c))
                    return INVALID;
                if (Character.isDigit(c))
                    return FLOAT;
                else return FLOATEND;
            case OPERATE:
                if (c=='=')
                    return OPERATE2;
                else return SYMBOLEND;
            case START:
                if (Character.isLetter(c))
                    return WORD;
                else if (Character.isDigit(c))
                    return INT;
                else if (c=='='||c=='>'||c=='<'||c=='!')
                    return OPERATE;
                else if (c==' ')
                    return START;
                else return SYMBOL;
            case OPERATE2:
                return OPERATEEND;
            case SYMBOL:
                return SYMBOLEND;

            default: return state;
        }
    }
}

运行结果:

用于测试的输入文本:

test1:

int main() {
    int i,j,k,sum = 0;
    for(i = 0;i<10;i++)
        sum++;
    return 0;
}

结果:

(Keyword,int)
(Identifier,main)
(Lparen ,()
(rparen ,))
(Symbol,{)
(Keyword,int)
(Identifier,i)
(Symbol,,)
(Identifier,j)
(Symbol,,)
(Identifier,k)
(Symbol,,)
(Identifier,sum)
(becomes,=)
(Number,0)
(comma ,;)
(Keyword,for)
(Lparen ,()
(Identifier,i)
(becomes,=)
(Number,0)
(comma ,;)
(Identifier,i)
(lss ,<)
(Number,10)
(comma ,;)
(Identifier,i)
(plus,+)
(plus,+)
(rparen ,))
(Identifier,sum)
(plus,+)
(plus,+)
(comma ,;)
(Keyword,return)
(Number,0)
(comma ,;)

test2:

int test(char *s) {
    s = "testppp";
    double Double = 1; 
    int(23.3);
    double(3);
    
    return 0;
}

结果:

(Keyword,int)
(Identifier,test)
(Lparen ,()
(Keyword,char)
(times ,*)
(Identifier,s)
(rparen ,))
(Symbol,{)
(Identifier,s)
(becomes,=)
(Symbol,")
(Identifier,testppp)
(Symbol,")
(comma ,;)
(Keyword,double)
(Identifier,Double)
(becomes,=)
(Number,1)
(comma ,;)
(Keyword,int)
(Lparen ,()
(Number,23.3)
(rparen ,))
(comma ,;)
(Keyword,double)
(Lparen ,()
(Number,3)
(rparen ,))
(comma ,;)
(Keyword,return)
(Number,0)
(comma ,;)

test3:

void test(){
    double indicator = 100;
    while(true){
        indicator/=1.1;
        if(indicator<1)  //对indicator循环除1.1
            break;
    }
}

结果:

(Keyword,void)
(Identifier,test)
(Lparen ,()
(rparen ,))
(Symbol,{)
(Keyword,double)
(Identifier,indicator)
(becomes,=)
(Number,100)
(comma ,;)
(Keyword,while)
(Lparen ,()
(Keyword,true)
(rparen ,))
(Symbol,{)
(Identifier,indicator)
(slash,/)
(becomes,=)
(Number,1.1)
(comma ,;)
(Keyword,if)
(Lparen ,()
(Identifier,indicator)
(lss ,<)
(Number,1)
(rparen ,))
(Keyword,break)
(comma ,;)
(Symbol,})