Twik: a tiny language for Go

As part of one of the projects we’ve been pushing at Canonical, I spent a few days researching about the possibility of extending a compiled Go application with a tiny language that would allow expressing simple procedural logic in a controlled environment. Although we’re not yet sure of the direction we’ll take, the result of this short experiment is being released as the twik language for open fiddling.

The implementation is straightforward, with under 400 lines for the parser and evaluator, and under 350 lines in the default functions provided for the language skeleton: var, func, do, if, and, or, etc.

It also comes with an interactive interpreter to play with. You can install it with:

$ go get

This is a short sample session:

> (var x 1)
> x
> (set x 2)
> x
> (set x (func (n) (+ n 1)))
> x
> (x 1)
> (func inc (n) (+ n 1))
> (inc 42)

Another one demonstrating the lexical scoping:

> (var add
.      (do
.          (var n 0)
.          (func (m) (set n (+ n m)) n)
.      )
. )
> (add 5)
> (add -1)
> n
twik source:1:1: undefined symbol: n

New functionality may be plugged in by providing Go functions. For example, here is a simple printf function:

func printf(args []interface{}) (interface{}, error) {
        if len(args) > 0 {
                if format, ok := args[0].(string); ok {
                        _, err := fmt.Printf(format, args[1:]...)
                        return nil, err
        return nil, fmt.Errorf("printf takes a format string")

func main() {
        err = scope.Create("printf", printf)

It can now greet the world:

$ cat test.twik

(func hello (name)
      (printf "Hello %s!\n" name)

(hello "world")

$ time ./twik test.twik
Hello world!
./twik test.twik  0.00s user 0.00s system 74% cpu 0.005 total

3 thoughts on “Twik: a tiny language for Go

  1. Luciano Ramalho

    My main gripe with s-expression syntax is not the overabundance of parenthesis, but the lack of syntactic clues about rules of evaluation. For example, given the expression (foo bar baz) the human reader does not know whether bar and baz will be evaluated. If foo is a function, bar and baz will be evaluated before the function is applied. If foo is not a function, all bets are off. A simple enhancement to s-expression syntax is to use parenthesis only for function application, and braces or brackets for other constructs. Here is an example adapted from yours:

    {func inc [n] {+ n 1}}
    > (inc 42)

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>