Created
May 5, 2020 01:52
-
-
Save m3m0r7/a8c37e8bebf2200cd1a8e59262ffe636 to your computer and use it in GitHub Desktop.
だなも言語 for animal crossing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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