Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 1621] ファイルの概要

このコミットは、Go言語の初期開発段階におけるusr/gri/pretty/parser.goファイルの変更を記録しています。主な目的は、パーサー関数の可視性(エクスポート制御)の調整、トレースコードの改善、および不要なコードの削除とクリーンアップです。

コミット

commit 89fc8465a8721da7f8855b0e5606462eeaf4e7c5
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Feb 5 14:22:09 2009 -0800

    - caseify parser functions (all but a few should not be exported)
    - more elegant tracing code
    - removed some dead code, cleanups
    
    R=r
    OCL=24452
    CL=24452

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/89fc8465a8721da7f8855b0e5606462eeaf4e7c5

元コミット内容

  • パーサー関数の「caseify」(一部を除きエクスポートしないようにする)
  • よりエレガントなトレースコード
  • 不要なコードの削除、クリーンアップ

変更の背景

このコミットは、Go言語の初期段階、特にGo 1.0リリースよりもはるか以前に行われたものです。当時のGo言語はまだ設計と実装が活発に進められており、言語仕様や標準ライブラリのAPIが頻繁に変更されていました。

コミットメッセージにある「caseify parser functions (all but a few should not be exported)」は、Go言語における識別子の可視性ルール(エクスポート制御)の適用を示唆しています。Goでは、識別子(関数名、変数名など)の最初の文字が大文字である場合、その識別子はパッケージ外にエクスポートされ、小文字である場合はパッケージ内部でのみ利用可能です。この変更は、パーサーの内部関数が外部に公開されるべきではないという設計判断に基づいています。これにより、APIの表面積を減らし、内部実装の詳細を隠蔽することで、将来的な変更に対する堅牢性を高め、利用者が誤って内部関数に依存することを防ぎます。

「more elegant tracing code」は、デバッグやパフォーマンス分析のためにコードに埋め込まれたトレース機能の改善を指します。初期のシステム開発では、動作の理解や問題の特定のために詳細なログやトレースが不可欠であり、そのコードがより効率的で読みやすく、あるいはオーバーヘッドが少ない形に改善されたことを示しています。

「removed some dead code, cleanups」は、開発過程で生じた未使用のコードや冗長な部分を削除し、全体的なコードベースの品質を向上させるための一般的なメンテナンス作業です。これは、コードの肥大化を防ぎ、可読性を高め、将来の変更を容易にする上で重要です。

前提知識の解説

Go言語の識別子の可視性(エクスポート制御)

Go言語では、パッケージ内の識別子(変数、関数、型、メソッドなど)の可視性は、その識別子の名前の最初の文字が大文字か小文字かによって決定されます。

  • 大文字で始まる識別子: パッケージ外にエクスポートされ、他のパッケージから参照・利用可能です。これは「公開(public)」に相当します。
  • 小文字で始まる識別子: パッケージ内部でのみ利用可能で、他のパッケージからは参照できません。これは「非公開(private)」に相当します。

このルールは非常にシンプルでありながら強力で、Go言語のモジュール性、カプセル化、およびAPI設計の基盤となっています。このコミットにおける「caseify parser functions」とは、パーサー内の関数名を小文字で始めるように変更し、それらをパッケージ内部に限定することで、外部からの不必要な依存を防ぐことを意味します。

パーサー(Parser)

プログラミング言語のコンパイラやインタプリタの構成要素の一つで、字句解析器(Lexer/Scanner)によって生成されたトークン列を入力として受け取り、その言語の文法規則に従って構文木(Abstract Syntax Tree: AST)を構築する役割を担います。

  • 字句解析(Lexical Analysis): ソースコードを最小単位の「トークン」(キーワード、識別子、演算子など)に分割するプロセス。
  • 構文解析(Syntax Analysis): トークン列が言語の文法規則に適合しているかを検証し、プログラムの構造を階層的に表現する構文木を構築するプロセス。

このコミットがparser.goファイルを変更していることから、Go言語自身のパーサーの実装に関する変更であることがわかります。

トレースコード

プログラムの実行中に特定のイベントや状態に関する情報を記録するためのコードです。デバッグ、パフォーマンス分析、およびプログラムの動作理解に役立ちます。トレース情報は、ログファイルに出力されたり、デバッガに表示されたりすることがあります。初期開発段階では、プログラムの複雑な挙動を追跡するために、詳細なトレースが頻繁に利用されます。

技術的詳細

このコミットの技術的詳細は、usr/gri/pretty/parser.goファイルに対する変更に集約されています。

  1. パーサー関数の「caseify」:

    • 多くの公開されていた(大文字で始まっていた)パーサー関連の関数名が、小文字で始まるように変更されています。例えば、PrintIndent()printIndent()に、Trace()trace()に、Next()next()に、ParseExpression()parseExpression()になるなど、多数の関数が変更されています。
    • これは、Go言語のエクスポートルールに従い、これらの関数がパーサーの内部実装の詳細であり、外部から直接呼び出されるべきではないという設計思想を反映しています。これにより、パーサーの内部構造が外部に漏洩するのを防ぎ、将来的なリファクタリングや変更が容易になります。
  2. トレースコードの改善:

    • P.Trace()P.Ecart()というトレース関連の関数が、trace()un/*trace*/()という新しい形式に置き換えられています。
    • 特に注目すべきは、trace関数が*Parserを返し、defer un(trace(P, "...")というパターンで利用されている点です。これはGoのdeferステートメントと関数の戻り値を組み合わせることで、関数の開始時と終了時に自動的にトレースメッセージを出力する、より簡潔でエラーに強いイディオムを導入しています。
    • printIndent()関数も改善されており、インデントの表示方法が最適化されています(for ; i > 10; i -= 10のループで高速化を図っている)。これは、トレース出力が大量になる場合にパフォーマンスを考慮した変更と考えられます。
    • P.verboseフィールドがP.traceにリネームされ、トレースの有効/無効を制御するフラグの意図がより明確になっています。
  3. 不要なコードの削除とクリーンアップ:

    • P.VerifyIndent()関数が削除されています。これは、トレースコードの変更により、インデントの整合性を手動で検証する必要がなくなったためと考えられます。
    • P.Next0()関数内のトレース出力ロジックが簡素化され、Scanner.TokenStringの出力整形に関する冗長なswitch文が削除されています。
    • 全体的に、コードの行数が減少し(553行削除、491行追加)、よりコンパクトで効率的な実装になっています。

これらの変更は、Go言語のパーサーがより成熟し、内部構造が整理され、デバッグメカニズムが洗練されていく過程を示しています。

コアとなるコードの変更箇所

usr/gri/pretty/parser.goファイル全体にわたる変更ですが、特に以下のパターンが顕著です。

  • 関数名の変更: 多くの関数がCamelCaseからcamelCase(Goの慣習では非エクスポート)に変更されています。
    • 例: func (P *Parser) PrintIndent() -> func (P *Parser) printIndent()
    • 例: func (P *Parser) Trace(msg string) -> func trace(P *Parser, msg string) *Parser
    • 例: func (P *Parser) ParseExpression(prec int) -> func (P *Parser) parseExpression(prec int)
  • トレースメカニズムの変更: P.Trace()P.Ecart()の呼び出しが、defer un(trace(P, "..."))の形式に置き換えられています。
    • 変更前:
      if P.verbose {
          P.Trace("Ident");
          defer P.Ecart();
      }
      
    • 変更後:
      if P.trace {
          defer un(trace(P, "Ident"));
      }
      
  • フィールド名の変更: P.verbose -> P.trace

コアとなるコードの解説

このコミットの最も重要な変更は、Go言語のパーサーにおける関数名の命名規則の変更と、それに伴うトレースメカニズムの刷新です。

関数名の「caseify」

Go言語では、識別子の最初の文字が大文字か小文字かでその可視性が決まります。このコミットでは、parser.go内の多くの関数が、外部パッケージからアクセスされるべきではない内部ヘルパー関数であると判断され、その名前が小文字で始まるように変更されました。

例えば、ParseExpressionのような関数は、パーサーの外部APIとして公開されるべきではなく、パーサー内部でのみ利用されるべきです。この変更により、Goの言語設計原則である「最小限の公開API」がパーサーの実装にも適用され、コードベースの健全性が向上します。

新しいトレースメカニズム

変更後のトレースコードは、Goのdeferステートメントを非常に効果的に利用しています。

func trace(P *Parser, msg string) *Parser {
    P.printIndent()
    fmt.Printf("%s (\\n", msg)
    P.indent++
    return P
}

func un/*trace*/(P *Parser) {
    P.indent--
    P.printIndent()
    fmt.Printf(")\\n")
}

そして、各パーサー関数の冒頭で以下のように呼び出されます。

func (P *Parser) parseIdent(scope *AST.Scope) *AST.Ident {
    if P.trace {
        defer un(trace(P, "Ident"))
    }
    // ... 関数の本体 ...
}

このパターンは以下のように機能します。

  1. trace(P, "Ident")が呼び出され、関数の開始を示すメッセージとインデントが出力されます。trace関数は*Parser型の値を返します。
  2. deferキーワードにより、un関数はparseIdent関数が終了する直前(returnステートメントが実行された後、またはパニックが発生した後)に実行されるようにスケジュールされます。
  3. un関数が実行される際、trace関数が返した*Parserインスタンスが引数として渡されます。これにより、un関数は適切なインデントレベルで関数の終了を示すメッセージを出力できます。

このdeferを使ったトレースは、従来の「関数の開始時にTrace()を呼び出し、終了時にEcart()を呼び出す」という手動のペアリングよりもはるかに堅牢です。なぜなら、deferは関数の終了時に必ず実行されるため、早期リターンやエラーパスがあってもトレースの終了処理が漏れることがないからです。これは、複雑なパーサーのデバッグにおいて非常に有効なイディオムです。

また、printIndent()関数が改善され、インデントレベルが10を超える場合に. . . . . . . . . . という文字列をまとめて出力することで、fmt.Printf(". ")を10回呼び出すよりも効率的にインデントを生成しています。これは、深いネストを持つ構文解析において、トレース出力のパフォーマンスを向上させるための細かな最適化です。

これらの変更は、Go言語の初期開発において、コードの品質、保守性、およびデバッグの容易性を高めるための継続的な努力の一環として行われたものです。

関連リンク

参考にした情報源リンク

  • Go言語の識別子の可視性に関する公式ドキュメントやチュートリアル
  • Go言語のdeferステートメントに関する公式ドキュメントや解説記事
  • コンパイラの設計と実装に関する一般的な情報源(字句解析、構文解析、ASTなど)
  • Go言語の初期のコミット履歴や設計に関する議論(Goのメーリングリストやデザインドキュメントなど)

[インデックス 1621] ファイルの概要

このコミットは、Go言語の初期開発段階におけるusr/gri/pretty/parser.goファイルの変更を記録しています。主な目的は、パーサー関数の可視性(エクスポート制御)の調整、トレースコードの改善、および不要なコードの削除とクリーンアップです。

コミット

commit 89fc8465a8721da7f8855b0e5606462eeaf4e7c5
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Feb 5 14:22:09 2009 -0800

    - caseify parser functions (all but a few should not be exported)
    - more elegant tracing code
    - removed some dead code, cleanups
    
    R=r
    OCL=24452
    CL=24452

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/89fc8465a8721da7f8855b0e5606462eeaf4e7c5

元コミット内容

  • パーサー関数の「caseify」(一部を除きエクスポートしないようにする)
  • よりエレガントなトレースコード
  • 不要なコードの削除、クリーンアップ

変更の背景

このコミットは、Go言語の初期段階、特にGo 1.0リリースよりもはるか以前に行われたものです。当時のGo言語はまだ設計と実装が活発に進められており、言語仕様や標準ライブラリのAPIが頻繁に変更されていました。

コミットメッセージにある「caseify parser functions (all but a few should not be exported)」は、Go言語における識別子の可視性ルール(エクスポート制御)の適用を示唆しています。Goでは、識別子(関数名、変数名など)の最初の文字が大文字である場合、その識別子はパッケージ外にエクスポートされ、小文字である場合はパッケージ内部でのみ利用可能です。この変更は、パーサーの内部関数が外部に公開されるべきではないという設計判断に基づいています。これにより、APIの表面積を減らし、内部実装の詳細を隠蔽することで、将来的な変更に対する堅牢性を高め、利用者が誤って内部関数に依存することを防ぎます。

「more elegant tracing code」は、デバッグやパフォーマンス分析のためにコードに埋め込まれたトレース機能の改善を指します。初期のシステム開発では、動作の理解や問題の特定のために詳細なログやトレースが不可欠であり、そのコードがより効率的で読みやすく、あるいはオーバーヘッドが少ない形に改善されたことを示しています。

「removed some dead code, cleanups」は、開発過程で生じた未使用のコードや冗長な部分を削除し、全体的なコードベースの品質を向上させるための一般的なメンテナンス作業です。これは、コードの肥大化を防ぎ、可読性を高め、将来の変更を容易にする上で重要です。

前提知識の解説

Go言語の識別子の可視性(エクスポート制御)

Go言語では、パッケージ内の識別子(変数、関数、型、メソッドなど)の可視性は、その識別子の名前の最初の文字が大文字か小文字かによって決定されます。

  • 大文字で始まる識別子: パッケージ外にエクスポートされ、他のパッケージから参照・利用可能です。これは「公開(public)」に相当します。
  • 小文字で始まる識別子: パッケージ内部でのみ利用可能で、他のパッケージからは参照できません。これは「非公開(private)」に相当します。

このルールは非常にシンプルでありながら強力で、Go言語のモジュール性、カプセル化、およびAPI設計の基盤となっています。このコミットにおける「caseify parser functions」とは、パーサー内の関数名を小文字で始めるように変更し、それらをパッケージ内部に限定することで、外部からの不必要な依存を防ぐことを意味します。

パーサー(Parser)

プログラミング言語のコンパイラやインタプリタの構成要素の一つで、字句解析器(Lexer/Scanner)によって生成されたトークン列を入力として受け取り、その言語の文法規則に従って構文木(Abstract Syntax Tree: AST)を構築する役割を担います。

  • 字句解析(Lexical Analysis): ソースコードを最小単位の「トークン」(キーワード、識別子、演算子など)に分割するプロセス。
  • 構文解析(Syntax Analysis): トークン列が言語の文法規則に適合しているかを検証し、プログラムの構造を階層的に表現する構文木を構築するプロセス。

このコミットがparser.goファイルを変更していることから、Go言語自身のパーサーの実装に関する変更であることがわかります。

トレースコード

プログラムの実行中に特定のイベントや状態に関する情報を記録するためのコードです。デバッグ、パフォーマンス分析、およびプログラムの動作理解に役立ちます。トレース情報は、ログファイルに出力されたり、デバッガに表示されたりすることがあります。初期開発段階では、プログラムの複雑な挙動を追跡するために、詳細なトレースが頻繁に利用されます。

技術的詳細

このコミットの技術的詳細は、usr/gri/pretty/parser.goファイルに対する変更に集約されています。

  1. パーサー関数の「caseify」:

    • 多くの公開されていた(大文字で始まっていた)パーサー関連の関数名が、小文字で始まるように変更されています。例えば、PrintIndent()printIndent()に、Trace()trace()に、Next()next()に、ParseExpression()parseExpression()になるなど、多数の関数が変更されています。
    • これは、Go言語のエクスポートルールに従い、これらの関数がパーサーの内部実装の詳細であり、外部から直接呼び出されるべきではないという設計思想を反映しています。これにより、パーサーの内部構造が外部に漏洩するのを防ぎ、将来的なリファクタリングや変更が容易になります。
  2. トレースコードの改善:

    • P.Trace()P.Ecart()というトレース関連の関数が、trace()un/*trace*/()という新しい形式に置き換えられています。
    • 特に注目すべきは、trace関数が*Parserを返し、defer un(trace(P, "...")というパターンで利用されている点です。これはGoのdeferステートメントと関数の戻り値を組み合わせることで、関数の開始時と終了時に自動的にトレースメッセージを出力する、より簡潔でエラーに強いイディオムを導入しています。
    • printIndent()関数も改善されており、インデントの表示方法が最適化されています(for ; i > 10; i -= 10のループで高速化を図っている)。これは、トレース出力が大量になる場合にパフォーマンスを考慮した変更と考えられます。
    • P.verboseフィールドがP.traceにリネームされ、トレースの有効/無効を制御するフラグの意図がより明確になっています。
  3. 不要なコードの削除とクリーンアップ:

    • P.VerifyIndent()関数が削除されています。これは、トレースコードの変更により、インデントの整合性を手動で検証する必要がなくなったためと考えられます。
    • P.Next0()関数内のトレース出力ロジックが簡素化され、Scanner.TokenStringの出力整形に関する冗長なswitch文が削除されています。
    • 全体的に、コードの行数が減少し(553行削除、491行追加)、よりコンパクトで効率的な実装になっています。

これらの変更は、Go言語のパーサーがより成熟し、内部構造が整理され、デバッグメカニズムが洗練されていく過程を示しています。

コアとなるコードの変更箇所

usr/gri/pretty/parser.goファイル全体にわたる変更ですが、特に以下のパターンが顕著です。

  • 関数名の変更: 多くの関数がCamelCaseからcamelCase(Goの慣習では非エクスポート)に変更されています。
    • 例: func (P *Parser) PrintIndent() -> func (P *Parser) printIndent()
    • 例: func (P *Parser) Trace(msg string) -> func trace(P *Parser, msg string) *Parser
    • 例: func (P *Parser) ParseExpression(prec int) -> func (P *Parser) parseExpression(prec int)
  • トレースメカニズムの変更: P.Trace()P.Ecart()の呼び出しが、defer un(trace(P, "..."))の形式に置き換えられています。
    • 変更前:
      if P.verbose {
          P.Trace("Ident");
          defer P.Ecart();
      }
      
    • 変更後:
      if P.trace {
          defer un(trace(P, "Ident"));
      }
      
  • フィールド名の変更: P.verbose -> P.trace

コアとなるコードの解説

このコミットの最も重要な変更は、Go言語のパーサーにおける関数名の命名規則の変更と、それに伴うトレースメカニズムの刷新です。

関数名の「caseify」

Go言語では、識別子の最初の文字が大文字か小文字かでその可視性が決まります。このコミットでは、parser.go内の多くの関数が、外部パッケージからアクセスされるべきではない内部ヘルパー関数であると判断され、その名前が小文字で始まるように変更されました。

例えば、ParseExpressionのような関数は、パーサーの外部APIとして公開されるべきではなく、パーサー内部でのみ利用されるべきです。この変更により、Goの言語設計原則である「最小限の公開API」がパーサーの実装にも適用され、コードベースの健全性が向上します。

新しいトレースメカニズム

変更後のトレースコードは、Goのdeferステートメントを非常に効果的に利用しています。

func trace(P *Parser, msg string) *Parser {
    P.printIndent()
    fmt.Printf("%s (\\n", msg)
    P.indent++
    return P
}

func un/*trace*/(P *Parser) {
    P.indent--
    P.printIndent()
    fmt.Printf(")\\n")
}

そして、各パーサー関数の冒頭で以下のように呼び出されます。

func (P *Parser) parseIdent(scope *AST.Scope) *AST.Ident {
    if P.trace {
        defer un(trace(P, "Ident"))
    }
    // ... 関数の本体 ...
}

このパターンは以下のように機能します。

  1. trace(P, "Ident")が呼び出され、関数の開始を示すメッセージとインデントが出力されます。trace関数は*Parser型の値を返します。
  2. deferキーワードにより、un関数はparseIdent関数が終了する直前(returnステートメントが実行された後、またはパニックが発生した後)に実行されるようにスケジュールされます。
  3. un関数が実行される際、trace関数が返した*Parserインスタンスが引数として渡されます。これにより、un関数は適切なインデントレベルで関数の終了を示すメッセージを出力できます。

このdeferを使ったトレースは、従来の「関数の開始時にTrace()を呼び出し、終了時にEcart()を呼び出す」という手動のペアリングよりもはるかに堅牢です。なぜなら、deferは関数の終了時に必ず実行されるため、早期リターンやエラーパスがあってもトレースの終了処理が漏れることがないからです。これは、複雑なパーサーのデバッグにおいて非常に有効なイディオムです。

また、printIndent()関数が改善され、インデントレベルが10を超える場合に. . . . . . . . . . という文字列をまとめて出力することで、fmt.Printf(". ")を10回呼び出すよりも効率的にインデントを生成しています。これは、深いネストを持つ構文解析において、トレース出力のパフォーマンスを向上させるための細かな最適化です。

これらの変更は、Go言語の初期開発において、コードの品質、保守性、およびデバッグの容易性を高めるための継続的な努力の一環として行われたものです。

関連リンク

参考にした情報源リンク

  • Go言語の識別子の可視性に関する公式ドキュメントやチュートリアル
  • Go言語のdeferステートメントに関する公式ドキュメントや解説記事
  • コンパイラの設計と実装に関する一般的な情報源(字句解析、構文解析、ASTなど)
  • Go言語の初期のコミット履歴や設計に関する議論(Goのメーリングリストやデザインドキュメントなど)