An esolang that uses letters, not symbols


33 is an esolang created to use letter characters instead of the strange symbols other esolangs use. Yes, there are some symbols, such as [] for loops, {} for functions, and “” and ‘’ for strings, but they’re more standard than the stuff some other esolangs use. There are 33 distinct letter commands in the language.


Get the latest release from the releases page of this repository. Put it on your $PATH if need be.

Running a program

After installing the interpreter, execute it with -f <filename> to execute a file. Adding the -d flag will show a debugger.

The debugger

The debugger allows you to step through the program, one instruction at a time, and see the internal values of the registers. It prints the values stored in the accumulator, counter, source string, destination string, and mutable list (in that order).


There are 3 output commands:

There are three input commands:


There are 2 integer memory registers, a general integer memory, two string memory registers, and 2 list memory registers.


Of the two integer memory registers, the accumulator is the one that can have mathematical operations performed on it, the one that can be checked conditionally, and the one that can be read from/written to general memory. The counter is the one that can have values put into it directly.

The general memory is accessed via strings.

Arithmetic operations

Memory operations


There are two string registers, a source, and a destination. Their functions are described in other sections. The source is set by enclosing a string in double quotes (""), and the destination by single quotes ('').

Modification operations


There are two list registers. One that you can perform operations on, and one for simply storing a second list. The list is initially filled in with argv

List operations

Flow control


Conditional operators skip the next operation if the condition is not met.


Functions use the string registers to create and call. The destination register is queried when creating a function, and the source when calling. Creating a function is as simple as setting the destination register, then enclosing a block of code in braces, for example: 'Print'{"Hello, World!"p}.

To call a function, you must set the source register, then perform the q operation. Following on from the previous example, that would be: "Print"q.

Register values are kept when changing stack frames. This means you can do stuff like this:

{"Hello"p}'Hello'{", "p}', '{"World"p}'World'{"!"pi}qqqq

to print “Hello, World!”


Loops are notated using square brackets ([]). When a closing bracket is found, the code jumps back to the matching opening bracket if the accumulator is not 0. For example: This code 10az1[oim]"Done."pi will count down from 10 until it reaches 1, then it will print “Done.”. To make it count down until it reaches 0, this code would be used: 11az1[moi]"Done."pi.

Program termination

When the program reaches the end of input, the interpreter terminates. No printing is done after this. The program can be terminated early with the @ operator.