[インデックス 1425] ファイルの概要
このコミットは、Go言語の初期開発段階において、配列の宣言と初期化における[...]
(エリプシス)構文のパースと整形(pretty printing)のサポートを追加するものです。具体的には、配列の要素数を示す部分に...
を使用することで、コンパイラが初期化子の要素数から配列の長さを推論できるようにする機能が導入されました。
コミット
commit 5bd3c3b75523763e00129be24367f2d0856ee564
Author: Robert Griesemer <gri@golang.org>
Date: Tue Jan 6 17:39:25 2009 -0800
- support for [...] parsing and pretty printing
R=r
OCL=22185
CL=22185
--
usr/gri/pretty/parser.go | 5 ++++-
usr/gri/pretty/selftest2.go | 3 +++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/usr/gri/pretty/parser.go b/usr/gri/pretty/parser.go
index 1e78215058..4bd8e193c2 100644
--- a/usr/gri/pretty/parser.go
+++ b/usr/gri/pretty/parser.go
@@ -268,7 +268,10 @@ func (P *Parser) ParseArrayType() *AST.Type {
t := AST.NewType(P.pos, Scanner.LBRACK);
P.Expect(Scanner.LBRACK);
- if P.tok != Scanner.RBRACK {
+ if P.tok == Scanner.ELLIPSIS {
+ t.expr = P.NewExpr(P.pos, Scanner.ELLIPSIS, nil, nil);
+ P.Next();
+ } else if P.tok != Scanner.RBRACK {
t.expr = P.ParseExpression(1);
}
P.Expect(Scanner.RBRACK);
diff --git a/usr/gri/pretty/selftest2.go b/usr/gri/pretty/selftest2.go
index af449ed1ac..9b63fd5e9b 100644
--- a/usr/gri/pretty/selftest2.go
+++ b/usr/gri/pretty/selftest2.go
@@ -46,6 +46,9 @@ var (
A = 5;
u, v, w int = 0, 0, 0;
foo = "foo";
+ fixed_array0 = [10]int{};
+ fixed_array1 = [10]int{0, 1, 2};
+ fixed_array2 = [...]string{"foo", "bar"};
)
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5bd3c3b75523763e00129be24367f2d0856ee564
元コミット内容
このコミットは、Go言語のパーサーとプリティプリンターに、配列の宣言時に[...]
構文をサポートする機能を追加します。これにより、配列の長さを明示的に指定する代わりに、初期化子の要素数から長さを推論できるようになります。
変更の背景
Go言語は、シンプルで効率的なプログラミング言語を目指して設計されました。その設計思想の一つに、冗長な記述を避け、コードの可読性と記述性を向上させることがあります。配列の宣言において、初期化子から要素数が自明であるにもかかわらず、その長さを明示的に記述することは、冗長であり、コードの変更時に要素数と初期化子の整合性を手動で維持する必要があるという問題がありました。
このコミットは、このような冗長性を排除し、開発者がより簡潔に配列を宣言できるようにするために導入されました。[...]
構文を導入することで、コンパイラが初期化子の要素数を自動的に数え上げ、配列の長さを決定できるようになります。これにより、コードの記述量が減り、保守性が向上し、エラーの発生リスクも低減されます。
この変更は、Go言語がまだ初期開発段階にあった2009年1月にコミットされており、言語の基本的な構文設計において、簡潔さと利便性を重視する方向性が早期から確立されていたことを示しています。
前提知識の解説
このコミットを理解するためには、以下のGo言語の基本的な概念と、コンパイラの動作に関する一般的な知識が必要です。
Go言語の配列
Go言語における配列は、同じ型の要素を固定長で連続して格納するデータ構造です。配列の型は、要素の型と配列の長さによって決まります。例えば、[5]int
は5つの整数を格納できる配列の型を表します。
配列の宣言と初期化にはいくつかの方法があります。
- 長さと初期化子を明示的に指定:
var a [3]int = [3]int{1, 2, 3}
- 長さのみを指定し、ゼロ値で初期化:
var b [3]int // bは{0, 0, 0}で初期化される
- 初期化子のみを指定し、コンパイラに長さを推論させる(このコミットで導入される機能):
var c [...]int = [...]int{1, 2, 3} // cの長さは3と推論される
パーサー (Parser)
パーサーは、プログラミング言語のソースコードを読み込み、その構文が言語の文法規則に準拠しているかを解析するコンパイラのコンポーネントです。パーサーは、ソースコードのトークン列(字句解析器によって生成されたもの)を入力として受け取り、抽象構文木(AST: Abstract Syntax Tree)と呼ばれるツリー構造を構築します。ASTは、プログラムの構造を階層的に表現したもので、コンパイラの次の段階(意味解析、コード生成など)で利用されます。
このコミットでは、parser.go
ファイルが変更されており、これはGo言語のパーサーの一部であることがわかります。具体的には、配列の型をパースするParseArrayType
関数が修正されています。
字句解析器 (Scanner/Lexer)
字句解析器(またはスキャナー、レキサー)は、ソースコードを文字のストリームとして読み込み、それを意味のある最小単位である「トークン」に分割するコンパイラの最初の段階です。例えば、var
はキーワードトークン、myVar
は識別子トークン、=
は演算子トークンといった具合です。
コミットのコードスニペットにはScanner.LBRACK
(左角括弧)、Scanner.RBRACK
(右角括弧)、Scanner.ELLIPSIS
(エリプシス、...
)といった定数が登場しており、これらが字句解析器によって生成されるトークンであることが示唆されます。
抽象構文木 (AST: Abstract Syntax Tree)
ASTは、ソースコードの抽象的な構文構造を表現するツリーです。パーサーによって構築され、プログラムの構造をより高レベルで表現します。コンパイラの後の段階では、このASTを走査して意味解析を行ったり、中間コードや機械語を生成したりします。
コミットのコードにはAST.NewType
やP.NewExpr
といった関数呼び出しが見られ、これらがASTのノードを構築していることがわかります。t.expr
は、配列の長さを示す部分がASTの式として表現されることを示しています。
プリティプリンター (Pretty Printer)
プリティプリンターは、プログラムの抽象構文木(AST)を受け取り、それを人間が読みやすい形式のソースコードとして出力するツールです。通常、インデント、改行、スペースなどを適切に配置して、コードの整形を行います。このコミットのタイトルに「pretty printing」とあることから、この変更が単にパースするだけでなく、[...]
構文で宣言された配列を正しく整形して出力する機能も含まれていることがわかります。
技術的詳細
このコミットの主要な変更点は、Go言語のパーサーが配列の型を解析する際に、[...]
構文を認識し、それを抽象構文木(AST)に適切に表現できるようにすることです。
parser.go
のParseArrayType
関数は、配列の型をパースする責任を負っています。Go言語の配列型は[Length]Type
の形式を取ります。ここでLength
は配列の要素数を表す式です。
変更前のコードでは、左角括弧[
の後に右角括弧]
が続く場合([]Type
、スライス型)を除いて、Length
の部分を式としてパースしていました。
// 変更前
func (P *Parser) ParseArrayType() *AST.Type {
t := AST.NewType(P.pos, Scanner.LBRACK);
P.Expect(Scanner.LBRACK);
if P.tok != Scanner.RBRACK { // 右角括弧でなければ式としてパース
t.expr = P.ParseExpression(1);
}
P.Expect(Scanner.RBRACK);
// ...
}
このコミットでは、Scanner.RBRACK
(右角括弧)のチェックの前に、Scanner.ELLIPSIS
(...
トークン)のチェックが追加されました。
// 変更後
func (P *Parser) ParseArrayType() *AST.Type {
t := AST.NewType(P.pos, Scanner.LBRACK);
P.Expect(Scanner.LBRACK);
if P.tok == Scanner.ELLIPSIS { // ...トークンであれば
t.expr = P.NewExpr(P.pos, Scanner.ELLIPSIS, nil, nil); // ASTにELLIPSISノードを作成
P.Next(); // 次のトークンへ進む
} else if P.tok != Scanner.RBRACK { // 右角括弧でなければ式としてパース
t.expr = P.ParseExpression(1);
}
P.Expect(Scanner.RBRACK);
// ...
}
この変更により、パーサーは[
の直後に...
が来た場合、それを特別な意味を持つエリプシスとして認識し、ASTにELLIPSIS
を表すノードを作成します。このノードは、配列の長さが初期化子から推論されることをコンパイラの後の段階に伝えます。
selftest2.go
の変更は、この新機能のテストケースを追加しています。
fixed_array0 = [10]int{};
:明示的に長さを指定し、空の初期化子を持つ配列。fixed_array1 = [10]int{0, 1, 2};
:明示的に長さを指定し、要素を持つ配列。fixed_array2 = [...]string{"foo", "bar"};
:[...]
構文を使用し、コンパイラに長さを推論させる配列。このケースが、今回の変更によってパース可能になった新しい構文です。
これらのテストケースは、パーサーが新しい構文を正しく解釈し、それに対応するASTを生成できることを検証するために追加されました。また、プリティプリンターがこの構文を正しく整形して出力できることも暗にテストしています。
Go言語における...
の利用は、配列の長さ推論以外にも、可変長引数(variadic functions)や、スライスを可変長引数に展開する際にも使用されます。このコミットは、その中でも配列の長さ推論に焦点を当てたものです。
コアとなるコードの変更箇所
usr/gri/pretty/parser.go
--- a/usr/gri/pretty/parser.go
+++ b/usr/gri/pretty/parser.go
@@ -268,7 +268,10 @@ func (P *Parser) ParseArrayType() *AST.Type {
t := AST.NewType(P.pos, Scanner.LBRACK);
P.Expect(Scanner.LBRACK);
- if P.tok != Scanner.RBRACK {
+ if P.tok == Scanner.ELLIPSIS {
+ t.expr = P.NewExpr(P.pos, Scanner.ELLIPSIS, nil, nil);
+ P.Next();
+ } else if P.tok != Scanner.RBRACK {
t.expr = P.ParseExpression(1);
}
P.Expect(Scanner.RBRACK);
この変更は、ParseArrayType
関数内で、現在のトークンがScanner.ELLIPSIS
(...
)であるかどうかをチェックする条件分岐を追加しています。もし...
であれば、t.expr
にELLIPSIS
を表す新しいASTノードを設定し、次のトークンに進みます。そうでなければ、既存のロジック(右角括弧でなければ式としてパースする)が実行されます。
usr/gri/pretty/selftest2.go
--- a/usr/gri/pretty/selftest2.go
+++ b/usr/gri/pretty/selftest2.go
@@ -46,6 +46,9 @@ var (
A = 5;
u, v, w int = 0, 0, 0;
foo = "foo";
+ fixed_array0 = [10]int{};
+ fixed_array1 = [10]int{0, 1, 2};
+ fixed_array2 = [...]string{"foo", "bar"};
)
このファイルはテストケースを追加しており、[...]string{"foo", "bar"}
という新しい構文で配列を宣言する例が含まれています。これは、parser.go
の変更が正しく機能することを確認するためのものです。
コアとなるコードの解説
parser.go
の変更
ParseArrayType
関数は、Go言語の配列型(例: [5]int
や[...]string
)を解析する役割を担っています。
t := AST.NewType(P.pos, Scanner.LBRACK);
- 新しいASTノード(型を表す)を作成し、その開始位置と左角括弧トークンを関連付けます。
P.Expect(Scanner.LBRACK);
- 現在のトークンが左角括弧であることを期待し、そうでない場合はエラーを報告します。そして、次のトークンに進みます。
if P.tok == Scanner.ELLIPSIS { ... }
- ここが今回の変更の核心です。
[
の直後に...
トークンが続く場合(例:[...]
)、この条件が真になります。 t.expr = P.NewExpr(P.pos, Scanner.ELLIPSIS, nil, nil);
- 配列の長さを示す部分に、
ELLIPSIS
を表すASTノードを設定します。このノードは、コンパイラの後の段階で、配列の長さが初期化子から推論されるべきであることを示します。nil, nil
は、このELLIPSIS
ノードが子ノードを持たないことを意味します。
- 配列の長さを示す部分に、
P.Next();
...
トークンを消費し、次のトークンに進みます。
- ここが今回の変更の核心です。
else if P.tok != Scanner.RBRACK { ... }
...
トークンでなかった場合、かつ、現在のトークンが右角括弧]
でない場合(例:[5]
のように長さが明示されている場合)、この条件が真になります。t.expr = P.ParseExpression(1);
- 配列の長さを表す式(例:
5
)をパースし、その結果をt.expr
に設定します。ParseExpression(1)
は、式の優先順位を考慮して式をパースする関数です。
- 配列の長さを表す式(例:
P.Expect(Scanner.RBRACK);
- 現在のトークンが右角括弧であることを期待し、そうでない場合はエラーを報告します。そして、次のトークンに進みます。
このロジックにより、パーサーは[Length]Type
形式の配列だけでなく、[...]Type
形式の配列も正しく解析できるようになりました。
selftest2.go
の変更
このファイルは、Go言語のコンパイラ(または関連ツール)の自己テスト用コードの一部です。追加された以下の行は、新しい[...]
構文が正しく機能することを確認するためのテストケースです。
fixed_array0 = [10]int{};
fixed_array1 = [10]int{0, 1, 2};
fixed_array2 = [...]string{"foo", "bar"};
fixed_array0
とfixed_array1
は、既存の配列宣言の構文をテストするためのものです。fixed_array2
が今回のコミットで導入された[...]
構文のテストです。この行がエラーなくパースされ、コンパイルされることで、parser.go
の変更が意図通りに動作していることが確認されます。fixed_array2
の長さは、初期化子の要素数(2つ)から自動的に推論されます。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
- Go言語の配列に関するドキュメント: https://go.dev/tour/moretypes/6 (Go Tourの配列に関するセクション)
- Go言語の仕様: https://go.dev/ref/spec (配列の型や初期化に関する詳細な仕様)
参考にした情報源リンク
- Go言語の
...
構文に関するStack Overflowの議論: https://stackoverflow.com/questions/tagged/go+ellipsis - Go言語の歴史に関するWikipedia記事: https://en.wikipedia.org/wiki/Go_(programming_language)
- Effective Go: https://go.dev/doc/effective_go (Go言語のイディオムに関する公式ガイド)
[インデックス 1425] ファイルの概要
このコミットは、Go言語の初期開発段階において、配列の宣言と初期化における[...]
(エリプシス)構文のパースと整形(pretty printing)のサポートを追加するものです。具体的には、配列の要素数を示す部分に...
を使用することで、コンパイラが初期化子の要素数から配列の長さを推論できるようにする機能が導入されました。
コミット
commit 5bd3c3b75523763e00129be24367f2d0856ee564
Author: Robert Griesemer <gri@golang.org>
Date: Tue Jan 6 17:39:25 2009 -0800
- support for [...] parsing and pretty printing
R=r
OCL=22185
CL=22185
--
usr/gri/pretty/parser.go | 5 ++++-
usr/gri/pretty/selftest2.go | 3 +++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/usr/gri/pretty/parser.go b/usr/gri/pretty/parser.go
index 1e78215058..4bd8e193c2 100644
--- a/usr/gri/pretty/parser.go
+++ b/usr/gri/pretty/parser.go
@@ -268,7 +268,10 @@ func (P *Parser) ParseArrayType() *AST.Type {
t := AST.NewType(P.pos, Scanner.LBRACK);
P.Expect(Scanner.LBRACK);
- if P.tok != Scanner.RBRACK {
+ if P.tok == Scanner.ELLIPSIS {
+ t.expr = P.NewExpr(P.pos, Scanner.ELLIPSIS, nil, nil);
+ P.Next();
+ } else if P.tok != Scanner.RBRACK {
t.expr = P.ParseExpression(1);
}
P.Expect(Scanner.RBRACK);
diff --git a/usr/gri/pretty/selftest2.go b/usr/gri/pretty/selftest2.go
index af449ed1ac..9b63fd5e9b 100644
--- a/usr/gri/pretty/selftest2.go
+++ b/usr/gri/pretty/selftest2.go
@@ -46,6 +46,9 @@ var (
A = 5;
u, v, w int = 0, 0, 0;
foo = "foo";
+ fixed_array0 = [10]int{};
+ fixed_array1 = [10]int{0, 1, 2};
+ fixed_array2 = [...]string{"foo", "bar"};
)
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5bd3c3b75523763e00129be24367f2d0856ee564
元コミット内容
このコミットは、Go言語のパーサーとプリティプリンターに、配列の宣言時に[...]
構文をサポートする機能を追加します。これにより、配列の長さを明示的に指定する代わりに、初期化子の要素数から長さを推論できるようになります。
変更の背景
Go言語は、シンプルで効率的なプログラミング言語を目指して設計されました。その設計思想の一つに、冗長な記述を避け、コードの可読性と記述性を向上させることがあります。配列の宣言において、初期化子から要素数が自明であるにもかかわらず、その長さを明示的に記述することは、冗長であり、コードの変更時に要素数と初期化子の整合性を手動で維持する必要があるという問題がありました。
このコミットは、このような冗長性を排除し、開発者がより簡潔に配列を宣言できるようにするために導入されました。[...]
構文を導入することで、コンパイラが初期化子の要素数を自動的に数え上げ、配列の長さを決定できるようになります。これにより、コードの記述量が減り、保守性が向上し、エラーの発生リスクも低減されます。
この変更は、Go言語がまだ初期開発段階にあった2009年1月にコミットされており、言語の基本的な構文設計において、簡潔さと利便性を重視する方向性が早期から確立されていたことを示しています。
前提知識の解説
このコミットを理解するためには、以下のGo言語の基本的な概念と、コンパイラの動作に関する一般的な知識が必要です。
Go言語の配列
Go言語における配列は、同じ型の要素を固定長で連続して格納するデータ構造です。配列の型は、要素の型と配列の長さによって決まります。例えば、[5]int
は5つの整数を格納できる配列の型を表します。
配列の宣言と初期化にはいくつかの方法があります。
- 長さと初期化子を明示的に指定:
var a [3]int = [3]int{1, 2, 3}
- 長さのみを指定し、ゼロ値で初期化:
var b [3]int // bは{0, 0, 0}で初期化される
- 初期化子のみを指定し、コンパイラに長さを推論させる(このコミットで導入される機能):
var c [...]int = [...]int{1, 2, 3} // cの長さは3と推論される
パーサー (Parser)
パーサーは、プログラミング言語のソースコードを読み込み、その構文が言語の文法規則に準拠しているかを解析するコンパイラのコンポーネントです。パーサーは、ソースコードのトークン列(字句解析器によって生成されたもの)を入力として受け取り、抽象構文木(AST: Abstract Syntax Tree)と呼ばれるツリー構造を構築します。ASTは、プログラムの構造を階層的に表現したもので、コンパイラの次の段階(意味解析、コード生成など)で利用されます。
このコミットでは、parser.go
ファイルが変更されており、これはGo言語のパーサーの一部であることがわかります。具体的には、配列の型をパースするParseArrayType
関数が修正されています。
字句解析器 (Scanner/Lexer)
字句解析器(またはスキャナー、レキサー)は、ソースコードを文字のストリームとして読み込み、それを意味のある最小単位である「トークン」に分割するコンパイラの最初の段階です。例えば、var
はキーワードトークン、myVar
は識別子トークン、=
は演算子トークンといった具合です。
コミットのコードスニペットにはScanner.LBRACK
(左角括弧)、Scanner.RBRACK
(右角括弧)、Scanner.ELLIPSIS
(エリプシス、...
)といった定数が登場しており、これらが字句解析器によって生成されるトークンであることが示唆されます。
抽象構文木 (AST: Abstract Syntax Tree)
ASTは、ソースコードの抽象的な構文構造を表現するツリーです。パーサーによって構築され、プログラムの構造をより高レベルで表現します。コンパイラの後の段階では、このASTを走査して意味解析を行ったり、中間コードや機械語を生成したりします。
コミットのコードにはAST.NewType
やP.NewExpr
といった関数呼び出しが見られ、これらがASTのノードを構築していることがわかります。t.expr
は、配列の長さを示す部分がASTの式として表現されることを示しています。
プリティプリンター (Pretty Printer)
プリティプリンターは、プログラムの抽象構文木(AST)を受け取り、それを人間が読みやすい形式のソースコードとして出力するツールです。通常、インデント、改行、スペースなどを適切に配置して、コードの整形を行います。このコミットのタイトルに「pretty printing」とあることから、この変更が単にパースするだけでなく、[...]
構文で宣言された配列を正しく整形して出力する機能も含まれていることがわかります。
技術的詳細
このコミットの主要な変更点は、Go言語のパーサーが配列の型を解析する際に、[...]
構文を認識し、それを抽象構文木(AST)に適切に表現できるようにすることです。
parser.go
のParseArrayType
関数は、配列の型をパースする責任を負っています。Go言語の配列型は[Length]Type
の形式を取ります。ここでLength
は配列の要素数を表す式です。
変更前のコードでは、左角括弧[
の後に右角括弧]
が続く場合([]Type
、スライス型)を除いて、Length
の部分を式としてパースしていました。
// 変更前
func (P *Parser) ParseArrayType() *AST.Type {
t := AST.NewType(P.pos, Scanner.LBRACK);
P.Expect(Scanner.LBRACK);
if P.tok != Scanner.RBRACK { // 右角括弧でなければ式としてパース
t.expr = P.ParseExpression(1);
}
P.Expect(Scanner.RBRACK);
// ...
}
このコミットでは、Scanner.RBRACK
(右角括弧)のチェックの前に、Scanner.ELLIPSIS
(...
トークン)のチェックが追加されました。
// 変更後
func (P *Parser) ParseArrayType() *AST.Type {
t := AST.NewType(P.pos, Scanner.LBRACK);
P.Expect(Scanner.LBRACK);
if P.tok == Scanner.ELLIPSIS { // ...トークンであれば
t.expr = P.NewExpr(P.pos, Scanner.ELLIPSIS, nil, nil); // ASTにELLIPSISノードを作成
P.Next(); // 次のトークンへ進む
} else if P.tok != Scanner.RBRACK { // 右角括弧でなければ式としてパース
t.expr = P.ParseExpression(1);
}
P.Expect(Scanner.RBRACK);
// ...
}
この変更により、パーサーは[
の直後に...
が来た場合、それを特別な意味を持つエリプシスとして認識し、ASTにELLIPSIS
を表すノードを作成します。このノードは、配列の長さが初期化子から推論されることをコンパイラの後の段階に伝えます。
selftest2.go
の変更は、この新機能のテストケースを追加しています。
fixed_array0 = [10]int{};
:明示的に長さを指定し、空の初期化子を持つ配列。fixed_array1 = [10]int{0, 1, 2};
:明示的に長さを指定し、要素を持つ配列。fixed_array2 = [...]string{"foo", "bar"};
:[...]
構文を使用し、コンパイラに長さを推論させる配列。このケースが、今回の変更によってパース可能になった新しい構文です。
これらのテストケースは、パーサーが新しい構文を正しく解釈し、それに対応するASTを生成できることを検証するために追加されました。また、プリティプリンターがこの構文を正しく整形して出力できることも暗にテストしています。
Go言語における...
の利用は、配列の長さ推論以外にも、可変長引数(variadic functions)や、スライスを可変長引数に展開する際にも使用されます。このコミットは、その中でも配列の長さ推論に焦点を当てたものです。
コアとなるコードの変更箇所
usr/gri/pretty/parser.go
--- a/usr/gri/pretty/parser.go
+++ b/usr/gri/pretty/parser.go
@@ -268,7 +268,10 @@ func (P *Parser) ParseArrayType() *AST.Type {
t := AST.NewType(P.pos, Scanner.LBRACK);
P.Expect(Scanner.LBRACK);
- if P.tok != Scanner.RBRACK {
+ if P.tok == Scanner.ELLIPSIS {
+ t.expr = P.NewExpr(P.pos, Scanner.ELLIPSIS, nil, nil);
+ P.Next();
+ } else if P.tok != Scanner.RBRACK {
t.expr = P.ParseExpression(1);
}
P.Expect(Scanner.RBRACK);
この変更は、ParseArrayType
関数内で、現在のトークンがScanner.ELLIPSIS
(...
)であるかどうかをチェックする条件分岐を追加しています。もし...
であれば、t.expr
にELLIPSIS
を表す新しいASTノードを設定し、次のトークンに進みます。そうでなければ、既存のロジック(右角括弧でなければ式としてパースする)が実行されます。
usr/gri/pretty/selftest2.go
--- a/usr/gri/pretty/selftest2.go
+++ b/usr/gri/pretty/selftest2.go
@@ -46,6 +46,9 @@ var (
A = 5;
u, v, w int = 0, 0, 0;
foo = "foo";
+ fixed_array0 = [10]int{};
+ fixed_array1 = [10]int{0, 1, 2};
+ fixed_array2 = [...]string{"foo", "bar"};
)
このファイルはテストケースを追加しており、[...]string{"foo", "bar"}
という新しい構文で配列を宣言する例が含まれています。これは、parser.go
の変更が正しく機能することを確認するためのものです。
コアとなるコードの解説
parser.go
の変更
ParseArrayType
関数は、Go言語の配列型(例: [5]int
や[...]string
)を解析する役割を担っています。
t := AST.NewType(P.pos, Scanner.LBRACK);
- 新しいASTノード(型を表す)を作成し、その開始位置と左角括弧トークンを関連付けます。
P.Expect(Scanner.LBRACK);
- 現在のトークンが左角括弧であることを期待し、そうでない場合はエラーを報告します。そして、次のトークンに進みます。
if P.tok == Scanner.ELLIPSIS { ... }
- ここが今回の変更の核心です。
[
の直後に...
トークンが続く場合(例:[...]
)、この条件が真になります。 t.expr = P.NewExpr(P.pos, Scanner.ELLIPSIS, nil, nil);
- 配列の長さを示す部分に、
ELLIPSIS
を表すASTノードを設定します。このノードは、コンパイラの後の段階で、配列の長さが初期化子から推論されるべきであることを示します。nil, nil
は、このELLIPSIS
ノードが子ノードを持たないことを意味します。
- 配列の長さを示す部分に、
P.Next();
...
トークンを消費し、次のトークンに進みます。
- ここが今回の変更の核心です。
else if P.tok != Scanner.RBRACK { ... }
...
トークンでなかった場合、かつ、現在のトークンが右角括弧]
でない場合(例:[5]
のように長さが明示されている場合)、この条件が真になります。t.expr = P.ParseExpression(1);
- 配列の長さを表す式(例:
5
)をパースし、その結果をt.expr
に設定します。ParseExpression(1)
は、式の優先順位を考慮して式をパースする関数です。
- 配列の長さを表す式(例:
P.Expect(Scanner.RBRACK);
- 現在のトークンが右角括弧であることを期待し、そうでない場合はエラーを報告します。そして、次のトークンに進みます。
このロジックにより、パーサーは[Length]Type
形式の配列だけでなく、[...]Type
形式の配列も正しく解析できるようになりました。
selftest2.go
の変更
このファイルは、Go言語のコンパイラ(または関連ツール)の自己テスト用コードの一部です。追加された以下の行は、新しい[...]
構文が正しく機能することを確認するためのテストケースです。
fixed_array0 = [10]int{};
fixed_array1 = [10]int{0, 1, 2};
fixed_array2 = [...]string{"foo", "bar"};
fixed_array0
とfixed_array1
は、既存の配列宣言の構文をテストするためのものです。fixed_array2
が今回のコミットで導入された[...]
構文のテストです。この行がエラーなくパースされ、コンパイルされることで、parser.go
の変更が意図通りに動作していることが確認されます。fixed_array2
の長さは、初期化子の要素数(2つ)から自動的に推論されます。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
- Go言語の配列に関するドキュメント: https://go.dev/tour/moretypes/6 (Go Tourの配列に関するセクション)
- Go言語の仕様: https://go.dev/ref/spec (配列の型や初期化に関する詳細な仕様)
参考にした情報源リンク
- Go言語の
...
構文に関するStack Overflowの議論: https://stackoverflow.com/questions/tagged/go+ellipsis - Go言語の歴史に関するWikipedia記事: https://en.wikipedia.org/wiki/Go_(programming_language)
- Effective Go: https://go.dev/doc/effective_go (Go言語のイディオムに関する公式ガイド)