Loading files
6bc89d73dThis 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.
*
*/
import { parse } from "esprima";
import _set from "lodash/set";
import evaluateExpression from "./evaluate-expression";
import logger from "./logger";
const log = logger("evaluate-assignment-expression");
const isStaticMemberExpression = (node) =>
typeof node === "object" &&
node.type === "MemberExpression" &&
!node.computed;
const isComputedMemberExpression = (node) =>
typeof node === "object" &&
node.type === "MemberExpression" &&
!!node.computed;
const lookupObjectPath = (node, vars) => {
if (!node) {
return [];
}
/*
* Expression: 'x = value'
*
* Identifier { type: 'Identifier', name: 'x' }
*/
if (node.type === "Identifier") {
return [node.name];
}
if (isComputedMemberExpression(node)) {
return [
...lookupObjectPath(node.object, vars),
evaluateExpression(node.property, vars),
];
}
if (isStaticMemberExpression(node)) {
/*
* Expression: 'x.y = value'
*
* StaticMemberExpression {
* type: 'MemberExpression',
* computed: false,
* object: Identifier { type: 'Identifier', name: 'x' },
* property: Identifier { type: 'Identifier', name: 'y' }
* }
*
* Expression: 'x[y] = value'
*
* ComputedMemberExpression {
* type: 'MemberExpression',
* computed: true,
* object: Identifier { type: 'Identifier', name: 'x' },
* property: Identifier { type: 'Identifier', name: 'y' }
* }
*/
if (node.property.type === "Identifier") {
return [...lookupObjectPath(node.object, vars), node.property.name];
}
/*
* Expression: 'x["y"] = value'
*
* ComputedMemberExpression {
* type: 'MemberExpression',
* computed: true,
* object: Identifier { type: 'Identifier', name: 'x' },
* property: Literal { type: 'Literal', value: 'y', raw: '"y"' }
* }
*/
if (node.property.type === "Literal") {
return [...lookupObjectPath(node.object, vars), node.property.value];
}
return [
...lookupObjectPath(node.object, vars),
evaluateExpression(node.property, vars),
];
}
return [node.name];
};
const walkAssignmentExpression = (node, vars) => {
console.assert(node && node.type === "AssignmentExpression");
const path = lookupObjectPath(node.left, vars);
if (path) {
const value = evaluateExpression(node.right, vars);
_set(vars, path, value);
}
};
const walkSequenceExpression = (node, vars) => {
console.assert(node && node.type === "SequenceExpression");
node.expressions.forEach((expr) => {
if (expr.type === "AssignmentExpression") {
walkAssignmentExpression(expr, vars);
return;
}
evaluateExpression(expr, vars);
});
};
const evaluateAssignmentExpression = (src, vars = {}) => {
if (!src) {
return vars;
}
try {
const ast = parse(src).body[0].expression;
if (ast.type === "AssignmentExpression") {
walkAssignmentExpression(ast, vars);
} else if (ast.type === "SequenceExpression") {
walkSequenceExpression(ast, vars);
} else {
evaluateExpression(ast, vars);
}
} catch (e) {
log.error(`src="${src}", vars=${JSON.stringify(vars)}`);
log.error(e);
}
return vars;
};
export default evaluateAssignmentExpression;