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

[インデックス 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.txtpkg 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 の変更

  1. 新しいエラーコード ErrUnexpectedParen の定義: const ErrUnexpectedParen ErrorCode = "unexpected )" この行は、正規表現のパース中に予期せぬ閉じ括弧が見つかった場合に返される新しいエラーコードを定義しています。これにより、以前の汎用的なErrInternalErrorよりも具体的なエラーメッセージを提供できるようになります。

  2. 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言語の公式ドキュメント: https://golang.org/pkg/regexp/syntax/
  • 正規表現の基本概念に関する一般的な情報源 (例: Wikipedia, MDN Web Docsなど)
  • Go言語のエラーハンドリングに関する一般的な情報源