[インデックス 1668] ファイルの概要
このコミットは、Go言語の初期のコンパイラツールチェーンの一部であるpretty
パッケージ内のファイル群に対する変更を含んでいます。pretty
パッケージは、Go言語のソースコードを解析し、抽象構文木(AST)を構築し、それを整形して出力する役割を担っていたと考えられます。
具体的に変更されたファイルは以下の通りです。
usr/gri/pretty/ast.go
: 抽象構文木(AST)のノード定義が含まれています。Goプログラムの構造を表現するためのデータ構造が定義されています。usr/gri/pretty/gds.go
: Go Development Server (GDS) の一部である可能性があり、HTTPサーバーのロギングに関する修正が含まれています。usr/gri/pretty/parser.go
: Goソースコードを解析し、ASTを構築するパーサーのロジックが含まれています。このコミットの主要な変更点が含まれています。usr/gri/pretty/printer.go
: ASTを整形して出力するプリンターのロジックが含まれています。
コミット
- fixed bugs related to the empty statement
(now in sync with the spec and with 6g)
- fixed incorrect logging statement in gds
R=r
OCL=24970
CL=24970
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8e7873672e7d5b1f727fbc5d56ba4dea322d54f7
元コミット内容
commit 8e7873672e7d5b1f727fbc5d56ba4dea322d54f7
Author: Robert Griesemer <gri@golang.org>
Date: Thu Feb 12 16:06:21 2009 -0800
- fixed bugs related to the empty statement
(now in sync with the spec and with 6g)
- fixed incorrect logging statement in gds
R=r
OCL=24970
CL=24970
---
usr/gri/pretty/ast.go | 6 ++++++
usr/gri/pretty/gds.go | 2 +-\
usr/gri/pretty/parser.go | 33 +++++++++++++++------------------
usr/gri/pretty/printer.go | 6 ++++++\
4 files changed, 28 insertions(+), 19 deletions(-)
diff --git a/usr/gri/pretty/ast.go b/usr/gri/pretty/ast.go
index b3260a1fa4..df8bfbf2cd 100644
--- a/usr/gri/pretty/ast.go
+++ b/usr/gri/pretty/ast.go
@@ -390,6 +390,10 @@ type (
Tok int; // BREAK, CONTINUE, GOTO, FALLTHROUGH
Label *Ident; // if any, or nil
};
+
+ EmptyStat struct {
+ Pos int; // position of ";"
+ };
)
@@ -405,6 +409,7 @@ type StatVisitor interface {
DoSwitchStat(s *SwitchStat);
DoSelectStat(s *SelectStat);
DoControlFlowStat(s *ControlFlowStat);
+ DoEmptyStat(s *EmptyStat);
}
@@ -419,6 +424,7 @@ func (s *CaseClause) Visit(v StatVisitor) { v.DoCaseClause(s); }\
func (s *SwitchStat) Visit(v StatVisitor) { v.DoSwitchStat(s); }\
func (s *SelectStat) Visit(v StatVisitor) { v.DoSelectStat(s); }\
func (s *ControlFlowStat) Visit(v StatVisitor) { v.DoControlFlowStat(s); }\
+func (s *EmptyStat) Visit(v StatVisitor) { v.DoEmptyStat(s); }\
// ----------------------------------------------------------------------------
diff --git a/usr/gri/pretty/gds.go b/usr/gri/pretty/gds.go
index d5637cc9df..284a9d5073 100644
--- a/usr/gri/pretty/gds.go
+++ b/usr/gri/pretty/gds.go
@@ -155,7 +155,7 @@ func main() {\
http.Handle("/", http.HandlerFunc(serve));
err2 := http.ListenAndServe(":" + *port, nil);
if err2 != nil {\
-\t\tlog.Exitf("ListenAndServe: ", err2.String())\
+\t\tlog.Exitf("ListenAndServe: %s", err2.String())\
}\
}\
diff --git a/usr/gri/pretty/parser.go b/usr/gri/pretty/parser.go
index b5cbcd72bf..5543d9eeb3 100644
--- a/usr/gri/pretty/parser.go
+++ b/usr/gri/pretty/parser.go
@@ -733,25 +733,21 @@ func (P *Parser) parseStatementList(list *array.Array) {\
defer un(trace(P, "StatementList"));\
}\
\n+\texpect_semi := false;\
for P.tok != Scanner.CASE && P.tok != Scanner.DEFAULT && P.tok != Scanner.RBRACE && P.tok != Scanner.EOF {\
-\t\ts := P.parseStatement();\
-\t\tif s != nil {\
-\t\t\t// not the empty statement\
-\t\t\tlist.Push(s);\
+\t\tif expect_semi {\
+\t\t\tP.expect(Scanner.SEMICOLON);\
+\t\t\texpect_semi = false;\
\t\t}\
+\t\tlist.Push(P.parseStatement());\
\t\tif P.tok == Scanner.SEMICOLON {\
\t\t\tP.next();\
\t\t} else if P.opt_semi {\
\t\t\tP.opt_semi = false; // "consume" optional semicolon\
\t\t} else {\
-\t\t\tbreak;\
+\t\t\texpect_semi = true;\
\t\t}\
\t}\
-\n-\t// Try to provide a good error message\n-\tif P.tok != Scanner.CASE && P.tok != Scanner.DEFAULT && P.tok != Scanner.RBRACE && P.tok != Scanner.EOF {\
-\t\tP.error(P.pos, "expected end of statement list (semicolon missing?)");\
-\t}\
}\
\n \n@@ -1273,12 +1269,9 @@ func (P *Parser) parseIfStat() *AST.IfStat {\
var else_ AST.Stat;\
if P.tok == Scanner.ELSE {\
P.next();\
-\t\tif P.tok == Scanner.IF || P.tok == Scanner.LBRACE {\
+\t\tif ok := P.tok == Scanner.IF || P.tok == Scanner.LBRACE; ok || P.sixg {\
\t\t\telse_ = P.parseStatement();\
-\t\t} else if P.sixg {\
-\t\t\telse_ = P.parseStatement();\
-\t\t\tif else_ != nil {\
-\t\t\t\t// not the empty statement\
+\t\t\tif !ok {\
\t\t\t\t// wrap in a block since we don\'t have one\
\t\t\t\tbody := AST.NewBlock(0, Scanner.LBRACE);\
\t\t\t\tbody.List.Push(else_);\
@@ -1290,7 +1283,7 @@ func (P *Parser) parseIfStat() *AST.IfStat {\
}\
P.closeScope();\
\n-\treturn &AST.IfStat{pos, init, cond, body, else_ };\
+\treturn &AST.IfStat{pos, init, cond, body, else_};\
}\
\n \n@@ -1438,10 +1431,14 @@ func (P *Parser) parseStatement() AST.Stat {\
\treturn P.parseSwitchStat();\
case Scanner.SELECT:\
\treturn P.parseSelectStat();\
+\tcase Scanner.SEMICOLON:\
+\t\t// don\'t consume the \";\", it is the separator following the empty statement\
+\t\treturn &AST.EmptyStat{P.pos};\
\t}\
\n-\t// empty statement\
-\treturn nil;\
+\t// no statement found\
+\tP.error(P.pos, "statement expected");\
+\treturn &AST.BadStat{P.pos};\
}\
\n \ndiff --git a/usr/gri/pretty/printer.go b/usr/gri/pretty/printer.go
index d29bfd1ee6..a0de7d06a6 100644
--- a/usr/gri/pretty/printer.go
+++ b/usr/gri/pretty/printer.go
@@ -919,6 +919,12 @@ func (P *Printer) DoControlFlowStat(s *AST.ControlFlowStat) {\
}\
\n \n+func (P *Printer) DoEmptyStat(s *AST.EmptyStat) {\
+\tP.String(s.Pos, "");\
+\tP.separator = semicolon;\
+}\
+\n+\n // ----------------------------------------------------------------------------\
// Declarations
\n
変更の背景
このコミットの主な目的は、Go言語のパーサーとプリンターにおける「空ステートメント(empty statement)」の扱いに関するバグを修正し、Go言語の仕様(spec)および当時の主要なGoコンパイラである6g
との同期を図ることです。
Go言語では、セミコロン(;
)のみで構成されるステートメントは「空ステートメント」として扱われます。これは、例えばループの本体が空である場合や、複数のステートメントを区切るために意図的に使用されることがあります。初期のGoコンパイラやツールでは、この空ステートメントの解釈や処理に不整合があったと考えられます。
具体的には、以下の問題が背景にあったと推測されます。
- パーサーの不正確な挙動:
parser.go
の変更から、パーサーが空ステートメントを正しく認識せず、ステートメントリストの終端を誤って判断したり、不要なエラーを報告したりするバグがあった可能性があります。特に、セミコロンの自動挿入(automatic semicolon insertion, ASI)のルールと空ステートメントの組み合わせで問題が生じていたかもしれません。 - AST表現の欠如: 空ステートメントをAST上で明示的に表現する型が存在しなかったため、パーサーが空ステートメントをスキップしたり、
nil
として扱ったりしていた可能性があります。これにより、後続のツール(例えばプリンターやセマンティックアナライザー)が空ステートメントの存在を認識できず、正しく処理できない問題が発生していました。 - プリンターの不整合: ASTに空ステートメントが正しく表現されていないため、プリンターが空ステートメントを正しく出力できなかったり、フォーマットが崩れたりする問題があったと考えられます。
6g
コンパイラとの互換性: 当時、Go言語の公式コンパイラとして6g
が存在しており、pretty
パッケージのようなツールは6g
と同じ挙動を示す必要がありました。空ステートメントの扱いの不一致は、互換性の問題を引き起こす可能性がありました。- Go言語仕様との同期: Go言語は初期段階から厳密な仕様策定が進められており、ツールがその仕様に準拠することは非常に重要でした。空ステートメントの扱いは、言語仕様で明確に定義されるべきものであり、このコミットはその定義に沿うようにツールを修正するものでした。
また、副次的な変更として、gds.go
におけるロギングステートメントの書式文字列のバグも修正されています。これは、ログ出力が意図した形式になっていなかったという軽微な問題です。
前提知識の解説
このコミットを理解するためには、以下の概念について知っておく必要があります。
1. 抽象構文木 (Abstract Syntax Tree, AST)
ASTは、プログラミング言語のソースコードの抽象的な構文構造を木構造で表現したものです。コンパイラやインタープリタがソースコードを解析する際に、字句解析(トークン化)と構文解析(パース)を経て生成されます。ASTは、コードの意味を理解し、最適化やコード生成を行うための重要な中間表現となります。
- ノード: ASTの各要素はノードと呼ばれ、変数宣言、関数呼び出し、制御構造(if文、for文など)、式、ステートメントなど、コードの様々な構成要素に対応します。
- 階層構造: ノードは親子関係を持ち、コードの構造的なネストを表現します。例えば、
if
文のノードは、条件式、thenブロック、elseブロックのノードを子として持ちます。 ast.go
の役割: このファイルは、Go言語のASTにおける様々なノードの型定義を含んでいます。例えば、Expr
(式)、Stat
(ステートメント)、Decl
(宣言)などのインターフェースや、それらを実装する具体的な構造体(IfStat
,ForStat
,CallExpr
など)が定義されています。
2. パーサー (Parser)
パーサーは、字句解析器(lexer/scanner)から受け取ったトークンのストリームを基に、言語の文法規則に従ってASTを構築する役割を担います。
- 構文解析: トークン列が言語の文法に合致するかどうかを検証し、合致すればASTを生成します。
- エラーハンドリング: 文法エラーを検出した場合、適切なエラーメッセージを生成し、可能であればエラーから回復して解析を続行しようとします。
parser.go
の役割: このファイルは、Go言語のソースコードを読み込み、トークン化し、ASTを構築するロジックを含んでいます。parseStatement
やparseStatementList
のような関数は、Goの文法規則に従ってステートメントを解析し、対応するASTノードを生成します。
3. プリンター (Printer)
プリンターは、ASTを受け取り、それを人間が読める形式のソースコードに変換して出力する役割を担います。これは、コードの整形(フォーマット)や、ASTをデバッグ目的で表示する際などに使用されます。
- ASTの走査: ASTを深さ優先探索などで走査し、各ノードに対応するコード片を生成します。
- フォーマット: インデント、改行、スペースなどを適切に挿入し、読みやすいコードを生成します。
printer.go
の役割: このファイルは、ASTノードを走査し、Go言語のソースコードとして出力するロジックを含んでいます。DoEmptyStat
のようなメソッドは、特定のASTノードタイプがどのように出力されるべきかを定義します。
4. 空ステートメント (Empty Statement)
多くのプログラミング言語において、空ステートメントは何も操作を行わないステートメントです。Go言語では、単一のセミコロン(;
)が空ステートメントとして扱われます。
- 用途:
- ループの本体が空の場合:
for {}
のように、無限ループで何も処理しない場合など。 - 意図的な区切り: コードの可読性を高めるため、あるいは特定の文法規則を満たすために使用されることがあります。
- セミコロンの自動挿入 (ASI) との関係: Go言語にはASIのルールがあり、特定の状況で改行の後に自動的にセミコロンが挿入されます。これにより、開発者が明示的にセミコロンを書かなくても、コンパイラがステートメントの区切りを認識します。しかし、意図的に空ステートメントを記述する場合や、ASIのルールが適用されない状況では、明示的なセミコロンが必要になります。
- ループの本体が空の場合:
5. 6g
コンパイラ
6g
は、Go言語の初期の公式コンパイラの一つで、Plan 9 Cコンパイラツールチェーンをベースにしていました。Go言語がオープンソース化された当初、6g
(x86-64アーキテクチャ向け)、8g
(x86-32アーキテクチャ向け)、5g
(ARMアーキテクチャ向け)といったコンパイラが存在しました。これらのコンパイラは、Go言語の進化とともに、現在のgo tool compile
に統合されていきました。このコミットの時点では、6g
がGo言語の挙動の「基準」の一つであったため、pretty
パッケージが6g
と同期することは重要でした。
技術的詳細
このコミットは、主にGo言語のパーサーとASTの設計における空ステートメントの扱いを改善しています。
1. ast.go
の変更
EmptyStat
構造体の追加:
これにより、空ステートメントがAST上で明示的なノードとして表現できるようになりました。type ( // ... EmptyStat struct { Pos int; // position of ";" }; )
Pos
フィールドは、ソースコード内でのセミコロンの位置を示します。StatVisitor
インターフェースへのDoEmptyStat
メソッドの追加:
ASTを走査するVisitorパターンにおいて、type StatVisitor interface { // ... DoEmptyStat(s *EmptyStat); }
EmptyStat
ノードを処理するためのメソッドが追加されました。これにより、ASTを走査する他のツール(例えば、コード分析ツールやプリンター)が空ステートメントを認識し、適切に処理できるようになります。EmptyStat
のVisit
メソッドの実装:func (s *EmptyStat) Visit(v StatVisitor) { v.DoEmptyStat(s); }
EmptyStat
構造体がStat
インターフェース(またはそれに準ずるもの)を実装し、Visitorを受け入れるためのVisit
メソッドが追加されました。
これらの変更により、空ステートメントはASTの正式な一部となり、その存在がコンパイラツールチェーン全体で認識されるようになりました。
2. parser.go
の変更
parser.go
の変更は、空ステートメントの解析ロジックを根本的に修正しています。
parseStatementList
関数の変更:- 変更前は、
P.parseStatement()
がnil
を返した場合(空ステートメントと解釈されていた場合)、そのステートメントはリストに追加されず、ループがbreak
していました。これは、空ステートメントがASTに表現されないため、パーサーがそれを「無視」していたことを示唆しています。 - 変更後は、
expect_semi
というフラグが導入され、ステートメントの後にセミコロンが期待されるかどうかを管理します。 list.Push(P.parseStatement())
という行により、parseStatement
が返すASTノード(EmptyStat
を含む)が常にステートメントリストに追加されるようになりました。- セミコロンの処理ロジックが変更され、
P.tok == Scanner.SEMICOLON
の場合にP.next()
でセミコロンを消費し、P.opt_semi
(オプションのセミコロン)の場合も同様に処理します。 - 重要なのは、
else
ブロックでexpect_semi = true
が設定されるようになった点です。これは、ステートメントの後にセミコロンが明示的に存在しない場合、次のステートメントの前にセミコロンが期待されることを示唆しています。 - 以前存在した「セミコロンが欠落している」というエラーメッセージを生成するロジックが削除されました。これは、空ステートメントの正しい解釈により、以前はエラーと見なされていた状況が正当な構文として扱われるようになったためと考えられます。
- 変更前は、
parseIfStat
関数の変更:if
文のelse
ブロックの解析ロジックが簡素化されました。- 以前は
P.sixg
(6g
コンパイラモード)の場合に空ステートメントを特別扱いするようなロジックがありましたが、これが!ok
(if
または{
でない場合)という条件に統合され、より一般的な方法でステートメントをブロックにラップするようになりました。これは、空ステートメントがASTノードとして表現されるようになったことで、特別なnil
チェックが不要になったためと考えられます。
parseStatement
関数の変更:Scanner.SEMICOLON
(セミコロン)が検出された場合、新しいAST.EmptyStat
ノードを返すようになりました。この際、セミコロン自体は消費されません。これは、セミコロンが空ステートメントの「区切り」であると同時に、それ自体が空ステートメントを構成する要素であるというGo言語の文法を反映しています。- 以前は、どのステートメントにもマッチしなかった場合に
nil
を返していましたが、変更後はP.error
を呼び出して「statement expected」(ステートメントが期待される)というエラーを報告し、AST.BadStat
ノードを返すようになりました。これは、パーサーがより厳密になり、予期しないトークンを空ステートメントとして黙って無視するのではなく、明示的なエラーとして扱うようになったことを示しています。
3. printer.go
の変更
DoEmptyStat
メソッドの追加:func (P *Printer) DoEmptyStat(s *AST.EmptyStat) { P.String(s.Pos, ""); P.separator = semicolon; }
EmptyStat
ノードがASTに導入されたことに伴い、プリンターがこのノードをどのように出力するかを定義するメソッドが追加されました。P.String(s.Pos, "")
: これは、空ステートメント自体は何も文字列を出力しないことを意味します。空ステートメントは単にセミコロンで表現されるため、プリンターはセミコロンを自動的に挿入するロジックを持っているか、またはセミコロンがステートメントの区切りとして別途処理されることを示唆しています。P.separator = semicolon;
: これは、空ステートメントの後にセミコロンが区切りとして続くことをプリンターに指示しています。
4. gds.go
の変更
- ロギング書式文字列の修正:
- log.Exitf("ListenAndServe: ", err2.String()) + log.Exitf("ListenAndServe: %s", err2.String())
log.Exitf
関数はfmt.Printf
のような書式文字列を期待しますが、変更前は第2引数に書式指定子(%s
)がありませんでした。これにより、err2.String()
の内容が正しくログに出力されなかった可能性があります。変更後は%s
が追加され、エラー文字列が正しく埋め込まれるようになりました。これは、Go言語の初期のロギングAPIの利用方法に関する軽微なバグ修正です。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、usr/gri/pretty/ast.go
、usr/gri/pretty/parser.go
、usr/gri/pretty/printer.go
における空ステートメントの扱いに関するものです。
usr/gri/pretty/ast.go
@@ -390,6 +390,10 @@ type (
Tok int; // BREAK, CONTINUE, GOTO, FALLTHROUGH
Label *Ident; // if any, or nil
};
+
+ EmptyStat struct {
+ Pos int; // position of ";"
+ };
)
@@ -405,6 +409,7 @@ type StatVisitor interface {
DoSwitchStat(s *SwitchStat);
DoSelectStat(s *SelectStat);
DoControlFlowStat(s *ControlFlowStat);
+ DoEmptyStat(s *EmptyStat);
}
@@ -419,6 +424,7 @@ func (s *CaseClause) Visit(v StatVisitor) { v.DoCaseClause(s); }\
func (s *SwitchStat) Visit(v StatVisitor) { v.DoSwitchStat(s); }\
func (s *SelectStat) Visit(v StatVisitor) { v.DoSelectStat(s); }\
func (s *ControlFlowStat) Visit(v StatVisitor) { v.DoControlFlowStat(s); }\
+func (s *EmptyStat) Visit(v StatVisitor) { v.DoEmptyStat(s); }\
usr/gri/pretty/parser.go
@@ -733,25 +733,21 @@ func (P *Parser) parseStatementList(list *array.Array) {\
defer un(trace(P, "StatementList"));\
}\
\n+\texpect_semi := false;\
for P.tok != Scanner.CASE && P.tok != Scanner.DEFAULT && P.tok != Scanner.RBRACE && P.tok != Scanner.EOF {\
-\t\ts := P.parseStatement();\
-\t\tif s != nil {\
-\t\t\t// not the empty statement\
-\t\t\tlist.Push(s);\
+\t\tif expect_semi {\
+\t\t\tP.expect(Scanner.SEMICOLON);\
+\t\t\texpect_semi = false;\
\t\t}\
+\t\tlist.Push(P.parseStatement());\
\t\tif P.tok == Scanner.SEMICOLON {\
\t\t\tP.next();\
\t\t} else if P.opt_semi {\
\t\t\tP.opt_semi = false; // "consume" optional semicolon\
\t\t} else {\
-\t\t\tbreak;\
+\t\t\texpect_semi = true;\
\t\t}\
\t}\
-\n-\t// Try to provide a good error message\n-\tif P.tok != Scanner.CASE && P.tok != Scanner.DEFAULT && P.tok != Scanner.RBRACE && P.tok != Scanner.EOF {\
-\t\tP.error(P.pos, "expected end of statement list (semicolon missing?)");\
-\t}\
}\
\n \n@@ -1438,10 +1431,14 @@ func (P *Parser) parseStatement() AST.Stat {\
\treturn P.parseSwitchStat();\
case Scanner.SELECT:\
\treturn P.parseSelectStat();\
+\tcase Scanner.SEMICOLON:\
+\t\t// don\'t consume the \";\", it is the separator following the empty statement\
+\t\treturn &AST.EmptyStat{P.pos};\
\t}\
\n-\t// empty statement\
-\treturn nil;\
+\t// no statement found\
+\tP.error(P.pos, "statement expected");\
+\treturn &AST.BadStat{P.pos};\
}\
usr/gri/pretty/printer.go
@@ -919,6 +919,12 @@ func (P *Printer) DoControlFlowStat(s *AST.ControlFlowStat) {\
}\
\n \n+func (P *Printer) DoEmptyStat(s *AST.EmptyStat) {\
+\tP.String(s.Pos, "");\
+\tP.separator = semicolon;\
+}\
+\n+\n // ----------------------------------------------------------------------------\
// Declarations
\n
コアとなるコードの解説
ast.go
の変更
EmptyStat
構造体の導入は、Go言語のASTが空ステートメントを明示的に表現するようになったことを意味します。これにより、パーサーは空ステートメントをnil
として扱うのではなく、具体的なASTノードとして生成できるようになります。StatVisitor
インターフェースへのDoEmptyStat
の追加は、ASTを走査する際に空ステートメントを特別に処理する必要があることを示しており、プリンターや他の分析ツールがこの新しいノードタイプに対応できるようになります。
parser.go
の変更
parseStatementList
関数における変更は、パーサーがステートメントリストを解析する際のロジックを大幅に改善しています。
- 以前は、
parseStatement()
がnil
を返した場合(空ステートメントと見なされていた場合)、そのステートメントはリストに追加されず、ループが中断されていました。これは、空ステートメントがASTに表現されないため、パーサーがそれを「無視」していたことを示唆しています。 - 新しいロジックでは、
expect_semi
フラグとlist.Push(P.parseStatement())
の組み合わせにより、空ステートメントを含むすべてのステートメントがASTリストに正しく追加されるようになりました。これにより、ASTはソースコードの構造をより忠実に反映するようになります。 parseStatement
関数におけるcase Scanner.SEMICOLON:
の追加は、セミコロンが単独で出現した場合に、それをEmptyStat
ノードとして明示的に解析するようになったことを示しています。これにより、パーサーは空ステートメントを正しく識別し、ASTに組み込むことができます。- 以前の
return nil;
(空ステートメントとして扱われていた)からP.error(...)
とAST.BadStat
を返すように変更されたことは、パーサーがより厳密になり、予期しないトークンをエラーとして扱うようになったことを示唆しています。これは、Go言語の文法解析の堅牢性を高めるための重要な変更です。
printer.go
の変更
DoEmptyStat
メソッドの追加は、EmptyStat
ノードがASTに導入されたことに伴い、プリンターがこのノードをどのように出力するかを定義しています。P.String(s.Pos, "")
は、空ステートメント自体は何も文字列を出力しないことを意味します。これは、Go言語のセミコロンの自動挿入(ASI)のルールや、セミコロンがステートメントの区切りとして別途処理されることを考慮しているためと考えられます。P.separator = semicolon;
は、空ステートメントの後にセミコロンが区切りとして続くことをプリンターに指示しており、これにより整形されたコードがGo言語の慣習に沿うように出力されます。
これらの変更全体として、Go言語の初期のコンパイラツールチェーンが、空ステートメントという言語の基本的な要素を、仕様と既存のコンパイラ(6g
)の挙動に合わせて、より正確かつ堅牢に処理できるように進化している様子が伺えます。
関連リンク
- Go言語の仕様 (The Go Programming Language Specification) - 特に「Statements」のセクションで空ステートメントについて言及されています。
- Go言語のASTパッケージ (go/ast) - 現在のGo言語のASTパッケージのドキュメント。このコミットで変更された
ast.go
の現代版に相当します。 - Go言語のパーサーパッケージ (go/parser) - 現在のGo言語のパーサーパッケージのドキュメント。このコミットで変更された
parser.go
の現代版に相当します。 - Go言語のプリンターパッケージ (go/printer) - 現在のGo言語のプリンターパッケージのドキュメント。このコミットで変更された
printer.go
の現代版に相当します。
参考にした情報源リンク
- Go言語の公式ドキュメントおよび仕様
- Go言語のGitHubリポジトリのコミット履歴
- Go言語の初期のコンパイラに関する議論やドキュメント(ウェブ検索を通じて得られた情報)
- 抽象構文木 (AST) およびコンパイラの基本に関する一般的な知識
- プログラミング言語における空ステートメントの概念に関する一般的な知識
- Go言語のセミコロン自動挿入 (ASI) に関する情報
log.Exitf
のようなGo言語の初期のロギングAPIに関する情報