From 7789caefdbfc6cd976a16015977b6aafd1f48f69 Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Sat, 25 May 2024 23:01:38 +0530 Subject: [PATCH] feat: add anyof noneof operators --- src/lexer.ts | 14 +++++++++- src/operators.ts | 2 ++ src/parser.ts | 38 ++++++++++++++++++++++++-- src/translator.ts | 9 ++---- src/types.ts | 2 ++ test/scenarios/comparisons/data.ts | 2 ++ test/scenarios/comparisons/template.jt | 2 ++ 7 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/lexer.ts b/src/lexer.ts index 9cc5ca2..b5177d8 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -165,8 +165,20 @@ export class JsonTemplateLexer { return this.matchKeywordValue(Keyword.SUBSETOF); } + matchAnyOf(): boolean { + return this.matchKeywordValue(Keyword.ANYOF); + } + + matchNoneOf(): boolean { + return this.matchKeywordValue(Keyword.NONEOF); + } + matchIN(): boolean { - return this.matchKeywordValue(Keyword.IN) || this.matchKeywordValue(Keyword.NOT_IN); + return this.matchKeywordValue(Keyword.IN); + } + + matchNotIN(): boolean { + return this.matchKeywordValue(Keyword.NOT_IN); } matchFunction(): boolean { diff --git a/src/operators.ts b/src/operators.ts index fe4e14c..eb29417 100644 --- a/src/operators.ts +++ b/src/operators.ts @@ -89,6 +89,8 @@ export const binaryOperators = { subsetof: (val1, val2): string => `${val1}.every((el) => {return ${val2}.includes(el);})`, + anyof: (val1, val2): string => `${val1}.some((el) => {return ${val2}.includes(el);})`, + '+': (val1, val2): string => `${val1}+${val2}`, '-': (val1, val2): string => `${val1}-${val2}`, diff --git a/src/parser.ts b/src/parser.ts index 1ce02c8..979ca3a 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -694,6 +694,15 @@ export class JsonTemplateParser { return expr; } + private parseInExpr(expr: Expression): BinaryExpression { + this.lexer.ignoreTokens(1); + return { + type: SyntaxType.IN_EXPR, + op: Keyword.IN, + args: [expr, this.parseRelationalExpr()], + }; + } + private parseRelationalExpr(): BinaryExpression | Expression { const expr = this.parseNextExpr(OperatorType.RELATIONAL); @@ -702,19 +711,44 @@ export class JsonTemplateParser { this.lexer.match('>') || this.lexer.match('<=') || this.lexer.match('>=') || - this.lexer.matchIN() || this.lexer.matchContains() || this.lexer.matchSize() || this.lexer.matchEmpty() || + this.lexer.matchAnyOf() || this.lexer.matchSubsetOf() ) { return { - type: this.lexer.matchIN() ? SyntaxType.IN_EXPR : SyntaxType.COMPARISON_EXPR, + type: SyntaxType.COMPARISON_EXPR, op: this.lexer.value(), args: [expr, this.parseRelationalExpr()], }; } + if (this.lexer.matchIN()) { + return this.parseInExpr(expr); + } + + if (this.lexer.matchNotIN()) { + return { + type: SyntaxType.UNARY_EXPR, + op: '!', + arg: this.parseInExpr(expr), + }; + } + + if (this.lexer.matchNoneOf()) { + this.lexer.ignoreTokens(1); + return { + type: SyntaxType.UNARY_EXPR, + op: '!', + arg: { + type: SyntaxType.COMPARISON_EXPR, + op: Keyword.ANYOF, + args: [expr, this.parseRelationalExpr()], + }, + }; + } + return expr; } diff --git a/src/translator.ts b/src/translator.ts index 6e86a5e..b074366 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -687,13 +687,8 @@ export class JsonTemplateTranslator { code.push(this.translateExpr(expr.args[0], val1, ctx)); code.push(this.translateExpr(expr.args[1], val2, ctx)); code.push(`if(typeof ${val2} === 'object'){`); - if (expr.op === Keyword.IN) { - const inCode = `(Array.isArray(${val2}) ? ${val2}.includes(${val1}) : ${val1} in ${val2})`; - code.push(JsonTemplateTranslator.generateAssignmentCode(resultVar, inCode)); - } else { - const notInCode = `(Array.isArray(${val2}) ? !${val2}.includes(${val1}) : !(${val1} in ${val2}))`; - code.push(JsonTemplateTranslator.generateAssignmentCode(resultVar, notInCode)); - } + const inCode = `(Array.isArray(${val2}) ? ${val2}.includes(${val1}) : ${val1} in ${val2})`; + code.push(JsonTemplateTranslator.generateAssignmentCode(resultVar, inCode)); code.push('} else {'); code.push(JsonTemplateTranslator.generateAssignmentCode(resultVar, 'false')); code.push('}'); diff --git a/src/types.ts b/src/types.ts index 4e4ddd5..5a0b694 100644 --- a/src/types.ts +++ b/src/types.ts @@ -12,6 +12,8 @@ export enum Keyword { NOT = 'not', CONTAINS = 'contains', SUBSETOF = 'subsetof', + ANYOF = 'anyof', + NONEOF = 'noneof', EMPTY = 'empty', SIZE = 'size', RETURN = 'return', diff --git a/test/scenarios/comparisons/data.ts b/test/scenarios/comparisons/data.ts index c686f71..76cd34a 100644 --- a/test/scenarios/comparisons/data.ts +++ b/test/scenarios/comparisons/data.ts @@ -37,6 +37,8 @@ export const data: Scenario[] = [ true, true, true, + true, + true, ], }, ]; diff --git a/test/scenarios/comparisons/template.jt b/test/scenarios/comparisons/template.jt index f39b2c5..efb5141 100644 --- a/test/scenarios/comparisons/template.jt +++ b/test/scenarios/comparisons/template.jt @@ -33,4 +33,6 @@ [] subsetof ["a", "b", "c"], "abc" =~ /a.*c/, "AdC" =~ /a.*c/i, +["a", "b"] anyof ["a", "c"], +["c", "d"] noneof ["a", "b"] ] \ No newline at end of file