@@ -7,13 +7,14 @@ * Based on Picol by Salvatore Sanfilippo () * (c) Stéphane Arnold 2007 * Richard Suchenwirth 2007, 2013: cleanup, additions * vim: syntax=javascript autoindent softtabwidth=4 */ -_step = 0; // set to 1 for debugging +// 'use strict'; // breaks some tests, like expr 0376, for loop +var _step = 0; // set to 1 for debugging var fs = require('fs'); -puts = console.log; +var puts = console.log; function TclInterp () { this.patchlevel = "0.5.3"; this.callframe = [{}]; this.level = 0; @@ -25,11 +26,11 @@ this.RET = 1; this.BRK = 2; this.CNT = 3; this.getVar = function(name) { var nm = name.toString(); - if (nm.match("^::env[(]")) nm=nm.substr(2); + if (nm.match("^::env[(]")) nm = nm.substr(2); if (nm.match("^env[(]")) { var key = nm.substr(4,nm.length-5); var val = process.env[key]; } else if (nm.match("^::")) { var val = this.callframe[0][nm.substr(2)]; // global @@ -50,37 +51,37 @@ this.setVar("argv0", process.argv[1]); this.setVar("argv", process.argv.slice(2)); this.setVar("errorInfo", ""); this.incrLevel = function() { - this.callframe[++this.level] = {}; - return this.level; + //puts("going down from ("+this.level+"): "+this.levelcall); + this.callframe[++this.level] = {}; + return this.level; } this.decrLevel = function() { - this.callframe[this.level] = null; - this.level--; - if (this.level<0) throw "Exit application"; - this.result = null; + this.callframe[this.level] = null; + this.level--; + if (this.level < 0) throw "Exit application"; + this.result = null; } this.getCommand = function(name) { try { return this.commands[name]; } catch (e) {throw "No such command '"+name+"'";} } this.registerCommand = function(name, func, privdata) { if (func == null) throw "No such function: "+name; this.commands[name] = new TclCommand(func, privdata); - } + } this.registerSubCommand = function(name, subcmd, func, privdata) { if (func == null) throw "No such subcommand: "+ name +" " + subcmd; var path = name.split(" "); var ens; name = path.shift(); var cmd = this.commands[name]; if (cmd == null) { ens = {}; - // ens["subcommands"] = new TclCommand(Tcl.InfoSubcommands, null); this.commands[name] = new TclCommand(Tcl.EnsembleCommand, null, ens); } ens = this.commands[name].ensemble; if (ens == null) throw "Not an ensemble command: '"+name+"'"; // walks deeply into the subcommands tree @@ -89,11 +90,10 @@ cmd = ens[name]; if (cmd == null) { cmd = new TclCommand(Tcl.EnsembleCommand, null, {}); ens[name] = cmd; ens = cmd.ensemble; - // ens["subcommands"] = new TclCommand(Tcl.InfoSubcommands, null); } } ens[subcmd] = new TclCommand(func, privdata); } this.eval = function (code) { @@ -129,10 +129,11 @@ try { text = this.eval2(text); } catch (e) {throw (e + "\nwhile parsing \"" + text + "\"");} } else if (parser.type == (parser.ESC)) { // escape handling missing! + // puts("escape handler called"); } else if (parser.type == (parser.SEP)) { prevtype = parser.type; continue; } text = this.objectify(text); @@ -140,12 +141,22 @@ prevtype = parser.type; if (args.length > 0) { try { result = this.call(args); } catch(e) { - if(e.toString().match("Cannot call method")) - throw 'invalid command name "'+args[0].toString()+'"'; + if(_step) puts("level: "+this.level+" args: "+args+" exception: "+e); + var cmd = this.getCommand(args[0]); + if (cmd == null) { + if(args.length==1 && (args[0].toString().match(/ /))) { + throw e; + } + throw 'invalid command name "'+args[0]+'"'; + } + if (cmd.ensemble != null) { + throw 'wrong # args: should be "'+args[0] + +' subcommand ?argument ...?"'; + } throw e; } if (this.code != this.OK) return this.objectify(result); } args = []; @@ -197,35 +208,65 @@ return Date.parse(args[1]); }); this.registerSubCommand("clock", "seconds", function (interp, args) { return Math.floor((new Date()).valueOf()/1000); }); - + this.registerCommand("concat", function (interp, args) { + this.arity(args, 1, Infinity); + var res = []; + for(var i = 1; i < args.length; i++) { + res = res.concat(args[i].toList()); + } + return res; + }); this.registerSubCommand("dict", "create", function (interp, args) { if(args.length % 2 == 0) throw 'wrong # args: should be "dict create ?key value ...?"'; return new TclObject(args.slice(1)); + }); + this.registerSubCommand("dict", "exists", function (interp, args) { + if(args.length < 2) + throw 'wrong # args: should be "dict exists dictionary ?key ...?"'; + var dict = args[1].toList(); + var key = args[2].toString(); + for (var i=0;i < dict.length;i+=2) { + if(dict[i].toString() == key) return 1; + } + return 0; }); this.registerSubCommand("dict", "get", function (interp, args) { if(args.length < 2) - throw 'wrong # args: should be "dict get ?key ...?"'; + throw 'wrong # args: should be "dict get dictionary ?key ...?"'; var dict = args[1].toList(); var key = args[2].toString(); for (var i=0;i < dict.length;i+=2) { if(dict[i].toString() == key) return dict[i+1]; } throw 'key "'+key+'" not known in dictionary'; + }); + this.registerSubCommand("dict", "keys", function (interp, args) { + if(args.length < 2 || args.length > 3) + throw 'wrong # args: should be "dict keys dictionary ?globPattern?"'; + var dict = args[1].toList(); + var pattern = ".*"; + if(args.length == 3) pattern = "^"+args[2].toString().replace(/\*/g,".*"); + var res = []; + for (var i=0;i < dict.length;i+=2) { + if(dict[i].toString().match(pattern)) res.push(dict[i]); + } + return res; }); this.registerSubCommand("dict", "set", function (interp, args) { this.arity(args, 4); var name = args[1]; - var dict = interp.getVar(name); var key = args[2].toString(); var val = args[3].toString(); + var dict = []; + try {dict = interp.getVar(name);} catch(e) {dict = new TclObject([])}; var found = false; var list = dict.toList(); - for (var i=0;i < list.length;i+=2) { + for (var i = 0; i < list.length; i += 2) { if(list[i].toString() == key) { list[i+1] = val; found = true; break; } @@ -233,10 +274,31 @@ if (!found) { list.push(interp.objectify(key)); list.push(interp.objectify(val)); } interp.setVar(name, dict); + return dict; + }); + this.registerSubCommand("dict", "unset", function (interp, args) { + this.arity(args, 3); + var name = args[1]; + var key = args[2].toString(); + var dict = []; + try {dict = interp.getVar(name);} catch(e) {dict = new TclObject([])}; + var found = false; + var list = dict.toList(); + for (var i = 0; i < list.length; i += 2) { + if(list[i].toString() == key) { + found = true; + break; + } + } + 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) { @@ -278,28 +340,31 @@ this.arity(args, 1,2); var rc = 0; if (args.length == 2) rc = args[1]; process.exit(rc); }); - acos = Math.acos; - exp = Math.exp; - sqrt = Math.sqrt; // "publish" other Math.* functions as needed + 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 + this.expr = function (interp, expression) { // also used in for, if, while try { - var $mx = $expression.match(/(\[.*\])/g); - for ($i in $mx) - puts("have to deal with "+$mx[i].toString()); + var 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 ($i in $mx) - eval("var "+$mx[$i]+" = "+$interp.getVar($mx[$i].slice(1))); - var res = eval($expression); + mx = expression.match(/(\$[A-Za-z0-9_:]+)/g); + for (i in mx) { + var val = interp.getVar(mx[i].slice(1)).toString(); + if(isNaN(val) || !isFinite(val)) val = '"'+val+'"'; + eval("var "+mx[i]+' = '+val); + } + var res = eval(expression); if(res == false) res = 0; else if(res == true) res = 1; return res; }; this.registerSubCommand("file", "dirname", function (interp, args) { this.arity(args, 2); @@ -335,33 +400,57 @@ var list = args[2].toList(); var body = args[3].toString(); var res = ""; interp.inLoop = true; interp.code = interp.OK; - for(i in list) { - interp.setVar(args[1],interp.objectify(list[i])); - interp.eval(body); - if(interp.code == interp.BRK) break; - if(interp.code == interp.CNT) continue; + for(var i in list) { + //puts("now at "+list[i]+" level: "+interp.level); + interp.setVar(args[1],interp.objectify(list[i])); + interp.eval(body); + if(interp.code == interp.BRK) break; + if(interp.code == interp.CNT) continue; } interp.inLoop = false; if(interp.code == interp.BRK || interp.code == interp.CNT) interp.code=interp.OK; return ""; }); - /* this.registerCommand("gets", function (interp, args) { + this.registerCommand("format", function (interp, args) { + this.arity(args, 3); + var fmt = args[1]; + var val = args[2]; + if(fmt=="%x") { + var x = new Number(val); + return x.toString(16); + } else if(fmt=="%X") { + var x = new Number(val); + return x.toString(16).toUpperCase(); + } + else throw "unknown format"; + + }); + /* + this.registerCommand("gets", function (interp, args) { this.arity(args, 2, 3); - var reply; // = prompt(args[1],""); - process.stdin.resume(); - process.stdin.on('data', function(str) { - reply = str; - }); + this.tmp = null; + rl.on('line', itp.gets); + while(true) { + if(this.tmp != null) break; + }; + rl.on('line', this.getsevalputs); + var reply = this.tmp; + // = prompt(args[1],""); + //process.stdin.resume(); + //process.stdin.on('data', function(str) { + //reply = str; + // }); if(args[2] != null) { - interp.setVar(args[2],interp.objectify(reply)); - return reply.length; + interp.setVar(args[2],interp.objectify(reply)); + return reply.length; } else return reply; - }); */ + }); + */ this.registerCommand("if", function (interp, args) { this.arity(args, 3, Infinity); var cond = args[1].toString(); var test = interp.objectify(interp.expr(interp, cond)); if (test.toBoolean()) return interp.eval(args[2].toString()); @@ -405,50 +494,59 @@ }); this.registerSubCommand("info", "commands", function (interp, args) { return interp.mkList(interp.commands); }); this.registerSubCommand("info", "exists", function (interp, args) { - var name = args[1]; - try {interp.getVar(name); return 1;} catch(e) {return 0;} + 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(); - return (interp.getCommand(name).ensemble != null); - }); + 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", "na", function (interp, args) { + this.registerSubCommand("info", "nameofexecutable", function (interp, args) { return process.execPath; }); this.registerSubCommand("info", "patchlevel", function (interp, args) { - return interp.patchlevel.toString(); + 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.mkList(interp.callframe[interp.level]); + var res = []; + for(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 = /^{.*}$/; - for (i in lst) { + for (var i in lst) { var word = lst[i].toString(); if (re.test(word)) word = word.substring(1,word.length-1); res.push(word); } return res.join(sep); @@ -462,10 +560,11 @@ try { var list = interp.getVar(vname); } catch(e) {var list = new TclObject([]);} list.toList(); for (var i = 2; i < args.length; i++) { + if(args[i] == "") args[i] = "{}"; list.content.push(interp.objectify(args[i])); } interp.setVar(vname, list); return list; }); @@ -497,10 +596,14 @@ var start = list.listIndex(args[2]); var end = list.listIndex(args[3])+1; try { return list.content.slice(start, end); } catch (e) {return [];} + }); + this.registerCommand("lreverse", function (interp, args) { + this.arity(args, 2); + return args[1].toList().reverse(); }); this.registerCommand("lset", function (interp, args) { this.arity(args, 4, Infinity); var list = interp.getVar(args[1].toString()); var elt = list; @@ -514,11 +617,11 @@ return list; }); this.registerCommand("lsearch", function (interp, args) { this.arity(args, 3); var lst = args[1].toList(); - for(i in lst) if(lst[i] == args[2].toString()) return i; + for(var i in lst) if(lst[i] == args[2].toString()) return i; return -1; }); this.registerCommand("lsort", function (interp, args) { this.arity(args, 2); return args[1].toList().sort(); @@ -594,16 +697,22 @@ var str = args[1].toString(); var sep = " "; if (args.length == 3) sep = args[2].toString(); var res = [], e; var tmp = str.split(sep); - for(i in tmp) { + for(var i in tmp) { e = tmp[i]; if(e == "") e = "{}"; res.push(e); } return res.join(" "); + }); + this.registerSubCommand("string", "compare", function (interp, args) { + this.arity(args, 3); + var a = args[1].toString(); + var b = args[2].toString(); + return a > b? "1": a < b? "-1": "0"; }); this.registerSubCommand("string", "equal", function (interp, args) { this.arity(args, 3); return (args[1].toString() == args[2].toString())? "1": "0"; }); @@ -652,15 +761,16 @@ for(var i = 0; i < n; i++) interp.eval(body); return (sec_msec()-t0)*1000/n + " microseconds per iteration"; }); this.registerCommand("unset", function (interp, args) { this.arity(args, 2, Infinity); - for (var i = 2; i < args.length; i++) - interp.setVar(args[i], null); + for (var i = 1; i < args.length; i++) + interp.setVar(args[i], null); }); this.registerCommand("uplevel",function (interp, args) { this.arity(args, 3, Infinity); + var mycallframe = interp.callframe[interp.level]; var delta = args[1].toInteger(); interp.level -= delta; if(interp.level < 0) { interp.level += delta; throw 'bad level "'+delta+'"'; @@ -669,10 +779,11 @@ if (args.length == 3) { var code = args[2]; } else var code = args.slice(2).join(" "); var res = interp.eval(code); interp.level += delta; + interp.callframe[interp.level] = mycallframe; return res; }); this.registerCommand("while", function (interp, args) { this.arity(args, 3); var cond = args[1].toString(); @@ -694,11 +805,13 @@ }); // native cmdname {function(interp, args) {...}} this.registerCommand("native", function (interp, args) { this.arity(args, 3); var cmd = args[1].toList(); + puts("before eval "+args[2]); var func = eval(args[2].toString()); + puts("ok so far"); //alert("in: "+args[2].toString()+", func: "+ func); if (cmd.length == 1) { interp.registerCommand(cmd[0].toString(), func); return; } @@ -720,11 +833,11 @@ case "!=": return a != b? "1":"0"; default: throw "Unknown operator: '"+name+"'"; } } var ops = ["+","-","*","/","%","<",">","==","!="]; - for (i in ops) + for (var i in ops) this.registerCommand(ops[i],function (interp, args) { this.arity(args, 3); var name = args[0].toString(); var a = interp.objectify(args[1]); var b = interp.objectify(args[2]); @@ -772,19 +885,20 @@ } return this.objectify(text); } this.call = function(args) { if(_step) puts("this.call "+args); - var func = this.getCommand(args[0].toString()); + var func = this.getCommand(args[0]); + if(func == null) throw 'invalid command name "'+args[0]+'"'; var res = func.call(this,args); switch (this.code) { case this.OK: case this.RET: return res; case this.BRK: - if (!this.inLoop) throw "Invoked break outside of a loop"; + if (!this.inLoop) throw 'invoked "break" outside of a loop'; break; case this.CNT: - if (!this.inLoop) throw "Invoked continue outside of a loop"; + if (!this.inLoop) throw 'invoked "continue" outside of a loop'; break; default: throw "Unknown return code " + this.code; } return res; } @@ -846,33 +960,36 @@ throw "Tcl.Proc exception "+e; } } /** Manage subcommands */ Tcl.EnsembleCommand = function (interp, args) { - var sub = args[1].toString(); - var main = args.shift().toString()+" "+sub; - args[0] = main; - var ens = this.ensemble; - if (ens == null) { - throw "Not an ensemble command: "+main; - } else if( ens[sub] == null) { - var r = []; - for (var i in ens) r.push(i); - r[r.length-1] = "or "+r[r.length-1]; - throw 'unknown or ambiguous subcommand "'+sub+'": must be '+ - r.join(", "); - } - return ens[sub].call(interp, args); -} -/** Get subcommands of the current ensemble command. */ -/* -Tcl.InfoSubcommands = function(interp, args) { - var r = []; - for (var i in this.ensemble) r.push(i); - return interp.objectify(r); -} -*/ + var sub = args[1].toString(); + var main = args.shift().toString()+" "+sub; + args[0] = main; + var ens = this.ensemble; + if (ens == null) { + throw "Not an ensemble command: "+main; + } else if (ens[sub] == null) { + + var matches = 0, lastmatch = ""; + for (var i in ens) { // maybe unambiguous prefix? + if (i.match("^"+sub) != null) { + matches += 1; + lastmatch = i; + } + } + if(matches == 1) { + sub = lastmatch; + } else { + var r = []; + for (i in ens) r.push(i); + r[r.length-1] = "or "+r[r.length-1]; + throw 'unknown or ambiguous subcommand "'+sub+'": must be '+r.join(", "); + } + } + return ens[sub].call(interp, args); +} function TclObject(text) { this.TEXT = 0; this.LIST = 1; this.INTEGER = 2; this.REAL = 3; @@ -920,98 +1037,96 @@ } if (res.length == 1) return res[0]; return res.join(" "); } this.toString = function () { - if (this.type != this.TEXT) { - if (this.type == this.LIST) - this.content = this.getString(this.content); - else this.content = this.content.toString(); - this.type = this.TEXT; - } - return this.content; + if (this.type != this.TEXT) { + if (this.type == this.LIST) + this.content = this.getString(this.content); + else this.content = this.content.toString(); + this.type = this.TEXT; + } + return this.content; } this.toList = function () { - if (this.type != this.LIST) { - if (this.type != this.TEXT) - this.content[0] = this.content; - else { - - var text = this.content; - if (text.charAt(0) == "{" && text.charAt(text.length-1) == "}") - text = text.substring(1, text.length-1); - if (text == "") - return []; - - var parser = new TclParser(text.toString()); - this.content = []; - for(;;) { - parser.parseList(); - this.content.push(new TclObject(parser.getText())); - if (parser.type == parser.EOL || parser.type == parser.ESC) - break; - } - } - - this.type = this.LIST; - } - return this.content; + if (this.type != this.LIST) { + if (this.type != this.TEXT) + this.content[0] = this.content; + else { + var text = this.content; + if (text.charAt(0) == "{" && text.charAt(text.length-1) == "}") + text = text.substring(1, text.length-1); + if (text == "") + return []; + + var parser = new TclParser(text.toString()); + this.content = []; + for(;;) { + parser.parseList(); + this.content.push(new TclObject(parser.getText())); + if (parser.type == parser.EOL || parser.type == parser.ESC) + break; + } + } + this.type = this.LIST; + } + return this.content; } this.toInteger = function () { - if (this.type == this.INTEGER) return this.content; - this.toString(); - if (this.content.match(Tcl.isHex)) - this.content = parseInt(this.content.substring(2), 16); - else if (this.content.match(Tcl.isOctal)) - this.content = parseInt(this.content, 8); - else if (this.content.match(Tcl.isDecimal)) - this.content = parseInt(this.content); - else throw "Not an integer: '"+this.content+"'"; - if (isNaN(this.content)) throw "Not an integer: '"+this.content+"'"; - this.type = this.INTEGER; - return this.content; + if (this.type == this.INTEGER) return this.content; + this.toString(); + if (this.content.match(Tcl.isHex)) + this.content = parseInt(this.content.substring(2), 16); + else if (this.content.match(Tcl.isOctal)) + this.content = parseInt(this.content, 8); + else if (this.content.match(Tcl.isDecimal)) + this.content = parseInt(this.content); + else throw "Not an integer: '"+this.content+"'"; + if (isNaN(this.content)) throw "Not an integer: '"+this.content+"'"; + this.type = this.INTEGER; + return this.content; } this.getFloat = function (text) { - if (!text.toString().match(Tcl.isReal)) + if (!text.toString().match(Tcl.isReal)) throw "Not a real: '"+text+"'"; - return parseFloat(text); + return parseFloat(text); } this.toReal = function () { - if (this.type == this.REAL) - return this.content; - this.toString(); - // parseFloat doesn't control all the string, so need to check it - this.content = this.getFloat(this.content); - if (isNaN(this.content)) throw "Not a real: '"+this.content+"'"; - this.type = this.REAL; - return this.content; + if (this.type == this.REAL) + return this.content; + this.toString(); + // parseFloat doesn't control all the string, so need to check it + this.content = this.getFloat(this.content); + if (isNaN(this.content)) throw "Not a real: '"+this.content+"'"; + this.type = this.REAL; + return this.content; } this.getNumber = function () { - try { - return this.toInteger(); - } catch (e) {return this.toReal();} + try { + return this.toInteger(); + } catch (e) {return this.toReal();} } this.toBoolean = function () { - if (this.type == this.BOOL) return this.content; - try { - this.content = (this.toInteger() != 0); - } catch (e) { - var t = this.content; - if (t instanceof Boolean) return t; - switch (t.toString().toLowerCase()) { - case "yes":case "true":case "on": - this.content = true; - break; - case "false":case "off":case "no": - this.content = false; - break; - default: - throw "Boolean expected, got: '"+this.content+"'"; - } - } - this.type = this.BOOL; - return this.content; + if (this.type == this.BOOL) return this.content; + try { + this.content = (this.toInteger() != 0); + } catch (e) { + var t = this.content; + if (t instanceof Boolean) return t; + switch (t.toString().toLowerCase()) { + case "yes": case "true": case "on": + this.content = true; + break; + case "false": case "off": case "no": + this.content = false; + break; + default: + throw "Boolean expected, got: '"+this.content+"'"; + } + } + this.type = this.BOOL; + return this.content; } } function TclCommand(func, privdata) { if (func == null) throw "No such function"; this.func = func; @@ -1064,11 +1179,11 @@ this.end = this.index-1; this.type = this.ESC; return this.OK; } if (this.cur == "\\") { - if (this.len >= 2) this.feedSequence(); + //if (this.len >= 2) this.feedSequence(); } else if ("$[ \t\n\r;".indexOf(this.cur)>=0) { if ("$[".indexOf(this.cur)>=0 || !this.insidequote) { this.end = this.index-1; this.type = this.ESC; @@ -1085,25 +1200,25 @@ this.feedchar(); } return this.OK; } this.parseList = function () { - level = 0; + var level = 0; this.start = this.index; while (true) { if (this.len == 0) { - this.end = this.index; + this.end = this.index; this.type = this.EOL; return; } switch (this.cur) { case "\\": if (this.len >= 2) this.feedSequence(); break; case " ": case "\t": case "\n": case "\r": if (level > 0) break; - this.end = this.index - 1; + this.end = this.index - 1; this.type = this.SEP; this.feedchar(); return; case '{': level++; break; case '}': level--; break; @@ -1213,12 +1328,14 @@ return this.parseString(); } return this.OK; // unreached } this.feedSequence = function () { + //return; if (this.cur != "\\") throw "Invalid escape sequence"; var cur = this.steal(1); + //puts("enter feedSequence, text: "+this.text+" cur: "+cur); var specials = {}; specials.a = "\a"; specials.b = "\b"; specials.f = "\f"; specials.n = "\n"; @@ -1232,13 +1349,15 @@ throw "Invalid unicode escape sequence: "+hex; cur = String.fromCharCode(parseInt(hex,16)); break; case 'x': var hex = this.steal(2); + puts("enter case x, hex: '"+hex+"'"); if (hex != Tcl.isHexSeq.exec(hex)) throw "Invalid unicode escape sequence: "+hex; cur = String.fromCharCode(parseInt(hex,16)); + //puts("hex: "+hex.toString()+" cur: "+cur); break; case "a": case "b": case "f": case "n": case "r": case "t": case "v": cur = specials[cur]; break; @@ -1256,10 +1375,11 @@ } this.steal = function (n) { var tail = this.text.substring(this.index+1); var word = tail.substr(0, n); this.text = this.text.substring(0, this.index-1) + tail.substring(n); + //puts("tail: "+tail+" word: "+word); return word; } this.feedcharstart = function () { this.feedchar(); this.start = this.index; @@ -1286,15 +1406,21 @@ }); var readline = require('readline'); var rl = readline.createInterface(process.stdin, process.stdout); rl.setPrompt('% '); rl.prompt(); -rl.on('line', function(line) { +itp.getsevalputs = function(line) { try { res = itp.eval(line.trim()); } catch(e) {res = e;} if(res != null && res.toString() != "" && res.toString().length) - console.log(res.toString()); + puts(res.toString()); rl.prompt(); - }).on('close',function() { +}; +itp.gets = function(line) { + puts("received "+line); + this.tmp = line; +} +rl.on('line', itp.getsevalputs +).on('close',function() { process.exit(0); });