实验二 词法分析
实验目的
- 根据 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,})
标签:return,Keyword,Symbol,词法,编译,comma,原理,Identifier
From: https://www.cnblogs.com/czy-blogs/p/17471878.html