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.
98 lines (95 loc) • 3 kB
JavaScript
import { INUMBER, IOP1, IOP2, IOP3, IVAR, IFUNCALL, IEXPR, IMEMBER } from './instruction';
export default function expressionToString(tokens, toJS) {
var nstack = [];
var n1, n2, n3;
var f;
for (var i = 0; i < tokens.length; i++) {
var item = tokens[i];
var type = item.type;
if (type === INUMBER) {
if (typeof item.value === 'number' && item.value < 0) {
nstack.push('(' + item.value + ')');
} else {
nstack.push(escapeValue(item.value));
}
} else if (type === IOP2) {
n2 = nstack.pop();
n1 = nstack.pop();
f = item.value;
if (toJS) {
if (f === '^') {
nstack.push('Math.pow(' + n1 + ', ' + n2 + ')');
} else if (f === 'and') {
nstack.push('(!!' + n1 + ' && !!' + n2 + ')');
} else if (f === 'or') {
nstack.push('(!!' + n1 + ' || !!' + n2 + ')');
} else if (f === '||') {
nstack.push('(String(' + n1 + ') + String(' + n2 + '))');
} else if (f === '==') {
nstack.push('(' + n1 + ' === ' + n2 + ')');
} else if (f === '!=') {
nstack.push('(' + n1 + ' !== ' + n2 + ')');
} else {
nstack.push('(' + n1 + ' ' + f + ' ' + n2 + ')');
}
} else {
nstack.push('(' + n1 + ' ' + f + ' ' + n2 + ')');
}
} else if (type === IOP3) {
n3 = nstack.pop();
n2 = nstack.pop();
n1 = nstack.pop();
f = item.value;
if (f === '?') {
nstack.push('(' + n1 + ' ? ' + n2 + ' : ' + n3 + ')');
} else {
throw new Error('invalid Expression');
}
} else if (type === IVAR) {
nstack.push(item.value);
} else if (type === IOP1) {
n1 = nstack.pop();
f = item.value;
if (f === '-' || f === '+') {
nstack.push('(' + f + n1 + ')');
} else if (toJS) {
if (f === 'not') {
nstack.push('(' + '!' + n1 + ')');
} else if (f === '!') {
nstack.push('fac(' + n1 + ')');
} else {
nstack.push(f + '(' + n1 + ')');
}
} else if (f === '!') {
nstack.push('(' + n1 + '!)');
} else {
nstack.push('(' + f + ' ' + n1 + ')');
}
} else if (type === IFUNCALL) {
var argCount = item.value;
var args = [];
while (argCount-- > 0) {
args.unshift(nstack.pop());
}
f = nstack.pop();
nstack.push(f + '(' + args.join(', ') + ')');
} else if (type === IMEMBER) {
n1 = nstack.pop();
nstack.push(n1 + '.' + item.value);
} else if (type === IEXPR) {
nstack.push('(' + expressionToString(item.value, toJS) + ')');
} else {
throw new Error('invalid Expression');
}
}
if (nstack.length > 1) {
throw new Error('invalid Expression (parity)');
}
return String(nstack[0]);
}
function escapeValue(v) {
if (typeof v === 'string') {
return JSON.stringify(v).replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
}
return v;
}