We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.

Brainlessfuck

From Esolang
Jump to navigation Jump to search

Brainlessfuck is an extension to brainfuck created by User:VIBaJ with the intent of making a tape-based language like brainfuck that's more practical to use while still having a very limited set of simple instructions.

Description

Brainlessfuck extends brainfuck by adding 8 new commands (for a total of 16) and a register. Brainlessfuck also changes the characters for most of brainfuck's commands, but the functions all remain the same. In the following table, t is the tape, p is the pointer, and r is the register.

Commands
Command from brainfuck Command Command name Effect
+ ^ Increment t[p] += 1
- v Decrement t[p] -= 1
* Shift left t[p] <<= 1
/ Shift right t[p] >>= 1
L Load r = t[p]
S Store t[p] = r
+ Add t[p] += r
- Subtract t[p] -= r
& AND t[p] &= r
? Compare Set the LSB and MSB of t[p] to t[p] < r for unsigned and signed comparisons respectively and set the other bits to 0
> > Right Move the pointer right 1 cell
< < Left Move the pointer left 1 cell
[ { Loop start Go to the matching } if t[p] == 0
] } Loop end Go to the matching { if t[p] != 0
. O Output Send t[p] to the output
, I Input Get a byte of input and set t[p] to it

Brainlessfuck also adds line comments started by a #. Unlike brainfuck, characters that aren't commands aren't ignored. Unless they're in a comment, they are considered invalid syntax, except whitespace, which is ignored.

Details

Name

  • Brainlessfuck means (and is pronounced as) "brain less fuck" (i.e., it hurts your brain less than brainfuck).
  • Brainlessfuck is always capitalized.
  • Brainlessfuck is always written with no spaces or other indication of word boundaries because this has the humorous side effect of making it look like "brainless fuck".

Memory

  • The tape extends infinitely to the left and right.
  • All cells are initialized to 0.
  • The size of each cell and the register is 8 bits.

Input and Output

It is up to the implementation for how exactly to handle input and output, as this is where esolang implementation details become less interesting.

Examples

Hello World

These programs print Hello, world! with a newline at the end.

Using a simple pattern:

^***^***O>^*^***^**^O>^*^**^*^**O>^*^**^*^**O>^*^**^*^*^*^O>^**^*^**O>^*****O>^*^*^**^*^*^O>^*^**^*^*^*^O>^*^*^***^*O>^*^**^*^**O>^*^***^**O>^*****^O>^**^*O

The author's first attempt at making the program as short as possible:

^***^***OvvvvL/+vOL>^*^*^+OOL>S^^^O>^**^*^**O>v///^O>^**^*^+O<<<O^^^O<O<vO>>>>^O//^^O

Print Current Cell Value

These programs assume the cells to the right of the current cell are 0.

Works if the value is in the range [0, 9]:

L>^*^****+O

Works for any value (always prints 3 digits for simplicity):

L>S>^*^****L>>S<^*^***^**L<<?v*{<-L>S>^>L<<?v*}>O>>L<<S<<L>S>>///vvL<<?v*{<-L>S>^>L<<?v*}>O<<L>>>>+O

Interpreter

Rust

By User:VIBaJ:

// Brainlessfuck interpreter

// Takes the file containing the Brainlessfuck program as a command line argument
// Has 3 debug commands which are NOT part of Brainlessfuck:
//     N -> toggles number IO mode, which makes IO commands use decimal numbers (always assumes unsigned)
//     _ -> prints a space
//     ; -> prints a newline

use std::env;
use std::io::{self, Read};
use std::fs;
use std::num::Wrapping;

enum Command {
    // Brainlessfuck commands
    Increment,
    Decrement,
    ShiftLeft,
    ShiftRight,
    Load,
    Store,
    Add,
    Subtract,
    And,
    Compare,
    Right,
    Left,
    LoopStart,
    LoopEnd,
    Output,
    Input,
    // debug commands
    ToggleNumberIO,
    Space,
    Newline
}

fn main() -> io::Result<()> {
    let args = env::args().collect::<Vec<_>>();
    if args.len() != 2 {
        println!("Error: expected 1 argument (the file containing the Brainlessfuck program), but received {}.", args.len() - 1);
        return Ok(());
    }
    let mut program = Vec::new();
    let mut comment = false;
    let mut line = 1;
    for c in fs::read_to_string(&args[1])?.chars() {
        match c {
            '\n' => {
                comment = false;
                line += 1;
            },
            _ if comment => {},
            '^' => program.push(Command::Increment),
            'v' => program.push(Command::Decrement),
            '*' => program.push(Command::ShiftLeft),
            '/' => program.push(Command::ShiftRight),
            'L' => program.push(Command::Load),
            'S' => program.push(Command::Store),
            '+' => program.push(Command::Add),
            '-' => program.push(Command::Subtract),
            '&' => program.push(Command::And),
            '?' => program.push(Command::Compare),
            '>' => program.push(Command::Right),
            '<' => program.push(Command::Left),
            '{' => program.push(Command::LoopStart),
            '}' => program.push(Command::LoopEnd),
            'O' => program.push(Command::Output),
            'I' => program.push(Command::Input),
            'N' => program.push(Command::ToggleNumberIO),
            '_' => program.push(Command::Space),
            ';' => program.push(Command::Newline),
            '#' => comment = true,
            _ => if !c.is_whitespace() {
                println!("Syntax error on line {line}: invalid command '{c}'.");
                return Ok(());
            }
        }
    }

    let mut input = io::stdin().bytes();
    let stdin = io::stdin();

    let mut tape = (Vec::new(), vec![Wrapping(0_u8)]);
    let mut register = 0;
    let mut instr_ptr = 0;
    let mut tape_ptr = 0_isize;
    let mut number_io = false;
    while instr_ptr < program.len() {
        let cell = if tape_ptr < 0 { &mut tape.0[-(tape_ptr + 1) as usize] } else { &mut tape.1[tape_ptr as usize] };
        match program[instr_ptr] {
            Command::Increment => *cell += 1,
            Command::Decrement => *cell -= 1,
            Command::ShiftLeft => *cell <<= 1,
            Command::ShiftRight => *cell >>= 1,
            Command::Load => register = cell.0,
            Command::Store => cell.0 = register,
            Command::Add => *cell += register,
            Command::Subtract => *cell -= register,
            Command::And => *cell &= register,
            Command::Compare => cell.0 = (cell.0 < register) as u8 | ((((cell.0 as i8) < register as i8) as u8) << 7),
            Command::Right => {
                tape_ptr += 1;
                if tape_ptr == tape.1.len() as isize {
                    tape.1.push(Wrapping(0));
                }
            },
            Command::Left => {
                tape_ptr -= 1;
                if -(tape_ptr + 1) == tape.0.len() as isize {
                    tape.0.push(Wrapping(0));
                }
            },
            Command::LoopStart => if cell.0 == 0 {
                let mut nesting_level = 1;
                while nesting_level != 0 {
                    instr_ptr += 1;
                    match program[instr_ptr] {
                        Command::LoopStart => nesting_level += 1,
                        Command::LoopEnd => nesting_level -= 1,
                        _ => {}
                    }
                }
            },
            Command::LoopEnd => if cell.0 != 0 {
                let mut nesting_level = 1;
                while nesting_level != 0 {
                    instr_ptr -= 1;
                    match program[instr_ptr] {
                        Command::LoopEnd => nesting_level += 1,
                        Command::LoopStart => nesting_level -= 1,
                        _ => {}
                    }
                }
            },
            Command::Output => {
                let output = cell.0;
                if number_io {
                    print!("{output}");
                } else {
                    print!("{}", output as char);
                }
            },
            Command::Input => cell.0 = if number_io {
                let mut s = String::new();
                stdin.read_line(&mut s)?;
                s.trim().parse().unwrap()
            } else {
                input.next().unwrap_or(Ok(0))?
            },
            Command::ToggleNumberIO => number_io = !number_io,
            Command::Space => print!(" "),
            Command::Newline => println!()
        }
        instr_ptr += 1;
    }

    Ok(())
}