examples

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 * +
 
?
閉じる
C-s
保存
C-a
行頭へ移動
C-e
行末へ移動
C-p
一行上へ
C-n
一行下へ
C-f
右に移動
C-b
左に移動
C-m
改行
C-h
backspace