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

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

このコミットは、Go言語のコンパイラにおける文法解析器(パーサー)の変更に関するものです。具体的には、Go言語の初期段階におけるセミコロンの扱い、特に「オプションのセミコロン」という言語仕様の導入に関連する文法規則の調整が行われています。src/cmd/gc/go.y ファイルは、Goコンパイラのフロントエンドの一部であり、Yacc/Bisonによって生成されるパーサーの文法定義を記述しています。

コミット

このコミットは、Go言語の文法においてセミコロンをオプションにするための変更を導入しています。これにより、特定の文の終わりにセミコロンを明示的に記述する必要がなくなります。これはGo言語の設計思想の一つである「簡潔さ」を追求する上で重要な変更点です。

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

https://github.com/golang/go/commit/0006c83231328b65e0ced34f044a98f96ff8b269

元コミット内容

commit 0006c83231328b65e0ced34f044a98f96ff8b269
Author: Ken Thompson <ken@golang.org>
Date:   Tue Jun 17 17:57:31 2008 -0700

    optional semi-colons
    
    SVN=123245

変更の背景

Go言語は、その設計当初からC言語のような構文を持ちつつも、より現代的なプログラミングパラダイムを取り入れることを目指していました。その中で、C言語やJavaのような言語で必須とされている文末のセミコロンが、コードの冗長性を高め、可読性を損なうという議論がありました。このコミットは、Go言語が自動セミコロン挿入(Automatic Semicolon Insertion: ASI)の概念を導入する初期段階の一部であり、開発者がセミコロンを明示的に記述する手間を省き、よりクリーンなコードを書けるようにするための変更です。

Go言語のASIは、改行位置に基づいてコンパイラが自動的にセミコロンを挿入する仕組みです。これにより、多くのGoプログラムではセミコロンをほとんど見かけることがありません。このコミットは、そのASIを実現するための文法規則の調整を行っています。

前提知識の解説

Yacc/Bison

Yacc (Yet Another Compiler Compiler) および Bison (GNU Parser Generator) は、文法定義から構文解析器(パーサー)を自動生成するためのツールです。コンパイラのフロントエンドにおいて、ソースコードが言語の文法規則に準拠しているかを検証し、抽象構文木(AST)を構築する役割を担います。

  • 文法定義ファイル (.y): Yacc/Bisonは、BNF (Backus-Naur Form) に似た形式で記述された文法規則ファイル(通常 .y 拡張子を持つ)を読み込みます。このファイルには、トークン(終端記号)、非終端記号、そしてそれらの組み合わせで構成される規則が記述されます。
  • 規則とアクション: 各文法規則には、その規則が認識されたときに実行されるC言語(またはGo言語)のコード片(アクション)を関連付けることができます。このアクションは、ASTのノードを作成したり、セマンティックな処理を行ったりするために使用されます。
  • シフト・リデュース解析: Yacc/Bisonによって生成されるパーサーは、通常、LALR(1)パーサーと呼ばれるボトムアップ型の構文解析アルゴリズムを使用します。これは、入力トークンを読み込み(シフト)、文法規則に一致する部分を非終端記号に置き換える(リデュース)ことで解析を進めます。

Go言語の文法とセミコロン挿入

Go言語の文法は、C言語の影響を強く受けていますが、いくつかの重要な違いがあります。その一つがセミコロンの扱いです。Go言語では、特定の状況下で改行がセミコロンとして扱われます。これは、開発者が明示的にセミコロンを記述する手間を省き、コードをより簡潔にするための設計判断です。

Go言語仕様では、以下のルールに基づいてセミコロンが自動挿入されます。

  1. 識別子、整数リテラル、浮動小数点リテラル、虚数リテラル、ルーンリテラル、文字列リテラル、breakcontinuefallthroughreturn++--)]} の直後に改行がある場合。
  2. forifswitch などの制御構造のブロックの閉じ括弧 } の直後に改行がある場合。

このコミットは、このような自動挿入のロジックをパーサーに組み込むための初期のステップと考えられます。

技術的詳細

このコミットは、src/cmd/gc/go.y ファイル、すなわちGoコンパイラの構文解析器の文法定義を変更しています。主な変更点は以下の通りです。

  1. Dstmt 非終端記号の追加: %type <node> Dstmt が追加され、Dstmt という新しい非終端記号が導入されました。この Dstmt は、new_name ':' という規則で定義されています。これは、ラベル定義(例: myLabel:)を表現するためのものです。

    -%type <node> Astmt Bstmt Cstmt
    +%type <node> Astmt Bstmt Cstmt Dstmt
    

    そして、Dstmt の具体的な規則が追加されています。

    +/*
    + * need semi in front YES
    + * need semi in back  NO
    + */
    +Dstmt:
    +	new_name ':'
    +	{
    +		$$ = nod(OLABEL, $1, N);
    +	}
    

    以前は complex_stmt の一部として定義されていたラベルの規則が、独立した Dstmt として切り出されています。

    -|\tnew_name ':'
    -\t{
    -\t\t$$ = nod(OLABEL, $1, N);\
    -\t}
    
  2. Astmt_list_r および Bstmt_list_r 規則の変更: Astmt_list_rBstmt_list_r は、文のリストを扱う非終端記号です。これらの規則が変更され、新しく導入された Dstmt を含むように拡張されています。

    • Astmt_list_r: このリストは、セミコロンを必要としない文(Astmt)のリストを表していました。変更後、Dstmt もこのリストの要素として扱われるようになりました。また、Bstmt_list_r の後に Astmt が続く場合も OLIST ノードとして結合されるようになりました。

       Astmt_list_r:
       	Astmt
      +|\tDstmt
       |\tAstmt_list_r Astmt
       	{
       		$$ = nod(OLIST, $1, $2);\
       	}
      -|\tBstmt_list_r ';'
      +|\tAstmt_list_r Dstmt
      +\t{
      +\t\t$$ = nod(OLIST, $1, $2);\
      +\t}
      +|\tBstmt_list_r Astmt
      +\t{
      +\t\t$$ = nod(OLIST, $1, $2);\
      +\t}
      
    • Bstmt_list_r: このリストは、セミコロンを必要とする文(BstmtCstmt)のリストを表していました。変更後、Astmt_list_r の後に BstmtCstmt が続く場合も OLIST ノードとして結合されるようになりました。

       Bstmt_list_r:
       	Bstmt
       |\tCstmt
      -|\tBstmt_list_r Bstmt
      +|\tAstmt_list_r Bstmt
       	{
       		$$ = nod(OLIST, $1, $2);\
       	}
      -|\tAstmt_list_r Bstmt
      +|\tAstmt_list_r Cstmt
       	{
       		$$ = nod(OLIST, $1, $2);\
       	}
      -|\tAstmt_list_r Cstmt
      +|\tBstmt_list_r Bstmt
       	{
       		$$ = nod(OLIST, $1, $2);\
       	}
      

これらの変更は、Go言語のパーサーが、ラベル定義(Dstmt)を文のリストの一部として適切に処理し、かつセミコロンの自動挿入ルールに適合するように文の結合ロジックを調整していることを示しています。特に、Astmt_list_r から ; を含む規則が削除され、Dstmt が追加されたことは、セミコロンが明示的に必要とされるケースを減らし、自動挿入に依存する方向への移行を示唆しています。

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

src/cmd/gc/go.y ファイルの変更がコアとなります。

--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -33,7 +33,7 @@
 %type	<node>		else_stmt1 else_stmt2
 %type	<node>		complex_stmt compound_stmt ostmt_list
 %type	<node>		stmt_list_r Astmt_list_r Bstmt_list_r
-%type	<node>		Astmt Bstmt Cstmt
+%type	<node>		Astmt Bstmt Cstmt Dstmt
 %type	<node>		for_stmt for_body for_header
 %type	<node>		if_stmt if_body if_header
 %type	<node>		range_header range_body range_stmt
@@ -314,10 +314,6 @@ complex_stmt:
 	\tpoptodcl();
 	\t$$ = nod(OXCASE, N, N);
 	}
-|\tnew_name ':'
-\t{
-\t\t$$ = nod(OLABEL, $1, N);
-\t}
 
 semi_stmt:
 	LFALL
@@ -1101,31 +1097,49 @@ Cstmt:
 	simple_stmt
 
 /*
- * statement list that need semi in back  NO
+ * need semi in front YES
+ * need semi in back  NO
+ */
+Dstmt:
+	new_name ':'
+	{
+		$$ = nod(OLABEL, $1, N);
+	}
+
+/*
+ * statement list that ends AorD
  */
 Astmt_list_r:
 	Astmt
+|\tDstmt
 |\tAstmt_list_r Astmt
 	{
 		$$ = nod(OLIST, $1, $2);
 	}
-|\tBstmt_list_r ';'
+|\tAstmt_list_r Dstmt
+\t{
+\t\t$$ = nod(OLIST, $1, $2);
+\t}
+|\tBstmt_list_r Astmt
+\t{
+\t\t$$ = nod(OLIST, $1, $2);
+\t}
 
 /*
- * statement list that need semi in back  YES
+ * statement list that ends BorC
  */
 Bstmt_list_r:
 	Bstmt
 |\tCstmt
-|\tBstmt_list_r Bstmt
+|\tAstmt_list_r Bstmt
 	{
 		$$ = nod(OLIST, $1, $2);
 	}
-|\tAstmt_list_r Bstmt
+|\tAstmt_list_r Cstmt
 	{
 		$$ = nod(OLIST, $1, $2);
 	}
-|\tAstmt_list_r Cstmt
+|\tBstmt_list_r Bstmt
 	{
 		$$ = nod(OLIST, $1, $2);
 	}

コアとなるコードの解説

この変更は、Go言語の構文解析器がどのように文(statement)を認識し、それらをリストとして結合するかを再定義しています。

  1. Dstmt の導入: Dstmtnew_name ':' という形式、つまりラベル定義(例: loop:)を表現するために導入されました。以前は complex_stmt の一部として扱われていたラベルが、独立した非終端記号として定義されたことで、パーサーがラベルをより柔軟に、かつ他の文と区別して扱えるようになりました。コメント /* need semi in front YES * need semi in back NO */ は、この Dstmt の前にセミコロンが必要となる可能性があるが、後には不要であることを示唆しています。これは、Goの自動セミコロン挿入ルールと密接に関連しています。

  2. Astmt_list_r の変更: Astmt_list_r は、セミコロンを必要としない文のリストを構築するための規則です。

    • Astmt_list_r: Astmt に加えて Astmt_list_r: Dstmt が追加されたことで、ラベルもセミコロンなしで文のリストに含めることができるようになりました。
    • Astmt_list_r AstmtAstmt_list_r Dstmt の規則は、既存のリストに新しい Astmt または Dstmt を追加し、OLIST ノードとして結合します。
    • 最も重要な変更は、Bstmt_list_r ';' という規則が削除されたことです。これは、以前は Bstmt_list_r(セミコロンを必要とする文のリスト)の後に明示的なセミコロンが続くことで Astmt_list_r が形成されていたことを意味します。この規則の削除は、Go言語が明示的なセミコロンを減らし、自動挿入に移行する方向性を示しています。
    • Bstmt_list_r Astmt が追加されたことで、セミコロンを必要とする文のリストの後に、セミコロンを必要としない文が続く場合も適切に処理できるようになりました。
  3. Bstmt_list_r の変更: Bstmt_list_r は、セミコロンを必要とする文のリストを構築するための規則です。

    • Bstmt_list_r BstmtAstmt_list_r Bstmt に変更されたことは、Astmt_list_r(セミコロンを必要としない文のリスト)の後に Bstmt(セミコロンを必要とする文)が続く場合も、OLIST ノードとして結合されることを意味します。これは、文の結合ロジックがより柔軟になったことを示しています。
    • 同様に、Astmt_list_r CstmtBstmt_list_r Bstmt が追加され、文のリストの結合パターンが拡張されています。

これらの変更は、Go言語のパーサーが、より複雑な文の組み合わせや、自動セミコロン挿入のルールに対応できるように進化していることを示しています。特に、ラベルの扱いを独立させ、文のリスト結合規則を柔軟にすることで、Go言語の簡潔な構文を実現するための基盤が強化されています。

関連リンク

参考にした情報源リンク

  • Go言語の自動セミコロン挿入に関する議論や歴史的な背景については、Go言語の公式ブログや初期の設計ドキュメントが参考になります。
  • Yacc/Bison の一般的な情報については、コンパイラ設計に関する書籍やオンラインリソースが役立ちます。
  • Go言語のコンパイラソースコード(特に src/cmd/gc ディレクトリ)は、これらの変更がどのように実装されているかを理解するための究極の情報源です。