Home

Awesome

GoDoc Go Report Card license

criTiCaL - A TCL interpreter written in golang

After re-reading TCL the Misunderstood, I decided to create a simple TCL evaluator of my own. This project is the result, and it has feature-parity with the two existing "small TCL" projects, written in C, which I examined:

There is a simple introduction to this project, and TCL syntax, on my blog here:

The name of this project was generated by looking for words containing the letters "T", "C", and "L", in order. I almost chose arTiCLe, TreaCLe, myThiCaL, or mysTiCaL. Perhaps somebody else can write their own version of this project with one of those names!

Building & Usage

This repository contains a TCL-like interpreter, along with a sample driver.

You can build both in the way you'd expect for golang applications:

$ go build .

Once built you can execute the application, supplying the path to a TCL script which you wish to execute. For example:

    $ ./critical examples/prime.tcl
    0
    1
    2 is prime
    3 is prime
    ..

The interpreter contains an embedded "standard-library", which you can view at stdlib/stdlib.tcl, which is loaded along with any file that you specify.

To disable the use of the standard library run:

   $ ./critical -no-stdlib path/to/file.tcl

Generally the point of a scripting language is you can embed it inside a (host) application of your own - exporting project-specific variables and functions.

You'll find an example of doing that beneath the embedded/ directory:

Examples

The following is a simple example program which shows what the code here looks like:

//
// Fibonacci sequence, written in the naive/recursive fashion.
//
proc fib {x} {
    if { <= $x 1 } {
        return 1
    } else {
        return [expr [fib [expr $x - 1]] + [fib [expr $x - 2]]]
    }
}

//
// Lets run this in a loop
//
set i 0
set max 20

while {<= $i $max } {
   puts "Fib $i is [fib $i]"
   incr i
}

Another example is the test-code which @antirez posted with his picol writeup which looks like this:

proc square {x} {
    * $x $x
}

set a 1
while {<= $a 10} {
    if {== $a 5} {
        puts {Missing five!}
        set a [+ $a 1]
        continue
    }
    puts "I can compute that $a*$a = [square $a]"
    set a [+ $a 1]
}

This example is contained within this repository as picol.tcl, so you can run it directly:

     $ ./critical ./picol.tcl
     I can compute that 1*1 = 1
     I can compute that 2*2 = 4
     ..

Additional TCL-code can be found beneath examples/.

Available Commands

The following commands are available, and work as you'd expect:

The complete list of standard TCL commands will almost certainly never be implemented, but pull-request to add omissions you need will be applied with thanks.

Features

Read the file input.tcl to get a feel for the language, but in-brief you've got the following facilities available:

Missing Features

The biggest missing feature is the complete absence of support for lists of any kind. This is common in the more minimal-TCL interpreters I examined.

The other obvious missing feature is support for the upvalue command, which means we're always a little at risk of scope-related issues.

Adding upvalue would be possible, but adding list-processing would be more work than I'd prefer to carry out at this time - see #19 for details of what would be required to implement this support.

Testing

Our code has 100% test-coverage, which you can exercise via the standard golang facilities:

$ go test ./...

There are also fuzz-based testers supplied for the lexer and parser packages, to run these run one of the following two sets of commands:

cd parser
go test -fuzztime=300s -parallel=1 -fuzz=FuzzParser -v
cd lexer
go test -fuzztime=300s -parallel=1 -fuzz=FuzzLexer -v

See Also

This repository was put together after experimenting with a scripting language, an evaluation engine, putting together a FORTH-like scripting language, writing a BASIC interpreter and creating yet another lisp..

I've also played around with a couple of compilers which might be interesting to refer to:

Bugs?

Please feel free to open a new issue with your example included so I can see how to fix it.

Steve