[インデックス 13937] ファイルの概要
このコミットは、Go言語の標準ライブラリである regexp/syntax
パッケージに、正規表現の構文定義を詳細に記述した doc.go
ファイルを追加するものです。これにより、regexp/syntax
パッケージが解釈する正規表現の具体的なルールが明確化され、開発者が正規表現を記述する際の参照点となります。
コミット
commit c7e0b8baa69825d69a4abc6b7b22cf08fca5ae63
Author: Russ Cox <rsc@golang.org>
Date: Mon Sep 24 15:33:53 2012 -0400
regexp/syntax: define accepted syntax
Update #3953.
R=bradfitz, campoy
CC=golang-dev
https://golang.org/cl/6543068
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c7e0b8baa69825d69a4abc6b7b22cf08fca5ae63
元コミット内容
regexp/syntax: define accepted syntax
このコミットは、regexp/syntax
パッケージが受け入れる正規表現の構文を定義します。これは、Issue #3953 の更新に対応するものです。
変更の背景
この変更の背景には、Go言語の正規表現エンジンが採用しているRE2ライブラリの構文が、Perl互換の正規表現とは異なる点があるため、その差異を明確にする必要があったことが挙げられます。Goのregexp
パッケージは、Googleが開発したRE2正規表現エンジンに基づいています。RE2は、線形時間でのマッチングを保証し、バックトラッキングによる指数関数的な時間計算量を避ける設計思想を持っています。そのため、一部のPerl互換正規表現の機能(例:後方参照)はサポートしていません。
このような背景から、regexp/syntax
パッケージが具体的にどのような正規表現構文をサポートしているのかを明文化し、開発者が混乱なく正規表現を利用できるようにすることが求められました。特に、regexp/syntax
パッケージは、正規表現をパースツリーに変換し、それをプログラムにコンパイルするための低レベルな機能を提供するため、その構文仕様は非常に重要です。
コミットメッセージにある Update #3953
は、この変更がGoのIssueトラッカーにおける特定の課題に対応していることを示唆しています。Issue #3953 の内容は、おそらく正規表現の構文に関するドキュメントの不足や不明瞭さについて言及していたと考えられます。
前提知識の解説
正規表現 (Regular Expression)
正規表現は、文字列のパターンを記述するための強力なツールです。特定の文字の並び、繰り返し、選択などを表現し、文字列の検索、置換、検証などに広く利用されます。多くのプログラミング言語やテキストエディタでサポートされており、その構文はPerl互換正規表現がデファクトスタンダードとなっています。
Go言語の regexp
パッケージ
Go言語の標準ライブラリには、正規表現を扱うための regexp
パッケージが用意されています。このパッケージは、Googleが開発したRE2正規表現エンジンをベースにしており、高速かつ安全な正規表現処理を提供します。RE2は、バックトラッキングを伴う正規表現エンジンとは異なり、線形時間でのマッチングを保証するため、DoS攻撃(ReDoS)のリスクを低減します。
Go言語の regexp/syntax
パッケージ
regexp/syntax
パッケージは、regexp
パッケージの内部で利用される低レベルなパッケージです。主な役割は以下の通りです。
- 正規表現のパース (Parsing): 正規表現文字列を解析し、抽象構文木(Parse Tree)に変換します。
- 構文木のコンパイル (Compiling): パースされた構文木を、正規表現エンジンが実行可能な内部表現(プログラム)にコンパイルします。
通常、Go言語で正規表現を利用する開発者は、直接 regexp/syntax
パッケージを操作することは稀で、regexp.Compile
や regexp.MatchString
といった regexp
パッケージの関数を利用します。しかし、regexp/syntax
パッケージは、正規表現の構文解析や内部表現の理解において重要な役割を担っています。
RE2
RE2は、Googleが開発した正規表現エンジンです。従来の正規表現エンジンがバックトラッキングによって指数関数的な時間計算量を持つ可能性があるのに対し、RE2は決定性有限オートマトン(DFA)に基づいており、入力文字列の長さに比例する線形時間でマッチングを完了します。これにより、パフォーマンスの予測可能性とセキュリティが向上します。RE2は、Perl互換正規表現の一部(特に後方参照)をサポートしない代わりに、これらの利点を提供します。
技術的詳細
このコミットの主要な変更は、src/pkg/regexp/syntax/doc.go
という新しいファイルを追加したことです。このファイルは、regexp/syntax
パッケージが解釈する正規表現の構文を詳細に記述したドキュメントです。
doc.go
ファイルは、Goのドキュメンテーションツール(go doc
コマンドなど)によって自動的にパッケージのドキュメントとして生成される特別なファイルです。このファイルに記述されたコメントは、パッケージの概要や使用方法、APIの詳細などを説明するために使用されます。
追加された doc.go
の内容は、以下の主要なセクションに分かれています。
- パッケージの概要:
regexp/syntax
パッケージの目的と、通常はregexp
パッケージを使用すべきであるという注意書き。 - 構文 (Syntax): Perlフラグを使用してパースする際の正規表現構文の概要。
- 単一文字 (Single characters):
.
,[xyz]
,[^xyz]
,\d
,\D
,[:alpha:]
,[:^alpha:]
, Unicode文字クラス (\pN
,\p{Greek}
,\PN
,\P{Greek}
) などの定義。 - 複合 (Composites):
xy
(連結),x|y
(選択) の定義。 - 繰り返し (Repetitions):
x*
,x+
,x?
,x{n,m}
,x{n,}
,x{n}
などの量指定子と、非貪欲な量指定子 (x*?
など) の定義。 - グループ化 (Grouping): キャプチャグループ (
(re)
,(?P<name>re)
), 非キャプチャグループ ((?:re)
), フラグ設定グループ ((?flags)
,(?flags:re)
) の定義。 - フラグ構文 (Flag syntax):
i
(大文字小文字を区別しない),m
(複数行モード),s
(.
が改行にマッチ),U
(非貪欲の入れ替え) などのフラグとその設定方法。 - 空文字列 (Empty strings): アンカー (
^
,$
,\A
,\b
,\B
,\z
) の定義。 - エスケープシーケンス (Escape sequences):
\a
,\f
,\t
,\n
,\r
,\v
などの特殊文字、リテラルエスケープ (\*
), 8進数/16進数文字コード (\123
,\x7F
,\x{10FFFF}
), リテラルテキスト (\Q...\E
) の定義。 - 文字クラス要素 (Character class elements):
x
(単一文字),A-Z
(文字範囲),\d
(Perl文字クラス),[:foo:]
(ASCII文字クラス),\p{Foo}
(Unicode文字クラス) の定義。 - 文字クラス要素としての名前付き文字クラス (Named character classes as character class elements):
[\d]
,[^\d]
,[[:name:]]
,[^[:name:]]
,[\p{Name}]
,[^\p{Name}]
などの定義。 - Perl文字クラス (Perl character classes):
\d
,\D
,\s
,\S
,\w
,\W
の定義。 - ASCII文字クラス (ASCII character classes):
[:alnum:]
,[:alpha:]
,[:ascii:]
,[:blank:]
,[:cntrl:]
,[:digit:]
,[:graph:]
,[:lower:]
,[:print:]
,[:punct:]
,[:space:]
,[:upper:]
,[:word:]
,[:xdigit:]
の定義。
これらの定義は、regexp/syntax
パッケージがRE2の構文をどのように解釈するかを明確に示しています。特に、Perl互換正規表現との差異や、Go独自の拡張(例:Unicode文字クラスの表現)が詳細に記述されています。
また、src/pkg/regexp/syntax/parse.go
からは、パッケージの概要を説明するコメントが削除されています。これは、新しく追加された doc.go
がパッケージ全体のドキュメントを担うため、重複を避けるための変更です。
この変更は、mksyntaxgo
というツールによってRE2の配布物から生成されたものであるとコメントに記載されており、Goの正規表現構文がRE2の構文に厳密に準拠していることを示しています。
コアとなるコードの変更箇所
src/pkg/regexp/syntax/doc.go
(新規追加)
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// DO NOT EDIT. This file is generated by mksyntaxgo from the RE2 distribution.
/*
Package syntax parses regular expressions into parse trees and compiles
parse trees into programs. Most clients of regular expressions will use the
facilities of package regexp (such as Compile and Match) instead of this package.
Syntax
The regular expression syntax understood by this package when parsing with the Perl flag is as follows.
Parts of the syntax can be disabled by passing alternate flags to Parse.
Single characters:
. any character, possibly including newline (flag s=true)
[xyz] character class
[^xyz] negated character class
\d Perl character class
\D negated Perl character class
[:alpha:] ASCII character class
[:^alpha:] negated ASCII character class
\pN Unicode character class (one-letter name)
\p{Greek} Unicode character class
\PN negated Unicode character class (one-letter name)
\P{Greek} negated Unicode character class
Composites:
xy x followed by y
x|y x or y (prefer x)
Repetitions:
x* zero or more x, prefer more
x+ one or more x, prefer more
x? zero or one x, prefer one
x{n,m} n or n+1 or ... or m x, prefer more
x{n,} n or more x, prefer more
x{n} exactly n x
x*? zero or more x, prefer fewer
x+? one or more x, prefer fewer
x?? zero or one x, prefer zero
x{n,m}? n or n+1 or ... or m x, prefer fewer
x{n,}? n or more x, prefer fewer
x{n}? exactly n x
Grouping:
(re) numbered capturing group
(?P<name>re) named & numbered capturing group
(?:re) non-capturing group
(?flags) set flags within current group; non-capturing
(?flags:re) set flags during re; non-capturing
Flag syntax is xyz (set) or -xyz (clear) or xy-z (set xy, clear z). The flags are:
i case-insensitive (default false)
m multi-line mode: ^ and $ match begin/end line in addition to begin/end text (default false)
s let . match \n (default false)
U ungreedy: swap meaning of x* and x*?, x+ and x+?, etc (default false)
Empty strings:
^ at beginning of text or line (flag m=true)
$ at end of text (like \z not \Z) or line (flag m=true)
\A at beginning of text
\b at word boundary (\w on one side and \W, \A, or \z on the other)
\B not a word boundary
\z at end of text
Escape sequences:
\a bell (== \007)
\f form feed (== \014)
\t horizontal tab (== \011)
\n newline (== \012)
\r carriage return (== \015)
\v vertical tab character (== \013)
\* literal *, for any punctuation character *
\123 octal character code (up to three digits)
\x7F hex character code (exactly two digits)
\x{10FFFF} hex character code
\Q...\E literal text ... even if ... has punctuation
Character class elements:
x single character
A-Z character range (inclusive)
\d Perl character class
[:foo:] ASCII character class foo
\p{Foo} Unicode character class Foo
\pF Unicode character class F (one-letter name)
Named character classes as character class elements:
[\d] digits (== \d)
[^\d] not digits (== \D)
[\D] not digits (== \D)
[^\D] not not digits (== \d)
[[:name:]] named ASCII class inside character class (== [:name:])
[^[:name:]] named ASCII class inside negated character class (== [:^name:])
[\p{Name}] named Unicode property inside character class (== \p{Name})
[^\p{Name}] named Unicode property inside negated character class (== \P{Name})
Perl character classes:
\d digits (== [0-9])
\D not digits (== [^0-9])
\s whitespace (== [\t\n\f\r ])
\S not whitespace (== [^\t\n\f\r ])
\w word characters (== [0-9A-Za-z_])
\W not word characters (== [^0-9A-Za-z_])
ASCII character classes:
[:alnum:] alphanumeric (== [0-9A-Za-z])
[:alpha:] alphabetic (== [A-Za-z])
[:ascii:] ASCII (== [\x00-\x7F])
[:blank:] blank (== [\t ])
[:cntrl:] control (== [\x00-\x1F\x7F])
[:digit:] digits (== [0-9])
[:graph:] graphical (== [!-~] == [A-Za-z0-9!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~])
[:lower:] lower case (== [a-z])
[:print:] printable (== [ -~] == [ [:graph:]])
[:punct:] punctuation (== [!-/:-@[-`{-~])
[:space:] whitespace (== [\t\n\v\f\r ])
[:upper:] upper case (== [A-Z])
[:word:] word characters (== [0-9A-Za-z_])
[:xdigit:] hex digit (== [0-9A-Fa-f])
*/
package syntax
src/pkg/regexp/syntax/parse.go
(変更)
--- a/src/pkg/regexp/syntax/parse.go
+++ b/src/pkg/regexp/syntax/parse.go
@@ -2,10 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package syntax parses regular expressions into parse trees and compiles
-// parse trees into programs. Most clients of regular expressions will use
-// the facilities of package regexp (such as Compile and Match) instead of
-// this package.
package syntax
import (
コアとなるコードの解説
このコミットの核心は、src/pkg/regexp/syntax/doc.go
ファイルの追加です。このファイルは、Goの正規表現エンジンがサポートする構文を網羅的に記述しており、Goの正規表現の「公式ドキュメント」としての役割を果たします。
doc.go
の内容は、RE2の正規表現構文に厳密に準拠しています。これは、ファイルの冒頭にある DO NOT EDIT. This file is generated by mksyntaxgo from the RE2 distribution.
というコメントからも明らかです。mksyntaxgo
は、RE2のソースコードやドキュメントからGoの正規表現構文定義を自動生成するためのツールであると考えられます。これにより、Goの正規表現実装がRE2の最新の構文仕様と常に同期されることが保証されます。
具体的に記述されている構文要素は、正規表現の基本的な構成要素から、より高度な機能まで多岐にわたります。
- 文字クラス:
.
(任意の文字),[xyz]
(文字集合),[^xyz]
(否定文字集合) といった基本的なものに加え、\d
,\D
といったPerl互換の文字クラス、さらに[:alpha:]
のようなPOSIX文字クラス、そして\p{Greek}
のようなUnicode文字クラスが詳細に定義されています。Unicode文字クラスのサポートは、GoがUnicodeを強力にサポートしていることの表れです。 - 量指定子:
*
,+
,?
といった基本的な量指定子に加え、{n,m}
のような範囲指定、そして*?
のような非貪欲(non-greedy)な量指定子が明記されています。非貪欲なマッチングは、最短のマッチを求める際に重要です。 - グループ化:
(re)
のようなキャプチャグループ、(?P<name>re)
のような名前付きキャプチャグループ、そして(?:re)
のような非キャプチャグループが定義されています。名前付きキャプチャグループは、マッチした部分文字列を名前で参照できるため、コードの可読性を向上させます。 - フラグ:
(?i:re)
のように正規表現内でフラグを設定できる機能も説明されています。これにより、正規表現の一部に対してのみ大文字小文字を区別しないマッチングを適用するなど、柔軟な制御が可能になります。 - アンカー:
^
(行頭/文字列先頭),$
(行末/文字列末尾),\b
(単語境界) など、マッチング位置を指定するアンカーが定義されています。特に、^
と$
がマルチラインモード (m
フラグ) でどのように振る舞うかが明確にされています。 - エスケープシーケンス:
\t
(タブ),\n
(改行) といった一般的なエスケープシーケンスに加え、\x7F
(16進数コード),\x{10FFFF}
(Unicodeコードポイント),\Q...\E
(リテラルテキスト) など、様々なエスケープ方法が説明されています。\Q...\E
は、特殊文字をエスケープせずにリテラルとして扱いたい場合に非常に便利です。
src/pkg/regexp/syntax/parse.go
からパッケージの概要コメントが削除されたのは、doc.go
がパッケージ全体のドキュメントを一元的に管理するためです。これにより、ドキュメントの重複が解消され、保守性が向上します。
このコミットは、Goの正規表現ライブラリの透明性と使いやすさを大幅に向上させるものです。開発者は、この doc.go
ファイルを参照することで、Goの正規表現がどのような構文をサポートしているかを正確に理解し、より効果的に正規表現を記述できるようになります。
関連リンク
- Go言語の
regexp
パッケージ公式ドキュメント: https://pkg.go.dev/regexp - Go言語の
regexp/syntax
パッケージ公式ドキュメント: https://pkg.go.dev/regexp/syntax - RE2 GitHubリポジトリ: https://github.com/google/re2
- Go Issue #3953 (関連する可能性のあるIssue): https://github.com/golang/go/issues/3953 (※コミット当時のIssueは現在と異なる可能性があります)
参考にした情報源リンク
- Go言語のソースコード (特に
src/pkg/regexp/syntax/doc.go
およびsrc/pkg/regexp/syntax/parse.go
の変更履歴) - RE2のドキュメント (RE2の構文に関する一般的な情報)
- Go言語の正規表現に関するブログ記事やチュートリアル (Goの正規表現がRE2に基づいていることや、Perl互換正規表現との違いに関する情報)
- Go言語のIssueトラッカー (Issue #3953 の内容を推測するため)
- Go言語のドキュメンテーション生成に関する情報 (doc.go の役割について)
- 正規表現の一般的な構文に関する情報 (Perl互換正規表現、POSIX文字クラスなど)