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

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

このコミットは、Go言語のcmd/yaccツールにおけるexpr(式)のサンプルプログラムがWindows環境でも動作するように修正するものです。具体的には、実行可能ファイルに拡張子を付与する変更と、キャリッジリターン(\r)文字のサポートを追加する変更が含まれています。

コミット

commit 0b26ba8af340c1ef5de8622c067b31b0d3fc4f1f
Author: ChaiShushan <chaishushan@gmail.com>
Date:   Thu Dec 19 12:14:07 2013 -0500

    cmd/yacc: expr example support windows
    
    1. expr append executable extension.
    2. support '\r' character.
    
    Fixes #6851.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/35330043

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

https://github.com/golang/go/commit/0b26ba8af340c1ef5de8622c067b31b0d3fc4f1f

元コミット内容

cmd/yacc: expr example support windows

  1. expr append executable extension.
  2. support \r character.

Fixes #6851.

R=golang-dev, rsc CC=golang-dev https://golang.org/cl/35330043

変更の背景

このコミットは、Go言語のcmd/yaccツールに含まれるexprというサンプルプログラムが、Windows環境で正しく動作しないという問題に対処するために行われました。

主な問題点は以下の2点です。

  1. 実行可能ファイルの拡張子: Windowsでは実行可能ファイルに.exeのような拡張子が付与されるのが一般的ですが、Makefileで生成されるexpr実行ファイルには拡張子が付いていませんでした。これにより、Windows環境で直接実行しようとすると問題が発生する可能性がありました。
  2. キャリッジリターン(\r)文字の扱い: テキストファイルにおける改行コードは、Unix系システムではラインフィード(\n)のみが使われることが多いですが、Windowsではキャリッジリターンとラインフィードの組み合わせ(\r\n)が一般的です。exprの字句解析器(lexer)が\r文字を適切に処理しないため、Windowsで作成された入力ファイルや、\r\n形式の改行を含む入力に対して正しく動作しない可能性がありました。

これらの問題を解決することで、cmd/yaccexprサンプルがより多くのプラットフォーム、特にWindows環境で期待通りに動作するようになります。

なお、コミットメッセージにある Fixes #6851 については、Goの公開Issueトラッカーでは直接該当するIssueは見つかりませんでした。これは、内部的なトラッキング番号であるか、あるいは非常に古いIssueで現在は参照できない状態である可能性があります。

前提知識の解説

1. cmd/yaccgoyacc

Go言語におけるyaccは、goyaccとして知られるツールを指します。これは、Unix系のシステムで広く使われているyacc(Yet Another Compiler-Compiler)のGo言語版です。yaccは、文法定義ファイル(通常.y拡張子を持つ)を読み込み、その文法に従って入力文字列を解析するためのパーサー(構文解析器)を生成するツールです。

  • パーサー生成: goyaccは、与えられた文法定義からGo言語のソースコード(通常y.goというファイル名)を生成します。この生成されたコードには、LALR(1)パーサーが実装されています。
  • 字句解析器(Lexer)との連携: パーサーは、入力文字列を直接扱うのではなく、字句解析器(lexer、またはスキャナー、トークナイザーとも呼ばれる)から受け取ったトークン(単語のようなもの)のストリームを処理します。字句解析器は、生の入力テキストを意味のあるトークンに分割する役割を担います。goyaccが生成するパーサーは、yyLexerインターフェースを実装したオブジェクトを期待します。
  • expr.y: このコミットで言及されているexpr.yは、cmd/yaccのサンプルとして提供されている、簡単な算術式を解析するための文法定義ファイルです。

2. Makefile

Makefileは、ソフトウェアのビルドプロセスを自動化するためのファイルです。makeコマンドによって解釈され、ソースコードのコンパイル、リンク、テストの実行など、一連のタスクを定義します。

  • ターゲット: Makefile内の各セクションは「ターゲット」と呼ばれ、特定のファイル(例: 実行可能ファイル)やアクション(例: clean)を生成するために必要な依存関係とコマンドを定義します。
  • 変数: Makefileでは変数を定義し、再利用することができます。例えば、$(TARG)のように参照されます。
  • go env GOEXE: Go言語の環境変数GOEXEは、現在のOSにおける実行可能ファイルの拡張子を返します。Windowsでは.exe、Unix系では空文字列となります。この変数をMakefile内で利用することで、クロスプラットフォーム対応のビルドスクリプトを作成できます。

3. 改行コード(\n\r

テキストファイルにおける改行の表現方法は、オペレーティングシステムによって異なります。

  • LF (Line Feed, \n): Unix、Linux、macOS(OS X以降)などで使用されます。単一のバイトで改行を表します。
  • CRLF (Carriage Return + Line Feed, \r\n): Windowsで使用されます。2つのバイトの組み合わせで改行を表します。
  • CR (Carriage Return, \r): 古いMac OSなどで使用されていましたが、現在ではほとんど見られません。

字句解析器が入力ストリームを処理する際、これらの改行コードを適切に認識し、無視したり、特定のトークンとして扱ったりする必要があります。特に、空白文字として無視すべき文字の中に\rが含まれていない場合、Windows環境で問題が発生する可能性があります。

技術的詳細

このコミットは、cmd/yaccディレクトリ内の2つのファイル、Makefileexpr.yに修正を加えています。

src/cmd/yacc/Makefileの変更

変更前は、exprというターゲットが直接実行可能ファイル名を指定していました。

expr: yacc.go expr.y
	go run yacc.go -p expr expr.y
	go build -o expr y.go

clean:
	rm -f y.go y.output expr

変更後は、TARGという変数を導入し、go env GOEXEコマンドの出力を使ってプラットフォームに応じた実行可能ファイルの拡張子を動的に付与するようにしました。

TARG=expr$(shell go env GOEXE)

$(TARG): yacc.go expr.y
	go run yacc.go -p expr expr.y
	go build -o $(TARG) y.go

clean:
	rm -f y.go y.output $(TARG)
  • TARG=expr$(shell go env GOEXE): この行は、TARGという変数に実行可能ファイル名を定義しています。$(shell go env GOEXE)は、go env GOEXEコマンドを実行し、その標準出力を変数に代入します。
    • Windows環境ではgo env GOEXE.exeを返すため、TARGexpr.exeとなります。
    • Unix系環境ではgo env GOEXEは空文字列を返すため、TARGexprとなります。
  • $(TARG): yacc.go expr.y: ビルドターゲットの名前がexprから$(TARG)に変わりました。これにより、go build -o $(TARG) y.goコマンドが、Windowsではgo build -o expr.exe y.goとして、Unix系ではgo build -o expr y.goとして実行されます。
  • cleanターゲットも同様に、削除するファイル名を$(TARG)に変更し、生成された実行可能ファイルを確実に削除できるようにしています。

この変更により、makeコマンドを実行する環境のOSに応じて、適切な名前の実行可能ファイルが生成されるようになり、Windows環境での実行が容易になります。

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

expr.yファイルは、exprパーサーの字句解析器の一部を定義しています。変更前は、空白文字としてスペース( )、タブ(\t)、ラインフィード(\n)のみを無視していました。

		case ' ', '\t', '\n':

変更後は、キャリッジリターン(\r)も空白文字として無視するように追加されました。

		case ' ', '\t', '\n', '\r':
  • func (x *exprLex) Lex(yylval *exprSymType) int: この関数は、exprパーサーの字句解析器の主要な部分です。入力ストリームから次のトークンを読み取り、その種類と値をパーサーに返します。
  • case ' ', '\t', '\n', '\r':: このcase文は、字句解析器が特定の文字を読み込んだときに、それらを無視して次の文字の読み込みに進む(つまり、トークンとして扱わない)ことを示しています。\rが追加されたことで、Windows形式の改行コード(\r\n)が入力に含まれていても、\rが適切にスキップされ、\nのみが改行として処理されるようになります。これにより、Windows環境で作成されたテキストファイルを入力として与えた場合でも、exprパーサーが正しく動作するようになります。

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

src/cmd/yacc/Makefile

--- a/src/cmd/yacc/Makefile
+++ b/src/cmd/yacc/Makefile
@@ -2,9 +2,11 @@
 # Use of this source code is governed by a BSD-style
 # license that can be found in the LICENSE file.
 
-expr: yacc.go expr.y
+TARG=expr$(shell go env GOEXE)
+
+$(TARG): yacc.go expr.y
 	go run yacc.go -p expr expr.y
-\tgo build -o expr y.go
+\tgo build -o $(TARG) y.go
 
 clean:
-\trm -f y.go y.output expr
+\trm -f y.go y.output $(TARG)

src/cmd/yacc/expr.y

--- a/src/cmd/yacc/expr.y
+++ b/src/cmd/yacc/expr.y
@@ -125,7 +125,7 @@ func (x *exprLex) Lex(yylval *exprSymType) int {
 		case '÷':
 			return '/'
 
-		case ' ', '\t', '\n':
+		case ' ', '\t', '\n', '\r':
 		default:
 			log.Printf("unrecognized character %q", c)
 		}

コアとなるコードの解説

Makefileの変更

  • TARG=expr$(shell go env GOEXE): この行は、TARGという変数に、Goの環境変数GOEXE(実行可能ファイルの拡張子)を付加した実行ファイル名を定義します。これにより、Windowsではexpr.exe、Linux/macOSではexprという名前の実行ファイルが生成されるようになります。これは、クロスプラットフォーム対応のビルドにおいて非常に一般的な手法です。
  • $(TARG): yacc.go expr.y: ビルドターゲットの名前を動的に生成される$(TARG)に変更しています。これにより、makeコマンドが実行される環境に応じて、適切な実行ファイル名でビルドが実行されます。
  • go build -o $(TARG) y.go: go buildコマンドの出力ファイル名を$(TARG)にすることで、OSに応じた実行ファイル名でビルドが行われます。
  • rm -f y.go y.output $(TARG): cleanターゲットでも同様に$(TARG)を使用することで、生成された実行ファイルを確実に削除できるようにしています。

expr.yの変更

  • case ' ', '\t', '\n', '\r':: exprLex構造体のLexメソッド内で、字句解析器が空白文字をスキップする部分に\r(キャリッジリターン)が追加されました。これにより、Windows環境で一般的なCRLF(\r\n)形式の改行コードを含む入力に対しても、字句解析器が正しく動作し、\rを無視して\nのみを改行として認識するようになります。これは、異なるOS間でテキストファイルをやり取りする際に発生しがちな互換性の問題を解決するために重要です。

これらの変更は、Goのツールが異なるオペレーティングシステム間でより一貫した動作を提供するための、細かではあるが重要な改善です。

関連リンク

参考にした情報源リンク

  • Go言語のcmd/yaccに関するWeb検索結果
  • Go言語のIssueトラッカー(#6851の検索結果)
  • Makefileの一般的な構文とshell関数
  • 改行コード(LF, CRLF, CR)に関する一般的な知識