Skip to content

Commit

Permalink
Fix JSONPath filter operator precedence.
Browse files Browse the repository at this point in the history
  • Loading branch information
jg-rp committed Oct 13, 2023
1 parent 03a6ceb commit ebc1378
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 5 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# JSON P3 Change Log

# Version 0.2.1

**Fixes**

- Fixed JSONPath filter operator precedence. Previously logical _and_ (`&&`) and _or_ (`||`) were binding more tightly than logical negation (`!`).

# Version 0.2.0

**Breaking Changes**
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "json-p3",
"version": "0.2.0",
"version": "0.2.1",
"author": "James Prior",
"license": "MIT",
"description": "JSONPath, JSON Pointer and JSON Patch",
Expand Down
6 changes: 3 additions & 3 deletions src/path/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ import {
import { Token, TokenKind, TokenStream } from "./token";

const PRECEDENCE_LOWEST = 1;
const PRECEDENCE_LOGICALRIGHT = 3;
const PRECEDENCE_LOGICAL_AND = 4;
const PRECEDENCE_LOGICAL_OR = 5;
const PRECEDENCE_COMPARISON = 6;
const PRECEDENCE_PREFIX = 7;

const PRECEDENCES: Map<TokenKind, number> = new Map([
[TokenKind.AND, PRECEDENCE_LOGICAL_AND],
Expand All @@ -42,7 +42,7 @@ const PRECEDENCES: Map<TokenKind, number> = new Map([
[TokenKind.LE, PRECEDENCE_COMPARISON],
[TokenKind.LT, PRECEDENCE_COMPARISON],
[TokenKind.NE, PRECEDENCE_COMPARISON],
[TokenKind.NOT, PRECEDENCE_LOGICALRIGHT],
[TokenKind.NOT, PRECEDENCE_PREFIX],
[TokenKind.OR, PRECEDENCE_LOGICAL_OR],
[TokenKind.RPAREN, PRECEDENCE_LOWEST],
]);
Expand Down Expand Up @@ -312,7 +312,7 @@ export class Parser {
return new PrefixExpression(
stream.current,
"!",
this.parseFilterExpression(stream, PRECEDENCE_LOGICALRIGHT),
this.parseFilterExpression(stream, PRECEDENCE_PREFIX),
);
}

Expand Down
40 changes: 39 additions & 1 deletion tests/path/parse.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,44 @@
import { JSONPathSyntaxError, compile, query } from "../../src/path";
import {
JSONPathEnvironment,
JSONPathSyntaxError,
compile,
query,
} from "../../src/path";

type TestCase = {
description: string;
path: string;
want: string;
};

const TEST_CASES: TestCase[] = [
{
description: "not binds more tightly than and",
path: "$[[email protected] && [email protected]]",
want: "$[?(!@['a'] && !@['b'])]",
},
{
description: "not binds more tightly than or",
path: "$[[email protected] || [email protected]]",
want: "$[?(!@['a'] || !@['b'])]",
},
{
description: "control precedence with parens",
path: "$[?!(@.a && [email protected])]",
want: "$[?!(@['a'] && !@['b'])]",
},
];

describe("parse", () => {
const env = new JSONPathEnvironment();

test.each<TestCase>(TEST_CASES)(
"$description",
({ path, want }: TestCase) => {
expect(env.compile(path).toString()).toBe(want);
},
);

test("well-typed nested functions", () => {
const data = {
regex: "a.*",
Expand Down

0 comments on commit ebc1378

Please sign in to comment.