Bc. Vojtěch Hordějčuk

„Dejte mi pevný bod a já pohnu Zemí.” - Archimedes

Domů » Wiki » Programovací jazyky » Jazyk Brainfuck

Jazyk Brainfuck

Programovací jazyk Brainfuck patří mezi ezoterické jazyky, určené k pobavení a zamyšlení programátorů. Navrhl jej v roce 1993 švýcarský programátor Urban Müller a obsahuje pouze 8 příkazů, kde každý z nich je reprezentován právě jedním ASCII znakem. Neznámé znaky jsou ignorovány. Brainfuck je však i přesto Turingovsky úplný a má tedy formálně stejné schopnosti jako ostatní programovací jazyky, například C++ nebo Java.

Seznam příkazů

Programovací jazyk Brainfuck obsahuje celkem 8 příkazů. Má k dispozici pole buněk o zadané velikosti (obvykle 30000), které je na počátku vynulováno. Každá buňka může nabývat hodnot 0–255 (rozsah je tedy jeden bajt). Aktuální buňku určuje tzv. datový ukazatel.

Příkaz Význam
< Přesune datový ukazatel o jednu buňku vlevo
> Přesune datový ukazatel o jednu buňku vpravo
+ Inkrementuje hodnotu v aktuální buňce
- Dekrementuje hodnotu v aktuální buňce
. Vypíše hodnotu v aktuální buňce (obvykle jako odpovídající znak ASCII)
, Načte hodnotu na vstupu do aktuální buňky (obvykle ASCII hodnotu zadaného znaku)
pravá hranatá závorka Je-li hodnota v aktuální buňce rovna 0, přesune instrukční ukazatel za odpovídající levou závorku
levá hranatá závorka Je-li hodnota v aktuální buňce různá od 0, přesune instrukční ukazatel na odpovídající pravou závorku

Program „Hello World!“

Triviální verze

+++++ +++++ +++++ +++++ +++++
+++++ +++++ +++++ +++++ +++++
+++++ +++++ +++++ +++++ ++ .
+++++ +++++ +++++ +++++ +++++
++++ .
+++++ ++ ..
+++ .
----- ----- ----- ----- -----
----- ----- ----- ----- -----
----- ----- ----- ----- -----
----- ----- .
+++++ +++++ +++++ +++++ +++++
+++++ +++++ +++++ +++++ +++++
+++++ +++++ +++++ +++++ +++++
+++++ +++++ +++++ +++ .
----- --- .
+++ .
----- - .
----- --- .
----- ----- ----- ----- -----
----- ----- ----- ----- -----
----- ----- ----- -- .
----- ----- ----- ----- .
--- .

zdrojový kód (Brainfuck) - zobrazit (516 znaků)

Optimalizovaná verze

Využitím cyklů lze kód značně zkrátit.

++++++++++[>+++++++>++++
++++++>+++>+<<<<-]>++.>+
.+++++++..+++.>++.<<++++
+++++++++++.>.+++.------
.--------.>+.>.

zdrojový kód (Brainfuck) - zobrazit (115 znaků)

Jednoduché programy

Nulování aktuální buňky

[-]

zdrojový kód (Brainfuck) - zobrazit (3 znaků)

Vyhledání první nenulové buňky

Dopředné hledání (fast forward):

[>]<

zdrojový kód (Brainfuck) - zobrazit (4 znaků)

Zpětné hledání (rewind):

[<]>

zdrojový kód (Brainfuck) - zobrazit (4 znaků)

Sčítání

Nechť buňka #0 obsahuje první sčítanec a buňka #1 druhý. Po skončení následující cyklu bude v buňce #0 nula a v buňce #1 součet.

[->+<]

zdrojový kód (Brainfuck) - zobrazit (6 znaků)

Interpretr (Java)

Třída v jazyce Java, která provádí interpretaci programů v jazyce Brainfuck, vypadá následovně:

import java.util.Scanner;

/**
 * Interpretr jazyka Brainfuck.
 * @author Vojtěch Hordějčuk
 */

public class Interpreter
{
  /**
   * Třída představující datovou buňku.
   */

  private static class Cell
  {
    /**
     * hodnota buňky
     */

    private int data;
    /**
     * ukazatel na předchozí buňku
     */

    private Cell previous;
    /**
     * ukazatatel na další buňku
     */

    private Cell next;

    /**
     * Vytvoří novou buňku s výchozí hodnotou
     */

    public Cell ()
    {
      this.data = 0;
      this.previous = null;
      this.next = null;
    }

    /**
     * Ověří, zda je buňka nulová.
     * @return TRUE právě když je buňka nulová
     */

    public boolean isZero ()
    {
      return (this.data == 0);
    }

    /**
     * Vypíše hodnotu buňky na standardní výstup jako ASCII symbol.
     */

    public void print ()
    {
      System.out.print ((char) this.data);
    }

    /**
     * Nastaví novou hodnotu buňky.
     * @param value nová hodnota buňky
     */

    public void set (final int value)
    {
      if (value >= 0 && value <= 255)
      {
        this.data = value;
      }
      else
      {
        System.err.println ("Cell value out of range (0 to 255).");
        System.exit (-10);
      }
    }

    /**
     * Dekrementuje hodnotu v buňce.
     */

    public void decrement ()
    {
      if (this.data > 0)
      {
        this.data --;
      }
      else
      {
        System.err.println ("Cannot decrease the value (min = 0).");
        System.exit (-11);
      }
    }

    /**
     * Inkrementuje hodnotu v buňce.
     */

    public void increment ()
    {
      if (this.data < 255)
      {
        this.data ++;
      }
      else
      {
        System.err.println ("Cannot increase the value (max = 255).");
        System.exit (-12);
      }
    }

    /**
     * Vrátí předchozí buňku, kterou v případě potřeby vytvoří.
     * @return předchozí buňka
     */

    public Cell getPrevious ()
    {
      if (this.previous == null)
      {
        this.previous = new Cell ();
        this.previous.next = this;
      }

      return this.previous;
    }

    /**
     * Vrátí následující buňku, kterou v případě potřeby vytvoří.
     * @return následující buňka
     */

    public Cell getNext ()
    {
      if (this.next == null)
      {
        this.next = new Cell ();
        this.next.previous = this;
      }

      return this.next;
    }
  };
  /**
   * výchozí datová buňka
   */

  private Cell head;
  /**
   * scanner pro zpracování standardního vstupu
   */

  final Scanner scanner;

  /**
   * Vytvoří nový interpretr Brainfucku.
   */

  public Interpreter ()
  {
    // vytvořit výchozí datovou buňku

    this.head = new Cell ();

    // vytvořit scanner

    this.scanner = new Scanner (System.in);
  }

  /**
   * Spustit zadaný program v jazyce Brainfuck.
   * @param program pole znaků, kde znaky jsou jednotlivé instrukce
   * @return TRUE právě když program skončil úspěšně
   */

  public boolean start (final char[] program)
  {
    // zkontrolovat parametry

    if (program.length < 1)
    {
      System.out.println ("The program is empty.");
      return true;
    }

    // inicializovat ukazatel

    int instructionPointer = 0;

    // inicializovat scanner

    this.scanner.reset ();

    // spustit provádění instrukce

    while (instructionPointer < program.length)
    {
      switch (program[instructionPointer])
      {
        case '+':

          // inkrementace aktuální buňky

          this.head.increment ();
          instructionPointer ++;
          break;

        case '-':

          // dekrementace aktuální buňky

          this.head.decrement ();
          instructionPointer ++;
          break;

        case '<':

          // posun o buňku vlevo

          this.head = this.head.getPrevious ();
          instructionPointer ++;
          break;

        case '>':

          // posun o buňku vpravo

          this.head = this.head.getNext ();
          instructionPointer ++;
          break;

        case ',':

          // načíst vstup do aktuální buňky

          String input;

          do
          {
            System.out.print ("Input: ");

            input = this.scanner.nextLine ();

            if (input.length () == 1)
            {
              break;
            }

            System.out.println ("Invalid input. Type one character only and press ENTER.");
          }
          while (true);

          this.head.set ((int) input.charAt (0));
          instructionPointer ++;
          break;

        case '.':

          // vypsat aktuální buňku na výstup jako ASCII

          this.head.print ();
          instructionPointer ++;
          break;

        case '[':

          // je-li hodnota aktuální buňky nulová, přesunout instrukční ukazatel za odpovídající "]"

          if (this.head.isZero ())
          {
            int depth = 0;
            int ictemp = instructionPointer + 1;

            while (ictemp < program.length - 1)
            {
              if (program[ictemp] == '[')
              {
                depth ++;
              }
              else if (program[ictemp] == ']')
              {
                if (depth == 0)
                {
                  instructionPointer = ictemp + 1;
                  break;
                }
                else
                {
                  depth --;
                }
              }

              ictemp ++;
            }

            if (depth != 0)
            {
              System.err.println ("Invalid parenthesis.");
              return false;
            }
          }
          else
          {
            instructionPointer ++;
          }

          break;

        case ']':

          // je-li hodnota aktuální buňky různá od nuly, přesunout instrukční ukazatel na odpovídající "["

          if (this.head.isZero ())
          {
            instructionPointer ++;
          }
          else
          {
            int depth = 0;
            int ictemp = instructionPointer - 1;

            while (ictemp > 0)
            {
              if (program[ictemp] == '[')
              {
                if (depth == 0)
                {
                  instructionPointer = ictemp;
                  break;
                }
                else
                {
                  depth --;
                }
              }
              else if (program[ictemp] == ']')
              {
                depth ++;
              }

              ictemp --;
            }

            if (depth != 0)
            {
              System.err.println ("Invalid parenthesis.");
              return false;
            }
          }

          break;

        default:

          // neznámé znaky budou přeskočeny

          instructionPointer ++;
          break;
      }
    }

    return true;
  }
}

zdrojový kód (Java) - zobrazit (6853 znaků)

Reference