Skip to content

Commit 14a8f4b

Browse files
committed
fix: some fixes and additional tests for stringifying parenthesis
1 parent 26be480 commit 14a8f4b

File tree

3 files changed

+49
-34
lines changed

3 files changed

+49
-34
lines changed

src/parse.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ export function parse(query: string, options?: JSONQueryParseOptions): JSONQuery
5151

5252
const right = parseOperator(precedenceLevel - 1)
5353

54-
const chained = name === left[0]
54+
const childName = left[0]
55+
const chained = name === childName
5556
if (chained && !allVarargOperators.includes(allOperatorsMap[name])) {
5657
i = start
5758
break

src/stringify.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isArray } from './is'
2-
import { extendOperators, operators } from './operators'
2+
import { extendOperators, extendVarargOperators, operators, varargOperators } from './operators'
33
import { unquotedPropertyRegex } from './regexps'
44
import type {
55
JSONPath,
@@ -37,6 +37,7 @@ export const stringify = (query: JSONQuery, options?: JSONQueryStringifyOptions)
3737
const customOperators = options?.operators ?? []
3838
const allOperators = extendOperators(operators, customOperators)
3939
const allOperatorsMap = Object.assign({}, ...allOperators)
40+
const allVarargOperators = extendVarargOperators(varargOperators, customOperators)
4041

4142
const _stringify = (query: JSONQuery, indent: string, parenthesis = false) =>
4243
isArray(query)
@@ -72,8 +73,14 @@ export const stringify = (query: JSONQuery, options?: JSONQueryStringifyOptions)
7273
const argsStr = args.map((child, index) => {
7374
const childName = child?.[0]
7475
const firstGroup = allOperators.filter((group) => name in group || childName in group)[0]
75-
const noParenthesis =
76-
(childName in firstGroup && index === 0 && name !== childName) || !(name in firstGroup)
76+
77+
const higherPrecedence = !(name in firstGroup)
78+
const selfWithVarargSupport =
79+
name === childName && allVarargOperators.includes(op) && index === 0
80+
const leftWithSamePrecedence =
81+
name !== childName && name in firstGroup && childName in firstGroup && index === 0
82+
83+
const noParenthesis = higherPrecedence || selfWithVarargSupport || leftWithSamePrecedence
7784

7885
return _stringify(child, indent + space, !noParenthesis)
7986
})

test-suite/stringify.test.json

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,40 @@
4444
}
4545
]
4646
},
47+
{
48+
"category": "operator",
49+
"description": "should wrap operators with the same precedence in parenthesis when needed",
50+
"tests": [
51+
{ "input": ["multiply", 2, 3, 4], "output": "2 * 3 * 4" },
52+
{ "input": ["multiply", ["multiply", 2, 3], 4], "output": "2 * 3 * 4" },
53+
{ "input": ["multiply", 2, ["multiply", 3, 4]], "output": "2 * (3 * 4)" },
54+
{ "input": ["divide", 2, 3, 4], "output": "2 / 3 / 4" },
55+
{ "input": ["divide", ["divide", 2, 3], 4], "output": "2 / 3 / 4" },
56+
{ "input": ["divide", 2, ["divide", 3, 4]], "output": "2 / (3 / 4)" },
57+
{ "input": ["divide", ["multiply", 2, 3], 4], "output": "2 * 3 / 4" },
58+
{ "input": ["divide", 2, ["multiply", 3, 4]], "output": "2 / (3 * 4)" },
59+
{ "input": ["divide", 2, 3, ["multiply", 4, 5]], "output": "2 / 3 / (4 * 5)" },
60+
{
61+
"input": ["divide", 2, ["multiply", 3, 4], ["multiply", 5, 6]],
62+
"output": "2 / (3 * 4) / (5 * 6)"
63+
},
64+
{ "input": ["multiply", ["divide", 2, 3], 4], "output": "2 / 3 * 4" },
65+
{ "input": ["mod", 2, 3, 4], "output": "2 % 3 % 4" },
66+
{ "input": ["mod", ["mod", 2, 3], 4], "output": "2 % 3 % 4" },
67+
{ "input": ["mod", 2, ["mod", 3, 4]], "output": "2 % (3 % 4)" },
68+
{ "input": ["mod", ["multiply", 2, 3], 4], "output": "2 * 3 % 4" },
69+
{ "input": ["multiply", ["mod", 2, 3], 4], "output": "2 % 3 * 4" },
70+
{ "input": ["add", 2, 3, 4], "output": "2 + 3 + 4" },
71+
{ "input": ["add", ["add", 2, 3], 4], "output": "2 + 3 + 4" },
72+
{ "input": ["add", 2, ["add", 3, 4]], "output": "2 + (3 + 4)" },
73+
{ "input": ["subtract", 2, 3, 4], "output": "2 - 3 - 4" },
74+
{ "input": ["subtract", ["subtract", 2, 3], 4], "output": "2 - 3 - 4" },
75+
{ "input": ["subtract", 2, ["subtract", 3, 4]], "output": "2 - (3 - 4)" },
76+
{ "input": ["subtract", ["add", 2, 3], 4], "output": "2 + 3 - 4" },
77+
{ "input": ["subtract", 2, ["add", 3, 4]], "output": "2 - (3 + 4)" },
78+
{ "input": ["add", ["subtract", 2, 3], 4], "output": "2 - 3 + 4" }
79+
]
80+
},
4781
{
4882
"category": "operator",
4983
"description": "should wrap operators with differing precedence in parenthesis when needed",
@@ -67,29 +101,10 @@
67101
},
68102
{
69103
"category": "operator",
70-
"description": "should wrap operators with the same precedence in parenthesis when needed (must maintain original JSON structure when parsing again)",
104+
"description": "should wrap operators in without vararg support in parenthesis",
71105
"tests": [
72-
{ "input": ["add", ["add", 2, 3], 4], "output": "(2 + 3) + 4" },
73-
{ "input": ["add", ["subtract", 2, 3], 4], "output": "2 - 3 + 4" },
74-
{ "input": ["add", 2, ["subtract", 3, 4]], "output": "2 + (3 - 4)" },
75-
{ "input": ["add", 2, ["add", 3, 4]], "output": "2 + (3 + 4)" },
76-
{ "input": ["subtract", ["subtract", 2, 3], 4], "output": "(2 - 3) - 4" },
77-
{ "input": ["subtract", ["add", 2, 3], 4], "output": "2 + 3 - 4" },
78-
{ "input": ["subtract", 2, ["add", 3, 4]], "output": "2 - (3 + 4)" },
79-
{ "input": ["subtract", 2, ["subtract", 3, 4]], "output": "2 - (3 - 4)" },
80-
{ "input": ["multiply", ["multiply", 2, 3], 4], "output": "(2 * 3) * 4" },
81-
{ "input": ["multiply", ["divide", 2, 3], 4], "output": "2 / 3 * 4" },
82-
{ "input": ["multiply", ["mod", 2, 3], 4], "output": "2 % 3 * 4" },
83-
{ "input": ["multiply", 2, ["multiply", 3, 4]], "output": "2 * (3 * 4)" },
84-
{ "input": ["divide", ["divide", 2, 3], 4], "output": "(2 / 3) / 4" },
85-
{ "input": ["divide", ["multiply", 2, 3], 4], "output": "2 * 3 / 4" },
86-
{ "input": ["divide", 2, ["divide", 3, 4]], "output": "2 / (3 / 4)" },
87-
{ "input": ["divide", 2, ["multiply", 3, 4]], "output": "2 / (3 * 4)" },
88-
{ "input": ["mod", ["mod", 2, 3], 4], "output": "(2 % 3) % 4" },
89-
{ "input": ["mod", ["multiply", 2, 3], 4], "output": "2 * 3 % 4" },
90-
{ "input": ["mod", ["divide", 2, 3], 4], "output": "2 / 3 % 4" },
91-
{ "input": ["mod", 2, ["multiply", 3, 4]], "output": "2 % (3 * 4)" },
92-
{ "input": ["mod", 2, ["mod", 3, 4]], "output": "2 % (3 % 4)" }
106+
{ "input": ["pow", ["pow", 2, 3], 4], "output": "(2 ^ 3) ^ 4" },
107+
{ "input": ["pow", 2, ["pow", 3, 4]], "output": "2 ^ (3 ^ 4)" }
93108
]
94109
},
95110
{
@@ -108,14 +123,6 @@
108123
{ "input": ["mod", 2, 3, 4], "output": "2 % 3 % 4" }
109124
]
110125
},
111-
{
112-
"category": "operator",
113-
"description": "should wrap operators in parenthesis when needed because of a lack of vararg support",
114-
"tests": [
115-
{ "input": ["pow", ["pow", 2, 3], 4], "output": "(2 ^ 3) ^ 4" },
116-
{ "input": ["pow", 2, ["pow", 3, 4]], "output": "2 ^ (3 ^ 4)" }
117-
]
118-
},
119126
{
120127
"category": "operator",
121128
"description": "should stringify a custom operator",

0 commit comments

Comments
 (0)