import jsep from 'jsep'; import { ColumnType } from './Api'; export const jsepCurlyHook = { name: 'curly', init(jsep) { jsep.hooks.add('gobble-token', function gobbleCurlyLiteral(env) { const OCURLY_CODE = 123; // { const CCURLY_CODE = 125; // } const { context } = env; if ( !jsep.isIdentifierStart(context.code) && context.code === OCURLY_CODE ) { context.index += 1; const nodes = context.gobbleExpressions(CCURLY_CODE); if (context.code === CCURLY_CODE) { context.index += 1; env.node = { type: jsep.IDENTIFIER, name: nodes.map((node) => node.name).join(' '), }; return env.node; } else { context.throwError('Unclosed }'); } } }); }, } as jsep.IPlugin; export async function substituteColumnAliasWithIdInFormula( formula, columns: ColumnType[] ) { const substituteId = async (pt: any) => { if (pt.type === 'CallExpression') { for (const arg of pt.arguments || []) { await substituteId(arg); } } else if (pt.type === 'Literal') { return; } else if (pt.type === 'Identifier') { const colNameOrId = pt.name; const column = columns.find( (c) => c.id === colNameOrId || c.column_name === colNameOrId || c.title === colNameOrId ); pt.name = '{' + column.id + '}'; } else if (pt.type === 'BinaryExpression') { await substituteId(pt.left); await substituteId(pt.right); } }; // register jsep curly hook jsep.plugins.register(jsepCurlyHook); const parsedFormula = jsep(formula); await substituteId(parsedFormula); return jsepTreeToFormula(parsedFormula); } export function substituteColumnIdWithAliasInFormula( formula, columns: ColumnType[], rawFormula? ) { const substituteId = (pt: any, ptRaw?: any) => { if (pt.type === 'CallExpression') { let i = 0; for (const arg of pt.arguments || []) { substituteId(arg, ptRaw?.arguments?.[i++]); } } else if (pt.type === 'Literal') { return; } else if (pt.type === 'Identifier') { const colNameOrId = pt?.name; const column = columns.find( (c) => c.id === colNameOrId || c.column_name === colNameOrId || c.title === colNameOrId ); pt.name = column?.title || ptRaw?.name || pt?.name; } else if (pt.type === 'BinaryExpression') { substituteId(pt.left, ptRaw?.left); substituteId(pt.right, ptRaw?.right); } }; // register jsep curly hook jsep.plugins.register(jsepCurlyHook); const parsedFormula = jsep(formula); const parsedRawFormula = rawFormula && jsep(rawFormula); substituteId(parsedFormula, parsedRawFormula); return jsepTreeToFormula(parsedFormula); } export function jsepTreeToFormula(node) { if (node.type === 'BinaryExpression' || node.type === 'LogicalExpression') { return ( '(' + jsepTreeToFormula(node.left) + ' ' + node.operator + ' ' + jsepTreeToFormula(node.right) + ')' ); } if (node.type === 'UnaryExpression') { return node.operator + jsepTreeToFormula(node.argument); } if (node.type === 'MemberExpression') { return ( jsepTreeToFormula(node.object) + '[' + jsepTreeToFormula(node.property) + ']' ); } if (node.type === 'Identifier') { const formulas = [ 'AVG', 'ADD', 'DATEADD', 'AND', 'OR', 'CONCAT', 'TRIM', 'UPPER', 'LOWER', 'LEN', 'MIN', 'MAX', 'CEILING', 'FLOOR', 'ROUND', 'MOD', 'REPEAT', 'LOG', 'EXP', 'POWER', 'SQRT', 'SQRT', 'ABS', 'NOW', 'REPLACE', 'SEARCH', 'INT', 'RIGHT', 'LEFT', 'SUBSTR', 'MID', 'IF', 'SWITCH', 'URL', ]; if (!formulas.includes(node.name)) return '{' + node.name + '}'; return node.name; } if (node.type === 'Literal') { if (typeof node.value === 'string') { return '"' + node.value + '"'; } return '' + node.value; } if (node.type === 'CallExpression') { return ( jsepTreeToFormula(node.callee) + '(' + node.arguments.map(jsepTreeToFormula).join(', ') + ')' ); } if (node.type === 'ArrayExpression') { return '[' + node.elements.map(jsepTreeToFormula).join(', ') + ']'; } if (node.type === 'Compound') { return node.body.map((e) => jsepTreeToFormula(e)).join(' '); } if (node.type === 'ConditionalExpression') { return ( jsepTreeToFormula(node.test) + ' ? ' + jsepTreeToFormula(node.consequent) + ' : ' + jsepTreeToFormula(node.alternate) ); } return ''; }