2010-04-08 (木)
*正規表現でPHPのコードをパースする
正規表現はテキスト処理の必需品ですが,構造化された文字列を処理するのはちょっと(?)難しいです.
ただ,PCREの拡張正規表現を使うと,比較的簡単にプログラムのコードのようなものから必要な情報を抜き出すことができます.
ちゃんとしたパーサがあれば使うことに越したことはありませんし,正確な情報が取れますが,手っ取り早く済ませたい場合,やはり正規表現で書くことになると思います.
$code = preg_replace('/([\"\'])([^\1\\\\]|(\\\\.))*?\1/',' _STR_ ',$code); // 文字列を置き換える $code = preg_replace('/\/\/.*/',' _COMMENT_ ',$code); // コメントを置き換える $code = preg_replace('/\s+/',' ',$code); // 連続する空白をスペースに $code = preg_replace('/((?<=[^\w_])\s+(?=[\w_]))|((?<=[\w_])\s+(?=[^\w_]))/','',$code); // 余分な空白削除 preg_match_all( <<< RE_END / ( # 記号 \{ | \} | # 予約語 (?<=[^\w]) (?:if|else|elseif|catch|try|switch|for|foreach) (?=[^\w]) ) # 予約後に続くパラメータ等 ((?<=[\w])\s*[^;\{\}:]+)? /x RE_END , $code, $match)
本当は複数行コメントとか色々ありますが,とりあえずこんな感じで書けると思います.
拡張正規表現の後方一致検索(?<=)や前方一致(?=)は正規表現の先頭や末尾でしか使わない人をたまに見かけますが,途中に入れてトークン切り出しの制約を付けると便利です.上のように書いておけば,$match[1]に記号や予約語が,$match[2]にパラメータにあたる文字列が入っているので,後々扱いやすくなります.
ただ,関数の宣言や呼びだしとか,変数への代入とかの情報も欲しくなると,恐ろしいことになるので気を付けた方が良いでしょう.複数の正規表現で書ければ良いのですが,前後関係の情報が失われてしまうのでできません.preg_系の関数がマッチした位置を列挙できれば良いのですが….
PHPプログラマっぽい日記を書きました.