Scanner scanner = new Scanner(info);

string source = "asdf04a 1107 else";
StringReader sr = new StringReader(source);

scanner.SetSource(new SourceReader(sr));
scanner.SetSkipTokens(WHITESPACE.Index);

Lexeme l1 = scanner.Read();
Console.WriteLine(l1.TokenIndex); //等于ID.Index
Console.WriteLine(l1.Value); //等于 asdf04a

Lexeme l2 = scanner.Read();
Console.WriteLine(l2.TokenIndex); //等于NUM.Index
Console.WriteLine(l2.Value); //等于 1107

Lexeme l3 = scanner.Read();
Console.WriteLine(l3.TokenIndex); //等于ELSE.Index
Console.WriteLine(l3.Value); //等于 else

Lexeme l4 = scanner.Read();
Console.WriteLine(l4.TokenIndex); //等于info.EndOfStreamTokenIndex
Console.WriteLine(l4.Value); //等于 null

创建Scanner对象时,需要传入上一步生成的ScannerInfo对象,接下来可以指定输入的源代码。这里我们用StringReader来读取一段字符串源代码。注意Scanner的SetSkipTokens方法,可以设定词法扫描器自动跳过的单词。比如我们不希望词法分析器返回空白字符的词素,就设定跳过WHITESPACE单词。在操作Scanner类的时候,所有与Token相关的操作都是通过Token.Index(一个整数)来完成的,因为Scanner内部仅仅保存了Token在Lexicon内部的索引值,这样可以减小内存使用并且提高效率。

一切准备就绪之后就可以调用scanner.Read()方法来进行词法分析了!每次调用scanner.Read()会返回下一个词素(Lexeme对象),从Lexeme的属性中我们可以拿到该词素对应的单词类型(仍然是以Token.Index整数形式),词素的字符串表示(Value属性)以及词素在源代码中位置等丰富的信息。当扫描到文件或字符串尾部时,scanner.Read()会返回一个特殊的词素表示End Of Stream。这个特殊词素的TokenIndex可以从ScannerInfo对象查询到(每个ScannerInfo的EndOfStreamTokenIndex会不一样)。大家可以试着运行一下上述代码,并且修改自己的词法定义或源代码,来观察Scanner类的各种行为。另外VBF.Compilers.Scanner库还提供了两种具有特殊能力的Scanner——分别是PeekableScanner和ForkableScanner,未来的篇章中我们会用到它们的特殊能力。

Logo

CANN开发者社区旨在汇聚广大开发者,围绕CANN架构重构、算子开发、部署应用优化等核心方向,展开深度交流与思想碰撞,携手共同促进CANN开放生态突破!

更多推荐