内容简介:首先,其中分为以下几个关键部分,每个部分在源文件中均有独立文件,稍后会解释这些部分在编译过程中所起到的左右。
首先, ts
的 github
地址: github.com/Microsoft/T… 。各位可先行下载。其编译部分位于 src/compiler
目录下。
其中分为以下几个关键部分,
- Scanner 扫描器 (
scanner.ts
) - Parser 解析器 (
parser.ts
) - Binder 绑定器 (
binder.ts
) - Checker 检查器 (
checker.ts
) - Emitter 发射器 (
emitter.ts
)
每个部分在源文件中均有独立文件,稍后会解释这些部分在编译过程中所起到的左右。
概览
上图简单说明 TypeScript 编译器如何将上述几个关键部分组合在一起:
- 源码 ~ scanner(扫描器) ~ token数据流 ~ parser(解析器) -> AST(抽象语法树)
- AST(抽象语法树) ~ binder(绑定器) -> symbols(符号)
- AST(抽象语法树) + symbols ~ checker(检查器) -> 类型检查功能
- AST(抽象语法树) + checker(检查器) ~ emitter(发射器) -> js代码
流程
源码 ~ scanner(扫描器) ~ token数据流 ~ parser(解析器) -> AST(抽象语法树)
typescript的扫描器位于 scanner.ts
,解析器位于 parser.ts
,在内部,由 parser解析器
控制 scanner扫描器
将 源码
转化为 抽象语法树(AST)
。流程如下:
若以常见的AST生成过程类比,可简单类比上述的 扫描器阶段
可对应为 词法分析过程
, 解析器阶段
可对应为 语法分析过程
。
有关 AST抽象语法树
可参考AST抽象语法树。
Parser 解析器对 Scanner 扫描器的使用
通过 parseSourceFile
设置初始状态并将工作交给 parseSourceFileWorker
函数。
parseSourceFile
export function parseSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, syntaxCursor: IncrementalParser.SyntaxCursor | undefined, setParentNodes = false, scriptKind?: ScriptKind): SourceFile { scriptKind = ensureScriptKind(fileName, scriptKind); //初始化状态 if (scriptKind === ScriptKind.JSON) { const result = parseJsonText(fileName, sourceText, languageVersion, syntaxCursor, setParentNodes); convertToObjectWorker(result, result.parseDiagnostics, /*returnValue*/ false, /*knownRootOptions*/ undefined, /*jsonConversionNotifier*/ undefined); result.referencedFiles = emptyArray; result.typeReferenceDirectives = emptyArray; result.libReferenceDirectives = emptyArray; result.amdDependencies = emptyArray; result.hasNoDefaultLib = false; result.pragmas = emptyMap; return result; } //专备好扫描器状态 initializeState(sourceText, languageVersion, syntaxCursor, scriptKind); //将工作交给 parseSourceFileWorker const result = parseSourceFileWorker(fileName, languageVersion, setParentNodes, scriptKind); clearState(); return result; } 复制代码
parseSourceFileWorker
该函数先创建一个 SourceFile AST 节点
,然后从 parseStatement
函数开始解析源代码。一旦返回结果,就用额外信息(例如 nodeCount
, identifierCount
等) 完善 SourceFile
节点。
function parseSourceFileWorker(fileName: string, languageVersion: ScriptTarget, setParentNodes: boolean, scriptKind: ScriptKind): SourceFile { const isDeclarationFile = isDeclarationFileName(fileName); if (isDeclarationFile) { contextFlags |= NodeFlags.Ambient; } // 先创造一个 SourceFile AST 节点 sourceFile = createSourceFile(fileName, languageVersion, scriptKind, isDeclarationFile); sourceFile.flags = contextFlags; // Prime the scanner. nextToken(); // A member of ReadonlyArray<T> isn't assignable to a member of T[] (and prevents a direct cast) - but this is where we set up those members so they can be readonly in the future processCommentPragmas(sourceFile as {} as PragmaContext, sourceText); processPragmasIntoFields(sourceFile as {} as PragmaContext, reportPragmaDiagnostic); // 调用 parseStatement 函数解析源码 sourceFile.statements = parseList(ParsingContext.SourceElements, parseStatement); Debug.assert(token() === SyntaxKind.EndOfFileToken); // 至871行 均为完善 sourcefile AST 节点 sourceFile.endOfFileToken = addJSDocComment(parseTokenNode()); setExternalModuleIndicator(sourceFile); sourceFile.nodeCount = nodeCount; sourceFile.identifierCount = identifierCount; sourceFile.identifiers = identifiers; sourceFile.parseDiagnostics = parseDiagnostics; if (setParentNodes) { fixupParentReferences(sourceFile); } return sourceFile; function reportPragmaDiagnostic(pos: number, end: number, diagnostic: DiagnosticMessage) { parseDiagnostics.push(createFileDiagnostic(sourceFile, pos, end, diagnostic)); } } 复制代码
节点创建:parseStatement/parseEmptyStatement/parseExpected等
其中 parseStatement
函数,它根据扫描器返回的当前 token
来切换(调用相应的 parseXXX
函数),生成AST节点。例如:如果当前 token
是一个 SemicolonToken(分号标记)
,就会调用 paserEmptyStatement
为空语句创建一个 AST 节点。
function parseStatement(): Statement { // 此处 token 为 scanner扫描器 返回的 当前token流, SyntaxKind为AST的常量枚举类型,根据不同的类型创建不同的节点 switch (token()) { // 类型为 SemicolonToken,调用parseEmptyStatement case SyntaxKind.SemicolonToken: return parseEmptyStatement(); case SyntaxKind.OpenBraceToken: return parseBlock(/*ignoreMissingOpenBrace*/ false); case SyntaxKind.VarKeyword: return parseVariableStatement(<VariableStatement>createNodeWithJSDoc(SyntaxKind.VariableDeclaration)); case SyntaxKind.LetKeyword: if (isLetDeclaration()) { return parseVariableStatement(<VariableStatement>createNodeWithJSDoc(SyntaxKind.VariableDeclaration)); } break; case SyntaxKind.FunctionKeyword: return parseFunctionDeclaration(<FunctionDeclaration>createNodeWithJSDoc(SyntaxKind.FunctionDeclaration)); case SyntaxKind.ClassKeyword: return parseClassDeclaration(<ClassDeclaration>createNodeWithJSDoc(SyntaxKind.ClassDeclaration)); case SyntaxKind.IfKeyword: return parseIfStatement(); case SyntaxKind.DoKeyword: return parseDoStatement(); case SyntaxKind.WhileKeyword: return parseWhileStatement(); case SyntaxKind.ForKeyword: return parseForOrForInOrForOfStatement(); case SyntaxKind.ContinueKeyword: return parseBreakOrContinueStatement(SyntaxKind.ContinueStatement); case SyntaxKind.BreakKeyword: return parseBreakOrContinueStatement(SyntaxKind.BreakStatement); case SyntaxKind.ReturnKeyword: return parseReturnStatement(); case SyntaxKind.WithKeyword: return parseWithStatement(); case SyntaxKind.SwitchKeyword: return parseSwitchStatement(); case SyntaxKind.ThrowKeyword: return parseThrowStatement(); case SyntaxKind.TryKeyword: // Include 'catch' and 'finally' for error recovery. case SyntaxKind.CatchKeyword: case SyntaxKind.FinallyKeyword: return parseTryStatement(); case SyntaxKind.DebuggerKeyword: return parseDebuggerStatement(); case SyntaxKind.AtToken: return parseDeclaration(); case SyntaxKind.AsyncKeyword: case SyntaxKind.InterfaceKeyword: case SyntaxKind.TypeKeyword: case SyntaxKind.ModuleKeyword: case SyntaxKind.NamespaceKeyword: case SyntaxKind.DeclareKeyword: case SyntaxKind.ConstKeyword: case SyntaxKind.EnumKeyword: case SyntaxKind.ExportKeyword: case SyntaxKind.ImportKeyword: case SyntaxKind.PrivateKeyword: case SyntaxKind.ProtectedKeyword: case SyntaxKind.PublicKeyword: case SyntaxKind.AbstractKeyword: case SyntaxKind.StaticKeyword: case SyntaxKind.ReadonlyKeyword: case SyntaxKind.GlobalKeyword: if (isStartOfDeclaration()) { return parseDeclaration(); } break; } return parseExpressionOrLabeledStatement(); } 复制代码
以上所述就是小编给大家介绍的《Typescript编译原理(一)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 小前端学编译原理
- 深入分析 Javac 编译原理
- 走进 Golang 之编译器原理
- C++ 服务编译耗时优化原理及实践
- 深入理解Flutter的编译原理与优化
- 深入理解 Flutter 的编译原理与优化
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。