From 2f297719365527c7b8c3c9ba24042a7b2329c4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C5=A9=20=C4=90=E1=BB=A9c=20Duy?= Date: Sun, 25 Dec 2022 15:29:49 +0700 Subject: [PATCH] Add ternary operator --- src/operations.test.ts | 42 +++++++++++++++++++++-------------- src/operations.ts | 50 +++++++++++++++++++++++++----------------- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/src/operations.test.ts b/src/operations.test.ts index a539fe6d..97240a51 100644 --- a/src/operations.test.ts +++ b/src/operations.test.ts @@ -244,22 +244,31 @@ describe('Test parseExpression', () => { expect(parseExpression('ASUM(a, b)', { a: [{b: 5}, {b: 10}, {b: 0}, {b: 15}] })).toBe(30); expect(parseExpression('ASUM(a, MULTIPLY(b, c))', { a: [{b: 5, c: 1}, {b: 10, c: 2}, {b: 1000, c: 0}, {b: 15, c: 10}] })).toBe(175); }); + + test('IF op', () => { + expect(parseExpression('IF(a, b, c)', { a: true, b: 1, c: 2})).toBe(1); + expect(parseExpression('IF(a, b, c)', { a: false, b: 1, c: 2})).toBe(2); + expect(parseExpression('IF(a, b, c)', { a: 1, b: 1, c: 2})).toBe(2); + expect(parseExpression('IF(a, b, c)', { a: '1', b: 1, c: 2})).toBe(2); + expect(parseExpression('IF(a, b, c)', { a: {}, b: 1, c: 2})).toBe(2); + expect(parseExpression('IF(a, b, c)', { a: [], b: 1, c: 2})).toBe(2); + expect(parseExpression('IF(EQUAL(a, 5), b, c)', { a: 5, b: 1, c: 2})).toBe(1); + expect(parseExpression('IF(AND(GT(a, 0), LT(a, 10)), b, c)', { a: 5, b: 1, c: 2})).toBe(1); + }); }); describe('Test parseOp', () => { test('Simple unary op', () => { expect(parseOp('OP_(var)')).toStrictEqual({ op: 'OP_', - a: 'var', - b: null, + args: ['var'], }); }); test('Simple binary op', () => { expect(parseOp('OP_(var1,var2)')).toStrictEqual({ op: 'OP_', - a: 'var1', - b: 'var2', + args: ['var1', 'var2'], }); }); @@ -274,48 +283,49 @@ describe('Test parseOp', () => { test('Complex op 1', () => { expect(parseOp('OP_(OP_(var1))')).toStrictEqual({ op: 'OP_', - a: 'OP_(var1)', - b: null, + args: ['OP_(var1)'], }); }); test('Complex op 2', () => { expect(parseOp('OP_(OP_(var1),var2)')).toStrictEqual({ op: 'OP_', - a: 'OP_(var1)', - b: 'var2', + args: ['OP_(var1)', 'var2'], }); }); test('Complex op 3', () => { expect(parseOp('OP_(OP_(var1),OP_(var2))')).toStrictEqual({ op: 'OP_', - a: 'OP_(var1)', - b: 'OP_(var2)', + args: ['OP_(var1)', 'OP_(var2)'], }); }); test('Complex op 4', () => { expect(parseOp('OP_(OP_(OP_(var1), var2),OP_(var3))')).toStrictEqual({ op: 'OP_', - a: 'OP_(OP_(var1), var2)', - b: 'OP_(var3)', + args: ['OP_(OP_(var1), var2)', 'OP_(var3)'], }); }); test('Complex op 5', () => { expect(parseOp('OP_(OP_(OP_(var1), var2),OP_(var3, OP_(var4, var5)))')).toStrictEqual({ op: 'OP_', - a: 'OP_(OP_(var1), var2)', - b: 'OP_(var3, OP_(var4, var5))', + args: ['OP_(OP_(var1), var2)', 'OP_(var3, OP_(var4, var5))'], }); }); test('Complex op 5', () => { expect(parseOp('OP_(OP_(OP_(var1, OP_(var2, OP_(var3, var4))), var5))')).toStrictEqual({ op: 'OP_', - a: 'OP_(OP_(var1, OP_(var2, OP_(var3, var4))), var5)', - b: null, + args: ['OP_(OP_(var1, OP_(var2, OP_(var3, var4))), var5)'], + }); + }); + + test('Ternary op', () => { + expect(parseOp('OP_(OP_(var1),var2,var3)')).toStrictEqual({ + op: 'OP_', + args: ['OP_(var1)', 'var2', 'var3'], }); }); }); diff --git a/src/operations.ts b/src/operations.ts index c31caa8a..74331bce 100644 --- a/src/operations.ts +++ b/src/operations.ts @@ -16,11 +16,11 @@ export function parseExpression(exp: string, values: Record): any { const opMatch = parseOp(exp); if (opMatch) { - const { op, a, b } = opMatch; - const valueA = parseExpression(a, values); + const { op, args } = opMatch; // unary operators - if (b === null) { + if (args.length === 1) { + const valueA = parseExpression(args[0], values); // type conversion if (op === 'INT') { return parseInt(valueA); @@ -113,12 +113,13 @@ export function parseExpression(exp: string, values: Record): any { } return 0; } - } else if (op === 'ASUM') { + } else if (op === 'ASUM' && args.length === 2) { // aggregated sum - return (values[a] as unknown[])?.reduce((acc, item) => acc + parseExpression(b, item as typeof values), 0) ?? 0; - } else { + return (values[args[0]] as unknown[])?.reduce((acc, item) => acc + parseExpression(args[1], item as typeof values), 0) ?? 0; + } else if (args.length === 2) { // binary operators - const valueB = parseExpression(b, values); + const valueA = parseExpression(args[0], values); + const valueB = parseExpression(args[1], values); // arithmetic if (op === 'SUM') { @@ -183,6 +184,13 @@ export function parseExpression(exp: string, values: Record): any { if (op === 'OR') { return valueA || valueB; } + } else if (args.length === 3) { + if (op === 'IF') { + if (parseExpression(args[0], values) === true) { + return parseExpression(args[1], values); + } + return parseExpression(args[2], values); + } } } @@ -196,29 +204,31 @@ export function parseExpression(exp: string, values: Record): any { return ''; } -export function parseOp(exp: string) { +export function parseOp(exp: string): { + op: string; + args: string[]; +} | null { const match = exp.match(/^([A-Z_]+)\((.+)\)$/); if (match) { + const args = []; const op = match[1] as string; const innerExp = match[2] as string; - let braceCount = 0; - for (let i = 0; i < innerExp.length; i += 1) { + + let braceCount = 0, i = 0, j = 0; + for (; i < innerExp.length; i += 1) { const c = innerExp[i]; if (c === '(') braceCount += 1; if (c === ')') braceCount -= 1; if (c === ',' && braceCount === 0) { - return { - op, - a: innerExp.slice(0, i), - b: innerExp.slice(i + 1), - }; + args.push(innerExp.slice(j, i)); + j = i + 1; } } - return { - op, - a: innerExp, - b: null, - }; + if (j < i) { + args.push(innerExp.slice(j, i)); + } + + return { op, args }; } return null; }