[インデックス 13063] ファイルの概要
このコミットは、Go言語の標準ライブラリであるregexp/syntax
パッケージにおける正規表現のパース処理に関する改善です。具体的には、不均衡な閉じ括弧()
)が正規表現文字列中に存在した場合に発生していた「内部エラー」を、より具体的で分かりやすい「予期せぬ閉じ括弧エラー」に置き換える変更が行われました。
変更されたファイルは以下の通りです。
api/go1.txt
: Go 1のAPI変更を記録するファイルで、新しいエラーコードErrUnexpectedParen
が追加されたことを示します。src/pkg/regexp/syntax/parse.go
: 正規表現の構文解析を行う主要なファイルです。ここでエラーハンドリングロジックが修正されました。src/pkg/regexp/syntax/parse_test.go
:regexp/syntax
パッケージのテストファイルです。新しいエラーケースを検証するためのテストが追加されました。
コミット
- コミットハッシュ:
bd13f6ff8eea412885d6a22a284a259edda57980
- 作者: Jan Mercl befelemepeseveze@gmail.com
- 日付: Mon May 14 11:50:25 2012 -0700
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bd13f6ff8eea412885d6a22a284a259edda57980
元コミット内容
regexp/syntax: replace internal error on unexpected ) w/ ErrUnexpectedParen
Unbalanced extra right parenthesis produced an internal error instead of
a more descriptive one.
Fixes #3406.
R=r, rsc
CC=golang-dev
https://golang.org/cl/6201063
変更の背景
この変更は、Go言語の正規表現パーサーが、予期せぬ閉じ括弧()
)を検出した際に、ユーザーにとって理解しにくい「内部エラー (internal error)」を返していた問題を解決するために行われました。元の実装では、正規表現の構文解析中に閉じ括弧のバランスが崩れた場合、パーサーが予期しない状態に陥り、一般的なエラーではなく、開発者向けのデバッグ情報に近い「内部エラー」を出力していました。
これは、ユーザーが正規表現の記述ミスをした際に、何が問題なのかを正確に把握できないというユーザビリティ上の課題がありました。コミットメッセージにある Fixes #3406
は、この問題がGoのIssueトラッカーで報告されていたことを示しています。より具体的で分かりやすいエラーメッセージを提供することで、ユーザーは自身の正規表現の誤りを迅速に特定し、修正できるようになります。
前提知識の解説
正規表現 (Regular Expression)
正規表現は、文字列のパターンを記述するための強力なツールです。特定の文字の並び、繰り返し、選択肢などを簡潔に表現でき、テキスト検索、置換、検証などに広く利用されます。正規表現は、その構文が厳密に定義されており、例えば括弧()
はグループ化やキャプチャのために使用され、開いた括弧と閉じた括弧は常にペアである必要があります。
Go言語の regexp/syntax
パッケージ
Go言語の標準ライブラリには、正規表現を扱うためのregexp
パッケージがあります。このパッケージは、内部的にregexp/syntax
パッケージを利用して正規表現文字列の構文解析(パース)を行っています。regexp/syntax
パッケージは、正規表現の文字列を抽象構文木(AST)のような内部表現に変換する役割を担っており、この過程で構文エラーを検出します。
エラーハンドリングにおける「内部エラー」と「記述的なエラー」
- 内部エラー (Internal Error): プログラムの予期せぬ状態、論理的な矛盾、または開発者が想定していなかったシナリオで発生するエラーです。通常、ユーザーには直接関係のない、プログラム内部の不整合を示すものであり、デバッグ目的で利用されます。ユーザーに表示されるべきではありません。
- 記述的なエラー (Descriptive Error): ユーザーの入力ミス、環境の問題、外部リソースの利用不可など、ユーザーが理解し、対処できる可能性のある問題を示すエラーです。何が問題で、どのように修正すればよいかを示す具体的なメッセージを含みます。
このコミットの目的は、内部エラーとして扱われていたユーザー起因の構文エラーを、記述的なエラーに昇格させることにあります。
技術的詳細
この変更の核心は、regexp/syntax
パッケージが正規表現のパース中に遭遇する「不均衡な閉じ括弧」の扱いを改善することです。
正規表現のパースは、通常、状態機械や再帰下降パーサーのような手法を用いて行われます。括弧のバランスチェックは、スタックを用いて行われることが一般的です。開き括弧が見つかるとスタックにプッシュされ、閉じ括弧が見つかるとスタックから対応する開き括弧がポップされます。閉じ括弧が見つかった際にスタックが空である、または対応する開き括弧がない場合、それは構文エラーとなります。
以前の実装では、parseRightParen
関数内で、閉じ括弧が予期せぬ状況で現れた場合(例えば、スタックが空であるにもかかわらず閉じ括弧が現れた場合や、スタックのトップが開き括弧ではない場合)に、ErrInternalError
を返していました。これは、パーサーが自身の内部状態の整合性が崩れたと判断していたためです。
今回の変更では、ErrUnexpectedParen
という新しいエラーコードが導入されました。そして、parseRightParen
関数内でErrInternalError
を返していた箇所をErrUnexpectedParen
に置き換えました。これにより、パーサーが「予期せぬ閉じ括弧」というユーザーが理解しやすい具体的なエラーを返すようになります。
また、api/go1.txt
にpkg regexp/syntax, const ErrUnexpectedParen ErrorCode
が追加されたことは、この新しいエラーコードがGo 1の公開APIの一部として正式に利用可能になったことを意味します。これは、Goの互換性保証の原則に従い、APIの変更が明示的に記録されることを示しています。
parse_test.go
に追加されたテストケースは、a)
, (a))
, a|b|)
, (a|b))
, [a-z)
, ([a-z]))
のような、不均衡な閉じ括弧を含む正規表現が正しくErrUnexpectedParen
を返すことを検証しています。これにより、変更が意図通りに機能し、回帰バグがないことが保証されます。
コアとなるコードの変更箇所
api/go1.txt
--- a/api/go1.txt
+++ b/api/go1.txt
@@ -5484,6 +5484,7 @@ pkg regexp/syntax, const ErrMissingBracket ErrorCode
pkg regexp/syntax, const ErrMissingParen ErrorCode
pkg regexp/syntax, const ErrMissingRepeatArgument ErrorCode
pkg regexp/syntax, const ErrTrailingBackslash ErrorCode
+pkg regexp/syntax, const ErrUnexpectedParen ErrorCode
pkg regexp/syntax, const FoldCase Flags
pkg regexp/syntax, const InstAlt InstOp
pkg regexp/syntax, const InstAltMatch InstOp
src/pkg/regexp/syntax/parse.go
--- a/src/pkg/regexp/syntax/parse.go
+++ b/src/pkg/regexp/syntax/parse.go
@@ -46,6 +46,7 @@ const (
ErrMissingParen ErrorCode = "missing closing )"
ErrMissingRepeatArgument ErrorCode = "missing argument to repetition operator"
ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression"
+ ErrUnexpectedParen ErrorCode = "unexpected )"
)
func (e ErrorCode) String() string {
@@ -1168,13 +1169,13 @@ func (p *parser) parseRightParen() error {
n := len(p.stack)
if n < 2 {
- return &Error{ErrInternalError, ""}
+ return &Error{ErrUnexpectedParen, p.wholeRegexp}
}
re1 := p.stack[n-1]
re2 := p.stack[n-2]
p.stack = p.stack[:n-2]
if re2.Op != opLeftParen {
- return &Error{ErrMissingParen, p.wholeRegexp}
+ return &Error{ErrUnexpectedParen, p.wholeRegexp}
}
// Restore flags at time of paren.
p.flags = re2.Flags
src/pkg/regexp/syntax/parse_test.go
--- a/src/pkg/regexp/syntax/parse_test.go
+++ b/src/pkg/regexp/syntax/parse_test.go
@@ -441,10 +441,18 @@ var invalidRegexps = []string{
`(`,
`)`,
`(a`,
+ `a)`,
+ `(a))`,
`(a|b|`,
+ `a|b|)`,
+ `(a|b))`,
`(a|b`,
+ `a|b)`,
+ `(a|b))`,
`[a-z`,
`([a-z)`,
+ `[a-z)`,
+ `([a-z]))`,
`x{1001}`,
`x{9876543210}`,
`x{2,1}`,
コアとなるコードの解説
src/pkg/regexp/syntax/parse.go
の変更
-
新しいエラーコード
ErrUnexpectedParen
の定義:const ErrUnexpectedParen ErrorCode = "unexpected )"
この行は、正規表現のパース中に予期せぬ閉じ括弧が見つかった場合に返される新しいエラーコードを定義しています。これにより、以前の汎用的なErrInternalError
よりも具体的なエラーメッセージを提供できるようになります。 -
parseRightParen
関数のエラーハンドリングの修正:parseRightParen
関数は、正規表現の閉じ括弧()
)を処理する際に呼び出されます。この関数内で、以下の2つの箇所でエラーの型が変更されました。-
スタックが空または要素が少ない場合:
if n < 2 { return &Error{ErrUnexpectedParen, p.wholeRegexp} }
以前は
ErrInternalError
を返していましたが、閉じ括弧を処理しようとした際にスタックに十分な要素がない(つまり、対応する開き括弧がない)場合、それは「予期せぬ閉じ括弧」であると判断し、ErrUnexpectedParen
を返すように変更されました。p.wholeRegexp
は、エラーが発生した正規表現全体をエラー情報に含めることで、デバッグを容易にします。 -
スタックのトップが開き括弧ではない場合:
if re2.Op != opLeftParen { return &Error{ErrUnexpectedParen, p.wholeRegexp} }
閉じ括弧を処理する際、スタックのトップに対応する開き括弧(
opLeftParen
)があることを期待します。もしそうでない場合(例えば、(
以外の何らかの正規表現要素がスタックのトップにある場合)、これもまた「予期せぬ閉じ括弧」の状況であるため、ErrMissingParen
ではなくErrUnexpectedParen
を返すように修正されました。ErrMissingParen
は「閉じ括弧がない」ことを意味しますが、ここでは「閉じ括弧が予期せぬ場所にある」というニュアンスがより適切です。
-
api/go1.txt
の変更
pkg regexp/syntax, const ErrUnexpectedParen ErrorCode
の追加は、Go 1のAPI互換性ポリシーに従い、新しい公開API要素が導入されたことを公式に記録するものです。これにより、このエラーコードがGo 1の安定したAPIの一部として利用可能であることが保証されます。
src/pkg/regexp/syntax/parse_test.go
の変更
invalidRegexps
スライスに、a)
, (a))
, a|b|)
, (a|b))
, a|b)
, (a|b))
, [a-z)
, ([a-z]))
といった新しいテストケースが追加されました。これらの正規表現はすべて、不均衡な閉じ括弧を含んでいます。これらのテストケースが追加されたことで、parse.go
の変更が正しく機能し、これらの不正な正規表現に対してErrUnexpectedParen
が返されることが検証されます。これにより、コードの品質と堅牢性が向上します。
関連リンク
- Go Issue #3406: regexp/syntax: internal error on unexpected ) (このコミットが修正したIssue)
- Go CL 6201063: regexp/syntax: replace internal error on unexpected ) w/ ErrUnexpectedParen (このコミットに対応するGerrit Change-Id)
参考にした情報源リンク
- Go言語の公式ドキュメント: https://golang.org/pkg/regexp/syntax/
- 正規表現の基本概念に関する一般的な情報源 (例: Wikipedia, MDN Web Docsなど)
- Go言語のエラーハンドリングに関する一般的な情報源