NOTE: According to the official page at Catseye, there is no documentation and there aren't any canonical example programs, so this page has been written according to the source code and the behaviour of the official interpreter, which appears to be the closest thing to a language definition there is available.
Maentwrog is a programming language created by Chris Pressey, and implemented by him in August 1993. According to the readme for Shelta, it was the first language he designed. It bears a resemblance to Forth, in that it is a stack-based language with user-definable whitespace-separated words. Even for an esoteric language, it is low-level: it allows direct OS-level memory access using pointers, as in C. It is the predecessor of Befunge.
A Maentwrog program consists of a series of words, separated by whitespace. Words can contain any character except whitespace. The way these words are executed depends on the character they begin with.
- A word that consists of digits is taken as an integer and pushed. A minus sign may be used to make negative numbers, so 25 and -14 are words that push 25 and -14 onto the stack. Extra characters at the end of the word are allowed, so 25abc and 25.14 both also push 25.
- A word that isn't a number is treated as either a function or a variable. If the word is defined as a function, it is executed; if it's a variable, its current value is pushed to the stack. Using an undefined word results in an error, though this doesn't stop further execution of the program.
- To define a word as a function, use the syntax : new-word words to execute ; (as in Forth). Redefining a word is not allowed, and neither are nested function definitions.
- To define a word as a variable, use the syntax *varname. This must be done before using a variable.
Additionally, words (except number words) can take one of a list of prefixes, changing how the word is executed.
|=||Assign to variable||=foo||A value popped from the stack is assigned to the variable foo.|
|@||If||@bye||Pop value, stop the program (see predefined words) if it isn't 0.|
|[||While||[xyz||Pop value, if it's not 0 execute word xyz, then pop another value and do it again; continue until a 0 is popped.|
|$||Repeat||$.||Pop value, then output that many values from the stack.|
|bye||Stop program immediately.|
|rem ... ;||Comment. (Ignore all words between rem and ;.)|
|: word ... ;||Define a new word.|
|debug||Turn on debugging (outputs all words executed).|
|vars||Output a list of currently defined variables and their values.|
|words||Output a list of currently defined words.|
|alloc||n | ptr||Allocate memory for n C longs, returns a pointer.|
|free||ptr | -||Free memory at pointer.|
|size||- | n||Push stack size.|
|dup||a | a a||Duplicate top of stack.|
|swap||a b | b a||Swap the two topmost stack values.|
|pop||a | -||Remove top value from stack.|
|get||ptr | value||Push value at pointer to stack (C *ptr).|
|put||ptr val | -||Store value at pointer (C *ptr = val).|
|rnd||- | n||Push random value.|
|>||a b | (a>b)||Push 1 if a is greater than b, else 0.|
|<||a b | (a<b)||Push 1 if a is less than b, else 0.|
|==||???||See note at the end.|
|.||n | -||Pop a value, output as integer, adding a newline.|
|..||n | -||Pop a value, output as an ASCII character.|
|mod||a b | (a%b)||Modulo.|
|+||a b | (a+b)||Addition.|
|-||a b | (a-b)||Subtraction.|
|*||a b | (a*b)||Multiplication.|
|/||a b | (a/b)||Division, result is rounded towards 0.|
Note: I have no idea what the == word is supposed to be. In the official interpreter, maentw.c, it seems to be defined as push(!pop() == !pop()); so it should be acting as boolean equality. However, actually trying to use it, it acts like pop. It looks like a bug in the implementation.
: puts dup .. @puts ; 0 10 33 100 108 114 111 119 32 44 111 108 108 101 72 puts
*a *b *c 0 =a 1 =b : fib a b + =c c . b =a c =b c 100000 < @fib ; 1 . fib
Prime number generator
A bigger example (I couldn't resist...)
rem array functions ; : dim 2 * alloc ; : idx 8 * + ; rem equality ; : eq2 pop 0 ; : eq - 1 swap @eq2 ; rem test each element in the array ; : walkarr2 i 1 + =i i cursz < @walkarr1 ; : walkarr1 curn arr i idx get mod 0 eq =fd fd 0 eq @walkarr2 ; : walkarr 0 dup =i =fd walkarr1 ; rem implementation of algorithm ; : sieve2 arr cursz idx curn put curn . cursz 1 + =cursz ; : sieve1 walkarr fd 0 eq @sieve2 curn 1 + =curn cursz maxsz < @sieve1 ; : sieve *i *fd *curn *cursz 2 . arr 2 put 3 =curn 1 =cursz sieve1 ; rem memory handling ; : primes *arr *maxsz dup =maxsz dim =arr sieve arr free ; rem change the number to change the amount of primes ; 25 primes