Skip to content

Instantly share code, notes, and snippets.

@Vannevelj
Created November 17, 2015 17:13
Show Gist options
  • Save Vannevelj/95aa28887cafff10367b to your computer and use it in GitHub Desktop.
Save Vannevelj/95aa28887cafff10367b to your computer and use it in GitHub Desktop.
'use strict';
const logger = require('../../services/logger.js');
const util = require('util');
const math = require('mathjs');
/**
* Evaluates the given expression
* @param expression {string} - The expression to be evaluated. Should support an expression of the form 'log(10) + abs([VALUE])'
* for a certain set of mathematical functions and placeholders.
* @param valueEntry {object} - The entry of the value in the BF_VALUES table contain metadata to use for placeholders.
*/
function evaluateExpression(expression, valueEntry) {
// In case our parsing of the mathematical interpretation fails, we just want to return the original expression
const initialExpression = expression;
if (util.isString(expression)) {
// We allow the usage of placeholders to indicate certain values.
// For example [VALUE] refers to the value currently being inserted while [NAME] refers to the name of the entity providing the value
// All these placeholders are documented at https://phabricator.bigfinite.com/T5
// This regex matches [BeID.Attribute] and [Attribute], stores the (optional) BeID and the Attribute in a capturing group and passes it to the replacePlaceholders function.
expression = expression.replace(/\[([^\.]*?)\.?([^\.\]]*)\]/g,
/**
* This function is used to delegate the replacing of placeholders to specific functions
* The first parameter will always be ignored since it just represents the matched substring.
* This should change [123abc-myBeID.VALUE] to the latest value of the entity with BeID '123abc-myBeID'.
* @param ignore {string} - Never use this
* @param beID {string} - The optional beID that should be used for the lookup
* @param attribute {string} - The actual attribute that is being used
*/
function replacePlaceholders(ignore, beID, attribute) {
switch (attribute) {
case 'VALUE' && !beID:
return getCurrentValue(valueEntry);
default:
logger.error(`An unrecognized attribute was encountered: ${attribute}`);
}
});
// Some functions are used differently by us than they are defined in mathjs
// That's why we do a simple search-and-replace for these few cases
// We have to replace 'log(a)' with 'log(a, 10)' so this has to be done before we change 'ln(b)' to 'log(b)'
expression = expression
.replace('average', 'mean')
.replace('random', 'random()')
.replace(/log\((\d*)\)/g, 'log($1, 10)')
.replace('ln', 'log');
}
let expressionResult;
try {
expressionResult = math.eval(expression);
} catch (err) {
logger.info(`Unable to parse ${initialExpression} mathematically.`);
return expression;
}
if (expressionResult.im) {
logger.info(`Parsed expression ${expression} into ${expressionResult.im}`);
return expressionResult.im;
}
logger.info(`Parsed expression ${expression} into ${expressionResult}`);
return expressionResult;
}
/**
* Returns the actual value recorded in the BF_VALUES record
*/
function getCurrentValue(valueEntry) {
if (valueEntry) {
return valueEntry.ValueData;
} else {
logger.error('Expression uses [VALUE] but no field ValueData was found');
}
}
module.exports = {
evaluateExpression,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment