Letterfuck
Letterfuck
Letterfuck (LF) is an esoteric programming language inspired by Brainfuck and Piet, but using letters as commands. LF combines a memory tray similar to Brainfuck with a stack like Piet. Programs are sequences of letters, with the differences between letters encoding commands and repeated letters forming blocks.
Overview
LF programs use a memory tray (array of cells) and a stack (LIFO). Each command is represented by the change in letters.
You read the LF bytecode in adjacent pairs of blocks.
- Syntax for each pair: aAbB
- a is a parameter for the current command
- b is a parameter for the next command
- A,B are letters from A-Z
- Opcode = index(B) - index(A) if B > A, or 26 + index(B) - index(A) if B < A.
- Repeated letters form a block, with size representing the parameter for some commands.
- Example: AAAB does command 1, parameter 3
One crucial rule: The last block for this command is the first block for the next command.
So aAbBcC has 2 commands: aAbB (command B-A, param a) and bBcC (command C-B, param b). You can look at the examples below to know more.
Commands
| Opcode | Command | Param | Description |
|---|---|---|---|
| 1 | IDXINC | VAL | Increase memory index: index += VAL |
| 2 | IDXDEC | VAL | Decrease memory index: index -= VAL |
| 3 | INC | VAL | Add VAL to current cell: tray[index] += VAL |
| 4 | DEC | VAL | Subtract VAL from current cell: tray[index] -= VAL |
| 5 | IN(CHAR) | NONE | Input a character: tray[index] = ASCII(input_char()). Identical to Brainfuck's ",". EOF: 0 |
| 6 | ZERO (ZZ) | NONE | Next command receives 0 as parameter. If its block size is above 1, use ZZ (zero-zero) as a parameter instead |
| 7 | IN(NUM) | NONE | Input a number: tray[index] = input_number() |
| 8 | OUT(CHAR) | CELL | Output ASCII character: system.write(tray[index]). Also recieves literal string. See LF Programs to know more. |
| 9 | OUT(NUM) | CELL | Output number: system.write(tray[index]) |
| 10 | NEG | VAL | Negation: NEG a = -a; if a=ZZ → 1 |
| 11 | STARTLOOP | VAL | Loop VAL times |
| 12 | ENDLOOP | NONE | End loop |
| 13 | EQ | VAL | Compare top of stack with VAL: 1 if equal, -1 if not |
| 14 | BRK | VAL | Break out of WHILE loops. Executes the next command after the nearest ENDWHILE if there are no parameters. Otherwise, executes the next command after the VAL-th nearest ENDWHILE. |
| 15 | WHILE | VAL | While VAL=1, repeat loop |
| 16 | ENDWHILE | NONE | End while loop |
| 17 | PUSH | CELL | Push cell to stack, reset cell. If it was used with param ZZ, do not reset cell |
| 18 | POP | STACK | Pop stack to cell. If it was used with param ZZ, turn to PEEK (pastes the top of stack to cell without removing it from stack) |
| 19 | CMP | STACK | Pop the 2 top stack values and compare them (Top vs Second). Push 0 if Top<Second, 1 if Top>Second, 2 if Top=Second |
| 20 | DUP | STACK | Duplicate top of stack |
| 21 | SUB | STACK | Pops Top and Second, subtract them (Second-Top), pushes the result back |
| 22 | ADD | STACK | Pops Top and Second, add them (Top+Second), pushes the result back |
| 23 | MUL | STACK | Pops Top and Second, multiply them (Top*Second), pushes the result back |
| 24 | DIV | STACK | Pops Top and Second, divide them and floor the result (Second/Top), pushes the result back |
| 25 | END | NONE | End program |
LFASM
LF is hard to read, so there's a helper syntax:
- LFASM (LF Assembly):
Uses mnemonics for easier coding. 9A8LOAIH →
STARTLOOP, 9 // 9A8L INC, 8 // 8LO ENDLOOP // OA OUT(CHAR) // AI END // IH
LF Programs
If any of you understands LF syntax, you can try writing a program here! I'd apperciate it, and even more if you manage to write a compiler for this thing.
LF Programs
Hello World!
out(char), "Hello World!" end
LF: A"Hello World!"IH
Cat program
WHILE, 1
IN(CHAR)
PUSH
STARTLOOP, EQ, 0
BRK
ENDLOOP
POP
OUT(CHAR)
ENDWHILE
END
LF: APULREPDPHPFE
ASCII Printer
idxdec, 1 in(num) while, 1 push startloop, eq, 0 brk endloop pop dec, 1 idxinc, 1 inc, 1 out(char) idxdec, 1 endwhile end
LF: Coming soon
This prints the ascii characters from 1 to N, with N being the input. Supports any N, even N > 127. Set stack cap to 1 and tray size to 3 on the compiler for it to be faster.
FizzBuzz
idxinc, 3 inc, 10 idxdec, 3 startloop, 100 while, 1 inc, 1 idxinc, 1 inc, 3 idxdec, 1 push, zz idxinc, 1 push, zz div push, zz mul idxdec, 1 push, zz cmp startloop, eq, 2 out(char), "Fizz" idxdec, 1 inc, 1 idxinc, 1 endloop idxinc, 1 inc, 2 idxdec, 1 push, zz idxinc, 1 push, zz div push, zz mul idxdec, 1 push, zz cmp startloop, eq, 2 out(char), "Buzz" idxdec, 1 push idxinc, 1 brk endloop idxdec, 1 push startloop, eq, 1 idxinc, 1 brk endloop idxinc, 1 out(num) brk endwhile idxinc, 3 out(char) idxdec, 2 push idxdec, 1 endloop end
LF: 3A10B3E100GRGJ3KNP2UMN2TKT2OFCE2KB2UHS"Fizz"ACFGS2TWY2EVW2CTR2XOLN2TK2DQB"Buzz"JLCDRDFWJUVJVWFT3JK2SULNZY
Prime tester
in(num) push, zz idxinc, 3 inc, 1 push cmp startloop, neg, eq, 0 out(char), "Not a prime." end endloop idxdec, 3 push, zz idxinc, 3 inc, 2 push cmp startloop, eq, 2 out(char), "A prime." end endloop idxdec, 3 while, 1 push, zz idxinc, 2 push, zz dup mul cmp startloop, eq, 1 brk endloop startloop, eq, 2 brk endloop inc, 1 idxdec, 2 endwhile idxdec, 1 inc, 1 while, 1 inc, 1 idxdec, 1 push, zz idxinc, 1 push, zz div push, zz mul idxdec, 1 push, zz cmp startloop, eq, 2 out(char), "Not a prime." brk endloop idxinc, 2 push startloop, eq, 2 out(char), "A prime." brk endloop pop dec, 1 idxdec, 1 endwhile end
LF: Coming soon (Note: In the compiler, for some edge cases, run with the normal RUN button to avoid bugs.)
Tip: Set stack cap to 3 and tray size to 4 for this to reach its maximum speed.
Prime printer
idxdec, 1 out(char), "Primes:\n" in(num) push, zz idxdec, 1 inc, 1 push idxinc, 1 cmp startloop, neg, eq, 0 out(char), "None!" end endloop push, zz idxdec, 1 inc, 2 push, zz idxinc, 1 cmp startloop, eq, 2 out(char), "2" end endloop push, zz idxdec, 1 push div idxinc, 1 pop idxinc, 1 inc, 1 out(char), "2\n" while, 1 inc, 2 push, zz while, 1 push, zz idxinc, 2 push, zz dup mul cmp startloop, neg, eq, 0 brk endloop inc, 1 idxdec, 2 endwhile idxdec, 1 inc, 1 while, 1 inc, 1 idxdec, 1 push, zz idxinc, 1 push, zz div push, zz mul idxdec, 1 push, zz cmp startloop, eq, 2 idxinc, 3 push idxdec, 3 brk endloop idxinc, 2 push startloop, eq, 2 idxinc, 1 inc, 1 push idxdec, 3 brk endloop pop dec, 1 idxdec, 1 endwhile startloop, eq, 1 out(num) out(char), "\n" endloop idxdec, 1 push startloop, eq, 1 brk endloop pop dec, 1 idxinc, 2 push idxdec, 1 endwhile
LF: Coming soon
Prints all prime numbers from 1 to N+1 if N is even, else print all prime number from 1 to N. Recieves N as an input.
Tip: set tray size to 5, stack cap to 3 for maximum speed.
Decimal to Base N (now with hexadecimal support)
dec, 999 push in(num) push, zz idxinc, 1 in(num) push, zz idxdec, 1 while, 1 // divmod idxinc, 1 pop idxdec, 1 pop push, zz idxinc, 1 push, zz div idxdec, 1 push, zz idxinc, 1 push, zz idxinc, 3 // mod pop idxinc, 1 pop, zz idxdec, 1 push, zz div push mul idxinc, 1 push sub dec, 1 push mul idxdec, 3 pop idxdec, 2 pop idxinc, 2 push idxdec, 2 push, zz startloop, eq, 0 pop brk endloop idxinc, 1 push, zz idxdec, 1 endwhile while, 1 while, 1 startloop, eq, -999 brk endloop startloop, eq, 10 out(char), "A" pop brk endloop startloop, eq, 11 out(char), "B" pop brk endloop startloop, eq, 12 out(char), "C" pop brk endloop startloop, eq, 13 out(char), "D" pop brk endloop startloop, eq, 14 out(char), "E" pop brk endloop startloop, eq, 15 out(char), "F" pop brk endloop pop out(num) endwhile endwhile end
LF: Coming soon.
Recieves a decimal number d and prints out its base-n representation. d is the first input, n is the second input.
Tip: Set tray size to 5. For stack size: when stack size = k and n = p (base p), the maximum number you can convert is p^(k-3) - 1. Exceed this limit and you might hang the compiler.
LF Gadgets
Gadgets for convenience. Pre-execution and post-execution included.
Modulo
// Pre: stack contains: [a, b] (bottom to top). Pointer at cell n. // Cell n and n+1 must be unused as this will zero both cells. // Also, initially, both cells have to be 0. pop idxinc, 1 pop, zz idxdec, 1 push, zz div push mul idxinc, 1 push sub dec, 1 push mul idxdec, 1 // Post: stack contains: [a%b]. Pointer stays at cell n. // Minimum stack cap: 2. // Minimum tray size: 2.
DivMod (Division + Modulo)
// Pre: stack contains [a, b] (bottom to top). Pointer at cell n. // Cell n and n+1 must be unused. Cell n will be set to a and cell n+1 will be set to b. idxinc, 1 pop idxdec, 1 pop push, zz idxinc, 1 push, zz div idxdec,1 push, zz idxinc,1 push, zz idxinc, 1 pop idxinc, 1 pop, zz idxdec, 1 push, zz div push mul idxinc, 1 push sub dec, 1 push mul idxdec, 3 // Post: stack contains: [floor(a/b), a%b] (bottom to top). Pointer stays at cell n.
Implementations
The official compiler for LF is live on GitHub: https://alphamain407-max.github.io/LF_Compiler/
Note (00:57 Jan 18 2026): The BRK with param is not in the compiler yet. Will implement it tomorrow.
Note (18:49 Jan 18 2026): v2.9.7 features Stack Cap. Go try it out! Also, BRK, VAL is implemented.
Note (01:40 Jan 20 2026): The Base N thing really took me a while. NEG doesn't work with STARTLOOP, gonna fix it later!