Lil Terminal is a command-line interface for the Lil programming language, allowing easy experimentation outside of Decker. Lilt also includes bindings for basic CLI and filesystem IO, making it potentially usable for shell scripting and other applications.


The source code for Lilt is available On GitHub.

Lilt depends upon the C standard library and some POSIX extensions. It should work on OSX or most Linux distros. To build and install Lilt, run the included make script. By default, a binary is installed in /usr/local/bin.

make lilt && make install

Invoking Lilt

$ lilt [FILE.lil...] [-e EXPR...]
	if present, execute a FILE and exit
	-e : evaluate EXPR and exit
	-h : display this information

Executing a FILE or EXPR argument will not automatically produce any output. Use show[] or print[] to produce results on stdout:

$ lilt -e "show[100+range 5]"

$ lilt -e "print[sys.version]"

If a FILE or EXPR argument has not been provided, Lilt will run in interactive “REPL” mode, which can be exited with Ctrl+C or the exit[] function. You may find it more comfortable to run the lilt REPL under the rlwrap utility (available in a *nix package manager near you), which provides command history and line editing features.

$ rlwrap lilt

For convenience, after each line is executed at the REPL, the result will be stored in a variable named _:


You can write reasonably portable shell scripts by starting a file with a “shebang” something like:

#!/usr/bin/env lilt

If an environment variable named LIL_HOME is set, Lilt will search that directory path at startup, executing any .lil files. These could in turn easily load datasets or other useful definitions every time you open a REPL. Startup scripts are always loaded prior to executing FILE or EXPR arguments.

Global Variables

Name Description
args List of CLI arguments to Lilt as strings.
env Dictionary of POSIX Environment variables.
sys An Interface which exposes information about the Lilt runtime.
rtext An Interface with routines for working with rich text.
pi The ratio of a circle’s circumference to its diameter. Roughly 3.141.
e Euler’s number; the base of the natural logarithm. Roughly 2.718.
colors A dictionary of named pattern indices.

Built-in Functions

Name Description Purpose
input[x] Read a line from stdin as a string, optionally displaying x as if with print[], without the newline. Console
show[x...] Print a human-comprehensible representation of the value x to stdout followed by a newline, and return x. Console
print[x...] Print a string x to stdout followed by a newline. If more args are provided, format all but the first using x. Console
error[x...] Print a string x to stderr followed by a newline. If more args are provided, format all but the first using x. Console
dir[x] List the content of a directory as a table.(1) Files
path[x y] Canonical path x (joined with y, if given) via realpath(). Files
read[x hint] Read a file x using hint as necessary to control its interpretation.(2) Files
write[x y] Write a value y to a file x. Returns 1 on success.(3) Files
exit[x] Stop execution with exit code x. System
shell[x] Execute string x as a shell command and block for its completion.(4) System
eval[x y] Parse and execute a string x as a Lil program, using any variable bindings in dictionary y.(5) System
random[x y] Choose y random elements from x. In Lilt, sys.seed is always pre-initialized to a constant. System
readcsv[x y d] Turn a RFC-4180 CSV string x into a Lil table with column spec y.(6) Data
writecsv[x y d] Turn a Lil table x into a CSV string with column spec y.(6) Data
readxml[x] Turn a useful subset of XML/HTML into a Lil structure.(6) Data
writexml[x] Turn a Lil structure x into an indented XML string.(6) Data
readdeck[x] Produce a deck interface from a file at path x. If no path is given, produce a new deck from scratch. Decker
writedeck[x y] Serialize a deck interface y to a file at path x. Returns 1 on success.(7) Decker
array[x y] Create a new array with size x and cast string y, or decode an encoded array string x. Decker
image[x] Create a new image interface with size x ((width,height)) or decode an encoded image string. Decker
sound[x] Create a new sound interface with size x (sample count) or decode an encoded sound string. Decker

1) dir[] of a file results in an empty table. Directory tables contain:

2) read[] recognizes several types of file by extension and will interpret each appropriately:

If a GIF file is unreadable or missing, it will be loaded as a 0x0 image. Only the first frame of a GIF will be loaded. If the image contains transparent pixels, they will be read as pattern 0. By default, other pixels will be adapted to Decker’s 16-color palette (patterns 32–47). If the hint argument is "gray", they will instead be converted to 256 grays based on a perceptual weighting of their RGB channels. Note that a 256 gray image is not suitable for direct display on e.g. a canvas, but can be re-paletted or posterized in a variety of ways via[] or dithered with image.transform["dither"].

The WAV file format is much more complex than one might imagine. For this reason, and in order to avoid drawing in large dependencies, read[] in Lilt accepts only a very specific subset of valid WAV files corresponding to the output of write[]: monophonic, 8khz, with 8-bit unsigned PCM samples and no optional headers. Any other format (or an altogether invalid audio file) will be read as a sound with a size of 0. For reference, you can convert nearly any audio file into a compatible format using ffmpeg like so:

ffmpeg -i input.mp3 -bitexact -map_metadata -1 -ac 1 -ar 8000 -acodec pcm_u8 output.wav

3) write[] recognizes several types of Lil value and will serialize each appropriately:

4) shell[] returns a dictionary containing:

5) eval[] returns a dictionary containing:

6) See the Decker Manual for details of readcsv[], writecsv[], readxml[], and writexml[].

7) If the path given to writedeck[] ends in a .html suffix, the deck will be written as a “standalone” deck with a bundled HTML+JS runtime. Otherwise, the deck will be written as a “bare” deck, which is smaller.

Working With Decks

The readdeck[] and writedeck[] functions allow Lilt to operate on Decker documents. Lilt can load, create, and manipulate multiple decks simultaneously, providing options for automated testing, data import/export, accessibility, and interacting with other technology from outside the Decker ecosystem.

In addition to the fields and methods available to Decker for the deck interface and each of its sub-interfaces, Lilt has access to event injector members which behave as if the corresponding event was produced by a user interacting with the deck, running the appropriate scripts to completion and producing any appropriate side-effects on the deck. These injectors are as follows:

Name Description[] Simulate clicking a button.[row] Simulate selecting a row in a grid.
grid.order[col] Simulate clicking a column header in a grid.
grid.change[table] Simulate editing the data in a grid. (Does not actually change grid.value unless the script does!)[pos] Simulate depressing the pointer on a canvas.
canvas.drag[pos] Simulate dragging the pointer on a canvas.
canvas.release[pos] Simulate releasing a held pointer on a canvas.[text] Simulate clicking a link in a rich-text field.[text] Simulate pressing shift+return with text selected in the field.
field.change[text] Simulate editing a field. (Does not actually change field.text or field.value unless the script does!)
slider.change[text] Simulate manipulating a slider. (Does not actually change slider.value unless the script does!)
card.navigate[dir] Simulate the user pressing navigation/cursor keys.

Furthermore, Lilt can “copy” and “paste” entire cards from a deck or lists of widgets within a card:

Name Description
card.copy[list] Save a list of widgets on card as a string.
card.paste[text] Append the widgets within a string to card, returning a list of the new widgets.
deck.copy[card] Save a card and its contents as a string.
deck.paste[text] Append a card and its contents from a string to deck, returning the new card.

All of these operations work with a serialized form of the corresponding deck components as a string. The format of these strings is subject to change in the future and should be treated as opaque, but a valid string will always begin with the prefix %%CRD0 (a copied card) or %%WGT0 (a list of copied widgets). Strings can be freely copied and pasted between decks, and even pasted multiple times. As with deck.add[] and card.add[], the name fields of pasted objects will be changed if they would collide with existing parts of the deck.