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

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

このコミットは、Go言語の標準ライブラリであるregexp/syntaxパッケージにおいて、正規表現のパース時に発生する「予期せぬ括弧」のエラーコードErrUnexpectedParenをエクスポート(公開)する変更です。これにより、この特定のエラーを外部から識別し、より詳細なエラーハンドリングが可能になります。

コミット

commit 1b46e4cd9ac75db790e75ee801a6e805b6f4ec20
Author: Brian Ketelsen <bketelsen@gmail.com>
Date:   Tue Dec 11 12:02:14 2012 -0500

    regexp/syntax: export ErrUnexpectedParen
    
    Fixes #3712
    
    R=golang-dev, dave, rsc
    CC=golang-dev
    https://golang.org/cl/6902069

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

https://github.com/golang/go/commit/1b46e4cd9ac75db790e75ee801a6e805b6f4ec20

元コミット内容

このコミットは、src/pkg/regexp/syntax/parse.goファイルにおいて、正規表現の構文解析中に発生するエラーの一つである「予期せぬ括弧」を示すエラーコードerrUnexpectedParenを、パッケージ外部から参照可能なErrUnexpectedParenとしてエクスポートするように変更しています。具体的には、既存の非公開定数errUnexpectedParenを削除し、公開定数ErrUnexpectedParenとして再定義し、その使用箇所を更新しています。

変更の背景

この変更の背景には、Go言語のエラーハンドリングの改善と、正規表現パーサーのAPIの使いやすさの向上が挙げられます。コミットメッセージにあるFixes #3712は、GoのIssueトラッカーにおけるIssue 3712を解決することを示しています。このIssueでは、正規表現のパースエラーをより詳細に区別できるように、特定のエラーコードをエクスポートする要望があったと考えられます。

以前はerrUnexpectedParenという非公開(unexported)の定数として定義されていたため、regexp/syntaxパッケージの外部からはこの特定のエラーを直接識別することができませんでした。例えば、正規表現のパースに失敗した場合、呼び出し元は一般的なエラー情報しか得られず、「なぜパースに失敗したのか」という具体的な原因(例:括弧の不一致)をプログラム的に判断するのが困難でした。

このコミットによってErrUnexpectedParenがエクスポートされることで、開発者は正規表現のパースエラーが発生した際に、それが「予期せぬ括弧」によるものであるかを明確にチェックできるようになります。これにより、よりきめ細やかなエラーメッセージの表示や、エラーの種類に応じた異なる処理ロジックの実装が可能となり、アプリケーションの堅牢性とユーザーエクスペリエンスが向上します。また、コード内のコメント// TODO: Export for Go 1.1.から、このエクスポートがGo 1.1リリースに向けて計画されていたAPI改善の一部であったことが伺えます。

前提知識の解説

Go言語におけるエクスポート(公開)と非エクスポート(非公開)

Go言語では、識別子(変数、関数、型、定数など)の可視性(スコープ)は、その名前の最初の文字が大文字か小文字かによって決まります。

  • 大文字で始まる識別子: パッケージ外部からアクセス可能です。これを「エクスポートされた(exported)」識別子と呼びます。
  • 小文字で始まる識別子: その識別子が定義されているパッケージ内でのみアクセス可能です。これを「非エクスポートされた(unexported)」識別子と呼びます。

このコミットでは、errUnexpectedParen(非エクスポート)をErrUnexpectedParen(エクスポート)に変更することで、このエラーコードがregexp/syntaxパッケージの外部からも利用できるようになります。

Go言語のエラーハンドリング

Go言語では、エラーは組み込みのerrorインターフェースによって表現されます。関数がエラーを返す場合、通常は戻り値の最後の要素としてerror型を返します。

func SomeFunction() (ResultType, error) {
    // ... 処理 ...
    if someErrorCondition {
        return nil, errors.New("something went wrong")
    }
    return someResult, nil
}

呼び出し元は、返されたerrornilでないかどうかをチェックすることで、エラーが発生したかどうかを判断します。

result, err := SomeFunction()
if err != nil {
    // エラー処理
}

特定のエラーの種類を識別するためには、エラー値を型アサーションしたり、エラー値が特定の定数と等しいかを比較したりします。このコミットのようにエラーコードがエクスポートされることで、後者の方法で特定のエラーを識別することが容易になります。

正規表現とregexp/syntaxパッケージ

正規表現は、文字列のパターンを記述するための強力なツールです。Go言語には、正規表現を扱うための標準ライブラリregexpパッケージがあります。 regexpパッケージは、内部的にregexp/syntaxパッケージを利用して正規表現のパターンを解析(パース)し、抽象構文木(AST)を構築します。このASTは、正規表現エンジンがパターンを効率的にマッチングするためにコンパイルされます。 regexp/syntaxパッケージは、正規表現の構文規則に従ってパターンを解析する役割を担っており、構文エラー(例:括弧の不一致、無効なエスケープシーケンスなど)が発生した場合には、適切なエラーを返します。

ErrorCode

regexp/syntaxパッケージ内で定義されているErrorCode型は、特定の正規表現パースエラーを示すためのカスタム文字列型です。

type ErrorCode string

この型は、エラーの種類を識別するための定数として使用されます。例えば、ErrMissingParenは閉じ括弧がないエラーを、ErrMissingRepeatArgumentは繰り返し演算子の引数がないエラーを示します。

技術的詳細

このコミットの技術的な核心は、Go言語の可視性ルール(エクスポート/非エクスポート)を利用して、特定のエラー定数を公開APIの一部とすることにあります。

変更前は、parse.goファイル内でerrUnexpectedParenという小文字で始まる定数が定義されていました。

// TODO: Export for Go 1.1.
const errUnexpectedParen ErrorCode = "unexpected )"

この定数は、同じパッケージ内のparseRightParen関数でのみ使用されていました。

// 変更前
if n < 2 {
    return &Error{errUnexpectedParen, p.wholeRegexp}
}
// ...
if re2.Op != opLeftParen {
    return &Error{errUnexpectedParen, p.wholeRegexp}
}

この状態では、regexp/syntaxパッケージの外部からerrUnexpectedParenという具体的なエラーコードを参照することはできませんでした。外部のコードが正規表現のパースエラーを捕捉した場合、それが「予期せぬ括弧」によるものかどうかを直接判断する手段がありませんでした。

このコミットでは、以下の変更が行われました。

  1. parse.goの定数ブロックに、大文字で始まるErrUnexpectedParenが追加されました。
    const (
        ErrMissingParen          ErrorCode = "missing closing )"
        ErrMissingRepeatArgument ErrorCode = "missing argument to repetition operator"
        ErrTrailingBackslash     ErrorCode = "trailing backslash at end of expression"
        ErrUnexpectedParen       ErrorCode = "unexpected )" // ここが追加
    )
    
  2. 既存の非公開定数errUnexpectedParenの定義と、関連するTODOコメントが削除されました。
  3. parseRightParen関数内でerrUnexpectedParenが使用されていた箇所が、新しくエクスポートされたErrUnexpectedParenに置き換えられました。
    // 変更後
    if n < 2 {
        return &Error{ErrUnexpectedParen, p.wholeRegexp} // errUnexpectedParen -> ErrUnexpectedParen
    }
    // ...
    if re2.Op != opLeftParen {
        return &Error{ErrUnexpectedParen, p.wholeRegexp} // errUnexpectedParen -> ErrUnexpectedParen
    }
    

この変更により、regexp/syntaxパッケージを利用する開発者は、正規表現のパースエラーが返された際に、以下のように特定のエラーをチェックできるようになります。

import (
    "regexp/syntax"
    "fmt"
)

func main() {
    _, err := syntax.Parse("abc)", syntax.Perl) // 予期せぬ閉じ括弧
    if err != nil {
        if parseErr, ok := err.(*syntax.Error); ok {
            if parseErr.Code == syntax.ErrUnexpectedParen {
                fmt.Println("エラー: 予期せぬ閉じ括弧です。")
            } else {
                fmt.Printf("その他のパースエラー: %v\n", parseErr.Code)
            }
        } else {
            fmt.Printf("不明なエラー: %v\n", err)
        }
    }
}

このように、エラーコードをエクスポートすることは、ライブラリの利用者にとってより詳細なエラーハンドリングを可能にし、APIの使いやすさと堅牢性を向上させる重要な変更です。

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

変更はsrc/pkg/regexp/syntax/parse.goファイルに集中しています。

--- a/src/pkg/regexp/syntax/parse.go
+++ b/src/pkg/regexp/syntax/parse.go
@@ -42,11 +42,9 @@ const (
 	ErrMissingParen          ErrorCode = "missing closing )"
 	ErrMissingRepeatArgument ErrorCode = "missing argument to repetition operator"
 	ErrTrailingBackslash     ErrorCode = "trailing backslash at end of expression"
+	ErrUnexpectedParen       ErrorCode = "unexpected )"
 )
 
-// TODO: Export for Go 1.1.
-const errUnexpectedParen ErrorCode = "unexpected )"
-
 func (e ErrorCode) String() string {
 	return string(e)
 }
@@ -1167,13 +1165,13 @@ func (p *parser) parseRightParen() error {
 
 	n := len(p.stack)
 	if n < 2 {
-		return &Error{errUnexpectedParen, p.wholeRegexp}
+		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{errUnexpectedParen, p.wholeRegexp}
+		return &Error{ErrUnexpectedParen, p.wholeRegexp}
 	}
 	// Restore flags at time of paren.
 	p.flags = re2.Flags

コアとなるコードの解説

上記の差分は、以下の3つの主要な変更点を示しています。

  1. ErrUnexpectedParenの追加とerrUnexpectedParenの削除:

    • constブロック内にErrUnexpectedParen ErrorCode = "unexpected )"が新しく追加されています。これは、Go言語の命名規則に従い、大文字で始まることでパッケージ外部に公開される定数となります。
    • その直下にあった// TODO: Export for Go 1.1.というコメントと、非公開定数const errUnexpectedParen ErrorCode = "unexpected )"の定義が削除されています。これは、エクスポートの目的が達成されたため、古い非公開定数が不要になったことを意味します。
  2. parseRightParen関数内の参照の更新:

    • parseRightParen関数は、正規表現の右括弧)をパースする際に呼び出されるメソッドです。この関数内で、予期せぬ右括弧が検出された場合にエラーを返すロジックがあります。
    • 変更前は、return &Error{errUnexpectedParen, p.wholeRegexp}のように非公開のerrUnexpectedParenを参照していました。
    • 変更後は、return &Error{ErrUnexpectedParen, p.wholeRegexp}のように、新しくエクスポートされたErrUnexpectedParenを参照するように修正されています。この変更は、関数内の2箇所で行われています。

これらの変更により、regexp/syntaxパッケージの利用者は、正規表現のパース時に「予期せぬ括弧」のエラーが発生した場合、そのエラーがsyntax.ErrUnexpectedParenであるかを直接比較して判断できるようになりました。これは、ライブラリのAPIをより使いやすく、エラーハンドリングをより詳細に行えるようにするための重要な改善です。

関連リンク

参考にした情報源リンク