81 lines
2.5 KiB
JavaScript
81 lines
2.5 KiB
JavaScript
import { evaluateExpr, parseExpr } from "./py_js/py";
|
|
import { BUILTINS } from "./py_js/py_builtin";
|
|
import { evaluate } from "./py_js/py_interpreter";
|
|
|
|
/**
|
|
* @typedef {{[key: string]: any}} Context
|
|
* @typedef {Context | string | undefined} ContextDescription
|
|
*/
|
|
|
|
/**
|
|
* Create an evaluated context from an arbitrary list of context representations.
|
|
* The evaluated context in construction is used along the way to evaluate further parts.
|
|
*
|
|
* @param {ContextDescription[]} contexts
|
|
* @param {Context} [initialEvaluationContext] optional evaluation context to start from.
|
|
* @returns {Context}
|
|
*/
|
|
export function makeContext(contexts, initialEvaluationContext) {
|
|
const evaluationContext = Object.assign({}, initialEvaluationContext);
|
|
const context = {};
|
|
for (let ctx of contexts) {
|
|
if (ctx !== "") {
|
|
ctx = typeof ctx === "string" ? evaluateExpr(ctx, evaluationContext) : ctx;
|
|
Object.assign(context, ctx);
|
|
Object.assign(evaluationContext, context); // is this behavior really wanted ?
|
|
}
|
|
}
|
|
return context;
|
|
}
|
|
|
|
/**
|
|
* Extract a partial list of variable names found in the AST.
|
|
* Note that it is not complete. It is used as an heuristic to avoid
|
|
* evaluating expressions that we know for sure will fail.
|
|
*
|
|
* @param {AST} ast
|
|
* @returns string[]
|
|
*/
|
|
function getPartialNames(ast) {
|
|
if (ast.type === 5) {
|
|
return [ast.value];
|
|
}
|
|
if (ast.type === 6) {
|
|
return getPartialNames(ast.right);
|
|
}
|
|
if (ast.type === 14 || ast.type === 7) {
|
|
return getPartialNames(ast.left).concat(getPartialNames(ast.right));
|
|
}
|
|
if (ast.type === 15) {
|
|
return getPartialNames(ast.obj);
|
|
}
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Allow to evaluate a context with an incomplete evaluation context. The evaluated context only
|
|
* contains keys whose values are static or can be evaluated with the given evaluation context.
|
|
*
|
|
* @param {string} context
|
|
* @param {Object} [evaluationContext={}]
|
|
* @returns {Context}
|
|
*/
|
|
export function evalPartialContext(_context, evaluationContext = {}) {
|
|
const ast = parseExpr(_context);
|
|
const context = {};
|
|
for (const key in ast.value) {
|
|
const value = ast.value[key];
|
|
if (
|
|
getPartialNames(value).some((name) => !(name in evaluationContext || name in BUILTINS))
|
|
) {
|
|
continue;
|
|
}
|
|
try {
|
|
context[key] = evaluate(value, evaluationContext);
|
|
} catch {
|
|
// ignore this key as we can't evaluate its value
|
|
}
|
|
}
|
|
return context;
|
|
}
|