理論上の正規表現は、括弧のマッチングを行うほど強力ではありません。理論上の正規表現は、左再帰/右再帰ルールのみを処理できます。中再帰規則は、正規表現 ( など<exp> -> "(" <exp> ")"
) では表現できません。
ただし、プログラミング言語の正規表現は、正規表現が通常の文法の力を超えることを可能にする機能を実装しています。たとえば、正規表現の後方参照を使用すると、非文脈自由言語に一致する正規表現を記述できます。ただし、後方参照を使用しても、括弧と正規表現のバランスを取ることはまだできません。
PCRE ライブラリはサブルーチン呼び出し機能を介して再帰的な正規表現をサポートしているため、このような式を正規表現で解析することは技術的に可能です。ただし、正規表現を自分で作成できる場合 (つまり、自分が何をしているのかを理解し、必要に応じて正規表現を変更できる場合) を除き、独自のパーサーを作成する必要があります。そうしないと、維持できない混乱が発生します。
(?(DEFINE)
(?<string>'[^']++')
(?<int>\b\d+\b)
(?<sp>\s*)
(?<key>\b\w+\b)
(?<value>(?&string)|(?&int))
(?<exp>(?&key) (?&sp) = (?&sp) (?&value))
(?<logic>\b (?:and|or) \b)
(?<main>
(?<token> \( (?&sp) (?&main) (?&sp) \) | (?&exp) )
(?:
(?&sp) (?&logic) (?&sp)
(?&token)
)*
)
)
(?:
^ (?&sp) (?= (?&main) (?&sp) $ )
|
(?!^) \G
(?&sp) (?&logic) (?&sp)
)
(?:
\( (?&sp) (?<m_main>(?&main)) (?&sp) \)
|
(?<m_key>(?&key)) (?&sp) = (?&sp) (?<m_value>(?&value))
)
regex101 のデモ
上記の正規表現は とともに使用しpreg_match_all
、 x フラグ (フリー スペーシング モード) で区切り文字の間に配置する必要があります/.../x
。
試合ごとに:
- キャプチャ グループにコンテンツがある場合
m_main
は、そのコンテンツを別の照合ラウンドにかけます。
m_key
それ以外の場合は、m_value
キャプチャ グループでキーと値を取得します。
説明
この(?(DEFINE)...)
ブロックを使用すると、メイン パターンとは別に、サブルーチン呼び出しで使用する名前付きキャプチャ グループを定義できます。
(?(DEFINE)
(?<string>'[^']++') # String literal
(?<int>\b\d+\b) # Integer
(?<sp>\s*) # Whitespaces between tokens
(?<key>\b\w+\b) # Field name
(?<value>(?&string)|(?&int)) # Field value
(?<exp>(?&key) (?&sp) = (?&sp) (?&value)) # Simple expression
(?<logic>\b (?:and|or) \b) # Logical operators
(?<main> # <token> ( <logic> <token> )*
# A token can contain a simple expression, or open a parentheses (...)
# When we open a parentheses, we recurse into the main pattern again
(?<token> \( (?&sp) (?&main) (?&sp) \) | (?&exp) )
(?:
(?&sp) (?&logic) (?&sp)
(?&token)
)*
)
)
パターンの残りの部分は、この手法に基づいて、すべて<token>
の<token> ( <logic> <token> )*
をグローバル マッチング操作で照合します。
正規表現の最後の部分は、 のように記述できますが(?&token)
、単純な式のフィールド名と値に一致するように展開されます。
(?:
\( (?&sp) (?<m_main>(?&main)) (?&sp) \)
|
(?<m_key>(?&key)) (?&sp) = (?&sp) (?<m_value>(?&value))
)