Skip to content

Commit 5760161

Browse files
authored
feat: implement support for operator precedence (#5)
1 parent 96c3473 commit 5760161

File tree

7 files changed

+55
-35
lines changed

7 files changed

+55
-35
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"@astrojs/svelte": "7.0.9",
1717
"@fortawesome/free-brands-svg-icons": "6.7.2",
1818
"@fortawesome/free-solid-svg-icons": "6.7.2",
19-
"@jsonquerylang/jsonquery": "4.1.1",
19+
"@jsonquerylang/jsonquery": "5.0.0",
2020
"astro": "5.5.6",
2121
"fracturedjsonjs": "4.0.2",
2222
"rehype-autolink-headings": "7.1.0",

src/components/QuickReference.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ let selectedDoc: ReferenceDoc | undefined = $state()
147147
font-size: var(--font-size-mono);
148148
}
149149
150-
pre code {
151-
background: none;
150+
pre {
151+
margin: 2px;
152152
}
153153
154154
.quick-reference-button {

src/components/data/examples.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export const examples: Example[] = [
3737
{
3838
name: 'example 2',
3939
input: input2,
40-
query: `filter((.city == "New York") and (.age > 30))\n`
40+
query: `filter(.city == "New York" and .age > 30)\n`
4141
},
4242
{
4343
name: 'example 3',

src/components/data/reference.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@
5858
},
5959
{
6060
"name": "Operator",
61-
"syntax": "(left operator right)",
62-
"description": "JSON Query supports all basic <b>operators</b>. When composing multiple operators,\n it is necessary to use parentheses. Operators do not have precedence since\n parentheses are required.",
63-
"examples": ["(.age >= 18)", "(.age >= 18) and (.age <= 65))"],
61+
"syntax": "left operator right",
62+
"description": "JSON Query supports all basic <b>operators</b>. Operators must have both a left and right hand side. To override the default precedence, an operator can be wrapped in parentheses <code>(...)</code>.",
63+
"examples": [".age >= 18", "filter(.age >= 18 and .age <= 65)"],
6464
"documentation": { "title": "Operators", "urlAnchor": "operators" },
6565
"references": [
6666
{ "title": "equal", "urlAnchor": "eq", "syntax": "a == b" },

src/content/documentation.md

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ The following table gives an overview of the JSON query Text Format:
4646
| Type | Syntax | Example |
4747
|-------------------------|------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|
4848
| [Function](#functions) | `name(argument1, argument2, ...)` | `sort(.age, "asc")` |
49-
| [Operator](#operators) | `(left operator right)` | `filter(.age >= 18)` |
49+
| [Operator](#operators) | `left operator right` | `filter(.age >= 18)` |
5050
| [Pipe](#pipes) | <code>query1 &#124; query2 &#124; ...</code> | <code>sort(.age) &#124; pick(.name, .age)</code> |
5151
| [Object](#objects) | `{ prop1: query1, prop2: query2, ... }` | `{ names: map(.name), total: sum() }` |
5252
| [Array](#arrays) | `[ item1, item2, ... ]` | `[ "New York", "Atlanta" ]` |
@@ -94,16 +94,16 @@ See page [Function reference](/reference) for a detailed overview of all availab
9494

9595
### Operators
9696

97-
JSON Query supports all basic operators. Operators must be wrapped in parentheses `(...)`, must have both a left and right hand side, and do not have precedence since parentheses are required. The syntax is:
97+
JSON Query supports all basic operators. Operators must have both a left and right hand side. To override the default precedence, an operator can be wrapped in parentheses `(...)`. The syntax is:
9898

9999
```text
100-
(left operator right)
100+
left operator right
101101
```
102102

103103
The following example tests whether a property `age` is greater than or equal to `18`:
104104

105105
```text
106-
(.age >= 18)
106+
.age >= 18
107107
```
108108

109109
Operators are for example used to specify filter conditions:
@@ -112,12 +112,27 @@ Operators are for example used to specify filter conditions:
112112
filter(.age >= 18)
113113
```
114114

115-
When composing multiple operators, it is necessary to use parentheses:
115+
When using multiple operators, they will be evaluated according to their precedence (highest first):
116116

117117
```text
118-
filter((.age >= 18) and (.age <= 65))
118+
filter(.age >= 18 and .age <= 65)
119119
```
120120

121+
Note that some operators, like `and`, `or`, `+`, and `-`, support more than two values and are evaluated left-to-right, like `2 + 3 + 4`. Others, like `^` and `==`, do not support more than two values. If needed, it is always possible to use parenthesis, like `(2 ^ 3) ^ 4`.
122+
123+
The operators have the following precedence, from highest to lowest:
124+
125+
| Precedence | Associativity | Operators |
126+
|-----------------------------|---------------|--------------------------------------|
127+
| 8: exponentiation | n/a | `^` |
128+
| 7: multiplicative operators | left-to-right | `*`, `/`, `%` |
129+
| 6: additive operators | left-to-right | `+`, `-` |
130+
| 5: relational operators | n/a | `>`, `>=`, `<`, `<=`, `in`, `not in` |
131+
| 4: equality operators | n/a | `==`, `!=` |
132+
| 3: and | left-to-right | `and` |
133+
| 2: or | left-to-right | `or` |
134+
| 1: pipe | left-to-right | `\|` |
135+
121136
See page [Function reference](/reference) for a detailed overview of all available functions and operators.
122137

123138
### Pipes

src/content/reference.md

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ jsonquery(data, 'filter((.age > 30) and (.address.city == "New York"))')
173173

174174
## sort
175175

176-
Sort a list with objects or values.
176+
Sort a list with objects or values. The function first orders values by type: `boolean`, `number`, `string`, and other types. Strings are compared alphabetically and case-sensitive. Objects and arrays are not re-ordered.
177177

178178
```text
179179
sort()
@@ -559,7 +559,7 @@ jsonquery(events, 'map(substring(.time, 0, 10))')
559559

560560
## uniq
561561

562-
Create a copy of an array where all duplicates are removed.
562+
Create a copy of an array where all duplicates are removed. Values are compared using the `eq` operator, which does a deep strict equal comparison.
563563

564564
```text
565565
uniq()
@@ -636,7 +636,7 @@ jsonquery("hello", 'size()') // 5
636636

637637
## sum
638638

639-
Calculate the sum of all values in an array.
639+
Calculate the sum of all values in an array. The function return `0` in case of an empty array.
640640

641641
```text
642642
sum()
@@ -651,7 +651,7 @@ jsonquery([2.4, 5.7], 'sum()') // 8.1
651651

652652
## min
653653

654-
Return the minimum of the values in an array.
654+
Return the minimum of the values in an array. The function returns `null` in case of an empty array.
655655

656656
```text
657657
min()
@@ -666,7 +666,7 @@ jsonquery([5, 7, 3], 'min()') // 3
666666

667667
## max
668668

669-
Return the maximum of the values in an array.
669+
Return the maximum of the values in an array. The function returns `null` in case of an empty array.
670670

671671
```text
672672
max()
@@ -681,7 +681,7 @@ jsonquery([5, 7, 3], 'max()') // 7
681681

682682
## prod
683683

684-
Calculate the product of the values in an array.
684+
Calculate the product of the values in an array. The function throws an error in case of an empty array.
685685

686686
```text
687687
prod()
@@ -696,7 +696,7 @@ jsonquery([2, 3, 2, 7, 1, 1], 'prod()') // 84
696696

697697
## average
698698

699-
Calculate the average of the values in an array.
699+
Calculate the average of the values in an array. The function throws an error in case of an empty array.
700700

701701
```text
702702
average()
@@ -711,7 +711,7 @@ jsonquery([2, 3, 2, 7, 1], 'average()') // 3
711711

712712
## eq (`==`)
713713

714-
Test whether two values are strictly equal. This will consider a string `"2"` and a number `2` to be _not_ equal for example since their data type differs.
714+
Test whether two values are deep strict equal. This will consider a string `"2"` and a number `2` to be _not_ equal for example, since their data type differs. Objects and arrays are compared recursively, so `{"id":1,"name":"Joe"}` and `{"name":"Joe","id":1}` are deep equal for example.
715715

716716
```text
717717
a == b
@@ -741,7 +741,8 @@ jsonquery({ a: 2 }, 'eq(.a, 2)') // true
741741

742742
## gt (`>`)
743743

744-
Test whether `a` is greater than `b`.
744+
Test whether `a` is greater than `b`. The operator supports comparing two numbers, two strings, or two booleans. In case of unsupported data types or mixed data types, the function returns `false.
745+
745746

746747
```text
747748
a > b
@@ -765,7 +766,7 @@ jsonquery(data, 'filter(.age > 18)')
765766

766767
## gte (`>=`)
767768

768-
Test whether `a` is greater than or equal to `b`.
769+
Test whether `a` is greater than or equal to `b`. The operator supports comparing two numbers, two strings, or two booleans. In case of unsupported data types or mixed data types, the function returns `false.
769770

770771
```text
771772
a >= b
@@ -790,7 +791,7 @@ jsonquery(data, 'filter(.age >= 18)')
790791

791792
## lt (`<`)
792793

793-
Test whether `a` is less than `b`.
794+
Test whether `a` is less than `b`. The operator supports comparing two numbers, two strings, or two booleans. In case of unsupported data types or mixed data types, the function returns `false.
794795

795796
```text
796797
a < b
@@ -814,7 +815,7 @@ jsonquery(data, 'filter(.age < 18)')
814815

815816
## lte (`<=`)
816817

817-
Test whether `a` is less than or equal to `b`.
818+
Test whether `a` is less than or equal to `b`. The operator supports comparing two numbers, two strings, or two booleans. In case of unsupported data types or mixed data types, the function returns `false.
818819

819820
```text
820821
a <= b
@@ -839,7 +840,7 @@ jsonquery(data, 'filter(.age <= 18)')
839840

840841
## ne (`!=`)
841842

842-
Test whether two values are not equal. This is the opposite of the strict equal function `eq`. Two values are considered unequal when their data type differs (for example one is a string and another is a number), or when the value itself is different. For example a string `"2"` and a number `2` are considered unequal, even though their mathematical value is equal.
843+
Test whether two values are not deep strict equal. This is the opposite of the strict equal function `eq`. Two values are considered unequal when their data type differs (for example one is a string and another is a number), or when the value itself is different. For example a string `"2"` and a number `2` are considered unequal, even though their mathematical value is equal. Objects and arrays are compared recursively, so `{"id":1,"name":"Joe"}` and `{"name":"Joe","id":1}` are deep equal for example.
843844

844845
```text
845846
a != b
@@ -868,11 +869,13 @@ jsonquery({ a: 2 }, 'a != "2"') // true (since not strictly equal)
868869

869870
## and
870871

871-
Test whether both values are truthy. A non-truthy value is any of `false`, `0`, `""`, `null`, or `undefined`.
872+
Test whether two or more values are truthy. A non-truthy value is any of `false`, `0`, `""`, `null`, or `undefined`. The function throws an error in case of zero arguments.
872873

873874
```text
874875
a and b
876+
a and b and c and ...
875877
and(a, b)
878+
and(a, b, c, ...)
876879
```
877880

878881
Examples:
@@ -892,11 +895,13 @@ jsonquery(data, 'filter((.name == "Chris") and (.age == 16))')
892895

893896
## or
894897

895-
Test whether one or both values are truthy. A non-truthy value is any of `false`, `0`, `""`, `null`, or `undefined`.
898+
Test whether at least one of the values is truthy. A non-truthy value is any of `false`, `0`, `""`, `null`, or `undefined`. The function throws an error in case of zero arguments.
896899

897900
```text
898901
a or b
902+
a or b or c or ...
899903
or(a, b)
904+
or(a, b, c, ...)
900905
```
901906

902907
Examples:
@@ -997,7 +1002,7 @@ jsonquery(data, 'if(.kid.age >= .minAge, .messageOk, .messageFail)')
9971002

9981003
## in
9991004

1000-
Test whether the search value is one of the values of the provided list.
1005+
Test whether the search value is one of the values of the provided list. Values are compared using the `eq` operator, which does a deep strict equal comparison.
10011006

10021007
```text
10031008
searchValue in values
@@ -1022,7 +1027,7 @@ jsonquery(data, 'filter(.age in [16, 18])')
10221027

10231028
## not in
10241029

1025-
Test whether the search value is _not_ one of the values of the provided list.
1030+
Test whether the search value is _not_ one of the values of the provided list. Values are compared using the `eq` operator, which does a deep strict equal comparison.
10261031

10271032
```text
10281033
searchValue not in values
@@ -1155,7 +1160,7 @@ jsonquery(data, '.a / .b') // 3
11551160

11561161
## pow (`^`)
11571162

1158-
Calculate the exponent. Returns the result of raising `a` to the power of `b`, like `a^b`
1163+
Calculate the exponent. Returns the result of raising `a` to the power of `b`, like `a ^ b`. The `^` operator does not support more than two values, so if you need to calculate a chain of multiple exponents you'll have to use parenthesis, like `(a ^ b) ^ c`.
11591164

11601165
```text
11611166
a ^ b

0 commit comments

Comments
 (0)