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

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

このコミットは、Go言語の初期コンパイラ(gc)において、可変長引数(variadic arguments)を表す...(ドット3つ)構文の内部的な処理を導入するものです。具体的には、パーサーが...を認識し、コンパイラの型システムでそれを表現するための変更が含まれています。

コミット

  • コミットハッシュ: 2fef4c7198e7d3215cbc14e530e1e08cbed3888d
  • 作者: Ken Thompson ken@golang.org
  • 日付: 2008年11月1日 土曜日 16:52:12 -0700

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

https://github.com/golang/go/commit/2fef4c7198e7d3215cbc14e530e1e08cbed3888d

元コミット内容

    DOTDOTDOT import/export
    
    R=r
    OCL=18319
    CL=18319

変更の背景

Go言語には、関数が可変個の引数を受け取ることができる「可変長引数(variadic arguments)」という機能があります。これは、関数定義の最後のパラメータの型名の前に...を付けることで実現されます(例: func sum(nums ...int))。このコミットは、Go言語の初期段階において、この...構文をコンパイラが正しく解析し、内部的に型として扱うための基盤を導入するものです。

Goコンパイラは、ソースコードを機械語に変換する過程で、まず字句解析(lexical analysis)と構文解析(parsing)を行います。この段階で、...という特殊な記号を認識し、それをコンパイラ内部で扱える形式に変換する必要があります。このコミットは、そのためのパーサーのルール追加と、内部的な型定義の追加を行っています。

前提知識の解説

1. コンパイラの基本構造

コンパイラは、ソースコードを読み込み、それを実行可能な形式(機械語など)に変換するソフトウェアです。一般的なコンパイラは、以下の主要なフェーズで構成されます。

  • 字句解析(Lexical Analysis): ソースコードをトークン(意味を持つ最小単位、例: 識別子、キーワード、演算子)の並びに分解します。
  • 構文解析(Syntax Analysis): トークンの並びが言語の文法規則に合致するかを検証し、抽象構文木(AST: Abstract Syntax Tree)を構築します。ASTは、プログラムの構造を木構造で表現したものです。
  • 意味解析(Semantic Analysis): ASTを走査し、型チェックや変数宣言の確認など、意味的な正当性を検証します。
  • 中間コード生成(Intermediate Code Generation): ASTから、特定の機械に依存しない中間表現を生成します。
  • コード最適化(Code Optimization): 中間コードをより効率的な形に変換します。
  • コード生成(Code Generation): 中間コードから、ターゲットとなる機械語を生成します。

2. Yacc/Bisonとgo.y

  • Yacc (Yet Another Compiler Compiler) および Bison (GNU Parser Generator): これらは、文法定義ファイルからパーサー(構文解析器)のソースコードを自動生成するツールです。文法規則をBNF(Backus-Naur Form)のような形式で記述することで、複雑な構文解析ロジックを手書きする手間を省きます。
  • go.y: Go言語の初期コンパイラgcにおいて、go.yファイルはGo言語の文法規則をYacc形式で記述したものです。このファイルは、Goソースコードの構文解析を担当するパーサーを生成するために使用されます。go.y内のルールは、特定のトークン列(例: LDDD)がどのように解釈され、ASTのどのノードに対応するかを定義します。

3. 可変長引数(Variadic Arguments)

Go言語における可変長引数は、関数が任意の数の引数を受け取れるようにする機能です。 例: func printArgs(args ...interface{}) この関数は、printArgs(1, "hello", true)のように任意の数の引数で呼び出すことができます。関数内部では、argsはスライス([]interface{})として扱われます。

4. gcコンパイラ

Go言語の初期の公式コンパイラはgc(Go Compiler)と呼ばれ、C言語で実装されていました。このコミットは、そのgcコンパイラのソースコードに対する変更です。

技術的詳細

このコミットは、Go言語の可変長引数構文...をコンパイラが認識し、内部的に処理するための2つの主要な変更を導入しています。

1. src/cmd/gc/go.yにおけるパーサーの拡張

go.yはGo言語の文法定義ファイルであり、パーサーの動作を規定します。この変更では、hidden_type1という文法規則にLDDDという新しいトークンを追加しています。

  • LDDD: これは、字句解析器(lexer)によって認識される...(ドット3つ)という記号に対応するトークン名であると推測されます。パーサーは、このLDDDトークンを検出すると、それに続くアクションを実行します。
  • $$ = typ(TDDD);: LDDDトークンが検出された場合、パーサーはTDDDという内部的な型を表すオブジェクトを生成し、それを現在の構文規則のセマンティック値($$)として設定します。これは、AST内で...構文が特定の型として表現されることを意味します。

この変更により、コンパイラのフロントエンド(パーサー)は、Goソースコード内の...構文を正しく識別し、その存在を抽象構文木に反映できるようになります。

2. src/cmd/gc/subr.cにおける内部型定義の追加

subr.cは、Goコンパイラのバックエンドの一部であり、様々なユーティリティ関数や内部的なデータ構造の定義を含んでいます。この変更では、basicnamesという配列にTDDDという新しい型を追加しています。

  • [TDDD] = "...": これは、コンパイラ内部でTDDDという定数(おそらく列挙型の一部)が、Go言語の...構文に対応する型であることを示しています。basicnames配列は、内部的な型定数とその文字列表現をマッピングするために使用されることが多いです。これにより、コンパイラの他の部分(例えば、型チェックやコード生成フェーズ)がTDDDを認識し、可変長引数に関連する処理を行うための参照点が得られます。

これらの変更は、Go言語の可変長引数機能がコンパイラによってどのように扱われるかの初期段階を示しています。LDDDはパーサーが認識するトークン、TDDDはそのトークンが表す内部的な型表現として機能します。

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

diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y
index cb802025a9..0fbe0c1739 100644
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -1871,6 +1871,10 @@ hidden_type1:
 		$$->type = $3;
 		$$->chan = Csend;
 	}\n+|\tLDDD
+\t{\n+\t\t$$ = typ(TDDD);\n+\t}\n hidden_type2:
 	LCHAN hidden_type
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index c0344a9899..9da4738292 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -968,6 +968,7 @@ basicnames[] =\n 	[TFLOAT80]\t= "float80",\n 	[TBOOL]\t\t= "bool",\n 	[TANY]\t\t= "any",\n+\t[TDDD]\t\t= "...",\n };
 int

コアとなるコードの解説

src/cmd/gc/go.y の変更

@@ -1871,6 +1871,10 @@ hidden_type1:
 		$$->type = $3;
 		$$->chan = Csend;
 	}\n+|\tLDDD
+\t{\n+\t\t$$ = typ(TDDD);\n+\t}\n hidden_type2:
 	LCHAN hidden_type
  • hidden_type1:: これはYaccの文法規則の一部で、特定の型の定義に関連するものです。
  • |\tLDDD: これは、hidden_type1が既存の定義に加えて、LDDDというトークンも受け入れることを示しています。LDDDは、Go言語の...構文に対応する字句解析器によって生成されるトークンです。
  • {\n+\t\t$$ = typ(TDDD);\n+\t}: これは、LDDDトークンが検出されたときに実行されるアクションです。
    • $$: Yaccにおいて、現在の規則のセマンティック値を表します。
    • typ(TDDD): TDDDという内部的な型定数に対応する型オブジェクトを生成する関数呼び出しです。
    • この行は、パーサーが...構文を認識した際に、その構文をコンパイラ内部でTDDDという特殊な型として表現するように指示しています。これにより、抽象構文木(AST)上で...が型情報として扱われるようになります。

src/cmd/gc/subr.c の変更

@@ -968,6 +968,7 @@ basicnames[] =\n 	[TFLOAT80]\t= "float80",\n 	[TBOOL]\t\t= "bool",\n 	[TANY]\t\t= "any",\n+\t[TDDD]\t\t= "...",\n };
 int
  • basicnames[] = { ... }: この配列は、コンパイラ内部の基本的な型定数とその文字列表現をマッピングするために使用されます。
  • [TDDD]\t\t= "...",: この行は、TDDDという内部的な型定数(おそらく列挙型の一部)が、Go言語の...という文字列に対応することを定義しています。これにより、コンパイラの他の部分がTDDDを認識し、可変長引数に関連する処理(例えば、エラーメッセージの生成やデバッグ情報の表示など)を行う際に、その意味を正しく解釈できるようになります。

これらの変更は、Go言語の可変長引数機能のコンパイラサポートの初期段階であり、パーサーが...を認識し、それを内部的な型システムに統合するための重要なステップです。

関連リンク

参考にした情報源リンク