扫描器

TypeScript 扫描器的源码均位于 scanner.ts。在内部,由解析器控制扫描器将源码转化为抽象语法树(AST)。期望结果如下:

  1. SourceCode ~~ 扫描器 ~~> Token ~~ 解析器 ~~> AST

解析器对扫描器的使用

为避免重复创建扫描器造成的开销,parser.ts 中创建了一个扫描器的单例。解析器根据需要使用 initializeState 函数准备该扫描器。

下面是解析器中的实际代码的简化版,你可以运行它演示以上概念

code/compiler/scanner/runScanner.ts

  1. import * as ts from 'ntypescript';
  2. // 单例扫描器
  3. const scanner = ts.createScanner(ts.ScriptTarget.Latest, /* 忽略杂项 */ true);
  4. // 此函数与初始化使用的 `initializeState` 函数相似
  5. function initializeState(text: string) {
  6. scanner.setText(text);
  7. scanner.setOnError((message: ts.DiagnosticMessage, length: number) => {
  8. console.error(message);
  9. });
  10. scanner.setScriptTarget(ts.ScriptTarget.ES5);
  11. scanner.setLanguageVariant(ts.LanguageVariant.Standard);
  12. }
  13. // 使用示例
  14. initializeState(
  15. `
  16. var foo = 123;
  17. `.trim()
  18. );
  19. // 开始扫描
  20. var token = scanner.scan();
  21. while (token != ts.SyntaxKind.EndOfFileToken) {
  22. console.log(ts.formatSyntaxKind(token));
  23. token = scanner.scan();
  24. }

该段代码输出以下内容:

  1. VarKeyword
  2. Identifier
  3. FirstAssignment
  4. FirstLiteralToken
  5. SemicolonToken

扫描器状态

调用 scan 后,扫描器更新其局部状态(扫描位置,当前 token 详情等)。扫描器提供了一组工具函数获取当前扫描器状态。下例中,我们创建一个扫描器并用它识别 token 以及 token 在代码中的位置。

code/compiler/scanner/runScannerWithPosition.ts

  1. // 使用示例
  2. initializeState(
  3. `
  4. var foo = 123;
  5. `.trim()
  6. );
  7. // 开始扫描
  8. var token = scanner.scan();
  9. while (token != ts.SyntaxKind.EndOfFileToken) {
  10. let currentToken = ts.formatSyntaxKind(token);
  11. let tokenStart = scanner.getStartPos();
  12. token = scanner.scan();
  13. let tokenEnd = scanner.getStartPos();
  14. console.log(currentToken, tokenStart, tokenEnd);
  15. }

该代码输出以下内容:

  1. VarKeyword 0 3
  2. Identifier 3 7
  3. FirstAssignment 7 9
  4. FirstLiteralToken 9 13
  5. SemicolonToken 13 14

独立扫描器

即便 TypeScript 解析器有单例扫描器,你仍可以使用 createScanner 创建独立的扫描器,然后可以用 setText/setTextPos 随意扫描文件的不同位置。

原文: https://jkchao.github.io/typescript-book-chinese/compiler/scanner.html