これを試すことができます:
$subject = '15 + func1 ("gis", 22, func (55), 87) + 95 + func2 () + 35';
$pattern = <<<'LOD'
~
#definitions:
(?(DEFINE)(?<int> [0-9]++ ))
(?(DEFINE)(?<str> "[^"]++" ))
(?(DEFINE)(?<f_name> \b[a-z]\w*+\b ))
(?(DEFINE)(?<sep> ,\h ))
#pattern:
(?=
(
(?<func>\g<f_name>) \s*+
\(
(?<args>
(?> (?> \g<int> | \g<str> | (?-3) ) \g<sep>?+ )*
)
\)
)
)
~x
LOD;
preg_match_all($pattern, $subject, $matches);
print_r($matches['func']);
print_r($matches['args']);
アイデアは、再帰を使用して関数内の関数を一致させ、すべてのパターンを先読み内に配置して、重複するすべての引数をキャプチャすることです。
(?-3)
パターンの最初のグループである左側の 3 番目のキャプチャ グループを参照するために再帰を使用することに注意してください(?1)
。ただし、このパターンをサブパターンとして使用する場合は、相対参照が役立ちます。
(?(DEFINE)..)
コメント モード(x 修飾子)と組み合わせると、非常に編集しやすく、データ型やパーサーが遭遇する可能性のあるその他の要素を追加または編集できるため、便利です。たとえば、一重引用符の間の文字列を許可する場合は、次の<str>
ようにサブパターンを変更できます。
(?(DEFINE)(?<str> "[^"]++" | '[^']++' ))
または、これをより寛容にする(引用符のエスケープを許可する):
(?(DEFINE)(?<str> "(?>[^"]++|(?<=\\)")++" | '(?>[^']++|(?<=\\)')++' ))