Syntax

Stutter inherits its syntax from the LISP family of languages. A Stutter program is a series of expressions, which are evaluated one by one. There is an interactive shell (stt) which for each typed line will evaluate all contained expressions and print the result of the last evaluation.

Expressions are separated by whitespace. Each expression is either an atom or a list.

A list is a number of expressions (0 or more) closed in parentheses.

Atoms are either symbols or single values (integers, numbers (floating point), strings and some other miscellany). Single values all evaluate to themselves:

> 8
8
> .5
0.5
> "Hello world!"
"Hello world!"

There are two important constant types: nil (the same as an empty list), which also means "false"; and T, which serves as "true".

Symbols (such as abc or 1- but not -1, which is an integer) serve as variable names. All letters in symbols are converted to upper case (so symbols are case insensitive). On evaluation, the interpreter attempts to look up a value associated with the symbol:

> abc
ERROR SE_UNDEF: Variable 'ABC' has no value at stdin:1
> t
T     (so the symbol 'T' has T (true) object bound to it)

Non-empty lists are evaluated in a special way, as S-expressions. An S-expression is a list in which the first element determines (that is, should evaluate to) a function, which is called, and the rest of the elements get passed to the function as parameters.

For example, the symbol + has a builtin function object bound to it:

> +
b:+     (printed representation of the + builtin)
> (+ 2 3)     (a list of three atoms: a symbol and two integers)
5

This is practically all the syntax Stutter has, except for comments (in each source line, everything after a semicolon (;) is ignored), and the single quote (') operator. 'expression is a shortcut for (quote expression), an S-expression calling the builtin quote, which just returns its parameter unevaluated. This provides an easy way to have your symbols and lists left alone, rather than getting them evaluated (as variable names or S-expressions):

> a
ERROR SE_UNDEF: Variable 'A' has no value at stdin:1
> 'a
A
> (1 2 3)
ERROR SE_WFUNC: Non-function in S-expression at stdin:1 [(1 2 3)]
> '(1 2 3)
(1 2 3)

There is some additional syntax for dotted pairs, explained in the Lists section.

Basics

You can use the set builtin to create new value-symbol bindings:

> (set 'a (+ 2 5))
7
> (+ a 3)
10

The symbol has to be quoted because set will evaluate the name before use:

> (set 'b 'a)  ; assign the symbol atom "A" to variable B
A
> (set b 8)
8
> a
8

You can create your own function objects with lambda. From the builtins documentation:

(lambda paramlist exp1 exp2 ...)

Returns a user function that receives parameters by the names listed in paramlist, with a body executing all expressions. The function will return the result of the last expression. For example:

> (set 'square (lambda (x) (* x x)))
l:SQUARE(X)
> (square 3)
9

Superfluous parameters are accessible in the special variable _rest, as a list. For example:

> (set 'foo (lambda (x) (append (list x) '(rabbit) _rest)))
l:FOO(X)
> (foo 'fox 'horse 'yoda)
(FOX RABBIT HORSE YODA)

Default values for some parameters may be supplied (thus making them optional):

> (set 'plus (lambda (x (y 3)) (+ x y)))
l:PLUS(X (Y 3))
> (plus 3)
6
> (plus 3 5)
8

Parameters may be given in key-value pairs:

> (set 'ab (lambda (a b) (list 'a 'is a 'b 'is b)))
l:AB(A B)
> (ab :b 20 :a 10)
(A IS 10 B IS 20)

Stutter functions have their own separate veriable context. This means that while all variables defined in the parent context (where the function was called from) are visible, bindings have effect only in the local scope.

If a function body has more than one expression and the first expression is a string in itself, then that string is removed from the body and instead taken as short documentation for the function.

> (set 'square (lambda (x) "Compute the square of X." (* x x)))
l:SQUARE(X) "Compute the square of X."

For the rest, you can see the Builtins and Library documentation (even the C API docs might provide helpful).

Lists

Stutter lists are composed of CONS objects. These are pairs of object references. The two 'sides' of a CONS are called CAR and CDR (for historical reasons), and are accessible by similarly named builtins.

A CONS in itself that contains two arbitrary values is called a dotted pair and may be specified by a syntax: (a . b)

When part of a list, the CONS has the list item in the CAR and the rest of the list in the CDR (or nil, when the item is the last one). For example, the list (1 2 3) might be written as: (1 . (2 . (3 . ()))).

It is conceivable that the CDR of the last CONS of a list isn't nil; in this case, the list may be described as (a b [..] c . x). This object isn't a "list", strictly speaking, so it shouldn't be used as such.

I/O

Stutter standard input is the stream bound to the global variable *input-stream*. All reading functions read from this stream:

All these functions will throw a SE_IO error on EOF (read and read-line only when they had no (meaningful) input at all). Additionally, read might throw SE_PARSE on a parse error.

Likewise, standard output is the stream bound to *output-stream*.

There are two ways to make streams: open-file, which will create a stream to read from or write to a file; and make-stream, which creates a stream out from a user lambda function.

User streams, when written to, call the user function for each written character; when read from, use the character that the user function returns. Thus, input stream functions have always to return a character (1-char long string), or throw SE_IO on EOF.

An easy and elegant way to do I/O with custom streams is to use a let block:

(set 'tmpfile (+ "/tmp/stutter-"
                 (->str (get-time))
                 "-"
                 (->str (random 10000000))))

(let ((*output-stream* (open-file tmpfile "w")))
  (print '(1 2 3 4))
  (print "Some string data goes here.")
  (print '(1 . 3))
  (close-file *output-stream*))

(let ((*input-stream* (open-file tmpfile)))
  (catch (list SE_IO)
         (lambda ())
         (while t
                (println (read))))
  (close-file *input-stream*))