cyanogilvie / Tcl.js

Javascript implementation of the Tcl language
Other
157 stars 6 forks source link

Tcl in Javascript

This project is an attempt at a robust, high performance subset of the Tcl language, implemented in Javascript.

Why Tcl in a Browser?

I use Tcl extensively as an extension language, or to implement domain specific languages for configuration or defining custom behaviour for business objects. These Tcl scripts often come from users, so Tcl's safe interpreters provided a nice sandbox for running these scripts.

The market has changed, and no longer wants to install native applications (in Tk, or anything else for that matter), particularly in an enterprise environment. To address this we moved our frontend application to Javascript in a browser, which also extends the reach to mobile devices. But the frontend still needs to support all the existing DSL code, and rewriting it in Javascript wouldn't solve it because running untrusted Javascript scripts in the browser (via eval or new Function) is an unacceptable security and stability risk. This interpreter addresses both these concerns.

Why Another One?

There are several other implementations of Tcl in Javascript, ranging from the toy level (around 1000 lines of js) to the behemoth. I needed something a bit different from all the existing implementations:

This attempt does not completely reach the above goals yet, but it is sufficiently close for my needs, and with time I hope that the supported subset will grow.

What Does It Look Like?

HTML:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Tcl.js</title>
        <script type="module" src="https://github.com/cyanogilvie/Tcl.js/raw/master/main.js"></script>
    </head>
    <body>
    </body>
</html>

main.js:

import Interp from './tcl/interp.js';

var I = Interp();

I.registerCommand('annoy_user', function(args){
    I.checkArgs(args, 1, 'string');
    alert(args[1]);
});

I.TclEval('annoy_user "hello, world"', function(result){
    switch (result.code) {
        case I.types.OK:
        case I.types.RETURN:
            break;
        case I.types.ERROR:
            console.warn('Uncaught error in Tcl script: '+result.result);
            break;
        default:
            console.warn('Unexpected return code from Tcl script: '+result.code);
    }
});

How Fast Is It?

The performance relative to a native Tcl interpreter will vary widely depending on which areas of the interpreter are stressed, but attempting to measure the command dispatch (and to an extent, expression evaluation and variable accesses), the following code:

for {set i 0} {$i < 10000} {incr i} nop

With nop implemented in native Tcl as:

proc nop args {}

and in Tcl.js as:

var I = Interp();
I.registerCommand('nop', function(){});

The timings are as follows (on my MacBook Air with a Core i5 @ 1.8GHz):

So it's around 3 times slower than the c interpreter on this benchmark. On other browsers it's a very different story unfortunately. Safari clocks in at about 68000 microseconds, and Firefox (v18.0.1 - with the shiny new IonMonkey engine) manages 140599 microseconds. I haven't the stomach (or the platform) to test IE.

2015 update: Performance in Firefox 40 is roughly on par with Chrome now.

What Is The License?

Tcl.js is copyright Cyan Ogilvie, and licensed under the same terms as Tcl. (BSD-ish)

Why Are All The Headings Questions?

I don't know. It bothers me also.