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.
- Syntax (bytecode): 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
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()) |
| 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]) |
| 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 | NONE | Break loop |
| 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 | Compare top two stack values (Top vs Second). Returns 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 (Top-Second), 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 and them and round the result (Top//Second), pushes the result back |
| 25 | END | NONE | End program |
LFSP and LFASM
LF is hard to read, so two helper syntaxes exist:
- LFSP (LF-Simplified): compresses repeated letters. Example:
AAAAAAAAALLLLLLLLOAIH → 9A8LOAIH
- LFASM (LF Assembly): easier coding:
9A8LOAIH →
STARTLOOP, 9 // 9A8L INC, 8 // 8LO ENDLOOP // OA OUT(CHAR) // AI END // IH
LF-High (LFH)
LF-High is an advanced version of LF with major improvements:
- Compact commands:
- in, form, dest (opcode 5)
- out, form, target, start, end (opcode 8)
- idx, val (val can be negative) (opcode 1)
- inc, val, ... (val can be negative, supports increasing multiple cells at once) (opcode 3)
- New commands (to fill in the spot of: IDXDEC, DEC, IN(NUM), OUT(NUM)):
- mov, pt, val (opcode 2)
- sel, target (opcode 4)
- Syntax expansion:
- cmp, val1, val2, dest
- add, val1, val2, val3,..., dest
- sub, va11, val2, val3,..., dest = val1-val2-val3-...
- div, val1, val2, val3,..., dest = (((...((val1//val2)//val3)//val4)...)))
- Zone support with flags:
- ^ → start zone
- !! → end zone
- # → zone pointer
- $ → get self (see below)
- ! → end param (see below)
- Advanced stack operations, select stack, arithmetic on stack and cell. Local pointers for zones, and stack pointer (sp).
Example that includes some of the new commands
inc, ^4 // start zone and makes the first value 4 sel, $ // select current cell idx, 2 inc, !!9 sel, $ add // adds the 2 selected cells, pushes them onto the stack out, num, sp // outs 13 end
Output: 13
- $ references to a unit, depending on the suffix:
- $c: current cell
- $z: current zone
- $n with n being a number: cell index n
- $pt: the zone that contains local pointer pt
- # references to the index of a unit:
- #c: current cell index
- Note:
- # alone as a param for inc will create a zone pointer.
- $#c = $c
- % references to the size of a unit:
- %c: get the current cell's "size". In fact, %c=1∀c
- %z: get the current zone's size
- %pt: get the size of the zone that contains local pointer pt
- ! explanation:
- Say we have the command
sub, add, 5, 3, 2. Is it (5+3)-2 or 5+3+2-0? We can't know becasue both takes many params. - ! solves the problem:
sub, add, 5, 3!, 2= (5+3)-2;sub, add, 5!, 3, 2= 5-3-2.
- Say we have the command
Mechanics of LF-High
Pointer focus: The cell that the last pointer that was moved points to will be the target for inc. To change focus without moving pointer, do idx, 0 or mov, pt, 0.
Notes
- add, sub, mul, div returns a literal when in $(...) but pushes the literal onto the stack when not in $(...)
Advantages and disadvantages
- Advantages: Very powerful; supports self-replicating code, complex stack operations, and zones.
- Disadvantages: Difficult to read and debug, requires hard thinking and wise coding.
LF/LFH 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.
Hello World!
out, char, "Hello World!" end
LF: A"Hello World!"AIH
MOD
out, char, "Enter the diviend:" //out, char with literal string as param wont modify the tape in, num, #c //diviend here inc, ^ out, char, "Enter the divisor:" in, num, add, #c, 1 //divisor here inc, !!, add, #c, 3 //inc stops when it encounters a destination push, $c, zz inc, #, pt1 mov, pt1, 2 inc, -1 while, 1 inc, 1 mul, $c, $(sub, #c, 1) cmp sloop, neg, eq, 0 //sloop = start loop brk eloop mov, pt1, -2 push, $c, zz mov, pt1, 2 ewhile sloop, eq, 1 inc, -1 mul, $c, $(sub, #c, 1) sub pop mul, $c, -1 out, num end sloop, eq, 2 out, num endloop end