From 6d2d8f5c1913278035e41ede7ca313f1fd09aa6e Mon Sep 17 00:00:00 2001 From: Glen Van Ginkel Date: Sat, 10 Oct 2020 14:48:37 +0100 Subject: [PATCH] Support root character --- src/Lexer.ts | 2 ++ src/Parser.ts | 3 +++ src/TreeInterpreter.ts | 4 ++++ test/jmespath.spec.js | 22 ++++++++++++++++++++++ 4 files changed, 31 insertions(+) diff --git a/src/Lexer.ts b/src/Lexer.ts index bbfd13b..0367481 100644 --- a/src/Lexer.ts +++ b/src/Lexer.ts @@ -12,6 +12,7 @@ export enum Token { TOK_RBRACE = 'Rbrace', TOK_NUMBER = 'Number', TOK_CURRENT = 'Current', + TOK_ROOT = 'Root', TOK_EXPREF = 'Expref', TOK_PIPE = 'Pipe', TOK_OR = 'Or', @@ -74,6 +75,7 @@ export const basicTokens = { '.': Token.TOK_DOT, ':': Token.TOK_COLON, '@': Token.TOK_CURRENT, + ['$']: Token.TOK_ROOT, ']': Token.TOK_RBRACKET, '{': Token.TOK_LBRACE, '}': Token.TOK_RBRACE, diff --git a/src/Parser.ts b/src/Parser.ts index ec295e3..cce8a8a 100644 --- a/src/Parser.ts +++ b/src/Parser.ts @@ -22,6 +22,7 @@ const bindingPower: { [token: string]: number } = { [Token.TOK_NUMBER]: 0, [Token.TOK_CURRENT]: 0, [Token.TOK_EXPREF]: 0, + [Token.TOK_ROOT]: 0, [Token.TOK_PIPE]: 1, [Token.TOK_OR]: 2, [Token.TOK_AND]: 3, @@ -134,6 +135,8 @@ class TokenParser { return this.parseMultiselectList(); case Token.TOK_CURRENT: return { type: Token.TOK_CURRENT }; + case Token.TOK_ROOT: + return { type: Token.TOK_ROOT }; case Token.TOK_EXPREF: expression = this.expression(bindingPower.Expref); return { type: 'ExpressionReference', children: [expression] } as ExpressionNode; diff --git a/src/TreeInterpreter.ts b/src/TreeInterpreter.ts index a295067..522fbf5 100644 --- a/src/TreeInterpreter.ts +++ b/src/TreeInterpreter.ts @@ -13,12 +13,14 @@ import { JSONValue } from '.'; export class TreeInterpreter { runtime: Runtime; + private _rootValue: JSONValue | null = null; constructor() { this.runtime = new Runtime(this); } search(node: ExpressionNodeTree, value: JSONValue): JSONValue { + this._rootValue = value; return this.visit(node, value) as JSONValue; } @@ -224,6 +226,8 @@ export class TreeInterpreter { return this.visit((node as ExpressionNode).children[1], left); case Token.TOK_CURRENT: return value; + case Token.TOK_ROOT: + return this._rootValue; case 'Function': const resolvedArgs: JSONValue[] = []; for (let j = 0; j < (node as ExpressionNode).children.length; j += 1) { diff --git a/test/jmespath.spec.js b/test/jmespath.spec.js index e3310ef..6b8690b 100644 --- a/test/jmespath.spec.js +++ b/test/jmespath.spec.js @@ -349,3 +349,25 @@ describe('registerFunction', () => { ).toThrow('ArgumentError: optionalArgs() takes 1 arguments but received 3'); }); }); + +describe('root', () => { + it('$ should give access to the root value', () => { + var value = search({ foo: { bar: 1 } }, 'foo.{ value: $.foo.bar }'); + expect(value.value).toBe(1); + }); + it('$ should give access to the root value after pipe', () => { + var value = search({ foo: { bar: 1 } }, 'foo | $.foo.bar'); + expect(value).toEqual(1); + }); + it('$ should give access in expressions', () => { + var value = search([{ foo: { bar: 1 } }, { foo: { bar: 99 } }], 'map(&foo.{boo: bar, root: $}, @)'); + expect(value).toEqual([ + { boo: 1, root: [{ foo: { bar: 1 } }, { foo: { bar: 99 } }] }, + { boo: 99, root: [{ foo: { bar: 1 } }, { foo: { bar: 99 } }] }, + ]); + }); + it('$ can be used in parallel', () => { + var value = search([{ foo: { bar: 1 } }, { foo: { bar: 99 } }], '[$[0].foo.bar, $[1].foo.bar]'); + expect(value).toEqual([1, 99]); + }); +});