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

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

このコミットは、Go言語のコンパイラ(gc)の文法定義ファイルである src/cmd/gc/go.y を変更し、特定のキーワード(type, func, var)を構造体のフィールド名として使用できるようにするための準備を行っています。これは、Go言語の初期段階における文法設計の柔軟性を高めるための変更の一部と考えられます。

コミット

commit ffafad1919cea67eadd743b56f4768fd120e9883
Author: Ken Thompson <ken@golang.org>
Date:   Sun Nov 23 15:58:48 2008 -0800

    setup for keywords in fieldnames

    R=r
    OCL=19883
    CL=19883

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

https://github.com/golang/go/commit/ffafad1919cea67eadd743b56f4768fd120e9883

元コミット内容

setup for keywords in fieldnames

このコミットメッセージは非常に簡潔ですが、その内容は「フィールド名にキーワードを使用するための設定」を意味しています。具体的には、Go言語の構造体(struct)のフィールド名として、通常は予約語であるキーワード(例: type, func, var)を使用できるようにするための文法変更を指します。

変更の背景

Go言語は、その設計初期段階において、シンプルさと実用性を追求していました。このコミットが行われた2008年11月は、Go言語がまだ一般に公開される前の開発初期段階にあたります。この時期には、言語の文法やセマンティクスに関する様々な試行錯誤が行われていました。

通常、プログラミング言語では予約語(キーワード)を識別子(変数名、関数名、フィールド名など)として使用することはできません。これは、パーサーがコードを正しく解釈し、予約語とユーザー定義の識別子を区別するために必要な制約です。しかし、Go言語の設計者たちは、特定の文脈(この場合は構造体のフィールド名)においては、これらの制約を緩和することで、より柔軟な命名を可能にすることを検討していた可能性があります。

この変更は、Go言語のパーサーが、構造体のフィールド名を解析する際に、特定のキーワードを通常の識別子と同様に扱えるようにするための基盤を構築するものです。これにより、例えば struct { type int } のような宣言が可能になることを目指しています。このような柔軟性は、特定のドメイン固有言語(DSL)の構築や、既存のデータ形式との互換性を保つ上で役立つ場合があります。

前提知識の解説

このコミットの変更内容を理解するためには、以下の知識が役立ちます。

  1. Go言語の基本:

    • 構造体 (Struct): 複数のフィールド(データ)をまとめた複合データ型。例: type Person struct { Name string; Age int }
    • キーワード (Keywords): Go言語において特別な意味を持つ予約語。例: func, var, type, ``if, for` など。これらは通常、変数名や関数名として使用できません。
  2. コンパイラの基本:

    • 字句解析 (Lexical Analysis): ソースコードをトークン(単語のようなもの)の列に分解するプロセス。例: func main() {func (キーワード), main (識別子), ( (記号) などに分解されます。
    • 構文解析 (Parsing): トークンの列が言語の文法規則に合致するかどうかを検証し、抽象構文木(AST)を構築するプロセス。
    • Yacc (Yet Another Compiler Compiler) / Bison: 構文解析器(パーサー)を自動生成するためのツール。文法規則を定義したファイル(通常 .y 拡張子)を読み込み、C言語などのソースコードを生成します。Go言語の初期コンパイラ gc は、Yaccによって生成されたパーサーを使用していました。
  3. Yacc/Bisonの文法定義:

    • ルール (Rules): 非終端記号: 終端記号 終端記号 ... { アクション } の形式で記述され、言語の構文を定義します。
    • 終端記号 (Terminals): 字句解析器によって生成されるトークン(例: IDENTIFIER, KEYWORD, LPAREN など)。Yaccファイルでは %token で宣言されます。
    • 非終端記号 (Non-terminals): 複数の終端記号や非終端記号から構成される文法要素(例: expression, statement, declaration など)。Yaccファイルでは %type で宣言され、そのルールが返す値の型を指定します。
    • アクション (Actions): ルールがマッチしたときに実行されるC言語のコードブロック。$$ は現在のルールの結果、$1, $2 などはルールの右辺の各要素の結果を参照します。
    • %type <node>: Yaccのディレクティブで、指定された非終端記号(例: name, new_field)が、パーサーの内部表現である node 型の値を返すことを示します。

技術的詳細

このコミットは、GoコンパイラのYacc文法ファイル src/cmd/gc/go.y を変更することで、構造体のフィールド名として特定のキーワードを許可するようにパーサーの挙動を調整しています。

変更の核心は、以下の点に集約されます。

  1. new_field 非終端記号の導入:

    • %type <node> new_field が追加され、new_field という新しい非終端記号が定義されました。これは、構造体のフィールド名を解析するための新しい文法要素です。
    • new_field: sym2 { $$ = newname($1); } というルールが追加されました。これは、new_fieldsym2 という非終端記号によって構成され、その結果として新しい名前ノード(newname($1))を生成することを示しています。
  2. sym2 非終端記号の拡張:

    • sym2 は、識別子やキーワードを扱うための非終端記号です。元々 sym2: sym1 でしたが、これに | LTYPE | LFUNC | LVAR が追加されました。
    • LTYPE, LFUNC, LVAR は、それぞれGo言語のキーワード type, func, var に対応する字句トークンです。
    • この変更により、sym2 は通常の識別子(sym1 経由)だけでなく、type, func, var といったキーワードも受け入れられるようになりました。
    • コメント /* keywords that can be field names */ が追加され、この sym2 の拡張がフィールド名にキーワードを許可するためのものであることが明示されています。
  3. structdcl ルールでの new_field の使用:

    • 構造体宣言を定義する structdcl ルールにおいて、フィールド名を解析する部分が new_name から new_field に変更されました。
    • 変更前: new_name ',' structdcl および new_name type oliteral
    • 変更後: new_field ',' structdcl および new_field type oliteral
    • これにより、構造体のフィールド名として new_field が解析できるようになったため、sym2 の拡張によって許可されたキーワードもフィールド名として有効になります。

これらの変更は、Go言語のパーサーが、構造体のフィールド名を解析する際に、より広い範囲の識別子(特定のキーワードを含む)を受け入れるようにするための、文法レベルでの調整です。

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

変更は src/cmd/gc/go.y ファイルに集中しています。

--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -56,7 +56,7 @@
 %type	<node>		simple_stmt osimple_stmt semi_stmt
 %type	<node>		expr uexpr pexpr expr_list oexpr oexpr_list expr_list_r
 %type	<node>		exprsym3_list_r exprsym3
-%type	<node>		name onew_name new_name new_name_list_r
+%type	<node>		name onew_name new_name new_name_list_r new_field
 %type	<node>		vardcl_list_r vardcl Avardcl Bvardcl
 %type	<node>		interfacedcl_list_r interfacedcl
 %type	<node>		structdcl_list_r structdcl embed
@@ -958,6 +958,12 @@ new_name:
 		$$ = newname($1);\n 	}\n \n+new_field:\n+\tsym2\n+\t{\n+\t\t$$ = newname($1);\n+\t}\n+\n new_type:\n 	sym1\n 	{\n@@ -980,8 +986,16 @@ sym1:\n 	sym\n |\tkeyword\n \n+/*\n+ * keywords that can be field names\n+ * pretty much any name can be allowed\n+ * limited only by good taste\n+ */\n sym2:\n 	sym1\n+|\tLTYPE\n+|\tLFUNC\n+|\tLVAR\n \n /*\n  * keywords that can be variables\n@@ -1402,12 +1416,12 @@ interfacedcl_list_r:\n 	}\n \n structdcl:\n-\tnew_name ',' structdcl\n+\tnew_field ',' structdcl\n 	{\n 		$$ = nod(ODCLFIELD, $1, N);\n 		$$ = nod(OLIST, $$, $3);\n 	}\n-|\tnew_name type oliteral\n+|\tnew_field type oliteral\n 	{\n 		$$ = nod(ODCLFIELD, $1, N);\n 		$$->type = $2;\n```

## コアとなるコードの解説

1.  **`%type <node> ... new_field` の追加**:
    *   Yaccの `%type` 宣言は、非終端記号が返す値の型を指定します。ここで `new_field` が追加されたことで、この新しい非終端記号がパーサーの内部ノード(`node`)を生成することが示されます。

2.  **`new_field` ルールの定義**:
    ```yacc
    new_field:
    	sym2
    	{
    		$$ = newname($1);
    	}
    ```
    *   このルールは、`new_field` が `sym2` という非終端記号にマッチすることを示します。
    *   アクション `$$ = newname($1);` は、`sym2` が解析した結果(`$1`)を使って、新しい名前を表すノードを作成し、それを `new_field` ルールの結果(`$$`)として設定します。

3.  **`sym2` ルールの拡張**:
    ```yacc
    sym2:
    	sym1
    |	LTYPE
    |	LFUNC
    |	LVAR
    ```
    *   `sym1` は、通常の識別子や一般的なキーワードを処理するルールです。
    *   `LTYPE`, `LFUNC`, `LVAR` は、それぞれGo言語の `type`, `func`, `var` キーワードに対応するトークンです。
    *   この拡張により、`sym2` は通常の識別子だけでなく、これらの特定のキーワードも有効なシンボルとして認識するようになります。これにより、これらのキーワードがフィールド名として使用される道が開かれます。

4.  **`structdcl` ルールの変更**:
    ```diff
    -|\tnew_name type oliteral
    +|\tnew_field type oliteral
    ```
    *   `structdcl` は構造体の宣言を処理するルールです。
    *   以前は `new_name` を使ってフィールド名を解析していましたが、この変更により `new_field` を使うようになりました。
    *   `new_field` は `sym2` を通じてキーワードを認識できるため、結果として `structdcl` もキーワードをフィールド名として受け入れるようになります。

これらの変更は、Go言語の文法が進化していく過程で、特定の文脈におけるキーワードの扱いをより柔軟にするための、初期段階の重要なステップを示しています。

## 関連リンク

*   Go言語の公式リポジトリ: [https://github.com/golang/go](https://github.com/golang/go)
*   Yacc (Wikipedia): [https://ja.wikipedia.org/wiki/Yacc](https://ja.wikipedia.org/wiki/Yacc)
*   Go言語の文法 (Go言語仕様): [https://go.dev/ref/spec](https://go.dev/ref/spec) (このコミット時点の仕様とは異なる可能性があります)

## 参考にした情報源リンク

*   Go言語のソースコード (特に `src/cmd/gc/go.y` の歴史): [https://github.com/golang/go/blob/master/src/cmd/compile/internal/syntax/syntax.go](https://github.com/golang/go/blob/master/src/cmd/compile/internal/syntax/syntax.go) (現在のGoコンパイラの構文解析はYaccからGoのコードに移行しています)
*   Go言語の初期のコミット履歴: [https://github.com/golang/go/commits?author=ken%40golang.org](https://github.com/golang/go/commits?author=ken%40golang.org)
*   Go言語の設計に関する議論 (Go Wikiなど): [https://go.dev/wiki/](https://go.dev/wiki/)
*   コンパイラ理論に関する一般的な情報源。