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 at the time of this writing, 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 gopkg.in/twik.v1/cmd/twik

This is a short sample session:

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

Another one demonstrating the lexical scoping:

> (var add
.      (do
.          (var n 0)
.          (func (m) (set n (+ n m)) n)
.      )
. )
> (add 5)
5
> (add -1)
4
> 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

This entry was posted in Architecture, Design, Go, Project, Snippet. Bookmark the permalink.

3 Responses to Twik: a tiny language for Go

  1. This would be great for implementing dynamic rules engine in go.

  2. 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}}
    #func
    > (inc 42)
    43

  3. That line should be:

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

    It is now an even better example of how using other braces enhances readability.

Leave a Reply

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