silphius/app/swcx/evaluators/assignment.js
2024-06-17 05:20:01 -05:00

85 lines
3.3 KiB
JavaScript

import {
isComputed,
isMemberExpression,
} from '@/swcx/types.js';
export default function(node, {evaluate, scope}) {
const {operator, left} = node;
const right = evaluate(node.right, {scope});
if (!isMemberExpression(left)) {
const assign = (value) => {
switch (operator) {
case '=' : return scope.set(left.value, value);
case '+=' : return scope.set(left.value, scope.get(left.value) + value);
case '-=' : return scope.set(left.value, scope.get(left.value) - value);
case '*=' : return scope.set(left.value, scope.get(left.value) * value);
case '/=' : return scope.set(left.value, scope.get(left.value) / value);
case '%=' : return scope.set(left.value, scope.get(left.value) % value);
case '**=' : return scope.set(left.value, scope.get(left.value) ** value);
case '<<=' : return scope.set(left.value, scope.get(left.value) << value);
case '>>=' : return scope.set(left.value, scope.get(left.value) >> value);
case '>>>=': return scope.set(left.value, scope.get(left.value) >>> value);
case '|=' : return scope.set(left.value, scope.get(left.value) | value);
case '^=' : return scope.set(left.value, scope.get(left.value) ^ value);
case '&=' : return scope.set(left.value, scope.get(left.value) & value);
case '||=' : return scope.set(left.value, scope.get(left.value) || value);
case '&&=' : return scope.set(left.value, scope.get(left.value) && value);
case '??=' : {
const l = scope.get(left.value);
return scope.set(left.value, (l === null || l === undefined) ? value : l);
}
/* v8 ignore next 2 */
default:
throw new Error(`operator not implemented: ${node.operator}`);
}
};
if (right.async) {
return {
async: true,
value: Promise.resolve(right.value).then(assign),
};
}
return {value: assign(right.value)};
}
const {
object,
property,
} = left;
const memberAssign = (O, P, value) => {
switch (operator) {
case '=' : return O[P] = value;
case '+=' : return O[P] += value;
case '-=' : return O[P] -= value;
case '*=' : return O[P] *= value;
case '/=' : return O[P] /= value;
case '%=' : return O[P] %= value;
case '**=' : return O[P] **= value;
case '<<=' : return O[P] <<= value;
case '>>=' : return O[P] >>= value;
case '>>>=': return O[P] >>>= value;
case '|=' : return O[P] |= value;
case '^=' : return O[P] ^= value;
case '&=' : return O[P] &= value;
case '||=' : return O[P] ||= value;
case '&&=' : return O[P] &&= value;
case '??=' : return O[P] = (O[P] === null || O[P] === undefined) ? value : O[P];
/* v8 ignore next 2 */
default:
throw new Error(`operator not implemented: ${node.operator}`);
}
};
const makeAsync = (O, P, value) => (
Promise.all([O, P, value]).then(([O, P, value]) => memberAssign(O, P, value))
);
const O = evaluate(object, {scope});
const P = isComputed(property) ? evaluate(property, {scope}) : {value: property.value};
if (right.async || O.async || P.async) {
return {
async: true,
value: makeAsync(O.value, P.value, right.value),
};
}
return {value: memberAssign(O.value, P.value, right.value)};
}