Skip to content

Instantly share code, notes, and snippets.

@m3m0r7
Created May 5, 2020 01:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save m3m0r7/a8c37e8bebf2200cd1a8e59262ffe636 to your computer and use it in GitHub Desktop.
Save m3m0r7/a8c37e8bebf2200cd1a8e59262ffe636 to your computer and use it in GitHub Desktop.
だなも言語 for animal crossing
<?php
class DanamoBuiltInFunction
{
public function 結果(array $node): int
{
['left' => $left, 'operator' => $operator, 'right' => $right] = $node['code'];
$left = $left['value'];
if ($right !== null) {
$right = $this->遡って計算($right);
}
return $this->四則演算($left, $operator, $right);
}
public function 階乗($number): int
{
$result = 1;
for ($i = $number; $i > 0; $i--) {
$result *= $i;
}
return $result;
}
public function 出力($string): void
{
echo $string . "\n";
}
public function 終了(): void
{
exit();
}
private function 遡って計算(array $node): int
{
['left' => $left, 'operator' => $operator, 'right' => $right] = $node;
if ($right !== null) {
$right = $this->遡って計算($right);
$left = $left['value'];
} else {
return $left['value'];
}
return $this->四則演算($left, $operator, $right);
}
private function 四則演算($left, $operator, $right)
{
switch ($operator) {
case '+':
return $left + $right;
case '-':
return $left - $right;
case '/':
if ($right === 0) {
throw new RuntimeException('0 で割れないんだなも!0には夢がいっぱい詰まってるんだなも');
}
return $left / $right;
case '*':
return $left * $right;
}
throw new RuntimeException('計算に失敗したんだなも!申し訳ないんだなも');
}
}
class DanamoLang
{
const EXPRESSION = 0;
const FUNCTION_CALL = 1;
const NUMBER = 1000;
const UNKNOWN = 9999;
protected $handle;
public function __construct(string $file)
{
if (!is_file($file)) {
throw new RuntimeException('だなも言語のファイルがそんざいしないんだなも!パスを確認するんだなも');
}
$this->handle = fopen($file, 'r');
}
public function run()
{
$lineNodes = $this->parseToNode();
$danamoBuiltInFunction = new DanamoBuiltInFunction();
// AST のノードからコードを走らせるんだなも
foreach ($lineNodes as $lineNode) {
foreach ($lineNode as &$node) {
['type' => $type, 'code' => $code, 'func' => $func] = $node;
if ($type !== static::FUNCTION_CALL) {
continue;
}
if (!method_exists($danamoBuiltInFunction, $func)) {
throw new RuntimeException('"' . $func . '" という関数はないんだなも!コードを見直すんだなも');
}
$node = $danamoBuiltInFunction->{$func}($code);
}
}
}
protected function parseToNode(): array
{
$nodes = [];
while ($line = fgets($this->handle)) {
$stacks = [];
$line = rtrim($line);
$m = mb_strlen($line);
$currentStack = 0;
$before = null;
$foundDanamo = false;
$current = null;
$chars = mb_substr($line, -3, 3);
if ($chars !== 'だなも') {
throw new RuntimeException('だなもで終わってないだなも!この構文はエラーになるんだなも!');
}
for ($i = 0; $i < $m; $i++) {
$tmpStack = null;
$char = mb_substr($line, $i, 1);
if (substr_count('0123456789+-/*', $char) > 0) {
$current = static::EXPRESSION;
} elseif ($char === 'の' || $char === 'を') {
$current = static::FUNCTION_CALL;
} else {
$current = static::UNKNOWN;
for (; $i < $m; $i++) {
$chars = mb_substr($line, $i, 2);
if ($chars === 'する') {
$i += 2;
if ($before !== null) {
$currentStack++;
}
$current = static::FUNCTION_CALL;
break;
}
$tmpStack .= mb_substr($line, $i, 1);
}
if ($current === static::FUNCTION_CALL) {
if (isset($stacks[$currentStack - 1])) {
$stacks[$currentStack] = [
'type' => $current,
'code' => &$stacks[$currentStack - 1],
'func' => $tmpStack,
];
} else {
$stacks[$currentStack] = [
'type' => $current,
'code' => null,
'func' => $tmpStack,
];
}
break;
}
}
if ($before !== null && $before !== $current) {
$currentStack++;
}
if ($foundDanamo === true) {
break;
}
if (!isset($stacks[$currentStack])) {
if ($current === static::FUNCTION_CALL) {
$stacks[$currentStack] = [
'type' => $current,
'code' => &$stacks[$currentStack - 1],
'func' => $tmpStack,
];
} else {
$stacks[$currentStack] = [
'type' => $current,
'code' => null,
'func' => null,
];
}
}
$before = $current;
switch ($current) {
case static::EXPRESSION:
$stacks[$currentStack]['code'] = ($stacks[$currentStack]['code'] ?? '') . $char;
break;
case static::FUNCTION_CALL:
$stacks[$currentStack]['code'] = &$stacks[$currentStack - 1];
$stacks[$currentStack]['func'] = '';
for ($i++; $i < $m; $i++) {
$char = mb_substr($line, $i, 1);
if ($char === 'の') {
$i--;
$currentStack++;
break;
}
if ($char === 'を') {
break;
}
$stacks[$currentStack]['func'] = ($stacks[$currentStack]['func'] ?? '') . $char;
}
break;
}
}
// 式をノード化するんだなも
foreach ($stacks as &$stack) {
[ 'type' => $type, 'code' => &$code ] = $stack;
switch ($type) {
case static::EXPRESSION:
$code = $this->extractToNode($code);
break;
}
}
$nodes[] = $stacks;
}
return $nodes;
}
protected function extractToNode(string $code): array
{
$node = [];
$leftOperand = '';
$rightOperand = null;
$operator = null;
for ($i = 0; $i < strlen($code); $i++) {
$char = $code[$i];
if (substr_count('+-/*', $char) > 0) {
$operator = $char;
$rightOperand = $this->extractToNode(substr($code, $i + 1));
break;
}
$leftOperand .= $char;
}
return [
'left' => ['type' => static::NUMBER, 'value' => $leftOperand],
'operator' => $operator,
'right' => $rightOperand,
];
}
}
try {
$danamo = new DanamoLang($argv[1]);
$danamo->run();
} catch (RuntimeException $e) {
echo $e->getMessage() . "\n";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment