Skip to content

Commit

Permalink
fix: add prefix for wildcard prop values (#68)
Browse files Browse the repository at this point in the history
* fix: object index filters

* fix: add prefix for wildcard prop values

* fix: mappings with root fields use case

* refactor: wild card props syntax

Release-As: 0.11.0

* refactor: object props parsing

* fix: grammar
  • Loading branch information
koladilip authored Jun 10, 2024
1 parent c00b99b commit c63d985
Show file tree
Hide file tree
Showing 12 changed files with 114 additions and 124 deletions.
2 changes: 1 addition & 1 deletion src/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class JsonTemplateLexer {
return this.matchPathSelector() || this.matchID();
}

matchObjectWildCardPropValue(): boolean {
matchObjectContextProp(): boolean {
return this.match('@') && this.matchID(1);
}

Expand Down
33 changes: 11 additions & 22 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
ObjectExpression,
ObjectFilterExpression,
ObjectPropExpression,
ObjectWildcardValueExpression,
OperatorType,
PathExpression,
PathOptions,
Expand Down Expand Up @@ -1088,18 +1087,6 @@ export class JsonTemplateParser {
};
}

private parseObjectPropWildcardValueExpr(): ObjectWildcardValueExpression {
const idToken = this.lexer.lookahead(1);
if (!['key', 'value'].includes(idToken.value)) {
throw new JsonTemplateParserError(`Invalid object wildcard prop value @${idToken.value}`);
}
this.lexer.ignoreTokens(2);
return {
type: SyntaxType.OBJECT_PROP_WILD_CARD_VALUE_EXPR,
value: idToken.value,
};
}

private parseObjectKeyExpr(): Expression | string {
let key: Expression | string;
if (this.lexer.match('[')) {
Expand All @@ -1109,9 +1096,7 @@ export class JsonTemplateParser {
} else if (this.lexer.matchID() || this.lexer.matchKeyword()) {
key = this.lexer.value();
} else if (this.lexer.matchLiteral() && !this.lexer.matchTokenType(TokenType.REGEXP)) {
key = this.parseLiteralExpr();
} else if (this.lexer.matchObjectWildCardPropValue()) {
key = this.parseObjectPropWildcardValueExpr();
key = this.lexer.value().toString();
} else {
this.lexer.throwUnexpectedToken();
}
Expand Down Expand Up @@ -1142,19 +1127,26 @@ export class JsonTemplateParser {
}
}

private static isWildcardPropKey(expr: any): boolean {
return typeof expr === 'object' && expr?.type === SyntaxType.OBJECT_PROP_WILD_CARD_VALUE_EXPR;
private getObjectPropContextVar(): string | undefined {
if (this.lexer.matchObjectContextProp()) {
this.lexer.ignoreTokens(1);
return this.lexer.value();
}
}

private parseNormalObjectPropExpr(): ObjectPropExpression {
const contextVar = this.getObjectPropContextVar();
const key = this.parseObjectKeyExpr();
if (contextVar && typeof key === 'string') {
throw new JsonTemplateParserError('Context prop should be used with a key expression');
}
this.lexer.expect(':');
const value = this.parseBaseExpr();
return {
type: SyntaxType.OBJECT_PROP_EXPR,
key,
value,
wildcard: JsonTemplateParser.isWildcardPropKey(key),
contextVar,
};
}

Expand Down Expand Up @@ -1390,9 +1382,6 @@ export class JsonTemplateParser {
return this.parsePathTypeExpr();
}

if (this.lexer.matchObjectWildCardPropValue()) {
return this.parseObjectPropWildcardValueExpr() as Expression;
}
if (this.lexer.matchPath()) {
return this.parsePath();
}
Expand Down
19 changes: 4 additions & 15 deletions src/reverse_translator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
ObjectExpression,
ObjectFilterExpression,
ObjectPropExpression,
ObjectWildcardValueExpression,
PathExpression,
PathOptions,
PathType,
Expand Down Expand Up @@ -111,19 +110,11 @@ export class JsonTemplateReverseTranslator {
return this.translateArrayIndexFilterExpression(expr as IndexFilterExpression);
case SyntaxType.RANGE_FILTER_EXPR:
return this.translateRangeFilterExpression(expr as RangeFilterExpression);
case SyntaxType.OBJECT_PROP_WILD_CARD_VALUE_EXPR:
return this.translateWildcardObjectPropValueExpression(
expr as ObjectWildcardValueExpression,
);
default:
return '';
}
}

translateWildcardObjectPropValueExpression(expr: ObjectWildcardValueExpression): string {
return `@${expr.value}`;
}

translateArrayFilterExpression(expr: ArrayFilterExpression): string {
return this.translateExpression(expr.filter);
}
Expand Down Expand Up @@ -446,14 +437,12 @@ export class JsonTemplateReverseTranslator {

translateObjectPropExpression(expr: ObjectPropExpression): string {
const code: string[] = [];
if (expr.contextVar) {
code.push(`@${expr.contextVar} `);
}
if (expr.key) {
if (typeof expr.key === 'string') {
code.push(expr.key);
} else if (
expr.key.type === SyntaxType.LITERAL ||
expr.key.type === SyntaxType.OBJECT_PROP_WILD_CARD_VALUE_EXPR
) {
code.push(this.translateExpression(expr.key));
code.push(escapeStr(expr.key));
} else {
code.push(this.translateWithWrapper(expr.key, '[', ']'));
}
Expand Down
43 changes: 17 additions & 26 deletions src/translator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import {
IncrementExpression,
LoopControlExpression,
ObjectPropExpression,
ObjectWildcardValueExpression,
} from './types';
import { convertToStatementsExpr, escapeStr } from './utils/common';
import { translateLiteral } from './utils/translator';
Expand Down Expand Up @@ -190,13 +189,6 @@ export class JsonTemplateTranslator {
case SyntaxType.THROW_EXPR:
return this.translateThrowExpr(expr as ThrowExpression, dest, ctx);

case SyntaxType.OBJECT_PROP_WILD_CARD_VALUE_EXPR:
return this.translateObjectWildcardValueExpr(
expr as ObjectWildcardValueExpression,
dest,
ctx,
);

default:
return '';
}
Expand Down Expand Up @@ -542,28 +534,27 @@ export class JsonTemplateTranslator {
return code.join('');
}

private translateObjectWildcardValueExpr(
expr: ObjectWildcardValueExpression,
dest: string,
_ctx: string,
): string {
return JsonTemplateTranslator.generateAssignmentCode(dest, expr.value);
}

private translateObjectWildcardProp(
private translateObjectContextProp(
expr: ObjectPropExpression,
dest: string,
ctx: string,
vars: string[] = [],
): string {
const code: string[] = [];
const keyExpr = expr.key as ObjectWildcardValueExpression;
code.push(JsonTemplateTranslator.generateAssignmentCode(dest, '{}'));
const keyVar = this.acquireVar();
const valueVar = this.acquireVar();
vars.push(valueVar);
code.push(`for(let [key, value] of Object.entries(${ctx})){`);
vars.push(keyVar, valueVar);
code.push(`for(let [${VARS_PREFIX}key, ${VARS_PREFIX}value] of Object.entries(${ctx})){`);
code.push(
JsonTemplateTranslator.generateAssignmentCode(
expr.contextVar as string,
`{key:${VARS_PREFIX}key,value:${VARS_PREFIX}value}`,
),
);
code.push(this.translateExpr(expr.key as Expression, keyVar, ctx));
code.push(this.translateExpr(expr.value, valueVar, ctx));
code.push(`${dest}[${keyExpr.value}] = ${valueVar};`);
code.push(`${dest}[${keyVar}] = ${valueVar};`);
code.push('}');
return code.join('');
}
Expand All @@ -574,10 +565,10 @@ export class JsonTemplateTranslator {
const vars: string[] = [];
for (const prop of expr.props) {
const propParts: string[] = [];
if (prop.wildcard) {
const wildCardPropVar = this.acquireVar();
code.push(this.translateObjectWildcardProp(prop, wildCardPropVar, ctx));
propExprs.push(`...${wildCardPropVar}`);
if (prop.contextVar) {
const propWithContextVar = this.acquireVar();
code.push(this.translateObjectContextProp(prop, propWithContextVar, ctx));
propExprs.push(`...${propWithContextVar}`);
} else {
if (prop.key) {
if (typeof prop.key !== 'string') {
Expand All @@ -586,7 +577,7 @@ export class JsonTemplateTranslator {
propParts.push(`[${keyVar}]`);
vars.push(keyVar);
} else {
propParts.push(prop.key);
propParts.push(escapeStr(prop.key));
}
propParts.push(':');
}
Expand Down
7 changes: 1 addition & 6 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ export enum SyntaxType {
ARRAY_FILTER_EXPR = 'array_filter_expr',
DEFINITION_EXPR = 'definition_expr',
ASSIGNMENT_EXPR = 'assignment_expr',
OBJECT_PROP_WILD_CARD_VALUE_EXPR = 'object_prop_wild_card_value_expr',
OBJECT_PROP_EXPR = 'object_prop_expr',
OBJECT_EXPR = 'object_expr',
ARRAY_EXPR = 'array_expr',
Expand Down Expand Up @@ -148,14 +147,10 @@ export interface BlockExpression extends Expression {
statements: Expression[];
}

export interface ObjectWildcardValueExpression extends Expression {
value: string;
}

export interface ObjectPropExpression extends Expression {
key?: Expression | string;
value: Expression;
wildcard?: boolean;
contextVar?: string;
}

export interface ObjectExpression extends Expression {
Expand Down
68 changes: 39 additions & 29 deletions src/utils/converter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable no-param-reassign */
import { EMPTY_EXPR } from '../constants';
import {
SyntaxType,
PathExpression,
Expand All @@ -9,7 +10,7 @@ import {
Expression,
IndexFilterExpression,
BlockExpression,
ObjectWildcardValueExpression,
TokenType,
} from '../types';
import { createBlockExpression, getLastElement } from './common';

Expand Down Expand Up @@ -58,42 +59,38 @@ function processArrayIndexFilter(
function processAllFilter(
currentInputAST: PathExpression,
currentOutputPropAST: ObjectPropExpression,
): ObjectExpression {
): Expression {
const filterIndex = currentInputAST.parts.findIndex(
(part) => part.type === SyntaxType.OBJECT_FILTER_EXPR,
);

if (filterIndex === -1) {
return currentOutputPropAST.value as ObjectExpression;
}
const matchedInputParts = currentInputAST.parts.splice(0, filterIndex + 1);
if (currentOutputPropAST.value.type !== SyntaxType.PATH) {
matchedInputParts.push(createBlockExpression(currentOutputPropAST.value));
currentOutputPropAST.value = {
type: SyntaxType.PATH,
root: currentInputAST.root,
pathType: currentInputAST.pathType,
parts: matchedInputParts,
returnAsArray: true,
} as PathExpression;
if (currentOutputPropAST.value.type === SyntaxType.OBJECT_EXPR) {
return currentOutputPropAST.value;
}
} else {
const matchedInputParts = currentInputAST.parts.splice(0, filterIndex + 1);
if (currentOutputPropAST.value.type !== SyntaxType.PATH) {
matchedInputParts.push(createBlockExpression(currentOutputPropAST.value));
currentOutputPropAST.value = {
type: SyntaxType.PATH,
root: currentInputAST.root,
pathType: currentInputAST.pathType,
parts: matchedInputParts,
returnAsArray: true,
} as PathExpression;
}
currentInputAST.root = undefined;
}
currentInputAST.root = undefined;

const blockExpr = getLastElement(currentOutputPropAST.value.parts) as BlockExpression;
return blockExpr.statements[0] as ObjectExpression;
const blockExpr = getLastElement(currentOutputPropAST.value.parts) as Expression;
return blockExpr?.statements?.[0] || EMPTY_EXPR;
}

function isWildcardSelector(expr: Expression): boolean {
return expr.type === SyntaxType.SELECTOR && expr.prop?.value === '*';
}

function createWildcardObjectPropValueExpression(value: string): ObjectWildcardValueExpression {
return {
type: SyntaxType.OBJECT_PROP_WILD_CARD_VALUE_EXPR,
value,
};
}

function processWildCardSelector(
flatMapping: FlatMappingAST,
currentOutputPropAST: ObjectPropExpression,
Expand All @@ -118,16 +115,29 @@ function processWildCardSelector(
parts: matchedInputParts,
} as PathExpression;
}
currentInputAST.root = createWildcardObjectPropValueExpression('value');
currentInputAST.root = 'e.value';

const blockExpr = getLastElement(currentOutputPropAST.value.parts) as BlockExpression;
const blockObjectExpr = blockExpr.statements[0] as ObjectExpression;
const objectExpr = createObjectExpression();
blockObjectExpr.props.push({
type: SyntaxType.OBJECT_PROP_EXPR,
key: createWildcardObjectPropValueExpression('key'),
key: {
type: SyntaxType.PATH,
root: 'e',
parts: [
{
type: SyntaxType.SELECTOR,
selector: '.',
prop: {
type: TokenType.ID,
value: 'key',
},
},
],
},
value: isLastPart ? currentInputAST : objectExpr,
wildcard: true,
contextVar: 'e',
});
return objectExpr;
}
Expand All @@ -136,7 +146,7 @@ function handleNextPart(
flatMapping: FlatMappingAST,
partNum: number,
currentOutputPropAST: ObjectPropExpression,
): ObjectExpression {
): Expression {
const nextOutputPart = flatMapping.outputExpr.parts[partNum];
if (nextOutputPart.filter?.type === SyntaxType.ALL_FILTER_EXPR) {
return processAllFilter(flatMapping.inputExpr, currentOutputPropAST);
Expand All @@ -154,7 +164,7 @@ function handleNextPart(
partNum === flatMapping.outputExpr.parts.length - 1,
);
}
return currentOutputPropAST.value as ObjectExpression;
return currentOutputPropAST.value;
}

function processFlatMappingPart(
Expand Down
Loading

0 comments on commit c63d985

Please sign in to comment.