BrainC
BrainC is a Turing-complete esoteric programming language that extends Brainfuck concepts by introducing named memory strips (Tapes), dynamic aliases, and a unique symbol resolution mechanism known as Resolve.
While traditional Brainfuck relies on a single anonymous data pointer, BrainC allows the programmer to manage multiple named tapes with varying sizes and dynamic head positioning.
Design Philosophy
The core of BrainC is the distinction between Physical Structure and Logical Access:
- Tapes: Named, persistent memory strips. They can be initialized with fixed sizes or resized dynamically during runtime (with a maximum size of 30,000 slots per tape).
- Aliases: Dynamic pointers that can point to either a Tape or a Sub (subroutine) or a another alias.
- Resolve: The process by which the interpreter determines the target of an alias at the moment of execution, allowing for polymorphic-like behavior.
Syntax
Statements are terminated by a semicolon (;). Identifiers for tapes or subs are prefixed with (&) (declaration/aliasing) or (@) (execution/call).
Operators
| Operator | Description | Brainfuck Equivalent |
|---|---|---|
| ++ / -- | Increment / Decrement value at current slot | + / - |
| >> / << | Move tape head forward / backward | > / < |
| ~> / <~ | Slot Set / Get: Copy value between two tapes | N/A |
| => / <= | Head Set / Get: Set head position (indexed access) | N/A |
| ?> / <? | Size Set / Get: Resize or read the length of a tape | N/A |
Control Structures
- Loops: &tape [ ... ]; — The loop continues as long as the value at the current slot of the target tape is non-zero, note that the loop resolve each time the target tape after each loop, so that the target tape can be changed while looping, when the current slot of the target tape is zero, it skip all the remaining line of code in the loop.
Operand Order
In BrainC, the order of operands depends strictly on the operator used. The syntax is designed to be directional and visual.
- Right-Directed (Source → Destination): For operators like (~>) the set value, (=>) the set head, and (?>) the set size, the left operand is the source and the right operand is the destination.
- Example:
10 ~> &tape; // the value 10 is moved into the tape current slot.
- Left-Directed (Destination ← Source): For operators like (<~) the get value, (<=) the get head, and (<?) the get size, the right operand is the source and the left operand is the destination.
- Example:
&tape <? &size_var; // the size of the src tape (here "size_var")is moved into the current slot of the &dst tape (here "tape").
- In-Place (Target Operator): For (++), (--), (>>), and (<<), the left operand acts as the amount and the right operand is the target.
- Example:
5 ++ &tape; // increment the current slot of the tape by 5.
Or
&src -- &dst // decrement the current slot of the &dst tape by the current slot value of the &src tape.
Subroutines (Chunks)
Subroutines in BrainC (often called Chunks) are named blocks of code designed for reuse. They differ from functions in traditional languages in several key ways:
- No Arguments: You cannot pass values directly to a sub. Instead, you must set the values of global Tapes (via aliases) before calling the sub.
- No Return Value: Subs do not return data. Any "output" must be stored in a agreed-upon global Tape.
- Global Scope: Subs execute directly on the global state. Modifying a tape inside a sub affects that tape everywhere.
Definition Syntax:
&my_chunk: {
1 ++ &some_tape;
};
Invocation Syntax:
@my_chunk;
This "Chunk" model relies on the Resolve mechanism: you point generic aliases (like &if_equal_left) to specific Tapes before invoking the subroutine that uses them.
Examples
Hello World
#std_io; &msg:["Hello, World!"]; // the string &std_out_txt_tgt:&msg; // the alias to be resovled that the builint depand on @std_out_txt; // the builtin sub to call
Conditional Logic (If Equal)
This example demonstrates how to implement an equality check using subtraction and tape-based flags:
&if_equal_left:[1];
&if_equal_right:[1];
&if_equal_cond:[1];
&if_equal_temp:[1];
&else_equal_cond:[1];
&if_equal:{
0 ~> &if_equal_cond;
1 ~> &else_equal_cond;
&if_equal_left ~> &if_equal_temp;
&if_equal_right -- &if_equal_temp;
&if_equal_temp [
1 ~> &if_equal_cond;
0 ~> &if_equal_temp;
];
&if_equal_cond [
@else_equal_body;
0 ~> &else_equal_cond;
0 ~> &if_equal_cond;
];
&else_equal_cond [
@if_equal_body;
0 ~> &else_equal_cond;
];
};
Guess Game
#std_io;
#"if_equal.bc";
&msg_win:["Win !"];
&msg_lose:["Lose..."];
&magic_number:[1];
42 ~> &magic_number;
&if_equal_body: {
&std_out_txt_tgt:&msg_win;
@std_out_txt;
};
&else_equal_body: {
&std_out_txt_tgt:&msg_lose;
@std_out_txt;
};
&std_out_txt_tgt:["Enter number : "];
@std_out_txt;
@std_in_num;
&std_in_num_res ~> &if_equal_left;
&magic_number ~> &if_equal_right;
@if_equal;
Implementation
The language features a robust interpreter written in C. It includes:
- A REPL (Interactive Shell) for real-time memory inspection.
- A Pre-processor for file inclusions (#include).
- A Standard Library (#std_io) for console input/output of numbers and strings.