[インデックス 16306] ファイルの概要
このコミットは、Go言語のcmd/yacc
ツールにおける挙動の修正に関するものです。具体的には、yacc
ツールが生成するGoコードにおいて、-l
(--nolines
)フラグが指定された場合に、ソースコードの行番号を示すコメント(//line
コメント)が出力されないように変更されています。これにより、生成されるコードの冗長性が減り、デバッグや可読性の向上に寄与します。
コミット
commit 2eeab323ad516dca2fb6f222a1810b3fadd61fc1
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Wed May 15 12:08:51 2013 +0800
cmd/yacc: don't emit line comment when -l is given
Fixes #5447.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/9343045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2eeab323ad516dca2fb6f222a1810b3fadd61fc1
元コミット内容
cmd/yacc: don't emit line comment when -l is given
Fixes #5447.
このコミットは、cmd/yacc
ツールが-l
オプション(--nolines
)を受け取った際に、生成されるGoコード内に//line
コメントを出力しないようにするものです。これは、Issue #5447で報告された問題の修正に対応しています。
変更の背景
yacc
のようなパーサジェネレータは、文法定義ファイルからソースコードを生成します。通常、生成されたコードには、元の文法定義ファイルのどの行からコードが生成されたかを示す//line
コメントが含まれます。これは、コンパイルエラーやランタイムエラーが発生した際に、生成されたコードではなく元の文法定義ファイルの行番号を参照できるようにするため、デバッグに非常に役立ちます。
しかし、場合によっては、これらの//line
コメントが不要、あるいは邪魔になることがあります。例えば、生成されたコードのサイズを最小限に抑えたい場合や、生成されたコード自体を直接デバッグするのではなく、元の文法定義ファイルのみを参照してデバッグを行うワークフローの場合などです。
Go言語のcmd/yacc
ツールには、このような//line
コメントの出力を抑制するための-l
(--nolines
)フラグが存在していました。しかし、このコミットが修正する問題(Issue #5447)は、この-l
フラグが正しく機能せず、フラグが指定されても//line
コメントが出力されてしまうというバグでした。このバグにより、ユーザーは生成されるコードから不要なコメントを削除するために手動での後処理が必要になるなど、不便を強いられていました。
このコミットは、このバグを修正し、-l
フラグの意図された挙動を回復させることで、ユーザーが生成コードのコメント出力をより細かく制御できるようにすることを目的としています。
前提知識の解説
Yacc (Yet Another Compiler Compiler)
Yaccは「Yet Another Compiler Compiler」の略で、コンパイラのパーサ部分を自動生成するためのツールです。BNF(Backus-Naur Form)やEBNF(Extended Backus-Naur Form)のような形式で記述された文法定義を読み込み、その文法を解析するためのC言語(または他の言語)のソースコードを生成します。生成されるコードは、通常、LALR(1)パーサと呼ばれる種類のパーサです。
Yaccは、字句解析器(Lexer)を生成するLex(またはFlex)と組み合わせて使用されることが多く、Lexが入力ストリームをトークンに分割し、Yaccがそのトークン列を文法規則に従って解析します。
cmd/yacc
cmd/yacc
は、Go言語で実装されたYacc互換のパーサジェネレータです。Go言語のプロジェクトでパーサを生成する際に使用されます。基本的な機能はオリジナルのYaccと同様ですが、Go言語のコードを生成するという点で異なります。
//line
コメント
Go言語のソースコードにおいて、//line
コメントは、コンパイラに対して、その行以降のコードが別のファイルや別の行番号から来ているかのように扱うよう指示する特殊なコメントです。
構文は以下の通りです。
//line <ファイル名>:<行番号>
例えば、//line myparser.y:100
というコメントがあれば、その後のコードはmyparser.y
ファイルの100行目から来ているとコンパイラに認識させることができます。これは、特にコードジェネレータによって生成されたファイルで非常に有用です。生成されたコードでエラーが発生した場合でも、元のソースファイル(この場合はmyparser.y
)の対応する行番号をエラーメッセージに表示できるため、デバッグが容易になります。
-l
(または --nolines
) フラグ
Yaccやその派生ツールにおいて、-l
または--nolines
フラグは、生成されるソースコードに//line
コメントを含めないようにするためのオプションです。このフラグを使用することで、生成されるファイルのサイズを削減したり、生成されたコードの可読性を向上させたりすることができます。
技術的詳細
このコミットは、src/cmd/yacc/yacc.go
ファイル内の3つの箇所で、//line
コメントの出力ロジックに条件分岐を追加しています。変更の核心は、グローバル変数lflag
(-l
オプションが指定された場合にtrue
となるフラグ)の値をチェックし、lflag
がtrue
の場合には//line
コメントの出力をスキップするという点です。
具体的には、以下の3つの関数内のfmt.Fprintf
呼び出しに条件が追加されています。
-
emitcode
関数内: この関数は、ユーザーが提供するアクションコード(文法規則に対応するGoコード)を生成する際に使用されます。元のコードでは、import __yyfmt__ "fmt"
のインポート文の後に、常に//line %v:%v
形式のコメントが出力されていました。 変更後:if !lflag { fmt.Fprintf(ftable, "//line %v:%v\\n\\t\\t", infile, lineno+i) }
これにより、-l
フラグが設定されている場合は、この//line
コメントが出力されなくなります。 -
output
関数内: この関数は、パーサのテーブルデータ(状態遷移テーブルなど)を生成する際に使用されます。元のコードでは、fmt.Fprintf(ftable, "\\n//line yacctab:1\\n")
という行が常に存在し、yacctab
という仮想ファイルからの行コメントを出力していました。 変更後:if !lflag { fmt.Fprintf(ftable, "\\n//line yacctab:1") }
これにより、-l
フラグが設定されている場合は、この//line
コメントが出力されなくなります。 -
others
関数内: この関数は、yaccpar
(Yaccが生成するパーサの共通部分)のコードをコピーする際に使用されます。元のコードでは、fmt.Fprintf(ftable, "\\n//line yaccpar:1\\n")
という行が常に存在し、yaccpar
という仮想ファイルからの行コメントを出力していました。 変更後:if !lflag { fmt.Fprintf(ftable, "\\n//line yaccpar:1\\n") }
これにより、-l
フラグが設定されている場合は、この//line
コメントが出力されなくなります。
これらの変更により、lflag
がtrue
の場合、つまり-l
オプションが指定された場合に、cmd/yacc
が生成するGoコードからすべての//line
コメントが抑制されるようになります。
コアとなるコードの変更箇所
--- a/src/cmd/yacc/yacc.go
+++ b/src/cmd/yacc/yacc.go
@@ -1130,7 +1130,9 @@ func emitcode(code []rune, lineno int) {
writecode(line)
if !fmtImported && isPackageClause(line) {
fmt.Fprintln(ftable, `import __yyfmt__ "fmt"`)
- fmt.Fprintf(ftable, "//line %v:%v\\n\\t\\t", infile, lineno+i)
+ if !lflag {
+ fmt.Fprintf(ftable, "//line %v:%v\\n\\t\\t", infile, lineno+i)
+ }
fmtImported = true
}
}
@@ -2193,8 +2195,10 @@ nextk:
func output() {
var c, u, v int
- fmt.Fprintf(ftable, "\n//line yacctab:1\n")
- fmt.Fprintf(ftable, "var %sExca = []int{\n", prefix)
+ if !lflag {
+ fmt.Fprintf(ftable, "\n//line yacctab:1")
+ }
+ fmt.Fprintf(ftable, "\nvar %sExca = []int{\n", prefix)
noset := mkset()
@@ -2963,7 +2967,9 @@ func others() {
}
// copy yaccpar
- fmt.Fprintf(ftable, "\n//line yaccpar:1\n")
+ if !lflag {
+ fmt.Fprintf(ftable, "\n//line yaccpar:1\n")
+ }
parts := strings.SplitN(yaccpar, prefix+"run()", 2)
fmt.Fprintf(ftable, "%v", parts[0])
コアとなるコードの解説
上記の差分は、src/cmd/yacc/yacc.go
ファイル内の3つの異なる箇所で、//line
コメントの出力に条件分岐を追加していることを示しています。
-
emitcode
関数内の変更: 元のコードでは、import __yyfmt__ "fmt"
の後に、常に//line
コメントが出力されていました。これは、生成されるGoコードが、元の文法定義ファイル(infile
)の特定の行(lineno+i
)から来ていることを示すためのものです。 変更後のif !lflag { ... }
という条件は、コマンドライン引数で-l
フラグが指定されていない場合(つまりlflag
がfalse
の場合)にのみ、この//line
コメントを出力するようにします。lflag
がtrue
(-l
が指定された)であれば、このコメントはスキップされます。 -
output
関数内の変更: この関数は、パーサの内部テーブル(%sExca
配列など)を生成します。元のコードでは、これらのテーブルの前に//line yacctab:1
というコメントが挿入されていました。これは、これらのデータがyacctab
という仮想的なファイル(Yaccの内部的なテーブル定義)の1行目から来ていることを示すものです。 ここでもif !lflag { ... }
という条件が追加され、-l
フラグが指定されている場合は、この//line
コメントが出力されなくなります。 -
others
関数内の変更: この関数は、Yaccパーサの共通ランタイムコードであるyaccpar
を生成されたファイルにコピーします。元のコードでは、yaccpar
のコードの前に//line yaccpar:1
というコメントが挿入されていました。これは、yaccpar
のコードがyaccpar
という仮想的なファイル(Yaccのランタイム定義)の1行目から来ていることを示すものです。 同様にif !lflag { ... }
という条件が追加され、-l
フラグが指定されている場合は、この//line
コメントが出力されなくなります。
これらの変更により、cmd/yacc
は-l
フラグの意図された挙動を正しく実装し、生成されるGoコードから不要な//line
コメントを抑制できるようになりました。これにより、生成コードのクリーンアップや、特定のビルドプロセスにおける要件を満たすことが可能になります。
関連リンク
- Go Issue #5447: cmd/yacc: -l flag doesn't suppress line comments - このコミットが修正したバグの報告。
- Gerrit Change 9343045: https://golang.org/cl/9343045 - このコミットに対応するGoのコードレビューシステム(Gerrit)上の変更セット。
参考にした情報源リンク
- Yacc: https://en.wikipedia.org/wiki/Yacc
- Go言語の
//line
ディレクティブ: Go言語の公式ドキュメントやコンパイラのソースコードで詳細を確認できますが、一般的な情報としては、コード生成ツールが元のソース位置を追跡するために使用されることが知られています。 cmd/yacc
のソースコード: https://github.com/golang/go/tree/master/src/cmd/yacc (コミット当時のバージョン)- Go言語のIssueトラッカー: https://github.com/golang/go/issues
- GoのGerrit: https://go.googlesource.com/go/+/refs/heads/master (GoプロジェクトのGerritインスタンス)