@@ -214,7 +214,10 @@ function parseIntegerLength(text, i, len) {
214214 return parseDigitsLength ( text , i , len ) ;
215215}
216216
217- function newEmptyValue ( p ) {
217+ function newEmptyValue ( p , options ) {
218+ if ( options . noEmptyComposite ) {
219+ return [ ] ;
220+ }
218221 if ( typeof p . emptyValue === "function" ) {
219222 return p . emptyValue ( ) ;
220223 }
@@ -600,6 +603,9 @@ function toJsonURLText_Object(options = {}, depth = 0) {
600603 } ) ;
601604
602605 if ( ! options . isImplied || depth > 0 ) {
606+ if ( options . noEmptyComposite && ret === undefined ) {
607+ ret = ":" ;
608+ }
603609 return ret === undefined ? "()" : "(" + ret + ")" ;
604610 }
605611
@@ -898,6 +904,11 @@ class JsonURL {
898904 * than back-to-back single quotes.
899905 * @param {boolean } options.coerceNullToEmptyString Replace
900906 * instances of the null value with an empty string.
907+ * @param {boolean } options.noEmptyComposite Distinguish
908+ * between empty array and empty object. Empty array is back-to-back parens,
909+ * e.g. (). Empty object is two parens with a single colon inside, e.g. (:).
910+ * Note that this prevents the parser from recognizing (:) as an object
911+ * with a single member whose key and value is the unquoted empty string.
901912 * @param {function } options.getMissingValue Provides a value for a
902913 * missing, top-level value.
903914 * @throws SyntaxError if there is a syntax error in the given text
@@ -990,9 +1001,11 @@ class JsonURL {
9901001 let c = text . charCodeAt ( pos ) ;
9911002
9921003 //
993- // literal value and literal value position
1004+ // literal value
1005+ // literal value position
1006+ // empty object bool
9941007 //
995- let lv , lvpos ;
1008+ let lv , lvpos , isEmptyObject ;
9961009
9971010 switch ( stateStack [ stateStack . depth ( ) ] ) {
9981011 case STATE_PAREN :
@@ -1019,12 +1032,12 @@ class JsonURL {
10191032
10201033 if ( stateStack . depth ( true ) === - 1 ) {
10211034 if ( pos === end ) {
1022- return newEmptyValue ( this ) ;
1035+ return newEmptyValue ( this , options ) ;
10231036 }
10241037 throw new SyntaxError ( errorMessage ( ERR_MSG_EXTRACHARS , pos ) ) ;
10251038 }
10261039
1027- valueStack . appendArrayValue ( pos , newEmptyValue ( this ) ) ;
1040+ valueStack . appendArrayValue ( pos , newEmptyValue ( this , options ) ) ;
10281041
10291042 if ( stateStack . depth ( ) === 0 ) {
10301043 if ( skipAmps ) {
@@ -1056,12 +1069,35 @@ class JsonURL {
10561069 }
10571070
10581071 //
1059- // run the limit check before parsing the literal
1072+ // run the limit check
10601073 //
10611074 valueStack . checkValueLimit ( pos ) ;
10621075
1076+ isEmptyObject =
1077+ options . noEmptyComposite &&
1078+ pos == lvpos &&
1079+ pos + 2 <= end &&
1080+ text . charCodeAt ( lvpos ) == CHAR_COLON &&
1081+ text . charCodeAt ( lvpos + 1 ) == CHAR_PAREN_CLOSE ;
1082+
1083+ if ( isEmptyObject ) {
1084+ // skip the colon so that we hit the close paren case in the
1085+ // switch below
1086+ lvpos ++ ;
1087+ }
1088+
1089+ //
1090+ // char immediately following the literal
1091+ //
10631092 c = text . charCodeAt ( lvpos ) ;
1064- lv = this . parseLiteral ( text , pos , lvpos , c === CHAR_COLON , options ) ;
1093+
1094+ if ( ! isEmptyObject ) {
1095+ //
1096+ // parse as a literal if it's a literal
1097+ //
1098+ lv = this . parseLiteral ( text , pos , lvpos , c === CHAR_COLON , options ) ;
1099+ }
1100+
10651101 pos = lvpos ;
10661102
10671103 switch ( c ) {
@@ -1087,10 +1123,14 @@ class JsonURL {
10871123 case CHAR_PAREN_CLOSE :
10881124 pos ++ ;
10891125
1090- //
1091- // single element array
1092- //
1093- valueStack . appendArrayValue ( pos , [ lv ] ) ;
1126+ if ( isEmptyObject ) {
1127+ valueStack . push ( { } ) ;
1128+ } else {
1129+ //
1130+ // single element array
1131+ //
1132+ valueStack . appendArrayValue ( pos , [ lv ] ) ;
1133+ }
10941134
10951135 switch ( stateStack . depth ( true ) ) {
10961136 case - 1 :
@@ -1404,6 +1444,9 @@ class JsonURL {
14041444 * bac-to-back single quotes.
14051445 * @param {boolean } options.coerceNullToEmptyString Replace instances
14061446 * of the null value with an empty string.
1447+ * @param {boolean } options.noEmptyComposite Distinguish
1448+ * between empty array and empty object. Empty array is back-to-back parens,
1449+ * e.g. (). Empty object is two parens with a single colon inside, e.g. (:).
14071450 * @returns {string } JSON->URL text, or undefined if the given value
14081451 * is undefined.
14091452 */
0 commit comments