UNPKG

tcl-js

Version:

tcl-js is a tcl intepreter written completely in Typescript. It is meant to replicate the tcl-sh interpreter as closely as possible.

626 lines 32 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; (function (factory) { if (typeof module === "object" && typeof module.exports === "object") { var v = factory(require, exports); if (v !== undefined) module.exports = v; } else if (typeof define === "function" && define.amd) { define(["require", "exports", "./parser", "./scope", "./types", "./tclerror", "./math/parser.js"], factory); } })(function (require, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var parser_1 = require("./parser"); var scope_1 = require("./scope"); var types_1 = require("./types"); var tclerror_1 = require("./tclerror"); var parser_js_1 = require("./math/parser.js"); var Interpreter = (function () { function Interpreter(tcl, input, scope) { this.lastValue = new types_1.TclSimple(''); var parser = new parser_1.Parser(input); this.program = parser.get(); this.scope = scope; this.tcl = tcl; } Interpreter.prototype.run = function () { return __awaiter(this, void 0, void 0, function () { var _i, _a, command, _b, checkLoop; return __generator(this, function (_c) { switch (_c.label) { case 0: _i = 0, _a = this.program.commands; _c.label = 1; case 1: if (!(_i < _a.length)) return [3, 4]; command = _a[_i]; _b = this; return [4, this.processCommand(command)]; case 2: _b.lastValue = _c.sent(); checkLoop = this.scope.getSetting('loop'); if (checkLoop && typeof checkLoop !== 'boolean') { if (checkLoop.break || checkLoop.continue) return [3, 4]; } _c.label = 3; case 3: _i++; return [3, 1]; case 4: return [2, this.lastValue]; } }); }); }; Interpreter.prototype.getScope = function () { return this.scope; }; Interpreter.prototype.getTcl = function () { return this.tcl; }; Interpreter.prototype.reset = function (scope) { if (scope) this.scope = scope; this.lastValue = new types_1.TclSimple(''); }; Interpreter.prototype.processCommand = function (command) { return __awaiter(this, void 0, void 0, function () { var args, i, processed, list, j, item, proc, options, helpers, _i, args_1, arg; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: args = []; i = 0; _a.label = 1; case 1: if (!(i < command.args.length)) return [3, 4]; return [4, this.processArg(command.args[i])]; case 2: processed = _a.sent(); if (command.args[i].expand) { list = processed.getList(); for (j = 0; j < list.getLength(); j++) { item = list.getSubValue(j); args.push(item); } } else { args.push(processed); } _a.label = 3; case 3: i++; return [3, 1]; case 4: proc = this.scope.resolveProc(command.command); if (!proc) throw new tclerror_1.TclError("invalid command name \"" + command.command + "\""); options = proc.options; helpers = { sendHelp: function (helpType) { var message = options.helpMessages[helpType] || 'Error'; if (options.arguments.pattern) message += ": should be \"" + options.arguments.pattern + "\""; throw new tclerror_1.TclError(message + "\n while reading: \"" + command.source + "\"\n at line #" + command.sourceLocation + "\n"); }, solveExpression: function (expression) { return __awaiter(_this, void 0, void 0, function () { var processedExpression, parser, solvedExpression; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4, this.deepProcess(expression)]; case 1: processedExpression = _a.sent(); if (typeof processedExpression !== 'string') { if (processedExpression instanceof types_1.TclSimple) processedExpression = processedExpression.getValue(); else throw new tclerror_1.TclError('expression resolved to unusable value'); } parser = new parser_js_1.Parser(); solvedExpression = parser.parse(processedExpression).evaluate(); if (typeof solvedExpression === 'string') solvedExpression = parseFloat(solvedExpression); if (typeof solvedExpression === 'boolean') solvedExpression = solvedExpression ? 1 : 0; if (typeof solvedExpression !== 'number' || isNaN(solvedExpression)) throw new tclerror_1.TclError('expression resolved to unusable value'); if (solvedExpression === Infinity) throw new tclerror_1.TclError('expression result is infinity'); return [2, solvedExpression]; } }); }); }, }; if (typeof options.arguments.amount === 'number') { if (args.length !== options.arguments.amount && options.arguments.amount !== -1) return [2, helpers.sendHelp('wargs')]; } else { if ((args.length < options.arguments.amount.start && options.arguments.amount.start !== -1) || (args.length > options.arguments.amount.end && options.arguments.amount.end !== -1)) return [2, helpers.sendHelp('wargs')]; } if (options.arguments.textOnly || options.arguments.simpleOnly) { for (_i = 0, args_1 = args; _i < args_1.length; _i++) { arg = args_1[_i]; if (!(arg instanceof types_1.TclSimple)) return [2, helpers.sendHelp('wtype')]; } } if (options.arguments.textOnly === true) { args = args.map(function (arg) { return arg.getValue(); }); } return [2, proc.callback(this, args, command, helpers)]; } }); }); }; Interpreter.prototype.processArg = function (arg) { return __awaiter(this, void 0, void 0, function () { var output; return __generator(this, function (_a) { switch (_a.label) { case 0: output = arg.value; if (!(arg.hasVariable && arg.hasVariable && typeof output === 'string')) return [3, 2]; return [4, this.deepProcess(output)]; case 1: output = _a.sent(); _a.label = 2; case 2: if (!arg.stopBackslash && typeof output === 'string') output = this.processBackSlash(output); if (typeof output === 'string') output = output.replace(/\\\n/g, ' '); return [2, typeof output === 'string' ? new types_1.TclSimple(output) : output]; } }); }); }; Interpreter.prototype.deepProcess = function (input, position) { if (position === void 0) { position = 0; } return __awaiter(this, void 0, void 0, function () { var output, toProcess; return __generator(this, function (_a) { switch (_a.label) { case 0: output = ''; _a.label = 1; case 1: return [4, this.resolveFirst(input, position)]; case 2: if (!(toProcess = _a.sent())) return [3, 3]; while (position < toProcess.startPosition) { output += input.charAt(position); position++; } position = toProcess.endPosition; if (toProcess.raw === input) return [2, toProcess.value]; output += toProcess.value.getValue(); return [3, 1]; case 3: while (position < input.length) { output += input.charAt(position); position++; } return [2, output]; } }); }); }; Interpreter.prototype.resolveFirst = function (input, position) { return __awaiter(this, void 0, void 0, function () { function read() { position += 1; char = input.charAt(position); } var char; return __generator(this, function (_a) { char = input.charAt(position); while (char !== '[' && char !== '$' && position < input.length) { if (char === '\\') read(); read(); } if (char === '[') { return [2, this.resolveFirstSquareBracket(input, position)]; } else if (char === '$') { return [2, this.resolveFirstVariable(input, position)]; } else { return [2, null]; } return [2]; }); }); }; Interpreter.prototype.resolveFirstVariable = function (input, position) { if (position === void 0) { position = 0; } return __awaiter(this, void 0, void 0, function () { function read(appendOnOriginal) { if (appendOnOriginal) currentVar.originalString += char; position += 1; char = input.charAt(position); } var char, currentVar, inBracket, startPosition, replaceVar, replaceVar, index, solved; return __generator(this, function (_a) { switch (_a.label) { case 0: char = input.charAt(position); currentVar = { name: '', bracket: '', originalString: '', curly: false, }; inBracket = false; while (char !== '$' && position < input.length) { if (char === '\\') read(false); read(false); } if (char !== '$') return [2, null]; char = char; startPosition = position; read(true); if (char === '{') { currentVar.curly = true; read(true); } _a.label = 1; case 1: if (!(position < input.length)) return [3, 8]; if (!inBracket) return [3, 6]; if (char === ')') { inBracket = false; read(true); return [3, 8]; } if (!(char === '$')) return [3, 3]; return [4, this.resolveFirstVariable(input, position)]; case 2: replaceVar = _a.sent(); if (replaceVar) { while (position < replaceVar.endPosition) { read(true); } currentVar.bracket += replaceVar.value.getValue(); return [3, 1]; } _a.label = 3; case 3: if (!(char === '[')) return [3, 5]; return [4, this.resolveFirstSquareBracket(input, position)]; case 4: replaceVar = _a.sent(); if (replaceVar) { while (position < replaceVar.endPosition) { read(true); } currentVar.bracket += replaceVar.value.getValue(); return [3, 1]; } _a.label = 5; case 5: return [3, 7]; case 6: if (char === '(') { inBracket = true; read(true); return [3, 1]; } if (!currentVar.curly && !char.match(/\w/g)) return [3, 8]; _a.label = 7; case 7: if (currentVar.curly && char === '}') return [3, 8]; if (char === '\\') { if (currentVar.curly) { if (inBracket) currentVar.bracket += char; else currentVar.name += char; } read(true); } if (inBracket) currentVar.bracket += char; else currentVar.name += char; read(true); return [3, 1]; case 8: if (currentVar.curly) { if (char !== '}') throw new tclerror_1.TclError('unexpected end of string'); read(true); } if (currentVar.name === '') return [2, this.resolveFirstVariable(input, position)]; solved = currentVar.bracket; if (solved === '') index = null; else if (isNumber(solved)) index = parseInt(solved, 10); else index = solved; return [2, { raw: currentVar.originalString, startPosition: startPosition, endPosition: position, value: this.getVariable(currentVar.name, index), }]; } }); }); }; Interpreter.prototype.getVariable = function (variableName, variableKey) { var name = variableName; var objectKey = typeof variableKey === 'string' ? variableKey : null; var arrayIndex = typeof variableKey === 'number' ? variableKey : null; var value = this.scope.resolve(name); if (!value) throw new tclerror_1.TclError("can't read \"" + name + "\": no such variable"); if (objectKey !== null) { if (!(value instanceof types_1.TclObject)) throw new tclerror_1.TclError("can't read \"" + name + "\": variable isn't object"); return value.getSubValue(objectKey); } else if (arrayIndex !== null) { if (!(value instanceof types_1.TclArray)) throw new tclerror_1.TclError("can't read \"" + name + "\": variable isn't array"); return value.getSubValue(arrayIndex); } else { return value; } }; Interpreter.prototype.setVariable = function (variableName, variableKey, variable) { var name = variableName; var objectKey = typeof variableKey === 'string' ? variableKey : null; var arrayIndex = typeof variableKey === 'number' ? variableKey : null; var output = variable; var existingValue = this.scope.resolve(name); if (objectKey !== null) { if (existingValue) { if (!(existingValue instanceof types_1.TclObject)) throw new tclerror_1.TclError("cant' set \"" + variableName + "\": variable isn't object"); existingValue.set(objectKey, variable); return; } var obj = new types_1.TclObject(undefined, name); obj.set(objectKey, variable); output = obj; } else if (arrayIndex !== null) { if (existingValue) { if (!(existingValue instanceof types_1.TclArray)) throw new tclerror_1.TclError("cant' set \"" + variableName + "\": variable isn't array"); existingValue.set(arrayIndex, variable); return; } var arr = new types_1.TclArray(undefined, name); arr.set(arrayIndex, variable); output = arr; } else { if (existingValue instanceof types_1.TclObject) throw new tclerror_1.TclError("cant' set \"" + variableName + "\": variable is object"); if (existingValue instanceof types_1.TclArray) throw new tclerror_1.TclError("cant' set \"" + variableName + "\": variable is array"); if (existingValue instanceof types_1.TclList) throw new tclerror_1.TclError("cant' set \"" + variableName + "\": variable is list"); } output.setName(name); this.scope.define(name, output); return; }; Interpreter.prototype.deleteVariable = function (variableName, variableKey, noComplain) { var name = variableName; var objectKey = typeof variableKey === 'string' ? variableKey : null; var arrayIndex = typeof variableKey === 'number' ? variableKey : null; var existingValue = this.scope.resolve(name); if (existingValue === null && !noComplain) throw new tclerror_1.TclError("can't unset \"" + variableName + "\": no such variable"); if (objectKey !== null) { if (!(existingValue instanceof types_1.TclObject)) throw new tclerror_1.TclError("cant' unset \"" + variableName + "\": variable isn't object"); existingValue.set(objectKey, undefined); return; } else if (arrayIndex !== null) { if (!(existingValue instanceof types_1.TclArray)) throw new tclerror_1.TclError("cant' unset \"" + variableName + "\": variable isn't array"); existingValue.set(arrayIndex, undefined); return; } this.scope.undefine(name); return; }; Interpreter.prototype.resolveFirstSquareBracket = function (input, position) { return __awaiter(this, void 0, void 0, function () { function read(appendOnOriginal) { if (appendOnOriginal) outbuf.originalString += char; position += 1; char = input.charAt(position); } var outbuf, char, depth, interpreter, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: outbuf = { originalString: '', startPosition: 0, endPosition: 0, expression: '', value: new types_1.TclVariable(''), }; char = input.charAt(position); depth = 0; while (char !== '[' && position < input.length) { if (char === '\\') read(false); read(false); } if (char !== '[') return [2, null]; char = char; outbuf.startPosition = position; read(true); depth = 1; while (position < input.length) { if (char === '[') { depth++; } if (char === ']') { depth--; if (depth === 0) break; } if (char === '\\') { outbuf.expression += char; read(true); } outbuf.expression += char; read(true); } if (depth !== 0) throw new tclerror_1.TclError('incorrect amount of square brackets'); read(true); interpreter = new Interpreter(this.tcl, outbuf.expression, new scope_1.Scope(this.scope)); _a = outbuf; return [4, interpreter.run()]; case 1: _a.value = _b.sent(); return [2, { raw: outbuf.originalString, startPosition: outbuf.startPosition, endPosition: position, value: outbuf.value, }]; } }); }); }; Interpreter.prototype.processBackSlash = function (input) { var simpleBackRegex = /\\(?<letter>[abfnrtv])/g; var octalBackRegex = /\\0(?<octal>[0-7]{0,2})/g; var unicodeBackRegex = /\\u(?<hexcode>[0-9a-fA-F]{1,4})/g; var hexBackRegex = /\\x(?<hexcode>[0-9a-fA-F]{1,2})/g; var cleanUpBackRegex = /\\(?<character>.)/g; function codeToChar(hexCode) { return String.fromCharCode(hexCode); } input = input.replace(simpleBackRegex, function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var groups = args[args.length - 1]; switch (groups.letter) { case 'a': return codeToChar(0x07); case 'b': return codeToChar(0x08); case 'f': return codeToChar(0x0c); case 'n': return codeToChar(0x0a); case 'r': return codeToChar(0x0d); case 't': return codeToChar(0x09); case 'v': return codeToChar(0x0b); default: throw new tclerror_1.TclError('program hit unreachable point'); } }); input = input.replace(octalBackRegex, function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var groups = args[args.length - 1]; var octal = parseInt(groups.octal, 8); return codeToChar(octal); }); input = input.replace(unicodeBackRegex, function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var groups = args[args.length - 1]; var hex = parseInt(groups.hexcode.toLowerCase(), 16); return codeToChar(hex); }); input = input.replace(hexBackRegex, function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var groups = args[args.length - 1]; var hex = parseInt(groups.hexcode.toLowerCase(), 16); return codeToChar(hex); }); input = input.replace(cleanUpBackRegex, function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var groups = args[args.length - 1]; return groups.character; }); return input; }; return Interpreter; }()); exports.Interpreter = Interpreter; function isNumber(input) { return !isNaN(input) && !isNaN(parseInt(input, 10)); } exports.isNumber = isNumber; }); //# sourceMappingURL=interpreter.js.map