Lispy Logo

Lispy Logo is a toy programming language, a proof-of-concept implementation of the tiny scripting engine described in a No Time To Play article. You can use it as a model or starting point, for teaching, maybe even embed it.

Why? Because it's easier to reason about a real, working interpreter.

Why Logo? Because I'm familiar with the language, and it's a change of pace from the usual Lisp or Scheme lookalikes.

Why so large? Because working on it gave me all kinds of ideas for things to add. None of it complicates the internals, and it makes a good impression.

To implement Lispy Logo as shown here, you need a host language with a variant data type capable of holding any value, even anonymous functions.

As of May 2021, this project is abandoned; no idea if the D source code even still compiles.

Quick start

To begin with, try something like:

	[sqrt [add [mul 3 3] [mul 4 4]]]

	[make "n [parse [prompt "Enter "two "numbers:]]]
	[make "a [item 0 n]] [make "b [item 1 n]]
	[ifelse [gt? a b] [alert "First!] [alert "Second!]]
   

	[foreach [list 1 2 3] [alert ?]]
	[repeat 3 [alert #]]
   

See the tutorial for details.

News

As of 7 February 2019, Lispy Logo also supports property lists.

As of 10 February 2019, the Javascript port is no longer included in the download package. You can still get it right here. In compensation, there is now a port to the D programming language.

As of 11 February 2019, queue, dequeue, push and pop are available in all implementations, working like their Logo counterparts. The test, iftrue and iffalse control structures were also added. Lispy Logo is now in beta.

As of 13 February 2019, the native port has proper command line options.

As of 15 February 2019, it's safe to compile the interpreter with -release, as all builtins check their argument count.

Current version as of 15 February 2019 is 1.0.3 beta.

Download

Apart from the online port (embedded above) you can download a source code archive to run locally.

All the code is open source under the MIT license. See inside for details.

Lispy Logo tutorial

A Lispy Logo script is made up of words grouped into lists. Words are separated by whitespace; lists sit in square brackets. A word normally denotes a variable lookup, while a list is a procedure call, with some exceptions:

You can also be explicit about it: a double-quote sign (") placed in front of a word or list (in short, an expression) causes it to be taken at face value. Beware, double quotes aren't paired like in other languages!

Last but not least, a few words have special status: quoted, if, ifelse, while, until, foreach and repeat. They look like procedures in normal use, but can't be looked up or deleted. These are called primitives instead.

Now let's try something a little more interesting than a "hello, world":

[sqrt [add [mul 3 3] [mul 4 4]]]

Note how all the words are in lowercase. Case matters in Lispy Logo, unlike classic dialects of the language. If you are familiar with those, there have been some changes apart from the obvious:

If you don't know either Lisp or Logo, don't panic! Lispy Logo is a lot simpler than either. Read on for a gentle introduction.

First steps

By itself, Lispy Logo is just a powerful calculator. Simply enter expressions like the first example above to be shown the result. You can do more than one-off calculations however. For example (use alert instead of print for the online version):

[make "a 15] [ifelse [eq? [modulo a 2] 1] "odd "even]
[foreach [list 1 2 3] [print ?]]
[repeat 3 [print #]]

Note how the last two examples respond with None (or undefined) after they're done. That's because foreach and repeat return nothing useful. But what do they do, exactly?

For those unfamiliar with Logo, make is the assignment operation. Note how the variable name has to be quoted. On the plus side, that means you can refer to a variable indirectly!

You can get a list of all constructs recognized by Lispy Logo with [names], [procedures] and [primitives], respectively.

More control structures

Lispy Logo also has more conventional ways to make loops and decisions:

[if [lt? 1 2] [make "a 1] [make "b 2] [make "c 3]]
[while [lte? a 3] [print a] [make "a [add a 1]]]
[until [gte? b 10] [print b] [make "b [mul b 2]]]

All of them take a condition followed by any number of other expressions. (Unlike ifelse, if doesn't have an "else" part.) Moreover, you're not limited to running fixed chunks of code exactly as you typed them. A few procedures help with that:

[run [list "add 1 2]]
[apply add [list 1 2]]
[invoke add 1 2]

Note that in the first example you can even use primitives! (Those don't work with apply or invoke.) You can refer to any procedure by name, store it in a variable and recall it later. For instance, to transform or filter a list:

[map sqrt [list 25 100 225]]
[filter number? [list 1 "a 3 "b 5]]

All that is possible because Lispy Logo code is composed of the same lists you use to store and manipulate data. And to help you do more with lists, the language comes with about a dozen useful operations, as you'll see in moments.

Working with lists

You've already seen a way of making new lists. Another is simply to quote a literal one, like this: "[1 2 3]. The difference is, the list procedure receives its arguments already evaluated, so you can mix the results of calculations among your data. And once you have it, there are a few things you can do with it:

All of these also work on words. Beware of differences between implementations: the Python interpreter will error out if the list is shorter than the index you give to item or setitem, while the Javascript version will simply return an empty value.

(Speaking of which: Logo lacks a concept of "null", or "None", so Lispy makes the empty? procedure work double duty to detect those.)

Last but not least, a list is a lot more useful when you can add and remove elements at will:

[make "a [list 1 2 3]]
[queue a 5] [dequeue a]
[push a 0] [pop a]

Beware however that these change the original list. If you'd rather make a new one, use their companions instead:

[fput 0 a] [first a] [butfirst a]
[lput 5 a] [last a] [butlast a]

Apart from fput and lput, these also work on words. To make a new word however you need word: [word "Lo "go].

Before concluding this section, there is another kind of list: the property list. This is what other languages call a dictionary, or map; in (Lispy) Logo they work a bit differently:

[pprop "synonyms "take "get]
[gprop "synonyms "take]
[remprop "synonyms "take]

For one thing, property lists are automatically created, unless a variable or procedure by that name already exists. And then, if the property list or the key doesn't yet exist, gprop returns an empty list instead of making it an error. It is an error however if you try to read or set the properties of something that isn't a property list.

One more thing

Until now, all the code examples here have been one-liners. That's because they were intended for the interactive console. But Lispy Logo can run scripts of any length, where expressions extend over as many lines as needed.

For that purpose, get the downloadable interpreter. It knows a few more tricks, including a load procedure that takes a file name as an argument and runs its contents in-place. These script files can even include comments: any line that begins with a semicolon (;) will be ignored.

That's about it for a starter. Enjoy Lispy Logo!