view: all

CSS風の文法のパーサを作るチュートリアル

このチュートリアルでは、CSS風のデータ言語(CSS Like Data Language、略してCDL)のパーサを作りながら本ライブラリの使い方を解説していく。

CDLの定義

まずCDLについて定義していく。

CDLは、CSS風の文法で連想配列の様なデータ構造を記述することができる。

foobar {
    foo : value;
    bar : value;
}
hogefuga {
    hoge : value;
    fuga : value;
}

CSSとは違って、セレクタはないので以下の文法は不正である。

foo > bar {
    foo : value;
    bar : value;
}

"#"に続く行末までの文字はコメントとして見なされる。

# コメント
foobar {
    # コメント
    foo : bar;
}

連想配列はCSSとは違って入れ子にできる。

foobar {
    hoge : fuga;
    piyo {
        foo : bar;
    }
}

属性名は、CSSと同様にアルファベットだけでなく"-"も受け付ける。

foo-bar {
    line-height : 3;
}

パーサの構築

上筆した文法にしたがってパーサを構築していく。

パーサは主にPEGクラスメソッドを通じて生成する。

コメント

$commentにCDLのコメントのパーサを定義している。

上の行のPHPのコメントには、PEGでの定義を書いた。

<?php
// comment <- "#" (!("\r" / "\n") .)* ("\r\n" / "\n" / "\r")?
$comment = PEG::seq('#', PEG::many(PEG::char("\r\n", true)), PEG::optional(PEG::newLine()));
空白
<?php
// space <- ("\r" / "\n" / "\t" / " ")+
$space = PEG::many1(PEG::char("\r\n\t "));
無視できる要素

空白とコメントは無視出来る要素である。

<?php
// ignore <- (space / comment)*
$ignore = PEG::memo(PEG::many(PEG::choice($space, $comment)));

PEG::memo()メソッドは、引数に与えたパーサの結果をコンテキストとその位置に対してキャッシュするパーサを返す。パーサを線形時間で動作させるのに必要となる。

属性名

"foo : bar;"のfooの部分。

<?php
// symbol <- [-_a-zA-Z]+
$symbol = PEG::memo(PEG::join(PEG::many1(PEG::char('abcdefghijklmnopqrstuvwqyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'))));

$symbol->parse(PEG::context('foo')); // => 'foo'
$symbol->parse(PEG::context('foo-bar')); // => 'foo-bar'
$symbol->parse(PEG::context(' foo-bar')); // => 失敗
属性の値としての文字列

";"以外の値を文字列の一部としてみなす。

"foo : bar;"の": bar;"の部分。

<?php
// str <- ":" ignore (!";")* ";" ignore
$str = PEG::memo(PEG::third(':', $ignore, PEG::join(PEG::many(PEG::char(';', true))), ';'));
属性
<?php
// attr <- symbol ignore ":" ignore (str / hash) ignore
$attr = PEG::memo(PEG::seq($symbol, PEG::drop($ignore), PEG::choice($str, PEG::ref($hash)), $ignore));

// attrs <- attr*
$tohash = function(Array $result) {
    $arr = array();
    foreach ($result as $pair) {
        list($key, $value) = $pair;
        $arr[$key] = $value;
    }
    return $arr;
};
$attrs = PEG::memo(PEG::hook($tohash, PEG::many($attr)));

$hashはまだ定義していないのでPEG::ref()メソッドで包んで渡す。

$tohashは属性名と値の配列を連想配列にする関数である。

連想配列
<?php
// hash <- "{" ignore attrs "}"
$hash = PEG::memo(PEG::third('{', $ignore, $attrs, '}'));
CDLパーサ
<?php
// parser <- ignore attr*
$parser = PEG::second($ignore, $attrs, PEG::eos());

PEG::eos()はコンテキストの終端にヒットするパーサを返す。

コンテキストの全ての文字列を消費出来なかった場合は失敗するためである。

パーサまとめ

以上の定義を一つにまとめると以下のようになる。

<?php
include_once dirname(__FILE__) . '/peg_trunk/code/PEG.php';

/* comment <- "#" (!("\r" / "\n") .)* ("\r\n" / "\n" / "\r")?
 * space   <- ("\r" / "\n" / "\t" / " ")+
 * ignore  <- (space / comment)*
 *
 * symbol  <- [-_a-zA-Z]+
 * str     <- ":" ignore (!";")* ";" ignore
 * attr    <- symbol ignore (str / hash) ignore
 *
 * attrs   <- attr*
 * hash    <- "{" ignore attrs "}"
 *
 * parser  <- ignore attrs
 */

$comment = PEG::seq('#', PEG::many(PEG::char("\r\n", true)), PEG::optional(PEG::newLine()));
$space   = PEG::many1(PEG::char("\r\n\t "));
$ignore  = PEG::memo(PEG::many(PEG::choice($space, $comment)));

$symbol  = PEG::memo(PEG::join(PEG::many1(PEG::char('abcdefghijklmnopqrstuvwqyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'))));
$str     = PEG::memo(PEG::third(':', $ignore, PEG::join(PEG::many(PEG::char(';', true))), ';'));
$attr    = PEG::memo(PEG::seq($symbol, PEG::drop($ignore), PEG::choice($str, PEG::ref($hash)), PEG::drop($ignore)));

$tohash  = function(Array $result) { 
    $arr = array();
    foreach ($result as $pair) {
        list($key, $value) = $pair;
        $arr[$key] = $value;
    }
    return $arr;
};
$attrs   = PEG::memo(PEG::hook($tohash, PEG::many($attr)));
$hash    = PEG::memo(PEG::third('{', $ignore, $attrs, '}'));

$parser  = PEG::second($ignore, $attrs, PEG::eos());

試してみる

var_export($parser->parse(PEG::context('
foobar {
    # コメント
    foo : bar;
    foo-bar : fugahoge;
    piyo {
        hoge :fuga;
    }
}
')));
/* 結果
array (
  'foobar' => 
  array (
    'foo' => 'bar',
    'foo-bar' => 'fugahoge',
    'piyo' => 
    array (
      'hoge' => 'fuga',
    ),
  ),
)
*/

var_export($parser->parse(PEG::context('
foo : bar;
hoge {
    fuga : piyo;
}
')));
/* 結果
array (
  'foo' => 'bar',
  'hoge' => 
  array (
    'fuga' => 'piyo',
  ),
)
*/

var_export($parser->parse(PEG::context('
foo : bar;
hoge {
    fuga : piyo;
'))); // { }の対応が合っていないので失敗する
 
?

PEG

Parsing Expression Grammer(解析表現文法)のこと。パーサの動きを記述するための形式文法で、その名前は本ライブラリのパッケージ名の元となっている。

PEGパーサは以下のような特徴を持つ。

  • 無限の先読み。いくらでもバックトラックできる
  • 結果が一意に定まる。曖昧ではない
  • 正規表現と違い再帰的な文法を扱うことができる
  • メモ化(packrat parser化)することで線形時間で動作する

PEGパーサとはPEGで記述できるパーサの事を指す。

本ライブラリの実装については本ライブラリについてを参照すること。

PEGの具体的な文法については以下のリンクを参照すること。

リンク

 
?

PEG_ArrayContext

http://svn.openpear.org/PEG/trunk/code/PEG/ArrayContext.php

PEG_IContextを実装した配列を対象としたコンテキスト。

このクラスのインスタンスはPEG::contextメソッドからも生成できる。

PEG_ArrayContext::__construct(Array $arr)

配列からインスタンスを生成する。

PEG_ArrayContext::read($i)

内部の配列の要素を$i分返す。

<?php

$context = PEG::context(array('a', 'b', 'c'));

$context->read(2); // => array('a', 'b')

$context->read(1); // => array('c')

PEG_ArrayContext::readElement()

内部の要素を一つ返す。

<?php

$context = PEG::context(array('a', 'b', 'c'));

$context->readElement(); // => 'a'

$context->readElement(); // => 'b'

PEG_ArrayContext::token(Array $args)

$args[0]をひとつの要素と仮定し、要素をマッチする。

<?php

PEG::context(array('abc', 'def', 'ghi'))->token(array('abc')); // => 'abc'

PEG::context(array('abc', 'def', 'ghi'))->token(array('ab')); // => 失敗
 
?

PEG_IContext

http://svn.openpear.org/PEG/trunk/code/PEG/IContext.php

このインターフェイスを実装することで任意のデータをパーサにかけることができる。

予め用意されているコンテキストについてはコンテキストを参照。

PEG_IContext::read($i)

$i分だけ要素を返し、現在の位置も$i分進める。

PEG_IContext::readElement()

ひとつだけ要素を返し、現在の位置も一つ進める。

PEG_IContext::token(Array $args)

$argsにしたがってなんらかの値か失敗を返す。

PEG::token()メソッドで生成されるバーサは、このメソッドを利用する。

PEG_IContext::eos()

コンテキストの要素を全て消費したかどうかを返す。

PEG_IContext::tell()

コンテキストの現在の位置を返す。

PEG_IContext::seek($i)

コンテキストの現在の位置を$iに設定する。

PEG_ICotnext::lastError()

登録されたエラーが有ったならば、array(位置, エラー内容)を返す。このとき返されるエラーは最も深い位置で登録されたエラーである。

そうでなければnullを返す。

 
?

PEG_IParser

http://svn.openpear.org/PEG/trunk/code/PEG/IParser.php

このインターフェイスを実装したクラスのインスタンスをパーサと呼ぶ。

parseメソッドは成功した場合は何らかの値を返し、そうでければ失敗を返す。

<?php

interface PEG_IParser
{
    /**
     * パースに失敗した場合はPEG_Failureを返すこと。
     * 成功した場合はなんらかの値を返すこと。
     * 
     * @param PEG_IContext
     * @return mixed
     */
    function parse(PEG_IContext $c);
}
 
?

PEG_StringContext

http://svn.openpear.org/PEG/trunk/code/PEG/StringContext.php

PEG_IContextを実装した文字列を対象としたコンテキスト

このクラスのインスタンスはPEG::contextメソッドからも生成できる。

PEG_StringContext::__construct($str)

文字列から自身のインスタンスを生成する。

PEG_StringContext::read($i)

$iバイト分だけ返す。

<?php

$context = PEG::context('abcde')

$context->read(3); // => 'abc'

$context->read(2); // => 'de'

PEG_StringContext::readElement()

1バイト分だけ返す。PEG_StringContext::read(1)と同様。

PEG_StringContext::token(Array $args)

$args[0]を文字列と仮定し、マッチする。

もし$args[1]がfalseであるとき、大文字小文字に関係なく比較される。

<?php

$context = PEG::context('abc');

$context->token(array('ab')); // => 'ab'

$context->token(array('d')); // => 失敗
<?php

PEG::context('abc')->token(array('ABC')); // 失敗

PEG::context('abc')->token(array('ABC', false)); // => 'abc'
 
?

PEGクラスメソッド

http://svn.openpear.org/PEG/trunk/code/PEG.php

PEGクラスメソッドは主にパーサを生成したりコンテキストを生成するのに利用される。

パーサを取るメソッドにパーサ以外の値を渡した場合

引数にパーサを取るメソッドにパーサ以外の値を渡した場合、PEG::token(値)であるとみなされる。

つまり、PEG::seq('a', 'b') は PEG::seq(PEG::token('a'), PEG::token('b')) と同じ意味のパーサを返す。

PEG::seq($parser) - 連接

PEGにおける連接を表現するパーサを返す。引数の数は任意である。

PEG::seq($a, $b) とした場合、このパーサは、$aパーサを実行した後に$bパーサを実行し、これらのパーサの結果を配列で返す。

引数に与えたパーサの結果としてnullが帰ってきた場合、無視される。パーサの返り値をnullにしたい場合はPEG::drop()を利用する。

<?php

PEG::seq('a', 'b', 'c')->parse(PEG::context('abc')); // => array('a', 'b', 'c')

PEG::seq('a', 'b', 'c')->parse(PEG::context('def')); // => 失敗

PEG::seq('a', 'b', 'c')->parse(PEG::context('abd')); // => 失敗

PEG::seq('a', PEG::drop('b'), 'c')->parse(PEG::context('abc')); // => array('a', 'c')

PEG::choice($parser) - 順序付き選択

PEGにおける順序付き選択を表現するパーサを返す。引数の数は任意である。

PEG::choice($a, $b) とした場合、このパーサは、まず$aパーサを実行し、失敗が帰ってきた場合は$aパーサを実行する前の状態にバックトラックして$bパーサを実行する。引数に与えられた全てのパーサに失敗した場合は、このパーサも失敗を返す。

<?php

PEG::choice('a', 'b')->parse(PEG::context('a')); // => 'a'

PEG::choice('a', 'b')->parse(PEG::context('b')); // => 'b'

PEG::choice('a', 'b')->parse(PEG::context('c')); // => 失敗

PEG::many($parser) - 繰り返し

PEGにおける繰り返しを表現するパーサを返す。

PEG::many($a) とした場合、このパーサは$aパーサを失敗するまで繰り返す実行し、返り値として$aパーサが返した結果の配列を返す。

PEG::seq()と同様に、引数に与えたパーサの結果としてnullが帰ってきた場合、無視される。

引数を二つ以上与えた場合、PEG::many($a, $b, $c) は PEG::many(PEG::seq($a, $b, $c)) と同じ意味を持ったパーサを返す。

<?php

PEG::many('a')->parse(PEG::context('aaa')); // => array('a', 'a', 'a')

PEG::many('a')->parse(PEG::context('aaabbb')); // => array('a', 'a', 'a')

PEG::many('a')->parse(PEG::context('bbb')); // => array()

PEG::many(PEG::drop('a'), 'b')->parse(PEG::context('ababab')); // => array(array('b'), array('b'), array('b'))

PEG::optional($parser) - 省略可能

PEGにおける省略可能を表現するパーサを返す。

引数を二つ以上与えた場合、PEG::optional($a, $b, $c) は PEG::optional(PEG::seq($a, $b, $c)) と同じ意味を持ったパーサを返す。

<?php

PEG::optional('a')->parse(PEG::context('a')); // => 'a'

PEG::optional('a')->parse(PEG::context('b')); // => false

PEG::many1($parser) - 一回以上の繰り返し

PEGにおける一回以上の繰り返しを表現するパーサを返す。

引数を二つ以上与えた場合、PEG::many1($a, $b, $c) は PEG::many1(PEG::seq($a, $b, $c)) と同じ意味を持ったパーサを返す。

<?php

PEG::many1('ab')->parse(PEG::context('ababab')); // => array('ab', 'ab', 'ab')

PEG::many1('ab')->parse(PEG::context('babab')); // => 失敗

PEG::anything() - どんな要素にもマッチする

どんな要素にもマッチするパーサを返す。

<?php

PEG::anything()->parse(PEG::context('a')); // => 'a'

PEG::many(PEG::anything())->parse(PEG::context('abcdefg')); // => array('a', 'b', 'c', 'd', 'e', 'f', 'g')

PEG::not($parser) - 否定先読み

PEGにおける否定先読みを表現するパーサを返す。

このパーサは、$parserを実行して成功した場合は失敗を返し、そうではない場合はfalseを返す。また、コンテキストは$parserを実行する前に状態に戻される。

引数を二つ以上与えた場合、PEG::not($a, $b, $c) は PEG::not(PEG::seq($a, $b, $c)) と同じ意味を持ったパーサを返す。

<?php

PEG::seq(PEG::drop(PEG::not('a')), PEG::anything())->parse(PEG::context('b')); // => 'b'

PEG::seq(PEG::drop(PEG::not('a')), PEG::anything())->parse(PEG::context('c')); // => 'c'

PEG::seq(PEG::drop(PEG::not('a')), PEG::anything())->parse(PEG::context('a')); // => 失敗

PEG::amp($parser) - 先読み

PEGにおける先読みを表現するパーサを返す。

このパーサは、$parserを実行してその結果をそのまま返す。また、コンテキストは$parserを実行する前の状態に戻される。

引数を二つ以上与えた場合、PEG::amp($a, $b, $c) は PEG::amp(PEG::seq($a, $b, $c)) と同じ意味を持ったパーサを返す。

PEG::subtract($parser, $a...)

PEG::subtract($parser, $a, $b, $c) は PEG::tail(PEG::not($a), PEG::not($b), PEG::not($c), $parser) と同じ意味のパーサを返す。

PEG::char($str, $except = false) - 文字にマッチする

PEG::char('abcd') は 'a', 'b', 'c', 'd' にマッチする。

PEG::char('abcd', true) は 'a', 'b', 'c', 'd' 以外の文字にマッチする。

PEG::hook($callback, $parser)

引数に与えられたパーサの結果を$callbackで加工するパーサを返す。

$parserが失敗した場合は$callbackは呼ばれない。

引数を二つ以上与えた場合、PEG::hook($a, $b, $c) は PEG::hook($a, PEG::seq($b, $c)) と同じ意味を持ったパーサを返す。

PEG::first($parser)

引数に与えられたパーサの結果の最初の要素を返すパーサを返す。

引数を二つ以上与えた場合、PEG::first($a, $b, $c) は PEG::first(PEG::seq($a, $b, $c)) と同じ意味を持ったパーサを返す。

<?php

PEG::seq('a', 'b', 'c')->parse(PEG::context('abc')); // => array('a', 'b', 'c')

PEG::first('a', 'b', 'c')->parse(PEG::context('abc')); // => 'a'

PEG::second($parser)

引数に与えられたパーサの結果の二番目の要素を返すパーサを返す。

引数を二つ以上与えた場合、PEG::second($a, $b, $c) は PEG::second(PEG::seq($a, $b, $c)) と同じ意味を持ったパーサを返す。

PEG::third($parser)

引数に与えられたパーサの結果の三番目の要素を返すパーサを返す。

引数を二つ以上与えた場合、PEG::third($a, $b, $c) は PEG::third(PEG::seq($a, $b, $c)) と同じ意味を持ったパーサを返す。

PEG::tail($parser)

$parserが返す配列の最後の値を返すパーサを返す。

PEG::drop($parser)

引数に与えられたパーサを実行し、結果としてnullを返すパーサを返す。

PEG::seq()やPEG::many(), PEG::many1()とともに利用することを想定している。

引数を二つ以上与えた場合、PEG::drop($a, $b, $c) は PEG::drop(PEG::seq($a, $b, $c)) と同じ意味を持ったパーサを返す。

PEG::parserOf($callback)

$callbackを元にパーサを構築する。$callbackはPEG_IParser::parse(PEG_IContext)メソッドの代わりとして用いられる。

PEG::ref(&$ref)

実行時に$refパーサを呼び出すパーサを返す。

再帰を含む文法のパーサを組み立てる際に利用される。

<?php

$parser = PEG::choice(PEG::seq('a', PEG::ref($parser)), 'a');

$parser->parse(PEG::context('aaaa')); // => array('a', array('a', array('a', 'a')))

PEG::delay($callback)

最初の実行時に$callbackを呼び出し、その返り値のパーサを実行するパーサを返す。

再帰を含む文法のパーサを組み立てる際に利用される。

PEG::error($msg) - エラーをコンテキストに記録するパーサ

実行時にコンテキストにエラーメッセージを登録し、失敗するパーサを返す。

PEG::token($token)

PEG_IContext::token(Array $args)メソッドに動きを委譲するパーサを返す。

PEG::pack($open, $parser, $close)

実行時に$open, $parser, $closeパーサを順番に実行し、$parserが返した値を返すパーサを返す。

PEG::second()とほとんど同じ。

PEG::flatten($parser)

$parserが返す配列を平にして返すパーサを返す。

引数を二つ以上与えた場合、PEG::flatten($a, $b, $c) は PEG::flatten(PEG::seq($a, $b, $c)) と同じ意味を持ったパーサを返す。

<?php

PEG::seq('a', PEG::seq('b', 'c'))->parse(PEG::context('abc')); // => array('a', array('b', 'c'))

PEG::flatten('a', PEG:seq('b', 'c'))->parse(PEG::context('abc')); // => array('a', 'b', 'c')

PEG::join($parser, $glue = '')

$parserが返す配列を平にした後ひとつの文字列に連結して返すパーサを返す。

PEG::memo($parser) - パーサをメモ化

$parserをpackrat parser化する。コンテキストとその位置に紐解かれた結果がキャッシュされるようになる。

PEG::alphabet()

アルファベットにヒットするパーサを返す。

PEG::lower()

アルファベットの小文字にヒットするパーサを返す。

PEG::upper()

アルファベットの大文字にヒットするパーサを返す。

PEG::digit()

0から9までの数字にヒットするパーサを返す。

PEG::failure() - 失敗を返す

失敗を返す。

PEG::newLine() - 改行にヒットするパーサ

改行にヒットするパーサを返す。

PEG::listOf($item, $glue) - リストのような形式にヒットするパーサ

PEG::seq($item, $glue, $item, $glue, ... , $glue, $item) という形式にヒットするパーサを返す。

パーサの結果としては、$itemの結果の配列を返す。

<?php

PEG::listOf(PEG::alphabet(), ',')->parse(PEG::context('a,b,c'); // => array('a', 'b', 'c')

PEG::listOf(PEG::alphabet(), ',')->parse(PEG::context('a'); // => array('a')

PEG::listOf(PEG::alphabet(), ',')->parse(PEG::context(','); // => 失敗
 
?

このサイトではopenpearで公開されているPEGパーサコンビネータのドキュメントを記述しています。

なるべく分かりやすい記述を心がけていますが、意味の分からない所などがあったら開発者に質問しましょう。

 
?

examples

簡易的なS式パーサ

シンボルとリストを解釈できる簡易的なS式パーサ。パーサを構築するのに8行程度。

<?php
include_once 'PEG.php';

// パーサの構築
$space  = PEG::many(PEG::char("\r\n \t"));
$symbol = PEG::join(PEG::many1(PEG::char("()\r\n\t ", true)));

$paren  = PEG::third('(', $space, PEG::many(PEG::ref($atom)), ')');
$atom   = PEG::memo(PEG::first(PEG::choice($paren, $symbol), $space));

$sexpr  = PEG::second($space, $atom);

// コンテキストの構築
$context = PEG::context('(a (b (c ())))');

// コンテキストをパーサにかける
$result = $sexpr->parse($context);

// 結果
var_export($result);
/*
array (
  0 => 'a',
  1 => 
  array (
    0 => 'b',
    1 => 
    array (
      0 => 'c',
      1 => 
      array (
      ),
    ),
  ),
)
*/

四則演算の数式を逆ポーランド記法に変換する

演算子や括弧で優先順位のつく四則演算の数式を解釈して逆ポーランド記法に変換するパーサ。

<?php
include_once 'PEG.php';

// フックに使う関数の定義
$fn = function($result) {
    list($first, $rest) = $result;

    return !$rest
        ? array($first)
        : array_merge(array($first), call_user_func_array('array_merge', $rest));
};
$listof = function($item, $glue) use($fn) {
    return PEG::hook($fn, PEG::seq($item, PEG::many($glue, $item)));
};
$torpn = function(Array $expr) use(&$torpn) {
    $first = array_shift($expr);
    $ret = is_array($first) ? $torpn($first) : $first ;
    foreach (array_chunk($expr, 2) as $buf) {
        list($op, $right) = $buf;
        $ret .= (is_array($right) ? " {$torpn($right)}" : " {$right}") . " {$op}";
    }
    return $ret;
};

// パーサの構築
$space   = PEG::many(PEG::char("\r\n \t"));
$zero    = PEG::seq(PEG::char('0'));
$notzero = PEG::seq(PEG::char('123456789'), PEG::many(PEG::digit()));
$strnum  = PEG::join(PEG::choice($notzero, $zero));

$num   = PEG::hook('intval', PEG::first($strnum, $space));
$paren = PEG::second(PEG::seq('(', $space), PEG::ref($exp1), PEG::seq(')', $space));
$atom  = PEG::choice($num, $paren);

$op = function($str) use($space) { return PEG::first(PEG::char($str), $space); };

$exp3 = PEG::memo($listof($atom, $op('/')));
$exp2 = PEG::memo($listof($exp3, $op('*')));
$exp1 = PEG::memo($listof($exp2, $op('+-')));
$exp  = PEG::second($space, PEG::hook($torpn, $exp1), PEG::eos());

// 試してみる
var_dump($exp->parse(PEG::context('1 + 2 * 3')));     // => 1 2 3 * +
var_dump($exp->parse(PEG::context('(1 + 2) * 3')));   // => 1 2 + 3 *
var_dump($exp->parse(PEG::context('1 + 2 * 3 / 4'))); // => 1 2 3 4 / * +
var_dump($exp->parse(PEG::context('1 + 2 * 3 * 4'))); // => 1 2 3 * 4 * +
 
?

コンテキスト

このライブラリにおいては、PEG_IContextを実装したクラスのインスタンスを指す。

コンテキストオブジェクトは、パースのための対象や、対象の現在の位置、キャッシュ等を保持する。また、PEG_Tokenパーサの動きを規定する。

予め用意されているPEG_IContextの実装クラスは以下の二種類。

  • PEG_StringContext - 文字列を対象としたコンテキスト。多くの場合このコンテキストを使う。
  • PEG_ArrayContext - 配列を対象としたコンテキスト。

これらのコンテキストは、各々のコンストラクタを利用する以外に、PEG::context()メソッドで生成できる。

<?php

PEG::context('abc') instanceof PEG_StringContext; // => true

PEG::context(array('a', 'b', 'c')) instanceof PEG_ArrayContext; // => true
 
?

パーサ

パーサとは、このライブラリにおいてはPEG_IParserインターフェイスを実装したクラスのインスタンスを指す。

また、「パーサを実行する」とはパーサのparseメソッドを実行することを指す。

 
?

利用例

本ライブラリの利用例の一覧。

 
?

失敗

本ライブラリでは、「失敗」とはPEG_Failureインスタンスのことを指す。

また、「パーサが失敗する」とは、パーサのparseメソッドが失敗を返すことを指す。

PEG_Failureインスタンスは、パーサがコンテキストから読み出した対象を解釈出来ない場合に返される。

このオブジェクトを取り寄せるには、PEG::failure()メソッドを利用する事ができる。

 
?

本ライブラリについて

特徴

  • PHPで記述されており、パーサの構築にはPHPそのものを記述する
  • PEGで定義されている演算子は、PEGクラスメソッドとして実装されている
  • PEGで定義されている演算子の他に、実際にパーサを書く際に便利なヘルパーメソッドを用意している
  • 文字列だけではなく、配列もパーサに与えることができる
  • PEG_IContextを実装することで任意のデータ構造をパーサに与えることができる

実装

  • 再帰下降型のパーサである
  • 左再帰には対応していないし、対応する予定も無い
  • 通常のパーサはメモ化されないので、メモ化する場合はPEG::memo()メソッドを利用する

基本的な使い方

  1. パーサの構築
  2. コンテキストの構築
  3. パーサにコンテキストを与える
パーサの構築

パーサは通常PEGクラスメソッドから生成される。

ユーザは解釈したい文法にあわせて任意のパーサを構築する。

<?php

$parser = PEG::seq('a', 'b');
コンテキストの構築

PEG::contextメソッドを使うことで通常利用するコンテキストは生成される。

コンテキストは、パーサが解釈する対象や、対象の現在の位置の状態等を持つ。

<?php

$context = PEG::context('abc');
パーサにコンテキストを与える

パーサにコンテキストを与えるにはparseメソッドを呼び出す。

この返り値がパーサが返す結果である。パーサは失敗を返す事もある。

<?php

$result = $parser->parse($context);
 
?
閉じる
C-s
保存
C-a
行頭へ移動
C-e
行末へ移動
C-p
一行上へ
C-n
一行下へ
C-f
右に移動
C-b
左に移動
C-m
改行
C-h
backspace