diff --git a/pkl-core/src/main/java/org/pkl/core/stdlib/json/ParserNodes.java b/pkl-core/src/main/java/org/pkl/core/stdlib/json/ParserNodes.java index 1321674dc..c5c90c8ed 100644 --- a/pkl-core/src/main/java/org/pkl/core/stdlib/json/ParserNodes.java +++ b/pkl-core/src/main/java/org/pkl/core/stdlib/json/ParserNodes.java @@ -27,6 +27,7 @@ import org.pkl.core.stdlib.ExternalMethod1Node; import org.pkl.core.stdlib.PklConverter; import org.pkl.core.util.EconomicMaps; +import org.pkl.core.util.ErrorMessages; import org.pkl.core.util.Nullable; import org.pkl.core.util.json.JsonHandler; import org.pkl.core.util.json.JsonParser; @@ -172,7 +173,13 @@ public void endObject(@Nullable EconomicMap members) { @Override public void startObjectValue(@Nullable EconomicMap members, String name) { - currPath.push(Identifier.get(name)); + var identifier = Identifier.get(name); + if (!useMapping && identifier == Identifier.DEFAULT) { + // https://github.com/apple/pkl/issues/561 + throw new ParseException( + ErrorMessages.create("jsonParseErrorDynamicPropertyDefault"), getLocation()); + } + currPath.push(identifier); } @Override diff --git a/pkl-core/src/main/java/org/pkl/core/stdlib/yaml/ParserNodes.java b/pkl-core/src/main/java/org/pkl/core/stdlib/yaml/ParserNodes.java index fbe28890b..7dedac4cc 100644 --- a/pkl-core/src/main/java/org/pkl/core/stdlib/yaml/ParserNodes.java +++ b/pkl-core/src/main/java/org/pkl/core/stdlib/yaml/ParserNodes.java @@ -31,6 +31,8 @@ import org.pkl.core.stdlib.ExternalMethod1Node; import org.pkl.core.stdlib.PklConverter; import org.pkl.core.util.EconomicMaps; +import org.pkl.core.util.ErrorMessages; +import org.pkl.core.util.yaml.ParseException; import org.pkl.core.util.yaml.snake.YamlUtils; import org.snakeyaml.engine.v2.api.ConstructNode; import org.snakeyaml.engine.v2.api.Load; @@ -115,6 +117,8 @@ private VmList doParseAll(VmTyped self, String text, String uri) { .build(); } throw exceptionBuilder().evalError("yamlParseError").withHint(e.getMessage()).build(); + } catch (ParseException e) { + throw exceptionBuilder().evalError("yamlParseError").withHint(e.getMessage()).build(); } return builder.build(); @@ -469,6 +473,13 @@ private void addMembers(MappingNode node, VmObject object) { var memberName = convertedKey instanceof String string && !useMapping ? Identifier.get(string) : null; + // https://github.com/apple/pkl/issues/561 + if (memberName == Identifier.DEFAULT) { + throw new ParseException( + ErrorMessages.create("yamlParseErrorDynamicPropertyDefault"), + keyNode.getStartMark().isEmpty() ? null : keyNode.getStartMark().get()); + } + var member = new ObjectMember( sourceSection, diff --git a/pkl-core/src/main/java/org/pkl/core/util/yaml/ParseException.java b/pkl-core/src/main/java/org/pkl/core/util/yaml/ParseException.java new file mode 100644 index 000000000..aa04b6219 --- /dev/null +++ b/pkl-core/src/main/java/org/pkl/core/util/yaml/ParseException.java @@ -0,0 +1,38 @@ +/* + * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pkl.core.util.yaml; + +import org.pkl.core.util.Nullable; +import org.snakeyaml.engine.v2.exceptions.Mark; + +/** An unchecked exception to indicate that an input does not qualify as valid YAML. */ +public final class ParseException extends RuntimeException { + private final @Nullable Mark location; + + public ParseException(String message, @Nullable Mark location) { + super(location == null ? message : message + location); + this.location = location; + } + + /** + * Returns the location at which the error occurred. + * + * @return the error location + */ + public @Nullable Mark getLocation() { + return location; + } +} diff --git a/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties b/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties index 193045e2d..d390a46c9 100644 --- a/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties +++ b/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties @@ -796,6 +796,11 @@ Converter path `{0}` has invalid syntax. jsonParseError=\ Error parsing JSON document. +jsonParseErrorDynamicPropertyDefault=\ +Cannot parse an object with key `"default"` into a `Dynamic`.\ +\n\ +Try parsing into a `Mapping` instead, by setting `useMapping = true` in the `pkl.json#Parser`. + yamlParseError=\ Error parsing YAML document. @@ -804,6 +809,11 @@ Error parsing YAML document: The number of aliases for collection nodes exceeds \n\ To increase the allowed maximum, set `YamlRenderer.maxCollectionAliases`. +yamlParseErrorDynamicPropertyDefault=\ +Cannot parse an object with key `default` into a `Dynamic`.\ +\n\ +Try parsing into a `Mapping` instead, by setting `useMapping = true` in the `pkl.yaml#Parser`. + evaluationTimedOut=\ Evaluation timed out after {0,number,#.##} second(s). diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/api/jsonParser1.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/api/jsonParser1.pkl index b4c7fabfd..dbdb40863 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/api/jsonParser1.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/api/jsonParser1.pkl @@ -66,3 +66,11 @@ res16 = parser.parse(""" // invalid syntax res17 = test.catch(() -> parser.parse("!@#$%")) + +res18 = test.catch(() -> parser.parse(""" + {"default": null} + """)) + +res19 = (parser) { useMapping = true }.parse(""" + {"default": null} + """) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/api/yamlParser2.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/api/yamlParser2.pkl index 043784188..fcf2bcf13 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/api/yamlParser2.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/api/yamlParser2.pkl @@ -91,3 +91,11 @@ res15 = parser.parseAll(""" hobby: surfing ... """) + +res16 = test.catch(() -> parser.parse(""" + {"default": null} + """)) + +res17 = (parser) { useMapping = true }.parse(""" + default: null + """) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/api/jsonParser1.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/api/jsonParser1.pcf index b51ba3d1c..0ac6d7813 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/api/jsonParser1.pcf +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/api/jsonParser1.pcf @@ -48,3 +48,7 @@ res16 { } } res17 = "Error parsing JSON document." +res18 = "Error parsing JSON document." +res19 { + ["default"] = null +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/api/yamlParser2.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/api/yamlParser2.pcf index 66336fde7..933efb6cd 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/api/yamlParser2.pcf +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/api/yamlParser2.pcf @@ -47,3 +47,7 @@ res15 = List(new { }, new { hobby = "surfing" }) +res16 = "Error parsing YAML document." +res17 { + ["default"] = null +}