Shiny Wheat
An interpreter for the Wheat esoteric programming language. Written in Gleam.
Wheat Language
Wheat is a Turing-complete esoteric language created by KeyMaker. Keymaker has a page describing it here, and Esolang Wiki has a page on it here.
Every Wheat program runs in a loop, outputting characters and reading in characters from the previous iteration. There is no external input. Variables are named using a digit or lowercase letter, and are either empty or hold one character. Instructions are written one per line, and grouped into blocks by indentation (one space per level). String literals are wrapped in double-quotes, and there is no escape character. Q and N are also considered string literals, and represent a double-quote and newline. Empty lines and trailing whitespace are not allowed.
The language is made up of six elements:
- Comments are written as
-a comment. They must not be indented. input zwill store the next character from the previous cycles's output into the variable z.output zoroutput "foo"will output the content of a variable or a string literal.if z "h":/if not z "h":will execute the following block only if the variable z does / doesn't contain the letter h.for-input z:will execute the following block in a loop, taking a character from input and storing it in the variable z each time.terminatewill stop execution of the program.
Usage
By default, Shiny Wheat takes the name of the file to execute as an argument and writes output to standard out. Errors are sent to standard error. A "cycles" argument can optionally be provided, which stops the interpreter after executing a given number of loops of the program.
Building a stand-alone executable
Gleam is required to run this project, and can be found here.
This project uses Gleepack to build to an executable. Run gleam run -m gleepack build to build for your current system. The executable will be placed in build/shiny_wheat.
To build for a different target: Use gleam run -m gleepack targets available to list the names of the available targets, then run (e.g.) gleam run -m gleepack build --target=amd64-win32-otp-29.0.2.
Using as a library
The parse_program function can be used to create an interpreter that yields the output from each cycle. This can then be used directly (it's a Yielder(String)) or passed to the run_collecting function.
run_collecting takes the interpreter and a maximum number of cycles (-1 for unlimited), and produces a single string containing all output. An example is below:
import shiny_wheat
const program = "-example program
output \" \"
input a
input b
input c
if not a \" \":
output \"-|\"
if a \" \":
output c
output b
output b
for-input d:
output d
"
pub fn main() {
let assert Ok(interpreter) = shiny_wheat.parse_program(program)
let output = shiny_wheat.run_collecting(interpreter, 8)
assert output == " -| |-- -||- |--|- -||-|- |--|-|- -||-|-|- |--|-|-|-"
}
Motivation
I found Keymaker's implementation difficult to read, and wanted to try creating my own. Keymaker's implementation is also written in Python2, which is slowly becoming less accessible. I picked Gleam for novelty and legibility.
Differences from official implementation
I tried to make this implementation match Keymaker's Python2 script, differing only in the text of error messages. However I added two small changes for convenience:
- Any whitespace after the end of the Wheat program is ignored. This means that a trailing newline (which is added automatically by many editors) is not an error.
- Lowercase "z" is a valid register. Keymaker's own description makes it pretty clear this is the intended behaviour. However the official interpreter will crash if this variable is read or written to, due to an off-by-one error.