Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
/*
* Copyright (C) 2021 Sienci Labs Inc.
*
* This file is part of gSender.
*
* gSender is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, under version 3 of the License.
*
* gSender is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gSender. If not, see <https://www.gnu.org/licenses/>.
*
* Contact for information regarding this program and its license
* can be sent through gSender@sienci.com or mailed to the main office
* of Sienci Labs Inc. in Waterloo, Ontario, Canada.
*
*/
/* eslint no-bitwise: 0 */
import { generate } from "escodegen";
import { parse } from "esprima";
import logger from "./logger";
const log = logger("evaluate-expression");
const UNRESOLVED = Symbol("unresolved");
const evaluateExpression = (src, vars) => {
if (!vars || typeof vars !== "object") {
vars = { ...vars };
}
const walk = (node) => {
console.log(node);
if (node.type === "Literal") {
return node.value;
}
if (node.type === "UnaryExpression") {
const val = walk(node.argument);
if (node.operator === "+") {
return +val;
}
if (node.operator === "-") {
return -val;
}
if (node.operator === "~") {
return ~val;
}
if (node.operator === "!") {
return !val;
}
return UNRESOLVED;
}
if (node.type === "ArrayExpression") {
const xs = [];
for (let i = 0, l = node.elements.length; i < l; i++) {
const x = walk(node.elements[i]);
if (x === UNRESOLVED) {
return UNRESOLVED;
}
xs.push(x);
}
return xs;
}
if (node.type === "ObjectExpression") {
const obj = {};
for (let i = 0; i < node.properties.length; i++) {
const prop = node.properties[i];
const value = prop.value === null ? prop.value : walk(prop.value);
if (value === UNRESOLVED) {
return UNRESOLVED;
}
obj[prop.key.value || prop.key.name] = value;
}
return obj;
}
if (node.type === "BinaryExpression" || node.type === "LogicalExpression") {
const l = walk(node.left);
if (l === UNRESOLVED) {
return UNRESOLVED;
}
const r = walk(node.right);
if (r === UNRESOLVED) {
return UNRESOLVED;
}
const op = node.operator;
if (op === "==") {
return l == r; // eslint-disable-line eqeqeq
}
if (op === "===") {
return l === r;
}
if (op === "!=") {
return l != r; // eslint-disable-line eqeqeq
}
if (op === "!==") {
return l !== r;
}
if (op === "+") {
return l + r;
}
if (op === "-") {
return l - r;
}
if (op === "*") {
return l * r;
}
if (op === "/") {
return l / r;
}
if (op === "%") {
return l % r;
}
if (op === "<") {
return l < r;
}
if (op === "<=") {
return l <= r;
}
if (op === ">") {
return l > r;
}
if (op === ">=") {
return l >= r;
}
if (op === "|") {
return l | r;
}
if (op === "&") {
return l & r;
}
if (op === "^") {
return l ^ r;
}
if (op === "&&") {
return l && r;
}
if (op === "||") {
return l || r;
}
return UNRESOLVED;
}
if (node.type === "Identifier") {
if (Object.hasOwn(vars, node.name)) {
let value = vars[node.name];
if (!Number.isNaN(Number(value))) {
value = Number(value);
}
return value;
}
return UNRESOLVED;
}
if (node.type === "ThisExpression") {
if (Object.hasOwn(vars, "this")) {
return vars["this"]; // eslint-disable-line dot-notation
}
return UNRESOLVED;
}
if (node.type === "CallExpression") {
const callee = walk(node.callee);
if (callee === UNRESOLVED) {
return UNRESOLVED;
}
if (typeof callee !== "function") {
return UNRESOLVED;
}
let ctx = node.callee.object ? walk(node.callee.object) : UNRESOLVED;
if (ctx === UNRESOLVED) {
ctx = null;
}
const args = [];
for (let i = 0, l = node.arguments.length; i < l; i++) {
const x = walk(node.arguments[i]);
if (x === UNRESOLVED) {
return UNRESOLVED;
}
args.push(x);
}
return callee.apply(ctx, args);
}
if (node.type === "MemberExpression") {
const obj = walk(node.object);
if (obj === UNRESOLVED) {
return UNRESOLVED;
}
if (node.property.type === "Identifier") {
return obj[node.property.name];
}
const prop = walk(node.property);
if (prop === UNRESOLVED) {
return UNRESOLVED;
}
return obj[prop];
}
if (node.type === "ConditionalExpression") {
const val = walk(node.test);
if (val === UNRESOLVED) {
return UNRESOLVED;
}
return val ? walk(node.consequent) : walk(node.alternate);
}
if (node.type === "ExpressionStatement") {
const val = walk(node.expression);
if (val === UNRESOLVED) {
return UNRESOLVED;
}
return val;
}
if (node.type === "ReturnStatement") {
return walk(node.argument);
}
if (node.type === "FunctionExpression") {
const bodies = node.body.body;
// Create a "scope" for our arguments
const oldVars = {};
Object.keys(vars).forEach((element) => {
oldVars[element] = vars[element];
});
for (let i = 0; i < node.params.length; i++) {
const key = node.params[i];
if (key.type !== "Identifier") {
return UNRESOLVED;
}
vars[key.name] = null;
}
for (const i in bodies) {
if (walk(bodies[i]) === UNRESOLVED) {
return UNRESOLVED;
}
}
// restore the vars and scope after walk
vars = oldVars;
const keys = Object.keys(vars);
const vals = keys.map((key) => {
return vars[key];
});
return Function(keys.join(", "), "return " + generate(node)).apply(
null,
vals,
); // eslint-disable-line no-new-func
}
if (node.type === "TemplateLiteral") {
let str = "";
let i = 0;
for (i = 0; i < node.expressions.length; i++) {
str += walk(node.quasis[i]);
str += walk(node.expressions[i]);
}
str += walk(node.quasis[i]);
return str;
}
if (node.type === "TaggedTemplateExpression") {
const tag = walk(node.tag);
const quasi = node.quasi;
const strings = quasi.quasis.map(walk);
const values = quasi.expressions.map(walk);
return tag.apply(null, [strings].concat(values));
}
if (node.type === "TemplateElement") {
return node.value.cooked;
}
return UNRESOLVED;
};
let result = UNRESOLVED;
try {
let ast;
if (typeof src === "string") {
ast = parse(src).body[0].expression;
} else {
ast = src;
}
result = walk(ast);
} catch (e) {
log.error(`src="${src}", vars=${JSON.stringify(vars)}`);
log.error(e);
}
return result === UNRESOLVED ? undefined : result;
};
export default evaluateExpression;
b6d6e761eThis file is pinned to the selected release. Download the original to open it in your design tool.