So ... mir war fad. Befunge Interpreter für CLI von PHP5 hier als Source (zu verwenden wie auch immer - aber mit Nennung vom ursprünglichen Autor bei weiterentwicklung). Wer kein PHP5 zur Hand hat und nicht mal schnell "./configure ; make ; make install" machen möchte kann natürlich die throws durch trigger_error() ersetzen und die PPP-Keywords weg lassen.
#!/usr/local/bin/php
<?php
error_reporting(E_ALL);
define('DIR_UP', 0);
define('DIR_RIGHT', 1);
define('DIR_DOWN', 2);
define('DIR_LEFT', 3);
class Befunge {
private $direction = DIR_RIGHT;
private $x = 0;
private $y = 0;
private $max_x = 0;
private $max_y = 0;
private $code = 0;
private $stack = array();
private $string_mode = false;
public $debug = false;
public $disable_input = false;
public function __construct($code) {
$code = str_replace(array("\n\r", "\r\n", "\r\r", "\n\n", "\r"), "\n", $code);
$this->code = explode("\n", $code);
foreach($this->code as $num=>$line) {
$this->max_y = $num;
if(strlen($line) > $this->max_x) {
$this->max_x = strlen($line);
}
}
}
private function get_instruction() {
if($this->x > strlen($this->code[$this->y])) {
return ' ';
} else {
return $this->code[$this->y]{$this->x};
}
}
private function move_ip($step = 1, $direction = null) {
if(!is_null($direction)) {
$this->direction = $direction;
}
switch($this->direction) {
case DIR_UP:
--$this->y;
break;
case DIR_RIGHT:
++$this->x;
break;
case DIR_DOWN:
++$this->y;
break;
case DIR_LEFT:
--$this->x;
break;
default:
throw new Exception('move_ip(): unknown direction '.$this->direction);
}
if($this->y > $this->max_y) {
$this->y = $this->y % $this->max_y;
}
if($this->y < 0) {
$this->y = $this->y % $this->max_y + $this->max_y;
}
if($this->x > $this->max_x || $this->x < 0) {
$this->x = $this->x % $this->max_x;
}
if($this->x < 0) {
$this->x = $this->x % $this->max_x + $this->max_x;
}
}
private function evalute_instruction($instruction) {
if($this->string_mode && $instruction != '"') {
$this->stack_push(ord($instruction));
$this->move_ip();
return;
}
switch($instruction) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
$this->stack_push($instruction);
break;
case '"':
$this->string_mode = !$this->string_mode;
break;
case '+':
$a = $this->stack_pop();
$b = $this->stack_pop();
$this->stack_push($a + $b);
break;
case '-':
$a = $this->stack_pop();
$b = $this->stack_pop();
$this->stack_push($b - $a);
break;
case '*':
$a = $this->stack_pop();
$b = $this->stack_pop();
$this->stack_push($a * $b);
break;
case '/':
$a = $this->stack_pop();
if(!$a) {
throw new Exception('evalute_instruction(): division by zero');
}
$b = $this->stack_pop();
$this->stack_push((int)($b / $a));
break;
case '%':
$a = $this->stack_pop();
if(!$a) {
throw new Exception('evalute_instruction(): division by zero');
}
$b = $this->stack_pop();
$this->stack_push($b % $a);
break;
case '!':
$this->stack_push($this->stack_pop() ? 0 : 1);
break;
case '`':
$a = $this->stack_pop();
$b = $this->stack_pop();
$this->stack_push($b > $a ? 1 : 0);
break;
case '^':
$this->move_ip(1, DIR_UP);
return;
break;
case '>':
$this->move_ip(1, DIR_RIGHT);
return;
break;
case 'v':
$this->move_ip(1, DIR_DOWN);
return;
break;
case '<':
$this->move_ip(1, DIR_LEFT);
return;
break;
case '?':
$this->move_ip(1, rand(0,3));
return;
break;
case '_':
$this->move_ip(1, $this->stack_pop() ? DIR_LEFT : DIR_RIGHT);
return;
break;
case '|':
$this->move_ip(1, $this->stack_pop() ? DIR_UP : DIR_DOWN);
return;
break;
case ':':
$a = $this->stack_pop();
$this->stack_push($a);
$this->stack_push($a);
break;
case '\\':
$a = $this->stack_pop();
$b = $this->stack_pop();
$this->stack_push($a);
$this->stack_push($b);
break;
case '$':
$this->stack_pop();
break;
case '.':
$this->output($this->stack_pop());
break;
case ',':
$this->output(chr($this->stack_pop()), true);
break;
case '#':
$this->move_ip(2);
return;
break;
case 'g':
$y = $this->stack_pop();
$x = $this->stack_pop();
if($x > $this->max_x || $y > $this->max_y) {
throw new Exception("evalute_instruction(): cannot get from position ($x|$y) - out of range");
}
$this->stack_push($this->code[$y]{$x});
break;
case 'p':
$y = $this->stack_pop();
$x = $this->stack_pop();
$v = $this->stack_pop();
if($x > $this->max_x || $y > $this->max_y) {
throw new Exception("evalute_instruction(): cannot put to position ($x|$y) - out of range");
}
$this->code[$y]{$x} = chr($v);
break;
case '&':
case '~':
if($this->disable_input) {
throw new Exception("evalute_instruction(): input instructions are diabled");
}
$input = fgets(STDIN);
if($input === false) {
throw new Exception("evalute_instruction(): cannnot read from STDIN");
}
if($instruction == '&') {
$input = trim($input);
if(!is_numeric($input)) {
throw new Exception("evalute_instruction(): you did not enter a number");
}
$input = (int)$input;
} else {
$input = ord($input{0});
}
$this->stack_push($input);
break;
case ' ':
case "\t":
break;
default:
throw new Exception("evalute_instruction(): $instruction is unkown");
break;
}
$this->move_ip();
}
private function stack_pop() {
if(empty($this->stack)) {
throw new Exception('stack_pop(): stack is empty - nothing to pop');
}
return array_pop($this->stack);
}
private function stack_push($value) {
return array_push($this->stack, $value);
}
private function output($value, $literal = false) {
if(!$literal) {
$value = "($value) ";
}
if($this->debug) {
$value = "\n\tOutput: $value\n";
}
echo $value;
}
private function print_code() {
echo "\n[Code]\n";
foreach($this->code as $y=>$line) {
echo ' ';
$length = strlen($line);
for($x = 0; $x < $length; ++$x) {
if($y == $this->y && $x == $this->x) {
echo "\010[";
}
echo $line{$x};
if($y == $this->y && $x == $this->x) {
echo ']';
} else {
echo ' ';
}
}
echo "\n";
}
}
private function print_stack() {
echo "\n[Stack]\n";
if(empty($this->stack)) {
echo '(empty)';
}
foreach($this->stack as $val) {
echo '[';
if($val > 32) {
echo chr($val).'=';
}
echo $val.'] ';
}
}
public function run() {
while(1) {
$instruction = $this->get_instruction();
if($this->debug) {
$this->print_stack();
$this->print_code();
}
if($instruction == '@') {
return;
} else {
$this->evalute_instruction($instruction);
}
}
}
}
if(in_array('--help', $argv) || in_array('-h', $argv)) {
echo <<<USAGE
Befunge interpreter
Author: Nico Edtinger <
[email protected]>
$argv[0] {--input <filename>} {--debug}
--input
filename for your befunge file, default is stdin
--debug
output debug information
USAGE;
die();
}
$disable_input = false;
if(in_array('--input', $argv)) {
$filename = $argv[array_search('--input', $argv) + 1];
if(!file_exists($filename)) {
throw new Exception($filename.' not found');
}
} else {
$filename = 'php://stdin';
$disable_input = true;
}
$code = file_get_contents($filename);
$befunge = new Befunge($code);
if(in_array('--debug', $argv)) {
$befunge->debug = true;
}
if($disable_input) {
$befunge->disable_input = true;
}
$befunge->run();
echo "\n";
?>
- und wer noch fragt "was mach ich jetzt damit?" - bitte Befunge-Code schreiben der Fibonacci Zahlen berechnen kann. Have fun.