Skip to content

Instantly share code, notes, and snippets.

@todays-mitsui
Created May 28, 2021 23:21
Show Gist options
  • Save todays-mitsui/814e0b80eeb0665e75104a659724217f to your computer and use it in GitHub Desktop.
Save todays-mitsui/814e0b80eeb0665e75104a659724217f to your computer and use it in GitHub Desktop.
Parsica デモ
{
"require": {
"parsica-php/parsica": "^0.8.1"
}
}
<?php
use Parsica\Parsica\Parser;
use function Parsica\Parsica\{
between,
char,
collect,
either,
integer,
recursive,
};
require(__DIR__."/vendor/autoload.php");
/**
* 整式を前置記法で表記する
*
* @param array|int $ast
* @return string
*/
function prefix_notation($ast)
{
if (!is_array($ast)) { return $ast; }
$operator = $ast["operator"];
$left = prefix_notation($ast["left"]);
$right = prefix_notation($ast["right"]);
return "{$operator} {$left} {$right}";
}
/**
* 整式を後置記法で表記する
*
* @param array|int $ast
* @return string
*/
function postfix_notation($ast)
{
if (!is_array($ast)) { return $ast; }
$operator = $ast["operator"];
$left = postfix_notation($ast["left"]);
$right = postfix_notation($ast["right"]);
return "{$left} {$right} {$operator}";
}
function main()
{
// 各種のパーサを定義
// <expression> := <term> [ "+" <expression> ]
// <term> := <factor> [ "*" <term> ]
// <factor> := <number> | "(" <expression> ")"
/** @var Parser<int> */
$pNumber = integer()->map("intval")->label("number");
/** @var Parser<array> */
$pExpression = recursive()->label("expression");
/** @var Parser<array> */
$pTerm = recursive()->label("term");
/** @var Parser<array> */
$pFactor = recursive()->label("factor");
$pExpression->recurse(
either(
collect($pTerm, char("+"), $pExpression)
->map(fn(array $vals) => [
"operator" => $vals[1],
"left" => $vals[0],
"right" => $vals[2],
])
,
$pTerm
)
);
$pTerm->recurse(
either(
collect($pFactor, char("*"), $pTerm)
->map(fn(array $vals) => [
"operator" => $vals[1],
"left" => $vals[0],
"right" => $vals[2],
])
,
$pFactor
)
);
$pFactor->recurse(
either(
$pNumber,
between(char("("), char(")"), $pExpression)
)
);
// 中置記法で書いた式
$infix_notation = "2*(3+4)+5";
// 抽象構文木 (AST) に変換
$ast = $pExpression->tryString($infix_notation)->output();
// => [
// "operator" => "+",
// "operator" => "*",
// "left" => 2,
// "right" => [
// "operator" => "+",
// "left" => 3,
// "right" => 4,
// ],
// ],
// "right" => 5,
// ]
// 前置記法で出力
echo prefix_notation($ast) . "\n"; // => "+ * 2 + 3 4 5"
// 後置記法で出力
echo postfix_notation($ast) . "\n"; // => "2 3 4 + * 5 +"
}
main();
<?php
use function Parsica\Parsica\{
assemble,
atLeastOne,
char,
everything,
integer,
noneOfS,
optional,
string,
};
require(__DIR__."/vendor/autoload.php");
// URI を Scheme, Host, Path などに分解する
// parse_url() 相当の処理
// 各パーツのパーサを作っていく
$pScheme = string("http")
->append(optional(char("s")))
->map(fn($val) => [ "scheme" => $val ]);
$pUserinfo = atLeastOne(noneOfS(":@"))
->append(char(":"))
->append(atLeastOne(noneOfS("@")))
->thenIgnore(char("@"))
->map(fn($val) => [ "userinfo" => $val ]);
$pHost = atLeastOne(noneOfS(":/"))
->map(fn($val) => [ "host" => $val ]);
$pPort = char(":")->followedBy(integer())
->map(fn($val) => [ "port" => $val ]);
$pPath = atLeastOne(noneOfS("?"))
->map(fn($val) => [ "path" => $val ]);
$pQuery = char("?")
->append(atLeastOne(noneOfS("#")))
->map(fn($val) => [ "query" => $val ]);
$pFragment = char("#")
->append(everything())
->map(fn($val) => [ "fragment" => $val ]);
// 小さなパーサを組み合わせて URI 用のパーサを作る
$pUri = assemble(
$pScheme,
string("://")->map(fn($_) => null),
optional($pUserinfo),
$pHost,
optional($pPort),
optional($pPath),
optional($pQuery),
optional($pFragment)
);
var_dump($pUri->tryString("https://example.com/")->output());
// => [
// "scheme" => "https",
// "host" => "example.com",
// "path" => "/",
// ]
var_dump($pUri->tryString("https://username:path@example.com:8080/a/b/c/?key=val#target")->output());
// => [
// "scheme" => "https",
// "userinfo" => "username:path",
// "host" => "example.com",
// "port" => "8080",
// "path" => "/a/b/c/",
// "query" => "?key=val",
// "fragment" => "#target",
// ]
var_dump($pUri->tryString("https//example.com/")->output());
// => Uncaught Parsica\Parsica\ParserHasFailed: <input>:1:6
// |
// 1 | ...//example.com/
// | ^— column 6
// Unexpected <slash>
// Expecting '://'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment