ADDED README.txt Index: README.txt ================================================================== --- README.txt +++ README.txt @@ -0,0 +1,33 @@ +tcl.js "A Tcl implementation in Javascript" + + * Released under the same terms as Tcl itself. + * (BSD license found at ) + * + * Based on Picol by Salvatore Sanfilippo () + * (c) Stéphane Arnold 2007 + * Richard Suchenwirth 2007, 2013: cleanup, additions + +This emulates (in part) an interpreter for the Tcl scripting language. Early versions were tested in browsers, but since the advent of node.js, I only use that, like a tclsh (interactive or with script file(s) to evaluate). + +The test suite is also frequently tested against a real tclsh, currently 8.5.13. Only a few tests dealing with special numbers (Infinity, NaN) are skipped when real Tcl runs. + +The tcljs project has a home at http://code.google.com/p/tcl-js/. +Version control via mercurial (hg). +Also via Fossil at https://chiselapp.com/user/suchenwi/repository/tcl-js/dir?ci=tip + +I used to develop this with node.js v0.6.19 (which was standard via apt-get). Now that backslash escapes are finally working, the test suite (which is in UTF-8) needs to be parsed as such, so I upgraded to node.js v0.10.22. + +On the command line you can pass code snippets that are executed before the Read-Eval-Print loop is entered For instance, this runs the test suite: + +$ DEBUG=0 node tcl053.js "source /home/suchenwi/tcl-js/test_tcljs.tcl" + +With DEBUG=1, all commands are reported before execution, and all exceptions. + +Still missing: +- blocking [exec] +- blocking [gets] +- [expr] to also accept command calls in braced expressions, e.g. + if {[llength $x] > 2} ... +- [open], [puts] to file, [close] +- +- and many more... Index: tcl053.js ================================================================== --- tcl053.js +++ tcl053.js @@ -319,22 +319,10 @@ list.splice(i, 2); interp.setVar(name, dict); } return dict; }); - /* - if(typeof(jQuery) != 'undefined') { - this.registerCommand("dom", function (interp, args) { - var selector = args[1].toString(); - var fn = args[2].toString(); - args = args.slice(3); - for (var i in args) args[i] = args[i].toString(); - var q = $(selector); - q[fn].apply(q,args); - return "dom " + selector; - }); - }*/ this.registerCommand("eval",function (interp, args) { this.arity(args, 2,Infinity); for (var i = 1; i < args.length; i++) args[i] = args[i].toString(); if (args.length == 2) var code = args[1]; else var code = args.slice(1).join(" "); @@ -362,20 +350,19 @@ this.arity(args, 1,2); var rc = 0; if (args.length == 2) rc = args[1]; process.exit(rc); }); - var acos = Math.acos; - var exp = Math.exp; - var sqrt = Math.sqrt; // "publish" other Math.* functions as needed - this.registerCommand("expr", function (interp, args) { var expression = args.slice(1).join(" "); return interp.expr(interp, expression); }); this.expr = function (interp, expression) { // also used in for, if, while var mx; + var acos = Math.acos; + var exp = Math.exp; + var sqrt = Math.sqrt; // "publish" other Math.* functions as needed try { mx = expression.match(/(\[.*\])/g); for (var i in mx) puts("have to deal with "+mx[i].toString()); } catch(e) {puts(i+". exception: "+e);} @@ -459,18 +446,18 @@ interp.code = interp.OK; while (true) { test = interp.objectify(interp.expr(interp, cond)); if (!test.toBoolean()) break; interp.eval(body); - var ic = interp.code; // tested after step command + var ic = interp.code; // will be tested after step command interp.eval(step); if(ic == interp.BRK) break; if(ic == interp.CNT) continue; } interp.inLoop = false; if(interp.code == interp.BRK || interp.code == interp.CNT) - interp.code = interp.OK; + interp.code = interp.OK; return ""; }); this.registerCommand("foreach", function (interp, args) { this.arity(args, 4); var list = args[2].toList(); @@ -599,19 +586,12 @@ this.arity(args, 2); var name = args[1]; try {interp.getVar(name); return 1;} catch(e) {return 0;} }); this.registerSubCommand("info", "globals", function (interp, args) { - return interp.mkList(interp.callframe[0]); + return interp.infovars(0); }); - /* not in "real" Tcl - this.registerSubCommand("info", "isensemble", function (interp, args) { - this.arity(args, 2); - var name = args[1].toString(); - var cmd = interp.getCommand(name); - return (cmd != null && cmd.ensemble != null)? "1" : "0"; - }); */ this.registerSubCommand("info", "level", function (interp, args) { if(args.length == 1) return interp.level; var delta = args[1]; return interp.levelcall[interp.level - delta]; @@ -627,18 +607,21 @@ }); this.registerSubCommand("info", "script", function (interp, args) { return interp.script; }); this.registerSubCommand("info", "vars", function (interp, args) { - var res = []; - for(var i in interp.callframe[interp.level]) { - try { - if(interp.getVar(i) != null) {res.push(i);} - } catch(e) {}; - } - return res; - }); + return interp.infovars(interp.level); + }); + this.infovars = function(level) { // also used in [info globals] + var res = []; + for(var i in this.callframe[level]) { + try { + if(this.getVar(i) != null) {res.push(i);} + } catch(e) {}; + } + return res; + } this.registerCommand("join", function (interp, args) { this.arity(args, 2, 3); var lst = args[1].toList(); var sep = " "; if(args.length == 3) sep = args[2].toString();