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
Symbols (such as
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
> 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 (
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.
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).
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.
CONS in itself that contains two arbitrary values is called a
dotted pair and may be specified by a syntax:
When part of a list, the
CONS has the list item in the
CAR and the rest of the list in the
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
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.
Stutter standard input is the stream bound to the global variable
*input-stream*. All reading functions read from this stream:
(read-char)- reads a single character (1-char long string)
(read-line)- reads until a newline
(read)- reads and parses a single valid expression
All these functions will throw a
SE_IO error on EOF
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
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
(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*))