<?php
/*************************************************\
* PHP Befunge interpeter class. *
* Befunge-93 completed 1/27/07 *
* *
* Original work By Jeremy Sturdivant *
* *
**Interpeter for the befunge programming language *
**Handles reading files, arrays *
**Will read unefunge programs correctly *
**Befunge-98 support planned *
**Easy to use, just include the class, spawn, *
* call init, call table_load, and loop cycle(). *
* \********\
* This work released under the Creative Commons *
* Attribution-Share Alike 2.5 License *
* http://creativecommons.org/licenses/by-sa/2.5/ *
* You are free to modify this work as long as credit *
* is given and the derivative is licenced under the same *
* licence as this one. *
* *
\**********************************************************/
// $stacks[0] is stack pointers, $stacks[0][0] is the stack stack pointer, for the stack stack
// befunge($char) should be called with one char (e.g. '<') to interpet
class php_befunge{
// Stacks go here...
public $stacks;
// Vector is coordinates and a direction. basically the instruction pointer in 2d :D
// like so: array(x,y,v) with v (vector)
// b00 (0) = up, b11 (3) = down, b01 (1) = right, b10 (2) = left
// that way (3 ^ vector) turns around and goes the other way :D
// also (1 ^ vector) is like a / type mirror :D
// plus (2 ^ vector) is like a \ type mirror :D
public $vector;
// The table is the playing field. its where the current code is :D
public $table;
// Debug mode is currently on if > 0 :D
public $debug;
// called at the beginning. sets everything to the norm :D
function init(){
$this->stacks[0][0] = 1;
$this->stacks[0][1] = 0;
$this->stacks[1][0] = 0;
$this->vector = array(0,0,1);
$this->debug = 0;
}
// loads a file, or an array, into the table variable...
function table_load($input,$type=1){
// $type 0 loads a file by name, type 1 takes a 2-d array of 1 char strings, type 2 takes an array of single characters
if($type==1){
$this->table = $input;
}elseif($type==2){
$i = 0;
foreach($input as $item){
$this->table[$i++] = $item;
}
}else{
$this->table = file($input);
}
}
// Shift the stack stack by x, -1 to go down, 1 to go up.
function shift_stack($howmuch){
$this->stacks[0][0] = (($howmuch + $this->stacks[0][0]) >= 0)?$this->stacks[0][0]+$howmuch:0;
}
// pop the current stack, and return it.
function stack_pop(){
$stack_index = $this->stacks[0][0];
$stack_pointer = ($this->stacks[0][$stack_index] == 0) ? 0 : $this->stacks[0][$stack_index]--;
return (($stack_pointer > 0) ? $this->stacks[$stack_index][$stack_pointer-1] : 0);
}
// push $val onto the current stack
function stack_push($val){
$stack_index = $this->stacks[0][0];
$stack_pointer = $this->stacks[0][$stack_index]++;
$this->stacks[$stack_index][$stack_pointer] = $val+0;
}
// incriment the vector
function move($invector){
switch($invector % 2){
case true:
if($invector == 3){
$this->vector[1]++;
}elseif($invector == 1){
$this->vector[0]++;
}
break;
case false:
if($invector == 2){
$this->vector[0]--;
}elseif($invector == 0){
$this->vector[1]--;
}
break;
}
}
// handle stuff that always happens every automatic cycle.
// simply using while(1){$funge->cycle();} after table_load and init
// will suffice for running a table.
function cycle(){
$x = $this->vector[0];
$y = $this->vector[1];
$table = $this->table;
$this->befunge($table[$y]{$x});
$this->move($this->vector[2]);
}
// The interpeter itself. requires stack_push/pop, $strmode, $vector, $table,
// $debug and a few PHP functions/defines like STDIN and floor...
function befunge($char){
$char = $char{0};
if($this->strmode == true && $char !== '"'){
$this->stack_push(ord($char));
}elseif($this->strmode != true || $char === '"'){
switch($char){
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
echo $this->debug > 0 ? 'Pushing '.$char : '';
$this->stack_push($char-0);
break;
case '@':
die("\n".'Program end.'."\n");
break;
case '<':
$this->vector[2] = 2;
break;
case '>':
$this->vector[2] = 1;
break;
case '^':
$this->vector[2] = 0;
break;
case 'v':
$this->vector[2] = 3;
break;
case '#':
$this->move($this->vector[2]);
break;
case '"':
$this->strmode ? $this->strmode = false : $this->strmode = true;
break;
case '.':
echo $this->stack_pop()-0;
break;
case ',':
echo chr($this->stack_pop()-0);
break;
case '$':
$this->stack_pop();
break;
case 'x':
var_dump($this->stacks);
break;
case '\\':
$tmp1 = $this->stack_pop();
$tmp2 = $this->stack_pop();
$this->stack_push($tmp1);
$this->stack_push($tmp2);
break;
case ':':
$tmp = $this->stack_pop();
$this->stack_push($tmp);
$this->stack_push($tmp);
break;
case '+':
$this->stack_push($this->stack_pop()+$this->stack_pop());
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();
if($a==0){
echo 'Debug catch hit, attept to divide by zero. '."\n".'enter a number for the result:';
$this->stack_push(fread(STDIN, 1024)+0);
}else{
echo $this->debug > 0 ? 'Dividing b/a a='.$a.' b='.$b."\n" : '';
$this->stack_push(floor($b/$a));
}
break;
case '*':
$this->stack_push($this->stack_pop()*$this->stack_pop());
break;
case 'p':
$y = $this->stack_pop();
$x = $this->stack_pop();
$v = $this->stack_pop();
$this->table[$y]{$x} = chr($v);
break;
case 'g':
$y = $this->stack_pop();
$x = $this->stack_pop();
$this->stack_push(ord($this->table[$y]{$x}));
break;
case '&':
echo 'Please enter an integer (max 1024 chars):';
$this->stack_push(fread(STDIN, 1024)+0);
break;
case '~':
echo 'Type a character and press return:';
$this->stack_push(ord(fread(STDIN, 1024)));
break;
case '?':
$this->vector[2] = rand(0,3);
break;
case '!':
$this->stack_push(!$this->stack_pop());
break;
case '_':
if($this->stack_pop() == 0){
$this->vector[2] = 1; //right (1)
}else{
$this->vector[2] = 2; //left (2)
}
break;
case '|':
if($this->stack_pop() == 0){
$this->vector[2] = 3; //down (3)
}else{
$this->vector[2] = 0; //up (0)
}
break;
case '`':
$this->stack_push(($this->stack_pop()<$this->stack_pop())-0);
break;
case '%':
$a = $this->stack_pop();
$b = $this->stack_pop();
if($a==0){
echo 'Debug catch hit, attept to modulus divide by zero. '."\n".'enter a number for the result:';
$this->stack_push(fread(STDIN, 1024)+0);
}else{
echo $this->debug > 0 ? 'Modulus dividing b/a a='.$a.' b='.$b."\n" : '';
$this->stack_push(floor($b%$a));
}
break;
}
}
}
}
?>