diff --git a/src/constants.ts b/src/constants.ts index 1583fa72c..2585865bc 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -10,3 +10,35 @@ export const encounterRepresentation = export const FormsStore = 'forms-engine-store'; export const PatientChartWorkspaceHeaderSlot = 'patient-chart-workspace-header-slot'; export const codedTypes = ['radio', 'checkbox', 'select', 'content-switcher']; +export const expressionDelimiters = [ + '+', + '*', + '/', + '%', + '==', + '!=', + '===', + '!==', + '>', + '<', + '>=', + '<=', + '&&', + '||', + '&', + '|', + '^', + '~', + '<<', + '>>', + '>>>', + '+=', + '-=', + '*=', + '/=', + '%=', + '?:', + '?', + ':', + '=>', +]; diff --git a/src/utils/common-expression-helpers.ts b/src/utils/common-expression-helpers.ts index 802cfd044..25ea613f6 100644 --- a/src/utils/common-expression-helpers.ts +++ b/src/utils/common-expression-helpers.ts @@ -11,7 +11,7 @@ import { type FormNode } from './expression-runner'; import { isEmpty as isValueEmpty } from '../validators/form-validator'; import * as apiFunctions from '../api/api'; import { getZRefByGenderAndAge } from './zscore-service'; -import { ConceptFalse, ConceptTrue } from '../constants'; +import { ConceptFalse, ConceptTrue, expressionDelimiters } from '../constants'; export class CommonExpressionHelpers { node: FormNode = null; @@ -491,3 +491,27 @@ export const booleanConceptToBoolean = (booleanConceptRepresentation): boolean = return false; } }; + +export const expressionFormatter = (expression: string): string => { + let sanitizedExpression; + + expressionDelimiters.forEach((delimiter) => { + const expressionParts = expression.split(delimiter).map((eachItem) => eachItem.trim()); + if (expressionParts.length > 1) { + const [firstPart, lastPart] = expressionParts; //add a map to check for all elements in the array + + if (/[a-zA-Z0-9'()]/.test(firstPart.charAt(firstPart.length - 1)) || /[a-zA-Z0-9'()]/.test(lastPart?.charAt(0))) { + sanitizedExpression = splitAndFormat(delimiter, expression); + } + } + }); + + return sanitizedExpression; +}; + +function splitAndFormat(delimiter: string, expression: string): string { + return expression + .split(delimiter) + .map((item) => item.trim()) + .join(` ${delimiter} `); +} diff --git a/src/utils/expression-parser.ts b/src/utils/expression-parser.ts index 8243e7254..f9a5c067d 100644 --- a/src/utils/expression-parser.ts +++ b/src/utils/expression-parser.ts @@ -1,6 +1,6 @@ import { type FormField } from '../types'; import { ConceptFalse, ConceptTrue } from '../constants'; -import { registerDependency } from './common-expression-helpers'; +import { expressionFormatter, registerDependency } from './common-expression-helpers'; import { type FormNode } from './expression-runner'; /** @@ -15,8 +15,10 @@ export function parseExpression(expression: string): string[] { let inQuote = false; let openParensCount = 0; - for (let i = 0; i < expression.length; i++) { - const char = expression.charAt(i); + const sanitizedExpression = expressionFormatter(expression); + + for (let i = 0; i < sanitizedExpression.length; i++) { + const char = sanitizedExpression.charAt(i); if (char === "'" || char === '"') { if (inQuote) { diff --git a/src/utils/expression-runner.ts b/src/utils/expression-runner.ts index 6b5ce27c3..d64fd0147 100644 --- a/src/utils/expression-runner.ts +++ b/src/utils/expression-runner.ts @@ -32,68 +32,6 @@ export function evaluateExpression( return null; } - const operators = [ - '+', - '*', - '/', - '%', - '==', - '!=', - '===', - '!==', - '>', - '<', - '>=', - '<=', - '&&', - '||', - '&', - '|', - '^', - '~', - '<<', - '>>', - '>>>', - '+=', - '-=', - '*=', - '/=', - '%=', - '?:', - '?', - ':', - '=>', - ]; - - function splitter(delimiter: string, expression: string) { - return expression - .split(delimiter) - .map((item) => item.trim()) - .join(` ${delimiter} `); - } - - const expressionFormatter = (expression: string): string => { - let sanitizedExpression; - - operators.forEach((operator) => { - const expressionParts = expression.split(operator).map((eachItem) => eachItem.trim()); - if (expressionParts.length > 1) { - const [firstPart, lastPart] = expressionParts; - - if (!/[a-zA-Z0-9']/.test(firstPart.charAt(firstPart.length - 1)) || !/[a-zA-Z0-9']/.test(lastPart?.charAt(0))) { - // these have been split by an invalid operator - console.log('split by invalid delimiter'); - } else { - sanitizedExpression = splitter(operator, expression); - } - } - }); - - return sanitizedExpression; - }; - - expressionFormatter(expression); - const allFieldsKeys = fields.map((f) => f.id); const parts = parseExpression(expression.trim()); // register dependencies