2626import java .util .HashMap ;
2727import java .util .List ;
2828import java .util .Map ;
29+ import java .util .Map .Entry ;
30+ import java .util .stream .Collectors ;
2931import java .util .Set ;
3032import org .rascalmpl .debug .IRascalMonitor ;
3133import org .rascalmpl .exceptions .RuntimeExceptionFactory ;
@@ -132,9 +134,10 @@ public IValue visitReal(Type type) throws IOException {
132134 }
133135
134136 private IValue inferNullValue (Map <Type , IValue > nulls , Type expected ) {
135- return nulls .keySet ().stream ()
136- .sorted ((x ,y ) -> x .compareTo (y )) // smaller types are matched first
137- .filter (superType -> expected .isSubtypeOf (superType )) // remove any type that does not fit
137+ return nulls .entrySet ().stream ()
138+ .map (Entry ::getKey )
139+ .sorted (Type ::compareTo )
140+ .filter (superType -> expected .isSubtypeOf (superType ))
138141 .findFirst () // give the most specific match
139142 .map (t -> nulls .get (t )) // lookup the corresponding null value
140143 .filter (r -> r .getType ().isSubtypeOf (expected )) // the value in the table still has to fit the currently expected type
@@ -157,27 +160,37 @@ public IValue visitString(Type type) throws IOException {
157160 return vf .string (nextString ());
158161 }
159162
160- @ Override
161- public IValue visitTuple (Type type ) throws IOException {
162- if (isNull ()) {
163- return null ;
164- }
163+ @ Override
164+ public IValue visitTuple (Type type ) throws IOException {
165+ if (isNull ()) {
166+ return null ;
167+ }
165168
166169 List <IValue > l = new ArrayList <>();
167170 in .beginArray ();
168171
169172 if (type .hasFieldNames ()) {
170173 for (int i = 0 ; i < type .getArity (); i ++) {
171- l .add (read ( in , type .getFieldType (i )));
174+ l .add (type .getFieldType (i ). accept ( this ));
172175 }
173176 }
174177 else {
175178 for (int i = 0 ; i < type .getArity (); i ++) {
176- l .add (read ( in , type .getFieldType (i )));
179+ l .add (type .getFieldType (i ). accept ( this ));
177180 }
178181 }
179182
180183 in .endArray ();
184+
185+ // filter all the null values
186+ l .forEach (e -> {
187+ if (e == null ) {
188+ throw parseErrorHere ("Tuples can not have null elements." );
189+ }
190+ });
191+
192+ assert type .getArity () == l .size ();
193+
181194 return vf .tuple (l .toArray (new IValue [l .size ()]));
182195 }
183196
@@ -193,6 +206,10 @@ public IValue visitFunction(Type type) throws IOException {
193206
194207 @ Override
195208 public IValue visitSourceLocation (Type type ) throws IOException {
209+ if (isNull ()) {
210+ return inferNullValue (nulls , type );
211+ }
212+
196213 switch (in .peek ()) {
197214 case STRING :
198215 return sourceLocationString ();
@@ -300,7 +317,7 @@ public IValue visitValue(Type type) throws IOException {
300317 case BEGIN_OBJECT :
301318 return visitNode (TF .nodeType ());
302319 case BOOLEAN :
303- return visitBool (TF .nodeType ());
320+ return visitBool (TF .boolType ());
304321 case NAME :
305322 // this would be weird though
306323 return vf .string (nextName ());
@@ -341,8 +358,8 @@ public IValue visitRational(Type type) throws IOException {
341358 switch (in .peek ()) {
342359 case BEGIN_ARRAY :
343360 in .beginArray ();
344- IInteger numA = (IInteger ) read ( in , TF .integerType ());
345- IInteger denomA = (IInteger ) read ( in , TF .integerType ());
361+ IInteger numA = (IInteger ) TF .integerType (). accept ( this );
362+ IInteger denomA = (IInteger ) TF .integerType (). accept ( this );
346363 in .endArray ();
347364 return vf .rational (numA , denomA );
348365 case STRING :
@@ -367,17 +384,23 @@ public IValue visitMap(Type type) throws IOException {
367384 }
368385
369386 while (in .hasNext ()) {
370- w .put (vf .string (nextName ()), read (in , type .getValueType ()));
387+ IString label = vf .string (nextName ());
388+ IValue value = type .getValueType ().accept (this );
389+ if (value != null ) {
390+ w .put (label , value );
391+ }
371392 }
372393 in .endObject ();
373394 return w .done ();
374395 case BEGIN_ARRAY :
375396 in .beginArray ();
376397 while (in .hasNext ()) {
377398 in .beginArray ();
378- IValue key = read (in , type .getKeyType ());
379- IValue value = read (in , type .getValueType ());
380- w .put (key ,value );
399+ IValue key = type .getKeyType ().accept (this );
400+ IValue value = type .getValueType ().accept (this );
401+ if (key != null && value != null ) {
402+ w .put (key ,value );
403+ }
381404 in .endArray ();
382405 }
383406 in .endArray ();
@@ -534,7 +557,7 @@ private IValue visitObjectAsAbstractData(Type type) throws IOException {
534557 if (explicitConstructorNames || explicitDataTypes ) {
535558 String consName = null ;
536559 String typeName = null ; // this one is optional, and the order with cons is not defined.
537- String consLabel = in . nextName ();
560+ String consLabel = nextName ();
538561
539562 // first we read either a cons name or a type name
540563 if (explicitConstructorNames && "_constructor" .equals (consLabel )) {
@@ -547,14 +570,14 @@ else if (explicitDataTypes && "_type".equals(consLabel)) {
547570 // optionally read the second field
548571 if (explicitDataTypes && typeName == null ) {
549572 // we've read a constructor name, but we still need a type name
550- consLabel = in . nextName ();
573+ consLabel = nextName ();
551574 if (explicitDataTypes && "_type" .equals (consLabel )) {
552575 typeName = in .nextString ();
553576 }
554577 }
555578 else if (explicitDataTypes && consName == null ) {
556579 // we've read type name, but we still need a constructor name
557- consLabel = in . nextName ();
580+ consLabel = nextName ();
558581 if (explicitDataTypes && "_constructor" .equals (consLabel )) {
559582 consName = in .nextString ();
560583 }
@@ -599,9 +622,9 @@ else if (alternatives.size() == 0) {
599622 }
600623
601624 while (in .hasNext ()) {
602- String label = in . nextName ();
625+ String label = nextName ();
603626 if (cons .hasField (label )) {
604- IValue val = read ( in , cons .getFieldType (label ));
627+ IValue val = cons .getFieldType (label ). accept ( this );
605628 if (val != null ) {
606629 args [cons .getFieldIndex (label )] = val ;
607630 }
@@ -611,7 +634,7 @@ else if (alternatives.size() == 0) {
611634 }
612635 else if (cons .hasKeywordField (label , store )) {
613636 if (!isNull ()) { // lookahead for null to give default parameters the preference.
614- IValue val = read ( in , store .getKeywordParameterType (cons , label ));
637+ IValue val = store .getKeywordParameterType (cons , label ). accept ( this );
615638 // null can still happen if the nulls map doesn't have a default
616639 if (val != null ) {
617640 // if the value is null we'd use the default value of the defined field in the constructor
@@ -688,7 +711,7 @@ public IValue visitAbstractData(Type type) throws IOException {
688711
689712 @ Override
690713 public IValue visitConstructor (Type type ) throws IOException {
691- return read ( in , type .getAbstractDataType ());
714+ return type .getAbstractDataType (). accept ( this );
692715 }
693716
694717 @ Override
@@ -711,14 +734,14 @@ public IValue visitNode(Type type) throws IOException {
711734 String kwName = nextName ();
712735
713736 if (kwName .equals ("_name" )) {
714- name = ((IString ) read ( in , TF .stringType ())).getValue ();
737+ name = ((IString ) TF .stringType (). accept ( this )).getValue ();
715738 continue ;
716739 }
717740
718741 boolean positioned = kwName .startsWith ("arg" );
719742
720743 if (!isNull ()) { // lookahead for null to give default parameters the preference.
721- IValue val = read ( in , TF .valueType ());
744+ IValue val = TF .valueType (). accept ( this );
722745
723746 if (val != null ) {
724747 // if the value is null we'd use the default value of the defined field in the constructor
@@ -744,6 +767,7 @@ public IValue visitNode(Type type) throws IOException {
744767
745768 IValue [] argArray = args .entrySet ().stream ()
746769 .sorted ((e , f ) -> e .getKey ().compareTo (f .getKey ()))
770+ .filter (e -> e .getValue () != null )
747771 .map (e -> e .getValue ())
748772 .toArray (IValue []::new );
749773
@@ -752,6 +776,10 @@ public IValue visitNode(Type type) throws IOException {
752776
753777 @ Override
754778 public IValue visitNumber (Type type ) throws IOException {
779+ if (isNull ()) {
780+ return inferNullValue (nulls , type );
781+ }
782+
755783 if (in .peek () == JsonToken .BEGIN_ARRAY ) {
756784 return visitRational (type );
757785 }
@@ -802,7 +830,13 @@ public IValue visitList(Type type) throws IOException {
802830 in .beginArray ();
803831 while (in .hasNext ()) {
804832 // here we pass label from the higher context
805- w .append (read (in , type .getElementType ()));
833+ IValue elem = isNull ()
834+ ? inferNullValue (nulls , type .getElementType ())
835+ : type .getElementType ().accept (this );
836+
837+ if (elem != null ) {
838+ w .append (elem );
839+ }
806840 }
807841
808842 in .endArray ();
@@ -818,7 +852,13 @@ public IValue visitSet(Type type) throws IOException {
818852 in .beginArray ();
819853 while (in .hasNext ()) {
820854 // here we pass label from the higher context
821- w .insert (read (in , type .getElementType ()));
855+ IValue elem = isNull ()
856+ ? inferNullValue (nulls , type .getElementType ())
857+ : type .getElementType ().accept (this );
858+
859+ if (elem != null ) {
860+ w .insert (elem );
861+ }
822862 }
823863
824864 in .endArray ();
@@ -972,7 +1012,11 @@ public IValue read(JsonReader in, Type expected) throws IOException {
9721012 var dispatch = new ExpectedTypeDispatcher (in );
9731013
9741014 try {
975- return expected .accept (dispatch );
1015+ var result = expected .accept (dispatch );
1016+ if (result == null ) {
1017+ throw new JsonParseException ("null occurred outside an optionality context and without a registered representation." );
1018+ }
1019+ return result ;
9761020 }
9771021 catch (EOFException | JsonParseException | NumberFormatException | MalformedJsonException | IllegalStateException | NullPointerException e ) {
9781022 throw dispatch .parseErrorHere (e .getMessage ());
0 commit comments