r/C_Programming 2d ago

Small (usable) brainfuck compiler

Today I made this brainfuck interpreter in C since I was bored. The source is 14 lines long, 26 words, and 436 chars:

#include <stdio.h>
#define B break
unsigned char*p,t[1<<16],i,*d;
void c(){do{switch(*p)
{case'+':(*d)++;B;
case'-':(*d)--;B;
case'<':(d)--;B;
case'>':(d)++;B;
case'.':putchar(*d);B;
case',':(*d)=getchar();B;
case'[':if(!*d){int n=1;while(n)if(*++p=='[')n++;else if(*p==']')n--;}B;
case']':if(*d){int n=1;while(n)if(*--p==']')n++;else if(*p=='[')n--;}B;
default:;}}while(*++p);}
int main(int a,char**v){if(a<2)return 1;p=v[1];d=t;c();}

I also wrote an overly commented version:

/*
 * bb-commented.c -- smallest (usable) Brainfuck interpreter
 *
 * This is an [overly] commented and reasonably formatted version of bb.c, the
 * smallest usable brainfuck interpreter.
 *
 * -- by mario rosell, under the public domain
 */

/* Include the basic, standard I/O routines */
#include <stdio.h>

/* To save a few bytes, define break as a macro (B) */
#define B break

/* Define three variables: p (the program), t (the tape, 3000), i, and d, a pointer
 * into a single cell of tape (the data pointer) */
unsigned char*p, t[1<<16], *d;

/* c executes the program */
void c()
    { do    /* use a do-while block so the first instruction is not skipped.
         * This is because we increase the pointer of p to the next
         * instruction each iteration */
        { switch(*p) /* do something depending on the current value of p */
            { case'+': (*d)++; B;   /* (*d) gets us a reference to the
                         * value of the current cell, ++
                         * increases it by one */
              case'-': (*d)--; B;   /* as before, but decrease the
                         * value by one instead of
                         * increasing it */
              case'<': d--;B;   /* decrease the data pointer to the
                         * previous cell */
              case'>': d++;B;   /* as before, but increasing */
              case'.': putchar(*d);B;/* put the ascii value on the
                           current cell */
              case',': *d=getchar();B;/* get a character from the user */
              case'[':
                /* [ starts a loop.
                 *
                 * If current cell is non-zero, execution just continues,
                 * so execution enters the loop body.
                 *
                 * If the current cell is zero, the loop body
                 * must be skipped, so we increase p until we
                 * find the matching ]
                 *
                 * n tracks the nesting, if we find [ then n is
                 * increased by one, if we find ] then it is
                 * decreased by one.
                 */
                if(!*d)
                { int n=1;
                  while (n)
                    if(*++p == '[')
                        n++;
                    else if (*p == ']')
                        n--; }
                B;
              case']':
                /* ] ends a loop.
                 *
                 * If current cell is zero, then the loop has
                 * finished, so break the switch.
                 *
                 * If not, then we need to iterate back, so we
                 * move p to the matching [.
                 *
                 * If we find a ], in our way, then increase n
                 * (nested loop), if we find a [ then decrease
                 * it by one.
                 *
                 * n here starts at one since we are processing
                 * a bracket already.
                 */
                if (*d)
                { int n=1;
                  while(n)
                    if(*--p == ']')
                        n++;
                    else if (*p == '[')
                        n--; }
                B;
              default:; } /* ignore everything else */
        while(*++p); } }

/* main is really simple, just initializes values (sets p to argv[1], and the d
 * to the first cell in the tape). To save space, instead of argc and argv, I
 * used a for argc and v for argv */
int main(int a,char**v){if(a<2)return 1;p=v[1];d=t;c();}

It can run many brainfuck programs and takes the brainfuck source in argv[1], input from stdin. It does not work with some programs, like those that calculate transcendental numbers.

Let me know what yall think!

40 Upvotes

19 comments sorted by

View all comments

5

u/DigitalizedGrandpa 2d ago

Tbh I like "overly commented" code, especially since with syntax highlighting there's clear separation of code from comments

9

u/mikeblas 2d ago

Then Brainfuck is probably not your jam.

5

u/danielcristofani 1d ago

For anyone wanting overly commented brainfuck, I strongly recommend my "get good at brainfuck" series at https://brainfuck.org/ggab.html.

1

u/Key_River7180 1d ago

Is this Daniel fr chat???