5MAT
5MAT is an esolang derived from Common Lisp FORMAT strings created by User:kg583. A data string is repeatedly passed to Common Lisp's format
function, whence a program made of numerous and powerful format directives processes the string.
Execution
A 5MAT program is a FORMAT string which repeatedly formats a tape of characters, terminating only via runtime error. The tape is always initially nil
.
Execution may be implemented in Common Lisp via
(setq tape nil) (loop (setq tape (coerce (format nil *program* tape) 'list)))
where coercion to a list is required to allow the ~{~}
directive to iterate over the tape.
To permit output to STDOUT, the tape is divided into data and output sections, separated by a \f
(0x0C
), a (non-printing) form feed control character. All contents of the output section are printed each iteration, while the data section is not printed.
Writing Programs
Note: This guide assumes familiarity with Common Lisp FORMAT strings.
Initialization
Use ~[INIT-OUTPUT~|INIT-DATA~;~]~:*
at the start of your program to initialize the tape.
Conditionals
To identify a character on the tape, use the two-argument and three-argument version of ~^
:
- If two parameters are given, termination occurs if they are equal.
- If three parameters are given, termination occurs if the first is less than or equal to the second and the second is less than or equal to the third.
For example:
; Iff the current character is not 'C' ~<~v,'C^STUFF~> ; Iff the current character is 'C' ~<~v,'B,'B^~:*~'D,'D,v^STUFF~>
Checking character ranges where possible can greatly shorten a program.
Looping
Since the tape is passed as a list, programs must use a containing ~{~}
to loop over it, even if all characters are exhausted in a single iteration.
Here are some common idioms:
; Go to the start of the tape ~@* ; Go to the end of the tape ~#* ; Go to the next form feed ~@{~v,'↡^~} ; Print a form feed ~| ; Print characters until <char> ~@{~v,'<char>^~:*~a~} ; Print characters in reverse ~@{~a~2:*~} ; Check how many characters remain ~#[ZERO~;ONE~;...~] ; Break if no characters remain ~^ ; Break unconditionally ~0^
Termination
Since 5MAT programs run forever, only a runtime error can end execution. The easiest option is ~v*
, which requires an integer parameter, but ~[~]
, ~{~}
(inside the main loop), and ~a
(if no characters remain) also suffice.
Arithmetic
A succinct general approach to decimal arithmetic is to store values on the tape backwards, then increment as follows:
- Turn all trailing 9's to 0's
- Increment the first digit that isn't a 9 by checking for each digit individually
- Pad with a leading zero if there are no digits left
- Copy the digits in reverse to output
The above generalizes easily to any base and any system of digits.
Miscellany
- 5MAT programs can be made "readable" using the
~\n
directive. - Comments may be inserted via unreachable pathways, e.g.
~1[COMMENT HERE~]
.
Examples
Hello, World!
Hello, World!~@[~?~]
Truth Machine
~:[INPUT~;~]~:*~{~<~v,'1^~v*~>1~}
FizzBuzz
~:[11~|10~|~;~]~:*~{~ ~<~'1,'1,v^1~>~:*~<~v,'0^~:*~'2,v^2~>~:*~<~v,'1,'1^0~>~ ~<~'1,'1,v^1~>~:*~<~v,'0^~:*~'2,'2,v^2~>~:*~<~v,'1,'1^~:*~'3,'3,v^3~>~:*~<~v,'2,'2^~:*~'4,'4,v^4~>~:*~<~v,'3,'3^0~>~|~*~ ~@{~v,'8,'8^0~}~:*~<~'1,'1,v^1~>~:*~<~v,'0^~:*~'2,'2,v^2~>~:*~<~v,'1,'1^~:*~'3,'3,v^3~>~:*~<~v,'2,'2^~:*~'4,'4,v^4~>~:*~ ~<~v,'3,'3^~:*~'5,'5,v^5~>~:*~<~v,'4,'4^~:*~'6,'6,v^6~>~:*~<~v,'5,'5^~:*~'7,'7,v^7~>~:*~<~v,'6,'6^~:*~'8,'8,v^8~>~:*~<~v,'7,'7^~:*~'9,v^9~>~ ~<~'0,'0,v^0~>~:*~@{~v,'↡^~:*~a~}~ ~|~@*~<~'1,'1,v^Fizz~>~<~'1,'1,v^Buzz~>~2:*~ ~<~v,'0^~v,'0^~#*~:*~@{~v,'↡^~2:*~}~3:*~@{~v,'↡^~:*~a~2:*~}~>~%~0^~ ~}
Fibonacci
~:[1~|1~|1~|00~|~;~:*~ ~1{~#*~:*~1@{~v,'\x0b,'\f^~:*~'\f,'\r,v^~ ~@*~1@{~@{~v,'\f^~:*~c~}\f~:}~@{~v,'\f^~:*~c~}~ ~|~1@{~v,'\x0b,'\f^~:*~'\f,'\r,v^~@{~v,'\f^~}~3:*~@{~v,'\f^~:*~c~2:*~}~%~#*~:*~:}~ ~#[~;~0^~]~ ~@{~v,'\f^~:*~c~}~|~@{~v,'8,'9^0~}~:*~ ~<~v,'/,'0^~:*~'0,'1,v^1~>~:*~<~v,'0,'1^~:*~'1,'2,v^2~>~:*~<~v,'1,'2^~:*~'2,'3,v^3~>~:*~<~v,'2,'3^~:*~'3,'4,v^4~>~:*~<~v,'3,'4^~:*~'4,'5,v^5~>~:*~ ~<~v,'4,'5^~:*~'5,'6,v^6~>~:*~<~v,'5,'6^~:*~'6,'7,v^7~>~:*~<~v,'6,'7^~:*~'7,'8,v^8~>~:*~<~v,'7,'8^~:*~'8,'9,v^9~>~<~'0,'0,v^0~>~:*~@{~v,'\f^~:*~c~}~|~:}~ ~#[~:;~0^~]~ ~:*~1@{~v,'\f^~@*~@{~v,'\f^~}~@{~v,'\f^~:*~c~}~ ~|~@*~@{~v,'\f^~:*~c~}~@{~v,'\f^~:*~c~}~|~@*~@{~v,'\f^~}~@{~v,'\f^~:*~c~}~|00~|~:}~}~]
Palindrome Detector
~:[~|INPUT-TUPNI~|~;~]~:*~~1{~#*~:*~<~v,'↡^~?~>~@*~<~v,'↡^~:*~<~v,v^~|False~#*~>~#[~;~;~|True~#*~;~|True~#*~:;~@{~a~}~]~>~#[~:;~a~#*~2:*~a~|~2@*~@{~#,2^~a~}~|~]~}
12 Days of Christmas
~:[~|~;~]~:*~ ~{~#*~:*~<~v,'↡^~@*~@{~v,'↡^~:*~a~}~|~#*~:*~>~#,1^~@*~ ~@{~v,'↡^~:*~a~}1~|~@* On the ~#[~;First~;Second~;Third~;Fourth~;Fifth~;Sixth~;Seventh~;Eighth~;Ninth~;Tenth~;Eleventh~;Twelfth~] day of Christmas~%~ My true love sent to me ~@{~#,1^~%~#[~;~;Two~;Three~;Four~;Five~;Six~;Seven~;Eight~;Nine~;Ten~;Eleven~;Twelve~] ~ ~#[~;~;Turtle Doves, and~0^~;French Hens~;Calling Birds~;Gold Rings~;Geese-a-Laying~;Swans-a-Swimming~;~ Maids-a-Milking~;Ladies Dancing~;Lords-a-Leaping~;Pipers Piping~;Drummers Drumming~;~?~],~*~}~%~ A Partridge in a Pear Tree.~2%~0^~}
Implementations
A 5MAT driver, debugger, and much of the example code provided above (and, in general, all active development) can be found in the 5MAT GitHub repo.
Turing Completeness
A 5MAT program which interprets arbitrary Bitwise Cyclic Tag programs is given below (and can be found with comments in the aforementioned GitHub repo).
~:[00111~|101~%~;~]~:*~{~<~v,'1^~@{~v,'↡^~:*~a~}0~|~*~@{~a~}~>~^~*~@{~v,'↡^~:*~a~}1~1@*~a~<~@{~v,'↡^~}~v,'0^~|1~@{~#,1^~a~}~1@*~a~%~#*~>~^~|0~@{~a~}~}
Compilation directly from a Turing machine or other discrete state machine is also fairly straightforward, as are cellular automata (e.g. Rule 110).