什么是词法分析?概念一大堆,反正我也不想看。按我自己的来理解吧
上面的Tokens,对于我们来说,看得懂,但对于程序来说,仅仅是字符串数组而已。和一长串的代码,也没有什么区别。
词法分析器的作用就是赋予这些字符串意义,然后程序能认识它们,知道它们是干什么用的。
我们来分析下
def 我们知道是定义东西用的,脚本规定好的,书面一点叫“关键字”
@表示定义变量
main 使我们类的名字
这就是这些单词的意义
好了,为了表示这些“意义”
在Lexel(还记得在哪吧)中定义枚举TokenType
public enum TokenType
{
End, // 脚本结束了
None, // 什么都不是
Key_Def, // 关键字def
Underline, // 下划线
Ident, // 标识符(字母开头的一次字符串,如main, a12, xxx等等)
At, // @符号
Equal, // 等号 =
Semicolon, // 分号
Key_End ,// 关键字 end
Real // 数字 包括 10 10.01 整数和小数
}
怎么把字符串转换为TokenType呢?
继续写个函数
简单暴力的写法
private TokenType GetTokenType(string token)
{
if ( token == “def” )
{
return TokenType.Key_Def;
}
if ( token == “_” )
{
return TokenType_Underline;
}
if ( token == “@”)
{
return TokenType.At;
}
if ( token == “=” )
{
return TokenType.Equal;
}
if ( token == “;” )
{
return TokenType.Semicolon;
}
if ( token == “end” )
{
return TokenType.Key_End;
}
}
这样写有个问题,Ident和10怎么判断呢?
因为Ident不是确定的一个值
可以写个函数
private bool IsReal(string token)
{
bool isFloat = false;
for (int i = 0; i < token.Length; i++)
{
if (token[i] == '.')
{
if (isFloat == false)
{
isFloat = true;
}
else
{
return false;
}
}
if (token[i] >= '0' && token[i] <= '9')
{
continue;
}
return false;
}
return true;
}
private bool IsIdent(string token)
{
if (!char.IsLetter(token[0]))
{
return false;
}
for (int i = 1; i < token.Length; i++)
{
if (!char.IsLetterOrDigit(token[i]))
{
return false;
}
}
return true;
}
这两个函数都比较简单
然后在GetTokenType中加上一句
if ( IsReal(token) )
{
return TokenType.Real;
}
if ( IsIdent(token) )
{
return TokenType.Ident;
}
现在我们可以转换字符串为TokenType了。
那么词法分析器怎么运作了?想想脚本是怎么运行的?
肯定是一步读取一个Token,边解析,边运行啊
既然这样,那就需要一个函数,读取一个Token
public TokenType GetToken()
{
try
{
if (index >= Tokens.Count)
{
currToken = string.Empty;
currType = TokenType.End;
return TokenType.End;
}
currToken = Tokens[index];
index++;
return GetTokenType(currToken);
}
catch (Exception ex)
{
throw ex;
}
}
还是比较短,首先判断是不是到末尾了如果index>=Tokens.Count了,那么表示分析完了。返回TokenType.End
否则的话当前让 currToken = Tokens[index];
并且index++;
然后调用函数GetTokenType(currToken)判断currToken的类型,最后返回给函数调用者
其中index表示当前词法分析器的位置,也就是Tokens数组的索引
currToken是一个string型的变量,储存当前Token,因为我们返回的是TokenType
如果是TokenType.At TokenType.Underline等,看到它我们就知道是@和_
那么如果是TokenType.Ident呢?是什么?所以用currToken保存当前Token的值
当前,如果你喜欢,完全可以反过来
用函数public string GetToken(); 然后把TokenType的值保存在currType中。这样也行
总之,我们需要调用GetToken函数,然后知道当前Token的值currToken以及它的类型currType
我比较喜欢两个都保存下来,所以我又申名了一个变量TokenType currType;
然后把
return GetTokenType(currToken);
改为
currType = GetTokenType(currToken);
return currType;
好了,词法分析器完成(其实差远了,但对于我我想测试的脚本,这就够了。一步步来嘛)
更改Snail中Execute函数的代码
public void Execute(string code)
{
Lexel lexel = new Lexel();
lexel.Start(code);
// 如果没有到文件末尾,就一直读取Token
while ( lexel.GetToken() != TokenType.End )
{
Console.WrtieLine(“{0} {1}”, lexel.CurrType.ToString(), lexel.CurrToken);
}
}
运行结果
Key_Def def
Underline _
Ident main
At @
Ident x
Equal =
Real 10
Semicolon ;
Key_End end
到现在,我们能对输入的脚本做一些比较简单的词法分析了。
可以虽然输入一些脚本测试下,比如
def_xxx
def_yyy
def_zzz
@zzzzz=1@@@@@22aaa;
看着比较怪异
而且语法上肯定是错误的,但词法上是正确的,因为完全符合词法意义
既然说到语法分析了,那下一步就语法分析吧~~其实叫分析,还不如叫运行,因为是解释型的脚本~
我要开始脚本的具体思路了