扫描器
TypeScript 扫描器的源码均位于 scanner.ts
。在内部,由解析器控制扫描器将源码转化为抽象语法树(AST)。期望结果如下:
SourceCode ~~ 扫描器 ~~> Token 流 ~~ 解析器 ~~> AST
解析器对扫描器的使用
为避免重复创建扫描器造成的开销,parser.ts
中创建了一个扫描器的单例。解析器根据需要使用 initializeState
函数准备该扫描器。
下面是解析器中的实际代码的简化版,你可以运行它演示以上概念
code/compiler/scanner/runScanner.ts
import * as ts from 'ntypescript';
// 单例扫描器
const scanner = ts.createScanner(ts.ScriptTarget.Latest, /* 忽略杂项 */ true);
// 此函数与初始化使用的 `initializeState` 函数相似
function initializeState(text: string) {
scanner.setText(text);
scanner.setOnError((message: ts.DiagnosticMessage, length: number) => {
console.error(message);
});
scanner.setScriptTarget(ts.ScriptTarget.ES5);
scanner.setLanguageVariant(ts.LanguageVariant.Standard);
}
// 使用示例
initializeState(
`
var foo = 123;
`.trim()
);
// 开始扫描
var token = scanner.scan();
while (token != ts.SyntaxKind.EndOfFileToken) {
console.log(ts.formatSyntaxKind(token));
token = scanner.scan();
}
该段代码输出以下内容:
VarKeyword
Identifier
FirstAssignment
FirstLiteralToken
SemicolonToken
扫描器状态
调用 scan
后,扫描器更新其局部状态(扫描位置,当前 token 详情等)。扫描器提供了一组工具函数获取当前扫描器状态。下例中,我们创建一个扫描器并用它识别 token 以及 token 在代码中的位置。
code/compiler/scanner/runScannerWithPosition.ts
// 使用示例
initializeState(
`
var foo = 123;
`.trim()
);
// 开始扫描
var token = scanner.scan();
while (token != ts.SyntaxKind.EndOfFileToken) {
let currentToken = ts.formatSyntaxKind(token);
let tokenStart = scanner.getStartPos();
token = scanner.scan();
let tokenEnd = scanner.getStartPos();
console.log(currentToken, tokenStart, tokenEnd);
}
该代码输出以下内容:
VarKeyword 0 3
Identifier 3 7
FirstAssignment 7 9
FirstLiteralToken 9 13
SemicolonToken 13 14
独立扫描器
即便 TypeScript 解析器有单例扫描器,你仍可以使用 createScanner
创建独立的扫描器,然后可以用 setText
/setTextPos
随意扫描文件的不同位置。
看完两件小事
如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:
- 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
- 关注公众号 「IT平头哥联盟」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程