字句解析器
字句解析器は次のような手順で作成します。
- 字句解析器を定義するファイル(.plex)を作成する。
- plexファイルをもとに、PHP_LexerGeneratorで字句解析器のためのクラス(.php)を生成する。
plexファイルは次のような構造になっています(詳細はパッケージに含まれているサンプル「TestLexer.plex」やPHP_LexerGeneratorのドキュメントを参照してください)。
<?php
class (1)クラス名
{
(2)lexerが利用する変数のリスト
(3)コンストラクタ
/*!lex2php
(4)lexerが利用する変数に関する指示のリスト
(5)トークンの表現のリスト
*/
/*!lex2php
(6)トークンに対する処理
*/
};
?>
(1)は任意です。ここではMyLexerとします。Lexer自体に特殊な処理をさせるのでなければ、(2)から(4)はサンプル「TestLexer.plex」のままで構いません。次のようになります。
<?php
class MyLexer
{
private $data;
private $N;
public $token;
public $value;
private $line;
private $state = 1;
function __construct($data) {
$this->data = $data;
$this->N = 0;
$this->line = 1;
}
/*!lex2php
%input $this->data
%counter $this->N
%token $this->token
%value $this->value
%line $this->line
トークンの表現のリスト(5)は次のようになります。トークン表現のリストは、文字列リテラルあるいは正規表現で指定します。正規表現は、@か/で挟む形式で書きます。
PLUS = "+"
MINUS = "-"
TIMES = "*"
DIVIDE = "/"
NUMERAL = /[0-9]*\.[0-9]+|[0-9]+/
LPAREN = "("
RPAREN = ")"
WHITESPACE = /[ \t\n]+/
例えば、+という文字には特別な意味を持たせたいので、PLUSという名前を付けておきます(名前は任意ですが、分かりやすいものをつけてください)。MINUSやTIMES、DIVIDE、LPAREN、,RPARENも同様です。数値(NUMERAL)はもう少し複雑です。「数字の列.数字の列」あるいは「数字の列」(小数点なし)を数値として扱いたいので、上記のような正規表現になります。空白(WHITESPACE)も同様です。
トークンに対する処理(6)は次のようになります。この段階では、空白(とタブ)の時に何もしないということ以外、特に指定することはありません(後で修正します)。「+」が加算を意味するというようなことは後述する構文解析器のほうで指定します。
PLUS {}
MINUS {}
TIMES {}
DIVIDE {}
NUMERAL {}
LPAREN {}
RPAREN {}
WHITESPACE { return false; }
LexerGeneratorによってplexファイルから字句解析器のクラスを生成します。
require_once "PHP/LexerGenerator.php";
new PHP_LexerGenerator('mylexer.plex');
上のようなコードを実行するとmylexer.phpが生成されます。このファイルで定義されるクラス(ここではMyLexer)には、次のような属性とメソッドがあります。
| メソッド | 概要 |
| MyLexer($str) | コンストラクタ。引数は字句解析する文字列。 |
| yylex() | 字句解析をして、トークンを一つ取り出すメソッド。 |
| token | 字句解析によって切り出したトークンの種類(番号)。 |
| value | 切り出したトークン。 |
解析したい文字列を与えてインスタンスを生成し、メソッドyylex()でトークンを取り出します。
require_once "mylexer.php";
$lexer=new MyLexer('10 + 2.1 * ( 3 + 1 )');//字句解析する文字列を与えてオブジェクトを生成する
while ($lexer->yylex()) {
echo "[token:{$lexer->token}, value:{$lexer->value}]\n";
}
実行結果は次のようになります。属性tokenが、先に定義したトークンの順番を表しています。例えば最初(1番目)に定義したのは+でした。
php test_lexer.php (略) [token:5, value:10] [token:1, value:+] [token:5, value:2.1] [token:3, value:*] [token:6, value:(] [token:5, value:3] [token:1, value:+] [token:5, value:1] [token:7, value:)]
