mirror of
https://github.com/apple/pkl.git
synced 2026-06-12 16:44:33 +02:00
SPICE-0024: Annotation converters (#1333)
This enables defining declarative key and/or value transformations in cases where neither `Class`- nor path-based converters can be applied gracefully. It is also the only way to express transforming the resulting property names in `Typed` objects without applying a converter to the entire containing type, which is cumbersome at best. SPICE: https://github.com/apple/pkl-evolution/pull/26
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -54,6 +54,44 @@ public final class ClassProperty extends ClassMember {
|
|||||||
this.initializer = initializer;
|
this.initializer = initializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<VmTyped> getAllAnnotations(boolean ascending) {
|
||||||
|
var annotations = new ArrayList<VmTyped>();
|
||||||
|
|
||||||
|
if (ascending) {
|
||||||
|
for (var clazz = getDeclaringClass(); clazz != null; clazz = clazz.getSuperclass()) {
|
||||||
|
var p = clazz.getDeclaredProperty(getName());
|
||||||
|
if (p != null) {
|
||||||
|
annotations.addAll(p.getAnnotations());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
doGetAllAnnotationsDescending(getDeclaringClass(), annotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
return annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doGetAllAnnotationsDescending(VmClass clazz, List<VmTyped> annotations) {
|
||||||
|
if (clazz.getSuperclass() != null) {
|
||||||
|
doGetAllAnnotationsDescending(clazz.getSuperclass(), annotations);
|
||||||
|
}
|
||||||
|
var p = clazz.getDeclaredProperty(getName());
|
||||||
|
if (p != null) {
|
||||||
|
annotations.addAll(p.getAnnotations());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmSet getAllModifierMirrors() {
|
||||||
|
var mods = 0;
|
||||||
|
for (var clazz = getDeclaringClass(); clazz != null; clazz = clazz.getSuperclass()) {
|
||||||
|
var parent = clazz.getDeclaredProperty(getName());
|
||||||
|
if (parent != null) {
|
||||||
|
mods |= parent.getModifiers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return VmModifier.getMirrors(mods, false);
|
||||||
|
}
|
||||||
|
|
||||||
public @Nullable PropertyTypeNode getTypeNode() {
|
public @Nullable PropertyTypeNode getTypeNode() {
|
||||||
return typeNode;
|
return typeNode;
|
||||||
}
|
}
|
||||||
@@ -68,44 +106,8 @@ public final class ClassProperty extends ClassMember {
|
|||||||
return name.toString();
|
return name.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Mirror {
|
public VmTyped getMirror() {
|
||||||
private final ClassProperty prop;
|
return MirrorFactories.propertyFactory.create(this);
|
||||||
private final VmClass clazz;
|
|
||||||
|
|
||||||
Mirror(ClassProperty prop, VmClass clazz) {
|
|
||||||
this.prop = prop;
|
|
||||||
this.clazz = clazz;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClassProperty getProperty() {
|
|
||||||
return prop;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<VmTyped> getAllAnnotations() {
|
|
||||||
var annotations = new ArrayList<VmTyped>();
|
|
||||||
for (var klazz = clazz; klazz != null; klazz = klazz.getSuperclass()) {
|
|
||||||
var p = klazz.getDeclaredProperty(prop.getName());
|
|
||||||
if (p != null) {
|
|
||||||
annotations.addAll(p.getAnnotations());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return annotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VmSet getAllModifierMirrors() {
|
|
||||||
var mods = 0;
|
|
||||||
for (var klazz = clazz; klazz != null; klazz = klazz.getSuperclass()) {
|
|
||||||
var parent = klazz.getDeclaredProperty(prop.getName());
|
|
||||||
if (parent != null) {
|
|
||||||
mods |= parent.getModifiers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return VmModifier.getMirrors(mods, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public VmTyped getMirror(VmClass clazz) {
|
|
||||||
return MirrorFactories.propertyFactory.create(new Mirror(this, clazz));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VmSet getModifierMirrors() {
|
public VmSet getModifierMirrors() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -195,6 +195,10 @@ public final class BaseModule extends StdLibModule {
|
|||||||
return AnnotationClass.instance;
|
return AnnotationClass.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static VmClass getConvertPropertyClass() {
|
||||||
|
return ConvertPropertyClass.instance;
|
||||||
|
}
|
||||||
|
|
||||||
public static VmClass getDeprecatedClass() {
|
public static VmClass getDeprecatedClass() {
|
||||||
return DeprecatedClass.instance;
|
return DeprecatedClass.instance;
|
||||||
}
|
}
|
||||||
@@ -343,6 +347,10 @@ public final class BaseModule extends StdLibModule {
|
|||||||
static final VmClass instance = loadClass("Annotation");
|
static final VmClass instance = loadClass("Annotation");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class ConvertPropertyClass {
|
||||||
|
static final VmClass instance = loadClass("ConvertProperty");
|
||||||
|
}
|
||||||
|
|
||||||
private static final class DeprecatedClass {
|
private static final class DeprecatedClass {
|
||||||
static final VmClass instance = loadClass("Deprecated");
|
static final VmClass instance = loadClass("Deprecated");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -50,7 +50,7 @@ public final class Identifier implements Comparable<Identifier> {
|
|||||||
// members of pkl.base#Listing and pkl.base#Mapping
|
// members of pkl.base#Listing and pkl.base#Mapping
|
||||||
public static final Identifier DEFAULT = get("default");
|
public static final Identifier DEFAULT = get("default");
|
||||||
|
|
||||||
// members of pkl.base#ValueRenderer subclasses
|
// members of pkl.base#BaseValueRenderer subclasses
|
||||||
public static final Identifier MODE = get("mode");
|
public static final Identifier MODE = get("mode");
|
||||||
public static final Identifier INDENT = get("indent");
|
public static final Identifier INDENT = get("indent");
|
||||||
public static final Identifier INDENT_WIDTH = get("indentWidth");
|
public static final Identifier INDENT_WIDTH = get("indentWidth");
|
||||||
@@ -62,8 +62,12 @@ public final class Identifier implements Comparable<Identifier> {
|
|||||||
public static final Identifier ROOT_ELEMENT_NAME = get("rootElementName");
|
public static final Identifier ROOT_ELEMENT_NAME = get("rootElementName");
|
||||||
public static final Identifier ROOT_ELEMENT_ATTRIBUTES = get("rootElementAttributes");
|
public static final Identifier ROOT_ELEMENT_ATTRIBUTES = get("rootElementAttributes");
|
||||||
public static final Identifier CONVERTERS = get("converters");
|
public static final Identifier CONVERTERS = get("converters");
|
||||||
|
public static final Identifier CONVERT_PROPERTY_TRANSFORMERS = get("convertPropertyTransformers");
|
||||||
public static final Identifier USE_MAPPING = get("useMapping");
|
public static final Identifier USE_MAPPING = get("useMapping");
|
||||||
|
|
||||||
|
// members of pkl.base#ConvertProperty
|
||||||
|
public static final Identifier RENDER = get("render");
|
||||||
|
|
||||||
// members of pkl.base#RegexMatch
|
// members of pkl.base#RegexMatch
|
||||||
public static final Identifier VALUE = get("value");
|
public static final Identifier VALUE = get("value");
|
||||||
|
|
||||||
@@ -143,9 +147,6 @@ public final class Identifier implements Comparable<Identifier> {
|
|||||||
// members of pkl.yaml
|
// members of pkl.yaml
|
||||||
public static final Identifier MAX_COLLECTION_ALIASES = get("maxCollectionAliases");
|
public static final Identifier MAX_COLLECTION_ALIASES = get("maxCollectionAliases");
|
||||||
|
|
||||||
// members of pkl.encoding
|
|
||||||
public static final Identifier IMPORTS = get("imports");
|
|
||||||
|
|
||||||
// common in lambdas etc
|
// common in lambdas etc
|
||||||
public static final Identifier IT = get("it");
|
public static final Identifier IT = get("it");
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public final class MirrorFactories {
|
|||||||
public static final VmObjectFactory<VmTypeAlias> typeAliasFactory =
|
public static final VmObjectFactory<VmTypeAlias> typeAliasFactory =
|
||||||
new VmObjectFactory<>(ReflectModule::getTypeAliasClass);
|
new VmObjectFactory<>(ReflectModule::getTypeAliasClass);
|
||||||
|
|
||||||
public static final VmObjectFactory<ClassProperty.Mirror> propertyFactory =
|
public static final VmObjectFactory<ClassProperty> propertyFactory =
|
||||||
new VmObjectFactory<>(ReflectModule::getPropertyClass);
|
new VmObjectFactory<>(ReflectModule::getPropertyClass);
|
||||||
|
|
||||||
public static final VmObjectFactory<ClassMethod> methodFactory =
|
public static final VmObjectFactory<ClassMethod> methodFactory =
|
||||||
@@ -166,33 +166,29 @@ public final class MirrorFactories {
|
|||||||
|
|
||||||
propertyFactory
|
propertyFactory
|
||||||
.addTypedProperty(
|
.addTypedProperty(
|
||||||
"location",
|
"location", property -> sourceLocationFactory.create(property.getHeaderSection()))
|
||||||
property -> sourceLocationFactory.create(property.getProperty().getHeaderSection()))
|
|
||||||
.addProperty(
|
.addProperty(
|
||||||
"docComment",
|
"docComment",
|
||||||
property ->
|
property -> VmNull.lift(VmUtils.exportDocComment(property.getDocComment())))
|
||||||
VmNull.lift(VmUtils.exportDocComment(property.getProperty().getDocComment())))
|
.addListProperty("annotations", property -> VmList.create(property.getAnnotations()))
|
||||||
.addListProperty(
|
.addListProperty(
|
||||||
"annotations", property -> VmList.create(property.getProperty().getAnnotations()))
|
"allAnnotations", property -> VmList.create(property.getAllAnnotations(true)))
|
||||||
.addListProperty("allAnnotations", property -> VmList.create(property.getAllAnnotations()))
|
.addSetProperty("modifiers", ClassProperty::getModifierMirrors)
|
||||||
.addSetProperty("modifiers", property -> property.getProperty().getModifierMirrors())
|
.addSetProperty("allModifiers", ClassProperty::getAllModifierMirrors)
|
||||||
.addSetProperty("allModifiers", ClassProperty.Mirror::getAllModifierMirrors)
|
.addStringProperty("name", property -> property.getName().toString())
|
||||||
.addStringProperty("name", property -> property.getProperty().getName().toString())
|
.addTypedProperty("type", ClassProperty::getTypeMirror)
|
||||||
.addTypedProperty("type", property -> property.getProperty().getTypeMirror())
|
|
||||||
.addProperty(
|
.addProperty(
|
||||||
"defaultValue",
|
"defaultValue",
|
||||||
property ->
|
property ->
|
||||||
property.getProperty().isAbstract()
|
property.isAbstract()
|
||||||
|| property.getProperty().isExternal()
|
|| property.isExternal()
|
||||||
|| property
|
|| property
|
||||||
.getProperty()
|
|
||||||
.getInitializer()
|
.getInitializer()
|
||||||
.isUndefined(VmUtils.createEmptyMaterializedFrame())
|
.isUndefined(VmUtils.createEmptyMaterializedFrame())
|
||||||
? VmNull.withoutDefault()
|
? VmNull.withoutDefault()
|
||||||
:
|
:
|
||||||
// get default from prototype because it's cached there
|
// get default from prototype because it's cached there
|
||||||
VmUtils.readMember(
|
VmUtils.readMember(property.getOwner(), property.getName()));
|
||||||
property.getProperty().getOwner(), property.getProperty().getName()));
|
|
||||||
|
|
||||||
methodFactory
|
methodFactory
|
||||||
.addTypedProperty(
|
.addTypedProperty(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -92,12 +92,12 @@ public final class ModuleCache {
|
|||||||
return BaseModule.getModule();
|
return BaseModule.getModule();
|
||||||
case "Benchmark":
|
case "Benchmark":
|
||||||
return BenchmarkModule.getModule();
|
return BenchmarkModule.getModule();
|
||||||
case "pklbinary":
|
|
||||||
return PklBinaryModule.getModule();
|
|
||||||
case "jsonnet":
|
case "jsonnet":
|
||||||
return JsonnetModule.getModule();
|
return JsonnetModule.getModule();
|
||||||
case "math":
|
case "math":
|
||||||
return MathModule.getModule();
|
return MathModule.getModule();
|
||||||
|
case "pklbinary":
|
||||||
|
return PklBinaryModule.getModule();
|
||||||
case "platform":
|
case "platform":
|
||||||
return PlatformModule.getModule();
|
return PlatformModule.getModule();
|
||||||
case "project":
|
case "project":
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ import org.pkl.core.util.MutableReference;
|
|||||||
|
|
||||||
/** Runs test results examples and facts. */
|
/** Runs test results examples and facts. */
|
||||||
public final class TestRunner {
|
public final class TestRunner {
|
||||||
private static final PklConverter converter = new PklConverter(VmMapping.empty());
|
private static final PklConverter converter = PklConverter.NOOP;
|
||||||
private final BufferedLogger logger;
|
private final BufferedLogger logger;
|
||||||
private final StackFrameTransformer stackFrameTransformer;
|
private final StackFrameTransformer stackFrameTransformer;
|
||||||
private final boolean overwrite;
|
private final boolean overwrite;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -575,7 +575,7 @@ public final class VmClass extends VmValue {
|
|||||||
var builder = VmMap.builder();
|
var builder = VmMap.builder();
|
||||||
for (var property : declaredProperties.getValues()) {
|
for (var property : declaredProperties.getValues()) {
|
||||||
if (property.isLocal()) continue;
|
if (property.isLocal()) continue;
|
||||||
builder.add(property.getName().toString(), property.getMirror(this));
|
builder.add(property.getName().toString(), property.getMirror());
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
@@ -584,7 +584,7 @@ public final class VmClass extends VmValue {
|
|||||||
var builder = VmMap.builder();
|
var builder = VmMap.builder();
|
||||||
for (var property : getAllProperties().getValues()) {
|
for (var property : getAllProperties().getValues()) {
|
||||||
if (property.isLocal()) continue;
|
if (property.isLocal()) continue;
|
||||||
builder.add(property.getName().toString(), property.getMirror(this));
|
builder.add(property.getName().toString(), property.getMirror());
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2025-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -38,7 +38,7 @@ public class VmPklBinaryEncoder extends AbstractRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public VmPklBinaryEncoder(MessageBufferPacker packer) {
|
public VmPklBinaryEncoder(MessageBufferPacker packer) {
|
||||||
this(packer, new PklConverter(VmMapping.empty()));
|
this(packer, PklConverter.NOOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void packCode(PklBinaryCode code) throws IOException {
|
private void packCode(PklBinaryCode code) throws IOException {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -15,6 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.pkl.core.runtime;
|
package org.pkl.core.runtime;
|
||||||
|
|
||||||
|
import org.pkl.core.ast.member.ClassProperty;
|
||||||
|
import org.pkl.core.util.Pair;
|
||||||
|
|
||||||
public interface VmValueConverter<T> {
|
public interface VmValueConverter<T> {
|
||||||
Object WILDCARD_PROPERTY =
|
Object WILDCARD_PROPERTY =
|
||||||
new Object() {
|
new Object() {
|
||||||
@@ -82,6 +85,9 @@ public interface VmValueConverter<T> {
|
|||||||
|
|
||||||
T convertFunction(VmFunction value, Iterable<Object> path);
|
T convertFunction(VmFunction value, Iterable<Object> path);
|
||||||
|
|
||||||
|
/** Returns with an empty identifier if the second value is a RenderDirective */
|
||||||
|
Pair<Identifier, T> convertProperty(ClassProperty property, Object value, Iterable<Object> path);
|
||||||
|
|
||||||
default T convert(Object value, Iterable<Object> path) {
|
default T convert(Object value, Iterable<Object> path) {
|
||||||
if (value instanceof VmValue vmValue) {
|
if (value instanceof VmValue vmValue) {
|
||||||
return vmValue.accept(this, path);
|
return vmValue.accept(this, path);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -19,6 +19,7 @@ import com.oracle.truffle.api.source.SourceSection;
|
|||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import org.pkl.core.ast.member.ClassProperty;
|
||||||
import org.pkl.core.runtime.BaseModule;
|
import org.pkl.core.runtime.BaseModule;
|
||||||
import org.pkl.core.runtime.Identifier;
|
import org.pkl.core.runtime.Identifier;
|
||||||
import org.pkl.core.runtime.VmClass;
|
import org.pkl.core.runtime.VmClass;
|
||||||
@@ -195,7 +196,12 @@ public abstract class AbstractRenderer implements VmValueVisitor {
|
|||||||
(memberKey, member, memberValue) -> {
|
(memberKey, member, memberValue) -> {
|
||||||
if (member.isClass() || member.isTypeAlias()) return true;
|
if (member.isClass() || member.isTypeAlias()) return true;
|
||||||
assert member.isProp();
|
assert member.isProp();
|
||||||
doVisitProperty((Identifier) memberKey, memberValue, member.getSourceSection(), isFirst);
|
doVisitProperty(
|
||||||
|
(Identifier) memberKey,
|
||||||
|
memberValue,
|
||||||
|
value.getVmClass().getProperty((Identifier) memberKey),
|
||||||
|
member.getSourceSection(),
|
||||||
|
isFirst);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -218,7 +224,7 @@ public abstract class AbstractRenderer implements VmValueVisitor {
|
|||||||
var sourceSection = member.getSourceSection();
|
var sourceSection = member.getSourceSection();
|
||||||
if (member.isProp()) {
|
if (member.isProp()) {
|
||||||
if (!canRenderPropertyOrEntry) cannotRenderObjectWithElementsAndOtherMembers(value);
|
if (!canRenderPropertyOrEntry) cannotRenderObjectWithElementsAndOtherMembers(value);
|
||||||
doVisitProperty((Identifier) memberKey, memberValue, sourceSection, isFirst);
|
doVisitProperty((Identifier) memberKey, memberValue, null, sourceSection, isFirst);
|
||||||
} else if (member.isEntry()) {
|
} else if (member.isEntry()) {
|
||||||
if (!canRenderPropertyOrEntry) cannotRenderObjectWithElementsAndOtherMembers(value);
|
if (!canRenderPropertyOrEntry) cannotRenderObjectWithElementsAndOtherMembers(value);
|
||||||
doVisitEntry(memberKey, memberValue, sourceSection, isFirst);
|
doVisitEntry(memberKey, memberValue, sourceSection, isFirst);
|
||||||
@@ -327,10 +333,19 @@ public abstract class AbstractRenderer implements VmValueVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void doVisitProperty(
|
private void doVisitProperty(
|
||||||
Identifier name, Object value, SourceSection sourceSection, MutableBoolean isFirst) {
|
Identifier name,
|
||||||
|
Object value,
|
||||||
|
@Nullable ClassProperty classProperty,
|
||||||
|
SourceSection sourceSection,
|
||||||
|
MutableBoolean isFirst) {
|
||||||
var prevSourceSection = currSourceSection;
|
var prevSourceSection = currSourceSection;
|
||||||
currSourceSection = sourceSection;
|
currSourceSection = sourceSection;
|
||||||
currPath.push(name);
|
currPath.push(name);
|
||||||
|
if (classProperty != null) {
|
||||||
|
var propVal = converter.convertProperty(classProperty, value, currPath);
|
||||||
|
name = propVal.getFirst();
|
||||||
|
value = propVal.getSecond();
|
||||||
|
}
|
||||||
var convertedValue = converter.convert(value, currPath);
|
var convertedValue = converter.convert(value, currPath);
|
||||||
if (!(skipNullProperties && convertedValue instanceof VmNull)) {
|
if (!(skipNullProperties && convertedValue instanceof VmNull)) {
|
||||||
visitProperty(name, convertedValue, isFirst.getAndSetFalse());
|
visitProperty(name, convertedValue, isFirst.getAndSetFalse());
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,13 +16,16 @@
|
|||||||
package org.pkl.core.stdlib;
|
package org.pkl.core.stdlib;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import org.pkl.core.ast.member.ClassProperty;
|
||||||
import org.pkl.core.runtime.*;
|
import org.pkl.core.runtime.*;
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
import org.pkl.core.util.Pair;
|
import org.pkl.core.util.Pair;
|
||||||
|
|
||||||
public final class PklConverter implements VmValueConverter<Object> {
|
public final class PklConverter implements VmValueConverter<Object> {
|
||||||
private final Map<VmClass, VmFunction> typeConverters;
|
private final Map<VmClass, VmFunction> typeConverters;
|
||||||
|
private final Map<VmClass, VmFunction> convertPropertyTransformers;
|
||||||
private final Pair<Object[], VmFunction>[] pathConverters;
|
private final Pair<Object[], VmFunction>[] pathConverters;
|
||||||
|
private final Object rendererOrParser;
|
||||||
|
|
||||||
private final @Nullable VmFunction stringConverter;
|
private final @Nullable VmFunction stringConverter;
|
||||||
private final @Nullable VmFunction booleanConverter;
|
private final @Nullable VmFunction booleanConverter;
|
||||||
@@ -44,12 +47,15 @@ public final class PklConverter implements VmValueConverter<Object> {
|
|||||||
private final @Nullable VmFunction classConverter;
|
private final @Nullable VmFunction classConverter;
|
||||||
private final @Nullable VmFunction typeAliasConverter;
|
private final @Nullable VmFunction typeAliasConverter;
|
||||||
|
|
||||||
public PklConverter(VmMapping converters) {
|
private PklConverter(
|
||||||
// As of 0.18, `converters` is forced by the mapping type check,
|
VmMapping converters, VmMapping convertPropertyTransformers, Object rendererOrParser) {
|
||||||
// but let's not rely on this implementation detail.
|
|
||||||
converters.force(false, false);
|
converters.force(false, false);
|
||||||
|
convertPropertyTransformers.force(false, false);
|
||||||
typeConverters = createTypeConverters(converters);
|
typeConverters = createTypeConverters(converters);
|
||||||
|
this.convertPropertyTransformers =
|
||||||
|
createConvertPropertyTransformers(convertPropertyTransformers);
|
||||||
pathConverters = createPathConverters(converters);
|
pathConverters = createPathConverters(converters);
|
||||||
|
this.rendererOrParser = rendererOrParser;
|
||||||
|
|
||||||
stringConverter = typeConverters.get(BaseModule.getStringClass());
|
stringConverter = typeConverters.get(BaseModule.getStringClass());
|
||||||
booleanConverter = typeConverters.get(BaseModule.getBooleanClass());
|
booleanConverter = typeConverters.get(BaseModule.getBooleanClass());
|
||||||
@@ -72,6 +78,22 @@ public final class PklConverter implements VmValueConverter<Object> {
|
|||||||
typeAliasConverter = typeConverters.get(BaseModule.getTypeAliasClass());
|
typeAliasConverter = typeConverters.get(BaseModule.getTypeAliasClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final PklConverter NOOP =
|
||||||
|
new PklConverter(VmMapping.empty(), VmMapping.empty(), VmNull.withoutDefault());
|
||||||
|
|
||||||
|
public static PklConverter fromRenderer(VmTyped renderer) {
|
||||||
|
var converters = (VmMapping) VmUtils.readMember(renderer, Identifier.CONVERTERS);
|
||||||
|
var convertPropertyTransformers =
|
||||||
|
(VmMapping) VmUtils.readMember(renderer, Identifier.CONVERT_PROPERTY_TRANSFORMERS);
|
||||||
|
return new PklConverter(converters, convertPropertyTransformers, renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PklConverter fromParser(VmTyped parser) {
|
||||||
|
var converters = (VmMapping) VmUtils.readMember(parser, Identifier.CONVERTERS);
|
||||||
|
return new PklConverter(
|
||||||
|
converters, VmMapping.empty(), parser); // no annotation converters in parsers
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object convertString(String value, Iterable<Object> path) {
|
public Object convertString(String value, Iterable<Object> path) {
|
||||||
return doConvert(value, path, stringConverter);
|
return doConvert(value, path, stringConverter);
|
||||||
@@ -177,6 +199,34 @@ public final class PklConverter implements VmValueConverter<Object> {
|
|||||||
return doConvert(value, path, nullConverter);
|
return doConvert(value, path, nullConverter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pair<Identifier, Object> convertProperty(
|
||||||
|
ClassProperty property, Object value, Iterable<Object> path) {
|
||||||
|
var name = property.getName();
|
||||||
|
|
||||||
|
var annotations = property.getAllAnnotations(false);
|
||||||
|
if (annotations.isEmpty()) {
|
||||||
|
return Pair.of(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var prop = new VmPair(name.toString(), value);
|
||||||
|
for (var annotation : annotations) {
|
||||||
|
if (!annotation.getVmClass().isSubclassOf(BaseModule.getConvertPropertyClass())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var transformer = findConvertPropertyTransformer(annotation.getVmClass());
|
||||||
|
if (transformer != null) {
|
||||||
|
annotation = (VmTyped) transformer.apply(annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
var renderFunction = (VmFunction) VmUtils.readMember(annotation, Identifier.RENDER);
|
||||||
|
prop = (VmPair) renderFunction.apply(prop, rendererOrParser);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair.of(Identifier.get((String) prop.getFirst()), prop.getSecond());
|
||||||
|
}
|
||||||
|
|
||||||
private Map<VmClass, VmFunction> createTypeConverters(VmMapping converters) {
|
private Map<VmClass, VmFunction> createTypeConverters(VmMapping converters) {
|
||||||
var result = new HashMap<VmClass, VmFunction>();
|
var result = new HashMap<VmClass, VmFunction>();
|
||||||
converters.iterateMemberValues(
|
converters.iterateMemberValues(
|
||||||
@@ -190,6 +240,19 @@ public final class PklConverter implements VmValueConverter<Object> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<VmClass, VmFunction> createConvertPropertyTransformers(
|
||||||
|
VmMapping convertPropertyTransformers) {
|
||||||
|
var result = new HashMap<VmClass, VmFunction>();
|
||||||
|
convertPropertyTransformers.iterateMemberValues(
|
||||||
|
(key, member, value) -> {
|
||||||
|
assert value != null; // forced in ctor
|
||||||
|
result.put((VmClass) key, ((VmFunction) value));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Pair<Object[], VmFunction>[] createPathConverters(VmMapping converters) {
|
private Pair<Object[], VmFunction>[] createPathConverters(VmMapping converters) {
|
||||||
var result = new ArrayList<Pair<Object[], VmFunction>>();
|
var result = new ArrayList<Pair<Object[], VmFunction>>();
|
||||||
@@ -221,8 +284,16 @@ public final class PklConverter implements VmValueConverter<Object> {
|
|||||||
* method will return the most specific converter for a type.
|
* method will return the most specific converter for a type.
|
||||||
*/
|
*/
|
||||||
private @Nullable VmFunction findTypeConverter(VmClass clazz) {
|
private @Nullable VmFunction findTypeConverter(VmClass clazz) {
|
||||||
|
return findConverterByType(typeConverters, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable VmFunction findConvertPropertyTransformer(VmClass clazz) {
|
||||||
|
return findConverterByType(convertPropertyTransformers, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> @Nullable T findConverterByType(Map<VmClass, T> bag, VmClass clazz) {
|
||||||
for (var current = clazz; current != null; current = current.getSuperclass()) {
|
for (var current = clazz; current != null; current = current.getSuperclass()) {
|
||||||
var found = typeConverters.get(current);
|
var found = bag.get(current);
|
||||||
if (found != null) return found;
|
if (found != null) return found;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -52,9 +52,7 @@ public final class JsonRendererNodes {
|
|||||||
private static JsonRenderer createRenderer(VmTyped self, StringBuilder builder) {
|
private static JsonRenderer createRenderer(VmTyped self, StringBuilder builder) {
|
||||||
var indent = (String) VmUtils.readMember(self, Identifier.INDENT);
|
var indent = (String) VmUtils.readMember(self, Identifier.INDENT);
|
||||||
var omitNullProperties = (boolean) VmUtils.readMember(self, Identifier.OMIT_NULL_PROPERTIES);
|
var omitNullProperties = (boolean) VmUtils.readMember(self, Identifier.OMIT_NULL_PROPERTIES);
|
||||||
var converters = (VmMapping) VmUtils.readMember(self, Identifier.CONVERTERS);
|
return new JsonRenderer(builder, indent, PklConverter.fromRenderer(self), omitNullProperties);
|
||||||
var converter = new PklConverter(converters);
|
|
||||||
return new JsonRenderer(builder, indent, converter, omitNullProperties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class JsonRenderer extends AbstractStringRenderer {
|
private static final class JsonRenderer extends AbstractStringRenderer {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -48,9 +48,7 @@ public final class PListRendererNodes {
|
|||||||
|
|
||||||
private static PListRenderer createRenderer(VmTyped self, StringBuilder builder) {
|
private static PListRenderer createRenderer(VmTyped self, StringBuilder builder) {
|
||||||
var indent = (String) VmUtils.readMember(self, Identifier.INDENT);
|
var indent = (String) VmUtils.readMember(self, Identifier.INDENT);
|
||||||
var converters = (VmMapping) VmUtils.readMember(self, Identifier.CONVERTERS);
|
return new PListRenderer(builder, indent, PklConverter.fromRenderer(self));
|
||||||
var converter = new PklConverter(converters);
|
|
||||||
return new PListRenderer(builder, indent, converter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// keep in sync with org.pkl.core.PListRenderer
|
// keep in sync with org.pkl.core.PListRenderer
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -46,12 +46,14 @@ public final class PcfRendererNodes {
|
|||||||
|
|
||||||
private static PcfRenderer createRenderer(VmTyped self, StringBuilder builder) {
|
private static PcfRenderer createRenderer(VmTyped self, StringBuilder builder) {
|
||||||
var indent = (String) VmUtils.readMember(self, Identifier.INDENT);
|
var indent = (String) VmUtils.readMember(self, Identifier.INDENT);
|
||||||
var converters = (VmMapping) VmUtils.readMember(self, Identifier.CONVERTERS);
|
|
||||||
var omitNullProperties = (boolean) VmUtils.readMember(self, Identifier.OMIT_NULL_PROPERTIES);
|
var omitNullProperties = (boolean) VmUtils.readMember(self, Identifier.OMIT_NULL_PROPERTIES);
|
||||||
var useCustomStringDelimiters =
|
var useCustomStringDelimiters =
|
||||||
(boolean) VmUtils.readMember(self, Identifier.USE_CUSTOM_STRING_DELIMITERS);
|
(boolean) VmUtils.readMember(self, Identifier.USE_CUSTOM_STRING_DELIMITERS);
|
||||||
var converter = new PklConverter(converters);
|
|
||||||
return new PcfRenderer(
|
return new PcfRenderer(
|
||||||
builder, indent, converter, omitNullProperties, useCustomStringDelimiters);
|
builder,
|
||||||
|
indent,
|
||||||
|
PklConverter.fromRenderer(self),
|
||||||
|
omitNullProperties,
|
||||||
|
useCustomStringDelimiters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -69,9 +69,8 @@ public final class PropertiesRendererNodes {
|
|||||||
private static PropertiesRenderer createRenderer(VmTyped self, StringBuilder builder) {
|
private static PropertiesRenderer createRenderer(VmTyped self, StringBuilder builder) {
|
||||||
var omitNullProperties = (boolean) VmUtils.readMember(self, Identifier.OMIT_NULL_PROPERTIES);
|
var omitNullProperties = (boolean) VmUtils.readMember(self, Identifier.OMIT_NULL_PROPERTIES);
|
||||||
var restrictCharset = (boolean) VmUtils.readMember(self, Identifier.RESTRICT_CHARSET);
|
var restrictCharset = (boolean) VmUtils.readMember(self, Identifier.RESTRICT_CHARSET);
|
||||||
var converters = (VmMapping) VmUtils.readMember(self, Identifier.CONVERTERS);
|
return new PropertiesRenderer(
|
||||||
var PklConverter = new PklConverter(converters);
|
builder, omitNullProperties, restrictCharset, PklConverter.fromRenderer(self));
|
||||||
return new PropertiesRenderer(builder, omitNullProperties, restrictCharset, PklConverter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class PropertiesRenderer extends AbstractStringRenderer {
|
private static final class PropertiesRenderer extends AbstractStringRenderer {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -69,10 +69,13 @@ public final class YamlRendererNodes {
|
|||||||
var indentWidth = ((Long) VmUtils.readMember(self, Identifier.INDENT_WIDTH)).intValue();
|
var indentWidth = ((Long) VmUtils.readMember(self, Identifier.INDENT_WIDTH)).intValue();
|
||||||
var omitNullProperties = (boolean) VmUtils.readMember(self, Identifier.OMIT_NULL_PROPERTIES);
|
var omitNullProperties = (boolean) VmUtils.readMember(self, Identifier.OMIT_NULL_PROPERTIES);
|
||||||
var isStream = (boolean) VmUtils.readMember(self, Identifier.IS_STREAM);
|
var isStream = (boolean) VmUtils.readMember(self, Identifier.IS_STREAM);
|
||||||
var converters = (VmMapping) VmUtils.readMember(self, Identifier.CONVERTERS);
|
|
||||||
var converter = new PklConverter(converters);
|
|
||||||
return new YamlRenderer(
|
return new YamlRenderer(
|
||||||
builder, " ".repeat(indentWidth), converter, omitNullProperties, mode, isStream);
|
builder,
|
||||||
|
" ".repeat(indentWidth),
|
||||||
|
PklConverter.fromRenderer(self),
|
||||||
|
omitNullProperties,
|
||||||
|
mode,
|
||||||
|
isStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class YamlRenderer extends AbstractStringRenderer {
|
private static final class YamlRenderer extends AbstractStringRenderer {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -51,7 +51,7 @@ public final class ParserNodes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Object doParse(VmTyped self, String text) {
|
private Object doParse(VmTyped self, String text) {
|
||||||
var converter = createConverter(self);
|
var converter = PklConverter.fromParser(self);
|
||||||
var useMapping = (boolean) VmUtils.readMember(self, Identifier.USE_MAPPING);
|
var useMapping = (boolean) VmUtils.readMember(self, Identifier.USE_MAPPING);
|
||||||
var handler = new Handler(converter, useMapping);
|
var handler = new Handler(converter, useMapping);
|
||||||
var parser = new JsonParser(handler);
|
var parser = new JsonParser(handler);
|
||||||
@@ -64,11 +64,6 @@ public final class ParserNodes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PklConverter createConverter(VmTyped self) {
|
|
||||||
var converters = (VmMapping) VmUtils.readMember(self, Identifier.CONVERTERS);
|
|
||||||
return new PklConverter(converters);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Handler
|
private static class Handler
|
||||||
extends JsonHandler<EconomicMap<Object, ObjectMember>, EconomicMap<Object, ObjectMember>> {
|
extends JsonHandler<EconomicMap<Object, ObjectMember>, EconomicMap<Object, ObjectMember>> {
|
||||||
private final PklConverter converter;
|
private final PklConverter converter;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -47,9 +47,7 @@ public final class RendererNodes {
|
|||||||
var indent = (String) VmNull.unwrap(VmUtils.readMember(self, Identifier.INDENT));
|
var indent = (String) VmNull.unwrap(VmUtils.readMember(self, Identifier.INDENT));
|
||||||
if (indent == null) indent = "";
|
if (indent == null) indent = "";
|
||||||
var omitNullProperties = (boolean) VmUtils.readMember(self, Identifier.OMIT_NULL_PROPERTIES);
|
var omitNullProperties = (boolean) VmUtils.readMember(self, Identifier.OMIT_NULL_PROPERTIES);
|
||||||
var converters = (VmMapping) VmUtils.readMember(self, Identifier.CONVERTERS);
|
return new Renderer(builder, indent, omitNullProperties, PklConverter.fromRenderer(self));
|
||||||
var converter = new PklConverter(converters);
|
|
||||||
return new Renderer(builder, indent, omitNullProperties, converter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class renderDocument extends ExternalMethod1Node {
|
public abstract static class renderDocument extends ExternalMethod1Node {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2025-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -19,12 +19,9 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
|||||||
import com.oracle.truffle.api.dsl.Specialization;
|
import com.oracle.truffle.api.dsl.Specialization;
|
||||||
import org.msgpack.core.MessageBufferPacker;
|
import org.msgpack.core.MessageBufferPacker;
|
||||||
import org.msgpack.core.MessagePack;
|
import org.msgpack.core.MessagePack;
|
||||||
import org.pkl.core.runtime.Identifier;
|
|
||||||
import org.pkl.core.runtime.VmBytes;
|
import org.pkl.core.runtime.VmBytes;
|
||||||
import org.pkl.core.runtime.VmMapping;
|
|
||||||
import org.pkl.core.runtime.VmPklBinaryEncoder;
|
import org.pkl.core.runtime.VmPklBinaryEncoder;
|
||||||
import org.pkl.core.runtime.VmTyped;
|
import org.pkl.core.runtime.VmTyped;
|
||||||
import org.pkl.core.runtime.VmUtils;
|
|
||||||
import org.pkl.core.stdlib.ExternalMethod1Node;
|
import org.pkl.core.stdlib.ExternalMethod1Node;
|
||||||
import org.pkl.core.stdlib.PklConverter;
|
import org.pkl.core.stdlib.PklConverter;
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
@@ -64,8 +61,6 @@ public final class RendererNodes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static VmPklBinaryEncoder createRenderer(VmTyped self, MessageBufferPacker packer) {
|
private static VmPklBinaryEncoder createRenderer(VmTyped self, MessageBufferPacker packer) {
|
||||||
var converters = (VmMapping) VmUtils.readMember(self, Identifier.CONVERTERS);
|
return new VmPklBinaryEncoder(packer, PklConverter.fromRenderer(self));
|
||||||
var converter = new PklConverter(converters);
|
|
||||||
return new VmPklBinaryEncoder(packer, converter);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -135,9 +135,8 @@ public final class RendererNodes {
|
|||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
private static ProtobufRenderer createRenderer(VmTyped self, StringBuilder builder) {
|
private static ProtobufRenderer createRenderer(VmTyped self, StringBuilder builder) {
|
||||||
var indent = (String) VmUtils.readMember(self, Identifier.INDENT);
|
var indent = (String) VmUtils.readMember(self, Identifier.INDENT);
|
||||||
var converters = (VmMapping) VmUtils.readMember(self, Identifier.CONVERTERS);
|
|
||||||
|
|
||||||
return new ProtobufRenderer(builder, indent, new PklConverter(converters));
|
return new ProtobufRenderer(builder, indent, PklConverter.fromRenderer(self));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class ProtobufRenderer extends AbstractStringRenderer {
|
private static final class ProtobufRenderer extends AbstractStringRenderer {
|
||||||
|
|||||||
@@ -236,8 +236,7 @@ public final class JUnitReport implements TestReport {
|
|||||||
|
|
||||||
private static String renderXML(String indent, String version, VmDynamic value) {
|
private static String renderXML(String indent, String version, VmDynamic value) {
|
||||||
var builder = new StringBuilder();
|
var builder = new StringBuilder();
|
||||||
var converter = new PklConverter(VmMapping.empty());
|
var renderer = new Renderer(builder, indent, version, "", VmMapping.empty(), PklConverter.NOOP);
|
||||||
var renderer = new Renderer(builder, indent, version, "", VmMapping.empty(), converter);
|
|
||||||
renderer.renderDocument(value);
|
renderer.renderDocument(value);
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -34,10 +34,13 @@ public final class RendererNodes {
|
|||||||
var rootElementName = (String) VmUtils.readMember(self, Identifier.ROOT_ELEMENT_NAME);
|
var rootElementName = (String) VmUtils.readMember(self, Identifier.ROOT_ELEMENT_NAME);
|
||||||
var rootElementAttributes =
|
var rootElementAttributes =
|
||||||
(VmMapping) VmUtils.readMember(self, Identifier.ROOT_ELEMENT_ATTRIBUTES);
|
(VmMapping) VmUtils.readMember(self, Identifier.ROOT_ELEMENT_ATTRIBUTES);
|
||||||
var converters = (VmMapping) VmUtils.readMember(self, Identifier.CONVERTERS);
|
|
||||||
var converter = new PklConverter(converters);
|
|
||||||
return new Renderer(
|
return new Renderer(
|
||||||
builder, indent, xmlVersion, rootElementName, rootElementAttributes, converter);
|
builder,
|
||||||
|
indent,
|
||||||
|
xmlVersion,
|
||||||
|
rootElementName,
|
||||||
|
rootElementAttributes,
|
||||||
|
PklConverter.fromRenderer(self));
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class renderDocument extends ExternalMethod1Node {
|
public abstract static class renderDocument extends ExternalMethod1Node {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -65,7 +65,7 @@ public final class ParserNodes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Object doParse(VmTyped self, String text, String uri) {
|
private Object doParse(VmTyped self, String text, String uri) {
|
||||||
var converter = createConverter(self);
|
var converter = PklConverter.fromParser(self);
|
||||||
var load = createLoad(self, text, uri, converter);
|
var load = createLoad(self, text, uri, converter);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -101,7 +101,7 @@ public final class ParserNodes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private VmList doParseAll(VmTyped self, String text, String uri) {
|
private VmList doParseAll(VmTyped self, String text, String uri) {
|
||||||
var converter = createConverter(self);
|
var converter = PklConverter.fromParser(self);
|
||||||
var load = createLoad(self, text, uri, converter);
|
var load = createLoad(self, text, uri, converter);
|
||||||
var builder = VmList.EMPTY.builder();
|
var builder = VmList.EMPTY.builder();
|
||||||
|
|
||||||
@@ -123,11 +123,6 @@ public final class ParserNodes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PklConverter createConverter(VmTyped self) {
|
|
||||||
var converters = (VmMapping) VmUtils.readMember(self, Identifier.CONVERTERS);
|
|
||||||
return new PklConverter(converters);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getMaxCollectionAliases(VmTyped self) {
|
private static int getMaxCollectionAliases(VmTyped self) {
|
||||||
var max = (Long) VmUtils.readMember(self, Identifier.MAX_COLLECTION_ALIASES);
|
var max = (Long) VmUtils.readMember(self, Identifier.MAX_COLLECTION_ALIASES);
|
||||||
return max.intValue(); // has Pkl type `Int32(isPositive)`
|
return max.intValue(); // has Pkl type `Int32(isPositive)`
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
class LineComment extends ConvertProperty {
|
||||||
|
text: String
|
||||||
|
|
||||||
|
valuePrefix: String = ""
|
||||||
|
prefix: String
|
||||||
|
suffix: String = ""
|
||||||
|
|
||||||
|
render = (prop, renderer) ->
|
||||||
|
Pair(prop.key, new RenderDirective {
|
||||||
|
text =
|
||||||
|
List(
|
||||||
|
valuePrefix,
|
||||||
|
(renderer as ValueRenderer).renderValue(prop.value),
|
||||||
|
prefix,
|
||||||
|
outer.text,
|
||||||
|
suffix,
|
||||||
|
).join("")
|
||||||
|
})
|
||||||
|
}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
open class Prefix extends ConvertProperty {
|
||||||
|
prefix: String
|
||||||
|
|
||||||
|
render = (prop, _) -> Pair(prefix + prop.key, prop.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
class CamelCase extends ConvertProperty {
|
||||||
|
render = (prop, _) ->
|
||||||
|
Pair(
|
||||||
|
prop.key.replaceAllMapped(Regex("[^A-Za-z0-9]+([A-Za-z0-9])"), (match) ->
|
||||||
|
match.groups[1].value.toUpperCase()
|
||||||
|
),
|
||||||
|
prop.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
class MultiplyValue extends ConvertProperty {
|
||||||
|
factor: Number
|
||||||
|
render = (prop, _) ->
|
||||||
|
Pair(prop.key, prop.value as Number * factor)
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubtractValue extends ConvertProperty {
|
||||||
|
difference: Number
|
||||||
|
render = (prop, _) ->
|
||||||
|
Pair(prop.key, prop.value as Number - difference)
|
||||||
|
}
|
||||||
|
|
||||||
|
open class Foo {
|
||||||
|
no_converter: Int = 1
|
||||||
|
|
||||||
|
@Prefix { prefix = "foo_" }
|
||||||
|
prefixed_with_foo: Int = 2
|
||||||
|
|
||||||
|
@Prefix { prefix = "foo_" }
|
||||||
|
base_class_first: Int = 3
|
||||||
|
|
||||||
|
@MultiplyValue { factor = 2 }
|
||||||
|
transform_value: Int = 4
|
||||||
|
|
||||||
|
@SubtractValue { difference = 2 }
|
||||||
|
@MultiplyValue { factor = 3 }
|
||||||
|
@ConvertProperty {
|
||||||
|
render = (prop, _) -> Pair(prop.key, prop.value as Number + 4)
|
||||||
|
}
|
||||||
|
@MultiplyValue { factor = 3 }
|
||||||
|
@SubtractValue { difference = 11 }
|
||||||
|
in_order: Int = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
class Bar extends Foo {
|
||||||
|
@CamelCase
|
||||||
|
base_class_first: Int = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
value = new Bar {}
|
||||||
|
renderer = new PcfRenderer {
|
||||||
|
convertPropertyTransformers {
|
||||||
|
[Prefix] { prefix = "foo_" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
import "pkl:json"
|
||||||
|
|
||||||
|
import ".../input-helper/api/annotationConverter.pkl"
|
||||||
|
|
||||||
|
@json.Property { name = "FOO" }
|
||||||
|
foo: String = "a"
|
||||||
|
|
||||||
|
@annotationConverter.LineComment { text = "bar" }
|
||||||
|
bar: String = "b"
|
||||||
|
|
||||||
|
baz: Nested
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
@annotationConverter.LineComment { text = "qux" }
|
||||||
|
qux: String = "c"
|
||||||
|
|
||||||
|
quux: String = "c"
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
renderer = new JsonRenderer {
|
||||||
|
convertPropertyTransformers {
|
||||||
|
// NB: this renders https://json5.org format which is a superset of JSON that supports commas.
|
||||||
|
[annotationConverter.LineComment] { prefix = ", // " }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
import "pkl:jsonnet"
|
||||||
|
|
||||||
|
import ".../input-helper/api/annotationConverter.pkl"
|
||||||
|
|
||||||
|
@jsonnet.Property { name = "FOO" }
|
||||||
|
foo: String = "a"
|
||||||
|
|
||||||
|
@annotationConverter.LineComment { text = "bar" }
|
||||||
|
bar: String = "b"
|
||||||
|
|
||||||
|
baz: Nested
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
@annotationConverter.LineComment { text = "qux" }
|
||||||
|
qux: String = "c"
|
||||||
|
|
||||||
|
quux: String = "c"
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
renderer = new jsonnet.Renderer {
|
||||||
|
convertPropertyTransformers {
|
||||||
|
[annotationConverter.LineComment] { prefix = ", // " }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
import ".../input-helper/api/annotationConverter.pkl"
|
||||||
|
|
||||||
|
foo: String = "a"
|
||||||
|
|
||||||
|
@annotationConverter.LineComment { text = "bar" }
|
||||||
|
bar: String = "b"
|
||||||
|
|
||||||
|
baz: Nested
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
@annotationConverter.LineComment { text = "qux" }
|
||||||
|
qux: String = "c"
|
||||||
|
|
||||||
|
quux: String = "c"
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
renderer = new PListRenderer {
|
||||||
|
convertPropertyTransformers {
|
||||||
|
[annotationConverter.LineComment] { prefix = "<!-- "; suffix = " -->" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import ".../input-helper/api/annotationConverter.pkl"
|
||||||
|
|
||||||
|
foo: String = "a"
|
||||||
|
|
||||||
|
@annotationConverter.LineComment { text = "bar" }
|
||||||
|
bar: String = "b"
|
||||||
|
|
||||||
|
baz: Nested
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
@annotationConverter.LineComment { text = "qux" }
|
||||||
|
qux: String = "c"
|
||||||
|
|
||||||
|
quux: String = "c"
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
renderer = new PcfRenderer {
|
||||||
|
convertPropertyTransformers {
|
||||||
|
[annotationConverter.LineComment] { prefix = " // "; valuePrefix = "= " }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
import ".../input-helper/api/annotationConverter.pkl"
|
||||||
|
|
||||||
|
foo: String = "a"
|
||||||
|
|
||||||
|
@annotationConverter.LineComment { text = "bar" }
|
||||||
|
bar: String = "b"
|
||||||
|
|
||||||
|
baz: Nested
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
@annotationConverter.LineComment { text = "qux" }
|
||||||
|
qux: String = "c"
|
||||||
|
|
||||||
|
quux: String = "c"
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
renderer = new PropertiesRenderer {
|
||||||
|
convertPropertyTransformers {
|
||||||
|
[annotationConverter.LineComment] { prefix = "\n# " }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import "pkl:protobuf"
|
||||||
|
|
||||||
|
import ".../input-helper/api/annotationConverter.pkl"
|
||||||
|
|
||||||
|
class Comment extends Annotation
|
||||||
|
|
||||||
|
@protobuf.Property { name = "FOO" }
|
||||||
|
foo: String = "a"
|
||||||
|
|
||||||
|
@annotationConverter.LineComment { text = "bar" }
|
||||||
|
bar: String = "b"
|
||||||
|
|
||||||
|
baz: Nested
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
@annotationConverter.LineComment { text = "qux" }
|
||||||
|
qux: String = "c"
|
||||||
|
|
||||||
|
quux: String = "c"
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
renderer = new protobuf.Renderer {
|
||||||
|
convertPropertyTransformers {
|
||||||
|
[annotationConverter.LineComment] { prefix = " # " }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import "pkl:xml"
|
||||||
|
|
||||||
|
import ".../input-helper/api/annotationConverter.pkl"
|
||||||
|
|
||||||
|
@xml.Property { name = "FOO" }
|
||||||
|
foo: String = "a"
|
||||||
|
|
||||||
|
@annotationConverter.LineComment { text = "bar" }
|
||||||
|
bar: String = "b"
|
||||||
|
|
||||||
|
baz: Nested
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
@annotationConverter.LineComment { text = "qux" }
|
||||||
|
qux: String = "c"
|
||||||
|
|
||||||
|
quux: String = "c"
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
renderer = new xml.Renderer {
|
||||||
|
convertPropertyTransformers {
|
||||||
|
[annotationConverter.LineComment] { prefix = "<!-- "; suffix = " -->" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
import "pkl:yaml"
|
||||||
|
|
||||||
|
import ".../input-helper/api/annotationConverter.pkl"
|
||||||
|
|
||||||
|
class Tag extends ConvertProperty {
|
||||||
|
tag: String(startsWith("!"))
|
||||||
|
|
||||||
|
render = (prop, renderer) ->
|
||||||
|
if (renderer is YamlRenderer)
|
||||||
|
Pair(prop.key, new RenderDirective {
|
||||||
|
text = " \(tag) \(renderer.renderValue(prop.value))"
|
||||||
|
})
|
||||||
|
else
|
||||||
|
prop
|
||||||
|
}
|
||||||
|
|
||||||
|
@yaml.Property { name = "FOO" }
|
||||||
|
foo: String = "a"
|
||||||
|
|
||||||
|
@annotationConverter.LineComment { text = "bar" }
|
||||||
|
bar: String = "b"
|
||||||
|
|
||||||
|
baz: Nested
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
@annotationConverter.LineComment { text = "qux" }
|
||||||
|
qux: String = "c"
|
||||||
|
|
||||||
|
quux: String = "c"
|
||||||
|
|
||||||
|
@Tag { tag = "!!foo" }
|
||||||
|
quuux: String = "d"
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
renderer = new YamlRenderer {
|
||||||
|
convertPropertyTransformers {
|
||||||
|
[annotationConverter.LineComment] { prefix = " # "; valuePrefix = " " }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
no_converter = 1
|
||||||
|
foo_prefixed_with_foo = 2
|
||||||
|
fooBaseClassFirst = 3
|
||||||
|
transform_value = 8
|
||||||
|
in_order = 28
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"FOO": "a",
|
||||||
|
"bar": "b", // bar,
|
||||||
|
"baz": {
|
||||||
|
"qux": "c", // qux,
|
||||||
|
"quux": "c"
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
FOO: 'a',
|
||||||
|
bar: 'b', // bar,
|
||||||
|
baz: {
|
||||||
|
qux: 'c', // qux,
|
||||||
|
quux: 'c',
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>foo</key>
|
||||||
|
<string>a</string>
|
||||||
|
<key>bar</key>
|
||||||
|
<string>b</string><!-- bar -->
|
||||||
|
<key>baz</key>
|
||||||
|
<dict>
|
||||||
|
<key>qux</key>
|
||||||
|
<string>c</string><!-- qux -->
|
||||||
|
<key>quux</key>
|
||||||
|
<string>c</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
foo = "a"
|
||||||
|
bar = "b" // bar
|
||||||
|
baz {
|
||||||
|
qux = "c" // qux
|
||||||
|
quux = "c"
|
||||||
|
}
|
||||||
+6
@@ -172,6 +172,12 @@
|
|||||||
-
|
-
|
||||||
- 3
|
- 3
|
||||||
- {}
|
- {}
|
||||||
|
-
|
||||||
|
- 16
|
||||||
|
- 'convertPropertyTransformers'
|
||||||
|
-
|
||||||
|
- 3
|
||||||
|
- {}
|
||||||
-
|
-
|
||||||
- 16
|
- 16
|
||||||
- 'extension'
|
- 'extension'
|
||||||
|
|||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
foo = a
|
||||||
|
bar = b
|
||||||
|
# bar
|
||||||
|
baz.qux = c
|
||||||
|
# qux
|
||||||
|
baz.quux = c
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
FOO: "a"
|
||||||
|
bar: "b" # bar
|
||||||
|
baz: {
|
||||||
|
qux: "c" # qux
|
||||||
|
quux: "c"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<FOO>a</FOO>
|
||||||
|
<bar>b<!-- bar --></bar>
|
||||||
|
<baz>
|
||||||
|
<qux>c<!-- qux --></qux>
|
||||||
|
<quux>c</quux>
|
||||||
|
</baz>
|
||||||
|
</root>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
FOO: a
|
||||||
|
bar: b # bar
|
||||||
|
baz:
|
||||||
|
qux: c # qux
|
||||||
|
quux: c
|
||||||
|
quuux: !!foo d
|
||||||
+82
-1
@@ -20,12 +20,14 @@
|
|||||||
@ModuleInfo { minPklVersion = "0.31.0" }
|
@ModuleInfo { minPklVersion = "0.31.0" }
|
||||||
module pkl.base
|
module pkl.base
|
||||||
|
|
||||||
// math import used for doc comments
|
// json, math, and yaml imports used for doc comments
|
||||||
|
import "pkl:json"
|
||||||
import "pkl:jsonnet"
|
import "pkl:jsonnet"
|
||||||
import "pkl:math"
|
import "pkl:math"
|
||||||
import "pkl:pklbinary"
|
import "pkl:pklbinary"
|
||||||
import "pkl:protobuf"
|
import "pkl:protobuf"
|
||||||
import "pkl:xml"
|
import "pkl:xml"
|
||||||
|
import "pkl:yaml"
|
||||||
|
|
||||||
/// The top type of the type hierarchy.
|
/// The top type of the type hierarchy.
|
||||||
///
|
///
|
||||||
@@ -351,11 +353,70 @@ abstract class BaseValueRenderer {
|
|||||||
/// both match path spec `server.timeout`, whereas path `server.timeout.millis` does not.
|
/// both match path spec `server.timeout`, whereas path `server.timeout.millis` does not.
|
||||||
converters: Mapping<Class | String, (unknown) -> Any>
|
converters: Mapping<Class | String, (unknown) -> Any>
|
||||||
|
|
||||||
|
/// Customizations for [ConvertProperty] annotation behaviors.
|
||||||
|
///
|
||||||
|
/// This property is consulted to transform [ConvertProperty] annotation values.
|
||||||
|
/// This can be used to customize or override the conversion behavior for a specific renderer.
|
||||||
|
/// If multiple entries match the annotation's class, the most specific class (according to class
|
||||||
|
/// hierarchy) wins.
|
||||||
|
///
|
||||||
|
/// See [ConvertProperty] for detailed information.
|
||||||
|
@Since { version = "0.31.0" }
|
||||||
|
convertPropertyTransformers: Mapping<Class, Mixin<ConvertProperty>>
|
||||||
|
|
||||||
/// The file extension associated with this output format,
|
/// The file extension associated with this output format,
|
||||||
/// or [null] if this format does not have an extension.
|
/// or [null] if this format does not have an extension.
|
||||||
extension: String?
|
extension: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Conversion to be applied to properties when rendered through [BaseValueRenderer].
|
||||||
|
///
|
||||||
|
/// During rendering, the annotation's [render] function is called.
|
||||||
|
/// The function must return a [Pair] of the converted property name and value.
|
||||||
|
///
|
||||||
|
/// Multiple [ConvertProperty] annotations can apply per property, and the output of one
|
||||||
|
/// annotation's [render] function is used as input to the next.
|
||||||
|
/// Annotations are applied the order as declared on the property.
|
||||||
|
/// If the annotated property is overriding a parent property, the parent property's annotations are
|
||||||
|
/// applied first.
|
||||||
|
///
|
||||||
|
/// These conversions can coexist with [BaseValueRenderer.converters], and applies first.
|
||||||
|
///
|
||||||
|
/// These conversions only affect rendering of class properties.
|
||||||
|
/// Applying this to other types of members does not impact rendering.
|
||||||
|
///
|
||||||
|
/// These conversions can be overriden with [BaseValueRenderer.convertPropertyTransformers].
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// // convert duration to the number of seconds
|
||||||
|
/// @ConvertProperty {
|
||||||
|
/// render = (property, _) -> Pair(property.key, property.value.toUnit("s"))
|
||||||
|
/// }
|
||||||
|
/// timeout: Duration
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [ConvertProperty] can be subclassed to define re-usable property converters.
|
||||||
|
/// The conversion defined in the previous example can be rewritten as:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// class ConvertDuration extends ConvertProperty {
|
||||||
|
/// unit: DurationUnit
|
||||||
|
///
|
||||||
|
/// render = (property: Pair<String, Duration>, _) -> Pair(property.key, property.value.toUnit(unit))
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @ConvertDuration { unit = "s" }
|
||||||
|
/// timeout: Duration
|
||||||
|
/// ```
|
||||||
|
@Since { version = "0.31.0" }
|
||||||
|
open class ConvertProperty extends Annotation {
|
||||||
|
/// Function called by [BaseValueRenderer] types during rendering to transform property
|
||||||
|
/// names and values.
|
||||||
|
render: (Pair<String, Any>, BaseValueRenderer) -> Pair<String, Any>
|
||||||
|
}
|
||||||
|
|
||||||
/// Base class for rendering Pkl values in some textual output format.
|
/// Base class for rendering Pkl values in some textual output format.
|
||||||
///
|
///
|
||||||
/// A renderer's output is guaranteed to be well-formed unless [RenderDirective] is part of the
|
/// A renderer's output is guaranteed to be well-formed unless [RenderDirective] is part of the
|
||||||
@@ -466,6 +527,16 @@ class PcfRenderDirective {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Renders values as JSON.
|
/// Renders values as JSON.
|
||||||
|
///
|
||||||
|
/// The [json.Property] annotation can be used to change how a property name renders into JSON.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```
|
||||||
|
/// import "pkl:json"
|
||||||
|
///
|
||||||
|
/// @json.Property { name = "wing_span" }
|
||||||
|
/// wingSpan: Int
|
||||||
|
/// ```
|
||||||
class JsonRenderer extends ValueRenderer {
|
class JsonRenderer extends ValueRenderer {
|
||||||
extension = "json"
|
extension = "json"
|
||||||
|
|
||||||
@@ -486,6 +557,16 @@ class JsonRenderer extends ValueRenderer {
|
|||||||
/// Renders values as YAML.
|
/// Renders values as YAML.
|
||||||
///
|
///
|
||||||
/// To render a YAML stream, set [isStream] to [true].
|
/// To render a YAML stream, set [isStream] to [true].
|
||||||
|
///
|
||||||
|
/// The [yaml.Property] annotation can be used to change how a property name renders into YAML.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```
|
||||||
|
/// import "pkl:yaml"
|
||||||
|
///
|
||||||
|
/// @yaml.Property { name = "wing_span" }
|
||||||
|
/// wingSpan: Int
|
||||||
|
/// ```
|
||||||
class YamlRenderer extends ValueRenderer {
|
class YamlRenderer extends ValueRenderer {
|
||||||
extension = "yaml"
|
extension = "yaml"
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,16 @@
|
|||||||
@ModuleInfo { minPklVersion = "0.31.0" }
|
@ModuleInfo { minPklVersion = "0.31.0" }
|
||||||
module pkl.json
|
module pkl.json
|
||||||
|
|
||||||
|
/// Annotate properties of classes and modules with this class to override how a [JsonRenderer]
|
||||||
|
/// interprets a property's name.
|
||||||
|
@Since { version = "0.31.0" }
|
||||||
|
class Property extends ConvertProperty {
|
||||||
|
/// The new name to use for the annotated property when rendered by [JsonRenderer].
|
||||||
|
name: String
|
||||||
|
|
||||||
|
render = (prop, renderer) -> if (renderer is JsonRenderer) Pair(name, prop.value) else prop
|
||||||
|
}
|
||||||
|
|
||||||
/// A JSON parser.
|
/// A JSON parser.
|
||||||
///
|
///
|
||||||
/// JSON values are mapped to Pkl values as follows:
|
/// JSON values are mapped to Pkl values as follows:
|
||||||
|
|||||||
@@ -68,6 +68,16 @@ function ExtVar(_name: String): ExtVar = new { name = _name }
|
|||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// The [Property] annotation can be used to change how a property name renders into Jsonnet.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```
|
||||||
|
/// import "pkl:jsonnet"
|
||||||
|
///
|
||||||
|
/// @jsonnet.Property { name = "wing_span" }
|
||||||
|
/// wingSpan: Int
|
||||||
|
/// ```
|
||||||
class Renderer extends ValueRenderer {
|
class Renderer extends ValueRenderer {
|
||||||
extension = "jsonnet"
|
extension = "jsonnet"
|
||||||
|
|
||||||
@@ -91,6 +101,16 @@ class Renderer extends ValueRenderer {
|
|||||||
external function renderValue(value: Any): String
|
external function renderValue(value: Any): String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Annotate properties of classes and modules with this class to override how a [Renderer]
|
||||||
|
/// interprets a property's name.
|
||||||
|
@Since { version = "0.31.0" }
|
||||||
|
class Property extends ConvertProperty {
|
||||||
|
/// The new name to use for the annotated property when rendered by [Renderer].
|
||||||
|
name: String
|
||||||
|
|
||||||
|
render = (prop, renderer) -> if (renderer is Renderer) Pair(name, prop.value) else prop
|
||||||
|
}
|
||||||
|
|
||||||
/// An `importstr` construct that, when evaluated by Jsonnet, returns the content of a UTF-8 text file.
|
/// An `importstr` construct that, when evaluated by Jsonnet, returns the content of a UTF-8 text file.
|
||||||
///
|
///
|
||||||
/// To construct an [ImportStr], use method [ImportStr()].
|
/// To construct an [ImportStr], use method [ImportStr()].
|
||||||
|
|||||||
@@ -32,6 +32,11 @@
|
|||||||
module pkl.pklbinary
|
module pkl.pklbinary
|
||||||
|
|
||||||
/// Render values as `pkl-binary`.
|
/// Render values as `pkl-binary`.
|
||||||
|
///
|
||||||
|
/// The `pkl-binary` renderer disables all [ConvertProperty] annotation converters by default
|
||||||
|
/// because `pkl-binary` data is intended to closely represent native Pkl types and data.
|
||||||
|
/// This behavior may be overridden for [ConvertProperty] or its subclasses by adding an entry to
|
||||||
|
/// [Renderer.convertPropertyTransformers].
|
||||||
class Renderer extends BytesRenderer {
|
class Renderer extends BytesRenderer {
|
||||||
/// Renders [value] as `pkl-binary`.
|
/// Renders [value] as `pkl-binary`.
|
||||||
external function renderValue(value: Any): Bytes
|
external function renderValue(value: Any): Bytes
|
||||||
@@ -40,4 +45,11 @@ class Renderer extends BytesRenderer {
|
|||||||
///
|
///
|
||||||
/// Every `pkl-binary` value is also a valid document.
|
/// Every `pkl-binary` value is also a valid document.
|
||||||
external function renderDocument(value: Any): Bytes
|
external function renderDocument(value: Any): Bytes
|
||||||
|
|
||||||
|
convertPropertyTransformers {
|
||||||
|
// disable all property conversions by default
|
||||||
|
[ConvertProperty] {
|
||||||
|
render = (property, _) -> property
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,16 @@ import "pkl:reflect"
|
|||||||
/// Note: This class is _experimental_ and not ready for production use.
|
/// Note: This class is _experimental_ and not ready for production use.
|
||||||
///
|
///
|
||||||
/// As of this release, only Protocol Buffers' text format is supported.
|
/// As of this release, only Protocol Buffers' text format is supported.
|
||||||
|
///
|
||||||
|
/// The [Property] annotation can be used to change how a property name renders into Protobuf.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```
|
||||||
|
/// import "pkl:protobuf"
|
||||||
|
///
|
||||||
|
/// @protobuf.Property { name = "wing_span" }
|
||||||
|
/// wingSpan: Int
|
||||||
|
/// ```
|
||||||
class Renderer extends ValueRenderer {
|
class Renderer extends ValueRenderer {
|
||||||
/// The characters to use for indenting output.
|
/// The characters to use for indenting output.
|
||||||
///
|
///
|
||||||
@@ -38,3 +48,13 @@ class Renderer extends ValueRenderer {
|
|||||||
/// Returns the canonical name for [type].
|
/// Returns the canonical name for [type].
|
||||||
external function renderType(type: reflect.Type): String
|
external function renderType(type: reflect.Type): String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Annotate properties of classes and modules with this class to override how a [Renderer]
|
||||||
|
/// interprets a property's name.
|
||||||
|
@Since { version = "0.31.0" }
|
||||||
|
class Property extends ConvertProperty {
|
||||||
|
/// The new name to use for the annotated property when rendered by [Renderer].
|
||||||
|
name: String
|
||||||
|
|
||||||
|
render = (prop, renderer) -> if (renderer is Renderer) Pair(name, prop.value) else prop
|
||||||
|
}
|
||||||
|
|||||||
+24
-4
@@ -32,6 +32,16 @@ module pkl.xml
|
|||||||
///
|
///
|
||||||
/// To set the name and attributes of the XML document's root element,
|
/// To set the name and attributes of the XML document's root element,
|
||||||
/// use [rootElementName] and [rootElementAttributes].
|
/// use [rootElementName] and [rootElementAttributes].
|
||||||
|
///
|
||||||
|
/// The [Property] annotation can be used to change how a property name renders into XML.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```
|
||||||
|
/// import "pkl:xml"
|
||||||
|
///
|
||||||
|
/// @xml.Property { name = "wing_span" }
|
||||||
|
/// wingSpan: Int
|
||||||
|
/// ```
|
||||||
class Renderer extends ValueRenderer {
|
class Renderer extends ValueRenderer {
|
||||||
extension = "xml"
|
extension = "xml"
|
||||||
|
|
||||||
@@ -52,6 +62,16 @@ class Renderer extends ValueRenderer {
|
|||||||
external function renderValue(value: Any): String
|
external function renderValue(value: Any): String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Annotate properties of classes and modules with this class to override how a [Renderer]
|
||||||
|
/// interprets a property's name.
|
||||||
|
@Since { version = "0.31.0" }
|
||||||
|
class Property extends ConvertProperty {
|
||||||
|
/// The new name to use for the annotated property when rendered by [Renderer].
|
||||||
|
name: String
|
||||||
|
|
||||||
|
render = (prop, renderer) -> if (renderer is Renderer) Pair(name, prop.value) else prop
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates an XML element with the given name.
|
/// Creates an XML element with the given name.
|
||||||
///
|
///
|
||||||
/// Use this method to directly define an XML element
|
/// Use this method to directly define an XML element
|
||||||
@@ -68,10 +88,10 @@ class Renderer extends ValueRenderer {
|
|||||||
///
|
///
|
||||||
/// To define the XML element's content, add child values (normally also called "elements") to the `Element` object:
|
/// To define the XML element's content, add child values (normally also called "elements") to the `Element` object:
|
||||||
/// ```
|
/// ```
|
||||||
/// order = xml.Element("order") { // element with one child
|
/// order = (xml.Element("order")) { // element with one child
|
||||||
/// xml.Element("item") { // element with two children
|
/// (xml.Element("item")) { // element with two children
|
||||||
/// xml.Element("name") { "banana" } // element with one child
|
/// (xml.Element("name")) { "banana" } // element with one child
|
||||||
/// xml.Element("quantity") { 42 } // element with one child
|
/// (xml.Element("quantity")) { 42 } // element with one child
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|||||||
@@ -18,6 +18,16 @@
|
|||||||
@ModuleInfo { minPklVersion = "0.31.0" }
|
@ModuleInfo { minPklVersion = "0.31.0" }
|
||||||
module pkl.yaml
|
module pkl.yaml
|
||||||
|
|
||||||
|
/// Annotate properties of classes and modules with this class to override how a [YamlRenderer]
|
||||||
|
/// interprets a property's name.
|
||||||
|
@Since { version = "0.31.0" }
|
||||||
|
class Property extends ConvertProperty {
|
||||||
|
/// The new name to use for the annotated property when rendered by [YamlRenderer].
|
||||||
|
name: String
|
||||||
|
|
||||||
|
render = (prop, renderer) -> if (renderer is YamlRenderer) Pair(name, prop.value) else prop
|
||||||
|
}
|
||||||
|
|
||||||
/// A YAML parser.
|
/// A YAML parser.
|
||||||
///
|
///
|
||||||
/// YAML values are mapped to Pkl values as follows:
|
/// YAML values are mapped to Pkl values as follows:
|
||||||
|
|||||||
Reference in New Issue
Block a user