词法分析的任务
词法分析器 (Lexical Analyzer) 又称扫描器 (Scanner)
输入源程序、输出单词符号
若一个种别只有一个单词符号,则种别编码就代表该单词符号。假定基本字、运算符和界符都是一符一种。
若一个种别有多个单词符号,则对于每个单词符号,给出种别编码和自身的值。
我们常用常量对应种别编码方便阅读。
如下图,'id’则是‘标识符’的种别编码。‘while’的是‘while’,‘(’是‘(’。
词法分析是作为一个独立的阶段,是否应当将其处理为一遍呢?
作为独立阶段的优点:
不作为一遍:
语法分析器驱动词法分析器。
当语法分析器需要下一个单词时,调用词法分析器。词法分析器扫描下一个单词,分析下一个种类编码和自身的值,交给语法分析器。
语法分析器拿到后,如果可以进行语法分析,则进行语法分析,不行则取下一个单词。
随后程序控制会返回到扫描器中,扫描器会扫描缓冲区,将预处理处理的比较规范的字符串取出。
依据此法规则,识别但此符号(基本子,标识符,常量等),将单词符号的二元组返回给外界。
两根指针,起点指示器表示起点,搜索指示器表示当前终点。以此来指示一个单词。
当单词过长或内容过多时,单词可能会被截断。如果只用一个缓冲区,我们需要设置阈值,超过就不能再填入单词了,但设置上比较麻烦。
为了解决这个问题,将缓冲区一分为二。两个半区互补使用。限制每一个单词不可超过半区长度。
当我们使用前半部分缓冲区时,后半部分用于存储溢出。当我们使用后半部分缓冲区时,前半部分用于存储溢出。当溢出时,就不再填入了。
基本字的识别:当有允许程序员在编写中不空格等,我们需要超前搜索才能确定哪些是基本字。
DO99K=1,10 DO 99 K = 1,10
标识符识别:比如字母开头的字母数字串,后跟界符或算符,我们也需要用超前搜索。
常数识别:识别出算术常数并将其转变为二进制内码表示。有些也要超前搜索。
算符和界符的识别:当有把多个字符符合而成的算符和界符拼合成一 个单一单词符号,我们也需要超前搜索。
:= , ** , .EQ. , ++ , -- , >=
但是,由于做超前搜索比较困难,一般会用一些限制,以避免超前搜索。
所有基本字都是保留字 ; 用户不能用它们作自己的标识符
基本字作为特殊的标识符来处理,使用保留字表
如果基本字、标识符和常数 ( 或标号 ) 之间没有确定的运算符或界符作间隔,则必须使用一个空白符作间隔
状态转换图是一张有限方向图
结点代表状态,用圆圈表示(词法分析器分析一个单词时,所处的不同状态)
状态之间用箭弧连结,箭弧上的标记(字符)代表射出结状态下可能出现的输入字符或字符类
一张转换图只包含有限个状态,其中有一个为初态,至少要有一个终态
状态转换图可用于识别(或接受)一定的字符串
65. 有效数字 - 力扣(LeetCode) (leetcode-cn.com)
若存在一条从初态到某一终态的道路,且这条路 上所有弧上的标记符连接成的字等于,则称为 该状态转换图所识别 ( 接受 )
直接用编码表示不便于记忆,因此用助忆符来表示编码。
实现:
定义
伪代码
int code,value;
string strToken = "";
Getchar();
GetBC();
if(IsLetter()){
while (IsLetter() || IsDigit()){
Concat();
GetChar();
}
Retract();
code = Reserve();
if (code = 0){//标识符
value := InsertId(strToken);
return ($ID, value);
}
else//保留字
return (code, -);
}
else if (IsDigit()){
while (IsDigit()){
Concat( );
GetChar( );
}
Retract();
value = InsertConst(strToken);
return($INT, value);
}
else if (ch =‘=’)
return ($ASSIGN, -);
else if (ch =‘+’)
return ($PLUS, -);
else if (ch =‘*’){
GetChar();
if (ch =‘*’)
return ($POWER, -);
Retract();
return ($STAR, -);
}
else if (ch =‘,’)
return ($COMMA, -);
else if (ch =‘(’)
return ($LPAR, -);
else if (ch =‘)’)
return ($RPAR, -);
else
ProcError( ); /* 错误处理 */
一般化,可以用二位数组表示状态图。
int curState = 0;//初态
GetChar();
while(stateTrans[curState][ch]){
// 存在后继状态,读入、拼接
Concat();
// 转换入下一状态,读入下一字符
curState = stateTrans[curState][ch];
if(cur_state == 8)
return strToken;
GetChar( );
}
如果想要实际实现一下代码,可以看一下力扣的65题:
65. 有效数字 - 力扣(LeetCode) (leetcode-cn.com)