This was a fun little project which started because I was playing around with the brainfuck programming language. BrainSharp is a CLI tool which can interpret brainfuck code and transpile it to C#. It can also output the generated source code or executable, or run it directly.
Brainfuck is a very minimalistic language which contains just a pointer, memory, in-, and output. The complete syntax consists of these following symbols:
+
and-
increment and decrement the cell value.>
and<
increment and decrement the pointer value..
prints the cell value as a character.,
reads a byte from the input.[
and]
denote a loop which runs while the cell value is not zero.
All other characters are also valid, and are simply interpreted as comments. A simple hello world brainfuck program looks like this:
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>
->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.
+++.------.--------.>>+.>++.
The way BrainSharp interprets this is by converting each character to an abstract instruction, and keeping a list with those instructions. After it built the complete list, it will go through it to merge instructions where possible. For example: >>>>
means “increment the pointer by 4”, which can be represented by a single instruction (pointer += 4;
).
From here generating the C# code is fairly simple. First there is some boilerplate which is necessary for any valid C# program, and there are some extra functions which handle I/O to make generating the code for that a bit simpler. The ${code}
placeholder is where the generated code will be put.
using System;
namespace brainfuck
{
public class Program
{
private static byte[] stack = new byte[256];
private static byte pointer = 0;
private static string input = String.Empty;
private static int inputPointer = 0;
private static void Main(string[] args)
{
for (int i = 0; i < args.Length; i++)
{
input += args[i];
if (i < args.Length - 1)
input += ' ';
}
${code}
Console.Read();
}
private static byte ReadByte()
{
return (byte)(inputPointer < input.Length
? input[inputPointer++]
: 0);
}
private static void PrintChar()
{
Console.Write((char)stack[pointer]);
}
}
}
Now the code for each instruction can be generated. Most of the instructions have very simple C# equivalents, from PrintChar()
for .
to pointer += 4
for >>>>
. To make sure the code stays properly indented it also keeps a counter for the number of tabs, which increases when a loop starts and decreases when it ends. After this is done we have successfully generated a C# program from brainfuck!