Brainfuck

Školení, která pořádám

Před časem jsem se dozvěděl o minimalistickém programovacím jazyce Brainfuck. Přestože má jenom osm příkazů, které se navíc zapisují jedním znakem a je tedy extrémně snadný na naučení a používání, tak na něj asi nepřejdu. Napsal jsem si tedy alespoň interpret tohoto jazyka v PHP:

<?php
$code = "++++++++[>+++++++++<-]>.<+++++[>++++++<-]>-.+++++++..+++.<++++++++[>>++++<<-]>>.<<++++[>------<-]>.<++++[>++++++<-]>.+++.------.--------.>+.";

$buffer = "";
$pos = 0;
$while = array(); // array($i, ...)
for ($i=0; $i < strlen($code); $i++) {
    switch ($code[$i]) {
        case '>': $pos++; break;
        case '<': $pos--; break;
        case '+': $buffer[$pos] = chr(ord($buffer[$pos]) + 1); break;
        case '-': $buffer[$pos] = chr(ord($buffer[$pos]) - 1); break;
        case '.': echo $buffer[$pos]; break;
        case ',': $buffer[$pos] = fgetc(STDIN); break;
        case '[': if (ord($buffer[$pos])) $while[] = $i; else for ($depth=1; $depth; $i++) {
            if ($code[$i+1] == '[') $depth++;
            elseif ($code[$i+1] == ']') $depth--;
        }
        break;
        case ']': $i = array_pop($while) - 1; break;
        default: echo "Unrecognized character.\n"; exit(1);
    }
}
?>

Skript je přímočarý – v řetězci $code je uložen kód programu v Brainfucku, tímto řetězcem se prochází přes proměnnou $i, zásobník while cyklu je uložen v proměnné $while, obsah pásky v $buffer a pozice na této pásce je uložena v $pos. Chyby v Brainfuck kódu nejsou ošetřeny, takže např. při chybějící pravé závorce se program může dostat do nekonečné smyčky. Za všimnutí možná stojí, že pro přímý přístup k jednotlivým znakům řetězce se používají složené závorky a že znak se načítá ze streamu STDIN (standardní vstup otevřený v PHP-cli). Zneužívá se faktu, že při přístupu ke znaku na neexistující pozici řetězce se s ním zachází jako s \0 prázdným řetězcem.

Ošetření příkazu ] tupě ukončí cyklus a vrátí běh na [, který cyklus v závislosti na obsahu pásky případně opět spustí. Optimalizovat by se to dalo tak, že by podmínku testoval už ]:

<?php
switch ($code[$i]) {
    case ']': if (ord($buffer[$pos])) $i = end($while); else array_pop($while); break;
}
?>
Jakub Vrána, Řešení problému, 7.10.2005, diskuse: 5 (nové: 0)

Diskuse

ikona llook:

"při přístupu ke znaku na neexistující pozici řetězce se s ním zachází jako s \0."

Ve skutečnosti má hodnotu prázdného řetězce. Ale ord('') vrací 0, takže se s tím dá takhle pracovat.

Horší je, že přístup k neexistující pozici vyvolává chybu úrovně E_NOTICE (Uninitialized string offset). Co je ovšem zajímavý - když zapnu E_STRICT, tak to tu chybu nedělá (PHP 5.1.0RC1).

ikona Jakub Vrána OpenID:

Díky za upozornění, opravil jsem to.

Řekl bych, že místo <?php error_reporting(E_ALL|E_STRICT); ?> voláš jenom <?php error_reporting(E_STRICT); ?>, ne?

ikona llook:

Já blbec. Spletl jsem si sčítání a násobení - psal jsem E_ALL & E_STRICT :-)

vd:

Priste prosim o interpret jazyka Whitespace ;) diky

Preema:

Jenom bych rad upozornil ze ze specifikace BrainFucku plyne, ze nerozpoznany znak se ma ignorovat - cehoz se vyuziva ke komentarum. Tj mel by se vynechat target "default" v tom switchi

Diskuse je zrušena z důvodu spamu.

avatar © 2005-2024 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.