@@ -13,6 +13,7 @@ var tokenize = require("./tokenize"),
1313 Enum = require ( "./enum" ) ,
1414 Service = require ( "./service" ) ,
1515 Method = require ( "./method" ) ,
16+ ReflectionObject = require ( "./object" ) ,
1617 types = require ( "./types" ) ,
1718 util = require ( "./util" ) ;
1819
@@ -26,7 +27,11 @@ var base10Re = /^[1-9][0-9]*$/,
2627 nameRe = / ^ [ a - z A - Z _ ] [ a - z A - Z _ 0 - 9 ] * $ / ,
2728 typeRefRe = / ^ (?: \. ? [ a - z A - Z _ ] [ a - z A - Z _ 0 - 9 ] * ) (?: \. [ a - z A - Z _ ] [ a - z A - Z _ 0 - 9 ] * ) * $ / ,
2829 fqTypeRefRe = / ^ (?: \. [ a - z A - Z _ ] [ a - z A - Z _ 0 - 9 ] * ) + $ / ,
29- featuresRefRe = / f e a t u r e s \. ( [ a - z A - Z _ ] * ) / ;
30+ featuresTypeRefRe = / ^ ( f e a t u r e s ) ( .* ) / ;
31+
32+ var editions2023Defaults = { features : { enum_type : 'OPEN' , field_presence : 'EXPLICIT' , json_format : 'ALLOW' , message_encoding : 'LENGTH_PREFIXED' , repeated_field_encoding : 'PACKED' , utf8_validation : 'VERIFY' } }
33+ var proto2Defaults = { features : { enum_type : 'CLOSED' , field_presence : 'EXPLICIT' , json_format : 'LEGACY_BEST_EFFORT' , message_encoding : 'LENGTH_PREFIXED' , repeated_field_encoding : 'EXPANDED' , utf8_validation : 'NONE' } }
34+ var proto3Defaults = { features : { enum_type : 'OPEN' , field_presence : 'IMPLICIT' , json_format : 'ALLOW' , message_encoding : 'LENGTH_PREFIXED' , repeated_field_encoding : 'PACKED' , utf8_validation : 'VERIFY' } }
3035
3136/**
3237 * Result object returned from {@link parse}.
@@ -269,6 +274,16 @@ function parse(source, root, options) {
269274 // Otherwise the meaning is ambiguous between proto2 and proto3
270275 root . setOption ( "syntax" , syntax ) ;
271276
277+ if ( isProto3 ) {
278+ for ( var key of Object . keys ( proto3Defaults ) ) {
279+ setParsedOption ( root , key , proto3Defaults [ key ] )
280+ }
281+ } else {
282+ for ( var key of Object . keys ( proto2Defaults ) ) {
283+ setParsedOption ( root , key , proto2Defaults [ key ] )
284+ }
285+ }
286+
272287 skip ( ";" ) ;
273288 }
274289
@@ -283,6 +298,9 @@ function parse(source, root, options) {
283298
284299 root . setOption ( "edition" , edition ) ;
285300
301+ for ( var key of Object . keys ( editions2023Defaults ) ) {
302+ setParsedOption ( root , key , editions2023Defaults [ key ] )
303+ }
286304 skip ( ";" ) ;
287305 }
288306
@@ -355,13 +373,17 @@ function parse(source, root, options) {
355373
356374 case "required" :
357375 case "repeated" :
376+ if ( edition )
377+ throw illegal ( token )
358378 parseField ( type , token ) ;
359379 break ;
360380
361381 case "optional" :
362382 /* istanbul ignore if */
363383 if ( isProto3 ) {
364384 parseField ( type , "proto3_optional" ) ;
385+ } else if ( edition ) {
386+ throw illegal ( token ) ;
365387 } else {
366388 parseField ( type , "optional" ) ;
367389 }
@@ -416,6 +438,7 @@ function parse(source, root, options) {
416438 var name = next ( ) ;
417439
418440 /* istanbul ignore if */
441+
419442 if ( ! nameRe . test ( name ) )
420443 throw illegal ( name , "name" ) ;
421444
@@ -605,13 +628,43 @@ function parse(source, root, options) {
605628 dummy . setOption = function ( name , value ) {
606629 if ( this . options === undefined )
607630 this . options = { } ;
631+
608632 this . options [ name ] = value ;
609633 } ;
610- dummy . setFeature = function ( name , value ) {
611- if ( this . features === undefined )
612- this . features = { } ;
613- this . features [ name ] = value ;
614- } ;
634+ dummy . setParsedOption = function ( name , value , propName ) {
635+ if ( ! this . parsedOptions ) {
636+ this . parsedOptions = [ ] ;
637+ }
638+ var parsedOptions = this . parsedOptions ;
639+ if ( propName ) {
640+ // If setting a sub property of an option then try to merge it
641+ // with an existing option
642+ var opt = parsedOptions . find ( function ( opt ) {
643+ return Object . prototype . hasOwnProperty . call ( opt , name ) ;
644+ } ) ;
645+ if ( opt ) {
646+ // If we found an existing option - just merge the property value
647+ // (If it's a feature, will just write over)
648+ var newValue = opt [ name ] ;
649+ util . setProperty ( newValue , propName , value ) ;
650+ } else {
651+ // otherwise, create a new option, set its property and add it to the list
652+ opt = { } ;
653+ opt [ name ] = util . setProperty ( { } , propName , value ) ;
654+ parsedOptions . push ( opt ) ;
655+ }
656+ } else {
657+ // Always create a new option when setting the value of the option itself
658+ var newOpt = { } ;
659+ newOpt [ name ] = value ;
660+ parsedOptions . push ( newOpt ) ;
661+ }
662+
663+ if ( / f e a t u r e s / . test ( name ) ) {
664+ var features = parsedOptions . find ( x => { return x . hasOwnProperty ( "features" ) } ) ;
665+ this . _features = features . features || { } ;
666+ }
667+ }
615668 ifBlock ( dummy , function parseEnumValue_block ( token ) {
616669
617670 /* istanbul ignore else */
@@ -624,48 +677,52 @@ function parse(source, root, options) {
624677 } , function parseEnumValue_line ( ) {
625678 parseInlineOptions ( dummy ) ; // skip
626679 } ) ;
627- parent . add ( token , value , dummy . comment , dummy . options , dummy . features ) ;
680+ parent . add ( token , value , dummy . comment , dummy . parsedOptions ) ;
628681 }
629682
630683 function parseOption ( parent , token ) {
631- // console.log(featuresRefRe.test(token = next()))
632- if ( featuresRefRe . test ( peek ( ) ) ) {
684+ var name ;
685+ var option ;
686+ var optionValue ;
687+ var propName ;
688+ // The two logic branches below are parallel tracks, but with different regexes for the following use cases:
689+ // features expects: option features.abc.amazing_feature = A;
690+ // custom options expects: option (mo_single_msg).nested.value = "x";
691+ if ( featuresTypeRefRe . test ( peek ( ) ) ) {
633692 var token = next ( ) ;
634- var name = token . match ( featuresRefRe ) [ 1 ]
635- skip ( "=" ) ;
636- setFeature ( parent , name , token = next ( ) )
693+ name = token ;
694+ option = token . match ( featuresTypeRefRe ) [ 1 ] ;
695+ var propNameWithPeriod = token . match ( featuresTypeRefRe ) [ 2 ] ;
696+ if ( fqTypeRefRe . test ( propNameWithPeriod ) ) {
697+ propName = propNameWithPeriod . slice ( 1 ) ; //remove '.' before property name
698+ }
637699 } else {
638700 var isCustom = skip ( "(" , true ) ;
639701 if ( ! typeRefRe . test ( token = next ( ) ) )
640702 throw illegal ( token , "name" ) ;
641703
642704
643- var name = token ;
644- var option = name ;
645- var propName ;
705+ name = token ;
706+ option = name ;
646707
647708 if ( isCustom ) {
648709 skip ( ")" ) ;
649710 name = "(" + name + ")" ;
650711 option = name ;
651712 token = peek ( ) ;
652- console . log ( 'in custom?' + token )
653713 if ( fqTypeRefRe . test ( token ) ) {
654714 propName = token . slice ( 1 ) ; //remove '.' before property name
655715 name += token ;
656716 next ( ) ;
657717 }
658718 }
659-
660- console . log ( token )
719+ }
661720 skip ( "=" ) ;
662721 var optionValue = parseOptionValue ( parent , name ) ;
663722 setParsedOption ( parent , option , optionValue , propName ) ;
664- }
665723 }
666724
667725 function parseOptionValue ( parent , name ) {
668- // { a: "foo" b { c: "bar" } }
669726 if ( skip ( "{" , true ) ) {
670727 var objectResult = { } ;
671728
@@ -683,12 +740,9 @@ function parse(source, root, options) {
683740
684741 skip ( ":" , true ) ;
685742
686- if ( peek ( ) === "{" )
743+ if ( peek ( ) === "{" ) {
687744 value = parseOptionValue ( parent , name + "." + token ) ;
688- else if ( peek ( ) === "[" ) {
689- // option (my_option) = {
690- // repeated_value: [ "foo", "bar" ]
691- // };
745+ } else if ( peek ( ) === "[" ) {
692746 value = [ ] ;
693747 var lastValue ;
694748 if ( skip ( "[" , true ) ) {
@@ -732,12 +786,6 @@ function parse(source, root, options) {
732786 parent . setOption ( name , value ) ;
733787 }
734788
735- function setFeature ( parent , name , value ) {
736- if ( parent . setFeature ) {
737- parent . setFeature ( name , value ) ;
738- }
739- }
740-
741789 function setParsedOption ( parent , name , value , propName ) {
742790 if ( parent . setParsedOption )
743791 parent . setParsedOption ( name , value , propName ) ;
@@ -761,8 +809,9 @@ function parse(source, root, options) {
761809
762810 var service = new Service ( token ) ;
763811 ifBlock ( service , function parseService_block ( token ) {
764- if ( parseCommon ( service , token ) )
812+ if ( parseCommon ( service , token ) ) {
765813 return ;
814+ }
766815
767816 /* istanbul ignore else */
768817 if ( token === "rpc" )
@@ -849,7 +898,7 @@ function parse(source, root, options) {
849898
850899 default :
851900 /* istanbul ignore if */
852- if ( ! isProto3 || ! typeRefRe . test ( token ) )
901+ if ( ( ! isProto3 && ! edition ) || ! typeRefRe . test ( token ) )
853902 throw illegal ( token ) ;
854903 push ( token ) ;
855904 parseField ( parent , "optional" , reference ) ;
0 commit comments