From f62cca3805266642d45a3ea10e8556ee038d0797 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Tue, 10 Dec 2024 14:23:30 +0100 Subject: [PATCH] resolved more merge problems --- .../lang/json/internal/JsonValueReader.java | 121 +++++++++++++----- .../tests/library/lang/json/JSONIOTests.rsc | 41 +----- 2 files changed, 96 insertions(+), 66 deletions(-) diff --git a/src/org/rascalmpl/library/lang/json/internal/JsonValueReader.java b/src/org/rascalmpl/library/lang/json/internal/JsonValueReader.java index fb7c7b5e43..7f7a09c2b7 100644 --- a/src/org/rascalmpl/library/lang/json/internal/JsonValueReader.java +++ b/src/org/rascalmpl/library/lang/json/internal/JsonValueReader.java @@ -544,26 +544,84 @@ private IValue visitStringAsAbstractData(Type type) throws IOException { * @throws IOException */ private IValue visitObjectAsAbstractData(Type type) throws IOException { - Set alternatives = store.lookupAlternatives(type); - if (alternatives.size() > 1) { - monitor.warning("selecting arbitrary constructor for " + type, vf.sourceLocation(in.getPath())); - } - Type cons = alternatives.iterator().next(); - + Set alternatives = null; + in.beginObject(); int startPos = getPos(); int startLine = getLine(); int startCol = getCol(); + // use explicit information in the JSON to select and filter constructors from the TypeStore + // we expect always to have the field _constructor before _type. + if (explicitConstructorNames || explicitDataTypes) { + String consName = null; + String typeName = null; // this one is optional, and the order with cons is not defined. + String consLabel = in.nextName(); + + // first we read either a cons name or a type name + if (explicitConstructorNames && "_constructor".equals(consLabel)) { + consName = in.nextString(); + } + else if (explicitDataTypes && "_type".equals(consLabel)) { + typeName = in.nextString(); + } + + // optionally read the second field + if (explicitDataTypes && typeName == null) { + // we've read a constructor name, but we still need a type name + consLabel = in.nextName(); + if (explicitDataTypes && "_type".equals(consLabel)) { + typeName = in.nextString(); + } + } + else if (explicitDataTypes && consName == null) { + // we've read type name, but we still need a constructor name + consLabel = in.nextName(); + if (explicitDataTypes && "_constructor".equals(consLabel)) { + consName = in.nextString(); + } + } + + if (explicitDataTypes && typeName == null) { + throw parseErrorHere("Missing a _type field: " + in.getPath()); + } + else if (explicitConstructorNames && consName == null) { + throw parseErrorHere("Missing a _constructor field: " + in.getPath()); + } + + if (typeName != null && consName != null) { + // first focus on the given type name + var dataType = TF.abstractDataType(store, typeName); + alternatives = store.lookupConstructor(dataType, consName); + } + else { + // we only have a constructor name + // lookup over all data types by constructor name + alternatives = store.lookupConstructors(consName); + } + } + else { + alternatives = store.lookupAlternatives(type); + } + + if (alternatives.size() > 1) { + monitor.warning("selecting arbitrary constructor for " + type, vf.sourceLocation(in.getPath())); + } + else if (alternatives.size() == 0) { + throw parseErrorHere("No fitting constructor found for " + in.getPath()); + } + + Type cons = alternatives.iterator().next(); + IValue[] args = new IValue[cons.getArity()]; Map kwParams = new HashMap<>(); - + if (!cons.hasFieldNames() && cons.getArity() != 0) { throw parseErrorHere("For the object encoding constructors must have field names " + in.getPath()); } - + while (in.hasNext()) { - String label = nextName(); + String label = in.nextName(); if (cons.hasField(label)) { IValue val = read(in, cons.getFieldType(label)); if (val != null) { @@ -578,29 +636,35 @@ else if (cons.hasKeywordField(label, store)) { IValue val = read(in, store.getKeywordParameterType(cons, label)); // null can still happen if the nulls map doesn't have a default if (val != null) { - // if the value is null we'd use the default value of the defined field in the constructor - kwParams.put(label, val); + // if the value is null we'd use the default value of the defined field in the constructor + kwParams.put(label, val); } } - } - else { // its a normal arg, pass its label to the child - if (!explicitConstructorNames && "_constructor".equals(label)) { - // ignore additional _constructor fields. - in.nextString(); // skip the constructor value - continue; - } - else if (!explicitDataTypes && "_type".equals(label)) { - // ignore additional _type fields. - in.nextString(); // skip the type value - continue; + else { + var nullValue = inferNullValue(nulls, cons.getAbstractDataType()); + if (nullValue != null) { + kwParams.put(label, nullValue); } - else { - // field label does not match data type definition + } + } + else { // its a normal arg, pass its label to the child + if (!explicitConstructorNames && "_constructor".equals(label)) { + // ignore additional _constructor fields. + in.nextString(); // skip the constructor value + continue; + } + else if (!explicitDataTypes && "_type".equals(label)) { + // ignore additional _type fields. + in.nextString(); // skip the type value + continue; + } + else { + // field label does not match data type definition throw parseErrorHere("Unknown field " + label + ":" + in.getPath()); - } + } } - } - + } + in.endObject(); int endPos = getPos(); int endLine = getLine(); @@ -612,11 +676,10 @@ else if (!explicitDataTypes && "_type".equals(label)) { } } - if (originTracking) { + if (src != null) { kwParams.put(kwParams.containsKey("src") ? "rascal-src" : "src", vf.sourceLocation(src, startPos, endPos - startPos + 1, startLine, endLine, startCol, endCol + 1)); } - return vf.constructor(cons, args, kwParams); } diff --git a/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc index 3f37866409..f543e5ef0b 100644 --- a/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc +++ b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc @@ -91,8 +91,8 @@ test bool dealWithNull() { assert parseJSON(#map[str,Maybe[str]], "{\"bla\": \"foo\"}") == ("bla":just("foo")); // keyword parameters and null - assert parseJSON(#Cons, "{\"bla\": \"foo\"}") == cons(bla="foo"); - assert parseJSON(#Cons, "{\"bla\": null}") == cons(); + assert cons(bla="foo") := parseJSON(#Cons, "{\"bla\": \"foo\"}"); + assert cons() := parseJSON(#Cons, "{\"bla\": null}"); return true; } @@ -147,44 +147,11 @@ test bool explicitDataTypes() { assert json == "{\"_constructor\":\"data4\",\"_type\":\"DATA4\",\"e\":{\"_constructor\":\"z\",\"_type\":\"Enum\"}}"; // _constructor and _type must be the first fields - assert parseJSON(#DATA4, json, explicitDataTypes=true) == tmp; + assert tmp := parseJSON(#DATA4, json, explicitDataTypes=true) ; // _type and _constructor may appear in a different order flippedJson = "{\"_type\":\"DATA4\",\"_constructor\":\"data4\",\"e\":{\"_constructor\":\"z\",\"_type\":\"Enum\"}}"; - assert parseJSON(#DATA4, flippedJson, explicitDataTypes=true) == tmp; - - // here we can't be sure to get z() back, but we will get some Enum - assert data4(e=Enum _) := parseJSON(#DATA4, json, explicitDataTypes=false); - - return true; -} - -test bool explicitConstructorNames() { - example = data4(e=z()); - json = asJSON(example, explicitConstructorNames=true); - - assert json == "{\"_constructor\":\"data4\",\"e\":{\"_constructor\":\"z\"}}"; - - assert parseJSON(#DATA4, json, explicitConstructorNames=true) == example; - - // here we can't be sure to get z() back, but we will get some Enum - assert data4(e=Enum _) := parseJSON(#DATA4, json, explicitConstructorNames=false); - - return true; -} - -test bool explicitDataTypes() { - example = data4(e=z()); - json = asJSON(example, explicitDataTypes=true); - - assert json == "{\"_constructor\":\"data4\",\"_type\":\"DATA4\",\"e\":{\"_constructor\":\"z\",\"_type\":\"Enum\"}}"; - - // _constructor and _type must be the first fields - assert parseJSON(#DATA4, json, explicitDataTypes=true) == example; - - // _type and _constructor may appear in a different order - flippedJson = "{\"_type\":\"DATA4\",\"_constructor\":\"data4\",\"e\":{\"_constructor\":\"z\",\"_type\":\"Enum\"}}"; - assert parseJSON(#DATA4, flippedJson, explicitDataTypes=true) == example; + assert tmp := parseJSON(#DATA4, flippedJson, explicitDataTypes=true); // here we can't be sure to get z() back, but we will get some Enum assert data4(e=Enum _) := parseJSON(#DATA4, json, explicitDataTypes=false);