Check-in [c9965b6d9d]
Overview
SHA1:c9965b6d9d780fbeb4e06434a4ee742dae0536de
Date: 2013-11-19 20:21:26
User: suchenwi
Comment:now passing all 105 tests
Timelines: family | ancestors | descendants | both | trunk
Downloads: Tarball | ZIP archive
Other Links: files | file ages | folders | manifest
Tags And Properties
Context
2013-11-20
20:58
[3d61c6fe53] added [catch] (user: suchenwi, tags: trunk)
2013-11-19
20:21
[c9965b6d9d] now passing all 105 tests (user: suchenwi, tags: trunk)
2013-11-13
22:44
[2079bac7f3] first (user: suchenwi, tags: trunk)
Changes

Modified tcl053.js from [44d4ee318f] to [7793cfca8e].

     5      5    * (BSD license found at <http://www.tcl.tk/software/tcltk/license.html>)
     6      6    *
     7      7    * Based on Picol by Salvatore Sanfilippo (<http://antirez.com/page/picol>)
     8      8    * (c) St├ęphane Arnold 2007
     9      9    * Richard Suchenwirth 2007, 2013: cleanup, additions
    10     10    * vim: syntax=javascript autoindent softtabwidth=4
    11     11    */
    12         -_step = 0; // set to 1 for debugging
           12  +// 'use strict'; // breaks some tests, like expr 0376, for loop
           13  +var _step = 0; // set to 1 for debugging
    13     14   var fs = require('fs');
    14         -puts = console.log;
           15  +var puts = console.log;
    15     16   
    16     17   function TclInterp () {
    17     18       this.patchlevel = "0.5.3";
    18     19       this.callframe  = [{}];
    19     20       this.level      = 0;
    20     21       this.levelcall  = [];
    21     22       this.commands   = {};
................................................................................
    23     24       this.script     = "";
    24     25       this.OK  = 0;
    25     26       this.RET = 1;
    26     27       this.BRK = 2;
    27     28       this.CNT = 3;
    28     29       this.getVar = function(name) {
    29     30           var nm = name.toString();
    30         -        if (nm.match("^::env[(]")) nm=nm.substr(2);
           31  +        if (nm.match("^::env[(]")) nm = nm.substr(2);
    31     32           if (nm.match("^env[(]")) {
    32     33               var key = nm.substr(4,nm.length-5);
    33     34               var val = process.env[key];
    34     35           } else if (nm.match("^::")) {
    35     36               var val = this.callframe[0][nm.substr(2)]; // global
    36     37           } else {
    37     38               var val = this.callframe[this.level][name];
................................................................................
    48     49       }
    49     50       this.setVar("argc",  process.argv.length-2);
    50     51       this.setVar("argv0", process.argv[1]);
    51     52       this.setVar("argv",  process.argv.slice(2));
    52     53       this.setVar("errorInfo", "");
    53     54   
    54     55       this.incrLevel = function() {
    55         -        this.callframe[++this.level] = {};
    56         -        return this.level;
           56  +      //puts("going down from ("+this.level+"): "+this.levelcall);
           57  +      this.callframe[++this.level] = {};
           58  +      return this.level;
    57     59       }
    58     60       this.decrLevel = function() {
    59         -        this.callframe[this.level] = null;
    60         -        this.level--;
    61         -        if (this.level<0) throw "Exit application";
    62         -        this.result = null;
           61  +      this.callframe[this.level] = null;
           62  +      this.level--;
           63  +      if (this.level < 0) throw "Exit application";
           64  +      this.result = null;
    63     65       }
    64     66       this.getCommand = function(name) {
    65     67           try {
    66     68               return this.commands[name];
    67     69           } catch (e) {throw "No such command '"+name+"'";}
    68     70       }
    69     71       this.registerCommand = function(name, func, privdata) {
    70     72           if (func == null) throw "No such function: "+name;
    71     73           this.commands[name] = new TclCommand(func, privdata);
    72         -    }
           74  +     }
    73     75       this.registerSubCommand = function(name, subcmd, func, privdata) {
    74     76         if (func == null) throw "No such subcommand: "+ name +" " + subcmd;
    75     77         var path = name.split(" ");
    76     78         var ens;
    77     79         name = path.shift();
    78     80         var cmd = this.commands[name];
    79     81         if (cmd == null) {
    80     82   	ens = {};
    81         -	// ens["subcommands"]  = new TclCommand(Tcl.InfoSubcommands, null);
    82     83   	this.commands[name] = new TclCommand(Tcl.EnsembleCommand, null, ens);
    83     84         }
    84     85         ens = this.commands[name].ensemble;
    85     86         if (ens == null) throw "Not an ensemble command: '"+name+"'";
    86     87         // walks deeply into the subcommands tree
    87     88         while (path.length > 0) {
    88     89   	name = path.shift();
    89     90   	cmd  = ens[name];
    90     91   	if (cmd == null) {
    91     92   	  cmd = new TclCommand(Tcl.EnsembleCommand, null, {});
    92     93   	  ens[name] = cmd;
    93     94   	  ens = cmd.ensemble;
    94         -	  // ens["subcommands"] = new TclCommand(Tcl.InfoSubcommands, null);
    95     95   	}
    96     96         }
    97     97         ens[subcmd] = new TclCommand(func, privdata);
    98     98       }
    99     99       this.eval = function (code) {
   100    100         try {
   101    101   	return this.eval2(code);
................................................................................
   127    127   	  text = this.getVar(text);
   128    128   	} else if (parser.type == (parser.CMD)) {
   129    129   	  try {
   130    130   	    text = this.eval2(text);
   131    131   	  } catch (e) {throw (e + "\nwhile parsing \"" + text + "\"");}
   132    132   	} else if (parser.type == (parser.ESC)) {
   133    133   	  // escape handling missing!
          134  +	  // puts("escape handler called");
   134    135   	} else if (parser.type == (parser.SEP)) {
   135    136   	  prevtype = parser.type;
   136    137   	  continue;
   137    138   	}
   138    139   	text = this.objectify(text);
   139    140   	if (parser.type ==parser.EOL || parser.type == parser.EOF) {
   140    141   	  prevtype = parser.type;
   141    142   	  if (args.length > 0) {
   142    143   	    try {
   143    144   	      result = this.call(args);
   144    145   	    } catch(e) {
   145         -	      if(e.toString().match("Cannot call method")) 
   146         -		throw 'invalid command name "'+args[0].toString()+'"';
          146  +	      if(_step) puts("level: "+this.level+" args: "+args+" exception: "+e);
          147  +	      var cmd = this.getCommand(args[0]);
          148  +	      if (cmd == null) {
          149  +		if(args.length==1 && (args[0].toString().match(/ /))) {
          150  +		  throw e;
          151  +		}
          152  +		throw 'invalid command name "'+args[0]+'"';
          153  +	      }
          154  +	      if (cmd.ensemble != null) {
          155  +		throw 'wrong # args: should be "'+args[0]
          156  +		  +' subcommand ?argument ...?"';
          157  +	      }
   147    158   	      throw e;
   148    159   	    }
   149    160   	    if (this.code != this.OK) return this.objectify(result);
   150    161   	  }
   151    162   	  args = [];
   152    163   	  continue;
   153    164   	}
................................................................................
   195    206         });
   196    207       this.registerSubCommand("clock", "scan", function (interp, args) {
   197    208           return Date.parse(args[1]);
   198    209         });
   199    210       this.registerSubCommand("clock", "seconds", function (interp, args) {
   200    211   	return Math.floor((new Date()).valueOf()/1000);
   201    212         });
   202         -    
          213  +    this.registerCommand("concat", function (interp, args) {
          214  +        this.arity(args, 1, Infinity);
          215  +	var res = [];
          216  +	for(var i = 1; i < args.length; i++) {
          217  +	  res = res.concat(args[i].toList());
          218  +	}
          219  +	return res;
          220  +      });
   203    221       this.registerSubCommand("dict", "create", function (interp, args) {
   204    222   	if(args.length % 2 == 0) 
   205    223   	  throw 'wrong # args: should be "dict create ?key value ...?"';
   206    224   	return new TclObject(args.slice(1));
          225  +      });
          226  +    this.registerSubCommand("dict", "exists", function (interp, args) {
          227  +	if(args.length < 2) 
          228  +	  throw 'wrong # args: should be "dict exists dictionary ?key ...?"';
          229  +	var dict = args[1].toList();
          230  +	var key  = args[2].toString();
          231  +	for (var i=0;i < dict.length;i+=2) {
          232  +	  if(dict[i].toString() == key) return 1;
          233  +	}
          234  +	return 0;
   207    235         });
   208    236       this.registerSubCommand("dict", "get", function (interp, args) {
   209    237   	if(args.length < 2) 
   210         -	  throw 'wrong # args: should be "dict get ?key ...?"';
          238  +	  throw 'wrong # args: should be "dict get dictionary ?key ...?"';
   211    239   	var dict = args[1].toList();
   212    240   	var key  = args[2].toString();
   213    241   	for (var i=0;i < dict.length;i+=2) {
   214    242   	  if(dict[i].toString() == key) return dict[i+1];
   215    243   	}
   216    244   	throw 'key "'+key+'" not known in dictionary';
          245  +      });
          246  +    this.registerSubCommand("dict", "keys", function (interp, args) {
          247  +	if(args.length < 2 || args.length > 3) 
          248  +	  throw 'wrong # args: should be "dict keys dictionary ?globPattern?"';
          249  +	var dict    = args[1].toList();
          250  +	var pattern = ".*";
          251  +	if(args.length == 3) pattern = "^"+args[2].toString().replace(/\*/g,".*");
          252  +	var res  = [];
          253  +	for (var i=0;i < dict.length;i+=2) {
          254  +	  if(dict[i].toString().match(pattern)) res.push(dict[i]);
          255  +	}
          256  +	return res;
   217    257         });
   218    258       this.registerSubCommand("dict", "set", function (interp, args) {
   219    259   	this.arity(args, 4);
   220    260   	var name  = args[1];
   221         -	var dict  = interp.getVar(name);
   222    261   	var key   = args[2].toString();
   223    262   	var val   = args[3].toString();
          263  +	var dict  = [];
          264  +	try {dict = interp.getVar(name);} catch(e) {dict = new TclObject([])};
   224    265   	var found = false;
   225    266   	var list  = dict.toList();
   226         -	for (var i=0;i < list.length;i+=2) {
          267  +	for (var i = 0; i < list.length; i += 2) {
   227    268   	  if(list[i].toString() == key) {
   228    269   	    list[i+1] = val;
   229    270   	    found = true;
   230    271   	    break;
   231    272   	  }
   232    273   	}
   233    274   	if (!found) {
   234    275   	  list.push(interp.objectify(key)); 
   235    276   	  list.push(interp.objectify(val));
   236    277   	} 
   237    278   	interp.setVar(name, dict);
          279  +	return dict;
          280  +      });
          281  +    this.registerSubCommand("dict", "unset", function (interp, args) {
          282  +	this.arity(args, 3);
          283  +	var name  = args[1];
          284  +	var key   = args[2].toString();
          285  +	var dict  = [];
          286  +	try {dict = interp.getVar(name);} catch(e) {dict = new TclObject([])};
          287  +	var found = false;
          288  +	var list  = dict.toList();
          289  +	for (var i = 0; i < list.length; i += 2) {
          290  +	  if(list[i].toString() == key) {
          291  +	    found = true;
          292  +	    break;
          293  +	  }
          294  +	}
          295  +	if(found) {
          296  +	  if(i == list.length) i -= 2;
          297  +	    list.splice(i, 2);
          298  +	    interp.setVar(name, dict);
          299  +	}
   238    300   	return dict;
   239    301         });
   240    302       /*
   241    303         if(typeof(jQuery) != 'undefined') {
   242    304         this.registerCommand("dom", function (interp, args) {
   243    305         var selector = args[1].toString();
   244    306         var fn = args[2].toString();
................................................................................
   276    338       */
   277    339       this.registerCommand("exit",function (interp, args) {
   278    340   	this.arity(args, 1,2);
   279    341   	var rc = 0;
   280    342   	if (args.length == 2) rc = args[1];
   281    343   	process.exit(rc);
   282    344         });
   283         -    acos = Math.acos;
   284         -    exp  = Math.exp;
   285         -    sqrt = Math.sqrt; // "publish" other Math.* functions as needed
          345  +    var acos = Math.acos;
          346  +    var exp  = Math.exp;
          347  +    var sqrt = Math.sqrt; // "publish" other Math.* functions as needed
   286    348   
   287    349       this.registerCommand("expr", function (interp, args) {
   288    350   	var expression = args.slice(1).join(" ");
   289    351   	return interp.expr(interp, expression);
   290    352         });
   291         -    this.expr = function ($interp, $expression) { // also used in for, if, while
          353  +    this.expr = function (interp, expression) { // also used in for, if, while
   292    354         try {
   293         -	var $mx = $expression.match(/(\[.*\])/g);
   294         -	for ($i in $mx)
   295         -	  puts("have to deal with "+$mx[i].toString());
          355  +	var mx = expression.match(/(\[.*\])/g);
          356  +	for (var i in mx)
          357  +	  puts("have to deal with "+mx[i].toString());
   296    358         } catch(e) {puts(i+". exception: "+e);}
   297         -      $mx = $expression.match(/(\$[A-Za-z0-9_:]+)/g);
   298         -      for ($i in $mx)
   299         -	eval("var "+$mx[$i]+" = "+$interp.getVar($mx[$i].slice(1)));
   300         -      var res = eval($expression);
          359  +      mx = expression.match(/(\$[A-Za-z0-9_:]+)/g);
          360  +      for (i in mx) {
          361  +	var val = interp.getVar(mx[i].slice(1)).toString();
          362  +	if(isNaN(val) || !isFinite(val)) val = '"'+val+'"';
          363  +	eval("var "+mx[i]+' = '+val);
          364  +      }
          365  +      var res = eval(expression);
   301    366         if(res == false) res = 0; else if(res == true) res = 1;
   302    367         return res;
   303    368       };
   304    369       this.registerSubCommand("file", "dirname", function (interp, args) {
   305    370           this.arity(args, 2);
   306    371   	var path = args[1].toString().split("/");
   307    372   	path.pop();
................................................................................
   333    398       this.registerCommand("foreach", function (interp, args) {
   334    399           this.arity(args, 4);
   335    400           var list = args[2].toList();
   336    401           var body = args[3].toString();
   337    402           var res    = "";
   338    403           interp.inLoop = true;
   339    404           interp.code = interp.OK;
   340         -        for(i in list) {
   341         -             interp.setVar(args[1],interp.objectify(list[i]));
   342         -             interp.eval(body);
   343         -             if(interp.code == interp.BRK) break;
   344         -             if(interp.code == interp.CNT) continue;
          405  +        for(var i in list) {
          406  +	  //puts("now at "+list[i]+" level: "+interp.level);
          407  +	  interp.setVar(args[1],interp.objectify(list[i]));
          408  +	  interp.eval(body);
          409  +	  if(interp.code == interp.BRK) break;
          410  +	  if(interp.code == interp.CNT) continue;
   345    411           }
   346    412           interp.inLoop = false;
   347    413           if(interp.code == interp.BRK || interp.code == interp.CNT)
   348    414               interp.code=interp.OK;
   349    415           return "";
   350    416       });
   351         -    /*    this.registerCommand("gets", function (interp, args) {
          417  +    this.registerCommand("format", function (interp, args) {
          418  +        this.arity(args, 3);
          419  +	var fmt = args[1];
          420  +	var val = args[2];
          421  +	if(fmt=="%x") {
          422  +	  var x = new Number(val);
          423  +	  return x.toString(16);
          424  +	} else if(fmt=="%X") {
          425  +	  var x = new Number(val);
          426  +	  return x.toString(16).toUpperCase();
          427  +	}
          428  +	else throw "unknown format";
          429  +	
          430  +      });
          431  +    /*
          432  +    this.registerCommand("gets", function (interp, args) {
   352    433           this.arity(args, 2, 3);
   353         -        var reply; // = prompt(args[1],"");
   354         -        process.stdin.resume();
   355         -        process.stdin.on('data', function(str) {
   356         -        reply = str;
   357         -            });
          434  +	this.tmp = null;
          435  +	rl.on('line', itp.gets);
          436  +	while(true) {
          437  +	  if(this.tmp != null) break;
          438  +	};
          439  +	rl.on('line', this.getsevalputs);
          440  +        var reply = this.tmp;
          441  +        // = prompt(args[1],"");
          442  +        //process.stdin.resume();
          443  +        //process.stdin.on('data', function(str) {
          444  +        //reply = str;
          445  +        //    });
   358    446           if(args[2] != null) {
   359         -            interp.setVar(args[2],interp.objectify(reply));
   360         -            return reply.length;
          447  +	  interp.setVar(args[2],interp.objectify(reply));
          448  +	  return reply.length;
   361    449           } else return reply;
   362         -        }); */
          450  +      });
          451  +    */
   363    452       this.registerCommand("if", function (interp, args) {
   364    453           this.arity(args, 3, Infinity);
   365    454           var cond = args[1].toString();
   366    455           var test = interp.objectify(interp.expr(interp, cond));
   367    456           if (test.toBoolean()) return interp.eval(args[2].toString());
   368    457           if (args.length == 3) return;
   369    458           for (var i = 3; i < args.length; ) {
................................................................................
   403    492           if (!interp.procs[name]) throw '"'+name+'" isn\'t a procedure';
   404    493           return interp.getCommand(name).privdata[1];
   405    494       });
   406    495       this.registerSubCommand("info", "commands", function (interp, args) {
   407    496           return interp.mkList(interp.commands);
   408    497       });
   409    498       this.registerSubCommand("info", "exists", function (interp, args) {
   410         -            var name = args[1];
   411         -            try {interp.getVar(name); return 1;} catch(e) {return 0;}
          499  +        this.arity(args, 2);
          500  +	var name = args[1];
          501  +	try {interp.getVar(name); return 1;} catch(e) {return 0;}
   412    502       });
   413    503       this.registerSubCommand("info", "globals", function (interp, args) {
   414    504           return interp.mkList(interp.callframe[0]);
   415    505       });
          506  +    /* not in "real" Tcl
   416    507       this.registerSubCommand("info", "isensemble", function (interp, args) {
   417    508           this.arity(args, 2);
   418    509           var name = args[1].toString();
   419         -        return (interp.getCommand(name).ensemble != null);
   420         -    });
          510  +	var cmd  = interp.getCommand(name);
          511  +        return (cmd != null && cmd.ensemble != null)? "1" : "0";
          512  +	}); */
   421    513       this.registerSubCommand("info", "level", function (interp, args) {
   422    514   	if(args.length == 1)
   423    515   	  return interp.level;
   424    516   	var delta = args[1];
   425    517   	return interp.levelcall[interp.level - delta];
   426    518       });
   427         -    this.registerSubCommand("info", "na", function (interp, args) {
          519  +    this.registerSubCommand("info", "nameofexecutable", function (interp, args) {
   428    520               return process.execPath;
   429    521       });
   430    522       this.registerSubCommand("info", "patchlevel", function (interp, args) {
   431         -            return interp.patchlevel.toString();
          523  +	return interp.patchlevel;
   432    524       });
   433    525       this.registerSubCommand("info", "procs", function (interp, args) {
   434    526           return interp.mkList(interp.procs);
   435    527       });
   436    528       this.registerSubCommand("info", "script", function (interp, args) {
   437    529           return interp.script;
   438    530       });
   439    531       this.registerSubCommand("info", "vars", function (interp, args) {
   440         -        return interp.mkList(interp.callframe[interp.level]);
          532  +	var res = [];
          533  +	for(i in interp.callframe[interp.level]) {
          534  +	  try {
          535  +	    if(interp.getVar(i) != null) {res.push(i);}
          536  +	  } catch(e) {};
          537  +	}
          538  +	return res;
   441    539       });
   442    540       this.registerCommand("join", function (interp, args) {
   443    541   	this.arity(args, 2, 3);
   444    542   	var lst = args[1].toList();
   445    543   	var sep = " ";
   446    544   	if(args.length == 3) sep = args[2].toString();
   447    545   	var res = [];
   448    546   	var re  = /^{.*}$/;
   449         -	for (i in lst) {
          547  +	for (var i in lst) {
   450    548   	  var word = lst[i].toString();
   451    549   	  if (re.test(word)) word = word.substring(1,word.length-1);
   452    550   	  res.push(word);
   453    551   	}
   454    552   	return res.join(sep);
   455    553         });
   456    554       this.registerCommand("jseval", function (interp, args) {
................................................................................
   460    558           this.arity(args, 2, Infinity);
   461    559           var vname = args[1].toString();
   462    560   	try {
   463    561   	  var list = interp.getVar(vname);
   464    562   	} catch(e) {var list = new TclObject([]);}
   465    563           list.toList();
   466    564           for (var i = 2; i < args.length; i++) {
          565  +	  if(args[i] == "") args[i] = "{}";
   467    566   	  list.content.push(interp.objectify(args[i]));
   468    567           }
   469    568           interp.setVar(vname, list);
   470    569           return list;
   471    570         });
   472    571       this.registerCommand("lindex", function (interp, args) {
   473    572           this.arity(args, 3, Infinity);
................................................................................
   495    594           this.arity(args, 4);
   496    595           var list    = interp.objectify(args[1]);
   497    596           var start = list.listIndex(args[2]);
   498    597           var end     = list.listIndex(args[3])+1;
   499    598           try {
   500    599   	  return list.content.slice(start, end);
   501    600           } catch (e) {return [];}
          601  +      });
          602  +    this.registerCommand("lreverse", function (interp, args) {
          603  +        this.arity(args, 2);
          604  +        return args[1].toList().reverse();
   502    605         });
   503    606       this.registerCommand("lset", function (interp, args) {
   504    607           this.arity(args, 4, Infinity);
   505    608           var list = interp.getVar(args[1].toString());
   506    609           var elt = list;
   507    610           for (var i = 2; i < args.length-2; i++) {
   508    611   	  elt.toList();
................................................................................
   512    615           i = args.length - 2;
   513    616           elt.content[elt.listIndex(args[i])] = interp.objectify(args[i+1]);
   514    617           return list;
   515    618         });
   516    619       this.registerCommand("lsearch", function (interp, args) {
   517    620           this.arity(args, 3);
   518    621           var lst = args[1].toList();
   519         -        for(i in lst) if(lst[i] == args[2].toString()) return i;
          622  +        for(var i in lst) if(lst[i] == args[2].toString()) return i;
   520    623           return -1;
   521    624         });
   522    625       this.registerCommand("lsort", function (interp, args) {
   523    626           this.arity(args, 2);
   524    627           return args[1].toList().sort();
   525    628         });
   526    629       this.registerCommand("pid", function (interp, args) {
................................................................................
   592    695       this.registerCommand("split", function (interp, args) {
   593    696           this.arity(args, 2, 3);
   594    697           var str = args[1].toString();
   595    698           var sep = " ";
   596    699           if (args.length == 3) sep = args[2].toString();
   597    700   	var res = [], e;
   598    701           var tmp = str.split(sep);
   599         -	for(i in tmp) {
          702  +	for(var i in tmp) {
   600    703   	  e = tmp[i];
   601    704   	  if(e == "") e = "{}";
   602    705   	  res.push(e);
   603    706   	}
   604    707   	return res.join(" ");
          708  +      });
          709  +    this.registerSubCommand("string", "compare", function (interp, args) {
          710  +        this.arity(args, 3);
          711  +	var a = args[1].toString();
          712  +	var b = args[2].toString();
          713  +	return a > b? "1": a < b? "-1": "0";
   605    714         });
   606    715       this.registerSubCommand("string", "equal", function (interp, args) {
   607    716           this.arity(args, 3);
   608    717           return (args[1].toString() == args[2].toString())? "1": "0";
   609    718         });
   610    719       this.registerSubCommand("string", "index", function (interp, args) {
   611    720           this.arity(args, 3);
................................................................................
   650    759           var n    = (args.length == 3)? args[2] : 1;
   651    760           var t0   = sec_msec();
   652    761           for(var i = 0; i < n; i++) interp.eval(body);
   653    762           return (sec_msec()-t0)*1000/n + " microseconds per iteration";
   654    763       });
   655    764       this.registerCommand("unset", function (interp, args) {
   656    765           this.arity(args, 2, Infinity);
   657         -        for (var i = 2; i < args.length; i++)
   658         -            interp.setVar(args[i], null);
          766  +        for (var i = 1; i < args.length; i++)
          767  +	  interp.setVar(args[i], null);
   659    768       });
   660    769       this.registerCommand("uplevel",function (interp, args) {
   661    770           this.arity(args, 3, Infinity);
          771  +	var mycallframe = interp.callframe[interp.level];
   662    772           var delta = args[1].toInteger();
   663    773           interp.level -= delta;
   664    774   	if(interp.level < 0) {
   665    775   	  interp.level += delta;
   666    776   	  throw 'bad level "'+delta+'"';
   667    777   	}
   668    778           for (var i = 2; i < args.length; i++) args[i] = args[i].toString();
   669    779           if (args.length == 3) {
   670    780   	  var code = args[2];
   671    781           } else var code = args.slice(2).join(" ");
   672    782           var res = interp.eval(code);
   673    783           interp.level += delta;
          784  +	interp.callframe[interp.level] = mycallframe;
   674    785           return res;
   675    786         });
   676    787       this.registerCommand("while", function (interp, args) {
   677    788           this.arity(args, 3);
   678    789           var cond = args[1].toString();
   679    790           var body = args[2].toString();
   680    791           var res  = "";
................................................................................
   692    803   	  interp.code=interp.OK;
   693    804           return interp.objectify(res);
   694    805         });
   695    806       // native cmdname {function(interp, args) {...}}
   696    807       this.registerCommand("native", function (interp, args) {
   697    808           this.arity(args, 3);
   698    809           var cmd = args[1].toList();
          810  +	puts("before eval "+args[2]);
   699    811           var func = eval(args[2].toString());
          812  +	puts("ok so far");
   700    813           //alert("in: "+args[2].toString()+", func: "+ func);
   701    814           if (cmd.length == 1) {
   702    815   	  interp.registerCommand(cmd[0].toString(), func);
   703    816   	  return;
   704    817           }
   705    818           base = cmd[0].toString();
   706    819           cmd.shift();
................................................................................
   718    831         case ">":  return a > b? "1":"0";
   719    832         case "==": return a == b? "1":"0";
   720    833         case "!=": return a != b? "1":"0";
   721    834         default:   throw "Unknown operator: '"+name+"'";
   722    835         }
   723    836       }
   724    837       var ops = ["+","-","*","/","%","<",">","==","!="];
   725         -    for (i in ops)
          838  +    for (var i in ops)
   726    839         this.registerCommand(ops[i],function (interp, args) {
   727    840   	  this.arity(args, 3);
   728    841   	  var name = args[0].toString();
   729    842   	  var a    = interp.objectify(args[1]);
   730    843   	  var b    = interp.objectify(args[2]);
   731    844   	  if (name == '==') 
   732    845   	    return new TclObject(a.toString() == b.toString()?"1":"0","BOOL");
................................................................................
   770    883   	text = [text];
   771    884         break;
   772    885         }
   773    886         return this.objectify(text);
   774    887       }
   775    888       this.call = function(args) {
   776    889         if(_step) puts("this.call "+args);
   777         -      var func = this.getCommand(args[0].toString());
          890  +      var func = this.getCommand(args[0]);
          891  +      if(func == null) throw 'invalid command name "'+args[0]+'"';
   778    892         var res  = func.call(this,args);
   779    893         switch (this.code) {
   780    894         case this.OK: case this.RET: return res;
   781    895         case this.BRK:
   782         -	if (!this.inLoop) throw "Invoked break outside of a loop";
          896  +	if (!this.inLoop) throw 'invoked "break" outside of a loop';
   783    897   	break;
   784    898         case this.CNT:
   785         -	if (!this.inLoop) throw "Invoked continue outside of a loop";
          899  +	if (!this.inLoop) throw 'invoked "continue" outside of a loop';
   786    900   	break;
   787    901         default: throw "Unknown return code " + this.code;
   788    902         }
   789    903         return res;
   790    904       }
   791    905   }
   792    906   
................................................................................
   844    958      } catch (e) {
   845    959          interp.decrLevel();
   846    960          throw "Tcl.Proc exception "+e;
   847    961      }
   848    962   }
   849    963   /** Manage subcommands */
   850    964   Tcl.EnsembleCommand = function (interp, args) {
   851         -   var sub    = args[1].toString();
   852         -   var main = args.shift().toString()+" "+sub;
   853         -   args[0] = main;
   854         -   var ens = this.ensemble;
   855         -   if (ens == null) {
   856         -        throw "Not an ensemble command: "+main;
   857         -    } else if( ens[sub] == null) {
   858         -     var r = [];
   859         -     for (var i in ens) r.push(i);
   860         -     r[r.length-1] = "or "+r[r.length-1];
   861         -     throw 'unknown or ambiguous subcommand "'+sub+'": must be '+
   862         -     r.join(", ");
   863         -    }
   864         -    return ens[sub].call(interp, args);
   865         -}
   866         -/** Get subcommands of the current ensemble command. */
   867         -/*
   868         -Tcl.InfoSubcommands = function(interp, args) {
   869         -    var r = [];
   870         -    for (var i in this.ensemble) r.push(i);
   871         -    return interp.objectify(r);
   872         -}
   873         -*/
          965  +  var sub  = args[1].toString();
          966  +  var main = args.shift().toString()+" "+sub;
          967  +  args[0]  = main;
          968  +  var ens  = this.ensemble;
          969  +  if (ens == null) {
          970  +    throw "Not an ensemble command: "+main;
          971  +  } else if (ens[sub] == null) {
          972  +    
          973  +    var matches   = 0, lastmatch = "";
          974  +    for (var i in ens) { // maybe unambiguous prefix?
          975  +      if (i.match("^"+sub) != null) {
          976  +	matches  += 1;
          977  +	lastmatch = i;
          978  +      } 
          979  +    }
          980  +    if(matches == 1) {
          981  +      sub = lastmatch;
          982  +    } else {
          983  +      var r = [];
          984  +      for (i in ens) r.push(i);
          985  +      r[r.length-1] = "or "+r[r.length-1];
          986  +      throw 'unknown or ambiguous subcommand "'+sub+'": must be '+r.join(", ");
          987  +    }
          988  +  }
          989  +  return ens[sub].call(interp, args);
          990  +}
   874    991   function TclObject(text) {
   875    992       this.TEXT    = 0;
   876    993       this.LIST    = 1;
   877    994       this.INTEGER = 2;
   878    995       this.REAL    = 3;
   879    996       this.BOOL    = 4;
   880    997       switch (arguments[0]) {
................................................................................
   918   1035               if (Tcl.isList.test(res[i]) && !Tcl.isNested.test(res[i]))
   919   1036                   res[i] = "{" + res[i] + "}";
   920   1037           }
   921   1038           if (res.length == 1) return res[0];
   922   1039           return res.join(" ");
   923   1040       }
   924   1041       this.toString = function () {
   925         -        if (this.type != this.TEXT) {
   926         -            if (this.type == this.LIST)
   927         -                this.content = this.getString(this.content);
   928         -            else this.content = this.content.toString();
   929         -            this.type = this.TEXT;
   930         -        }
   931         -        return this.content;
         1042  +      if (this.type != this.TEXT) {
         1043  +	if (this.type == this.LIST)
         1044  +	  this.content = this.getString(this.content);
         1045  +	else this.content = this.content.toString();
         1046  +	this.type = this.TEXT;
         1047  +      }
         1048  +      return this.content;
   932   1049       }
   933   1050       this.toList = function () {
   934         -        if (this.type != this.LIST) {
   935         -            if (this.type != this.TEXT)
   936         -                this.content[0] = this.content;
   937         -            else {
   938         -
   939         -                var text = this.content;
   940         -                if (text.charAt(0) == "{" && text.charAt(text.length-1) == "}")
   941         -                    text = text.substring(1, text.length-1);
   942         -                if (text == "")
   943         -                    return [];
   944         -
   945         -                var parser = new TclParser(text.toString());
   946         -                this.content = [];
   947         -                for(;;) {
   948         -                    parser.parseList();
   949         -                    this.content.push(new TclObject(parser.getText()));
   950         -                    if (parser.type == parser.EOL || parser.type == parser.ESC)
   951         -                        break;
   952         -                }
   953         -            }
   954         -
   955         -            this.type = this.LIST;
   956         -        }
   957         -        return this.content;
         1051  +      if (this.type != this.LIST) {
         1052  +	if (this.type != this.TEXT)
         1053  +	  this.content[0] = this.content;
         1054  +	else {
         1055  +	  var text = this.content;
         1056  +	  if (text.charAt(0) == "{" && text.charAt(text.length-1) == "}")
         1057  +	    text = text.substring(1, text.length-1);
         1058  +	  if (text == "")
         1059  +	    return [];
         1060  +	  
         1061  +	  var parser = new TclParser(text.toString());
         1062  +	  this.content = [];
         1063  +	  for(;;) {
         1064  +	    parser.parseList();
         1065  +	    this.content.push(new TclObject(parser.getText()));
         1066  +	    if (parser.type == parser.EOL || parser.type == parser.ESC)
         1067  +	      break;
         1068  +	  }
         1069  +	}	
         1070  +	this.type = this.LIST;
         1071  +      }
         1072  +      return this.content;
   958   1073       }
   959   1074       this.toInteger = function () {
   960         -        if (this.type == this.INTEGER) return this.content;
   961         -        this.toString();
   962         -        if (this.content.match(Tcl.isHex))
   963         -            this.content = parseInt(this.content.substring(2), 16);
   964         -        else if (this.content.match(Tcl.isOctal))
   965         -            this.content = parseInt(this.content, 8);
   966         -        else if (this.content.match(Tcl.isDecimal))
   967         -            this.content = parseInt(this.content);
   968         -        else throw "Not an integer: '"+this.content+"'";
   969         -        if (isNaN(this.content)) throw "Not an integer: '"+this.content+"'";
   970         -        this.type = this.INTEGER;
   971         -        return this.content;
         1075  +      if (this.type == this.INTEGER) return this.content;
         1076  +      this.toString();
         1077  +      if (this.content.match(Tcl.isHex))
         1078  +	this.content = parseInt(this.content.substring(2), 16);
         1079  +      else if (this.content.match(Tcl.isOctal))
         1080  +	this.content = parseInt(this.content, 8);
         1081  +      else if (this.content.match(Tcl.isDecimal))
         1082  +	this.content = parseInt(this.content);
         1083  +      else throw "Not an integer: '"+this.content+"'";
         1084  +      if (isNaN(this.content)) throw "Not an integer: '"+this.content+"'";
         1085  +      this.type = this.INTEGER;
         1086  +      return this.content;
   972   1087       }
   973   1088       this.getFloat = function (text) {
   974         -        if (!text.toString().match(Tcl.isReal))
         1089  +      if (!text.toString().match(Tcl.isReal))
   975   1090           throw "Not a real: '"+text+"'";
   976         -        return parseFloat(text);
         1091  +      return parseFloat(text);
   977   1092       }
   978   1093       this.toReal = function () {
   979         -        if (this.type == this.REAL)
         1094  +      if (this.type == this.REAL)
   980   1095           return this.content;
   981         -        this.toString();
   982         -        // parseFloat doesn't control all the string, so need to check it
   983         -        this.content = this.getFloat(this.content);
   984         -        if (isNaN(this.content)) throw "Not a real: '"+this.content+"'";
   985         -        this.type = this.REAL;
   986         -        return this.content;
         1096  +      this.toString();
         1097  +      // parseFloat doesn't control all the string, so need to check it
         1098  +      this.content = this.getFloat(this.content);
         1099  +      if (isNaN(this.content)) throw "Not a real: '"+this.content+"'";
         1100  +      this.type = this.REAL;
         1101  +      return this.content;
   987   1102       }
   988   1103       this.getNumber = function () {
   989         -        try {
   990         -            return this.toInteger();
   991         -        } catch (e) {return this.toReal();}
         1104  +      try {
         1105  +	return this.toInteger();
         1106  +      } catch (e) {return this.toReal();}
   992   1107       }
   993   1108       this.toBoolean = function () {
   994         -        if (this.type == this.BOOL) return this.content;
   995         -        try {
   996         -            this.content = (this.toInteger() != 0);
   997         -        } catch (e) {
   998         -            var t = this.content;
   999         -            if (t instanceof Boolean) return t;
  1000         -            switch (t.toString().toLowerCase()) {
  1001         -            case "yes":case "true":case "on":
  1002         -                this.content = true;
  1003         -                break;
  1004         -            case "false":case "off":case "no":
  1005         -                this.content = false;
  1006         -                break;
  1007         -            default:
  1008         -                throw "Boolean expected, got: '"+this.content+"'";
  1009         -            }
  1010         -        }
  1011         -        this.type = this.BOOL;
  1012         -        return this.content;
         1109  +      if (this.type == this.BOOL) return this.content;
         1110  +      try {
         1111  +	this.content = (this.toInteger() != 0);
         1112  +      } catch (e) {
         1113  +	var t = this.content;
         1114  +	if (t instanceof Boolean) return t;
         1115  +	switch (t.toString().toLowerCase()) {
         1116  +	case "yes": case "true": case "on":
         1117  +	  this.content = true;
         1118  +	  break;
         1119  +	case "false": case "off": case "no":
         1120  +	  this.content = false;
         1121  +	  break;
         1122  +	default:
         1123  +	  throw "Boolean expected, got: '"+this.content+"'";
         1124  +	}
         1125  +      }
         1126  +      this.type = this.BOOL;
         1127  +      return this.content;
  1013   1128       }
  1014   1129   }
  1015   1130   function TclCommand(func, privdata) {
  1016   1131     if (func == null) throw "No such function";
  1017   1132     this.func     = func;
  1018   1133     this.privdata = privdata;
  1019   1134     this.ensemble = arguments[2];
................................................................................
  1062   1177       while (true) {
  1063   1178         if (this.len == 0) {
  1064   1179   	this.end = this.index-1;
  1065   1180   	this.type = this.ESC;
  1066   1181   	return this.OK;
  1067   1182         }
  1068   1183         if (this.cur == "\\") {
  1069         -	if (this.len >= 2) this.feedSequence();
         1184  +	//if (this.len >= 2) this.feedSequence();
  1070   1185         }
  1071   1186         else if ("$[ \t\n\r;".indexOf(this.cur)>=0) {
  1072   1187   	if ("$[".indexOf(this.cur)>=0 || !this.insidequote) {
  1073   1188   	  this.end = this.index-1;
  1074   1189   	  this.type = this.ESC;
  1075   1190   	  return this.OK;
  1076   1191   	}
................................................................................
  1083   1198   	return this.OK;
  1084   1199         }
  1085   1200         this.feedchar();
  1086   1201       }
  1087   1202       return this.OK;
  1088   1203     }
  1089   1204     this.parseList = function () {
  1090         -    level = 0;
         1205  +    var level = 0;
  1091   1206       this.start = this.index;
  1092   1207       while (true) {
  1093   1208         if (this.len == 0) {
  1094         -	this.end = this.index;
         1209  +	this.end  = this.index;
  1095   1210   	this.type = this.EOL;
  1096   1211   	return;
  1097   1212         }
  1098   1213         switch (this.cur) {
  1099   1214         case "\\":
  1100   1215   	if (this.len >= 2) this.feedSequence();
  1101   1216   	break;
  1102   1217         case " ": case "\t": case "\n": case "\r":
  1103   1218   	if (level > 0) break;
  1104         -	this.end    = this.index - 1;
         1219  +	this.end  = this.index - 1;
  1105   1220   	this.type = this.SEP;
  1106   1221   	this.feedchar();
  1107   1222   	return;
  1108   1223         case '{': level++; break;
  1109   1224         case '}': level--; break;
  1110   1225         }
  1111   1226         this.feedchar();
................................................................................
  1211   1326   	continue;
  1212   1327         }
  1213   1328         return this.parseString();
  1214   1329       }
  1215   1330       return this.OK; // unreached
  1216   1331     }
  1217   1332     this.feedSequence = function () {
         1333  +    //return;
  1218   1334       if (this.cur != "\\") throw "Invalid escape sequence";
  1219   1335       var cur = this.steal(1);
         1336  +    //puts("enter feedSequence, text: "+this.text+" cur: "+cur);
  1220   1337       var specials = {};
  1221   1338       specials.a = "\a";
  1222   1339       specials.b = "\b";
  1223   1340       specials.f = "\f";
  1224   1341       specials.n = "\n";
  1225   1342       specials.r = "\r";
  1226   1343       specials.t = "\t";
................................................................................
  1230   1347         var hex = this.steal(4);
  1231   1348         if (hex != Tcl.isHexSeq.exec(hex))
  1232   1349   	throw "Invalid unicode escape sequence: "+hex;
  1233   1350         cur = String.fromCharCode(parseInt(hex,16));
  1234   1351         break;
  1235   1352       case 'x':
  1236   1353         var hex = this.steal(2);
         1354  +      puts("enter case x, hex: '"+hex+"'");
  1237   1355         if (hex != Tcl.isHexSeq.exec(hex))
  1238   1356   	throw "Invalid unicode escape sequence: "+hex;
  1239   1357         cur = String.fromCharCode(parseInt(hex,16));
         1358  +      //puts("hex: "+hex.toString()+" cur: "+cur);
  1240   1359         break;
  1241   1360       case "a": case "b": case "f": case "n":
  1242   1361       case "r": case "t": case "v":
  1243   1362         cur = specials[cur];
  1244   1363         break;
  1245   1364       default:
  1246   1365         if ("0123456789".indexOf(cur) >= 0) {
................................................................................
  1254   1373       this.text[index] = cur;
  1255   1374       this.feedchar();
  1256   1375     }
  1257   1376     this.steal = function (n) {
  1258   1377       var tail = this.text.substring(this.index+1);
  1259   1378       var word = tail.substr(0, n);
  1260   1379       this.text = this.text.substring(0, this.index-1) + tail.substring(n);
         1380  +    //puts("tail: "+tail+" word: "+word);
  1261   1381       return word;
  1262   1382     }
  1263   1383     this.feedcharstart = function () {
  1264   1384       this.feedchar();
  1265   1385       this.start = this.index;
  1266   1386     }
  1267   1387     this.setPos = function (index) {
................................................................................
  1284   1404   process.argv.slice(2).forEach(function(cmd,index,array) {
  1285   1405          itp.eval(cmd);
  1286   1406        });
  1287   1407   var readline = require('readline');
  1288   1408   var rl = readline.createInterface(process.stdin, process.stdout);
  1289   1409   rl.setPrompt('% ');
  1290   1410   rl.prompt();
  1291         -rl.on('line', function(line) {
         1411  +itp.getsevalputs = function(line) {
  1292   1412       try {
  1293   1413         res = itp.eval(line.trim());
  1294   1414       } catch(e) {res = e;}
  1295   1415       if(res != null && res.toString() != "" && res.toString().length) 
  1296         -      console.log(res.toString());
         1416  +      puts(res.toString());
  1297   1417       rl.prompt();
  1298         -  }).on('close',function() {
         1418  +};
         1419  +itp.gets = function(line) {
         1420  +  puts("received "+line); 
         1421  +  this.tmp = line;
         1422  +}
         1423  +rl.on('line', itp.getsevalputs
         1424  +).on('close',function() {
  1299   1425       process.exit(0);
  1300   1426     });

Modified test_tcljs.tcl from [9b4e7f495f] to [c15ca09a61].

     1      1   # test suite for TclJS
     2      2   # This file is designed so it can also run in a tclsh. Some JavaScript goodies,
     3      3   # like 1/0, sqrt(-1) were excluded from the tests.
     4      4   # [clock format 0] was excluded because the timezone string differed.
     5      5   
            6  +set vars [info vars] ;# for later cleanup
     6      7   set version 0.5.3
     7         -set total  0
     8         -set passed 0
     9         -set fail   0
    10         -puts "------------------------ [info script]"
            8  +set total   0
            9  +set passed  0
           10  +set fail    0
           11  +puts "------------------------ [info script] patchlevel: [info patchlevel]"
    11     12   
    12     13   proc e.g. {cmd -> expected} {
    13     14       incr ::total
    14     15       incr ::fail ;# to also count exceptions
    15         -    set mres [uplevel 1 $cmd]
    16         -    if [!= $mres $expected] {
    17         -	puts "**** $cmd -> $mres, expected $expected"
           16  +    set res [uplevel 1 $cmd]
           17  +    if ![string equal $res $expected] {
           18  +    #if {$res != $expected} {} #should work, but wouldn't
           19  +	puts "**** $cmd -> $res, expected: $expected"
    18     20       } else {incr ::passed; incr ::fail -1}
    19     21   }
    20         -#------------------------------- commands not in real Tcl
    21         -if [info exists auto_path] {
    22         -    proc func {name argl body} {proc $name $argl [list expr $body]}
    23         -    func +   {a b} {$a +  $b}
    24         -    func !=  {a b} {$a != $b}
    25         -    func *   {a b} {$a *  $b}
    26         -    func ==  {a b} {$a == $b}
    27         -    func <   {a b} {$a <  $b}
    28         -    set noTcl 0
    29         -} else {set noTcl 1}
    30     22   
    31         -
    32         -# e.g. {exec echo hello} -> hello
           23  +# e.g. {exec echo hello} -> hello ;# needs blocking exec
    33     24   
    34     25   e.g. {append new hello} -> hello
    35     26   e.g. {set x foo}        -> foo
    36     27   e.g. {append x bar}     -> foobar
    37     28   
           29  +proc sum args {expr [join $args +]}
           30  +e.g. {sum 1 2 3} -> 6
           31  +#native sum2 {function (interp, args) {return eval(args.join("+"));}}
           32  +#e.g. {sum2 2 3 4} -> 9
           33  +
           34  +e.g. {concat {a b} {c d}} -> {a b c d}
           35  +
    38     36   e.g. {set d [dict create a 1 b 2 c 3]} -> {a 1 b 2 c 3}
    39         -e.g. {dict get $d b} -> 2
    40         -e.g. {dict set d b 5} -> {a 1 b 5 c 3}
    41         -e.g. {dict set d x 7} -> {a 1 b 5 c 3 x 7}
           37  +e.g. {dict exists $d c} -> 1
           38  +e.g. {dict exists $d x} -> 0
           39  +e.g. {dict get $d b}    -> 2
           40  +e.g. {dict keys $d}     -> {a b c}
           41  +e.g. {dict set d b 5}   -> {a 1 b 5 c 3}
           42  +e.g. {dict set d x 7}   -> {a 1 b 5 c 3 x 7}
           43  +e.g. {dict unset d b}   -> {a 1 c 3 x 7}
           44  +e.g. {dict unset d x}   -> {a 1 c 3}
           45  +e.g. {dict unset d nix} -> {a 1 c 3}
           46  +e.g. {dict set dx a 1}  -> {a 1} ;# create new dict if not exists
    42     47   
    43     48   e.g. {set home [file dirname [pwd]]; list} -> {}
    44     49   e.g. {string equal [set env(HOME)] $home}   -> 1
    45     50   e.g. {string equal [set ::env(HOME)] $home} -> 1
    46     51   
    47     52   e.g. {expr 6*7}         -> 42
    48     53   e.g. {expr {6 * 7 + 1}} -> 43
    49     54   e.g. {set x 43}         -> 43
    50     55   e.g. {expr {$x-1}}      -> 42
    51     56   e.g. {expr $x-1}        -> 42
    52         -if $noTcl {
           57  +if ![info exists auto_path] { ;#these tests are not for a real tclsh
    53     58       e.g. {clock format 0} -> {Thu Jan 01 1970 01:00:00 GMT+0100 (CET)}
    54     59       e.g. {set i [expr 1/0]} -> Infinity
    55     60       e.g. {expr $i==$i+42}   -> 1
    56     61       e.g. {set n [expr sqrt(-1)]} -> NaN
    57     62       e.g. {expr $n == $n} -> 0
    58     63       e.g. {expr $n==$n}   -> 0
    59     64       e.g. {expr $n!=$n}   -> 1
           65  +    e.g. {info patchlevel} -> $version
           66  +    e.g. {set vars}      -> {argc argv0 argv errorInfo} ;# many more in real Tcl
           67  +
    60     68   }
    61         -e.g. {expr 0xFF}   -> 255
    62         -e.g. {expr 0376}   -> 254
           69  +e.g. {expr 0xFF}               -> 255
           70  +e.g. {set x 0xFF; expr {$x+0}} -> 255
           71  +e.g. {expr 0376}               -> 254
           72  +e.g. {set x 0375; expr $x}     -> 253
           73  +e.g. {expr {$x}}   -> 253
    63     74   e.g. {expr 6 * 7}  -> 42
    64         -
    65     75   e.g. {expr 1 == 2} -> 0
    66     76   e.g. {expr 1 < 2}  -> 1
           77  +e.g. {expr !0}     -> 1
           78  +e.g. {expr !42}    -> 0
           79  +e.g. {set x 3}     -> 3
           80  +e.g. {expr $x+1}   -> 4
           81  +e.g. {expr {$x+1}} -> 4
           82  +e.g. {set x a; set y b; expr {$x == $y}} -> 0
           83  +e.g. {expr {$x != $y}} -> 1
    67     84   
    68     85   set forres ""
    69     86   e.g. {for {set i 0} {$i < 5} {incr i} {append forres $i}; set forres} -> 01234
           87  +e.g. {foreach i {a b c d e} {append foreachres $i}; set foreachres}   -> abcde
           88  +
           89  +e.g. {format %x 255} -> ff
           90  +e.g. {format %X 254} -> FE
    70     91   
    71     92   e.g. {set x 41}  -> 41
    72     93   e.g. {incr x}    -> 42
    73     94   e.g. {incr x 2}  -> 44
    74     95   e.g. {incr x -3} -> 41
    75     96   
    76     97   e.g. {info args e.g.} -> {cmd -> expected}
    77     98   e.g. {unset -nocomplain foo} -> {}
    78     99   e.g. {info exists foo} -> 0
    79    100   e.g. {set foo 42}      -> 42
    80    101   e.g. {info exists foo} -> 1
    81    102   e.g. {info level}      -> 0 ;# e.g. runs the command one level up
    82         -e.g. {info patchlevel} -> $version
          103  +e.g. {proc f x {set y 0; info vars}} -> ""
          104  +e.g. {f 41}            -> {x y}
          105  +set tmp [f 40]; e.g. {lappend tmp z} -> {x y z}
          106  +e.g. {info args f}      -> x
          107  +e.g. {info body f}      -> {set y 0; info vars}
          108  +e.g. {info bod f}       -> {set y 0; info vars}
    83    109   
    84    110   e.g. {join {a b c}}     -> {a b c}
    85    111   e.g. {join {a b c} +}   -> {a+b+c}
    86    112   e.g. {join {a {b c} d}} -> {a b c d}
          113  +e.g. {join {a b c} ""}  -> abc
    87    114   
    88         -e.g. {expr !0}  -> 1
    89         -e.g. {expr !42} -> 0
          115  +e.g. {set x {a b c}}      -> {a b c}
          116  +e.g. {set x [list a b c]} -> {a b c}
          117  +e.g. {lappend x}          -> {a b c}
          118  +e.g. {lappend x {}}       -> {a b c {}}
          119  +e.g. {set x}              -> {a b c {}}
          120  +e.g. {lset x 3 e}         -> {a b c e}
          121  +e.g. {llength $x}         -> 4
          122  +e.g. {lindex $x 2}        -> c
          123  +e.g. {lrange $x 1 2}      -> {b c}
          124  +e.g. {lreverse {a b c}}   -> {c b a}
          125  +e.g. {lsearch $x b}       -> 1
          126  +e.g. {lsearch $x y}       -> -1
          127  +e.g. {lsort {z x y}}      -> {x y z}
    90    128   
    91    129   e.g. {regexp {X[ABC]Y} XAY}    -> 1
    92    130   e.g. {regexp {X[ABC]Y} XDY}    -> 0
    93    131   e.g. {regsub {[A-C]+} uBAAD x} -> uxD 
    94    132   
    95    133   e.g. {split "a b  c d"}     -> {a b {} c d}
    96    134   e.g. {split " a b  c d"}     -> {{} a b {} c d}
    97    135   e.g. {split "a b  c d "}     -> {a b {} c d {}}
    98    136   e.g. {split usr/local/bin /} -> {usr local bin}
          137  +e.g. {split /usr/local/bin /} -> {{} usr local bin}
          138  +e.g. {split abc ""}          -> {a b c}
    99    139   
          140  +e.g. {string compare a b}     -> -1
          141  +e.g. {string compare b a}     -> 1
          142  +e.g. {string compare b b}     -> 0
   100    143   e.g. {string equal foo foo}   -> 1
   101    144   e.g. {string equal foo bar}   -> 0
   102    145   e.g. {string index abcde 2}   -> c
   103    146   e.g. {string length ""}       -> 0
   104    147   e.g. {string length foo}      -> 3
   105    148   e.g. {string range hello 1 3} -> ell
   106         -e.g. {string tolower TCL}     -> tcl
   107         -e.g. {string toupper tcl}     -> TCL
          149  +e.g. {string tolower Tcl}     -> tcl
          150  +e.g. {string toupper Tcl}     -> TCL
   108    151   e.g. {string trim " foo "}    -> foo
   109    152   
   110         -e.g. {set x {a b c}} -> {a b c}
   111         -e.g. {lappend x d}   -> {a b c d}
   112         -e.g. {set x}         -> {a b c d}
   113         -e.g. {lset x 3 e}    -> {a b c e}
   114         -e.g. {llength $x}    -> 4
   115         -e.g. {lindex $x 2}   -> c
   116         -e.g. {lrange $x 1 2} -> {b c}
   117         -e.g. {lsearch $x b}  -> 1
   118         -e.g. {lsearch $x y}  -> -1
   119         -e.g. {lsort {z x y}} -> {x y z}
   120         -
   121         -e.g. {proc f x {set y 0; info vars}} -> ""
   122         -e.g. {f 41} -> {x y} ;# must fix proc call in uplevel issue
   123         -set tmp [f 41]; e.g. {set tmp} -> {x y}
   124         -e.g. {info args f} -> x
   125         -e.g. {info body f} -> {set y 0; info vars}
   126         -#e.g. {f 42} -> {x y} ;# must fix proc call in uplevel issue
          153  +#e.g. {set x a.\x62.c} -> a.b.c ;# severe malfunction, breaks test suite operation :(
   127    154   
   128    155   puts "total $total tests, passed $passed, failed $fail"
          156  +#----------- clean up variables used in tests
          157  +foreach var [info vars] {
          158  +    set pos [lsearch $vars $var] ;# expr can't substitute commands yet
          159  +    set neq [string compare $var vars]
          160  +    if {$var != "vars" && $pos < 0} {unset $var}
          161  +}
          162  +unset vars var pos neq
          163  +puts "vars now: [info vars]"
          164  +puts "[llength [info commands]] commands implemented"