@@ -6,6 +6,7 @@ import { JSONPathQuery } from "./path";
66import { Token } from "./token" ;
77import { FilterContext , Nothing , SerializationOptions } from "./types" ;
88import { isNumber , isString } from "../types" ;
9+ import { toCanonical } from "./serialize" ;
910
1011/**
1112 * Base class for all filter expressions.
@@ -70,7 +71,7 @@ export class StringLiteral extends FilterExpressionLiteral {
7071 }
7172
7273 public toString ( ) : string {
73- return JSON . stringify ( this . value ) ;
74+ return toCanonical ( this . value ) ;
7475 }
7576}
7677
@@ -117,6 +118,10 @@ export class PrefixExpression extends FilterExpression {
117118 }
118119}
119120
121+ const PRECEDENCE_LOGICAL_OR = 4 ;
122+ const PRECEDENCE_LOGICAL_AND = 5 ;
123+ const PRECEDENCE_PREFIX = 7 ;
124+
120125export class InfixExpression extends FilterExpression {
121126 readonly logical : boolean ;
122127
@@ -159,6 +164,7 @@ export class InfixExpression extends FilterExpression {
159164 }
160165
161166 public toString ( options ?: SerializationOptions ) : string {
167+ // Note that `LogicalExpression.toString()` does not call this.
162168 if ( this . logical ) {
163169 return `(${ this . left . toString ( options ) } ${
164170 this . operator
@@ -183,7 +189,43 @@ export class LogicalExpression extends FilterExpression {
183189 }
184190
185191 public toString ( options ?: SerializationOptions ) : string {
186- return this . expression . toString ( options ) ;
192+ // Minimize parentheses in logical expressions.
193+ function _toString (
194+ expression : FilterExpression ,
195+ parentPrecedence : number ,
196+ ) : string {
197+ let precedence : number ;
198+ let op : string ;
199+ let left : string ;
200+ let right : string ;
201+
202+ if ( expression instanceof InfixExpression ) {
203+ if ( expression . operator === "&&" ) {
204+ precedence = PRECEDENCE_LOGICAL_AND ;
205+ op = "&&" ;
206+ left = _toString ( expression . left , precedence ) ;
207+ right = _toString ( expression . right , precedence ) ;
208+ } else if ( expression . operator === "||" ) {
209+ precedence = PRECEDENCE_LOGICAL_OR ;
210+ op = "||" ;
211+ left = _toString ( expression . left , precedence ) ;
212+ right = _toString ( expression . right , precedence ) ;
213+ } else {
214+ return expression . toString ( options ) ;
215+ }
216+ } else if ( expression instanceof PrefixExpression ) {
217+ const operand = _toString ( expression . right , PRECEDENCE_PREFIX ) ;
218+ const expr = `!${ operand } ` ;
219+ return parentPrecedence > PRECEDENCE_PREFIX ? `(${ expr } )` : expr ;
220+ } else {
221+ return expression . toString ( options ) ;
222+ }
223+
224+ const expr = `${ left } ${ op } ${ right } ` ;
225+ return precedence < parentPrecedence ? `(${ expr } )` : expr ;
226+ }
227+
228+ return _toString ( this . expression , 0 ) ;
187229 }
188230}
189231
0 commit comments