@nent/core
Version:
107 lines (106 loc) • 3.65 kB
JavaScript
/*!
* NENT 2022
*/
import { removeLineFeeds, requireValue, toBoolean, warnIf, } from '../common';
import { evalExpression } from './evaluate.worker';
import { dataState } from './state';
import { hasToken, resolveTokens } from './tokens';
const operatorRegex = /(in |for |[><+\-=])/gi;
const jsonRegEx = /(\{.*?\})/g;
const alphabet = 'abcdefghijklmnopqrstuvwxyz';
/**
* It returns true if the given string contains an operator
* @param {string} value - The value of the attribute.
* @returns A boolean value.
*/
export function hasExpression(value) {
return value.match(operatorRegex);
}
/**
* It takes a string, finds all the JSON in it, replaces the JSON with a variable, and returns the
* string with the JSON replaced and an object with the JSON and the variable
* @param {string} expression - The expression to convert
* @returns An object with two properties: data and expression.
*/
export function convertFromJson(expression) {
const data = {};
let newExpression = removeLineFeeds(expression).split(`'`).join(`"`);
let index = 0;
let match;
let resultingExpression = newExpression.slice();
while ((match = jsonRegEx.exec(newExpression))) {
const json = match[1];
const value = JSON.parse(json);
let variable = alphabet[index];
data[variable] = value;
index++;
resultingExpression = resultingExpression
.split(json)
.join(variable);
}
return {
data,
expression: resultingExpression,
};
}
/**
* It takes an expression and a context, and returns the result of evaluating the expression
* @param {string} expression - The expression to evaluate.
* @param {ExpressionContext} context - ExpressionContext = {}
* @returns A promise that resolves to a number, boolean, or string.
*/
async function evaluate(expression, context = {}) {
requireValue(expression, 'expression');
if (!hasExpression(expression))
return expression;
try {
context.null = null;
const resolved = convertFromJson(expression);
Object.assign(context, resolved.data);
return await evalExpression(resolved.expression, context);
}
catch (error) {
warnIf(dataState.debug, `An exception was raised evaluating expression '${expression}': ${error}`);
return false;
}
}
/**
* This function first resolves any data-tokens, then passes the response to the
* {evaluate} function.
*
* @export evaluateExpression
* @param {string} expression
* @param {*} [context={}]
* @return {*} {Promise<any>}
*/
export async function evaluateExpression(expression, context = {}) {
requireValue(expression, 'expression');
const detokenizedExpression = await resolveTokens(expression, true);
return evaluate(detokenizedExpression, context);
}
/**
* This function first resolves any data-tokens, then passes the response to the
* {evaluate} function, but uses the value to determine a true/false.
*
* @export
* @param {string} expression
* @param {ExpressionContext} [context={}]
* @return {*} {Promise<boolean>}
*/
export async function evaluatePredicate(expression, context = {}) {
requireValue(expression, 'expression');
let workingExpression = expression.slice();
if (hasToken(workingExpression))
workingExpression = await resolveTokens(workingExpression, true);
if (!workingExpression)
return false;
const negation = workingExpression.startsWith('!');
if (negation) {
workingExpression = workingExpression.slice(1, workingExpression.length);
}
let result = toBoolean(workingExpression);
if (hasExpression(workingExpression)) {
result = await evaluate(workingExpression, context);
}
return negation ? !result : result;
}