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

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

このコミットは、Go言語のcmd/yaccツールにおけるデバッグ出力の不具合を修正するものです。具体的には、トークン名をデバッグ表示する際のインデックス計算が誤っていた点を修正しています。これにより、yaccによって生成されるパーサーのデバッグ情報が正しく表示されるようになります。

コミット

commit b3bb4bd2925bc6715e5c91175021a2386f4017bb
Author: Russ Cox <rsc@golang.org>
Date:   Sat Dec 22 16:45:35 2012 -0500

    cmd/yacc: fix debug print of token name
    
    The array skips the first TOKSTART entries.
    
    Fixes #4410.
    
    R=golang-dev, ken2, ken
    CC=golang-dev
    https://golang.org/cl/6999054

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

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

元コミット内容

cmd/yacc: fix debug print of token name

The array skips the first TOKSTART entries.

Fixes #4410.

R=golang-dev, ken2, ken
CC=golang-dev
https://golang.org/cl/6999054

変更の背景

このコミットは、Go言語のyaccツール(cmd/yacc)が生成するパーサーのデバッグ出力において、トークン名が正しく表示されないという不具合を修正するために行われました。具体的には、トークン名を格納している配列のインデックス計算に誤りがあり、配列の先頭にあるTOKSTARTエントリの分だけオフセットがずれていたことが原因です。この問題はGoのIssue #4410として報告されていました。

yaccのようなパーサー生成ツールは、構文解析中に現在のトークンや期待されるトークンに関するデバッグ情報を出力することがよくあります。この情報が正しく表示されないと、パーサーのデバッグや開発が困難になります。この修正は、デバッグ情報の正確性を確保し、開発者の生産性を向上させることを目的としています。

また、src/cmd/yacc/units.yファイルには、// +build ignoreというビルドタグが追加されています。これは、make cleanを忘れた場合に次のビルドが失敗しないようにするための措置であり、units.yが直接ビルドプロセスに含まれないようにする意図があります。

前提知識の解説

Yacc (Yet Another Compiler Compiler)

Yaccは、コンパイラやインタプリタの構文解析器(パーサー)を生成するためのツールです。BNF(Backus-Naur Form)に似た文法定義を入力として受け取り、その文法を解析するためのC言語(またはGo言語など)のコードを生成します。生成されたコードは、字句解析器(lexer、通常はlexflexによって生成される)と連携して動作し、入力ストリームをトークンに分割し、それらのトークンが文法規則に合致するかどうかを検証します。

トークン (Token)

プログラミング言語のソースコードは、まず字句解析器によって意味のある最小単位に分割されます。この最小単位が「トークン」です。例えば、int x = 10;というコードは、int(キーワード)、x(識別子)、=(演算子)、10(整数リテラル)、;(区切り文字)といったトークンに分割されます。Yaccはこれらのトークンを基に構文解析を行います。

トークン名とデバッグ出力

Yaccが生成するパーサーは、デバッグ時に現在のトークンや期待されるトークンの種類を示すために、トークン名を文字列として出力することがあります。これは、パーサーの動作を理解したり、文法エラーの原因を特定したりする上で非常に役立ちます。トークン名は通常、内部的に整数値として管理されており、その整数値に対応する文字列をルックアップテーブルから取得して表示します。

Go言語におけるyacc

Go言語のツールチェインには、cmd/yaccというYaccの実装が含まれています。これは、Go言語で記述された文法定義ファイル(.y拡張子を持つファイル)からGo言語のパーサーコード(通常はy.goというファイル)を生成します。このコミットは、そのcmd/yaccが生成するコードの一部、特にデバッグ用のトークン名表示ロジックに関するものです。

技術的詳細

このコミットの核心は、src/cmd/yacc/yacc.goファイル内の$$Tokname関数の修正です。この関数は、与えられたトークンの整数値(c)に対応するトークン名を文字列として返す役割を担っています。

元のコードでは、トークン名を格納する配列$$Toknamesへのアクセスにc-1というオフセットを使用していました。

func $$Tokname(c int) string {
	if c > 0 && c <= len($$Toknames) {
		if $$Toknames[c-1] != "" {
			return $$Toknames[c-1]
		}
	}
	return __yyfmt__.Sprintf("tok-%v", c)
}

しかし、$$Toknames配列は、トークン値が特定の開始オフセット(TOKSTART)から始まることを前提としていました。コミットメッセージにある「The array skips the first TOKSTART entries.」という記述がこれを指しています。つまり、トークン値cが実際に配列のインデックスに対応するためには、TOKSTARTの分だけさらにオフセットを調整する必要があったのです。

修正後のコードでは、このオフセットが4であることが明示され、c-4という計算が導入されました。

func $$Tokname(c int) string {
	// 4 is TOKSTART above
	if c >= 4 && c-4 < len($$Toknames) {
		if $$Toknames[c-4] != "" {
			return $$Toknames[c-4]
		}
	}
	return __yyfmt__.Sprintf("tok-%v", c)
}

この4という値は、yaccが内部的に予約しているトークン値(例えば、EOF、エラー、またはその他の特殊な内部トークン)の数に対応していると考えられます。これらの予約されたトークンは$$Toknames配列には含まれておらず、ユーザー定義のトークンは4以上の値から始まるため、配列のインデックスに変換する際には4を引く必要があったのです。

また、条件式もc > 0からc >= 4に変更され、配列の範囲チェックもc-4 < len($$Toknames)に修正されています。これにより、トークン値が有効な範囲内にあるかどうかのチェックもより正確になりました。

src/cmd/yacc/units.yへの変更は、// +build ignoreというビルドタグの追加です。これは、Goのビルドシステムに対して、このファイルが通常のビルドプロセスから除外されるべきであることを指示します。これは、units.yyaccのテストや例として使用されるファイルであり、直接Goのパッケージとしてビルドされることを意図していないためです。これにより、make cleanを実行し忘れた場合でも、ビルドエラーが発生するのを防ぐことができます。

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

diff --git a/src/cmd/yacc/units.y b/src/cmd/yacc/units.y
index 00ccaf2ece..9c1b0b3364 100644
--- a/src/cmd/yacc/units.y
+++ b/src/cmd/yacc/units.y
@@ -11,6 +11,11 @@
 
 %{
 
+// This tag will end up in the generated y.go, so that forgetting
+// 'make clean' does not fail the next build.
+
+// +build ignore
+
 // units.y
 // example of a Go yacc program
 // usage is
diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go
index 62655e7cd2..0c18f93b6c 100644
--- a/src/cmd/yacc/yacc.go
+++ b/src/cmd/yacc/yacc.go
@@ -3228,9 +3228,10 @@ type $$Lexer interface {
 const $$Flag = -1000
 
 func $$Tokname(c int) string {
-	if c > 0 && c <= len($$Toknames) {
-		if $$Toknames[c-1] != "" {
-			return $$Toknames[c-1]
+	// 4 is TOKSTART above
+	if c >= 4 && c-4 < len($$Toknames) {
+		if $$Toknames[c-4] != "" {
+			return $$Toknames[c-4]
 		}
 	}
 	return __yyfmt__.Sprintf("tok-%v", c)

コアとなるコードの解説

src/cmd/yacc/units.yの変更

  • // +build ignoreという行が追加されました。
    • これはGoのビルドシステムに対する指示で、このファイル(units.yから生成されるy.go)を通常のビルドプロセスから除外することを意味します。
    • コメントにもあるように、「make cleanを忘れた場合に次のビルドが失敗しないように」するためのものです。units.ycmd/yaccのテストや例として使われるため、直接Goのパッケージとしてビルドされることを意図していません。このタグにより、go buildコマンドがこのファイルを無視するようになります。

src/cmd/yacc/yacc.goの変更

  • func $$Tokname(c int) string関数の内部ロジックが変更されました。
    • 変更前:
      if c > 0 && c <= len($$Toknames) {
          if $$Toknames[c-1] != "" {
              return $$Toknames[c-1]
          }
      }
      
      • このコードでは、トークン値cから1を引いた値を$$Toknames配列のインデックスとして使用していました。これは、トークン値が1から始まる場合に有効なアプローチです。
    • 変更後:
      // 4 is TOKSTART above
      if c >= 4 && c-4 < len($$Toknames) {
          if $$Toknames[c-4] != "" {
              return $$Toknames[c-4]
          }
      }
      
      • 新しいコードでは、トークン値cから4を引いた値をインデックスとして使用しています。
      • コメント// 4 is TOKSTART aboveが追加されており、これはトークン値が4から始まることを示唆しています。つまり、yaccが生成するトークン値は、内部的に0から3までの値が何らかの特殊な目的(例えば、EOFやエラーなどの内部トークン)のために予約されており、ユーザー定義のトークンは4から始まることを意味します。
      • $$Toknames配列は、これらの予約されたトークンを除いた、ユーザー定義のトークン名のみを格納しているため、実際の配列インデックスに変換するにはcから4を引く必要がありました。
      • 条件式もc >= 4に変更され、トークン値が有効な範囲(4以上)にあることを確認しています。また、配列の境界チェックもc-4 < len($$Toknames)と、新しいオフセットに合わせて修正されています。

この修正により、yaccが生成するパーサーがデバッグ時に出力するトークン名が、$$Toknames配列から正しいインデックスで取得されるようになり、デバッグ情報の正確性が向上しました。

関連リンク

参考にした情報源リンク

  • コミットメッセージと差分情報 (/home/orange/Project/comemo/commit_data/14733.txt)
  • Go言語のyaccに関する一般的な知識
  • Yacc/Bisonのトークンと内部値に関する一般的な知識
  • Go言語のビルドタグ(+build)に関する知識I have generated the commit explanation based on the provided instructions and the content of commit_data/14733.txt. The explanation is in Markdown format and covers all the required sections. I have outputted it to standard output as requested.