silphius/app/astride/evaluators/assignment.js
2024-06-22 10:47:17 -05:00

88 lines
3.3 KiB
JavaScript

import {
isMemberExpression,
} from '@/astride/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.name, value);
case '+=' : return scope.set(left.name, scope.get(left.name) + value);
case '-=' : return scope.set(left.name, scope.get(left.name) - value);
case '*=' : return scope.set(left.name, scope.get(left.name) * value);
case '/=' : return scope.set(left.name, scope.get(left.name) / value);
case '%=' : return scope.set(left.name, scope.get(left.name) % value);
case '**=' : return scope.set(left.name, scope.get(left.name) ** value);
case '<<=' : return scope.set(left.name, scope.get(left.name) << value);
case '>>=' : return scope.set(left.name, scope.get(left.name) >> value);
case '>>>=': return scope.set(left.name, scope.get(left.name) >>> value);
case '|=' : return scope.set(left.name, scope.get(left.name) | value);
case '^=' : return scope.set(left.name, scope.get(left.name) ^ value);
case '&=' : return scope.set(left.name, scope.get(left.name) & value);
case '||=' : return scope.set(left.name, scope.get(left.name) || value);
case '&&=' : return scope.set(left.name, scope.get(left.name) && value);
case '??=' : {
const l = scope.get(left.name);
return scope.set(left.name, (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 {
computed,
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 = computed
? evaluate(property, {scope})
// Otherwise, identifier
: {value: property.name};
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)};
}