Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TypeScript_コンパイラの内側に片足を入れる

ryounasso
November 23, 2023

 TypeScript_コンパイラの内側に片足を入れる

kansai.ts #4 で発表した資料
https://kansaits.connpass.com/event/299545/

ryounasso

November 23, 2023
Tweet

More Decks by ryounasso

Other Decks in Programming

Transcript

  1. TypeScript コンパイラの
    内側に片足入れる
    ryounasso

    View full-size slide

  2. 自己紹介
    サイボウズ株式会社
    kintone 新機能開発チーム
    SNS アカウント : @ryounasso
    TypeScript を触る機会
    - 個人開発 (Next.js, React)
    - 業務 (React)
    人生初 LT で緊張してます...

    View full-size slide

  3. このスライドで目指すところ
    1. ざっくりと処理の流れがわかる
    2. それぞれの処理で、メインで動く関数とファイルがわかる
    TypeScript へのコントリビュートの足がかりに...!!
    > Good First Issue Issues · microsoft/TypeScript · GitHub
    25件ほどあった

    View full-size slide

  4. TypeScript はそのまま動かない
    TypeScript はブラウザや Node.js など JavaScript の実行環境で動作させるために
    JavaScript にコンパイルする必要がある
    TypeScript のコンパイラのソースは src/compiler フォルダ以下にある
    > https://github.com/Microsoft/TypeScript/tree/main/src/compiler
    主要なファイルは以下の通り
    - scanner.ts
    - parser.ts
    - binder.ts
    - checker.ts
    - emitter.ts

    View full-size slide

  5. 大まかな流れ
    字句解析 構文解析
    識別子の
    結合

    チェック
    トランス
    パイル
    ソース
    ファイル
    出力
    AST の作成

    View full-size slide

  6. - Abstract Syntax Tree (抽象構文木) のこと
    - コードをパースして、ソースコードを意味的に表す木構造のこと
    - 端的にいうとコードを表すオブジェクト
    AST とは 字句解析 構文解析
    識別子の
    結合

    チェック
    トランス
    パイル
    AST の作成

    View full-size slide

  7. 字句解析
    (scanner.ts)
    ● 字句解析を行う
    ● ソースコードをトークンに分割する。
    ● scan がメインの関数
    ● Scanner は createScanner で生成される。
    ● scan を実行すると、スキャン中の位置や現在のトークンの詳細などを更新す

    ● Parser によって内部的に制御される
    ● parser.ts でシングルトンの scanner が生成される (生成コスト削減)
    字句解析 構文解析
    識別子の
    結合

    チェック
    トランス
    パイル
    AST の作成

    View full-size slide

  8. 字句解析
    (scanner.ts)
    // Sample usage
    initializeState(`
    var foo = 123;
    `.trim());
    // Start the scanning
    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
    字句解析 構文解析
    識別子の
    結合

    チェック
    トランス
    パイル
    AST の作成

    View full-size slide

  9. 構文解析 (parser.ts)
    ● 構文解析を行う
    ● createSourceFile がメインの関数
    ● createParser で生成される
    シングルトンとして実装されている
    主にやっていることは、
    1. ソースコードをトークンに分割 (scanner)
    2. 文法規則に従って、AST を作成する
    字句解析 構文解析
    識別子の
    結合

    チェック
    トランス
    パイル
    AST の作成

    View full-size slide

  10. var sourceCode = `
    var foo = 123;
    `.trim();
    var sourceFile = ts.createSourceFile('foo.ts', sourceCode, ts.ScriptTarget.ES5, true);
    -----
    /** createSourceFileの中で **/
    result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined,
    setParentNodes, ScriptKind.JSON, noop, jsDocParsingMode);
    -----
    printAllChildren(sourceFile);
    SourceFile 0 14
    ---- SyntaxList 0 14
    -------- VariableStatement 0 14
    ------------ VariableDeclarationList 0 13
    ---------------- VarKeyword 0 3
    ---------------- SyntaxList 3 13
    -------------------- VariableDeclaration 3 13
    ------------------------ Identifier 3 7
    ------------------------ FirstAssignment 7 9
    ------------------------ FirstLiteralToken 9 13
    ------------ SemicolonToken 13 14
    ---- EndOfFileToken 14 14
    構文解析 (parser.ts) 字句解析 構文解析
    識別子の
    結合

    チェック
    トランス
    パイル
    AST の作成

    View full-size slide

  11. 識別子の結合 (bind.ts)
    ● AST を読み取り、変数・関数などの宣言や定義を発見し、
    代入先となる識別子と代入される値をバインドする
    ● bindSourceFile がメインの関数
    ● createBinder で生成される
    主にやっていることは
    1. AST の巡回
    2. シンボルの作成
    ソースコードの識別子 (変数や関数名など)に関する情報を持つオブジェクト
    3. スコープの解決
    4. 型情報の収集
    字句解析 構文解析
    識別子の
    結合

    チェック
    トランス
    パイル
    AST の作成

    View full-size slide

  12. 識別子の結合 (bind.ts)
    const msg: string = "Hello, world";
    welcome(msg);
    function welcome(str: string) {
    console.log(str)
    }
    Global Scope
    msg declared line 0
    flags: BlockSopedVariable
    welcome declared line 3
    flags: Function
    welcome Function Scope
    parent Global Scope
    str declared line 3
    flags: BlockScopedVariable
    字句解析 構文解析
    識別子の
    結合

    チェック
    トランス
    パイル
    AST の作成

    View full-size slide

  13. 型チェック (checker.ts)
    tsc の中枢
    コードの量がすごい...! (Github 上では多すぎて表示されなかった...)
    バインディングされた情報を元に、変数の型推論、型の整合性のチェック、
    型のアサーションの確認などを行う。
    checkSourceFile がメインの関数
    字句解析 構文解析
    識別子の
    結合

    チェック
    トランス
    パイル
    AST の作成

    View full-size slide

  14. 型チェック (checker.ts)
    const msg: string = "Hello, world";
    (簡略化した AST)
    SourceFile
    └─ VariableStatement
    ├─ constKeyword
    └─ DeclarationList
    ├─ msg
    ├─ ColonToken
    ├─ StringKeyword
    ├─ EqualsToken
    └─ StringLiteral
    └─ "Hello World"
    字句解析 構文解析
    識別子の
    結合

    チェック
    トランス
    パイル
    AST の作成

    View full-size slide

  15. 型チェック (checker.ts)
    const msg: string = "Hello, world";
    (簡略化した AST)
    SourceFile
    └─ VariableStatement
    ├─ constKeyword
    └─ DeclarationList
    ├─ msg
    ├─ ColonToken
    ├─ StringKeyword
    ├─ EqualsToken
    └─ StringLiteral
    └─ "Hello World"
    字句解析 構文解析
    識別子の
    結合

    チェック
    トランス
    パイル
    AST の作成

    View full-size slide

  16. トランスパイル
    (emitter.ts)
    最後に、TypeScript コードを JavaScript コードにトランスパイルする
    emitFiles がメインの関数
    JavaScript はバージョンによって使える文法が大きく変わる。
    トランスパイルのために、transformer を使用
    src/compiler/transformers フォルダ以下に、
    それぞれES版に対するトランスフォーマー等が存在している
    流れは以下の通り
    1. getTransformers で適用する transformer の配列を取得
    2. emitFiles で JavaScript コードにトランスパイル
    字句解析 構文解析
    識別子の
    結合

    チェック
    トランス
    パイル
    AST の作成

    View full-size slide

  17. トランスパイル
    (emitter.ts)
    JavaScript では型情報が不要になる (transformers)
    const msg: string = "Hello, world";
    SourceFile
    └─ VariableStatement
    ├─ constKeyword
    └─ DeclarationList
    ├─ msg
    ├─ ColonToken
    ├─ StringKeyword
    ├─ EqualsToken
    └─ StringLiteral
    └─ "Hello World"
    const msg: string = "Hello, world";
    SourceFile
    └─ VariableStatement
    ├─ constKeyword
    └─ DeclarationList
    ├─ msg
    ├─ EqualsToken
    └─ StringLiteral
    └─ "Hello World"
    字句解析 構文解析
    識別子の
    結合

    チェック
    トランス
    パイル
    AST の作成

    View full-size slide

  18. まとめ
    字句解析
    scanner.ts
    scan
    構文解析
    parser.ts
    createSourceFile
    識別子の結合
    bind.ts
    bindSourceFile
    ソース
    ファイル
    出力
    型チェック
    checker.ts
    checkSourceFile
    トランスパイル
    emitter.ts
    emitFiles

    View full-size slide

  19. 参考記事
    - TypeScriptコンパイラの内側 - TypeScript Deep Dive 日本語版 (gitbook.io)
    - https://www.youtube.com/watch?v=X8k_4tZ16qU&list=PLYUbsZda9oHu-
    EiIdekbAzNO0-pUM5Iqj&index=5
    - https://qiita.com/eloy/items/9d0d8227121a1dcbdf71
    - https://github.com/microsoft/TypeScript

    View full-size slide