设计一门脚本语言——(三)词法分析

本文详细介绍了词法分析的概念及其在脚本解析过程中的作用。通过定义枚举 TokenType,将字符串转换为有意义的类型,并实现词法分析器以识别脚本中的关键字、变量、标识符等元素。进一步展示如何通过函数 GetToken() 实现逐个读取 Token 并进行解析。最终,演示如何使用此词法分析器对脚本进行简单的词法分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是词法分析?概念一大堆,反正我也不想看。按我自己的来理解吧

上面的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;

}

}

这样写有个问题,Ident10怎么判断呢?

因为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;

看着比较怪异

而且语法上肯定是错误的,但词法上是正确的,因为完全符合词法意义

既然说到语法分析了,那下一步就语法分析吧~~其实叫分析,还不如叫运行,因为是解释型的脚本~

我要开始脚本的具体思路了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值