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!

reddit.com
u/Key_River7180 — 13 days ago
▲ 21 r/brainfuck+1 crossposts

Small C brainfuck interpreter

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, 65536 cells), 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!

reddit.com
u/Key_River7180 — 13 days ago

On a bootloader where I was too lazy to implement a string printing function

The weird symbol is supposed to be a ␣ but this font didn't want to render it.

u/Key_River7180 — 17 days ago

The spanish -> english translation is horrible

It literally has no words! Scrape rae.es for the love of god! Madrugar, Cierzo, and other mildly complex words AREN'T THERE!

reddit.com
u/Key_River7180 — 22 days ago
▲ 57 r/youtube

These "small creators" that only beg annoy me way too much

There are "small creators" that have 2 subs and only upload videos begging for likes or for us to respect "small creators" with the cookie filter and stuff like this.

While small creators are good and healthy, beggars aren't. Like, if you wanna grow your channel, then you upload videos that people like, if people like your videos, then you'll get more subs and likes than if you just ask for likes.

reddit.com
u/Key_River7180 — 26 days ago
▲ 6 r/ada

Better Emacs modes for Ada?

I'm using ada-mode right now and it really sucks, indentation behaves weirdly, I cannot capitalize stuff like I want to, ...

Does anyone know a better Ada mode for Emacs?

EDIT: Solved! (Thanks, u/spacetruckn)

reddit.com
u/Key_River7180 — 1 month ago
▲ 19 r/emacs

M-x doctor RET

TIL there is an Emacs command (doctor) that acts like a (really dumb) psychologist.

Next time you wipe your prod db, you can talk with him I guess...

reddit.com
u/Key_River7180 — 1 month ago
▲ 2 r/tui

r/humanTUI

Since this sub is full of AI SLOP, I made a new sub: r/humanTUI.

Basically like this sub but AI is not allowed.

reddit.com
u/Key_River7180 — 1 month ago

hotwrap: hot reloading for C! [selfpromo]

I've made a tool called hotwrap; a simple tool that hot-reloads a given module (a shared object with a plugin_main_impl function exported) whenever it, or a list of watched files change.

It also has an Emacs package, not on MELPA yet, it gives you a run-hotwrap command with signals, interactive module selection, ...

Under the CC0! Repo at https://sr.ht/~rosell/hotwrap/

reddit.com
u/Key_River7180 — 1 month ago
▲ 21 r/emacs

Do any of you use vc-dir?

Just curious, I've tried vc-dir before but overall it seems like everyone has moved to magit, do people still use it?

EDIT: I'm trying vc-dir again now, it is pretty good, actually.

reddit.com
u/Key_River7180 — 2 months ago

compliance - do you consider Common Lisp suckless?

Hello.

I've been participating on suckless and LISP communities at the same time, and ugh... just wanted to ask a quick question: do you consider Common Lisp suckless? And its ecosystem?

Thanks.

reddit.com
u/Key_River7180 — 2 months ago
▲ 3 r/osdev

tutorial - using Scheme in your OS

Scheme is a programming language and derived from the LISP programming language, and a very good language for shells, overall.

Here I will (0) tell you about why you'd want to use Scheme as your shell, and (1) give you a simple generic way to implement it. :)

As aforementioned, Scheme is a programming language of the LISP family of languages, and is homoiconic, so data and code are the same thing, kinda.

Everything is expressed as an S-expression (s-exp, symbolic expression), which is a parenthesized expression format made on top of lists. It can look somewhat like this:

(+ 1 (* 2 3))

The first element of an unquoted list in eval notation (that is, a list not starting with '), (sometimes referred to as the car of the list) is the function to be executed (here +), and the rest the operands. Superfluous parenthesization is mostly forbidden, but you can still nest executable lists like on the example above, which would print 7, by the way.

S-expressions are also used to represent data, as an alternative for things like XML, JSON, etc.. The benefit is simplicity, less syntax, and ease to parse.

In an S-exp, there are three main types of values:

  • lists
  • atoms: undividable data
  • symbols: a name that may, or may not in any significant way, a value.

They often look somewhat like this:

(person
  (name "John Doe")
  (age  30)
  (email "john@johndoe.es"))

In JSON, this would look somewhat like:

{
  "name": "John Doe",
  "age":  30,
  "email": "john@johndoe.es"
}

S-expressions can also be composed exclusively of atoms, like an array:

(1 2 3)

In JSON:

[ 1, 2, 3 ]

Another example: configuring a web server:

(server-config
  (port 80)
  (webmaster-mail "webmaster@johndoe.es")
  (for-route "www.johndoe.es"
    (do 'redirect "johndoe.es"))
  (for-route "johndoe.es"
    (do 'serve "/var/www/htdocs/main/")))

On JSON, it'd look like this:

{
  "port": 80,
  "webmaster-mail": "webmaster@johndoe.es",
  "for-route": { "www.johndoe.es", "do": { "redirect": "johndoe.es" } },
  "for-route": { "johndoe.es", "do" : { "serve": "/var/www/htdocs/main/" } },

You can evaluate quoted S-expressions using the eval builtin, but you shouldn't, really.

Some objects are unreadable, which makes them very useful for internal data you want to make opaque, e.g.: a file. These are mostly printed as: #<SOME DATA> (e.g.: #<FILE name "file.txt" size 512 mode 777 owner "john">).

For example, you may have a ls function that does something like:

> (ls)
("hello.scm")
>

Which could be defined as (pseudocode):

(define (ls (optional dir))
  (space-separated-list-to-sexp (syscall 'get-files (dir-path dir))))

S-expressions are really useful for an operating system, since they standardize a nice, powerful format across all applications, which can be REALLY useful.

Adding Scheme to your OS

There are two Scheme impls I'd recommend:

  • TinyScheme: Very smol, needs little C runtime, but less mantained.
  • Chez Scheme: Production grade, big, but needs the heck of a bunch of CRT.

Add them to your initrd script, make it run: chez-scheme -q boot.scm. On boot.scm, add:

(load "customlib.scm") ; load the standard library (; makes a comment, by the way)

(load "login.scm") ; load the login script
(on-userspace ; on-userspace is a fictional function that will run a S-exp on userspace
  (new-cafe)) ; make a new REPL

;; panic is a fictional-function that causes the kernel to panic
(panic "Shell returned.")

I used a few fictional functions:

  • on-userspace: Evaluate an S-exp on userspace
  • panic: panics, I guess...

On TinyScheme, you have to relaunch TinyScheme, there isn't any (new-cafe) function.

Thanks in advance.

reddit.com
u/Key_River7180 — 2 months ago