Skip to content

Commit 568bc05

Browse files
committed
feat: make the parenthesis behavior of parse and stringify simpler and more consistent
1 parent 9f31731 commit 568bc05

File tree

4 files changed

+30
-17
lines changed

4 files changed

+30
-17
lines changed

src/parse.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export function parse(query: string, options?: JSONQueryParseOptions): JSONQuery
3838
return parseParenthesis()
3939
}
4040

41+
const leftParenthesis = query[i] === '('
4142
let left = parseOperator(precedenceLevel - 1)
4243

4344
while (true) {
@@ -52,7 +53,7 @@ export function parse(query: string, options?: JSONQueryParseOptions): JSONQuery
5253
const right = parseOperator(precedenceLevel - 1)
5354

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

src/stringify.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isArray } from './is'
2-
import { extendOperators, extendVarargOperators, operators, varargOperators } from './operators'
2+
import { extendOperators, operators } from './operators'
33
import { unquotedPropertyRegex } from './regexps'
44
import type {
55
JSONPath,
@@ -37,7 +37,6 @@ 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)
4140

4241
const _stringify = (query: JSONQuery, indent: string, parenthesis = false) =>
4342
isArray(query)
@@ -74,16 +73,9 @@ export const stringify = (query: JSONQuery, options?: JSONQueryStringifyOptions)
7473
const childName = child?.[0]
7574
const precedence = allOperators.findIndex((group) => name in group)
7675
const childPrecedence = allOperators.findIndex((group) => childName in group)
76+
const parenthesis = precedence <= childPrecedence && (index > 0 || name === childName)
7777

78-
const higherPrecedence = precedence > childPrecedence
79-
const isFirstArgument = index === 0 // we only support left associative operators
80-
const selfWithVarargSupport =
81-
isFirstArgument && name === childName && allVarargOperators.includes(op)
82-
const notSelf = isFirstArgument && name !== childName
83-
84-
const noParenthesis = higherPrecedence || selfWithVarargSupport || notSelf
85-
86-
return _stringify(child, indent + space, !noParenthesis)
78+
return _stringify(child, indent + space, parenthesis)
8779
})
8880

8981
return join(argsStr, [start, ` ${op} `, end], [start, `\n${indent + space}${op} `, end])

test-suite/parse.test.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,26 @@
230230
{ "input": "2 * (3 + 4)", "output": ["multiply", 2, ["add", 3, 4]] }
231231
]
232232
},
233+
{
234+
"category": "operator",
235+
"description": "should keep the structure based on parenthesis",
236+
"tests": [
237+
{ "input": "(2 * 3) * 4", "output": ["multiply", ["multiply", 2, 3], 4] },
238+
{
239+
"input": "((2 * 3) * 4) * 5",
240+
"output": ["multiply", ["multiply", ["multiply", 2, 3], 4], 5]
241+
},
242+
{
243+
"input": "(2 * 3) * (4 * 5)",
244+
"output": ["multiply", ["multiply", 2, 3], ["multiply", 4, 5]]
245+
},
246+
{ "input": "2 * (3 * 4)", "output": ["multiply", 2, ["multiply", 3, 4]] },
247+
{ "input": "(2 + 3) + 4", "output": ["add", ["add", 2, 3], 4] },
248+
{ "input": "2 + (3 + 4)", "output": ["add", 2, ["add", 3, 4]] },
249+
{ "input": "(2 - 3) - 4", "output": ["subtract", ["subtract", 2, 3], 4] },
250+
{ "input": "2 - (3 - 4)", "output": ["subtract", 2, ["subtract", 3, 4]] }
251+
]
252+
},
233253
{
234254
"category": "operator",
235255
"description": "should throw an error in case of an unknown operator",

test-suite/stringify.test.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@
4949
"description": "should wrap operators with the same precedence in parenthesis when needed",
5050
"tests": [
5151
{ "input": ["multiply", 2, 3, 4], "output": "2 * 3 * 4" },
52-
{ "input": ["multiply", ["multiply", 2, 3], 4], "output": "2 * 3 * 4" },
52+
{ "input": ["multiply", ["multiply", 2, 3], 4], "output": "(2 * 3) * 4" },
5353
{ "input": ["multiply", 2, ["multiply", 3, 4]], "output": "2 * (3 * 4)" },
5454
{ "input": ["divide", 2, 3, 4], "output": "2 / 3 / 4" },
55-
{ "input": ["divide", ["divide", 2, 3], 4], "output": "2 / 3 / 4" },
55+
{ "input": ["divide", ["divide", 2, 3], 4], "output": "(2 / 3) / 4" },
5656
{ "input": ["divide", 2, ["divide", 3, 4]], "output": "2 / (3 / 4)" },
5757
{ "input": ["divide", ["multiply", 2, 3], 4], "output": "2 * 3 / 4" },
5858
{ "input": ["divide", 2, ["multiply", 3, 4]], "output": "2 / (3 * 4)" },
@@ -63,15 +63,15 @@
6363
},
6464
{ "input": ["multiply", ["divide", 2, 3], 4], "output": "2 / 3 * 4" },
6565
{ "input": ["mod", 2, 3, 4], "output": "2 % 3 % 4" },
66-
{ "input": ["mod", ["mod", 2, 3], 4], "output": "2 % 3 % 4" },
66+
{ "input": ["mod", ["mod", 2, 3], 4], "output": "(2 % 3) % 4" },
6767
{ "input": ["mod", 2, ["mod", 3, 4]], "output": "2 % (3 % 4)" },
6868
{ "input": ["mod", ["multiply", 2, 3], 4], "output": "2 * 3 % 4" },
6969
{ "input": ["multiply", ["mod", 2, 3], 4], "output": "2 % 3 * 4" },
7070
{ "input": ["add", 2, 3, 4], "output": "2 + 3 + 4" },
71-
{ "input": ["add", ["add", 2, 3], 4], "output": "2 + 3 + 4" },
71+
{ "input": ["add", ["add", 2, 3], 4], "output": "(2 + 3) + 4" },
7272
{ "input": ["add", 2, ["add", 3, 4]], "output": "2 + (3 + 4)" },
7373
{ "input": ["subtract", 2, 3, 4], "output": "2 - 3 - 4" },
74-
{ "input": ["subtract", ["subtract", 2, 3], 4], "output": "2 - 3 - 4" },
74+
{ "input": ["subtract", ["subtract", 2, 3], 4], "output": "(2 - 3) - 4" },
7575
{ "input": ["subtract", 2, ["subtract", 3, 4]], "output": "2 - (3 - 4)" },
7676
{ "input": ["subtract", ["add", 2, 3], 4], "output": "2 + 3 - 4" },
7777
{ "input": ["subtract", 2, ["add", 3, 4]], "output": "2 - (3 + 4)" },

0 commit comments

Comments
 (0)