Check-in [b0688623ce]
Overview
Comment:factored out infovars, added README.txt
Timelines: family | ancestors | trunk
Files: files | file ages | folders
SHA1: b0688623ce37f7ae2de31779c707f209916fd588
User & Date: suchenwi on 2013-11-24 10:21:03
Other Links: manifest | tags
Context
2013-11-24
10:21
factored out infovars, added README.txt Leaf check-in: b0688623ce user: suchenwi tags: trunk
08:24
added [file join], [file split] check-in: 07c2ea91bc user: suchenwi tags: trunk
Changes

Added README.txt version [a3de5c087d].



































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
tcl.js "A Tcl implementation in Javascript"

 * Released under the same terms as Tcl itself.
 * (BSD license found at <http://www.tcl.tk/software/tcltk/license.html>)
 *
 * Based on Picol by Salvatore Sanfilippo (<http://antirez.com/page/picol>)
 * (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...

Modified tcl053.js from [dc45822eaf] to [d5a4940aef].

317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
	if(found) {
	  if(i == list.length) i -= 2;
	    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(" ");
        return interp.eval(code);
      });







<
<
<
<
<
<
<
<
<
<
<
<







317
318
319
320
321
322
323












324
325
326
327
328
329
330
	if(found) {
	  if(i == list.length) i -= 2;
	    list.splice(i, 2);
	    interp.setVar(name, dict);
	}
	return dict;
      });












    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(" ");
        return interp.eval(code);
      });
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376



377
378
379
380
381
382
383
    */
    this.registerCommand("exit",function (interp, args) {
	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;



      try {
	mx = expression.match(/(\[.*\])/g);
	for (var i in mx)
	  puts("have to deal with "+mx[i].toString());
      } catch(e) {puts(i+". exception: "+e);}
      mx = expression.match(/(\$[A-Za-z0-9_:]+)/g);
      for (var i in mx) {







<
<
<
<






>
>
>







348
349
350
351
352
353
354




355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
    */
    this.registerCommand("exit",function (interp, args) {
	this.arity(args, 1,2);
	var rc = 0;
	if (args.length == 2) rc = args[1];
	process.exit(rc);
      });




    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);}
      mx = expression.match(/(\$[A-Za-z0-9_:]+)/g);
      for (var i in mx) {
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
        var body = args[4].toString();
        interp.inLoop = true;
        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
            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;
        return "";
    });
    this.registerCommand("foreach", function (interp, args) {
        this.arity(args, 4);
        var list = args[2].toList();
        var body = args[3].toString();
        var res    = "";







|






|







444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
        var body = args[4].toString();
        interp.inLoop = true;
        interp.code = interp.OK;
        while (true) {
            test = interp.objectify(interp.expr(interp, cond));
            if (!test.toBoolean()) break;
            interp.eval(body);
            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;
        return "";
    });
    this.registerCommand("foreach", function (interp, args) {
        this.arity(args, 4);
        var list = args[2].toList();
        var body = args[3].toString();
        var res    = "";
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631



632
633
634
635
636
637
638
639

640
641
642
643
644
645
646
    });
    this.registerSubCommand("info", "exists", function (interp, args) {
        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]);
    });
    /* 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];
    });
    this.registerSubCommand("info", "nameofexecutable", function (interp, args) {
            return process.execPath;
    });
    this.registerSubCommand("info", "patchlevel", function (interp, args) {
	return interp.patchlevel;
    });
    this.registerSubCommand("info", "procs", function (interp, args) {
        return interp.mkList(interp.procs);
    });
    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;
    });

    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();
	var res = [];
	var re  = /^{.*}$/;







|

<
<
<
<
<
<
<



















>
>
>
|
|
|
|
|
|
|
<
>







584
585
586
587
588
589
590
591
592







593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621

622
623
624
625
626
627
628
629
    });
    this.registerSubCommand("info", "exists", function (interp, args) {
        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.infovars(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];
    });
    this.registerSubCommand("info", "nameofexecutable", function (interp, args) {
            return process.execPath;
    });
    this.registerSubCommand("info", "patchlevel", function (interp, args) {
	return interp.patchlevel;
    });
    this.registerSubCommand("info", "procs", function (interp, args) {
        return interp.mkList(interp.procs);
    });
    this.registerSubCommand("info", "script", function (interp, args) {
        return interp.script;
    });
    this.registerSubCommand("info", "vars", function (interp, args) {
	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();
	var res = [];
	var re  = /^{.*}$/;