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

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

このコミットは、Go言語のcmd/yaccツールにおけるサンプルコードの変更に関するものです。具体的には、既存のunits(単位変換)の例を、よりシンプルでライセンス上の懸念がないexpr(式評価)の例に置き換えることを目的としています。

コミット

commit fdaf88ea5b7a3bc937e68875d55b5741ecb2f092
Author: Ian Lance Taylor <iant@golang.org>
Date:   Wed Sep 11 09:01:47 2013 -0700

    cmd/yacc: replace units example with simpler expr example

    The units example is nice but is covered by the Lucent
    license, which may be a concern for some people making a
    commercial source code distribution of Go.

    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/13283045

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

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

元コミット内容

cmd/yacc: replace units example with simpler expr example

unitsの例は良いものだが、Lucentライセンスにカバーされており、Goの商用ソースコード配布を行う一部の人々にとっては懸念事項となる可能性がある。

変更の背景

このコミットの主な背景は、Go言語のcmd/yaccツールに含まれていたunits(単位変換)のサンプルコードが、Lucent Public License (LPL) の下でライセンスされていたことにあります。

Lucent Public Licenseは、オープンソースライセンスの一つですが、その条項が一部の商用利用シナリオにおいて懸念を引き起こす可能性がありました。特に、Go言語の公式配布物の一部として含まれる場合、Go自体がBSDライセンスという非常に寛容なライセンスを採用しているため、より制約の厳しいLPLのコードが含まれることは、Goの商用利用を検討する開発者や企業にとって法的な複雑さや不確実性をもたらす可能性がありました。

Goプロジェクトは、そのエコシステム全体で可能な限り寛容なライセンス(BSDライセンスなど)を採用し、開発者がGoを自由に利用・配布できることを重視しています。このような背景から、ライセンス上の懸念を排除し、より広範な利用を促進するために、LPLでライセンスされたunitsの例を、よりシンプルでライセンス上の問題がないexprの例に置き換えることが決定されました。

前提知識の解説

Go言語のcmd/yacc (goyacc)

cmd/yaccは、Go言語で書かれたYacc(Yet Another Compiler Compiler)ツールです。Yaccは、文法定義から構文解析器(パーサー)を自動生成するためのツールであり、コンパイラやインタプリタのフロントエンド開発において広く利用されます。

goyaccは、C言語のYaccと同様に、BNF(Backus-Naur Form)に似た文法定義ファイル(通常.y拡張子)を読み込み、Go言語のソースコードとしてパーサーを生成します。生成されたパーサーは、字句解析器(レクサー)からのトークンストリームを受け取り、文法規則に従って構文木を構築したり、直接アクションを実行したりします。

goyaccの主な特徴は以下の通りです:

  • Go言語への統合: 生成されるコードはGo言語であり、Goの標準ライブラリや慣習に沿っています。
  • 再入可能性: 生成されるパーサーはデフォルトで再入可能です。これは、複数のゴルーチンから同時にパーサーを使用できることを意味し、並行処理が重要なGoアプリケーションに適しています。
  • 字句解析器との連携: ユーザーはLexメソッドとErrorメソッドを持つインターフェースを実装した字句解析器を提供する必要があります。

Lucent Public License (LPL)

Lucent Public License (LPL) は、Bell Labs (旧Lucent Technologies) が開発したオープンソースライセンスです。LPLは、フリーソフトウェア財団(FSF)によってフリーソフトウェアライセンスとして認識されていますが、GNU General Public License (GPL) のようなコピーレフトライセンスとは異なり、より寛容な性質を持っています。

LPLの主な特徴は以下の通りです:

  • ソースコードの公開: LPLの下で配布されるソフトウェアの派生版を配布する場合、そのソースコードを公開する必要があります。
  • 特許条項: LPLには特許に関する条項が含まれており、ライセンスされたソフトウェアを使用、複製、配布、変更する者に対して、関連する特許の非独占的、ロイヤリティフリーのライセンスを付与します。
  • 互換性: 他のオープンソースライセンス、特にGPLとの互換性について議論されることがあります。LPLはGPLと直接互換性がないと見なされることがあり、LPLとGPLのコードを組み合わせたプロジェクトの配布には注意が必要です。

Goプロジェクトのような大規模なオープンソースプロジェクトでは、ライセンスの複雑さを避け、開発者が安心してコードを利用できるように、よりシンプルで広く受け入れられているライセンス(例: BSD, MIT, Apache 2.0)を好む傾向があります。LPLの特許条項やGPLとの互換性の問題は、Goの商用利用を検討する際に潜在的な法的リスクと見なされる可能性があったため、このコミットでunitsの例が削除されることになりました。

math/bigパッケージ

Go言語の標準ライブラリに含まれるmath/bigパッケージは、任意精度の算術演算を提供します。これは、標準のintfloat64型では表現できない非常に大きな整数、有理数、または浮動小数点数を扱う必要がある場合に非常に有用です。

expr.yの例では、big.Rat型が使用されています。

  • big.Rat: 有理数を表現するための型で、分子と分母を任意精度の整数で保持します。これにより、浮動小数点演算で発生する可能性のある精度誤差を回避し、正確な分数計算が可能です。

expr.ybig.Ratを使用しているのは、シンプルな式評価において、特に除算を含む場合に正確な結果を保証するためです。例えば、1/3のような計算は浮動小数点数では循環小数になりますが、big.Ratでは1/3として正確に表現できます。

技術的詳細

このコミットは、goyaccのサンプルコードを置き換えることで、ライセンス上の懸念を解消し、よりシンプルで教育的な例を提供します。

変更の具体的な内容:

  1. units例の削除:

    • src/cmd/yacc/units.y: 単位変換パーサーのYacc文法定義ファイルが削除されました。
    • src/cmd/yacc/units.txt: unitsパーサーが使用する単位定義データファイルが削除されました。
    • これらのファイルは、Plan 9のunitsツールから派生したもので、Lucent Public Licenseの下でライセンスされていました。
  2. expr例の追加:

    • src/cmd/yacc/expr.y: 新しいシンプルな式評価パーサーのYacc文法定義ファイルが追加されました。
    • このexpr.yは、四則演算(加算、減算、乗算、除算)と括弧をサポートする基本的な電卓のような機能を提供します。
    • 数値の表現には、Goのmath/bigパッケージのbig.Rat型を使用しており、これにより任意精度の有理数計算が可能となり、浮動小数点数の精度問題を回避しています。
    • 字句解析器(lexer)は、数字、演算子、括弧を認識し、Unicodeの乗算記号(×)と除算記号(÷)もサポートしています。
  3. ビルドシステムの更新:

    • src/cmd/yacc/Makefile: ビルドターゲットがunitsからexprに変更されました。これにより、goyaccツールを使ってexpr.yからパーサーを生成し、実行可能ファイルexprをビルドするようになります。
  4. ドキュメントの更新:

    • src/cmd/yacc/doc.go: cmd/yaccツールのドキュメントが更新され、units.yの代わりに新しく追加されたexpr.yが例として参照されるようになりました。

expr.yの技術的詳細:

  • Yacc文法:

    • %union: Yaccのセマンティック値(構文解析中にノードに関連付けられる値)の型を定義します。ここではnum *big.Ratが定義されており、数値はbig.Ratのポインタとして扱われます。
    • %type: 非終端記号(expr, expr1, expr2, expr3)のセマンティック値の型をnum*big.Rat)として指定します。
    • %token: 終端記号(トークン)の型を定義します。NUMトークンはnum型、その他の演算子や括弧は文字そのものがトークン値となります。
    • 文法規則: 算術式の優先順位と結合規則に従って定義されています。
      • top: 最上位の規則で、最終的な結果を出力します。
      • expr, expr1, expr2, expr3: 演算子の優先順位(+, - < *, /)と結合性(左結合)を表現するために階層的に定義されています。
      • NUM: 数値リテラル。
      • '(' expr ')': 括弧によるグループ化。
    • アクション: 各文法規則には、Go言語のコードブロック(アクション)が関連付けられています。これらのアクションは、big.Ratのメソッド(Add, Sub, Mul, Quo, Neg)を呼び出して計算を実行し、結果を$$(現在の規則のセマンティック値)に代入します。
  • 字句解析器 (exprLex):

    • Lexメソッド: goyaccが生成するパーサーが呼び出す字句解析器の主要なメソッドです。入力ストリームから次のトークンを読み取り、その型とセマンティック値をyylvalに設定して返します。
    • numメソッド: 数字列を解析し、big.Ratオブジェクトに変換します。小数点や指数表記(e, E)もサポートしています。
    • nextメソッド: 入力ストリームから次のUnicodeルーンを読み取ります。
    • Errorメソッド: 字句解析エラーが発生した場合に呼び出されます。
  • main関数:

    • 標準入力から1行ずつ式を読み込み、exprParse関数(goyaccによって生成されるパーサーのエントリポイント)を呼び出して解析・評価を行います。
    • 結果は標準出力に表示されます。big.Ratが整数であれば整数として、そうでなければ分数として出力されます。

この変更により、cmd/yaccのサンプルは、よりシンプルで、Goのmath/bigパッケージの利用例としても機能する、教育的なものとなりました。

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

このコミットにおけるコアとなるコードの変更は、主に以下のファイルの追加と削除、そして既存ファイルの修正に集約されます。

  1. src/cmd/yacc/Makefile:

    --- a/src/cmd/yacc/Makefile
    +++ b/src/cmd/yacc/Makefile
    @@ -2,9 +2,9 @@
     # Use of this source code is governed by a BSD-style
     # license that can be found in the LICENSE file.
    
    -units: yacc.go units.y
    -	go run yacc.go -p units_ units.y
    -	go build -o units y.go
    +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 units
    +	rm -f y.go y.output expr
    
    • unitsターゲットをexprターゲットに置き換え、ビルド対象のYacc文法ファイルと出力ファイル名を変更。
  2. src/cmd/yacc/doc.go:

    --- a/src/cmd/yacc/doc.go
    +++ b/src/cmd/yacc/doc.go
    @@ -20,10 +20,8 @@ written in C and documented at
     Adepts of the original yacc will have no trouble adapting to this
     form of the tool.
    
    -The file units.y in this directory is a yacc grammar for a version of
    -the Unix tool units, also written in Go and largely transliterated
    -from the Plan 9 C version. It needs the flag "-p units_" (see
    -below).
    +The file expr.y in this directory is a yacc grammar for a very simple
    +expression parser.  It needs the flag "-p expr" (see below).
    
     The generated parser is reentrant. Parse expects to be given an
     argument that conforms to the following interface:
    
    • ドキュメント内のunits.yへの参照をexpr.yに更新。
  3. src/cmd/yacc/expr.y: (新規追加ファイル、全205行)

    • 新しい式評価パーサーのYacc文法定義とGo言語の字句解析器・メイン関数を含む。
    • math/bigパッケージのbig.Rat型を使用して任意精度の有理数計算を実装。
    • 四則演算、括弧、Unicode演算子(×, ÷)をサポート。
  4. src/cmd/yacc/units.txt: (削除ファイル、全576行)

    • 既存のunits例で使用されていた単位定義データファイル。
  5. src/cmd/yacc/units.y: (削除ファイル、全768行)

    • 既存のunits例のYacc文法定義ファイル。

コアとなるコードの解説

このコミットの核心は、units例の削除とexpr例の追加にあります。

units例の削除 (units.y, units.txt): これらのファイルは、UnixのunitsツールをGoで再実装したもので、様々な単位間の変換を行うパーサーでした。しかし、そのコードがLucent Public Licenseの下でライセンスされていたため、Goの商用利用におけるライセンス上の懸念を避けるために削除されました。これは、Goプロジェクトがライセンスのシンプルさと寛容性を重視していることの表れです。

expr例の追加 (expr.y): このファイルは、新しく追加されたシンプルな式評価パーサーの例です。

  • 文法定義: Yaccの文法定義セクションでは、算術式の構文が定義されています。expr, expr1, expr2, expr3といった非終端記号を階層的に定義することで、演算子の優先順位(乗除算が加減算より優先)と結合性(左結合)が表現されています。例えば、expr1: expr1 '+' expr2は加算が左結合であることを示し、expr2: expr2 '*' expr3は乗算が左結合であることを示します。
  • セマンティックアクション: 各文法規則にはGo言語のコードブロックが埋め込まれており、これがセマンティックアクションです。これらのアクションは、構文解析中に実行され、式の評価を行います。特に注目すべきは、math/bigパッケージのbig.Rat型を使用している点です。
    • %union { num *big.Rat }: これにより、パーサーのスタックに*big.Rat型の値を保持できるようになります。
    • $$ = $1.Add($1, $3)のような行は、big.RatAddメソッドを呼び出して加算を行い、その結果を現在の規則のセマンティック値($$)に代入しています。big.Ratを使用することで、1/3のような割り切れない計算でも正確な有理数として結果を保持できます。
  • 字句解析器 (exprLex): expr.yのGo言語セクションには、字句解析器exprLexの実装が含まれています。
    • Lexメソッドは、入力ストリームから文字を読み込み、トークン(数値、演算子、括弧など)に変換します。
    • numメソッドは、数字列を解析し、big.Ratオブジェクトに変換します。これにより、整数だけでなく、浮動小数点表記の数値も正確に有理数として扱えます。
    • Unicodeの乗算記号(×)と除算記号(÷)を認識し、それぞれASCIIの*/としてパーサーに渡すことで、より柔軟な入力に対応しています。
  • main関数: 標準入力から式を読み込み、exprParsegoyaccが生成するパーサーのエントリポイント)を呼び出して解析と評価を実行します。結果はbig.RatIsInt()メソッドで整数かどうかを判定し、適切な形式で出力されます。

このexpr.yの例は、goyaccの基本的な使い方、Yacc文法の構造、セマンティックアクションによる値の計算、そしてGoのmath/bigパッケージの強力な機能を示す、優れた教育的サンプルとなっています。

関連リンク

参考にした情報源リンク