Experimental Little Language
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Přemysl Janouch cd0f978b09
Use Go modules
1 week ago
cmd Go: make use of multiple return values 2 months ago
ell Go: move the stdlib to a different file 2 months ago
.gitignore Add a REPL for toying around 1 year ago
LICENSE Relicense to 0BSD, update mail address 5 months ago
Makefile Add a REPL for toying around 1 year ago
README.adoc Update README 2 months ago
ell.c C: add const specifiers where appropriate 2 months ago
ell.vim Add VIM syntax highlighting 1 year ago
go.mod Use Go modules 1 week ago
go.sum Use Go modules 1 week ago
greet.ell Add VIM syntax highlighting 1 year ago
interpreter.c Relicense to 0BSD, update mail address 5 months ago
repl.c Relicense to 0BSD, update mail address 5 months ago


ell is a middle ground between Scheme and Tcl. The goal was to conceive a programming language implementable with as little code as possible while still being reasonably comfortable to use.

This package contains two implementations of said language--one in C and another in Go--which are meant to be self-contained, portable and reusable. Performance is specifically not an intent.

The project is currently in a "proof of concept" stage with many useful data operations missing but I believe it won't be a problem to implement them as needed for anyone interested.


Owing to its heritage, ell is homoiconic, that is a program can be directly expressed using the language's data types. There are only two of those: the list and the string. Any numerical conversions are made on an as-needed basis. Similarly, strings act like atoms/symbols when executed.

The parser, however, does a bunch of transformations:

  • [a b c] makes a call to (list a b c);

  • @var is a shorthand for (set var);

  • { code } is the most complex one. Each line within the curly braces is wrapped in parentheses, and the resulting sequence is wrapped in a quoted list, so that it doesn't execute immediately.

As an example, consider the following snippet:

print (if { eq? @var foo } {
    values 'Hello world\n'
} else {
    values 'Error\n'

which gets expanded to the following:

((print (if (block (eq? (set var) foo))
            (block (values 'Hello world\n'))
            (block (values 'Error\n')))))

Observe that the whole program is enclosed in an implicit pair of {} and that block is all that's left of special forms.

For a slightly more realistic example you can have a look at greet.ell.


Variables use per-block dynamic scoping. Arguments to a block (which is a list of lists) are assigned to local variables named 1, 2, etc., and the full list of them is stored in args.

When evaluating a command, the first argument is typically a string with its name and it is resolved as if set was called on it. Lists are left for execution as they are.

The last expression in a block is the block's return value.

Special Forms

block [<arg>]…​

Like list but doesn't evaluate arguments. A more appropriate name might be quoted-list, which is not as descriptive in terms of syntax. If simple quoting is desired, the list can be unpacked by an ordinary command.

Standard Library

The standard library interprets the empty list and the empty string as false values, everything else is considered true. Numbers are floating point with double precision, trailing zeroes are truncated.

Where a <body> is expected, strings retain their value, and block evaluation is postponed as necessary.

local <names> [<value>]…​

Create local variables in the current block. Names for which there are no values left default to ().

set <name> [<value>]

Retrieve or set a named variable. The syntax sugar for retrieval is @.

list [<item>]…​

Return a list made of given arguments. The syntax sugar for lists is [].

values [<item>]…​

Return an arbitrary number of values.

if <cond> <body> [elif <cond> <body>]…​ [else <body>]

Conditional evaluation.

for <list> <body>

Run the body for each element.


Abort the running loop.

map <list> <body>

Transform each element with the given function into any number of values.

filter <list> <body>

Return a new list consisting of matching elements only.

.. [<string>]…​

Concatenate strings.

print [<item>]…​

Print all items in sequence--strings directly, lists as source code.

system <command>

Run a system command and return its return value.

parse <program>

Parse a program into a list of lists.

try <body> <handler>

Execute the body and pass any error to the handler instead of propagating it.

throw <message>

Throw an error. Messages starting on an underscore don't generate backtraces, which can be used to catch them.

not <value>

Return a boolean with the opposite truthiness.

and [<body>]…​, or [<body>]…​

Short-circuit evaluation, trying to return whatever the bodies result in.

+, -, `*`, /

Arithmetic operations on floating point numbers.

=, <>, <, >, <=, >=

Arithmetic comparisons on floating point numbers.

eq?, ne?, lt?, gt?, le?, ge?

Simple string comparisons.

Building and Running

By default, running `make' will only build the interpreter:

$ make
$ ./interpreter greet.ell

Install development packages for GNU Readline to get a REPL for toying around:

$ make repl
$ ./repl

The Go port can be built using standard Go tools and behaves the same.

Possible Ways of Complicating

  • `local [_a _b _rest] @args` would elegantly solve the problem of varargs, that is, unpack a list when names are list, and make the last element a list when there are more arguments than names

  • reference counting: in the C version, currently all values are always copied as needed, which is good enough for all imaginable use cases, simpler and less error-prone

Contributing and Support

Use https://git.janouch.name/p/ell to report any bugs, request features, or submit pull requests. git send-email is tolerated. If you want to discuss the project, feel free to join me at ircs://irc.janouch.name, channel #dev.

Bitcoin donations are accepted at: 12r5uEWEgcHC46xd64tt3hHt9EUvYYDHe9


This software is released under the terms of the 0BSD license, the text of which is included within the package along with the list of authors.